/* * Copyright (c) 2022 Renesas Electronics Corporation * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT renesas_smartbond_flash_controller #define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash) #define QSPIF_NODE DT_NODELABEL(qspif) #include #include #include #include #include #include #include #include #define FLASH_ERASE_SIZE DT_PROP(SOC_NV_FLASH_NODE, erase_block_size) #define FLASH_PAGE_SIZE 256 struct flash_smartbond_config { uint32_t qspif_base_address; }; static const struct flash_parameters flash_smartbond_parameters = { .write_block_size = DT_PROP(SOC_NV_FLASH_NODE, write_block_size), .erase_value = 0xff, }; static bool range_is_valid(off_t offset, uint32_t size) { return (offset + size) <= (CONFIG_FLASH_SIZE * 1024); } static ALWAYS_INLINE void qspic_data_write8(uint8_t data) { volatile uint8_t *reg8 = (uint8_t *)&QSPIC->QSPIC_WRITEDATA_REG; *reg8 = data; } static ALWAYS_INLINE void qspic_data_write32(uint32_t data) { volatile uint32_t *reg32 = (uint32_t *)&QSPIC->QSPIC_WRITEDATA_REG; *reg32 = data; } static ALWAYS_INLINE uint8_t qspic_data_read8(void) { volatile uint8_t *reg8 = (uint8_t *)&QSPIC->QSPIC_READDATA_REG; return *reg8; } static __ramfunc uint8_t qspic_read_status(void) { uint8_t status; QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_EN_CS_Msk; qspic_data_write8(0x05); status = qspic_data_read8(); QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_DIS_CS_Msk; return status; } static __ramfunc void qspic_wait_busy(void) { do { } while (qspic_read_status() & 0x01); } static __ramfunc void qspic_automode_exit(void) { QSPIC->QSPIC_CTRLMODE_REG &= ~QSPIC_QSPIC_CTRLMODE_REG_QSPIC_AUTO_MD_Msk; QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_SET_SINGLE_Msk; QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_EN_CS_Msk; qspic_data_write8(0xff); qspic_data_write8(0xff); QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_DIS_CS_Msk; } static __ramfunc void qspic_write_enable(void) { uint8_t status; do { QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_EN_CS_Msk; qspic_data_write8(0x06); QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_DIS_CS_Msk; do { status = qspic_read_status(); } while (status & 0x01); } while (!(status & 0x02)); } static __ramfunc size_t qspic_write_page(uint32_t address, const uint8_t *data, size_t size) { size_t written; /* Make sure we write up to page boundary */ size = MIN(size, FLASH_PAGE_SIZE - (address & (FLASH_PAGE_SIZE - 1))); written = size; QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_EN_CS_Msk; address = sys_cpu_to_be32(address); qspic_data_write32(address | 0x02); while (size >= 4) { qspic_data_write32(*(uint32_t *) data); data += 4; size -= 4; } while (size) { qspic_data_write8(*data); data++; size--; } QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_DIS_CS_Msk; return written; } static __ramfunc void qspic_write(uint32_t address, const uint8_t *data, size_t size) { size_t written; while (size) { qspic_write_enable(); written = qspic_write_page(address, data, size); address += written; data += written; size -= written; qspic_wait_busy(); } } static int flash_smartbond_read(const struct device *dev, off_t offset, void *data, size_t size) { const struct flash_smartbond_config *config = dev->config; if (!range_is_valid(offset, size)) { return -EINVAL; } if (!size) { return 0; } memcpy(data, (uint8_t *)(config->qspif_base_address + offset), size); return 0; } static __ramfunc int flash_smartbond_write(const struct device *dev, off_t offset, const void *data, size_t size) { unsigned int key; uint32_t ctrlmode; if (!range_is_valid(offset, size)) { return -EINVAL; } if (!size) { return 0; } key = irq_lock(); ctrlmode = QSPIC->QSPIC_CTRLMODE_REG; qspic_automode_exit(); qspic_wait_busy(); qspic_write(offset, data, size); QSPIC->QSPIC_CTRLMODE_REG = ctrlmode; CACHE->CACHE_CTRL1_REG |= CACHE_CACHE_CTRL1_REG_CACHE_FLUSH_Msk; irq_unlock(key); return 0; } static __ramfunc int flash_smartbond_erase(const struct device *dev, off_t offset, size_t size) { unsigned int key; uint32_t ctrlmode; uint32_t address; if (!range_is_valid(offset, size)) { return -EINVAL; } if ((offset % FLASH_ERASE_SIZE) != 0) { return -EINVAL; } if ((size % FLASH_ERASE_SIZE) != 0) { return -EINVAL; } if (!size) { return 0; } key = irq_lock(); ctrlmode = QSPIC->QSPIC_CTRLMODE_REG; qspic_automode_exit(); qspic_wait_busy(); while (size) { qspic_write_enable(); QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_EN_CS_Msk; address = sys_cpu_to_be32(offset); qspic_data_write32(address | 0x20); QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_DIS_CS_Msk; qspic_wait_busy(); offset += FLASH_ERASE_SIZE; size -= FLASH_ERASE_SIZE; } QSPIC->QSPIC_CTRLMODE_REG = ctrlmode; CACHE->CACHE_CTRL1_REG |= CACHE_CACHE_CTRL1_REG_CACHE_FLUSH_Msk; irq_unlock(key); return 0; } static const struct flash_parameters * flash_smartbond_get_parameters(const struct device *dev) { ARG_UNUSED(dev); return &flash_smartbond_parameters; } #if CONFIG_FLASH_PAGE_LAYOUT static const struct flash_pages_layout flash_smartbond_0_pages_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), }; void flash_smartbond_page_layout(const struct device *dev, const struct flash_pages_layout **layout, size_t *layout_size) { *layout = &flash_smartbond_0_pages_layout; *layout_size = 1; } #endif /* CONFIG_FLASH_PAGE_LAYOUT */ static const struct flash_driver_api flash_smartbond_driver_api = { .read = flash_smartbond_read, .write = flash_smartbond_write, .erase = flash_smartbond_erase, .get_parameters = flash_smartbond_get_parameters, #ifdef CONFIG_FLASH_PAGE_LAYOUT .page_layout = flash_smartbond_page_layout, #endif }; static const struct flash_smartbond_config flash_smartbond_0_config = { .qspif_base_address = DT_REG_ADDR(QSPIF_NODE), }; DEVICE_DT_INST_DEFINE(0, NULL, NULL, NULL, &flash_smartbond_0_config, POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, &flash_smartbond_driver_api);