From 71c86b38c3862da28368acf0d44779eb1fcc088b Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Tue, 1 May 2012 17:32:17 -0700 Subject: stm32: add flash programming support Implements the on-chip flash erasing and writing functions. The actual writing is done from a routine in internal RAM (using the special .iram.text section) with interrupt disabled as we cannot read flash during the writing process. The write-protect feature is only lightly tested. Signed-off-by: Vincent Palatin BUG=chrome-os-partner:8865 TEST=run on Daisy, from the EC console, use flasherase and flashwrite commands and observe the results using rw command. Change-Id: I4c64cf28b23df52b18500b42a32a7d3668d45ba6 --- board/daisy/board.h | 3 + chip/stm32/build.mk | 1 + chip/stm32/flash-stm32l15x.c | 281 +++++++++++++++++++++++++++++++++++++++++++ chip/stm32/registers.h | 17 ++- common/flash_common.c | 5 + core/cortex-m/ec.lds.S | 2 + 6 files changed, 305 insertions(+), 4 deletions(-) create mode 100644 chip/stm32/flash-stm32l15x.c diff --git a/board/daisy/board.h b/board/daisy/board.h index ceefa5e38e..c29701382c 100644 --- a/board/daisy/board.h +++ b/board/daisy/board.h @@ -14,6 +14,9 @@ /* Use USART1 as console serial port */ #define CONFIG_CONSOLE_UART 1 +/* support programming on-chip flash */ +#define CONFIG_FLASH + #define USB_CHARGE_PORT_COUNT 0 /* GPIO signal list */ diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index f499f45079..e7be41db77 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -14,3 +14,4 @@ chip-$(CONFIG_TASK_SPI_WORK)+=spi.o chip-$(CONFIG_TASK_I2C2_WORK)+=i2c.o chip-$(CONFIG_TASK_WATCHDOG)+=watchdog.o chip-$(CONFIG_TASK_KEYSCAN)+=keyboard_scan.o +chip-$(CONFIG_FLASH)+=flash-$(CHIP_VARIANT).o diff --git a/chip/stm32/flash-stm32l15x.c b/chip/stm32/flash-stm32l15x.c new file mode 100644 index 0000000000..550372c986 --- /dev/null +++ b/chip/stm32/flash-stm32l15x.c @@ -0,0 +1,281 @@ +/* Copyright (c) 2012 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. + */ + +/* Flash memory module for Chrome EC */ + +#include "console.h" +#include "flash.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "util.h" +#include "watchdog.h" + +#define FLASH_WRITE_BYTES 128 +#define FLASH_ERASE_BYTES 256 +#define FLASH_PROTECT_BYTES 4096 + +#define US_PER_SECOND 1000000 + +/* the approximate number of CPU cycles per iteration of the loop when polling + * the flash status + */ +#define CYCLE_PER_FLASH_LOOP 10 + +/* Flash page programming timeout. This is 2x the datasheet max. */ +#define FLASH_TIMEOUT_US 16000 +#define FLASH_TIMEOUT_LOOP \ + (FLASH_TIMEOUT_US * (CPU_CLOCK / US_PER_SECOND) / CYCLE_PER_FLASH_LOOP) + +/* Flash unlocking keys */ +#define PEKEY1 0x89ABCDEF +#define PEKEY2 0x02030405 +#define PRGKEY1 0x8C9DAEBF +#define PRGKEY2 0x13141516 +#define OPTKEY1 0xFBEAD9C8 +#define OPTKEY2 0x24252627 + +/* Lock bits*/ +#define PE_LOCK (1<<0) +#define PRG_LOCK (1<<1) +#define OPT_LOCK (1<<2) + +int flash_get_write_block_size(void) +{ + return FLASH_WRITE_BYTES; +} + + +int flash_get_erase_block_size(void) +{ + return FLASH_ERASE_BYTES; +} + + +int flash_get_protect_block_size(void) +{ + BUILD_ASSERT(FLASH_PROTECT_BYTES == CONFIG_FLASH_BANK_SIZE); + return FLASH_PROTECT_BYTES; +} + + +int flash_physical_size(void) +{ + return CONFIG_FLASH_SIZE; +} + + +int flash_physical_read(int offset, int size, char *data) +{ + /* Just read the flash from its memory window. */ + /* TODO: (crosbug.com/p/7473) is this affected by data cache? */ + memcpy(data, (char *)offset, size); + return EC_SUCCESS; +} + +static int unlock(int locks) +{ + /* unlock PECR if needed */ + if (STM32_FLASH_PECR & PE_LOCK) { + STM32_FLASH_PEKEYR = PEKEY1; + STM32_FLASH_PEKEYR = PEKEY2; + } + /* unlock program memory if required */ + if ((locks & PRG_LOCK) && (STM32_FLASH_PECR & PRG_LOCK)) { + STM32_FLASH_PRGKEYR = PRGKEY1; + STM32_FLASH_PRGKEYR = PRGKEY2; + } + /* unlock option memory if required */ + if ((locks & OPT_LOCK) && (STM32_FLASH_PECR & 4)) { + STM32_FLASH_OPTKEYR = OPTKEY1; + STM32_FLASH_OPTKEYR = OPTKEY2; + } + + return (STM32_FLASH_PECR & (locks | PE_LOCK)) ? + EC_ERROR_UNKNOWN : EC_SUCCESS; +} + +static void lock(void) +{ + STM32_FLASH_PECR = 0x7; +} + +static uint8_t read_optb(int byte) +{ + return *(uint8_t *)(STM32_OPTB_BASE + byte); + +} + +static void write_optb(int byte, uint8_t value) +{ + volatile int32_t *word = (uint32_t *)(STM32_OPTB_BASE + (byte & ~0x3)); + uint32_t val = *word; + int shift = (byte & 0x3) * 8; + + if (unlock(OPT_LOCK) != EC_SUCCESS) + return; + + val &= ~((0xff << shift) | (0xff << (shift + STM32_OPTB_COMPL_SHIFT))); + val |= (value << shift) | (~value << (shift + STM32_OPTB_COMPL_SHIFT)); + *word = val; + + /* TODO reboot by writing OBL_LAUNCH bit ? */ + + lock(); +} + +/** + * This function lives in internal RAM, + * as we cannot read flash during writing. + * You should neither call other function from this one, + * nor declare it static. + */ +void __attribute__((section(".iram.text"))) + iram_flash_write(uint32_t *addr, uint32_t *data) +{ + int i; + + interrupt_disable(); + + /* wait to be ready */ + for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP) ; + i++) + ; + + /* set PROG and FPRG bits */ + STM32_FLASH_PECR |= (1<<3) | (1<<10); + + /* send words for the half page */ + for (i = 0; i < FLASH_WRITE_BYTES / sizeof(uint32_t); i++) + *addr++ = *data++; + + /* Wait for writes to complete */ + for (i = 0; ((STM32_FLASH_SR & 9) != 8) && (i < FLASH_TIMEOUT_LOOP) ; + i++) + ; + + /* Disable PROG and FPRG bits */ + STM32_FLASH_PECR &= ~((1<<3) | (1<<10)); + + interrupt_enable(); +} + +int flash_physical_write(int offset, int size, const char *data) +{ + /* this is pretty nasty, we need to enforce alignment instead of this + * wild cast : TODO crosbug.com/p/9526 + */ + uint32_t *data32 = (uint32_t *)data; + uint32_t *address; + int res = EC_SUCCESS; + + if (unlock(PRG_LOCK) != EC_SUCCESS) { + res = EC_ERROR_UNKNOWN; + goto exit_wr; + } + + /* Clear previous error status */ + STM32_FLASH_SR = 0xf00; + + for (address = (uint32_t *)(CONFIG_FLASH_BASE + offset) ; + size > 0; size -= FLASH_WRITE_BYTES) { +#ifdef CONFIG_TASK_WATCHDOG + /* Reload the watchdog timer to avoid watchdog reset when doing + * long writing with interrupt disabled. + */ + watchdog_reload(); +#endif + iram_flash_write(address, data32); + + address += FLASH_WRITE_BYTES / sizeof(uint32_t); + data32 += FLASH_WRITE_BYTES / sizeof(uint32_t); + if (STM32_FLASH_SR & 1) { + res = EC_ERROR_TIMEOUT; + goto exit_wr; + } + + /* Check for error conditions - erase failed, voltage error, + * protection error */ + if (STM32_FLASH_SR & 0xF00) { + res = EC_ERROR_UNKNOWN; + goto exit_wr; + } + } + +exit_wr: + lock(); + + return res; +} + + +int flash_physical_erase(int offset, int size) +{ + uint32_t *address; + int res = EC_SUCCESS; + + if (unlock(PRG_LOCK) != EC_SUCCESS) + return EC_ERROR_UNKNOWN; + + /* Clear previous error status */ + STM32_FLASH_SR = 0xf00; + + /* set PROG and ERASE bits */ + STM32_FLASH_PECR |= (1<<3) | (1<<9); + + for (address = (uint32_t *)(CONFIG_FLASH_BASE + offset) ; + size > 0; size -= FLASH_ERASE_BYTES, + address += FLASH_ERASE_BYTES/sizeof(uint32_t)) { + timestamp_t deadline; + + /* Start erase */ + *address = 0x00000000; + +#ifdef CONFIG_TASK_WATCHDOG + /* Reload the watchdog timer in case the erase takes long time + * so that erasing many flash pages + */ + watchdog_reload(); +#endif + + deadline.val = get_time().val + FLASH_TIMEOUT_US; + /* Wait for erase to complete */ + while ((STM32_FLASH_SR & 1) && + (get_time().val < deadline.val)) { + usleep(300); + } + if (STM32_FLASH_SR & 1) { + res = EC_ERROR_TIMEOUT; + goto exit_er; + } + + /* Check for error conditions - erase failed, voltage error, + * protection error */ + if (STM32_FLASH_SR & 0xF00) { + res = EC_ERROR_UNKNOWN; + goto exit_er; + } + } + +exit_er: + lock(); + + return res; +} + + +int flash_physical_get_protect(int block) +{ + uint8_t val = read_optb(STM32_OPTB_WRP_OFF(block/8)); + return val & (1 << (block % 8)); +} + + +void flash_physical_set_protect(int block) +{ + int byte_off = STM32_OPTB_WRP_OFF(block/8); + uint8_t val = read_optb(byte_off) | (1 << (block % 8)); + write_optb(byte_off, val); +} diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h index f169dab72e..d06538c7e2 100644 --- a/chip/stm32/registers.h +++ b/chip/stm32/registers.h @@ -440,10 +440,12 @@ static inline uint16_t *stm32_i2c_reg(int port, int offset) #define STM32_OPTB_BASE 0x1FF80000 -#define STM32_OPTB_RDP REG32(STM32_OPTB_BASE + 0x00) -#define STM32_OPTB_USER REG32(STM32_OPTB_BASE + 0x04) -#define STM32_OPTB_WRP01 REG32(STM32_OPTB_BASE + 0x08) -#define STM32_OPTB_WRP23 REG32(STM32_OPTB_BASE + 0x0c) +#define STM32_OPTB_RDP_OFF 0x00 +#define STM32_OPTB_USER_OFF 0x04 +#define STM32_OPTB_WRP_OFF(n) (0x08 + (n&1) + (n&2) * 2) +#define STM32_OPTB_WRP23 0x0c + +#define STM32_OPTB_COMPL_SHIFT 16 #elif defined(CHIP_VARIANT_stm32f100) #define STM32_FLASH_REGS_BASE 0x40022000 @@ -459,6 +461,13 @@ static inline uint16_t *stm32_i2c_reg(int port, int offset) #define STM32_OPTB_BASE 0x1FFFF800 +#define STM32_OPTB_RDP_OFF 0x00 +#define STM32_OPTB_USER_OFF 0x02 +#define STM32_OPTB_WRP_OFF(n) (0x08 + (n&3) * 2) +#define STM32_OPTB_WRP23 0x0c + +#define STM32_OPTB_COMPL_SHIFT 8 + #else #error Unsupported chip variant #endif diff --git a/common/flash_common.c b/common/flash_common.c index 36caa5e96a..466742a66f 100644 --- a/common/flash_common.c +++ b/common/flash_common.c @@ -29,7 +29,12 @@ static struct persist_state pstate; /* RAM copy of pstate data */ /* Return non-zero if the write protect pin is asserted */ static int wp_pin_asserted(void) { +#ifdef CHIP_stm32 + /* TODO (vpalatin) : write protect scheme for stm32 */ + return 0; /* always disable write protect */ +#else return gpio_get_level(GPIO_WRITE_PROTECT); +#endif } diff --git a/core/cortex-m/ec.lds.S b/core/cortex-m/ec.lds.S index ba0b2733b5..ea89e241e9 100644 --- a/core/cortex-m/ec.lds.S +++ b/core/cortex-m/ec.lds.S @@ -76,6 +76,8 @@ SECTIONS *(.data.tasks) *(.data) . = ALIGN(4); + *(.iram.text) + . = ALIGN(4); __data_end = .; } > IRAM #if defined(SECTION_IS_RO) && defined(CONFIG_VBOOT) -- cgit v1.2.1