From bd5edb6775b698802234bacca48739b539e37c96 Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Mon, 9 May 2022 22:29:41 +0200 Subject: [PATCH] drivers: flash: Add driver for smartbond This adds flash driver for Renesas SmartBond(tm) family. This technically uses QSPI controller but since default and most commonly used configuration is to boot from external QSPI flash (DA1469x do not have built-in flash) and that flash is mapped into memory space, it can be represented as internal flash. Signed-off-by: Andrzej Kaczmarek Signed-off-by: Ben Lauret --- drivers/flash/CMakeLists.txt | 1 + drivers/flash/Kconfig | 2 + drivers/flash/Kconfig.smartbond | 12 + drivers/flash/flash_smartbond.c | 295 ++++++++++++++++++ dts/arm/renesas/smartbond/da1469x.dtsi | 20 ++ .../renesas,smartbond-flash-controller.yaml | 8 + 6 files changed, 338 insertions(+) create mode 100644 drivers/flash/Kconfig.smartbond create mode 100644 drivers/flash/flash_smartbond.c create mode 100644 dts/bindings/flash_controller/renesas,smartbond-flash-controller.yaml diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index b61b132454..a65fb59523 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -26,6 +26,7 @@ zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_MX25UM51345G flash_mcux_f zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_NOR flash_mcux_flexspi_nor.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_HYPERFLASH flash_mcux_flexspi_hyperflash.c) zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_ESP32 flash_esp32.c) +zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_SMARTBOND flash_smartbond.c) if(CONFIG_FLASH_MCUX_FLEXSPI_XIP) dt_chosen(chosen_flash PROPERTY "zephyr,flash") diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index 9bf12889fb..f43c7af749 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -104,4 +104,6 @@ source "drivers/flash/Kconfig.rv32m1" source "drivers/flash/Kconfig.nordic_qspi_nor" +source "drivers/flash/Kconfig.smartbond" + endif # FLASH diff --git a/drivers/flash/Kconfig.smartbond b/drivers/flash/Kconfig.smartbond new file mode 100644 index 0000000000..3cc587ba7e --- /dev/null +++ b/drivers/flash/Kconfig.smartbond @@ -0,0 +1,12 @@ +# Copyright (c) 2022 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config SOC_FLASH_SMARTBOND + bool "Renesas SmartBond(tm) flash driver" + default y + depends on DT_HAS_RENESAS_SMARTBOND_FLASH_CONTROLLER_ENABLED + select FLASH_HAS_PAGE_LAYOUT + select FLASH_HAS_DRIVER_ENABLED + select MPU_ALLOW_FLASH_WRITE if ARM_MPU + help + Enable flash driver for Renesas SmartBond(tm) MCU family. diff --git a/drivers/flash/flash_smartbond.c b/drivers/flash/flash_smartbond.c new file mode 100644 index 0000000000..a4733b05c3 --- /dev/null +++ b/drivers/flash/flash_smartbond.c @@ -0,0 +1,295 @@ +/* + * 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 int flash_smartbond_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + return 0; +} + +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, flash_smartbond_init, NULL, NULL, &flash_smartbond_0_config, + POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, &flash_smartbond_driver_api); diff --git a/dts/arm/renesas/smartbond/da1469x.dtsi b/dts/arm/renesas/smartbond/da1469x.dtsi index e0ce744434..deb800b88b 100644 --- a/dts/arm/renesas/smartbond/da1469x.dtsi +++ b/dts/arm/renesas/smartbond/da1469x.dtsi @@ -24,6 +24,26 @@ compatible = "mmio-sram"; }; + qspif: memory@16000000 { + compatible = "zephyr,memory-region"; + reg = <0x16000000 DT_SIZE_K(32768)>; + zephyr,memory-region = "QSPIF"; + }; + + flash_controller: flash-controller@38000000 { + compatible = "renesas,smartbond-flash-controller"; + reg = <0x38000000 0xb0>; + + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "soc-nv-flash"; + erase-block-size = <4096>; + write-block-size = <1>; + }; + }; + pinctrl: pin-controller@50020a00 { compatible = "renesas,smartbond-pinctrl"; reg = <0x50020a00 0x100>; diff --git a/dts/bindings/flash_controller/renesas,smartbond-flash-controller.yaml b/dts/bindings/flash_controller/renesas,smartbond-flash-controller.yaml new file mode 100644 index 0000000000..b914dc016c --- /dev/null +++ b/dts/bindings/flash_controller/renesas,smartbond-flash-controller.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas SmartBond(tm) family flash controller + +compatible: "renesas,smartbond-flash-controller" + +include: flash-controller.yaml