/* Copyright 2014 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. */ /* Common flash memory module for STM32F and STM32F0 */ #include "battery.h" #include "console.h" #include "flash.h" #include "hooks.h" #include "registers.h" #include "panic.h" #include "system.h" #include "task.h" #include "timer.h" #include "util.h" #include "watchdog.h" /* * 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 / SECOND) / CYCLE_PER_FLASH_LOOP) /* Flash unlocking keys */ #define KEY1 0x45670123 #define KEY2 0xCDEF89AB /* Lock bits for FLASH_CR register */ #define PG (1<<0) #define PER (1<<1) #define OPTPG (1<<4) #define OPTER (1<<5) #define STRT (1<<6) #define CR_LOCK (1<<7) #define PRG_LOCK 0 #define OPT_LOCK (1<<9) static int write_optb(int byte, uint8_t value); static int wait_busy(void) { int timeout = FLASH_TIMEOUT_LOOP; while (STM32_FLASH_SR & (1 << 0) && timeout-- > 0) udelay(CYCLE_PER_FLASH_LOOP); return (timeout > 0) ? EC_SUCCESS : EC_ERROR_TIMEOUT; } static int unlock(int locks) { /* * 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 & CR_LOCK) { STM32_FLASH_KEYR = KEY1; STM32_FLASH_KEYR = KEY2; } /* unlock option memory if required */ if ((locks & OPT_LOCK) && !(STM32_FLASH_CR & OPT_LOCK)) { STM32_FLASH_OPTKEYR = KEY1; STM32_FLASH_OPTKEYR = KEY2; } /* Re-enable bus fault handler */ ignore_bus_fault(0); return ((STM32_FLASH_CR ^ OPT_LOCK) & (locks | CR_LOCK)) ? EC_ERROR_UNKNOWN : EC_SUCCESS; } static void lock(void) { STM32_FLASH_CR = CR_LOCK; } /* * Option byte organization * * [31:24] [23:16] [15:8] [7:0] * * 0x1FFF_F800 nUSER USER nRDP RDP * * 0x1FFF_F804 nData1 Data1 nData0 Data0 * * 0x1FFF_F808 nWRP1 WRP1 nWRP0 WRP0 * * 0x1FFF_F80C nWRP3 WRP2 nWRP2 WRP2 * * Note that the variable with n prefix means the complement. */ static uint8_t read_optb(int byte) { return *(uint8_t *)(STM32_OPTB_BASE + byte); } static int erase_optb(void) { int rv; rv = wait_busy(); if (rv) return rv; rv = unlock(OPT_LOCK); if (rv) return rv; /* Must be set in 2 separate lines. */ STM32_FLASH_CR |= OPTER; STM32_FLASH_CR |= STRT; rv = wait_busy(); if (rv) return rv; lock(); return EC_SUCCESS; } /* * Since the option byte erase is WHOLE erase, this function is to keep * rest of bytes, but make this byte 0xff. * Note that this could make a recursive call to write_optb(). */ static int preserve_optb(int byte) { int i, rv; uint8_t optb[8]; /* The byte has been reset, no need to run preserve. */ if (*(uint16_t *)(STM32_OPTB_BASE + byte) == 0xffff) return EC_SUCCESS; for (i = 0; i < ARRAY_SIZE(optb); ++i) optb[i] = read_optb(i * 2); optb[byte / 2] = 0xff; rv = erase_optb(); if (rv) return rv; for (i = 0; i < ARRAY_SIZE(optb); ++i) { rv = write_optb(i * 2, optb[i]); if (rv) return rv; } return EC_SUCCESS; } static int write_optb(int byte, uint8_t value) { volatile int16_t *hword = (uint16_t *)(STM32_OPTB_BASE + byte); int rv; rv = wait_busy(); if (rv) return rv; /* The target byte is the value we want to write. */ if (*(uint8_t *)hword == value) return EC_SUCCESS; /* Try to erase that byte back to 0xff. */ rv = preserve_optb(byte); if (rv) return rv; /* The value is 0xff after erase. No need to write 0xff again. */ if (value == 0xff) return EC_SUCCESS; rv = unlock(OPT_LOCK); if (rv) return rv; /* set OPTPG bit */ STM32_FLASH_CR |= OPTPG; *hword = ((~value) << STM32_OPTB_COMPL_SHIFT) | value; /* reset OPTPG bit */ STM32_FLASH_CR &= ~OPTPG; rv = wait_busy(); if (rv) return rv; lock(); return EC_SUCCESS; } /*****************************************************************************/ /* Physical layer APIs */ int flash_physical_write(int offset, int size, const char *data) { uint16_t *address = (uint16_t *)(CONFIG_PROGRAM_MEMORY_BASE + offset); int res = EC_SUCCESS; int i; if (unlock(PRG_LOCK) != EC_SUCCESS) { res = EC_ERROR_UNKNOWN; goto exit_wr; } /* Clear previous error status */ STM32_FLASH_SR = 0x34; /* set PG bit */ STM32_FLASH_CR |= PG; for (; size > 0; size -= sizeof(uint16_t)) { /* * Reload the watchdog timer to avoid watchdog reset when doing * long writing with interrupt disabled. */ watchdog_reload(); /* wait to be ready */ for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP); i++) ; /* write the half word */ *address++ = data[0] + (data[1] << 8); data += 2; /* Wait for writes to complete */ for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP); i++) ; 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 & 0x14) { res = EC_ERROR_UNKNOWN; goto exit_wr; } } exit_wr: /* Disable PG bit */ STM32_FLASH_CR &= ~PG; lock(); return res; } int flash_physical_erase(int offset, int size) { int res = EC_SUCCESS; if (unlock(PRG_LOCK) != EC_SUCCESS) return EC_ERROR_UNKNOWN; /* Clear previous error status */ STM32_FLASH_SR = 0x34; /* set PER bit */ STM32_FLASH_CR |= PER; for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE, offset += CONFIG_FLASH_ERASE_SIZE) { timestamp_t deadline; /* Do nothing if already erased */ if (flash_is_erased(offset, CONFIG_FLASH_ERASE_SIZE)) continue; /* select page to erase */ STM32_FLASH_AR = CONFIG_PROGRAM_MEMORY_BASE + offset; /* set STRT bit : start erase */ STM32_FLASH_CR |= STRT; /* * Reload the watchdog timer to avoid watchdog reset during a * long erase operation. */ watchdog_reload(); 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 & 0x14) { res = EC_ERROR_UNKNOWN; goto exit_er; } } exit_er: /* reset PER bit */ STM32_FLASH_CR &= ~PER; lock(); return res; } static int flash_physical_get_protect_at_boot(int block) { uint8_t val = read_optb(STM32_OPTB_WRP_OFF(block/8)); return (!(val & (1 << (block % 8)))) ? 1 : 0; } int flash_physical_protect_at_boot(enum flash_wp_range range) { int block; int i; int original_val[4], val[4]; enum flash_wp_range cur_range; for (i = 0; i < 4; ++i) original_val[i] = val[i] = read_optb(i * 2 + 8); for (block = WP_BANK_OFFSET; block < WP_BANK_OFFSET + PHYSICAL_BANKS; block++) { int byte_off = STM32_OPTB_WRP_OFF(block/8) / 2 - 4; if (block >= WP_BANK_OFFSET + WP_BANK_COUNT) cur_range = FLASH_WP_ALL; else cur_range = FLASH_WP_RO; if (cur_range <= range) val[byte_off] = val[byte_off] & (~(1 << (block % 8))); else val[byte_off] = val[byte_off] | (1 << (block % 8)); } for (i = 0; i < 4; ++i) if (original_val[i] != val[i]) write_optb(i * 2 + 8, val[i]); #ifdef CONFIG_WP_ALWAYS /* * Set a permanent protection by increasing RDP to level 1, * trying to unprotected the flash will trigger a full erase. */ write_optb(0, 0x11); #endif return EC_SUCCESS; } /** * Check if write protect register state is inconsistent with RO_AT_BOOT and * ALL_AT_BOOT state. * * @return zero if consistent, non-zero if inconsistent. */ static int registers_need_reset(void) { uint32_t flags = flash_get_protect(); int i; int ro_at_boot = (flags & EC_FLASH_PROTECT_RO_AT_BOOT) ? 1 : 0; int ro_wp_region_start = WP_BANK_OFFSET; int ro_wp_region_end = WP_BANK_OFFSET + WP_BANK_COUNT; for (i = ro_wp_region_start; i < ro_wp_region_end; i++) if (flash_physical_get_protect_at_boot(i) != ro_at_boot) return 1; return 0; } static void unprotect_all_blocks(void) { int i; for (i = 4; i < 8; ++i) write_optb(i * 2, 0xff); } /*****************************************************************************/ /* High-level APIs */ int flash_pre_init(void) { uint32_t reset_flags = system_get_reset_flags(); uint32_t prot_flags = flash_get_protect(); int need_reset = 0; if (flash_physical_restore_state()) return EC_SUCCESS; /* * 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) return EC_SUCCESS; if (prot_flags & EC_FLASH_PROTECT_GPIO_ASSERTED) { if ((prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) && !(prot_flags & EC_FLASH_PROTECT_RO_NOW)) { /* * Pstate wants RO protected at boot, but the write * protect register wasn't set to protect it. Force an * update to the write protect register and reboot so * it takes effect. */ flash_physical_protect_at_boot(FLASH_WP_RO); need_reset = 1; } if (registers_need_reset()) { /* * Write protect register was in an inconsistent state. * Set it back to a good state and reboot. * * TODO(crosbug.com/p/23798): this seems really similar * to the check above. One of them should be able to * go away. */ flash_protect_at_boot( (prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) ? FLASH_WP_RO : FLASH_WP_NONE); need_reset = 1; } } else { if (prot_flags & EC_FLASH_PROTECT_RO_NOW) { /* * Write protect pin unasserted but some section is * protected. Drop it and reboot. */ unprotect_all_blocks(); need_reset = 1; } } if ((flash_physical_get_valid_flags() & EC_FLASH_PROTECT_ALL_AT_BOOT) && (!!(prot_flags & EC_FLASH_PROTECT_ALL_AT_BOOT) != !!(prot_flags & EC_FLASH_PROTECT_ALL_NOW))) { /* * ALL_AT_BOOT and ALL_NOW should be both set or both unset * at boot. If they are not, it must be that the chip requires * OBL_LAUNCH to be set to reload option bytes. Let's reset * the system with OBL_LAUNCH set. * This assumes OBL_LAUNCH is used for hard reset in * chip/stm32/system.c. */ need_reset = 1; } if (need_reset) system_reset(SYSTEM_RESET_HARD | SYSTEM_RESET_PRESERVE_FLAGS); return EC_SUCCESS; }