blob: cb57b76fd17b68d55d83521e3c412baac591a030 [file] [log] [blame]
/*
* Copyright (c) 2017, 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/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
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(void);
static int_fast16_t extFlashPowerStandby(void);
static int_fast16_t extFlashWaitReady(void);
static int_fast16_t extFlashWriteEnable(void);
static int_fast16_t extFlashMassErase(void);
static void extFlashSelect(void);
static void extFlashDeselect(void);
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) {
// Put the part in low power mode
extFlashPowerDown();
SPI_close(object->spiHandle);
spiHandles[hwAttrs->spiIndex] = NULL;
}
}
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());
}
/*
* ======== 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 sem;
GPIO_init();
SPI_init();
/* speculatively create a binary semaphore for thread safety */
sem = SemaphoreP_createBinary(1);
/* sem == NULL will be detected in 'open' */
key = HwiP_disable();
if (writeSem == NULL) {
/* use the binary sem created above */
writeSem = sem;
HwiP_restore(key);
}
else {
/* init already called */
HwiP_restore(key);
/* delete unused Semaphore */
if (sem) SemaphoreP_delete(sem);
}
}
/*
* ======== NVSSPI25X_lock =======
*/
int_fast16_t NVSSPI25X_lock(NVS_Handle handle, uint32_t timeout)
{
switch (SemaphoreP_pend(writeSem, timeout)) {
case SemaphoreP_OK:
return (NVS_STATUS_SUCCESS);
case SemaphoreP_TIMEOUT:
return (NVS_STATUS_TIMEOUT);
case SemaphoreP_FAILURE:
default:
return (NVS_STATUS_ERROR);
}
}
/*
* ======== 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 flaah 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;
/*
* 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(hwAttrs->spiCsnGpioIndex, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_HIGH);
object->opened = true;
/* Put the part in standby mode */
extFlashPowerStandby();
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;
size_t xferSize;
uint8_t *dstBuf;
int retval = NVS_STATUS_SUCCESS;
hwAttrs = handle->hwAttrs;
/* Validate offset and bufferSize */
if (offset + bufferSize > hwAttrs->regionSize) {
return (NVS_STATUS_INV_OFFSET);
}
dstBuf = buffer;
/*
* 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);
/*
* break read down into 1024 byte pieces to workaround TIDRIVERS-1173
*/
while (bufferSize) {
if (bufferSize <= 1024) {
xferSize = bufferSize;
}
else {
xferSize = 1024;
}
retval = doRead(handle, offset, dstBuf, xferSize);
if (retval != NVS_STATUS_SUCCESS) {
break;
}
else {
offset += xferSize;
dstBuf += xferSize;
bufferSize -= xferSize;
}
}
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) {
retval = doErase(handle, offset & object->sectorBaseMask,
(bufferSize + hwAttrs->sectorSize) & object->sectorBaseMask);
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();
if (ret) {
status = false;
break;
}
ret = extFlashWriteEnable();
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.
*/
extFlashSelect();
if (extFlashSpiWrite(wbuf, sizeof(wbuf)) != NVS_STATUS_SUCCESS) {
status = false;
break;
}
if (extFlashSpiWrite(srcBuf, ilen) != NVS_STATUS_SUCCESS) {
status = false;
break;
}
srcBuf += ilen;
extFlashDeselect();
}
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 sectorSize;
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;
sectorBase = (uint32_t)hwAttrs->regionBaseOffset + offset;
sectorSize = hwAttrs->sectorSize;
/* assume that 4k sectors use SS ERASE, else Sector Erase */
if (sectorSize == 4096) {
wbuf[0] = SPIFLASH_SUBSECTOR_ERASE;
}
else {
wbuf[0] = SPIFLASH_SECTOR_ERASE;
}
while (size) {
/* Wait till previous erase/program operation completes */
int ret = extFlashWaitReady();
if (ret) {
return (NVS_STATUS_ERROR);
}
ret = extFlashWriteEnable();
if (ret) {
return (NVS_STATUS_ERROR);
}
wbuf[1] = (sectorBase >> 16) & 0xff;
wbuf[2] = (sectorBase >> 8) & 0xff;
wbuf[3] = sectorBase & 0xff;
extFlashSelect();
if (extFlashSpiWrite(wbuf, sizeof(wbuf))) {
/* failure */
extFlashDeselect();
return (NVS_STATUS_ERROR);
}
extFlashDeselect();
sectorBase += sectorSize;
size -= sectorSize;
}
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();
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;
extFlashSelect();
if (extFlashSpiWrite(wbuf, sizeof(wbuf))) {
/* failure */
extFlashDeselect();
return (NVS_STATUS_ERROR);
}
retval = extFlashSpiRead(buffer, bufferSize);
extFlashDeselect();
return (retval);
}
/*
* ======== extFlashSelect =======
* Assert SPI flash /CS
*/
static void extFlashSelect(void)
{
GPIO_write(spiCsnGpioIndex, 0);
}
/*
* ======== extFlashDeselect =======
* De-assert SPI flash /CS
*/
static void extFlashDeselect(void)
{
GPIO_write(spiCsnGpioIndex, 1);
}
/*
* ======== extFlashPowerDown =======
* Issue power down command
*/
static int_fast16_t extFlashPowerDown(void)
{
uint8_t cmd;
int_fast16_t status;
cmd = SPIFLASH_DP;
extFlashSelect();
status = extFlashSpiWrite(&cmd,sizeof(cmd));
extFlashDeselect();
return (status);
}
/*
* ======== extFlashPowerStandby =======
* Issue standby command
*/
static int_fast16_t extFlashPowerStandby(void)
{
uint8_t cmd;
int_fast16_t status;
cmd = SPIFLASH_RDP;
extFlashSelect();
status = extFlashSpiWrite(&cmd, sizeof(cmd));
extFlashDeselect();
if (status == NVS_STATUS_SUCCESS) {
status = extFlashWaitReady();
}
return (status);
}
/*
* ======== extFlashMassErase =======
* Issue mass erase command
*/
static int_fast16_t extFlashMassErase(void)
{
uint8_t cmd;
int_fast16_t status;
/* wait for previous operation to complete */
if (extFlashWaitReady()) {
return (NVS_STATUS_ERROR);
}
cmd = SPIFLASH_MASS_ERASE;
extFlashSelect();
status = extFlashSpiWrite(&cmd,sizeof(cmd));
extFlashDeselect();
if (status != NVS_STATUS_SUCCESS) {
return (status);
}
/* wait for mass erase to complete */
return (extFlashWaitReady());
}
/*
* ======== extFlashWaitReady =======
* Wait for any previous job to complete.
*/
static int_fast16_t extFlashWaitReady(void)
{
const uint8_t wbuf[1] = { SPIFLASH_READ_STATUS };
int_fast16_t ret;
uint8_t buf;
for (;;) {
extFlashSelect();
extFlashSpiWrite(wbuf, sizeof(wbuf));
ret = extFlashSpiRead(&buf,sizeof(buf));
extFlashDeselect();
if (ret != NVS_STATUS_SUCCESS) {
/* Error */
return (ret);
}
if (!(buf & SPIFLASH_STATUS_BIT_BUSY)) {
/* Now ready */
break;
}
}
return (NVS_STATUS_SUCCESS);
}
/*
* ======== extFlashWriteEnable =======
* Issue SPIFLASH_WRITE_ENABLE command
*/
static int_fast16_t extFlashWriteEnable(void)
{
const uint8_t wbuf[] = { SPIFLASH_WRITE_ENABLE };
int_fast16_t ret;
extFlashSelect();
ret = extFlashSpiWrite(wbuf,sizeof(wbuf));
extFlashDeselect();
return (ret);
}
/*
* ======== extFlashSpiWrite =======
*/
static int_fast16_t extFlashSpiWrite(const uint8_t *buf, size_t len)
{
SPI_Transaction masterTransaction;
masterTransaction.count = len;
masterTransaction.txBuf = (void*)buf;
masterTransaction.arg = NULL;
masterTransaction.rxBuf = NULL;
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.count = len;
masterTransaction.txBuf = NULL;
masterTransaction.arg = NULL;
masterTransaction.rxBuf = buf;
return (SPI_transfer(spiHandle, &masterTransaction) ? NVS_STATUS_SUCCESS : NVS_STATUS_ERROR);
}