summaryrefslogtreecommitdiff
path: root/chip/max32660/flash_chip.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/max32660/flash_chip.c')
-rw-r--r--chip/max32660/flash_chip.c404
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(&current_data[4 - bytes_written], data, bytes_written);
+
+ // Write the modified data
+ MXC_FLC->addr = offset - (offset % 4);
+ 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())
+ ;
+
+ 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");