knx/source/ti/drivers/nvs/NVSSPI25X.c
Nanosonde e51b65f8c2 Squashed 'examples/knx-cc1310/coresdk_cc13xx_cc26xx/' content from commit 0d78d32
git-subtree-dir: examples/knx-cc1310/coresdk_cc13xx_cc26xx
git-subtree-split: 0d78d3280357416a5c0388148cda13717c9ffaa5
2020-10-21 10:00:49 +02:00

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