summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorJett Rink <jettrink@chromium.org>2020-02-10 16:05:20 -0700
committerCommit Bot <commit-bot@chromium.org>2020-02-25 02:28:19 +0000
commit6331dd76feaa4dfeb5f66635f8b570b884380cb7 (patch)
tree9056a777a62ffb14e3dacd6e4551dfed3ca4387a /chip
parentb7de2bf7b2879d582f0c9878034e6f2bba55cbe5 (diff)
downloadchrome-ec-6331dd76feaa4dfeb5f66635f8b570b884380cb7.tar.gz
servo_micro: move ite flashing code
In preparation for servo_micro and c2d2 to sharing the ite, i2c flashing code, move it to a stm specify common file. It is STM specific because it explicitly uses STM registers to accomplish the non-compliant i2c waveforms needed to put the ITE EC into flash mode. BRANCH=servo BUG=b:148610186,b:79684405 TEST=flash ampton with servo_micro using this code Change-Id: Ia0f3f944df2f8a8ad47ea5a62c5f0edae2c71943 Signed-off-by: Jett Rink <jettrink@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2064592 Reviewed-by: Diana Z <dzigterman@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r--chip/stm32/build.mk1
-rw-r--r--chip/stm32/i2c_ite_flash_support.c335
2 files changed, 336 insertions, 0 deletions
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk
index 6ffe44f88e..8949349aed 100644
--- a/chip/stm32/build.mk
+++ b/chip/stm32/build.mk
@@ -48,6 +48,7 @@ chip-$(CONFIG_SPI_MASTER)+=spi_master$(SPI_TYPE).o
chip-$(CONFIG_COMMON_GPIO)+=gpio.o gpio-$(CHIP_FAMILY).o
chip-$(CONFIG_COMMON_TIMER)+=hwtimer$(TIMER_TYPE).o
chip-$(CONFIG_I2C)+=i2c-$(CHIP_FAMILY).o
+chip-$(CONFIG_ITE_FLASH_SUPPORT)+=i2c_ite_flash_support.o
chip-$(CONFIG_STREAM_USART)+=usart.o usart-$(CHIP_FAMILY).o
chip-$(CONFIG_STREAM_USART)+=usart_rx_interrupt-$(CHIP_FAMILY).o
chip-$(CONFIG_STREAM_USART)+=usart_tx_interrupt.o
diff --git a/chip/stm32/i2c_ite_flash_support.c b/chip/stm32/i2c_ite_flash_support.c
new file mode 100644
index 0000000000..6d1631a5bb
--- /dev/null
+++ b/chip/stm32/i2c_ite_flash_support.c
@@ -0,0 +1,335 @@
+/* Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/* STM implementation for flashing ITE-based ECs over i2c */
+
+#include "i2c_ite_flash_support.h"
+#include "i2c.h"
+#include "registers.h"
+#include "time.h"
+
+/*
+ * As of 2018-11-27 the default for both is 60 bytes. These larger values allow
+ * for reflashing of ITE EC chips over I2C
+ * (https://issuetracker.google.com/79684405) in reasonably speedy fashion. If
+ * the EC firmware defaults are ever raised significantly, consider removing
+ * these overrides.
+ *
+ * As of 2018-11-27 the actual maximum write size supported by the I2C-over-USB
+ * protocol is (1<<12)-1, and the maximum read size supported is
+ * (1<<15)-1. However compile time assertions require that these values be
+ * powers of 2 after overheads are included. Thus, the write limit set here
+ * /should/ be (1<<12)-4 and the read limit should be (1<<15)-6, however those
+ * ideal limits are not actually possible because stm32 lacks sufficient
+ * spare memory for them. With symmetrical limits, the maximum that currently
+ * fits is (1<<11)-4 write limit and (1<<11)-6 read limit, leaving 1404 bytes of
+ * RAM available.
+ *
+ * However even with a sufficiently large write value here, the maximum that
+ * actually works as of 2018-12-03 is 255 bytes. Additionally, ITE EC firmware
+ * image verification requires exactly 256 byte reads. Thus the only useful
+ * powers-of-2-minus-overhead limits to set here are (1<<9)-4 writes and
+ * (1<<9)-6 reads, leaving 6012 bytes of RAM available, down from 7356 bytes of
+ * RAM available with the default 60 byte limits.
+ */
+#if CONFIG_USB_I2C_MAX_WRITE_COUNT != ((1<<9) - 4)
+#error Must set CONFIG_USB_I2C_MAX_WRITE_COUNT to ((1<<9) - 4)
+#endif
+#if CONFIG_USB_I2C_MAX_READ_COUNT != ((1<<9) - 6)
+#error Must set CONFIG_USB_I2C_MAX_WRITE_COUNT to ((1<<9) - 6)
+#endif
+
+/*
+ * iteflash requires 256 byte reads for verifying ITE EC firmware. Without this
+ * the limit is CONFIG_I2C_CHIP_MAX_READ_SIZE which is 255 for STM32F0 due to an
+ * 8 bit field, per src/platform/ec/include/config.h comment.
+ */
+#ifndef CONFIG_I2C_XFER_LARGE_READ
+#error Must define CONFIG_I2C_XFER_LARGE_READ
+#endif
+
+#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_FLAGS 0x5A
+#define ITE_DFU_I2C_DATA_ADDR_FLAGS 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 ite_dfu_config.i2c_port. */
+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 7-bit 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_unlocked(ite_dfu_config.i2c_port,
+ ITE_DFU_I2C_CMD_ADDR_FLAGS,
+ &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_unlocked(ite_dfu_config.i2c_port,
+ ITE_DFU_I2C_DATA_ADDR_FLAGS,
+ 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(ite_dfu_config.i2c_port, 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(ite_dfu_config.i2c_port, 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 GPIO to alternate mode TIM(16|17)_CH1(N)? */
+ gpio_config_pin(MODULE_I2C_TIMERS, ite_dfu_config.scl, 1);
+ gpio_config_pin(MODULE_I2C_TIMERS, ite_dfu_config.sda, 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 GPIO back to alternate mode I2C. */
+ gpio_config_pin(MODULE_I2C, ite_dfu_config.scl, 1);
+ gpio_config_pin(MODULE_I2C, ite_dfu_config.sda, 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. */
+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)");