summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDino Li <Dino.Li@ite.com.tw>2021-01-06 15:45:16 +0800
committerCommit Bot <commit-bot@chromium.org>2021-01-11 03:53:07 +0000
commit034be0ff1c2ff20a92dac94662e9302595043b0b (patch)
tree3d07a23e19988322216e4583ecdfd6681e384167
parent749330ad548119c6235cb5868c2ea0f096ea22d5 (diff)
downloadchrome-ec-034be0ff1c2ff20a92dac94662e9302595043b0b.tar.gz
baseboard/kukui: add eMMC CMD0 driver of IT81202
BUG=b:170795623 BRANCH=none TEST=run reboot test over 2000 times on reworked Juniper. Signed-off-by: Dino Li <Dino.Li@ite.com.tw> Change-Id: If4e6e4b734d3d9a1473689c7096b09737afa9e70 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2612626 Reviewed-by: Eric Yilun Lin <yllin@chromium.org>
-rw-r--r--baseboard/kukui/build.mk7
-rw-r--r--baseboard/kukui/emmc_ite.c242
2 files changed, 247 insertions, 2 deletions
diff --git a/baseboard/kukui/build.mk b/baseboard/kukui/build.mk
index ebd0882707..c64f6978c8 100644
--- a/baseboard/kukui/build.mk
+++ b/baseboard/kukui/build.mk
@@ -6,9 +6,12 @@
# Baseboard specific files build
#
+# Select eMMC CMD0 driver.
+EMMC_CMD0_DRIVER=$(if $(CHIP_IT83XX),emmc_ite.o,emmc.o)
+
baseboard-y=baseboard.o
baseboard-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_policy.o
-baseboard-$(CONFIG_BOOTBLOCK)+=emmc.o
+baseboard-$(CONFIG_BOOTBLOCK)+=$(EMMC_CMD0_DRIVER)
baseboard-$(VARIANT_KUKUI_BATTERY_MAX17055)+=battery_max17055.o
baseboard-$(VARIANT_KUKUI_BATTERY_MM8013)+=battery_mm8013.o
@@ -19,7 +22,7 @@ baseboard-$(VARIANT_KUKUI_CHARGER_MT6370)+=charger_mt6370.o
baseboard-$(VARIANT_KUKUI_POGO_KEYBOARD)+=base_detect_kukui.o
-$(out)/RO/baseboard/$(BASEBOARD)/emmc.o: $(out)/bootblock_data.h
+$(out)/RO/baseboard/$(BASEBOARD)/$(EMMC_CMD0_DRIVER): $(out)/bootblock_data.h
# bootblock size from 12769.0
DEFAULT_BOOTBLOCK_SIZE:=21504
diff --git a/baseboard/kukui/emmc_ite.c b/baseboard/kukui/emmc_ite.c
new file mode 100644
index 0000000000..79019164ec
--- /dev/null
+++ b/baseboard/kukui/emmc_ite.c
@@ -0,0 +1,242 @@
+/* Copyright 2021 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.
+ */
+
+#include "chipset.h"
+#include "console.h"
+#include "endian.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "hwtimer.h"
+#include "intc.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+
+#include "bootblock_data.h"
+
+#define CPRINTS(format, args...) cprints(CC_SPI, format, ## args)
+
+enum emmc_cmd {
+ EMMC_ERROR = -1,
+ EMMC_IDLE = 0,
+ EMMC_PRE_IDLE,
+ EMMC_BOOT,
+};
+
+static void emmc_reset_spi_tx(void)
+{
+ /* Reset TX FIFO and count monitor */
+ IT83XX_SPI_TXFCR = IT83XX_SPI_TXFR | IT83XX_SPI_TXFCMR;
+ /* Send idle state (high/0xff) if master clocks in data. */
+ IT83XX_SPI_FCR = 0;
+}
+
+static void emmc_reset_spi_rx(void)
+{
+ /* End Rx FIFO access */
+ IT83XX_SPI_TXRXFAR = 0;
+ /* Reset RX FIFO and count monitor */
+ IT83XX_SPI_FCR = IT83XX_SPI_RXFR | IT83XX_SPI_RXFCMR;
+}
+
+/*
+ * Set SPI module work as eMMC Alternative Boot Mode.
+ * (CS# pin isn't required, and dropping data until CMD goes low)
+ */
+static void emmc_enable_spi(void)
+{
+ /* Set SPI pin mux to eMMC (GPM2:CLK, GPM3:CMD, GPM6:DATA0) */
+ IT83XX_GCTRL_PIN_MUX0 |= BIT(7);
+ /* Enable eMMC Alternative Boot Mode */
+ IT83XX_SPI_EMMCBMR |= IT83XX_SPI_EMMCABM;
+ /* Reset TX and RX FIFO */
+ emmc_reset_spi_tx();
+ emmc_reset_spi_rx();
+ /* Response idle state (high) */
+ IT83XX_SPI_SPISRDR = 0xff;
+ /* FIFO will be overwritten once it's full */
+ IT83XX_SPI_GCR2 = 0;
+ /* Write to clear pending interrupt bits */
+ IT83XX_SPI_ISR = 0xff;
+ IT83XX_SPI_RX_VLISR = IT83XX_SPI_RVLI;
+ /* Enable RX fifo full interrupt */
+ IT83XX_SPI_IMR = 0xff;
+ IT83XX_SPI_RX_VLISMR |= IT83XX_SPI_RVLIM;
+ IT83XX_SPI_IMR &= ~IT83XX_SPI_RX_FIFO_FULL;
+ /*
+ * Enable interrupt to detect AP's BOOTBLOCK_EN_L. So EC is able to
+ * switch SPI module back to communication mode once BOOTBLOCK_EN_L
+ * goes high (AP Jumped to bootloader).
+ */
+ gpio_clear_pending_interrupt(GPIO_BOOTBLOCK_EN_L);
+ gpio_enable_interrupt(GPIO_BOOTBLOCK_EN_L);
+
+ disable_sleep(SLEEP_MASK_EMMC);
+ CPRINTS("eMMC emulation enabled");
+}
+DECLARE_HOOK(HOOK_CHIPSET_STARTUP, emmc_enable_spi, HOOK_PRIO_FIRST);
+
+static void emmc_disable_spi(void)
+{
+ /* Set SPI pin mux to AP communication mode */
+ IT83XX_GCTRL_PIN_MUX0 &= ~BIT(7);
+ /* Disable eMMC Alternative Boot Mode */
+ IT83XX_SPI_EMMCBMR &= ~IT83XX_SPI_EMMCABM;
+ /* Reset TX and RX FIFO */
+ emmc_reset_spi_tx();
+ emmc_reset_spi_rx();
+ /* Restore setting of SPI slave for communication */
+ /* EC responses "ready to receive" while AP clock in bytes. */
+ IT83XX_SPI_SPISRDR = EC_SPI_OLD_READY;
+ /* FIFO won't be overwritten once it's full */
+ IT83XX_SPI_GCR2 = IT83XX_SPI_RXF2OC | IT83XX_SPI_RXF1OC
+ | IT83XX_SPI_RXFAR;
+ /* Write to clear pending interrupt bits */
+ IT83XX_SPI_ISR = 0xff;
+ IT83XX_SPI_RX_VLISR = IT83XX_SPI_RVLI;
+ /* Enable RX valid length interrupt */
+ IT83XX_SPI_IMR = 0xff;
+ IT83XX_SPI_RX_VLISMR &= ~IT83XX_SPI_RVLIM;
+ /* Disable interrupt of detection of AP's BOOTBLOCK_EN_L */
+ gpio_disable_interrupt(GPIO_BOOTBLOCK_EN_L);
+
+ enable_sleep(SLEEP_MASK_EMMC);
+ CPRINTS("eMMC emulation disabled");
+}
+
+static void emmc_init_spi(void)
+{
+ /* Enable alternate function */
+ gpio_config_module(MODULE_SPI_FLASH, 1);
+}
+DECLARE_HOOK(HOOK_INIT, emmc_init_spi, HOOK_PRIO_INIT_SPI + 1);
+
+static void emmc_send_data_over_spi(uint8_t *tx, int tx_size, int rst_tx)
+{
+ int i;
+
+ /* Reset TX FIFO and count monitor */
+ if (rst_tx)
+ IT83XX_SPI_TXFCR = IT83XX_SPI_TXFR | IT83XX_SPI_TXFCMR;
+ /* CPU access TX FIFO1 and FIFO2 */
+ IT83XX_SPI_TXRXFAR = IT83XX_SPI_CPUTFA;
+
+ /* Write response data to TX FIFO */
+ for (i = 0; i < tx_size; i += 4)
+ IT83XX_SPI_CPUWTFDB0 = *(uint32_t *)(tx + i);
+ /*
+ * After writing data to TX FIFO is finished, this bit will
+ * be to indicate the SPI slave controller.
+ */
+ IT83XX_SPI_TXFCR = IT83XX_SPI_TXFS;
+ /* End CPU access TX FIFO */
+ IT83XX_SPI_TXRXFAR = 0;
+ /* SPI module access TX FIFO */
+ IT83XX_SPI_FCR = IT83XX_SPI_SPISRTXF;
+}
+
+static void emmc_bootblock_transfer(void)
+{
+ int tx_size, sent = 0, remaining = sizeof(bootblock_raw_data);
+ uint8_t *raw = (uint8_t *)bootblock_raw_data;
+ const uint32_t timeout_us = 200;
+ uint32_t start;
+
+ /*
+ * HW will transmit the data of FIFO1 or FIFO2 in turn.
+ * So when a FIFO is empty, we need to fill the FIFO out
+ * immediately.
+ */
+ emmc_send_data_over_spi(&raw[sent], 256, 1);
+ sent += 256;
+
+ while (sent < remaining) {
+ /* Wait for FIFO1 or FIFO2 have been transmitted */
+ start = __hw_clock_source_read();
+ while (!(IT83XX_SPI_TXFSR & BIT(0)) &&
+ (__hw_clock_source_read() - start < timeout_us))
+ ;
+ /* Abort an ongoing transfer due to a command is received. */
+ if (IT83XX_SPI_ISR & IT83XX_SPI_RX_FIFO_FULL)
+ break;
+ /* fill out next 128 bytes to FIFO1 or FIFO2 */
+ tx_size = (remaining - sent) < 128 ? (remaining - sent) : 128;
+ emmc_send_data_over_spi(&raw[sent], tx_size, 0);
+ sent += tx_size;
+ }
+}
+
+static enum emmc_cmd emmc_parse_command(int index, uint32_t *cmd0)
+{
+ int32_t shift0;
+ uint32_t data[3];
+
+ data[0] = htobe32(cmd0[index]);
+ data[1] = htobe32(cmd0[index+1]);
+ data[2] = htobe32(cmd0[index+2]);
+
+ if ((data[0] & 0xff000000) != 0x40000000) {
+ /* Figure out alignment (cmd starts with 01) */
+ /* Number of leading ones. */
+ shift0 = __builtin_clz(~data[0]);
+
+ data[0] = (data[0] << shift0) | (data[1] >> (32-shift0));
+ data[1] = (data[1] << shift0) | (data[2] >> (32-shift0));
+ }
+
+ if (data[0] == 0x40000000 && data[1] == 0x0095ffff) {
+ /* 400000000095 GO_IDLE_STATE */
+ CPRINTS("goIdle");
+ return EMMC_IDLE;
+ }
+
+ if (data[0] == 0x40f0f0f0 && data[1] == 0xf0fdffff) {
+ /* 40f0f0f0f0fd GO_PRE_IDLE_STATE */
+ CPRINTS("goPreIdle");
+ return EMMC_PRE_IDLE;
+ }
+
+ if (data[0] == 0x40ffffff && data[1] == 0xfae5ffff) {
+ /* 40fffffffae5 BOOT_INITIATION */
+ CPRINTS("bootInit");
+ return EMMC_BOOT;
+ }
+
+ CPRINTS("eMMC error");
+ return EMMC_ERROR;
+}
+
+/* AP has booted */
+void emmc_ap_jump_to_bl(enum gpio_signal signal)
+{
+ /* Transmission completed. Set SPI communication mode */
+ emmc_disable_spi();
+
+ CPRINTS("AP Jumped to BL");
+}
+
+void spi_emmc_cmd0_isr(uint32_t *cmd0_payload)
+{
+ enum emmc_cmd cmd;
+
+ for (int i = 0; i < 8; i++) {
+ if (cmd0_payload[i] == 0xffffffff)
+ continue;
+
+ cmd = emmc_parse_command(i, &cmd0_payload[i]);
+
+ if (cmd == EMMC_IDLE || cmd == EMMC_PRE_IDLE) {
+ /* Abort an ongoing transfer. */
+ emmc_reset_spi_tx();
+ break;
+ }
+
+ if (cmd == EMMC_BOOT) {
+ emmc_bootblock_transfer();
+ break;
+ }
+ }
+}