diff --git a/CODEOWNERS b/CODEOWNERS index ff5e41ae188..0763bd9daee 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -270,6 +270,7 @@ /drivers/flash/ @nashif @nvlsianpu /drivers/flash/*stm32_qspi* @lmajewski /drivers/flash/*b91* @yurvyn +/drivers/flash/*cc13xx_cc26xx* @pepe2k /drivers/flash/*nrf* @nvlsianpu /drivers/flash/*esp32* @glaubermaroto /drivers/fpga/ @tgorochowik @kgugala diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index b7b03746342..b61b1324547 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -2,6 +2,7 @@ zephyr_library() +zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_CC13XX_CC26XX soc_flash_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_TELINK_B91 soc_flash_b91.c) zephyr_library_sources_ifdef(CONFIG_SPI_NOR spi_nor.c) zephyr_library_sources_ifdef(CONFIG_NORDIC_QSPI_NOR nrf_qspi_nor.c) diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index e53144c76b2..9bf12889fb0 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -68,6 +68,8 @@ config FLASH_INIT_PRIORITY source "drivers/flash/Kconfig.b91" +source "drivers/flash/Kconfig.cc13xx_cc26xx" + source "drivers/flash/Kconfig.at45" source "drivers/flash/Kconfig.esp32" diff --git a/drivers/flash/Kconfig.cc13xx_cc26xx b/drivers/flash/Kconfig.cc13xx_cc26xx new file mode 100644 index 00000000000..3710ddf47d3 --- /dev/null +++ b/drivers/flash/Kconfig.cc13xx_cc26xx @@ -0,0 +1,12 @@ +# Copyright (c) 2022 Piotr Dymacz +# SPDX-License-Identifier: Apache-2.0 + +config SOC_FLASH_CC13XX_CC26XX + bool "TI SimpleLink CC13xx/CC26xx flash controller driver" + depends on SOC_SERIES_CC13X2_CC26X2 + select FLASH_HAS_PAGE_LAYOUT + select FLASH_HAS_DRIVER_ENABLED + select MPU_ALLOW_FLASH_WRITE if ARM_MPU + default y + help + Enables TI SimpleLink CC13xx/CC26xx flash controller driver. diff --git a/drivers/flash/soc_flash_cc13xx_cc26xx.c b/drivers/flash/soc_flash_cc13xx_cc26xx.c new file mode 100644 index 00000000000..dfb30897407 --- /dev/null +++ b/drivers/flash/soc_flash_cc13xx_cc26xx.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2022 Piotr Dymacz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include + +#define DT_DRV_COMPAT ti_cc13xx_cc26xx_flash_controller +#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash) + +#define FLASH_ADDR DT_REG_ADDR(SOC_NV_FLASH_NODE) +#define FLASH_SIZE DT_REG_SIZE(SOC_NV_FLASH_NODE) +#define FLASH_ERASE_SIZE DT_PROP(SOC_NV_FLASH_NODE, erase_block_size) +#define FLASH_WRITE_SIZE DT_PROP(SOC_NV_FLASH_NODE, write_block_size) + + +struct flash_priv { + struct k_sem mutex; +}; + +static const struct flash_parameters flash_cc13xx_cc26xx_parameters = { + .write_block_size = FLASH_WRITE_SIZE, + .erase_value = 0xff, +}; + + +static int flash_cc13xx_cc26xx_init(const struct device *dev) +{ + struct flash_priv *priv = dev->data; + + k_sem_init(&priv->mutex, 1, 1); + + return 0; +} + +static void flash_cc13xx_cc26xx_cache_restore(uint32_t vims_mode) +{ + while (VIMSModeGet(VIMS_BASE) == VIMS_MODE_CHANGING) + ; + + /* Restore VIMS mode and line buffers */ + if (vims_mode != VIMS_MODE_DISABLED) { + VIMSModeSafeSet(VIMS_BASE, vims_mode, true); + } + + VIMSLineBufEnable(VIMS_BASE); +} + +static uint32_t flash_cc13xx_cc26xx_cache_disable(void) +{ + uint32_t vims_mode; + + /* VIMS and both line buffers should be off during flash update */ + VIMSLineBufDisable(VIMS_BASE); + + while (VIMSModeGet(VIMS_BASE) == VIMS_MODE_CHANGING) + ; + + /* Save current VIMS mode for restoring it later */ + vims_mode = VIMSModeGet(VIMS_BASE); + if (vims_mode != VIMS_MODE_DISABLED) { + VIMSModeSafeSet(VIMS_BASE, VIMS_MODE_DISABLED, true); + } + + return vims_mode; +} + +static bool flash_cc13xx_cc26xx_range_protected(off_t offs, size_t size) +{ + off_t sector, end; + + sector = (offs / FLASH_ERASE_SIZE) * FLASH_ERASE_SIZE; + end = offs + size; + + /* + * From TI's HAL 'driverlib/flash.h': + * + * After write protecting a sector this sector can only be set back + * to unprotected by a device reset. + * + * Return early if any of sectors from requested range is protected. + */ + do { + if (FlashProtectionGet(sector) == FLASH_WRITE_PROTECT) { + return true; + } + + sector += FLASH_ERASE_SIZE; + } while (sector < end); + + return false; +} + +static int flash_cc13xx_cc26xx_erase(const struct device *dev, off_t offs, + size_t size) +{ + struct flash_priv *priv = dev->data; + uint32_t vims_mode; + unsigned int key; + int i, rc = 0; + size_t cnt; + + if (!size) { + return 0; + } + + /* Offset and length should be multiple of erase size */ + if (((offs % FLASH_ERASE_SIZE) != 0) || + ((size % FLASH_ERASE_SIZE) != 0)) { + return -EINVAL; + } + + if (flash_cc13xx_cc26xx_range_protected(offs, size)) { + return -EINVAL; + } + + if (k_sem_take(&priv->mutex, K_FOREVER)) { + return -EACCES; + } + + vims_mode = flash_cc13xx_cc26xx_cache_disable(); + + /* + * Disable all interrupts to prevent flash read, from TI's TRF: + * + * During a FLASH memory write or erase operation, the FLASH memory + * must not be read. + */ + key = irq_lock(); + + /* Erase sector/page one by one, break out in case of an error */ + cnt = size / FLASH_ERASE_SIZE; + for (i = 0; i < cnt; i++, offs += FLASH_ERASE_SIZE) { + while (FlashCheckFsmForReady() != FAPI_STATUS_FSM_READY) + ; + + rc = FlashSectorErase(offs); + if (rc != FAPI_STATUS_SUCCESS) { + rc = -EIO; + break; + } + } + + irq_unlock(key); + + flash_cc13xx_cc26xx_cache_restore(vims_mode); + + k_sem_give(&priv->mutex); + + return rc; +} + +static int flash_cc13xx_cc26xx_write(const struct device *dev, off_t offs, + const void *data, size_t size) +{ + struct flash_priv *priv = dev->data; + uint32_t vims_mode; + unsigned int key; + int rc = 0; + + if (!size) { + return 0; + } + + if ((offs < 0) || (size < 1)) { + return -EINVAL; + } + + if ((offs + size) > FLASH_SIZE) { + return -EINVAL; + } + + /* + * From TI's HAL 'driverlib/flash.h': + * + * The pui8DataBuffer pointer can not point to flash. + */ + if ((data >= (void *)FLASH_ADDR) && + (data <= (void *)(FLASH_ADDR + FLASH_SIZE))) { + return -EINVAL; + } + + if (flash_cc13xx_cc26xx_range_protected(offs, size)) { + return -EINVAL; + } + + if (k_sem_take(&priv->mutex, K_FOREVER)) { + return -EACCES; + } + + vims_mode = flash_cc13xx_cc26xx_cache_disable(); + + key = irq_lock(); + + while (FlashCheckFsmForReady() != FAPI_STATUS_FSM_READY) + ; + rc = FlashProgram((uint8_t *)data, offs, size); + if (rc != FAPI_STATUS_SUCCESS) { + rc = -EIO; + } + + irq_unlock(key); + + flash_cc13xx_cc26xx_cache_restore(vims_mode); + + k_sem_give(&priv->mutex); + + return rc; +} + +static int flash_cc13xx_cc26xx_read(const struct device *dev, off_t offs, + void *data, size_t size) +{ + ARG_UNUSED(dev); + + if (!size) { + return 0; + } + + if ((offs < 0) || (size < 1)) { + return -EINVAL; + } + + if ((offs + size) > FLASH_SIZE) { + return -EINVAL; + } + + memcpy(data, (void *)offs, size); + + return 0; +} + +static const struct flash_parameters * +flash_cc13xx_cc26xx_get_parameters(const struct device *dev) +{ + ARG_UNUSED(dev); + + return &flash_cc13xx_cc26xx_parameters; +} + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +static const struct flash_pages_layout dev_layout = { + .pages_count = FLASH_SIZE / FLASH_ERASE_SIZE, + .pages_size = FLASH_ERASE_SIZE, +}; + +static void flash_cc13xx_cc26xx_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_cc13xx_cc26xx_api = { + .erase = flash_cc13xx_cc26xx_erase, + .write = flash_cc13xx_cc26xx_write, + .read = flash_cc13xx_cc26xx_read, + .get_parameters = flash_cc13xx_cc26xx_get_parameters, +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + .page_layout = flash_cc13xx_cc26xx_layout, +#endif +}; + +static struct flash_priv flash_data; + +DEVICE_DT_INST_DEFINE(0, flash_cc13xx_cc26xx_init, NULL, &flash_data, NULL, + POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, + &flash_cc13xx_cc26xx_api);