blob: f54289b86463460141810e6bbe8658647f950554 [file] [log] [blame]
/*
* Copyright (c) 2021 ITE Corporation. All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ite_it8xxx2_flash_controller
#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
#define FLASH_WRITE_BLK_SZ DT_PROP(SOC_NV_FLASH_NODE, write_block_size)
#define FLASH_ERASE_BLK_SZ DT_PROP(SOC_NV_FLASH_NODE, erase_block_size)
#include <string.h>
#include <zephyr/device.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/linker/linker-defs.h>
#include <ilm.h>
#include <soc.h>
#define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(flash_ite_it8xxx2);
#define FLASH_IT8XXX2_REG_BASE \
((struct smfi_it8xxx2_regs *)DT_INST_REG_ADDR(0))
struct flash_it8xxx2_dev_data {
struct k_sem sem;
};
/*
* One page program instruction allows maximum 256 bytes (a page) of data
* to be programmed.
*/
#define CHIP_FLASH_WRITE_PAGE_MAX_SIZE 256
/* Program is run directly from storage */
#define CHIP_MAPPED_STORAGE_BASE DT_REG_ADDR(DT_NODELABEL(flash0))
/* flash size */
#define CHIP_FLASH_SIZE_BYTES DT_REG_SIZE(DT_NODELABEL(flash0))
/* protect bank size */
#define CHIP_FLASH_BANK_SIZE 0x00001000
/*
* This is the block size of the ILM on the it8xxx2 chip.
* The ILM for static code cache, CPU fetch instruction from
* ILM(ILM -> CPU)instead of flash(flash -> I-Cache -> CPU) if enabled.
*/
#define IT8XXX2_ILM_BLOCK_SIZE 0x00001000
/* page program command */
#define FLASH_CMD_PAGE_WRITE 0x2
/* sector erase command (erase size is 4KB) */
#define FLASH_CMD_SECTOR_ERASE 0x20
/* command for flash write */
#define FLASH_CMD_WRITE FLASH_CMD_PAGE_WRITE
/* Write status register */
#define FLASH_CMD_WRSR 0x01
/* Write disable */
#define FLASH_CMD_WRDI 0x04
/* Write enable */
#define FLASH_CMD_WREN 0x06
/* Read status register */
#define FLASH_CMD_RS 0x05
/* Set FSCE# as high level by writing 0 to address xfff_fe00h */
#define FLASH_FSCE_HIGH_ADDRESS 0x0FFFFE00
/* Set FSCE# as low level by writing data to address xfff_fd00h */
#define FLASH_FSCE_LOW_ADDRESS 0x0FFFFD00
enum flash_status_mask {
FLASH_SR_NO_BUSY = 0,
/* Internal write operation is in progress */
FLASH_SR_BUSY = 0x01,
/* Device is memory Write enabled */
FLASH_SR_WEL = 0x02,
FLASH_SR_ALL = (FLASH_SR_BUSY | FLASH_SR_WEL),
};
enum flash_transaction_cmd {
CMD_CONTINUE,
CMD_END,
};
static const struct flash_parameters flash_it8xxx2_parameters = {
.write_block_size = FLASH_WRITE_BLK_SZ,
.erase_value = 0xff,
};
void __soc_ram_code ramcode_reset_i_cache(void)
{
struct gctrl_it8xxx2_regs *const gctrl_regs = GCTRL_IT8XXX2_REGS_BASE;
/* I-Cache tag sram reset */
gctrl_regs->GCTRL_MCCR |= IT8XXX2_GCTRL_ICACHE_RESET;
/* Make sure the I-Cache is reset */
__asm__ volatile ("fence.i" ::: "memory");
gctrl_regs->GCTRL_MCCR &= ~IT8XXX2_GCTRL_ICACHE_RESET;
__asm__ volatile ("fence.i" ::: "memory");
}
void __soc_ram_code ramcode_flash_follow_mode(void)
{
struct smfi_it8xxx2_regs *const flash_regs = FLASH_IT8XXX2_REG_BASE;
/*
* ECINDAR3-0 are EC-indirect memory address registers.
*
* Enter follow mode by writing 0xf to low nibble of ECINDAR3 register,
* and set high nibble as 0x4 to select internal flash.
*/
flash_regs->SMFI_ECINDAR3 = (EC_INDIRECT_READ_INTERNAL_FLASH |
((FLASH_FSCE_HIGH_ADDRESS >> 24) & GENMASK(3, 0)));
/* Set FSCE# as high level by writing 0 to address xfff_fe00h */
flash_regs->SMFI_ECINDAR2 = (FLASH_FSCE_HIGH_ADDRESS >> 16) & GENMASK(7, 0);
flash_regs->SMFI_ECINDAR1 = (FLASH_FSCE_HIGH_ADDRESS >> 8) & GENMASK(7, 0);
flash_regs->SMFI_ECINDAR0 = FLASH_FSCE_HIGH_ADDRESS & GENMASK(7, 0);
/* Writing 0 to EC-indirect memory data register */
flash_regs->SMFI_ECINDDR = 0x00;
}
void __soc_ram_code ramcode_flash_follow_mode_exit(void)
{
struct smfi_it8xxx2_regs *const flash_regs = FLASH_IT8XXX2_REG_BASE;
/* Exit follow mode, and keep the setting of selecting internal flash */
flash_regs->SMFI_ECINDAR3 = EC_INDIRECT_READ_INTERNAL_FLASH;
flash_regs->SMFI_ECINDAR2 = 0x00;
}
void __soc_ram_code ramcode_flash_fsce_high(void)
{
struct smfi_it8xxx2_regs *const flash_regs = FLASH_IT8XXX2_REG_BASE;
struct gctrl_it8xxx2_regs *const gctrl_regs = GCTRL_IT8XXX2_REGS_BASE;
/* FSCE# high level */
flash_regs->SMFI_ECINDAR1 = (FLASH_FSCE_HIGH_ADDRESS >> 8) & GENMASK(7, 0);
/*
* A short delay (15~30 us) before #CS be driven high to ensure
* last byte has been latched in.
*
* For a loop that writing 0 to WNCKR register for N times, the delay
* value will be: ((N-1) / 65.536 kHz) to (N / 65.536 kHz).
* So we perform 2 consecutive writes to WNCKR here to ensure the
* minimum delay is 15us.
*/
gctrl_regs->GCTRL_WNCKR = 0;
gctrl_regs->GCTRL_WNCKR = 0;
/* Writing 0 to EC-indirect memory data register */
flash_regs->SMFI_ECINDDR = 0x00;
}
void __soc_ram_code ramcode_flash_write_dat(uint8_t wdata)
{
struct smfi_it8xxx2_regs *const flash_regs = FLASH_IT8XXX2_REG_BASE;
/* Write data to FMOSI */
flash_regs->SMFI_ECINDDR = wdata;
}
void __soc_ram_code ramcode_flash_transaction(int wlen, uint8_t *wbuf, int rlen, uint8_t *rbuf,
enum flash_transaction_cmd cmd_end)
{
struct smfi_it8xxx2_regs *const flash_regs = FLASH_IT8XXX2_REG_BASE;
int i;
/* FSCE# with low level */
flash_regs->SMFI_ECINDAR1 = (FLASH_FSCE_LOW_ADDRESS >> 8) & GENMASK(7, 0);
/* Write data to FMOSI */
for (i = 0; i < wlen; i++) {
flash_regs->SMFI_ECINDDR = wbuf[i];
}
/* Read data from FMISO */
for (i = 0; i < rlen; i++) {
rbuf[i] = flash_regs->SMFI_ECINDDR;
}
/* FSCE# high level if transaction done */
if (cmd_end == CMD_END) {
ramcode_flash_fsce_high();
}
}
void __soc_ram_code ramcode_flash_cmd_read_status(enum flash_status_mask mask,
enum flash_status_mask target)
{
struct smfi_it8xxx2_regs *const flash_regs = FLASH_IT8XXX2_REG_BASE;
uint8_t cmd_rs[] = {FLASH_CMD_RS};
/* Send read status command */
ramcode_flash_transaction(sizeof(cmd_rs), cmd_rs, 0, NULL, CMD_CONTINUE);
/*
* We prefer no timeout here. We can always get the status
* we want, or wait for watchdog triggered to check
* e-flash's status instead of breaking loop.
* This will avoid fetching unknown instruction from e-flash
* and causing exception.
*/
while ((flash_regs->SMFI_ECINDDR & mask) != target) {
/* read status and check if it is we want. */
;
}
/* transaction done, drive #CS high */
ramcode_flash_fsce_high();
}
void __soc_ram_code ramcode_flash_cmd_write_enable(void)
{
uint8_t cmd_we[] = {FLASH_CMD_WREN};
/* enter EC-indirect follow mode */
ramcode_flash_follow_mode();
/* send write enable command */
ramcode_flash_transaction(sizeof(cmd_we), cmd_we, 0, NULL, CMD_END);
/* read status and make sure busy bit cleared and write enabled. */
ramcode_flash_cmd_read_status(FLASH_SR_ALL, FLASH_SR_WEL);
/* exit EC-indirect follow mode */
ramcode_flash_follow_mode_exit();
}
void __soc_ram_code ramcode_flash_cmd_write_disable(void)
{
uint8_t cmd_wd[] = {FLASH_CMD_WRDI};
/* enter EC-indirect follow mode */
ramcode_flash_follow_mode();
/* send write disable command */
ramcode_flash_transaction(sizeof(cmd_wd), cmd_wd, 0, NULL, CMD_END);
/* make sure busy bit cleared. */
ramcode_flash_cmd_read_status(FLASH_SR_ALL, FLASH_SR_NO_BUSY);
/* exit EC-indirect follow mode */
ramcode_flash_follow_mode_exit();
}
int __soc_ram_code ramcode_flash_verify(int addr, int size, const char *data)
{
int i;
uint8_t *wbuf = (uint8_t *)data;
uint8_t *flash = (uint8_t *)addr;
if (data == NULL) {
/* verify for erase */
for (i = 0; i < size; i++) {
if (flash[i] != 0xFF) {
return -EINVAL;
}
}
} else {
/* verify for write */
for (i = 0; i < size; i++) {
if (flash[i] != wbuf[i]) {
return -EINVAL;
}
}
}
return 0;
}
void __soc_ram_code ramcode_flash_cmd_write(int addr, int wlen, uint8_t *wbuf)
{
int i;
uint8_t flash_write[] = {FLASH_CMD_WRITE, ((addr >> 16) & 0xFF),
((addr >> 8) & 0xFF), (addr & 0xFF)};
/* enter EC-indirect follow mode */
ramcode_flash_follow_mode();
/* send flash write command (aai word or page program) */
ramcode_flash_transaction(sizeof(flash_write), flash_write, 0, NULL, CMD_CONTINUE);
for (i = 0; i < wlen; i++) {
/* send data byte */
ramcode_flash_write_dat(wbuf[i]);
/*
* we want to restart the write sequence every IDEAL_SIZE
* chunk worth of data.
*/
if (!(++addr % CHIP_FLASH_WRITE_PAGE_MAX_SIZE)) {
uint8_t w_en[] = {FLASH_CMD_WREN};
ramcode_flash_fsce_high();
/* make sure busy bit cleared. */
ramcode_flash_cmd_read_status(FLASH_SR_BUSY, FLASH_SR_NO_BUSY);
/* send write enable command */
ramcode_flash_transaction(sizeof(w_en), w_en, 0, NULL, CMD_END);
/* make sure busy bit cleared and write enabled. */
ramcode_flash_cmd_read_status(FLASH_SR_ALL, FLASH_SR_WEL);
/* re-send write command */
flash_write[1] = (addr >> 16) & GENMASK(7, 0);
flash_write[2] = (addr >> 8) & GENMASK(7, 0);
flash_write[3] = addr & GENMASK(7, 0);
ramcode_flash_transaction(sizeof(flash_write), flash_write,
0, NULL, CMD_CONTINUE);
}
}
ramcode_flash_fsce_high();
/* make sure busy bit cleared. */
ramcode_flash_cmd_read_status(FLASH_SR_BUSY, FLASH_SR_NO_BUSY);
/* exit EC-indirect follow mode */
ramcode_flash_follow_mode_exit();
}
void __soc_ram_code ramcode_flash_write(int addr, int wlen, const char *wbuf)
{
ramcode_flash_cmd_write_enable();
ramcode_flash_cmd_write(addr, wlen, (uint8_t *)wbuf);
ramcode_flash_cmd_write_disable();
}
void __soc_ram_code ramcode_flash_cmd_erase(int addr, int cmd)
{
uint8_t cmd_erase[] = {cmd, ((addr >> 16) & 0xFF),
((addr >> 8) & 0xFF), (addr & 0xFF)};
/* enter EC-indirect follow mode */
ramcode_flash_follow_mode();
/* send erase command */
ramcode_flash_transaction(sizeof(cmd_erase), cmd_erase, 0, NULL, CMD_END);
/* make sure busy bit cleared. */
ramcode_flash_cmd_read_status(FLASH_SR_BUSY, FLASH_SR_NO_BUSY);
/* exit EC-indirect follow mode */
ramcode_flash_follow_mode_exit();
}
void __soc_ram_code ramcode_flash_erase(int addr, int cmd)
{
ramcode_flash_cmd_write_enable();
ramcode_flash_cmd_erase(addr, cmd);
ramcode_flash_cmd_write_disable();
}
/* Read data from flash */
static int __soc_ram_code flash_it8xxx2_read(const struct device *dev, off_t offset, void *data,
size_t len)
{
struct smfi_it8xxx2_regs *const flash_regs = FLASH_IT8XXX2_REG_BASE;
uint8_t *data_t = data;
int i;
for (i = 0; i < len; i++) {
flash_regs->SMFI_ECINDAR3 = EC_INDIRECT_READ_INTERNAL_FLASH;
flash_regs->SMFI_ECINDAR2 = (offset >> 16) & GENMASK(7, 0);
flash_regs->SMFI_ECINDAR1 = (offset >> 8) & GENMASK(7, 0);
flash_regs->SMFI_ECINDAR0 = (offset & GENMASK(7, 0));
/*
* Read/Write to this register will access one byte on the
* flash with the 32-bit flash address defined in ECINDAR3-0
*/
data_t[i] = flash_regs->SMFI_ECINDDR;
offset++;
}
return 0;
}
/* Write data to the flash, page by page */
static int __soc_ram_code flash_it8xxx2_write(const struct device *dev, off_t offset,
const void *src_data, size_t len)
{
struct flash_it8xxx2_dev_data *data = dev->data;
int ret = -EINVAL;
unsigned int key;
/*
* Check that the offset and length are multiples of the write
* block size.
*/
if ((offset % FLASH_WRITE_BLK_SZ) != 0) {
return -EINVAL;
}
if ((len % FLASH_WRITE_BLK_SZ) != 0) {
return -EINVAL;
}
if (!it8xxx2_is_ilm_configured()) {
return -EACCES;
}
k_sem_take(&data->sem, K_FOREVER);
/*
* CPU can't fetch instruction from flash while use
* EC-indirect follow mode to access flash, interrupts need to be
* disabled.
*/
key = irq_lock();
ramcode_flash_write(offset, len, src_data);
ramcode_reset_i_cache();
/* Get the ILM address of a flash offset. */
offset |= CHIP_MAPPED_STORAGE_BASE;
ret = ramcode_flash_verify(offset, len, src_data);
irq_unlock(key);
k_sem_give(&data->sem);
return ret;
}
/* Erase multiple blocks */
static int __soc_ram_code flash_it8xxx2_erase(const struct device *dev, off_t offset, size_t len)
{
struct flash_it8xxx2_dev_data *data = dev->data;
int v_size = len, v_addr = offset, ret = -EINVAL;
unsigned int key;
/*
* Check that the offset and length are multiples of the write
* erase block size.
*/
if ((offset % FLASH_ERASE_BLK_SZ) != 0) {
return -EINVAL;
}
if ((len % FLASH_ERASE_BLK_SZ) != 0) {
return -EINVAL;
}
if (!it8xxx2_is_ilm_configured()) {
return -EACCES;
}
k_sem_take(&data->sem, K_FOREVER);
/*
* CPU can't fetch instruction from flash while use
* EC-indirect follow mode to access flash, interrupts need to be
* disabled.
*/
key = irq_lock();
/* Always use sector erase command */
for (; len > 0; len -= FLASH_ERASE_BLK_SZ) {
ramcode_flash_erase(offset, FLASH_CMD_SECTOR_ERASE);
offset += FLASH_ERASE_BLK_SZ;
}
ramcode_reset_i_cache();
/* get the ILM address of a flash offset. */
v_addr |= CHIP_MAPPED_STORAGE_BASE;
ret = ramcode_flash_verify(v_addr, v_size, NULL);
irq_unlock(key);
k_sem_give(&data->sem);
return ret;
}
static const struct flash_parameters *
flash_it8xxx2_get_parameters(const struct device *dev)
{
ARG_UNUSED(dev);
return &flash_it8xxx2_parameters;
}
static int flash_it8xxx2_init(const struct device *dev)
{
struct smfi_it8xxx2_regs *const flash_regs = FLASH_IT8XXX2_REG_BASE;
struct flash_it8xxx2_dev_data *data = dev->data;
/* By default, select internal flash for indirect fast read. */
flash_regs->SMFI_ECINDAR3 = EC_INDIRECT_READ_INTERNAL_FLASH;
/*
* If the embedded flash's size of this part number is larger
* than 256K-byte, enable the page program cycle constructed
* by EC-Indirect Follow Mode.
*/
flash_regs->SMFI_FLHCTRL6R |= IT8XXX2_SMFI_MASK_ECINDPP;
/* Initialize mutex for flash controller */
k_sem_init(&data->sem, 1, 1);
return 0;
}
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
static const struct flash_pages_layout dev_layout = {
.pages_count = DT_REG_SIZE(SOC_NV_FLASH_NODE) /
DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
.pages_size = DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
};
static void flash_it8xxx2_pages_layout(const struct device *dev,
const struct flash_pages_layout **layout,
size_t *layout_size)
{
*layout = &dev_layout;
*layout_size = 1;
}
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
static const struct flash_driver_api flash_it8xxx2_api = {
.erase = flash_it8xxx2_erase,
.write = flash_it8xxx2_write,
.read = flash_it8xxx2_read,
.get_parameters = flash_it8xxx2_get_parameters,
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
.page_layout = flash_it8xxx2_pages_layout,
#endif
};
static struct flash_it8xxx2_dev_data flash_it8xxx2_data;
DEVICE_DT_INST_DEFINE(0, flash_it8xxx2_init, NULL,
&flash_it8xxx2_data, NULL,
PRE_KERNEL_1,
CONFIG_FLASH_INIT_PRIORITY,
&flash_it8xxx2_api);