From 2f1f5a555105cdff9d6b0a3278bf13e617143c9c Mon Sep 17 00:00:00 2001 From: Scott Worley Date: Thu, 21 Dec 2017 15:06:32 -0500 Subject: ec_chip_mchp: Add LFW files Add Microchip MEC17xx family little-firmware (LFW) folder and files. Change-Id: I9142266d41234574730fadccd5a2cc27fe3d8fd7 Signed-off-by: Scott Worley --- chip/mchp/lfw/ec_lfw.c | 425 ++++++++++++++++++++++++++++++++++++++++++++++++ chip/mchp/lfw/ec_lfw.h | 38 +++++ chip/mchp/lfw/ec_lfw.ld | 78 +++++++++ 3 files changed, 541 insertions(+) create mode 100644 chip/mchp/lfw/ec_lfw.c create mode 100644 chip/mchp/lfw/ec_lfw.h create mode 100644 chip/mchp/lfw/ec_lfw.ld 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 + +#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); +} -- cgit v1.2.1