/* * 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 #include #include #include #include #include #include #include #include #include #include #include /* 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); } }