blob: 0add8c4b02a8aaa6d7efc71ced9e68d7be6ae79b [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "flash_ctrl.h"
#include <stdint.h>
#include "common.h"
static uint32_t get_clr_err(void) {
uint32_t err_status;
// extract error status
err_status =
REG32(FLASH_CTRL_INTR_STATE(0)) & (0x1 << FLASH_CTRL_INTR_STATE_OP_ERROR);
// clear error if set
REG32(FLASH_CTRL_INTR_STATE(0)) = err_status;
// return status
return err_status;
}
// flash initialization done
void wait_flash_init(void) {
while ((REG32(FLASH_CTRL_STATUS(0)) & (1 << FLASH_CTRL_STATUS_INIT_WIP)) >
0) {
}
}
// wait for flash done and ack
void wait_done_and_ack(void) {
while ((REG32(FLASH_CTRL_OP_STATUS(0)) & (1 << FLASH_CTRL_OP_STATUS_DONE)) ==
0)
;
REG32(FLASH_CTRL_OP_STATUS(0)) = 0;
}
// setup flash prog
void setup_flash_prog(uint32_t addr, uint32_t num) {
uint32_t val;
val = FlashProg << FLASH_CTRL_CONTROL_OP_OFFSET |
(num - 1) << FLASH_CTRL_CONTROL_NUM_OFFSET |
0x1 << FLASH_CTRL_CONTROL_START;
REG32(FLASH_CTRL_ADDR(0)) = addr;
REG32(FLASH_CTRL_CONTROL(0)) = val;
}
// program data
uint32_t prog_flash(uint32_t addr, uint32_t num, uint32_t *data) {
uint32_t i = 0;
// setup flash programming
setup_flash_prog(addr, num);
// beginning filling up the fifo
for (i = 0; i < num; i++) {
REG32(FLASH_CTRL_PROG_FIFO(0)) = *data;
data++;
}
// wait for operation finish
wait_done_and_ack();
// return error status
return get_clr_err();
}
// read data
uint32_t read_flash(uint32_t addr, uint32_t num, uint32_t *data) {
uint32_t val;
uint32_t i = 0;
// kick off flash operation
val = FlashRead << FLASH_CTRL_CONTROL_OP_OFFSET |
(num - 1) << FLASH_CTRL_CONTROL_NUM_OFFSET |
0x1 << FLASH_CTRL_CONTROL_START;
REG32(FLASH_CTRL_ADDR(0)) = addr;
REG32(FLASH_CTRL_CONTROL(0)) = val;
while (i < num) {
// if not empty, read a word
if (((REG32(FLASH_CTRL_STATUS(0)) >> FLASH_CTRL_STATUS_RD_EMPTY) & 0x1) ==
0) {
*data++ = REG32(FLASH_CTRL_RD_FIFO(0));
i++;
}
}
// wait for operation finish
wait_done_and_ack();
// return error status
return get_clr_err();
}
// page erase flash
// wrap down to closest down to page boundary
uint32_t page_erase(uint32_t addr) {
uint32_t val;
uint32_t data[ERASE_CHECK_WORDS];
uint32_t verify_rounds;
uint32_t error;
error = 0;
verify_rounds = WORDS_PER_PAGE / ERASE_CHECK_WORDS;
// kick off flash operation
val = FlashErase << FLASH_CTRL_CONTROL_OP_OFFSET |
PageErase << FLASH_CTRL_CONTROL_ERASE_SEL |
0x1 << FLASH_CTRL_CONTROL_START;
REG32(FLASH_CTRL_ADDR(0)) = addr;
REG32(FLASH_CTRL_CONTROL(0)) = val;
// wait for operation finish
wait_done_and_ack();
error += get_clr_err();
// verify erase
for (uint32_t i = 0; i < verify_rounds; i++) {
error += read_flash(addr + i * ERASE_CHECK_WORDS * BYTES_PER_WORD,
ERASE_CHECK_WORDS, data);
for (uint32_t j = 0; j < ERASE_CHECK_WORDS; j++) {
if (data[i] != 0xFFFFFFFF) {
REG32(FLASH_CTRL_SCRATCH(0)) = data[i];
// re-init array
data[i] = 0;
error++;
}
}
}
// return error status
return error;
}
void flash_default_region(uint32_t rd_en, uint32_t prog_en, uint32_t erase_en) {
REG32(FLASH_CTRL_DEFAULT_REGION(0)) =
rd_en << FLASH_CTRL_DEFAULT_REGION_RD_EN |
prog_en << FLASH_CTRL_DEFAULT_REGION_PROG_EN |
erase_en << FLASH_CTRL_DEFAULT_REGION_ERASE_EN;
}
void flash_cfg_region(mp_region_t region_cfg) {
REG32(FLASH_CTRL_MP_REGION_CFG0(0) + region_cfg.num * 4) =
region_cfg.base << FLASH_CTRL_MP_REGION_CFG0_BASE0_OFFSET |
region_cfg.size << FLASH_CTRL_MP_REGION_CFG0_SIZE0_OFFSET |
region_cfg.rd_en << FLASH_CTRL_MP_REGION_CFG0_RD_EN0 |
region_cfg.prog_en << FLASH_CTRL_MP_REGION_CFG0_PROG_EN0 |
region_cfg.erase_en << FLASH_CTRL_MP_REGION_CFG0_ERASE_EN0 |
0x1 << FLASH_CTRL_MP_REGION_CFG0_EN0;
}