diff options
Diffstat (limited to 'chip/max32660/flash_chip.c')
-rw-r--r-- | chip/max32660/flash_chip.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/chip/max32660/flash_chip.c b/chip/max32660/flash_chip.c new file mode 100644 index 0000000000..ace87294a7 --- /dev/null +++ b/chip/max32660/flash_chip.c @@ -0,0 +1,404 @@ +/* Copyright 2019 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. + */ + +/* MAX32660 Flash Memory Module for Chrome EC */ + +#include "flash.h" +#include "switch.h" +#include "system.h" +#include "timer.h" +#include "util.h" +#include "watchdog.h" +#include "registers.h" +#include "common.h" +#include "icc_regs.h" +#include "flc_regs.h" + +#define CPUTS(outstr) cputs(CC_SYSTEM, outstr) +#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ##args) + +/***** Definitions *****/ + +/// Bit mask that can be used to find the starting address of a page in flash +#define MXC_FLASH_PAGE_MASK ~(MXC_FLASH_PAGE_SIZE - 1) + +/// Calculate the address of a page in flash from the page number +#define MXC_FLASH_PAGE_ADDR(page) \ + (MXC_FLASH_MEM_BASE + ((unsigned long)page * MXC_FLASH_PAGE_SIZE)) + +void flash_operation(void) +{ + volatile uint32_t *line_addr; + volatile uint32_t __attribute__((unused)) line; + + // Clear the cache + MXC_ICC->cache_ctrl ^= MXC_F_ICC_CACHE_CTRL_CACHE_EN; + MXC_ICC->cache_ctrl ^= MXC_F_ICC_CACHE_CTRL_CACHE_EN; + + // Clear the line fill buffer + line_addr = (uint32_t *)(MXC_FLASH_MEM_BASE); + line = *line_addr; + + line_addr = (uint32_t *)(MXC_FLASH_MEM_BASE + MXC_FLASH_PAGE_SIZE); + line = *line_addr; +} + +static int flash_busy(void) +{ + return (MXC_FLC->cn & + (MXC_F_FLC_CN_WR | MXC_F_FLC_CN_ME | MXC_F_FLC_CN_PGE)); +} + +static int flash_init_controller(void) +{ + // Set flash clock divider to generate a 1MHz clock from the APB clock + MXC_FLC->clkdiv = SystemCoreClock / 1000000; + + /* Check if the flash controller is busy */ + if (flash_busy()) { + return EC_ERROR_BUSY; + } + + /* Clear stale errors */ + if (MXC_FLC->intr & MXC_F_FLC_INTR_AF) { + MXC_FLC->intr &= ~MXC_F_FLC_INTR_AF; + } + + /* Unlock flash */ + MXC_FLC->cn = (MXC_FLC->cn & ~MXC_F_FLC_CN_UNLOCK) | + MXC_S_FLC_CN_UNLOCK_UNLOCKED; + + return EC_SUCCESS; +} + +static int flash_device_page_erase(uint32_t address) +{ + int err; + + if ((err = flash_init_controller()) != EC_SUCCESS) + return err; + + // Align address on page boundary + address = address - (address % MXC_FLASH_PAGE_SIZE); + + /* Write paflash_init_controllerde */ + MXC_FLC->cn = (MXC_FLC->cn & ~MXC_F_FLC_CN_ERASE_CODE) | + MXC_S_FLC_CN_ERASE_CODE_ERASEPAGE; + /* Issue page erase command */ + MXC_FLC->addr = address; + MXC_FLC->cn |= MXC_F_FLC_CN_PGE; + + /* Wait until flash operation is complete */ + while (flash_busy()) + ; + + /* Lock flash */ + MXC_FLC->cn &= ~MXC_F_FLC_CN_UNLOCK; + + /* Check access violations */ + if (MXC_FLC->intr & MXC_F_FLC_INTR_AF) { + MXC_FLC->intr &= ~MXC_F_FLC_INTR_AF; + return EC_ERROR_UNKNOWN; + } + + flash_operation(); + + return EC_SUCCESS; +} + +int flash_physical_write(int offset, int size, const char *data) +{ + int err; + uint32_t bytes_written; + uint8_t current_data[4]; + + if ((err = flash_init_controller()) != EC_SUCCESS) + return err; + + // write in 32-bit units until we are 128-bit aligned + MXC_FLC->cn &= ~MXC_F_FLC_CN_BRST; + MXC_FLC->cn |= MXC_F_FLC_CN_WDTH; + + // Align the address and read/write if we have to + if (offset & 0x3) { + + // Figure out how many bytes we have to write to round up the + // address + bytes_written = 4 - (offset & 0x3); + + // Save the data currently in the flash + memcpy(current_data, (void *)(offset & (~0x3)), 4); + + // Modify current_data to insert the data from buffer + memcpy(¤t_data[4 - bytes_written], data, bytes_written); + + // Write the modified data + MXC_FLC->addr = offset - (offset % 4); + memcpy((void *)&MXC_FLC->data[0], ¤t_data, 4); + MXC_FLC->cn |= MXC_F_FLC_CN_WR; + + /* Wait until flash operation is complete */ + while (flash_busy()) + ; + + offset += bytes_written; + size -= bytes_written; + data += bytes_written; + } + + while ((size >= 4) && ((offset & 0x1F) != 0)) { + MXC_FLC->addr = offset; + memcpy((void *)&MXC_FLC->data[0], data, 4); + MXC_FLC->cn |= MXC_F_FLC_CN_WR; + + /* Wait until flash operation is complete */ + while (flash_busy()) + ; + + offset += 4; + size -= 4; + data += 4; + } + + if (size >= 16) { + + // write in 128-bit bursts while we can + MXC_FLC->cn &= ~MXC_F_FLC_CN_WDTH; + + while (size >= 16) { + MXC_FLC->addr = offset; + memcpy((void *)&MXC_FLC->data[0], data, 16); + MXC_FLC->cn |= MXC_F_FLC_CN_WR; + + /* Wait until flash operation is complete */ + while (flash_busy()) + ; + + offset += 16; + size -= 16; + data += 16; + } + + // Return to 32-bit writes. + MXC_FLC->cn |= MXC_F_FLC_CN_WDTH; + } + + while (size >= 4) { + MXC_FLC->addr = offset; + memcpy((void *)&MXC_FLC->data[0], data, 4); + MXC_FLC->cn |= MXC_F_FLC_CN_WR; + + /* Wait until flash operation is complete */ + while (flash_busy()) + ; + + offset += 4; + size -= 4; + data += 4; + } + + if (size > 0) { + // Save the data currently in the flash + memcpy(current_data, (void *)(offset), 4); + + // Modify current_data to insert the data from data + memcpy(current_data, data, size); + + MXC_FLC->addr = offset; + memcpy((void *)&MXC_FLC->data[0], current_data, 4); + MXC_FLC->cn |= MXC_F_FLC_CN_WR; + + /* Wait until flash operation is complete */ + while (flash_busy()) + ; + } + + /* Lock flash */ + MXC_FLC->cn &= ~MXC_F_FLC_CN_UNLOCK; + + /* Check access violations */ + if (MXC_FLC->intr & MXC_F_FLC_INTR_AF) { + MXC_FLC->intr &= ~MXC_F_FLC_INTR_AF; + return EC_ERROR_UNKNOWN; + } + + flash_operation(); + + return EC_SUCCESS; +} + +/*****************************************************************************/ +/* Physical layer APIs */ + +int flash_physical_erase(int offset, int size) +{ + int i; + int pages; + int error_status; + + /* + * erase 'size' number of bytes starting at address 'offset' + */ + /* calculate the number of pages */ + pages = size / CONFIG_FLASH_ERASE_SIZE; + /* iterate over the number of pages */ + for (i = 0; i < pages; i++) { + /* erase the page after calculating the start address */ + error_status = flash_device_page_erase( + offset + (i * CONFIG_FLASH_ERASE_SIZE)); + if (error_status != EC_SUCCESS) { + return error_status; + } + } + return EC_SUCCESS; +} + +int flash_physical_get_protect(int bank) +{ + /* Not protected */ + return 0; +} + +uint32_t flash_physical_get_protect_flags(void) +{ + /* no flags set */ + return 0; +} + +uint32_t flash_physical_get_valid_flags(void) +{ + /* These are the flags we're going to pay attention to */ + 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) +{ + /* no flags writable */ + return 0; +} + +int flash_physical_protect_at_boot(uint32_t new_flags) +{ + /* nothing to do here */ + return EC_SUCCESS; +} + +int flash_physical_protect_now(int all) +{ + /* nothing to do here */ + return EC_SUCCESS; +} + +/*****************************************************************************/ +/* High-level APIs */ + +int flash_pre_init(void) +{ + return EC_SUCCESS; +} + +/*****************************************************************************/ +/* Test Commands */ + +/* + * Read, Write, and Erase a page of flash memory using chip routines + * NOTE: This is a DESTRUCTIVE test for the range of flash pages tested + * make sure that PAGE_START is beyond your flash code. + */ +static int command_flash_test1(int argc, char **argv) +{ + int i; + uint8_t *ptr; + const uint32_t PAGE_START = 9; + const uint32_t PAGE_END = 32; + uint32_t page; + int error_status; + uint32_t flash_address; + const int BUFFER_SIZE = 32; + uint8_t buffer[BUFFER_SIZE]; + + /* + * As a test, write unique data to each page in this for loop, later + * verify data in pages + */ + for (page = PAGE_START; page < PAGE_END; page++) { + flash_address = page * CONFIG_FLASH_ERASE_SIZE; + + /* + * erase page + */ + error_status = flash_physical_erase(flash_address, + CONFIG_FLASH_ERASE_SIZE); + if (error_status != EC_SUCCESS) { + CPRINTS("Error with flash_physical_erase\n"); + return EC_ERROR_UNKNOWN; + } + + /* + * verify page was erased + */ + // CPRINTS("read flash page %d, address %x, ", page, + // flash_address); + ptr = (uint8_t *)flash_address; + for (i = 0; i < CONFIG_FLASH_ERASE_SIZE; i++) { + if (*ptr++ != 0xff) { + CPRINTS("Error with verifying page erase\n"); + return EC_ERROR_UNKNOWN; + } + } + + /* + * write pattern to page, just write BUFFER_SIZE worth of data + */ + for (i = 0; i < BUFFER_SIZE; i++) { + buffer[i] = i + page; + } + error_status = flash_physical_write(flash_address, BUFFER_SIZE, + buffer); + if (error_status != EC_SUCCESS) { + CPRINTS("Error with flash_physical_write\n"); + return EC_ERROR_UNKNOWN; + } + } + + /* + * Verify data in pages + */ + for (page = PAGE_START; page < PAGE_END; page++) { + flash_address = page * CONFIG_FLASH_ERASE_SIZE; + + /* + * read a portion of flash memory + */ + ptr = (uint8_t *)flash_address; + for (i = 0; i < BUFFER_SIZE; i++) { + if (*ptr++ != (i + page)) { + CPRINTS("Error with verifing written test " + "data\n"); + return EC_ERROR_UNKNOWN; + } + } + CPRINTS("Verified Erase, Write, Read page %d", page); + } + + /* + * Clean up after tests + */ + for (page = PAGE_START; page <= PAGE_END; page++) { + flash_address = page * CONFIG_FLASH_ERASE_SIZE; + error_status = flash_physical_erase(flash_address, + CONFIG_FLASH_ERASE_SIZE); + if (error_status != EC_SUCCESS) { + CPRINTS("Error with flash_physical_erase\n"); + return EC_ERROR_UNKNOWN; + } + } + + CPRINTS("done command_flash_test1."); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(flashtest1, command_flash_test1, "flashtest1", + "Flash chip routine tests"); |