Skip to content

Commit

Permalink
drivers: flash: introduce on-chip flash driver for TI CC13xx/CC26xx
Browse files Browse the repository at this point in the history
This includes a basic driver for built-in flash on the Texas Intruments
SimpleLink CC13xx/CC26xx SoC series.

The driver makes use of driverlib HAL from TI's SDK and was tested on
two LaunchXL development boards with CC1352R and CC2652R SoCs:

- CC1352R1 LaunchXL
- CC26x2R1 LaunchXL

Tests were done using:

- flash shell sample (samples/drivers/flash_shell)
- littlefs filesystem sample (samples/subsys/fs/littlefs)*
- MCUboot (bootloader/mcuboot/boot/zephyr)*

  * additional changes in DTS for the boards were required (partitions
    table) and are not part of this changeset (will be introduced later)

Some additional information about the implementation:

1. TI's Technical Reference Manual for CC13x2 and CC26x2 points out that
   "An individual 64-bit word can be programmed to change bits 1 to 0"
   but it seems this 'alignment' requirement is handled internally by
   the ROM function and thus 'write-block-size' is set to 1.

2. Interrupts, VIMS and line buffers are disabled during flash content
   update (write or erase) and restored afterwards as recommended by TI.

3. Only RAM to flash write is supported (source of data to be written to
   flash can't point to flash).

4. The driver doesn't take care of flash sector protection disable as
   that functionality is handled by CCFG. Write or erase requests which
   refer to a protected area will fail.

Signed-off-by: Piotr Dymacz <pepe2k@gmail.com>
  • Loading branch information
pepe2k authored and mbolivar-nordic committed Jun 6, 2022
1 parent cd66c9f commit 360d70a
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 0 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions drivers/flash/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions drivers/flash/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
12 changes: 12 additions & 0 deletions drivers/flash/Kconfig.cc13xx_cc26xx
Original file line number Diff line number Diff line change
@@ -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.
276 changes: 276 additions & 0 deletions drivers/flash/soc_flash_cc13xx_cc26xx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
/*
* Copyright (c) 2022 Piotr Dymacz
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/device.h>
#include <zephyr/drivers/flash.h>
#include <string.h>

#include <driverlib/flash.h>
#include <driverlib/vims.h>

#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);

0 comments on commit 360d70a

Please sign in to comment.