summaryrefslogtreecommitdiff
path: root/board/servo_micro
diff options
context:
space:
mode:
authorMatthew Blecker <matthewb@chromium.org>2018-07-26 20:55:35 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-08-09 22:04:40 -0700
commitb3f08549e2b42a3ac4d9070c559fb91e0b94ef4f (patch)
tree1c66d9d99039285101a72a91c20d537d7dc81c44 /board/servo_micro
parent740427a24398b3fd66904e913b742f751371d09c (diff)
downloadchrome-ec-b3f08549e2b42a3ac4d9070c559fb91e0b94ef4f.tar.gz
servo_micro: Add enable_ite_dfu and get_ite_chipid console commands.
enable_ite_dfu: Enable direct firmware update (DFU) over I2C mode on ITE IT8320 EC chip by sending special non-I2C waveforms over the I2C bus wires. get_ite_chipid: Verify that DFU mode is enabled by querying the EC over I2C for its CHIPID1 and CHIPID2 registers. It will only respond over I2C when in DFU mode. BRANCH=none BUG=b:79684405 TEST=get_ite_chipid succeeds after enable_ite_dfu Change-Id: Ief2c12ebd902285ea3d285767deb8d35c0017592 Signed-off-by: Matthew Blecker <matthewb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1152565 Reviewed-by: Nick Sanders <nsanders@chromium.org>
Diffstat (limited to 'board/servo_micro')
-rw-r--r--board/servo_micro/board.c293
-rw-r--r--board/servo_micro/gpio.inc3
2 files changed, 295 insertions, 1 deletions
diff --git a/board/servo_micro/board.c b/board/servo_micro/board.c
index 077c7ef86b..a54e5581f8 100644
--- a/board/servo_micro/board.c
+++ b/board/servo_micro/board.c
@@ -14,6 +14,7 @@
#include "registers.h"
#include "spi.h"
#include "task.h"
+#include "timer.h"
#include "update_fw.h"
#include "usart-stm32f0.h"
#include "usart_tx_dma.h"
@@ -170,6 +171,298 @@ DECLARE_CONSOLE_COMMAND(parity, command_uart_parity,
"Set parity on uart");
/******************************************************************************
+ * Commands for sending the magic non-I2C handshake over I2C bus wires to an
+ * ITE IT8320 EC chip to enable direct firmware update (DFU) over I2C mode.
+ */
+
+#define KHz 1000
+#define MHz (1000 * KHz)
+
+/*
+ * These constants are values that one might want to try changing if
+ * enable_ite_dfu stops working, or does not work on a new ITE EC chip revision.
+ */
+
+#define ITE_DFU_I2C_CMD_ADDR 0xB4 /* 7 bit form is 0x5A */
+#define ITE_DFU_I2C_DATA_ADDR 0x6A /* 7 bit form is 0x35 */
+
+#define SMCLK_WAVEFORM_PERIOD_HZ (100 * KHz)
+#define SMDAT_WAVEFORM_PERIOD_HZ (200 * KHz)
+
+#define START_DELAY_MS 5
+#define SPECIAL_WAVEFORM_MS 50
+#define PLL_STABLE_MS 10
+
+/*
+ * Digital line levels to hold before (PRE_) or after (POST_) sending the
+ * special waveforms. 0 for low, 1 for high.
+ */
+#define SMCLK_PRE_LEVEL 0
+#define SMDAT_PRE_LEVEL 0
+#define SMCLK_POST_LEVEL 0
+#define SMDAT_POST_LEVEL 0
+
+/* The caller should hold the i2c_lock() for I2C_PORT_MASTER. */
+static int ite_i2c_read_register(uint8_t register_offset, uint8_t *output)
+{
+ /*
+ * Ideally the write and read would be done in one I2C transaction, as
+ * is normally done when reading from the same I2C address that the
+ * write was sent to. The ITE EC is abnormal in that regard, with its
+ * different addresses for writes vs reads.
+ *
+ * i2c_xfer() does not support that. Its I2C_XFER_START and
+ * I2C_XFER_STOP flag bits do not cleanly support that scenario, they
+ * are for continuing transfers without either of STOP or START
+ * in-between.
+ *
+ * For what it's worth, the iteflash.c FTDI-based implementation of this
+ * does the same thing, issuing a STOP between the write and read. This
+ * works, even if perhaps it should not.
+ */
+ int ret;
+ /* Tell the ITE EC which register we want to read. */
+ ret = i2c_xfer(I2C_PORT_MASTER, ITE_DFU_I2C_CMD_ADDR, &register_offset,
+ sizeof(register_offset), NULL, 0, I2C_XFER_SINGLE);
+ if (ret != EC_SUCCESS)
+ return ret;
+ /* Read in the 1 byte register value. */
+ ret = i2c_xfer(I2C_PORT_MASTER, ITE_DFU_I2C_DATA_ADDR, NULL, 0,
+ output, sizeof(*output), I2C_XFER_SINGLE);
+ return ret;
+}
+
+/* Helper function to read ITE chip ID, for verifying ITE DFU mode. */
+static int cprint_ite_chip_id(void)
+{
+ /*
+ * Per i2c_read8() implementation, use an array even for single byte
+ * reads to ensure alignment for DMA on STM32.
+ */
+ uint8_t chipid1[1];
+ uint8_t chipid2[1];
+ uint8_t chipver[1];
+
+ int ret;
+ int chip_version;
+ int flash_kb;
+
+ i2c_lock(I2C_PORT_MASTER, 1);
+
+ /* Read the CHIPID1 register. */
+ ret = ite_i2c_read_register(0x00, chipid1);
+ if (ret != EC_SUCCESS)
+ goto unlock;
+
+ /* Read the CHIPID2 register. */
+ ret = ite_i2c_read_register(0x01, chipid2);
+ if (ret != EC_SUCCESS)
+ goto unlock;
+
+ /* Read the CHIPVER register. */
+ ret = ite_i2c_read_register(0x02, chipver);
+
+unlock:
+ i2c_lock(I2C_PORT_MASTER, 0);
+ if (ret != EC_SUCCESS)
+ return ret;
+
+ /*
+ * Compute chip version and embedded flash size from the CHIPVER value.
+ *
+ * Chip version is mapping from bit 3-0
+ * Flash size is mapping from bit 7-4
+ *
+ * Chip Version (bits 3-0)
+ * 0: AX
+ * 1: BX
+ * 2: CX
+ * 3: DX
+ *
+ * CX or prior flash size (bits 7-4)
+ * 0:128KB
+ * 4:192KB
+ * 8:256KB
+ *
+ * DX flash size (bits 7-4)
+ * 0:128KB
+ * 2:192KB
+ * 4:256KB
+ * 6:384KB
+ * 8:512KB
+ */
+ chip_version = chipver[0] & 0x07;
+ if (chip_version < 0x3) {
+ /* Chip version is CX or earlier. */
+ switch (chipver[0] >> 4) {
+ case 0:
+ flash_kb = 128;
+ break;
+ case 4:
+ flash_kb = 192;
+ break;
+ case 8:
+ flash_kb = 256;
+ break;
+ default:
+ flash_kb = -2;
+ }
+ } else if (chip_version == 0x3) {
+ /* Chip version is DX. */
+ switch (chipver[0] >> 4) {
+ case 0:
+ flash_kb = 128;
+ break;
+ case 2:
+ flash_kb = 192;
+ break;
+ case 4:
+ flash_kb = 256;
+ break;
+ case 6:
+ flash_kb = 384;
+ break;
+ case 8:
+ flash_kb = 512;
+ break;
+ default:
+ flash_kb = -3;
+ }
+ } else {
+ /* Unrecognized chip version. */
+ flash_kb = -1;
+ }
+
+ ccprintf("ITE EC info: CHIPID1=0x%02X CHIPID2=0x%02X CHIPVER=0x%02X ",
+ chipid1[0], chipid2[0], chipver[0]);
+ ccprintf("version=%d flash_bytes=%d\n", chip_version, flash_kb << 10);
+
+ /*
+ * IT8320_eflash_SMBus_Programming_Guide.pdf says it is an error if
+ * CHIPID1 != 0x83.
+ */
+ if (chipid1[0] != 0x83)
+ ret = EC_ERROR_HW_INTERNAL;
+
+ return ret;
+}
+
+/* Enable ITE direct firmware update (DFU) mode. */
+static int command_enable_ite_dfu(int argc, char **argv)
+{
+ if (argc > 1)
+ return EC_ERROR_PARAM_COUNT;
+
+ /* Enable peripheral clocks. */
+ STM32_RCC_APB2ENR |=
+ STM32_RCC_APB2ENR_TIM16EN | STM32_RCC_APB2ENR_TIM17EN;
+
+ /* Reset timer registers which are not otherwise set below. */
+ STM32_TIM_CR2(16) = 0x0000;
+ STM32_TIM_CR2(17) = 0x0000;
+ STM32_TIM_DIER(16) = 0x0000;
+ STM32_TIM_DIER(17) = 0x0000;
+ STM32_TIM_SR(16) = 0x0000;
+ STM32_TIM_SR(17) = 0x0000;
+ STM32_TIM_CNT(16) = 0x0000;
+ STM32_TIM_CNT(17) = 0x0000;
+ STM32_TIM_RCR(16) = 0x0000;
+ STM32_TIM_RCR(17) = 0x0000;
+ STM32_TIM_DCR(16) = 0x0000;
+ STM32_TIM_DCR(17) = 0x0000;
+ STM32_TIM_DMAR(16) = 0x0000;
+ STM32_TIM_DMAR(17) = 0x0000;
+
+ /* Prescale to 1 MHz and use ARR to achieve NNN KHz periods. */
+ /* This approach is seen in STM's documentation. */
+ STM32_TIM_PSC(16) = (CPU_CLOCK / MHz) - 1;
+ STM32_TIM_PSC(17) = (CPU_CLOCK / MHz) - 1;
+
+ /* Set the waveform periods based on 1 MHz prescale. */
+ STM32_TIM_ARR(16) = (MHz / SMCLK_WAVEFORM_PERIOD_HZ) - 1;
+ STM32_TIM_ARR(17) = (MHz / SMDAT_WAVEFORM_PERIOD_HZ) - 1;
+
+ /* Set output compare 1 mode to PWM mode 1 and enable preload. */
+ STM32_TIM_CCMR1(16) =
+ STM32_TIM_CCMR1_OC1M_PWM_MODE_1 | STM32_TIM_CCMR1_OC1PE;
+ STM32_TIM_CCMR1(17) =
+ STM32_TIM_CCMR1_OC1M_PWM_MODE_1 | STM32_TIM_CCMR1_OC1PE;
+
+ /* Enable output compare 1. */
+ STM32_TIM_CCER(16) = STM32_TIM_CCER_CC1E;
+ STM32_TIM_CCER(17) = STM32_TIM_CCER_CC1E;
+
+ /* Enable main output. */
+ STM32_TIM_BDTR(16) = STM32_TIM_BDTR_MOE;
+ STM32_TIM_BDTR(17) = STM32_TIM_BDTR_MOE;
+
+ /* Update generation (reinitialize counters). */
+ STM32_TIM_EGR(16) = STM32_TIM_EGR_UG;
+ STM32_TIM_EGR(17) = STM32_TIM_EGR_UG;
+
+ /* Set duty cycle to 0% or 100%, pinning each channel low or high. */
+ STM32_TIM_CCR1(16) = SMCLK_PRE_LEVEL ? 0xFFFF : 0x0000;
+ STM32_TIM_CCR1(17) = SMDAT_PRE_LEVEL ? 0xFFFF : 0x0000;
+
+ /* Enable timer counters. */
+ STM32_TIM_CR1(16) = STM32_TIM_CR1_CEN;
+ STM32_TIM_CR1(17) = STM32_TIM_CR1_CEN;
+
+ /* Set PB8 GPIO to alternate mode TIM16_CH1. */
+ /* Set PB9 GPIO to alternate mode TIM17_CH1. */
+ gpio_config_module(MODULE_I2C_TIMERS, 1);
+
+ msleep(START_DELAY_MS);
+
+ /* Set pulse width to half of waveform period. */
+ STM32_TIM_CCR1(16) = (MHz / SMCLK_WAVEFORM_PERIOD_HZ) / 2;
+ STM32_TIM_CCR1(17) = (MHz / SMDAT_WAVEFORM_PERIOD_HZ) / 2;
+
+ msleep(SPECIAL_WAVEFORM_MS);
+
+ /* Set duty cycle to 0% or 100%, pinning each channel low or high. */
+ STM32_TIM_CCR1(16) = SMCLK_POST_LEVEL ? 0xFFFF : 0x0000;
+ STM32_TIM_CCR1(17) = SMDAT_POST_LEVEL ? 0xFFFF : 0x0000;
+
+ msleep(PLL_STABLE_MS);
+
+ /* Set PB8 GPIO to alternate mode I2C1_SCL. */
+ /* Set PB9 GPIO to alternate mode I2C1_DAT. */
+ gpio_config_module(MODULE_I2C, 1);
+
+ /* Disable timer counters. */
+ STM32_TIM_CR1(16) = 0x0000;
+ STM32_TIM_CR1(17) = 0x0000;
+
+ /* Disable peripheral clocks. */
+ STM32_RCC_APB2ENR &=
+ ~(STM32_RCC_APB2ENR_TIM16EN | STM32_RCC_APB2ENR_TIM17EN);
+
+ return cprint_ite_chip_id();
+}
+DECLARE_CONSOLE_COMMAND(
+ enable_ite_dfu, command_enable_ite_dfu, "",
+ "Enable ITE Direct Firmware Update (DFU) mode");
+
+/* Read ITE chip ID. Can be used to verify ITE DFU mode. */
+/*
+ * TODO(b/79684405): There is nothing specific about Servo Micro in the
+ * implementation of the "get_ite_chipid" command. Move the implementation to a
+ * common place so that it need not be reimplemented for every Servo version
+ * that "enable_ite_dfu" is implemented for.
+ */
+static int command_get_ite_chipid(int argc, char **argv)
+{
+ if (argc > 1)
+ return EC_ERROR_PARAM_COUNT;
+
+ return cprint_ite_chip_id();
+}
+DECLARE_CONSOLE_COMMAND(
+ get_ite_chipid, command_get_ite_chipid, "",
+ "Read ITE EC chip ID, version, flash size (must be in DFU mode)");
+
+/******************************************************************************
* Define the strings used in our USB descriptors.
*/
const void *const usb_strings[] = {
diff --git a/board/servo_micro/gpio.inc b/board/servo_micro/gpio.inc
index 2118487860..ea05c74907 100644
--- a/board/servo_micro/gpio.inc
+++ b/board/servo_micro/gpio.inc
@@ -55,6 +55,7 @@ ALTERNATE(PIN_MASK(A, 0x000C), 1, MODULE_USART, 0) /* USART2: PA2/PA3 - Servo UA
ALTERNATE(PIN_MASK(B, 0x0C00), 4, MODULE_USART, 0) /* USART3: PB10/PB11 - Servo UART2 */
ALTERNATE(PIN_MASK(A, 0x0003), 4, MODULE_USART, 0) /* USART4: PA0/PA1 - Servo UART3 */
-ALTERNATE(PIN_MASK(B, 0x0300), 1, MODULE_I2C, 0) /* I2C MASTER:PB8/9 GPIO_ODR_HIGH */
+ALTERNATE(PIN_MASK(B, 0x0300), 1, MODULE_I2C, 0) /* I2C MASTER:PB8/PB9 GPIO_ODR_HIGH */
+ALTERNATE(PIN_MASK(B, 0x0300), 2, MODULE_I2C_TIMERS, 0) /* I2C MASTER:PB8/PB9 TIM16_CH1/TIM17_CH1 */
ALTERNATE(PIN_MASK(B, 0xE000), 0, MODULE_SPI_FLASH, 0) /* SPI: PB15 - PB12 MOSI, MISO, CLK, CS */