summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2012-05-01 17:32:17 -0700
committerVincent Palatin <vpalatin@chromium.org>2012-05-09 01:13:26 +0000
commit71c86b38c3862da28368acf0d44779eb1fcc088b (patch)
tree8e7b6448217c7db7d32d5d05d6aca28e9dd95803
parent87b427829993e7c5b2fb66fc1e134ace43d28cf9 (diff)
downloadchrome-ec-71c86b38c3862da28368acf0d44779eb1fcc088b.tar.gz
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 <vpalatin@chromium.org> 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
-rw-r--r--board/daisy/board.h3
-rw-r--r--chip/stm32/build.mk1
-rw-r--r--chip/stm32/flash-stm32l15x.c281
-rw-r--r--chip/stm32/registers.h17
-rw-r--r--common/flash_common.c5
-rw-r--r--core/cortex-m/ec.lds.S2
6 files changed, 305 insertions, 4 deletions
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)