summaryrefslogtreecommitdiff
path: root/chip/stm32/flash-stm32f4.c
diff options
context:
space:
mode:
authorNick Sanders <nsanders@chromium.org>2016-07-26 13:17:09 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-08-17 16:19:07 -0700
commit6fcd163da5169bfca36ab8c15cfd9d0624acae19 (patch)
treecc1e3cd999fa3df95547356e8160fd966aa26bc3 /chip/stm32/flash-stm32f4.c
parent6fad4f8588242cd6202e1177e145073c6aff6b7a (diff)
downloadchrome-ec-6fcd163da5169bfca36ab8c15cfd9d0624acae19.tar.gz
stm32f446e-eval: add support for stm32f446
This adds basic support for the stm32f446. This consists of: * New DMA model for stm32f4 * New clock domain support. * MCO oscillator gpio export support. * Flash support for irregular blocks. BUG=chromium:608039 TEST=boots w/ correct clock, stm32f0 also boots. BRANCH=None Change-Id: I1c5cf6ddca09009c9dac60da8a3d0c5ceedfcf4d Signed-off-by: Nick Sanders <nsanders@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/363992 Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Diffstat (limited to 'chip/stm32/flash-stm32f4.c')
-rw-r--r--chip/stm32/flash-stm32f4.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/chip/stm32/flash-stm32f4.c b/chip/stm32/flash-stm32f4.c
new file mode 100644
index 0000000000..48815d6956
--- /dev/null
+++ b/chip/stm32/flash-stm32f4.c
@@ -0,0 +1,384 @@
+/* Copyright 2016 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 stm32f4 */
+
+#include "clock.h"
+#include "compile_time_macros.h"
+#include "console.h"
+#include "common.h"
+#include "flash.h"
+#include "hooks.h"
+#include "registers.h"
+#include "system.h"
+#include "panic.h"
+#include "watchdog.h"
+
+
+#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args)
+
+/*
+ * 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
+
+static inline int calculate_flash_timeout(void)
+{
+ return (FLASH_TIMEOUT_US *
+ (clock_get_freq() / SECOND) / CYCLE_PER_FLASH_LOOP);
+}
+
+
+/* Flag indicating whether we have locked down entire flash */
+static int entire_flash_locked;
+
+#define FLASH_SYSJUMP_TAG 0x5750 /* "WP" - Write Protect */
+#define FLASH_HOOK_VERSION 1
+/* The previous write protect state before sys jump */
+struct flash_wp_state {
+ int entire_flash_locked;
+};
+
+/*****************************************************************************/
+/* Physical layer APIs */
+
+/* Flash unlocking keys */
+#define PRG_LOCK 0
+#define KEY1 0x45670123
+#define KEY2 0xCDEF89AB
+
+static int unlock(void)
+{
+ /*
+ * We may have already locked the flash module and get a bus fault
+ * in the attempt to unlock. Need to disable bus fault handler now.
+ */
+ ignore_bus_fault(1);
+
+ /* unlock CR if needed */
+ if (STM32_FLASH_CR & FLASH_CR_LOCK) {
+ STM32_FLASH_KEYR = KEY1;
+ STM32_FLASH_KEYR = KEY2;
+ }
+
+ /* Re-enable bus fault handler */
+ ignore_bus_fault(0);
+
+ return (STM32_FLASH_CR & FLASH_CR_LOCK) ?
+ EC_ERROR_UNKNOWN : EC_SUCCESS;
+}
+
+static void lock(void)
+{
+ STM32_FLASH_CR = FLASH_CR_LOCK;
+}
+
+
+int flash_physical_get_protect(int block)
+{
+ /* TODO: not sure if write protect can be implemented like this. */
+ return 0;
+}
+
+uint32_t flash_physical_get_protect_flags(void)
+{
+ return entire_flash_locked ? EC_FLASH_PROTECT_ALL_NOW : 0;
+}
+
+int flash_physical_protect_now(int all)
+{
+ if (all) {
+ /*
+ * Lock by writing a wrong key to FLASH_KEYR. This triggers a
+ * bus fault, so we need to disable bus fault handler while
+ * doing this.
+ *
+ * This incorrect key fault causes the flash to become
+ * permanenlty locked until reset, a correct keyring write
+ * will not unlock it. In this way we can implement system
+ * write protect.
+ */
+ ignore_bus_fault(1);
+ STM32_FLASH_KEYR = 0xffffffff;
+ ignore_bus_fault(0);
+
+ entire_flash_locked = 1;
+
+ /* Check if lock happened */
+ if (STM32_FLASH_CR & FLASH_CR_LOCK)
+ return EC_SUCCESS;
+ }
+
+ /* No way to protect just the RO flash until next boot */
+ return EC_ERROR_INVAL;
+}
+
+uint32_t flash_physical_get_valid_flags(void)
+{
+ return EC_FLASH_PROTECT_RO_AT_BOOT |
+ EC_FLASH_PROTECT_RO_NOW |
+ EC_FLASH_PROTECT_ALL_NOW;
+}
+
+uint32_t flash_physical_get_writable_flags(uint32_t cur_flags)
+{
+ uint32_t ret = 0;
+
+ /* If RO protection isn't enabled, its at-boot state can be changed. */
+ if (!(cur_flags & EC_FLASH_PROTECT_RO_NOW))
+ ret |= EC_FLASH_PROTECT_RO_AT_BOOT;
+
+ /*
+ * If entire flash isn't protected at this boot, it can be enabled if
+ * the WP GPIO is asserted.
+ */
+ if (!(cur_flags & EC_FLASH_PROTECT_ALL_NOW) &&
+ (cur_flags & EC_FLASH_PROTECT_GPIO_ASSERTED))
+ ret |= EC_FLASH_PROTECT_ALL_NOW;
+
+ return ret;
+}
+
+int flash_physical_restore_state(void)
+{
+ uint32_t reset_flags = system_get_reset_flags();
+ int version, size;
+ const struct flash_wp_state *prev;
+
+ /*
+ * If we have already jumped between images, an earlier image could
+ * have applied write protection. Nothing additional needs to be done.
+ */
+ if (reset_flags & RESET_FLAG_SYSJUMP) {
+ prev = (const struct flash_wp_state *)system_get_jump_tag(
+ FLASH_SYSJUMP_TAG, &version, &size);
+ if (prev && version == FLASH_HOOK_VERSION &&
+ size == sizeof(*prev))
+ entire_flash_locked = prev->entire_flash_locked;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int flash_idle(void)
+{
+ timestamp_t deadline;
+
+ deadline.val = get_time().val + FLASH_TIMEOUT_US;
+ /* Wait for flash op to complete.
+ * This function is used for both reads and writes, so
+ * we need a long timeout, but a relatively short poll interval.
+ */
+ while ((STM32_FLASH_SR & FLASH_SR_BUSY) &&
+ (get_time().val < deadline.val)) {
+ usleep(1);
+ }
+
+ if (STM32_FLASH_SR & FLASH_SR_BUSY)
+ return EC_ERROR_TIMEOUT;
+
+ return EC_SUCCESS;
+}
+
+static void clear_flash_errors(void)
+{
+ /* Clear previous error status */
+ STM32_FLASH_SR = FLASH_SR_ERR_MASK;
+}
+
+/*****************************************************************************/
+/* Physical layer APIs */
+
+int flash_physical_protect_at_boot(enum flash_wp_range range)
+{
+ return EC_SUCCESS;
+}
+
+int flash_physical_write(int offset, int size, const char *data)
+{
+ uint32_t *address = (uint32_t *)(CONFIG_MAPPED_STORAGE_BASE + offset);
+ int res = EC_SUCCESS;
+
+ if (unlock() != EC_SUCCESS) {
+ res = EC_ERROR_UNKNOWN;
+ goto exit_wr;
+ }
+
+ /* Wait for busy to clear */
+ res = flash_idle();
+ if (res)
+ goto exit_wr;
+ clear_flash_errors();
+
+ /* set PG bit */
+ STM32_FLASH_CR &= ~FLASH_CR_PSIZE_MASK;
+ STM32_FLASH_CR |= FLASH_CR_PSIZE(FLASH_CR_PSIZE_32);
+ STM32_FLASH_CR |= FLASH_CR_PG;
+
+ for (; size > 0; size -= sizeof(uint32_t)) {
+ /*
+ * Reload the watchdog timer to avoid watchdog reset when doing
+ * long writing with interrupt disabled.
+ */
+ watchdog_reload();
+
+ res = flash_idle();
+ if (res)
+ goto exit_wr;
+
+ /* write the word */
+ *address = data[0] + (data[1] << 8) +
+ (data[2] << 16) + (data[3] << 24);
+
+ address++;
+ data += sizeof(uint32_t);
+
+ res = flash_idle();
+ if (res)
+ goto exit_wr;
+
+ if (STM32_FLASH_SR & FLASH_SR_BUSY) {
+ res = EC_ERROR_TIMEOUT;
+ goto exit_wr;
+ }
+
+ /* Check for error conditions - erase failed, voltage error,
+ * protection error.
+ */
+ if (STM32_FLASH_SR & FLASH_SR_ERR_MASK) {
+ res = EC_ERROR_UNKNOWN;
+ goto exit_wr;
+ }
+ }
+
+exit_wr:
+ /* Disable PG bit */
+ STM32_FLASH_CR &= ~FLASH_CR_PG;
+
+ lock();
+
+ return res;
+}
+
+
+
+/* "@Internal Flash /0x08000000/04*016Kg,01*064Kg,03*128Kg" */
+struct flash_sector {
+ int base;
+ int size;
+};
+static const struct flash_sector sectors[] = {
+ {(0 * 1024), (16 * 1024)},
+ {(16 * 1024), (16 * 1024)},
+ {(32 * 1024), (16 * 1024)},
+ {(48 * 1024), (16 * 1024)},
+ {(64 * 1024), (64 * 1024)},
+ {(128 * 1024), (128 * 1024)},
+ {(256 * 1024), (128 * 1024)},
+ {(384 * 1024), (128 * 1024)}
+};
+static const int num_sectors = ARRAY_SIZE(sectors);
+
+int flash_physical_erase(int offset, int size)
+{
+ int res = EC_SUCCESS;
+ int start_sector;
+ int end_sector;
+
+ /* Check that offset/size align with sectors. */
+ for (start_sector = 0; start_sector < num_sectors; start_sector++)
+ if (offset == sectors[start_sector].base)
+ break;
+ for (end_sector = start_sector; end_sector < num_sectors; end_sector++)
+ if ((offset + size) ==
+ (sectors[end_sector].base + sectors[end_sector].size))
+ break;
+
+ /* We can only erase on sector boundaries. */
+ if ((start_sector >= num_sectors) || (end_sector >= num_sectors))
+ return EC_ERROR_PARAM1;
+
+ if (unlock() != EC_SUCCESS)
+ return EC_ERROR_UNKNOWN;
+
+ res = flash_idle();
+ if (res)
+ goto exit_er;
+
+ clear_flash_errors();
+
+ for (; start_sector <= end_sector; start_sector++) {
+ /* Do nothing if already erased */
+ if (flash_is_erased(sectors[start_sector].base,
+ sectors[start_sector].size))
+ continue;
+
+ res = flash_idle();
+ if (res)
+ goto exit_er;
+
+ /* set Sector Erase bit and select sector */
+ STM32_FLASH_CR = (STM32_FLASH_CR & ~FLASH_CR_SNB_MASK) |
+ FLASH_CR_SER | FLASH_CR_SNB(start_sector);
+
+ /* set STRT bit : start erase */
+ STM32_FLASH_CR |= FLASH_CR_STRT;
+
+ /*
+ * Reload the watchdog timer to avoid watchdog reset during a
+ * long erase operation.
+ */
+ watchdog_reload();
+
+ /* Wait for erase to complete, this will be awhile */
+ res = flash_idle();
+ if (res)
+ goto exit_er;
+ /*
+ * Check for error conditions - erase failed, voltage error,
+ * protection error
+ */
+ if (STM32_FLASH_SR & FLASH_SR_ERR_MASK) {
+ res = EC_ERROR_UNKNOWN;
+ goto exit_er;
+ }
+ }
+
+exit_er:
+ /* reset PER bit */
+ STM32_FLASH_CR &= ~FLASH_CR_SER;
+
+ lock();
+
+ return res;
+}
+
+/*****************************************************************************/
+/* High-level APIs */
+
+int flash_pre_init(void)
+{
+ return EC_SUCCESS;
+}
+
+/*****************************************************************************/
+/* Hooks */
+
+static void flash_preserve_state(void)
+{
+ struct flash_wp_state state;
+
+ state.entire_flash_locked = entire_flash_locked;
+
+ system_add_jump_tag(FLASH_SYSJUMP_TAG, FLASH_HOOK_VERSION,
+ sizeof(state), &state);
+}
+DECLARE_HOOK(HOOK_SYSJUMP, flash_preserve_state, HOOK_PRIO_DEFAULT);
+