| /* |
| * Copyright (c) 2025 ITE Corporation. All Rights Reserved. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT ite_it51xxx_manual_flash_1k |
| |
| #include <soc.h> |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/flash.h> |
| #include <zephyr/drivers/flash/it51xxx_flash_api_ex.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <zephyr/init.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/linker/linker-defs.h> |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(flash_ite_it51xxx, CONFIG_FLASH_LOG_LEVEL); |
| |
| #define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash) |
| #define FLASH_SIZE DT_REG_SIZE(SOC_NV_FLASH_NODE) |
| #define FLASH_READ_MAX_SZ KB(1) |
| #define FLASH_WRITE_MAX_SZ KB(1) |
| #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) |
| |
| /* it51xxx SMFI registers definition */ |
| #define IT51XXX_M1K_REGS_BASE DT_INST_REG_ADDR_BY_IDX(0, 0) |
| #define IT51XXX_SMFI_REGS_BASE DT_INST_REG_ADDR_BY_IDX(0, 1) |
| |
| /* 0x3b: EC-Indirect Memory Address Register 0 */ |
| #define SMFI_ECINDAR0 (IT51XXX_SMFI_REGS_BASE + 0x3b) |
| /* 0x3c: EC-Indirect Memory Address Register 1 */ |
| #define SMFI_ECINDAR1 (IT51XXX_SMFI_REGS_BASE + 0x3c) |
| /* 0x3d: EC-Indirect Memory Address Register 2 */ |
| #define SMFI_ECINDAR2 (IT51XXX_SMFI_REGS_BASE + 0x3d) |
| /* 0x3e: EC-Indirect Memory Address Register 3 */ |
| #define SMFI_ECINDAR3 (IT51XXX_SMFI_REGS_BASE + 0x3e) |
| #define SEL_SPI 0x00 |
| #define SEL_EFLASH 0x01 |
| #define ECINDA31_30(n) FIELD_PREP(GENMASK(7, 6), n) |
| /* 0x3f: EC-Indirect Memory Data Register */ |
| #define SMFI_ECINDDR (IT51XXX_SMFI_REGS_BASE + 0x3f) |
| /* 0x63: Flash Control Register 3 */ |
| #define SMFI_FLHCTRL3R (IT51XXX_SMFI_REGS_BASE + 0x63) |
| #define SIFE BIT(3) |
| #define FFSPITRI BIT(0) |
| /* 0x64: Flash Control Register 4 */ |
| #define SMFI_FLHCTRL4R (IT51XXX_SMFI_REGS_BASE + 0x64) |
| #define EN2FLH BIT(7) |
| /* 0x81: Flash Control Register 6 */ |
| #define SMFI_FLHCTRL6R (IT51XXX_SMFI_REGS_BASE + 0x81) |
| #define FSPI28AMEN BIT(4) |
| /* 0xa6: Manual Flash 1K Command Control 1 */ |
| #define SMFI_M1KFLHCTRL1 (IT51XXX_M1K_REGS_BASE + 0x00) |
| #define W1S_M1K_PE BIT(1) |
| #define W1S_READ BIT(0) |
| /* 0xa8: Manual Flash 1K Command Control 3 */ |
| #define SMFI_M1KFLHCTRL3 (IT51XXX_M1K_REGS_BASE + 0x02) |
| #define M1KPHY BIT(5) |
| /* 0xa9: Manual Flash 1K Command Control 4 */ |
| #define SMFI_M1KFLHCTRL4 (IT51XXX_M1K_REGS_BASE + 0x03) |
| /* 0xaa: Manual Flash 1K Command Control 5 */ |
| #define SMFI_M1KFLHCTRL5 (IT51XXX_M1K_REGS_BASE + 0x04) |
| #define M1K_READ_CMD(n) FIELD_PREP(GENMASK(7, 6), n) |
| #define M1K_READ 0x01 |
| #define M1K_FETCH 0x02 |
| #define M1K_SFDP 0x03 |
| #define M1K_READ_BCNT(n) FIELD_PREP(GENMASK(1, 0), n) |
| /* 0xab: Manual Flash 1K Command Control 6 */ |
| #define SMFI_M1KFLHCTRL6 (IT51XXX_M1K_REGS_BASE + 0x05) |
| /* 0xb9: Manual Flash 1K Command Control 7 */ |
| #define SMFI_M1KFLHCTRL7 (IT51XXX_M1K_REGS_BASE + 0x13) |
| #define M1K_PE_CMD(n) FIELD_PREP(GENMASK(7, 6), n) |
| #define M1K_PROG 0x01 |
| #define M1K_ERASE 0x02 |
| #define M1K_PROG_BCNT(n) FIELD_PREP(GENMASK(1, 0), n) |
| /* 0xac: M1K DLM BASE Address Byte 0 */ |
| #define SMFI_M1K_DLM_BA0 (IT51XXX_M1K_REGS_BASE + 0x06) |
| /* 0xad: M1K DLM BASE Address Byte 1 */ |
| #define SMFI_M1K_DLM_BA1 (IT51XXX_M1K_REGS_BASE + 0x07) |
| /* 0xae: M1K DLM BASE Address Byte 2 */ |
| #define SMFI_M1K_DLM_BA2 (IT51XXX_M1K_REGS_BASE + 0x08) |
| /* 0xaf: M1K Status Register 1 */ |
| #define SMFI_M1KSTS1 (IT51XXX_M1K_REGS_BASE + 0x09) |
| /* 0xbc: M1K Status Register 2 */ |
| #define SMFI_M1KSTS2 (IT51XXX_M1K_REGS_BASE + 0x16) |
| enum m1ksts2 { |
| DMA_FETCH_CYC = 3, |
| M1K_READ_DUTY, |
| M1K_RESERVED, |
| M1K_PE_CYC, |
| }; |
| /* 0xd0: M1K-PROG/M1K-ERASE Lower Bound Address Byte 0 */ |
| #define SMFI_M1K_PE_LBA0 (IT51XXX_M1K_REGS_BASE + 0x2a) |
| /* 0xd1: M1K-PROG/M1K-ERASE Lower Bound Address Byte 1 */ |
| #define SMFI_M1K_PE_LBA1 (IT51XXX_M1K_REGS_BASE + 0x2b) |
| /* 0xd2: M1K-PROG/M1K-ERASE Lower Bound Address Byte 2 */ |
| #define SMFI_M1K_PE_LBA2 (IT51XXX_M1K_REGS_BASE + 0x2c) |
| /* 0xd3: M1K-PROG/M1K-ERASE Lower Bound Address Byte 3 */ |
| #define SMFI_M1K_PE_LBA3 (IT51XXX_M1K_REGS_BASE + 0x2d) |
| #define M1K_PE_SEL_FSPI BIT(7) |
| #define M1K_PE_SEL_FSCE1 BIT(6) |
| /* 0xd5: M1K-ERASE Upper Bound Address Byte 1 */ |
| #define SMFI_M1K_ERASE_UBA1 (IT51XXX_M1K_REGS_BASE + 0x2f) |
| #define M1K_ERASE_UBA(n) FIELD_PREP(GENMASK(7, 2), n) |
| /* 0xd6: M1K-ERASE Lower Bound Address Byte 2 */ |
| #define SMFI_M1K_ERASE_UBA2 (IT51XXX_M1K_REGS_BASE + 0x30) |
| /* 0xd7: M1K-ERASE Lower Bound Address Byte 3 */ |
| #define SMFI_M1K_ERASE_UBA3 (IT51XXX_M1K_REGS_BASE + 0x31) |
| /* 0xd8: M1K-READ Lower Bound Address Byte 0 */ |
| #define SMFI_M1K_READ_LBA0 (IT51XXX_M1K_REGS_BASE + 0x32) |
| /* 0xd9: M1K-READ Lower Bound Address Byte 1 */ |
| #define SMFI_M1K_READ_LBA1 (IT51XXX_M1K_REGS_BASE + 0x33) |
| /* 0xda: M1K-READ Lower Bound Address Byte 2 */ |
| #define SMFI_M1K_READ_LBA2 (IT51XXX_M1K_REGS_BASE + 0x34) |
| /* 0xdb: M1K-READ Lower Bound Address Byte 3 */ |
| #define SMFI_M1K_READ_LBA3 (IT51XXX_M1K_REGS_BASE + 0x35) |
| #define M1K_READ_SEL_FSPI BIT(7) |
| #define M1K_READ_SEL_FSCE1 BIT(6) |
| |
| #define M1K_READ_BCNT_MASK GENMASK(9, 0) |
| #define M1K_PROG_BCNT_MASK GENMASK(9, 0) |
| |
| struct flash_it51xxx_dev_data { |
| struct k_sem sem; |
| enum flash_it51xxx_ex_op flash; |
| }; |
| |
| struct flash_it51xxx_config { |
| const struct pinctrl_dev_config *pcfg; |
| enum flash_it51xxx_ex_op target_flash; |
| }; |
| |
| static bool is_valid_range(off_t offset, uint32_t len) |
| { |
| return (offset >= 0) && ((offset + len) <= FLASH_SIZE); |
| } |
| |
| static void flash_set_m1k_read_lba(const struct device *dev, uint32_t lb_addr) |
| { |
| uint8_t m1k_pe_lba3_value; |
| |
| m1k_pe_lba3_value = sys_read8(SMFI_M1K_READ_LBA3); |
| /* Start address of M1K-READ[27:24] */ |
| sys_write8(m1k_pe_lba3_value | FIELD_GET(GENMASK(27, 24), lb_addr), SMFI_M1K_READ_LBA3); |
| /* Start address of M1K-READ[23:16] */ |
| sys_write8(FIELD_GET(GENMASK(23, 16), lb_addr), SMFI_M1K_READ_LBA2); |
| /* Start address of M1K-READ[15:8] */ |
| sys_write8(FIELD_GET(GENMASK(15, 8), lb_addr), SMFI_M1K_READ_LBA1); |
| /* Start address of M1K-READ[7:0] */ |
| sys_write8(FIELD_GET(GENMASK(7, 0), lb_addr), SMFI_M1K_READ_LBA0); |
| } |
| |
| static void flash_set_m1k_pe_lba(const struct device *dev, uint32_t lb_addr) |
| { |
| uint8_t m1k_pe_lba3_value; |
| |
| m1k_pe_lba3_value = sys_read8(SMFI_M1K_PE_LBA3); |
| /* Start address of M1K-PROG / Lower bound address of M1K-ERASE[27:24] */ |
| sys_write8(m1k_pe_lba3_value | FIELD_GET(GENMASK(27, 24), lb_addr), SMFI_M1K_PE_LBA3); |
| /* Start address of M1K-PROG / Lower bound address of M1K-ERASE[23:16] */ |
| sys_write8(FIELD_GET(GENMASK(23, 16), lb_addr), SMFI_M1K_PE_LBA2); |
| /* Start address of M1K-PROG / Lower bound address of M1K-ERASE[15:8] */ |
| sys_write8(FIELD_GET(GENMASK(15, 8), lb_addr), SMFI_M1K_PE_LBA1); |
| /* Start address of M1K-PROG / Lower bound address of M1K-ERASE[7:0] */ |
| sys_write8(FIELD_GET(GENMASK(7, 0), lb_addr), SMFI_M1K_PE_LBA0); |
| } |
| |
| static void flash_set_m1k_erase_uba(const struct device *dev, uint32_t ub_addr) |
| { |
| /* Upper bound address of M1K-ERASE[27:24] */ |
| sys_write8(FIELD_GET(GENMASK(27, 24), ub_addr), SMFI_M1K_ERASE_UBA3); |
| /* Upper bound address of M1K-ERASE[23:16] */ |
| sys_write8(FIELD_GET(GENMASK(23, 16), ub_addr), SMFI_M1K_ERASE_UBA2); |
| /* Upper bound address of M1K-ERASE[15:10] */ |
| sys_write8(M1K_ERASE_UBA(FIELD_GET(GENMASK(15, 10), ub_addr)), SMFI_M1K_ERASE_UBA1); |
| } |
| |
| static void flash_set_m1k_dlm_ba(const struct device *dev, uint32_t dlm_addr) |
| { |
| /* M1K DLM base address[17:16] */ |
| sys_write8(FIELD_GET(GENMASK(17, 16), dlm_addr), SMFI_M1K_DLM_BA2); |
| /* M1K DLM base address[15:8] */ |
| sys_write8(FIELD_GET(GENMASK(15, 8), dlm_addr), SMFI_M1K_DLM_BA1); |
| /* M1K DLM base address[7:0] */ |
| sys_write8(FIELD_GET(GENMASK(7, 0), dlm_addr), SMFI_M1K_DLM_BA0); |
| } |
| |
| static int flash_wait_status(const struct device *dev, enum m1ksts2 state) |
| { |
| /* Waiting for M1K-READ to complete */ |
| if (WAIT_FOR(!IS_BIT_SET(sys_read8(SMFI_M1KSTS2), state), INT_MAX, NULL) == false) { |
| LOG_ERR("%s: Timeout waiting for status", __func__); |
| |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| static int m1k_flash_read(const struct device *dev, off_t offset, const void *dst_data, size_t len) |
| { |
| int ret; |
| uint16_t count; |
| uint8_t m1kflhctrl5_cmd; |
| |
| /* It's the start address of M1K-READ */ |
| flash_set_m1k_read_lba(dev, offset); |
| |
| /* M1K DLM base address */ |
| flash_set_m1k_dlm_ba(dev, (uint32_t)dst_data); |
| |
| /* M1k-READ size (Maximum 1024 bytes) */ |
| count = (len - 1) & M1K_READ_BCNT_MASK; |
| |
| m1kflhctrl5_cmd = sys_read8(SMFI_M1KFLHCTRL5) & ~GENMASK(1, 0); |
| /* M1K-READ byte count[9:8] */ |
| sys_write8(m1kflhctrl5_cmd | M1K_READ_BCNT(FIELD_GET(GENMASK(9, 8), count)), |
| SMFI_M1KFLHCTRL5); |
| /* M1K-READ byte count[7:0] */ |
| sys_write8(FIELD_GET(GENMASK(7, 0), count), SMFI_M1KFLHCTRL4); |
| |
| m1kflhctrl5_cmd = sys_read8(SMFI_M1KFLHCTRL5) & ~GENMASK(7, 6); |
| /* Read data from the flash to DLM */ |
| sys_write8(m1kflhctrl5_cmd | M1K_READ_CMD(M1K_READ), SMFI_M1KFLHCTRL5); |
| |
| /* Write-1-Start M1K-READ */ |
| sys_write8(W1S_READ, SMFI_M1KFLHCTRL1); |
| ret = flash_wait_status(dev, M1K_READ_DUTY); |
| |
| /* Reset the M1K setting and counter to 0 */ |
| sys_write8(0, SMFI_M1KFLHCTRL4); |
| sys_write8(0, SMFI_M1KFLHCTRL5); |
| |
| return ret; |
| } |
| |
| static int m1k_flash_write(const struct device *dev, off_t offset, const void *src_data, size_t len) |
| { |
| int ret; |
| uint16_t count; |
| uint8_t m1kflhctrl7_cmd; |
| |
| /* It's the start address of M1K-PROG */ |
| flash_set_m1k_pe_lba(dev, offset); |
| |
| /* M1K DLM base address */ |
| flash_set_m1k_dlm_ba(dev, (uint32_t)src_data); |
| |
| /* M1k-PROG size (Maximum 1024 bytes) */ |
| count = (len - 1) & M1K_PROG_BCNT_MASK; |
| |
| m1kflhctrl7_cmd = sys_read8(SMFI_M1KFLHCTRL7) & ~GENMASK(1, 0); |
| /* M1K-PROG byte count[9:8] */ |
| sys_write8(m1kflhctrl7_cmd | M1K_PROG_BCNT(FIELD_GET(GENMASK(9, 8), count)), |
| SMFI_M1KFLHCTRL7); |
| /* M1K-PROG byte count[7:0] */ |
| sys_write8(FIELD_GET(GENMASK(7, 0), count), SMFI_M1KFLHCTRL6); |
| |
| m1kflhctrl7_cmd = sys_read8(SMFI_M1KFLHCTRL7) & ~GENMASK(7, 6); |
| /* Copy byte count data from the DLM to flash */ |
| sys_write8(m1kflhctrl7_cmd | M1K_PE_CMD(M1K_PROG), SMFI_M1KFLHCTRL7); |
| |
| /* Write-1-Start M1K-PROG/M1K-ERASE */ |
| sys_write8(W1S_M1K_PE, SMFI_M1KFLHCTRL1); |
| ret = flash_wait_status(dev, M1K_PE_CYC); |
| |
| /* Reset counter to 0 */ |
| sys_write8(0, SMFI_M1KFLHCTRL6); |
| sys_write8(0, SMFI_M1KFLHCTRL7); |
| |
| return ret; |
| } |
| |
| static int m1k_flash_erase(const struct device *dev, off_t offset, size_t len) |
| { |
| int ret; |
| uint8_t m1kflhctrl7_cmd; |
| |
| /* It's the lower bound address of M1K-ERASE */ |
| flash_set_m1k_pe_lba(dev, offset); |
| |
| /* It's the upper bound address of M1K-ERASE */ |
| flash_set_m1k_erase_uba(dev, offset + FLASH_ERASE_BLK_SZ); |
| |
| m1kflhctrl7_cmd = sys_read8(SMFI_M1KFLHCTRL7) & ~GENMASK(7, 6); |
| /* Erase the flash within an address range */ |
| sys_write8(m1kflhctrl7_cmd | M1K_PE_CMD(M1K_ERASE), SMFI_M1KFLHCTRL7); |
| |
| /* Write-1-Start M1K-ERASE */ |
| sys_write8(W1S_M1K_PE, SMFI_M1KFLHCTRL1); |
| ret = flash_wait_status(dev, M1K_PE_CYC); |
| |
| return ret; |
| } |
| |
| static int m1k_flash_sel_access(const struct device *dev, enum flash_it51xxx_ex_op target_flash) |
| { |
| const struct flash_it51xxx_config *cfg = dev->config; |
| struct flash_it51xxx_dev_data *data = dev->data; |
| int ret; |
| uint8_t flhctrl3r_val; |
| |
| LOG_DBG("%s: Runtime M1K select access flash=%d", __func__, target_flash); |
| |
| /* Save the opcode to device data */ |
| data->flash = target_flash; |
| |
| /* Disable two-flash */ |
| sys_write8(sys_read8(SMFI_FLHCTRL4R) & ~EN2FLH, SMFI_FLHCTRL4R); |
| |
| flhctrl3r_val = sys_read8(SMFI_FLHCTRL3R); |
| if (target_flash != FLASH_IT51XXX_INTERNAL) { |
| /* Enable SPI flash and SPI pins are normal operation */ |
| sys_write8((flhctrl3r_val | SIFE) & ~FFSPITRI, SMFI_FLHCTRL3R); |
| |
| /* M1K-READ will access the SPI flash (FSPI) */ |
| sys_write8(M1K_READ_SEL_FSPI, SMFI_M1K_READ_LBA3); |
| /* M1K-PROG/M1K-ERASE will access the SPI flash (FSPI) */ |
| sys_write8(M1K_PE_SEL_FSPI, SMFI_M1K_PE_LBA3); |
| |
| /* Set the pin to FSPI alternate function. */ |
| ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); |
| if (ret < 0) { |
| LOG_ERR("%s: Failed to configure FSPI pins", dev->name); |
| return ret; |
| } |
| |
| if (target_flash == FLASH_IT51XXX_EXTERNAL_FSPI_CS1) { |
| /* M1K-READ will access the SPI flash on FSCE1# */ |
| sys_write8(sys_read8(SMFI_M1K_READ_LBA3) | M1K_READ_SEL_FSCE1, |
| SMFI_M1K_READ_LBA3); |
| /* M1K-PROG/M1K-ERASE will access the SPI flash on FSCE1# */ |
| sys_write8(sys_read8(SMFI_M1K_PE_LBA3) | M1K_PE_SEL_FSCE1, |
| SMFI_M1K_PE_LBA3); |
| /* Enable two-flash */ |
| sys_write8(sys_read8(SMFI_FLHCTRL4R) | EN2FLH, SMFI_FLHCTRL4R); |
| } |
| } else { |
| /* Use internal flash, the SPI pins should be set to tri-state */ |
| sys_write8((flhctrl3r_val & ~SIFE) | FFSPITRI, SMFI_FLHCTRL3R); |
| |
| /* M1K-READ will access the e-flash */ |
| sys_write8(0, SMFI_M1K_READ_LBA3); |
| /* M1K-PROG/M1K-ERASE will access the e-flash */ |
| sys_write8(0, SMFI_M1K_PE_LBA3); |
| } |
| |
| return 0; |
| } |
| |
| static void m1k_flash_address_mode(const struct device *dev, enum flash_it51xxx_ex_op addr_mode) |
| { |
| struct flash_it51xxx_dev_data *data = dev->data; |
| uint8_t sel_flash; |
| |
| volatile uint8_t ecinddr __unused; |
| |
| LOG_DBG("%s: Addressing mode=%d", __func__, addr_mode); |
| |
| /* Decide whether legacy(24-bit) or external SPI flash(28-bit) addressing */ |
| if (addr_mode == FLASH_IT51XXX_ADDR_4B) { |
| sys_write8(sys_read8(SMFI_FLHCTRL6R) | FSPI28AMEN, SMFI_FLHCTRL6R); |
| } else { |
| sys_write8(sys_read8(SMFI_FLHCTRL6R) & ~FSPI28AMEN, SMFI_FLHCTRL6R); |
| } |
| |
| /* EC-Indirect memory address (SPI or e-flash)*/ |
| sel_flash = data->flash == FLASH_IT51XXX_INTERNAL ? SEL_EFLASH : SEL_SPI; |
| sys_write8(ECINDA31_30(sel_flash), SMFI_ECINDAR3); |
| sys_write8(0, SMFI_ECINDAR2); |
| sys_write8(0, SMFI_ECINDAR1); |
| sys_write8(0, SMFI_ECINDAR0); |
| |
| /* Dummy read memory data*/ |
| ecinddr = sys_read8(SMFI_ECINDDR); |
| } |
| |
| /* Read data from flash */ |
| static int flash_it51xxx_read(const struct device *dev, off_t offset, void *dst_data, size_t len) |
| { |
| struct flash_it51xxx_dev_data *data = dev->data; |
| int ret; |
| uint8_t *dst = (uint8_t *)dst_data; |
| |
| LOG_DBG("%s: offset=%lx, data addr=%p, len=%u", __func__, offset, (uint8_t *)dst_data, len); |
| |
| if (len == 0) { |
| return 0; |
| } |
| |
| if (!is_valid_range(offset, len)) { |
| LOG_ERR("Out of boundaries: FLASH_SIZE=%#x, offset=%#lx, len=%u", FLASH_SIZE, |
| offset, len); |
| return -EINVAL; |
| } |
| |
| k_sem_take(&data->sem, K_FOREVER); |
| |
| /* For M1K_READ, setting 1 will not plus EC image location */ |
| sys_write8(sys_read8(SMFI_M1KFLHCTRL3) | M1KPHY, SMFI_M1KFLHCTRL3); |
| |
| while (len > 0) { |
| size_t read_len = MIN(len, FLASH_READ_MAX_SZ); |
| |
| ret = m1k_flash_read(dev, offset, dst, read_len); |
| if (ret != 0) { |
| LOG_ERR("%s: failed at offset=%#lx", __func__, offset); |
| break; |
| } |
| |
| offset += read_len; |
| dst += read_len; |
| len -= read_len; |
| } |
| |
| /* Reset the M1K setting and counter to 0 */ |
| sys_write8(0, SMFI_M1KFLHCTRL3); |
| sys_write8(0, SMFI_M1KFLHCTRL4); |
| sys_write8(0, SMFI_M1KFLHCTRL5); |
| |
| k_sem_give(&data->sem); |
| |
| return ret; |
| } |
| |
| /* Write data to the flash, page by page */ |
| static int flash_it51xxx_write(const struct device *dev, off_t offset, const void *src_data, |
| size_t len) |
| { |
| struct flash_it51xxx_dev_data *data = dev->data; |
| int ret; |
| const uint8_t *src = (const uint8_t *)src_data; |
| |
| LOG_DBG("%s: offset=%lx, data addr=%p, len=%u", __func__, offset, (const uint8_t *)src_data, |
| len); |
| |
| if (len == 0) { |
| return 0; |
| } |
| |
| if (!is_valid_range(offset, len)) { |
| LOG_ERR("Out of boundaries: FLASH_SIZE=%#x, offset=%#lx, len=%u", FLASH_SIZE, |
| offset, len); |
| return -EINVAL; |
| } |
| |
| k_sem_take(&data->sem, K_FOREVER); |
| |
| while (len > 0) { |
| size_t write_len = MIN(len, FLASH_WRITE_MAX_SZ); |
| |
| ret = m1k_flash_write(dev, offset, src, write_len); |
| if (ret != 0) { |
| LOG_ERR("%s: failed at offset=%#lx", __func__, offset); |
| break; |
| } |
| |
| offset += write_len; |
| src += write_len; |
| len -= write_len; |
| } |
| |
| /* Reset counter to 0 */ |
| sys_write8(0, SMFI_M1KFLHCTRL6); |
| sys_write8(0, SMFI_M1KFLHCTRL7); |
| |
| k_sem_give(&data->sem); |
| |
| return ret; |
| } |
| |
| /* Erase multiple blocks */ |
| static int flash_it51xxx_erase(const struct device *dev, off_t offset, size_t len) |
| { |
| struct flash_it51xxx_dev_data *data = dev->data; |
| int ret; |
| |
| LOG_DBG("%s: offset=%lx, len=%u", __func__, offset, len); |
| |
| if (len == 0) { |
| return 0; |
| } |
| |
| if (!is_valid_range(offset, len)) { |
| LOG_ERR("Out of boundaries: FLASH_SIZE=%#x, offset=%#lx, len=%u", FLASH_SIZE, |
| offset, len); |
| return -EINVAL; |
| } |
| |
| /* Check that the offset and length are multiples of the erase block size */ |
| if ((offset % FLASH_ERASE_BLK_SZ) || (len % FLASH_ERASE_BLK_SZ)) { |
| LOG_ERR("Erase range is not a multiple of the block size. offset=%#lx, len=%u", |
| offset, len); |
| return -EINVAL; |
| } |
| |
| k_sem_take(&data->sem, K_FOREVER); |
| |
| while (len > 0) { |
| ret = m1k_flash_erase(dev, offset, len); |
| if (ret != 0) { |
| LOG_ERR("%s: failed at offset=%#lx", __func__, offset); |
| break; |
| } |
| |
| offset += FLASH_ERASE_BLK_SZ; |
| len -= FLASH_ERASE_BLK_SZ; |
| } |
| |
| k_sem_give(&data->sem); |
| |
| return ret; |
| } |
| |
| static const struct flash_parameters flash_it51xxx_parameters = { |
| .write_block_size = FLASH_WRITE_BLK_SZ, |
| .erase_value = 0xff, |
| }; |
| |
| static const struct flash_parameters *flash_it51xxx_get_parameters(const struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| return &flash_it51xxx_parameters; |
| } |
| |
| #if defined(CONFIG_FLASH_PAGE_LAYOUT) |
| static const struct flash_pages_layout dev_layout = { |
| .pages_count = FLASH_SIZE / FLASH_ERASE_BLK_SZ, |
| .pages_size = FLASH_ERASE_BLK_SZ, |
| }; |
| |
| static void flash_it51xxx_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 */ |
| |
| #ifdef CONFIG_FLASH_EX_OP_ENABLED |
| static int flash_it51xxx_ex_op(const struct device *dev, uint16_t opcode, const uintptr_t in, |
| void *out) |
| { |
| struct flash_it51xxx_dev_data *data = dev->data; |
| int ret = 0; |
| |
| LOG_DBG("%s: Extended operation code=%x", __func__, opcode); |
| |
| k_sem_take(&data->sem, K_FOREVER); |
| |
| switch (opcode) { |
| case FLASH_IT51XXX_INTERNAL: |
| case FLASH_IT51XXX_EXTERNAL_FSPI_CS0: |
| case FLASH_IT51XXX_EXTERNAL_FSPI_CS1: |
| ret = m1k_flash_sel_access(dev, opcode); |
| break; |
| case FLASH_IT51XXX_ADDR_3B: |
| case FLASH_IT51XXX_ADDR_4B: |
| m1k_flash_address_mode(dev, opcode); |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| |
| k_sem_give(&data->sem); |
| |
| return ret; |
| } |
| #endif |
| |
| static DEVICE_API(flash, flash_it51xxx_api) = { |
| .read = flash_it51xxx_read, |
| .write = flash_it51xxx_write, |
| .erase = flash_it51xxx_erase, |
| .get_parameters = flash_it51xxx_get_parameters, |
| #if defined(CONFIG_FLASH_PAGE_LAYOUT) |
| .page_layout = flash_it51xxx_pages_layout, |
| #endif |
| #if defined(CONFIG_FLASH_EX_OP_ENABLED) |
| .ex_op = flash_it51xxx_ex_op, |
| #endif |
| }; |
| |
| static int flash_it51xxx_init(const struct device *dev) |
| { |
| const struct flash_it51xxx_config *cfg = dev->config; |
| struct flash_it51xxx_dev_data *data = dev->data; |
| int ret; |
| |
| LOG_DBG("%s: M1K select access flash=%d", __func__, cfg->target_flash); |
| |
| /* Default to access flash */ |
| ret = m1k_flash_sel_access(dev, cfg->target_flash); |
| /* Default addressing mode */ |
| m1k_flash_address_mode(dev, FLASH_IT51XXX_ADDR_3B); |
| |
| /* Initialize mutex for flash controller */ |
| k_sem_init(&data->sem, 1, 1); |
| |
| return ret; |
| } |
| |
| static struct flash_it51xxx_dev_data flash_it51xxx_data; |
| |
| PINCTRL_DT_INST_DEFINE(0); |
| |
| static const struct flash_it51xxx_config flash_it51xxx_cfg = { |
| .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), |
| .target_flash = DT_INST_ENUM_IDX(0, m1k_sel_access_flash), |
| }; |
| |
| BUILD_ASSERT(!((DT_INST_ENUM_IDX(0, m1k_sel_access_flash) >= FLASH_IT51XXX_EXTERNAL_FSPI_CS0) && |
| !DT_INST_NODE_HAS_PROP(0, pinctrl_0)), |
| "Access external-fspi-cs0/cs1, pinctrl must be configured."); |
| |
| DEVICE_DT_INST_DEFINE(0, flash_it51xxx_init, NULL, &flash_it51xxx_data, &flash_it51xxx_cfg, |
| PRE_KERNEL_2, CONFIG_FLASH_INIT_PRIORITY, &flash_it51xxx_api); |