mirror of
https://github.com/thelsing/knx.git
synced 2025-10-12 11:15:54 +02:00
git-subtree-dir: examples/knx-cc1310/coresdk_cc13xx_cc26xx git-subtree-split: 0d78d3280357416a5c0388148cda13717c9ffaa5
977 lines
26 KiB
C
977 lines
26 KiB
C
/*
|
|
* Copyright (c) 2017-2019, Texas Instruments Incorporated
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* * Neither the name of Texas Instruments Incorporated nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* ======== NVSSPI25X.c ========
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <ti/drivers/dpl/HwiP.h>
|
|
#include <ti/drivers/dpl/SemaphoreP.h>
|
|
#include <ti/drivers/dpl/ClockP.h>
|
|
|
|
#include <ti/drivers/NVS.h>
|
|
#include <ti/drivers/nvs/NVSSPI25X.h>
|
|
|
|
#include <ti/drivers/SPI.h>
|
|
#include <ti/drivers/GPIO.h>
|
|
|
|
/* Instruction codes */
|
|
#define SPIFLASH_WRITE 0x02 /**< Page Program */
|
|
#define SPIFLASH_READ 0x03 /**< Read Data */
|
|
#define SPIFLASH_READ_STATUS 0x05 /**< Read Status Register */
|
|
#define SPIFLASH_WRITE_ENABLE 0x06 /**< Write Enable */
|
|
#define SPIFLASH_SUBSECTOR_ERASE 0x20 /**< SubSector (4K Byte) Erase */
|
|
#define SPIFLASH_SECTOR_ERASE 0xD8 /**< Sector (64K Byte) Erase */
|
|
#define SPIFLASH_MASS_ERASE 0xC7 /**< Erase entire flash */
|
|
|
|
#define SPIFLASH_RDP 0xAB /**< Release from Deep Power Down */
|
|
#define SPIFLASH_DP 0xB9 /**< Deep Power Down */
|
|
|
|
/* Bitmasks of the status register */
|
|
#define SPIFLASH_STATUS_BIT_BUSY 0x01 /**< Busy bit of status register */
|
|
|
|
/* Write page size assumed by this driver */
|
|
#define SPIFLASH_PROGRAM_PAGE_SIZE 256
|
|
|
|
/* Highest supported SPI instance index */
|
|
#define MAX_SPI_INDEX 3
|
|
|
|
/* Size of hardware sector erased by SPIFLASH_SECTOR_ERASE */
|
|
#define SPIFLASH_SECTOR_SIZE 0x10000
|
|
|
|
static int_fast16_t checkEraseRange(NVS_Handle handle, size_t offset, size_t size);
|
|
static int_fast16_t doErase(NVS_Handle handle, size_t offset, size_t size);
|
|
static int_fast16_t doRead(NVS_Handle handle, size_t offset, void *buffer,
|
|
size_t bufferSize);
|
|
static int_fast16_t doWriteVerify(NVS_Handle handle, size_t offset,
|
|
void *src, size_t srcBufSize, void *dst,
|
|
size_t dstBufSize, bool preFlag);
|
|
|
|
static int_fast16_t extFlashSpiWrite(const uint8_t *buf, size_t len);
|
|
static int_fast16_t extFlashSpiRead(uint8_t *buf, size_t len);
|
|
static int_fast16_t extFlashPowerDown(NVS_Handle nvsHandle);
|
|
static int_fast16_t extFlashPowerStandby(NVS_Handle nvsHandle);
|
|
static int_fast16_t extFlashWaitReady(NVS_Handle nvsHandle);
|
|
static int_fast16_t extFlashWriteEnable(NVS_Handle nvsHandle);
|
|
static int_fast16_t extFlashMassErase(NVS_Handle nvsHandle);
|
|
|
|
extern NVS_Config NVS_config[];
|
|
extern const uint8_t NVS_count;
|
|
|
|
/* NVS function table for NVSSPI25X implementation */
|
|
const NVS_FxnTable NVSSPI25X_fxnTable = {
|
|
NVSSPI25X_close,
|
|
NVSSPI25X_control,
|
|
NVSSPI25X_erase,
|
|
NVSSPI25X_getAttrs,
|
|
NVSSPI25X_init,
|
|
NVSSPI25X_lock,
|
|
NVSSPI25X_open,
|
|
NVSSPI25X_read,
|
|
NVSSPI25X_unlock,
|
|
NVSSPI25X_write
|
|
};
|
|
|
|
/* Manage SPI indexes */
|
|
static SPI_Handle spiHandles[MAX_SPI_INDEX + 1];
|
|
static uint8_t spiHandleUsers[MAX_SPI_INDEX + 1];
|
|
|
|
/*
|
|
* Currently active (protected within Semaphore_pend() block)
|
|
* SPI handle, and CSN pin
|
|
*/
|
|
static SPI_Handle spiHandle;
|
|
static uint32_t spiCsnGpioIndex;
|
|
|
|
/*
|
|
* Semaphore to synchronize access to flash region.
|
|
*/
|
|
static SemaphoreP_Handle writeSem;
|
|
|
|
/*
|
|
* ======== NVSSPI25X_close ========
|
|
*/
|
|
void NVSSPI25X_close(NVS_Handle handle)
|
|
{
|
|
NVSSPI25X_HWAttrs const *hwAttrs;
|
|
NVSSPI25X_Object *object;
|
|
|
|
SemaphoreP_pend(writeSem, SemaphoreP_WAIT_FOREVER);
|
|
|
|
hwAttrs = handle->hwAttrs;
|
|
object = handle->object;
|
|
|
|
spiHandle = object->spiHandle;
|
|
spiCsnGpioIndex = hwAttrs->spiCsnGpioIndex;
|
|
|
|
/* Close the SPI if we opened it */
|
|
if (hwAttrs->spiHandle == NULL) {
|
|
spiHandleUsers[hwAttrs->spiIndex] -= 1;
|
|
|
|
/* Close SPI if this is the last region that uses it */
|
|
if (spiHandleUsers[hwAttrs->spiIndex] == 0) {
|
|
/* Ensure part is responsive */
|
|
extFlashWaitReady(handle);
|
|
|
|
/* Put the part in low power mode */
|
|
extFlashPowerDown(handle);
|
|
|
|
SPI_close(object->spiHandle);
|
|
spiHandles[hwAttrs->spiIndex] = NULL;
|
|
}
|
|
}
|
|
|
|
NVSSPI25X_deinitSpiCs(handle, spiCsnGpioIndex);
|
|
|
|
object->opened = false;
|
|
|
|
SemaphoreP_post(writeSem);
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_control ========
|
|
*/
|
|
int_fast16_t NVSSPI25X_control(NVS_Handle handle, uint_fast16_t cmd, uintptr_t arg)
|
|
{
|
|
NVSSPI25X_HWAttrs const *hwAttrs;
|
|
NVSSPI25X_Object *object;
|
|
|
|
if (cmd != NVSSPI25X_CMD_MASS_ERASE) return (NVS_STATUS_UNDEFINEDCMD);
|
|
|
|
hwAttrs = handle->hwAttrs;
|
|
object = handle->object;
|
|
|
|
/* Set protected global variables */
|
|
spiHandle = object->spiHandle;
|
|
spiCsnGpioIndex = hwAttrs->spiCsnGpioIndex;
|
|
|
|
return (extFlashMassErase(handle));
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_erase ========
|
|
*/
|
|
int_fast16_t NVSSPI25X_erase(NVS_Handle handle, size_t offset, size_t size)
|
|
{
|
|
int_fast16_t status;
|
|
|
|
SemaphoreP_pend(writeSem, SemaphoreP_WAIT_FOREVER);
|
|
|
|
status = doErase(handle, offset, size);
|
|
|
|
SemaphoreP_post(writeSem);
|
|
|
|
return (status);
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_getAttrs ========
|
|
*/
|
|
void NVSSPI25X_getAttrs(NVS_Handle handle, NVS_Attrs *attrs)
|
|
{
|
|
NVSSPI25X_HWAttrs const *hwAttrs;
|
|
|
|
hwAttrs = handle->hwAttrs;
|
|
|
|
/* FlashSectorSizeGet() returns the size of a flash sector in bytes. */
|
|
attrs->regionBase = NVS_REGION_NOT_ADDRESSABLE;
|
|
attrs->regionSize = hwAttrs->regionSize;
|
|
attrs->sectorSize = hwAttrs->sectorSize;
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_init ========
|
|
*/
|
|
void NVSSPI25X_init()
|
|
{
|
|
unsigned int key;
|
|
SemaphoreP_Handle tempSem;
|
|
|
|
SPI_init();
|
|
|
|
/* Speculatively create semaphore so critical section is faster */
|
|
tempSem = SemaphoreP_createBinary(1);
|
|
/* tempSem == NULL will be detected in 'open' */
|
|
|
|
key = HwiP_disable();
|
|
|
|
if (writeSem == NULL) {
|
|
/* First time init, assign handle */
|
|
writeSem = tempSem;
|
|
|
|
HwiP_restore(key);
|
|
}
|
|
else {
|
|
/* Init already called */
|
|
HwiP_restore(key);
|
|
|
|
/* Delete unused Semaphores */
|
|
if (tempSem) {
|
|
SemaphoreP_delete(tempSem);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_lock =======
|
|
*/
|
|
int_fast16_t NVSSPI25X_lock(NVS_Handle handle, uint32_t timeout)
|
|
{
|
|
if (SemaphoreP_pend(writeSem, timeout) != SemaphoreP_OK) {
|
|
return (NVS_STATUS_TIMEOUT);
|
|
}
|
|
return (NVS_STATUS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_open =======
|
|
*/
|
|
NVS_Handle NVSSPI25X_open(uint_least8_t index, NVS_Params *params)
|
|
{
|
|
NVSSPI25X_Object *object;
|
|
NVSSPI25X_HWAttrs const *hwAttrs;
|
|
size_t sectorSize;
|
|
NVS_Handle handle;
|
|
SPI_Params spiParams;
|
|
|
|
/* Confirm that 'init' has successfully completed */
|
|
if (writeSem == NULL) {
|
|
NVSSPI25X_init();
|
|
if (writeSem == NULL) {
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
/* Verify NVS region index */
|
|
if (index >= NVS_count) {
|
|
return (NULL);
|
|
}
|
|
|
|
SemaphoreP_pend(writeSem, SemaphoreP_WAIT_FOREVER);
|
|
|
|
handle = &NVS_config[index];
|
|
object = NVS_config[index].object;
|
|
hwAttrs = NVS_config[index].hwAttrs;
|
|
|
|
if (object->opened == true) {
|
|
SemaphoreP_post(writeSem);
|
|
return (NULL);
|
|
}
|
|
|
|
sectorSize = hwAttrs->sectorSize;
|
|
object->sectorBaseMask = ~(sectorSize - 1);
|
|
|
|
/* The regionBase must be aligned on a flash page boundary */
|
|
if ((hwAttrs->regionBaseOffset) & (sectorSize - 1)) {
|
|
SemaphoreP_post(writeSem);
|
|
return (NULL);
|
|
}
|
|
|
|
/* The region cannot be smaller than a sector size */
|
|
if (hwAttrs->regionSize < sectorSize) {
|
|
SemaphoreP_post(writeSem);
|
|
return (NULL);
|
|
}
|
|
|
|
/* The region size must be a multiple of sector size */
|
|
if (hwAttrs->regionSize != (hwAttrs->regionSize & object->sectorBaseMask)) {
|
|
SemaphoreP_post(writeSem);
|
|
return (NULL);
|
|
}
|
|
|
|
if (hwAttrs->spiHandle) {
|
|
/* Use the provided SPI Handle */
|
|
object->spiHandle = *hwAttrs->spiHandle;
|
|
}
|
|
else {
|
|
if (hwAttrs->spiIndex > MAX_SPI_INDEX) {
|
|
SemaphoreP_post(writeSem);
|
|
return (NULL);
|
|
}
|
|
/* Open SPI if this driver hasn't already opened this SPI instance */
|
|
if (spiHandles[hwAttrs->spiIndex] == NULL) {
|
|
SPI_Handle spi;
|
|
|
|
SPI_Params_init(&spiParams);
|
|
spiParams.bitRate = hwAttrs->spiBitRate;
|
|
spiParams.mode = SPI_MASTER;
|
|
spiParams.transferMode = SPI_MODE_BLOCKING;
|
|
|
|
/* Attempt to open SPI. */
|
|
spi = SPI_open(hwAttrs->spiIndex, &spiParams);
|
|
|
|
if (spi == NULL) {
|
|
SemaphoreP_post(writeSem);
|
|
return (NULL);
|
|
}
|
|
|
|
spiHandles[hwAttrs->spiIndex] = spi;
|
|
}
|
|
object->spiHandle = spiHandles[hwAttrs->spiIndex];
|
|
/* Keep track of how many regions use the same SPI handle */
|
|
spiHandleUsers[hwAttrs->spiIndex] += 1;
|
|
}
|
|
|
|
/* Set protected global variables */
|
|
spiHandle = object->spiHandle;
|
|
spiCsnGpioIndex = hwAttrs->spiCsnGpioIndex;
|
|
|
|
|
|
/* Initialize chip select output */
|
|
NVSSPI25X_initSpiCs(handle, spiCsnGpioIndex);
|
|
|
|
object->opened = true;
|
|
|
|
/* Put the part in standby mode */
|
|
extFlashPowerStandby(handle);
|
|
|
|
SemaphoreP_post(writeSem);
|
|
|
|
return (handle);
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_read =======
|
|
*/
|
|
int_fast16_t NVSSPI25X_read(NVS_Handle handle, size_t offset, void *buffer,
|
|
size_t bufferSize)
|
|
{
|
|
NVSSPI25X_HWAttrs const *hwAttrs;
|
|
int retval = NVS_STATUS_SUCCESS;
|
|
|
|
hwAttrs = handle->hwAttrs;
|
|
|
|
/* Validate offset and bufferSize */
|
|
if (offset + bufferSize > hwAttrs->regionSize) {
|
|
return (NVS_STATUS_INV_OFFSET);
|
|
}
|
|
|
|
/*
|
|
* Get exclusive access to the region. We don't want someone
|
|
* else to erase the region while we are reading it.
|
|
*/
|
|
SemaphoreP_pend(writeSem, SemaphoreP_WAIT_FOREVER);
|
|
|
|
retval = doRead(handle, offset, buffer, bufferSize);
|
|
|
|
SemaphoreP_post(writeSem);
|
|
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_unlock =======
|
|
*/
|
|
void NVSSPI25X_unlock(NVS_Handle handle)
|
|
{
|
|
SemaphoreP_post(writeSem);
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_write =======
|
|
*/
|
|
int_fast16_t NVSSPI25X_write(NVS_Handle handle, size_t offset, void *buffer,
|
|
size_t bufferSize, uint_fast16_t flags)
|
|
{
|
|
NVSSPI25X_Object *object;
|
|
NVSSPI25X_HWAttrs const *hwAttrs;
|
|
size_t length, foffset;
|
|
uint32_t status = true;
|
|
uint8_t *srcBuf;
|
|
int retval = NVS_STATUS_SUCCESS;
|
|
uint8_t wbuf[4];
|
|
|
|
hwAttrs = handle->hwAttrs;
|
|
object = handle->object;
|
|
|
|
/* Validate offset and bufferSize */
|
|
if (offset + bufferSize > hwAttrs->regionSize) {
|
|
return (NVS_STATUS_INV_OFFSET);
|
|
}
|
|
|
|
/* Get exclusive access to the Flash region */
|
|
SemaphoreP_pend(writeSem, SemaphoreP_WAIT_FOREVER);
|
|
|
|
/* Set protected global variables */
|
|
spiHandle = object->spiHandle;
|
|
spiCsnGpioIndex = hwAttrs->spiCsnGpioIndex;
|
|
|
|
/* If erase is set, erase destination sector(s) first */
|
|
if (flags & NVS_WRITE_ERASE) {
|
|
length = bufferSize & object->sectorBaseMask;
|
|
if (bufferSize & (~object->sectorBaseMask)) {
|
|
length += hwAttrs->sectorSize;
|
|
}
|
|
|
|
retval = doErase(handle, offset & object->sectorBaseMask, length);
|
|
if (retval != NVS_STATUS_SUCCESS) {
|
|
SemaphoreP_post(writeSem);
|
|
return (retval);
|
|
}
|
|
}
|
|
else if (flags & NVS_WRITE_PRE_VERIFY) {
|
|
if ((hwAttrs->verifyBuf == NULL) || (hwAttrs->verifyBufSize == 0)) {
|
|
SemaphoreP_post(writeSem);
|
|
return (NVS_STATUS_ERROR);
|
|
}
|
|
|
|
retval = doWriteVerify(handle, offset, buffer, bufferSize,
|
|
hwAttrs->verifyBuf, hwAttrs->verifyBufSize, true);
|
|
|
|
if (retval != NVS_STATUS_SUCCESS) {
|
|
SemaphoreP_post(writeSem);
|
|
return (retval);
|
|
}
|
|
}
|
|
|
|
srcBuf = buffer;
|
|
length = bufferSize;
|
|
foffset = (size_t)hwAttrs->regionBaseOffset + offset;
|
|
|
|
while (length > 0)
|
|
{
|
|
size_t ilen; /* Interim length per instruction */
|
|
|
|
/* Wait till previous erase/program operation completes */
|
|
int ret = extFlashWaitReady(handle);
|
|
|
|
if (ret) {
|
|
status = false;
|
|
break;
|
|
}
|
|
|
|
ret = extFlashWriteEnable(handle);
|
|
|
|
if (ret) {
|
|
status = false;
|
|
break;
|
|
}
|
|
|
|
ilen = SPIFLASH_PROGRAM_PAGE_SIZE - (foffset % SPIFLASH_PROGRAM_PAGE_SIZE);
|
|
if (length < ilen) {
|
|
ilen = length;
|
|
}
|
|
|
|
wbuf[0] = SPIFLASH_WRITE;
|
|
wbuf[1] = (foffset >> 16) & 0xff;
|
|
wbuf[2] = (foffset >> 8) & 0xff;
|
|
wbuf[3] = foffset & 0xff;
|
|
|
|
foffset += ilen;
|
|
length -= ilen;
|
|
|
|
/*
|
|
* Up to 100ns CS hold time (which is not clear
|
|
* whether it's application only in between reads)
|
|
* is not imposed here since above instructions
|
|
* should be enough to delay
|
|
* as much.
|
|
*/
|
|
NVSSPI25X_assertSpiCs(handle, spiCsnGpioIndex);
|
|
|
|
if (extFlashSpiWrite(wbuf, sizeof(wbuf)) != NVS_STATUS_SUCCESS) {
|
|
status = false;
|
|
break;
|
|
}
|
|
|
|
if (extFlashSpiWrite(srcBuf, ilen) != NVS_STATUS_SUCCESS) {
|
|
status = false;
|
|
break;
|
|
}
|
|
|
|
srcBuf += ilen;
|
|
NVSSPI25X_deassertSpiCs(handle, spiCsnGpioIndex);
|
|
}
|
|
|
|
if (status == false) {
|
|
retval = NVS_STATUS_ERROR;
|
|
}
|
|
else if (flags & NVS_WRITE_POST_VERIFY) {
|
|
if ((hwAttrs->verifyBuf == NULL) || (hwAttrs->verifyBufSize == 0)) {
|
|
SemaphoreP_post(writeSem);
|
|
return (NVS_STATUS_ERROR);
|
|
}
|
|
|
|
retval = doWriteVerify(handle, offset, buffer, bufferSize,
|
|
hwAttrs->verifyBuf, hwAttrs->verifyBufSize, false);
|
|
}
|
|
|
|
SemaphoreP_post(writeSem);
|
|
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* ======== doWriteVerify =======
|
|
*/
|
|
static int_fast16_t doWriteVerify(NVS_Handle handle, size_t offset, void *src,
|
|
size_t srcBufSize, void *dst, size_t dstBufSize, bool preFlag)
|
|
{
|
|
size_t i, j;
|
|
uint8_t *srcBuf, *dstBuf;
|
|
bool bad;
|
|
int_fast16_t retval;
|
|
|
|
srcBuf = src;
|
|
dstBuf = dst;
|
|
|
|
j = dstBufSize;
|
|
|
|
for (i = 0; i < srcBufSize; i++, j++) {
|
|
if (j == dstBufSize) {
|
|
retval = doRead(handle, offset + i, dstBuf, j);
|
|
if (retval != NVS_STATUS_SUCCESS) {
|
|
break;
|
|
}
|
|
j = 0;
|
|
}
|
|
if (preFlag) {
|
|
bad = srcBuf[i] != (srcBuf[i] & dstBuf[j]);
|
|
}
|
|
else {
|
|
bad = srcBuf[i] != dstBuf[j];
|
|
}
|
|
if (bad) return (NVS_STATUS_INV_WRITE);
|
|
}
|
|
return (NVS_STATUS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* ======== checkEraseRange ========
|
|
*/
|
|
static int_fast16_t checkEraseRange(NVS_Handle handle, size_t offset, size_t size)
|
|
{
|
|
NVSSPI25X_Object *object;
|
|
NVSSPI25X_HWAttrs const *hwAttrs;
|
|
|
|
object = handle->object;
|
|
hwAttrs = handle->hwAttrs;
|
|
|
|
if (offset != (offset & object->sectorBaseMask)) {
|
|
return (NVS_STATUS_INV_ALIGNMENT); /* Poorly aligned start address */
|
|
}
|
|
|
|
if (offset >= hwAttrs->regionSize) {
|
|
return (NVS_STATUS_INV_OFFSET); /* Offset is past end of region */
|
|
}
|
|
|
|
if (offset + size > hwAttrs->regionSize) {
|
|
return (NVS_STATUS_INV_SIZE); /* Size is too big */
|
|
}
|
|
|
|
if (size != (size & object->sectorBaseMask)) {
|
|
return (NVS_STATUS_INV_SIZE); /* Size is not a multiple of sector size */
|
|
}
|
|
|
|
return (NVS_STATUS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* ======== doErase ========
|
|
*/
|
|
static int_fast16_t doErase(NVS_Handle handle, size_t offset, size_t size)
|
|
{
|
|
NVSSPI25X_HWAttrs const *hwAttrs;
|
|
NVSSPI25X_Object *object;
|
|
uint32_t sectorBase;
|
|
size_t eraseSize;
|
|
int_fast16_t rangeStatus;
|
|
uint8_t wbuf[4];
|
|
|
|
/* Sanity test the erase args */
|
|
rangeStatus = checkEraseRange(handle, offset, size);
|
|
|
|
if (rangeStatus != NVS_STATUS_SUCCESS) {
|
|
return (rangeStatus);
|
|
}
|
|
|
|
hwAttrs = handle->hwAttrs;
|
|
object = handle->object;
|
|
|
|
/* Set protected global variables */
|
|
spiHandle = object->spiHandle;
|
|
spiCsnGpioIndex = hwAttrs->spiCsnGpioIndex;
|
|
|
|
/* Start erase at this address */
|
|
sectorBase = (uint32_t)hwAttrs->regionBaseOffset + offset;
|
|
|
|
while (size) {
|
|
/* Wait till previous erase/program operation completes */
|
|
int ret = extFlashWaitReady(handle);
|
|
if (ret) {
|
|
return (NVS_STATUS_ERROR);
|
|
}
|
|
|
|
ret = extFlashWriteEnable(handle);
|
|
if (ret) {
|
|
return (NVS_STATUS_ERROR);
|
|
}
|
|
|
|
|
|
/* Determine which erase command to use */
|
|
if (size >= SPIFLASH_SECTOR_SIZE &&
|
|
((sectorBase & (SPIFLASH_SECTOR_SIZE - 1)) == 0)){
|
|
/* Erase size is one sector (64kB) */
|
|
eraseSize = SPIFLASH_SECTOR_SIZE;
|
|
wbuf[0] = SPIFLASH_SECTOR_ERASE;
|
|
}
|
|
else{
|
|
/* Erase size is one sub-sector (4kB)*/
|
|
eraseSize = hwAttrs->sectorSize;
|
|
wbuf[0] = SPIFLASH_SUBSECTOR_ERASE;
|
|
}
|
|
|
|
|
|
/* Format command to send over SPI */
|
|
wbuf[1] = (sectorBase >> 16) & 0xff;
|
|
wbuf[2] = (sectorBase >> 8) & 0xff;
|
|
wbuf[3] = sectorBase & 0xff;
|
|
|
|
/* Send erase command to external flash */
|
|
NVSSPI25X_assertSpiCs(handle, spiCsnGpioIndex);
|
|
if (extFlashSpiWrite(wbuf, sizeof(wbuf))) {
|
|
NVSSPI25X_deassertSpiCs(handle, spiCsnGpioIndex);
|
|
return (NVS_STATUS_ERROR);
|
|
}
|
|
NVSSPI25X_deassertSpiCs(handle, spiCsnGpioIndex);
|
|
|
|
sectorBase += eraseSize;
|
|
size -= eraseSize;
|
|
}
|
|
|
|
return (NVS_STATUS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* ======== doRead =======
|
|
*/
|
|
static int_fast16_t doRead(NVS_Handle handle, size_t offset, void *buffer,
|
|
size_t bufferSize)
|
|
{
|
|
NVSSPI25X_Object *object;
|
|
NVSSPI25X_HWAttrs const *hwAttrs;
|
|
size_t loffset;
|
|
uint8_t wbuf[4];
|
|
int retval = NVS_STATUS_SUCCESS;
|
|
|
|
hwAttrs = handle->hwAttrs;
|
|
object = handle->object;
|
|
|
|
/* Set protected global variables */
|
|
spiHandle = object->spiHandle;
|
|
spiCsnGpioIndex = hwAttrs->spiCsnGpioIndex;
|
|
|
|
loffset = offset + hwAttrs->regionBaseOffset;
|
|
|
|
/* Wait till previous erase/program operation completes */
|
|
retval = extFlashWaitReady(handle);
|
|
if (retval) {
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* SPI is driven with very low frequency (1MHz < 33MHz fR spec)
|
|
* in this temporary implementation.
|
|
* and hence it is not necessary to use fast read.
|
|
*/
|
|
wbuf[0] = SPIFLASH_READ;
|
|
wbuf[1] = (loffset >> 16) & 0xff;
|
|
wbuf[2] = (loffset >> 8) & 0xff;
|
|
wbuf[3] = loffset & 0xff;
|
|
|
|
NVSSPI25X_assertSpiCs(handle, spiCsnGpioIndex);
|
|
|
|
if (extFlashSpiWrite(wbuf, sizeof(wbuf))) {
|
|
NVSSPI25X_deassertSpiCs(handle, spiCsnGpioIndex);
|
|
return (NVS_STATUS_ERROR);
|
|
}
|
|
|
|
retval = extFlashSpiRead(buffer, bufferSize);
|
|
|
|
NVSSPI25X_deassertSpiCs(handle, spiCsnGpioIndex);
|
|
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* ======== extFlashPowerDown =======
|
|
* Issue power down command
|
|
*/
|
|
static int_fast16_t extFlashPowerDown(NVS_Handle nvsHandle)
|
|
{
|
|
uint8_t cmd;
|
|
int_fast16_t status;
|
|
|
|
cmd = SPIFLASH_DP;
|
|
NVSSPI25X_assertSpiCs(nvsHandle, spiCsnGpioIndex);
|
|
status = extFlashSpiWrite(&cmd,sizeof(cmd));
|
|
NVSSPI25X_deassertSpiCs(nvsHandle, spiCsnGpioIndex);
|
|
|
|
return (status);
|
|
}
|
|
|
|
/*
|
|
* ======== extFlashPowerStandby =======
|
|
* Issue standby command
|
|
*/
|
|
static int_fast16_t extFlashPowerStandby(NVS_Handle nvsHandle)
|
|
{
|
|
uint8_t cmd;
|
|
int_fast16_t status;
|
|
|
|
cmd = SPIFLASH_RDP;
|
|
NVSSPI25X_assertSpiCs(nvsHandle, spiCsnGpioIndex);
|
|
status = extFlashSpiWrite(&cmd, sizeof(cmd));
|
|
NVSSPI25X_deassertSpiCs(nvsHandle, spiCsnGpioIndex);
|
|
|
|
if (status == NVS_STATUS_SUCCESS) {
|
|
status = extFlashWaitReady(nvsHandle);
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
/*
|
|
* ======== extFlashMassErase =======
|
|
* Issue mass erase command
|
|
*/
|
|
static int_fast16_t extFlashMassErase(NVS_Handle nvsHandle)
|
|
{
|
|
uint8_t cmd;
|
|
int_fast16_t status;
|
|
|
|
/* Wait for previous operation to complete */
|
|
if (extFlashWaitReady(nvsHandle)) {
|
|
return (NVS_STATUS_ERROR);
|
|
}
|
|
|
|
cmd = SPIFLASH_MASS_ERASE;
|
|
NVSSPI25X_assertSpiCs(nvsHandle, spiCsnGpioIndex);
|
|
status = extFlashSpiWrite(&cmd,sizeof(cmd));
|
|
NVSSPI25X_deassertSpiCs(nvsHandle, spiCsnGpioIndex);
|
|
|
|
if (status != NVS_STATUS_SUCCESS) {
|
|
return (status);
|
|
}
|
|
|
|
/* Wait for mass erase to complete */
|
|
return (extFlashWaitReady(nvsHandle));
|
|
}
|
|
|
|
/*
|
|
* ======== extFlashWaitReady =======
|
|
* Wait for any previous job to complete.
|
|
*/
|
|
static int_fast16_t extFlashWaitReady(NVS_Handle nvsHandle)
|
|
{
|
|
const uint8_t wbuf[1] = { SPIFLASH_READ_STATUS };
|
|
int_fast16_t ret;
|
|
uint8_t buf;
|
|
|
|
NVSSPI25X_HWAttrs const *hwAttrs;
|
|
hwAttrs = nvsHandle->hwAttrs;
|
|
|
|
for (;;) {
|
|
NVSSPI25X_assertSpiCs(nvsHandle, spiCsnGpioIndex);
|
|
extFlashSpiWrite(wbuf, sizeof(wbuf));
|
|
ret = extFlashSpiRead(&buf,sizeof(buf));
|
|
NVSSPI25X_deassertSpiCs(nvsHandle, spiCsnGpioIndex);
|
|
|
|
if (ret != NVS_STATUS_SUCCESS) {
|
|
/* Error */
|
|
return (ret);
|
|
}
|
|
if (!(buf & SPIFLASH_STATUS_BIT_BUSY)) {
|
|
/* Now ready */
|
|
break;
|
|
}
|
|
if (hwAttrs->statusPollDelayUs){
|
|
/* Sleep to avoid excessive polling and starvation */
|
|
ClockP_usleep(hwAttrs->statusPollDelayUs);
|
|
}
|
|
}
|
|
|
|
return (NVS_STATUS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* ======== extFlashWriteEnable =======
|
|
* Issue SPIFLASH_WRITE_ENABLE command
|
|
*/
|
|
static int_fast16_t extFlashWriteEnable(NVS_Handle nvsHandle)
|
|
{
|
|
const uint8_t wbuf[] = { SPIFLASH_WRITE_ENABLE };
|
|
int_fast16_t ret;
|
|
|
|
NVSSPI25X_assertSpiCs(nvsHandle, spiCsnGpioIndex);
|
|
ret = extFlashSpiWrite(wbuf,sizeof(wbuf));
|
|
NVSSPI25X_deassertSpiCs(nvsHandle, spiCsnGpioIndex);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* ======== extFlashSpiWrite =======
|
|
*/
|
|
static int_fast16_t extFlashSpiWrite(const uint8_t *buf, size_t len)
|
|
{
|
|
SPI_Transaction masterTransaction;
|
|
|
|
masterTransaction.rxBuf = NULL;
|
|
|
|
/*
|
|
* Work around SPI transfer from address 0x0
|
|
* transfer first byte from local buffer
|
|
*/
|
|
if (buf == NULL) {
|
|
uint8_t byte0;
|
|
byte0 = *buf++;
|
|
masterTransaction.count = 1;
|
|
masterTransaction.txBuf = (void*)&byte0;
|
|
if (!SPI_transfer(spiHandle, &masterTransaction)) {
|
|
return (NVS_STATUS_ERROR);
|
|
}
|
|
len = len - 1;
|
|
if (len == 0) {
|
|
return (NVS_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
masterTransaction.count = len;
|
|
masterTransaction.txBuf = (void*)buf;
|
|
|
|
return (SPI_transfer(spiHandle, &masterTransaction) ? NVS_STATUS_SUCCESS : NVS_STATUS_ERROR);
|
|
}
|
|
|
|
|
|
/*
|
|
* ======== extFlashSpiRead =======
|
|
*/
|
|
static int_fast16_t extFlashSpiRead(uint8_t *buf, size_t len)
|
|
{
|
|
SPI_Transaction masterTransaction;
|
|
|
|
masterTransaction.txBuf = NULL;
|
|
masterTransaction.count = len;
|
|
masterTransaction.rxBuf = buf;
|
|
|
|
return (SPI_transfer(spiHandle, &masterTransaction) ? NVS_STATUS_SUCCESS : NVS_STATUS_ERROR);
|
|
}
|
|
|
|
/*
|
|
* Below are the default (weak) GPIO-driver based implementations of:
|
|
* NVSSPI25X_initSpiCs()
|
|
* NVSSPI25X_deinitSpiCs()
|
|
* NVSSPI25X_assertSpiCs()
|
|
* NVSSPI25X_deassertSpiCs()
|
|
*/
|
|
|
|
/*
|
|
* ======== NVSSPI25X_initSpiCs =======
|
|
*/
|
|
#if defined(__IAR_SYSTEMS_ICC__)
|
|
__weak void NVSSPI25X_initSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#elif defined(__GNUC__) && !defined(__ti__)
|
|
void __attribute__((weak)) NVSSPI25X_initSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#else
|
|
#pragma WEAK (NVSSPI25X_initSpiCs)
|
|
void NVSSPI25X_initSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#endif
|
|
{
|
|
if (csId != NVSSPI25X_SPI_MANAGES_CS) {
|
|
GPIO_init();
|
|
|
|
/*
|
|
* Make SPI Chip Select GPIO an output, and set it high.
|
|
* Since the same device may be used for multiple regions, configuring
|
|
* the same Chip Select pin may be done multiple times. No harm done.
|
|
*/
|
|
GPIO_setConfig(csId, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_HIGH);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_deinitSpiCs =======
|
|
*/
|
|
#if defined(__IAR_SYSTEMS_ICC__)
|
|
__weak void NVSSPI25X_deinitSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#elif defined(__GNUC__) && !defined(__ti__)
|
|
void __attribute__((weak)) NVSSPI25X_deinitSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#else
|
|
#pragma WEAK (NVSSPI25X_deinitSpiCs)
|
|
void NVSSPI25X_deinitSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_assertSpiCs =======
|
|
* Assert SPI flash /CS
|
|
*/
|
|
#if defined(__IAR_SYSTEMS_ICC__)
|
|
__weak void NVSSPI25X_assertSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#elif defined(__GNUC__) && !defined(__ti__)
|
|
void __attribute__((weak)) NVSSPI25X_assertSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#else
|
|
#pragma WEAK (NVSSPI25X_assertSpiCs)
|
|
void NVSSPI25X_assertSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#endif
|
|
{
|
|
if (csId != NVSSPI25X_SPI_MANAGES_CS) {
|
|
GPIO_write(csId, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======== NVSSPI25X_deassertSpiCs =======
|
|
* De-assert SPI flash /CS
|
|
*/
|
|
#if defined(__IAR_SYSTEMS_ICC__)
|
|
__weak void NVSSPI25X_deassertSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#elif defined(__GNUC__) && !defined(__ti__)
|
|
void __attribute__((weak)) NVSSPI25X_deassertSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#else
|
|
#pragma WEAK (NVSSPI25X_deassertSpiCs)
|
|
void NVSSPI25X_deassertSpiCs(NVS_Handle nvsHandle, uint16_t csId)
|
|
#endif
|
|
{
|
|
if (csId != NVSSPI25X_SPI_MANAGES_CS) {
|
|
GPIO_write(csId, 1);
|
|
}
|
|
}
|