|  | /* | 
|  | * 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 <stddef.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/drivers/flash.h> | 
|  | #include <zephyr/sys/byteorder.h> | 
|  | #include <DA1469xAB.h> | 
|  |  | 
|  | #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 DEVICE_API(flash, 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); |