405 lines
9.3 KiB
C
405 lines
9.3 KiB
C
/* 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");
|