summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Worley <scott.worley@microchip.corp-partner.google.com>2017-12-21 15:06:32 -0500
committerchrome-bot <chrome-bot@chromium.org>2017-12-28 12:35:06 -0800
commit2f1f5a555105cdff9d6b0a3278bf13e617143c9c (patch)
tree1948401dd9bbbaeeec26db4785f4580bfeb1ab97
parent137a0e850aa5b598864a8b02bf9777415d892047 (diff)
downloadchrome-ec-2f1f5a555105cdff9d6b0a3278bf13e617143c9c.tar.gz
ec_chip_mchp: Add LFW files
Add Microchip MEC17xx family little-firmware (LFW) folder and files. Change-Id: I9142266d41234574730fadccd5a2cc27fe3d8fd7 Signed-off-by: Scott Worley <scott.worley@microchip.corp-partner.google.com>
-rw-r--r--chip/mchp/lfw/ec_lfw.c425
-rw-r--r--chip/mchp/lfw/ec_lfw.h38
-rw-r--r--chip/mchp/lfw/ec_lfw.ld78
3 files changed, 541 insertions, 0 deletions
diff --git a/chip/mchp/lfw/ec_lfw.c b/chip/mchp/lfw/ec_lfw.c
new file mode 100644
index 0000000000..a9f923d5bb
--- /dev/null
+++ b/chip/mchp/lfw/ec_lfw.c
@@ -0,0 +1,425 @@
+/* Copyright 2017 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.
+ *
+ * MCHP MEC SoC little FW
+ *
+ */
+
+#include <stdint.h>
+
+#include "config.h"
+#include "gpio.h"
+#include "spi.h"
+#include "spi_flash.h"
+#include "util.h"
+#include "timer.h"
+#include "dma.h"
+#include "registers.h"
+#include "cpu.h"
+#include "clock.h"
+#include "system.h"
+#include "version.h"
+#include "hwtimer.h"
+#include "gpio_list.h"
+#include "tfdp_chip.h"
+
+#ifdef CONFIG_MCHP_LFW_DEBUG
+#include "dma_chip.h"
+#endif
+
+#include "ec_lfw.h"
+
+/*
+ * Check if LFW build is pulling in GPSPI which is not
+ * used for EC firmware SPI flash access.
+ */
+#ifdef CONFIG_MCHP_GPSPI
+#error "FORCED BUILD ERROR: CONFIG_MCHP_CMX_GPSPI is defined"
+#endif
+
+#define LFW_SPI_BYTE_TRANSFER_TIMEOUT_US (1 * MSEC)
+#define LFW_SPI_BYTE_TRANSFER_POLL_INTERVAL_US 100
+
+__attribute__ ((section(".intvector")))
+const struct int_vector_t hdr_int_vect = {
+ /* init sp, unused. set by MEC ROM loader */
+ (void *)lfw_stack_top, /* preserve ROM log. was (void *)0x11FA00, */
+ &lfw_main, /* was &lfw_main, */ /* reset vector */
+ &fault_handler, /* NMI handler */
+ &fault_handler, /* HardFault handler */
+ &fault_handler, /* MPU fault handler */
+ &fault_handler /* Bus fault handler */
+};
+
+/* SPI devices - from board.c */
+const struct spi_device_t spi_devices[] = {
+ { CONFIG_SPI_FLASH_PORT, 4, GPIO_QMSPI_CS0 },
+};
+const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices);
+
+/*
+ * At POR or EC reset MCHP Boot-ROM should only load LFW and jumps
+ * into LFW entry point located at offset 0x04 of LFW.
+ * Entry point is programmed into SPI Header by Python SPI image
+ * builder at chip/mec1701/util/pack_ec.py
+ *
+ * EC_RO/RW calling LFW should enter through this routine if you
+ * want the vector table updated. The stack should be set to
+ * LFW linker file parameter lfw_stack_top because we do not
+ * know if the callers stack is OK.
+ *
+ * Make sure lfw_stack_top will not overwrite panic data!
+ * from include/panic.h
+ * Panic data goes at the end of RAM. This is safe because we don't
+ * context switch away from the panic handler before rebooting,
+ * and stacks and data start at the beginning of RAM.
+ *
+ * chip level config_chip.h
+ * #define CONFIG_RAM_SIZE 0x00008000
+ * #define CONFIG_RAM_BASE 0x120000 - 0x8000 = 0x118000
+ *
+ * #define PANIC_DATA_PTR ((struct panic_data *)\
+ * (CONFIG_RAM_BASE + CONFIG_RAM_SIZE - sizeof(struct panic_data)))
+ *
+ * LFW stack located by ec_lfw.ld linker file 256 bytes below top of
+ * data SRAM.
+ * PROVIDE( lfw_stack_top = 0x11F000 );
+ *
+ * !!!WARNING!!!
+ * Current MEC BootROM's zeros all memory therefore any chip reset
+ * will destroy panic data.
+ */
+
+/*
+ * Configure 32-bit basic timer 0 for 1MHz, auto-reload and
+ * no interrupt.
+ */
+void timer_init(void)
+{
+ uint32_t val = 0;
+
+ /* Ensure timer is not running */
+ MCHP_TMR32_CTL(0) &= ~(1 << 5);
+
+ /* Enable timer */
+ MCHP_TMR32_CTL(0) |= (1 << 0);
+
+ val = MCHP_TMR32_CTL(0);
+
+ /* Pre-scale = 48 -> 1MHz -> Period = 1us */
+ val = (val & 0xffff) | (47 << 16);
+
+ MCHP_TMR32_CTL(0) = val;
+
+ /* Set preload to use the full 32 bits of the timer */
+ MCHP_TMR32_PRE(0) = 0xffffffff;
+
+ /* Override the count */
+ MCHP_TMR32_CNT(0) = 0xffffffff;
+
+ /* Auto restart */
+ MCHP_TMR32_CTL(0) |= (1 << 3);
+
+ /* Start counting in timer 0 */
+ MCHP_TMR32_CTL(0) |= (1 << 5);
+
+}
+
+/*
+ * Use copy of SPI flash read compiled for LFW (no semaphores).
+ * LFW timeout code does not use interrupts so reset timer
+ * before starting SPI read to minimize probability of
+ * timer wrap.
+ */
+static int spi_flash_readloc(uint8_t *buf_usr,
+ unsigned int offset,
+ unsigned int bytes)
+{
+ uint8_t cmd[4] = {SPI_FLASH_READ,
+ (offset >> 16) & 0xFF,
+ (offset >> 8) & 0xFF,
+ offset & 0xFF};
+
+ if (offset + bytes > CONFIG_FLASH_SIZE)
+ return EC_ERROR_INVAL;
+
+ __hw_clock_source_set(0); /* restart free run timer */
+ return spi_transaction(SPI_FLASH_DEVICE, cmd, 4, buf_usr, bytes);
+}
+
+/*
+ * Load EC_RO/RW image from local SPI flash.
+ * If CONFIG_MEC_TEST_EC_RORW_CRC was define the last 4 bytes
+ * of the binary is IEEE 802.3 CRC32 of the previous bytes.
+ * Use DMA channel 0 CRC32 HW to check data integrity.
+ */
+int spi_image_load(uint32_t offset)
+{
+ uint8_t *buf = (uint8_t *) (CONFIG_RW_MEM_OFF +
+ CONFIG_PROGRAM_MEMORY_BASE);
+ uint32_t i;
+#ifdef CONFIG_MCHP_LFW_DEBUG
+ uint32_t crc_calc, crc_exp;
+ int rc;
+#endif
+
+ BUILD_ASSERT(CONFIG_RO_SIZE == CONFIG_RW_SIZE);
+
+ /* Why fill all but last 4-bytes? */
+ memset((void *)buf, 0xFF, (CONFIG_RO_SIZE - 4));
+
+ for (i = 0; i < CONFIG_RO_SIZE; i += SPI_CHUNK_SIZE)
+#ifdef CONFIG_MCHP_LFW_DEBUG
+ rc = spi_flash_readloc(&buf[i], offset + i, SPI_CHUNK_SIZE);
+ if (rc != EC_SUCCESS) {
+ trace2(0, LFW, 0,
+ "spi_flash_readloc block %d ret = %d",
+ i, rc);
+ while (MCHP_PCR_PROC_CLK_CTL)
+ MCHP_PCR_CHIP_OSC_ID &= 0x1FE;
+ }
+#else
+ spi_flash_readloc(&buf[i], offset + i, SPI_CHUNK_SIZE);
+#endif
+
+#ifdef CONFIG_MCHP_LFW_DEBUG
+ dma_crc32_start(buf, (CONFIG_RO_SIZE - 4), 0);
+ do {
+ MCHP_USEC_DELAY(31); /* delay(stall) CPU by 32 us */
+ i = dma_is_done_chan(0);
+ } while (i == 0);
+ crc_calc = MCHP_DMA_CH0_CRC32_DATA;
+ crc_exp = *((uint32_t *)&buf[CONFIG_RO_SIZE - 4]);
+ trace12(0, LFW, 0, "EC image CRC32 = 0x%08x expected = 0x%08x",
+ crc_calc, crc_exp);
+#endif
+
+ return 0;
+}
+
+void udelay(unsigned int us)
+{
+ uint32_t t0 = __hw_clock_source_read();
+
+ while (__hw_clock_source_read() - t0 < us)
+ ;
+}
+
+void usleep(unsigned int us)
+{
+ udelay(us);
+}
+
+int timestamp_expired(timestamp_t deadline, const timestamp_t *now)
+{
+ timestamp_t now_val;
+
+ if (!now) {
+ now_val = get_time();
+ now = &now_val;
+ }
+
+ return ((int64_t)(now->val - deadline.val) >= 0);
+}
+
+/*
+ * LFW does not use interrupts so no ISR will fire to
+ * increment high 32-bits of timestap_t. Force high
+ * word to zero. NOTE: There is a risk of false timeout
+ * errors due to timer wrap. We will reset timer before
+ * each SPI transaction.
+ */
+timestamp_t get_time(void)
+{
+ timestamp_t ts;
+
+ ts.le.hi = 0; /* clksrc_high; */
+ ts.le.lo = __hw_clock_source_read();
+ return ts;
+}
+
+void uart_write_c(char c)
+{
+ /* Put in carriage return prior to newline to mimic uart_vprintf() */
+ if (c == '\n')
+ uart_write_c('\r');
+
+ /* Wait for space in transmit FIFO. */
+ while (!(MCHP_UART_LSR(0) & (1 << 5)))
+ ;
+ MCHP_UART_TB(0) = c;
+}
+
+void uart_puts(const char *str)
+{
+ if (!str || !*str)
+ return;
+
+ do {
+ uart_write_c(*str++);
+ } while (*str);
+}
+
+void fault_handler(void)
+{
+ uart_puts("EXCEPTION!\nTriggering watchdog reset\n");
+ /* trigger reset in 1 ms */
+ usleep(1000);
+ MCHP_PCR_SYS_RST = MCHP_PCR_SYS_SOFT_RESET;
+ while (1)
+ ;
+
+}
+
+void jump_to_image(uintptr_t init_addr)
+{
+ void (*resetvec)(void) = (void(*)(void))init_addr;
+
+ resetvec();
+}
+
+void uart_init(void)
+{
+ /* Set UART to reset on VCC1_RESET instaed of nSIO_RESET */
+ MCHP_UART_CFG(0) &= ~(1 << 1);
+
+ /* Baud rate = 115200. 1.8432MHz clock. Divisor = 1 */
+
+ /* Set CLK_SRC = 0 */
+ MCHP_UART_CFG(0) &= ~(1 << 0);
+
+ /* Set DLAB = 1 */
+ MCHP_UART_LCR(0) |= (1 << 7);
+
+ /* PBRG0/PBRG1 */
+ MCHP_UART_PBRG0(0) = 1;
+ MCHP_UART_PBRG1(0) = 0;
+
+ /* Set DLAB = 0 */
+ MCHP_UART_LCR(0) &= ~(1 << 7);
+
+ /* Set word length to 8-bit */
+ MCHP_UART_LCR(0) |= (1 << 0) | (1 << 1);
+
+ /* Enable FIFO */
+ MCHP_UART_FCR(0) = (1 << 0);
+
+ /* Activate UART */
+ MCHP_UART_ACT(0) |= (1 << 0);
+
+ gpio_config_module(MODULE_UART, 1);
+}
+
+/*
+ * If any of VTR POR, VBAT POR, chip resets, or WDT reset are active
+ * force VBAT image type to none causing load of EC_RO.
+ */
+void system_init(void)
+{
+ uint32_t wdt_sts = MCHP_VBAT_STS & MCHP_VBAT_STS_ANY_RST;
+ uint32_t rst_sts = MCHP_PCR_PWR_RST_STS &
+ MCHP_PWR_RST_STS_VTR;
+
+ trace12(0, LFW, 0,
+ "VBAT_STS = 0x%08x PCR_PWR_RST_STS = 0x%08x",
+ wdt_sts, rst_sts);
+
+ if (rst_sts || wdt_sts)
+ MCHP_VBAT_RAM(MCHP_IMAGETYPE_IDX)
+ = SYSTEM_IMAGE_UNKNOWN;
+}
+
+enum system_image_copy_t system_get_image_copy(void)
+{
+ return MCHP_VBAT_RAM(MCHP_IMAGETYPE_IDX);
+}
+
+
+/*
+ * lfw_main is entered by MEC BootROM or EC_RO/RW calling it directly.
+ * NOTE: Based on LFW from MEC1322
+ * Upon chip reset, BootROM loads image = LFW+EC_RO and enters LFW.
+ * LFW checks reset type:
+ * VTR POR, chip reset, WDT reset then set VBAT Load type to Unknown.
+ * LFW reads VBAT Load type:
+ * SYSTEM_IMAGE_RO then read EC_RO from SPI flash and jump into it.
+ * SYSTEM_IMAGE_RO then read EC_RW from SPI flash and jump into it.
+ * Other then jump into EC image loaded by Boot-ROM.
+ */
+void lfw_main(void)
+{
+
+ uintptr_t init_addr;
+
+ /* install vector table */
+ *((uintptr_t *) 0xe000ed08) = (uintptr_t) &hdr_int_vect;
+
+ /* Use 48 MHz processor clock to power through boot */
+ MCHP_PCR_PROC_CLK_CTL = 1;
+
+#ifdef CONFIG_WATCHDOG
+ /* Reload watchdog which may be running in case of sysjump */
+ MCHP_WDG_KICK = 1;
+#ifdef CONFIG_WATCHDOG_HELP
+ /* Stop aux timer */
+ MCHP_TMR16_CTL(0) &= ~1;
+#endif
+#endif
+ /*
+ * TFDP functions will compile to nothing if CONFIG_MEC1701_TFDP
+ * is not defined.
+ */
+ tfdp_power(1);
+ tfdp_enable(1, 1);
+ trace0(0, LFW, 0, "LFW first trace");
+
+ timer_init();
+ clock_init();
+ cpu_init();
+ dma_init();
+ uart_init();
+ system_init();
+
+ spi_enable(CONFIG_SPI_FLASH_PORT, 1);
+
+ uart_puts("littlefw ");
+ uart_puts(current_image_data.version);
+ uart_puts("\n");
+
+ switch (system_get_image_copy()) {
+ case SYSTEM_IMAGE_RW:
+ trace0(0, LFW, 0, "LFW EC_RW Load");
+ uart_puts("lfw-RW load\n");
+
+ init_addr = CONFIG_RW_MEM_OFF + CONFIG_PROGRAM_MEMORY_BASE;
+ spi_image_load(CONFIG_EC_WRITABLE_STORAGE_OFF +
+ CONFIG_RW_STORAGE_OFF);
+ break;
+ case SYSTEM_IMAGE_RO:
+ trace0(0, LFW, 0, "LFW EC_RO Load");
+ uart_puts("lfw-RO load\n");
+
+ init_addr = CONFIG_RO_MEM_OFF + CONFIG_PROGRAM_MEMORY_BASE;
+ spi_image_load(CONFIG_EC_PROTECTED_STORAGE_OFF +
+ CONFIG_RO_STORAGE_OFF);
+ break;
+ default:
+ trace0(0, LFW, 0, "LFW default: use EC_RO loaded by BootROM");
+ uart_puts("lfw-default case\n");
+
+ MCHP_VBAT_RAM(MCHP_IMAGETYPE_IDX) =
+ SYSTEM_IMAGE_RO;
+
+ init_addr = CONFIG_RO_MEM_OFF + CONFIG_PROGRAM_MEMORY_BASE;
+ }
+
+ trace11(0, LFW, 0, "Get EC reset handler from 0x%08x", (init_addr + 4));
+ trace11(0, LFW, 0, "Jump to EC @ 0x%08x",
+ *((uint32_t *)(init_addr + 4)));
+ jump_to_image(*(uintptr_t *)(init_addr + 4));
+
+ /* should never get here */
+ while (1)
+ ;
+}
diff --git a/chip/mchp/lfw/ec_lfw.h b/chip/mchp/lfw/ec_lfw.h
new file mode 100644
index 0000000000..8d9da760a7
--- /dev/null
+++ b/chip/mchp/lfw/ec_lfw.h
@@ -0,0 +1,38 @@
+/* Copyright 2017 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.
+ *
+ * MCHP MEC SoC little FW
+ *
+ */
+
+/* Why naked? This is dangerous except for
+ * function/ISR wrappers using inline assembly.
+ * lfw_main() makes many calls and has one local variable.
+ * Naked C functions should not use local data unless the local
+ * data can fit in CPU registers.
+ * Note other C functions called by lfw_main() are not marked naked and
+ * do include compiler generated prolog and epilog code.
+ * We also do not know how much stack space is available when
+ * EC_RO calls lfw_main().
+ *
+void lfw_main(void) __attribute__ ((noreturn, naked));
+*/
+void lfw_main(void) __attribute__ ((noreturn));
+void fault_handler(void) __attribute__((naked));
+
+/*
+ * Defined in linker file ec_lfw.ld
+ */
+extern uint32_t lfw_stack_top[];
+
+struct int_vector_t {
+ void *stack_ptr;
+ void *reset_vector;
+ void *nmi;
+ void *hard_fault;
+ void *bus_fault;
+ void *usage_fault;
+};
+
+#define SPI_CHUNK_SIZE 1024
diff --git a/chip/mchp/lfw/ec_lfw.ld b/chip/mchp/lfw/ec_lfw.ld
new file mode 100644
index 0000000000..315ad1bcfd
--- /dev/null
+++ b/chip/mchp/lfw/ec_lfw.ld
@@ -0,0 +1,78 @@
+/* Copyright 2017 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.
+ *
+ * MCHP MEC parts with 256KB SRAM SoC little FW
+ *
+ */
+
+/*
+ * Memory Spaces Definitions
+ * LFW occupies first 4KB of CODE SRAM.
+ * First 24 bytes contain a minimal Cortex-M4
+ * vector table.
+ */
+MEMORY
+{
+ VECTOR(r ) : ORIGIN = 0x0E0000, LENGTH = 24
+ SRAM (xrw) : ORIGIN = 0x0E0018, LENGTH = 0x1000 - LENGTH(VECTOR)
+}
+
+/*
+ * The entry point is informative, for debuggers and simulators,
+ * since the Cortex-M vector points to it anyway.
+ */
+ENTRY(lfw_main)
+
+/*
+ * MEC 256KB SRAM 0xE0000 - 0x11FFFF
+ * Data Top 32KB at 0x118000 - 0x11FFFF
+ * Boot-ROM log is 0x11FF00 - 0x11FFFF
+ * Set top of LFW stack 1KB below top of SRAM
+ * because EC panic and jump data live at
+ * top of SRAM.
+ * !!!WARNING!!!
+ * POR or any chip reset will cause MEC BootROM
+ * to run. BootROM will clear all CODE & DATA SRAM.
+ * Panic data will be lost.
+ *
+ */
+PROVIDE( lfw_stack_top = 0x11F000 );
+
+/* Sections Definitions */
+
+SECTIONS
+{
+
+ /*
+ * The vector table goes first
+ */
+ .intvector :
+ {
+ . = ALIGN(4);
+ KEEP(*(.intvector))
+ } > VECTOR
+
+ /*
+ * The program code is stored in the .text section,
+ * which goes to FLASH.
+ */
+
+ .text :
+ {
+ *(.text .text.*) /* all remaining code */
+ *(.rodata .rodata.*) /* read-only data (constants) */
+ } >SRAM
+
+ . = ALIGN(4);
+
+ /* Padding */
+
+ .fill : {
+ FILL(0xFF);
+ . = ORIGIN(SRAM) + LENGTH(SRAM) - 1;
+ BYTE(0xFF); /* emit at least a byte to make linker happy */
+ }
+
+ __image_size = LOADADDR(.text) + SIZEOF(.text) - ORIGIN(VECTOR);
+}