/* * 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 #include #include #include #include #include #include #include #define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL #include 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);