-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drivers: flash: introduce on-chip flash driver for TI CC13xx/CC26xx
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
1 parent
cd66c9f
commit 360d70a
Showing
5 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |