| /* |
| * 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. |
| */ |
| |
| /* |
| * ======== NVSRAM.c ======== |
| */ |
| |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include <ti/drivers/dpl/HwiP.h> |
| #include <ti/drivers/dpl/SemaphoreP.h> |
| |
| #include <ti/drivers/NVS.h> |
| #include <ti/drivers/nvs/NVSRAM.h> |
| |
| 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); |
| |
| extern NVS_Config NVS_config[]; |
| extern const uint8_t NVS_count; |
| |
| /* NVS function table for NVSRAM implementation */ |
| const NVS_FxnTable NVSRAM_fxnTable = { |
| NVSRAM_close, |
| NVSRAM_control, |
| NVSRAM_erase, |
| NVSRAM_getAttrs, |
| NVSRAM_init, |
| NVSRAM_lock, |
| NVSRAM_open, |
| NVSRAM_read, |
| NVSRAM_unlock, |
| NVSRAM_write |
| }; |
| |
| /* |
| * Semaphore to synchronize access to the region. |
| */ |
| static SemaphoreP_Handle writeSem; |
| |
| /* |
| * ======== NVSRAM_close ======== |
| */ |
| void NVSRAM_close(NVS_Handle handle) |
| { |
| ((NVSRAM_Object *) handle->object)->isOpen = false; |
| } |
| |
| /* |
| * ======== NVSRAM_control ======== |
| */ |
| int_fast16_t NVSRAM_control(NVS_Handle handle, uint_fast16_t cmd, |
| uintptr_t arg) |
| { |
| return (NVS_STATUS_UNDEFINEDCMD); |
| } |
| |
| /* |
| * ======== NVSRAM_erase ======== |
| */ |
| int_fast16_t NVSRAM_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); |
| } |
| |
| /* |
| * ======== NVSRAM_getAttrs ======== |
| */ |
| void NVSRAM_getAttrs(NVS_Handle handle, NVS_Attrs *attrs) |
| { |
| NVSRAM_HWAttrs const *hwAttrs = handle->hwAttrs; |
| |
| attrs->regionBase = hwAttrs->regionBase; |
| attrs->regionSize = hwAttrs->regionSize; |
| attrs->sectorSize = hwAttrs->sectorSize; |
| } |
| |
| /* |
| * ======== NVSRAM_init ======== |
| */ |
| void NVSRAM_init() |
| { |
| uintptr_t key; |
| SemaphoreP_Handle sem; |
| |
| /* 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); |
| } |
| } |
| } |
| |
| /* |
| * ======== NVSRAM_lock ======= |
| */ |
| int_fast16_t NVSRAM_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); |
| } |
| } |
| |
| /* |
| * ======== NVSRAM_open ======= |
| */ |
| NVS_Handle NVSRAM_open(uint_least8_t index, NVS_Params *params) |
| { |
| NVS_Handle handle; |
| NVSRAM_Object *object; |
| NVSRAM_HWAttrs const *hwAttrs; |
| |
| /* Confirm that 'init' has successfully completed */ |
| if (writeSem == NULL) { |
| NVSRAM_init(); |
| if (writeSem == NULL) { |
| return (NULL); |
| } |
| } |
| |
| /* verify NVS region index */ |
| if (index >= NVS_count) { |
| return (NULL); |
| } |
| |
| handle = &NVS_config[index]; |
| object = NVS_config[index].object; |
| hwAttrs = NVS_config[index].hwAttrs; |
| |
| /* for efficient argument checking */ |
| object->sectorBaseMask = ~(hwAttrs->sectorSize - 1); |
| |
| SemaphoreP_pend(writeSem, SemaphoreP_WAIT_FOREVER); |
| |
| if (object->isOpen) { |
| SemaphoreP_post(writeSem); |
| |
| return (NULL); |
| } |
| |
| /* The regionBase must be aligned on a page boundary */ |
| if ((size_t) (hwAttrs->regionBase) & (hwAttrs->sectorSize - 1)) { |
| SemaphoreP_post(writeSem); |
| |
| return (NULL); |
| } |
| |
| /* The region cannot be smaller than a sector size */ |
| if (hwAttrs->regionSize < hwAttrs->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); |
| } |
| |
| object->isOpen = true; |
| |
| SemaphoreP_post(writeSem); |
| |
| return (handle); |
| } |
| |
| /* |
| * ======== NVSRAM_read ======= |
| */ |
| int_fast16_t NVSRAM_read(NVS_Handle handle, size_t offset, void *buffer, |
| size_t bufferSize) |
| { |
| NVSRAM_HWAttrs const *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); |
| |
| memcpy(buffer, (char *)(hwAttrs->regionBase) + offset, bufferSize); |
| |
| SemaphoreP_post(writeSem); |
| |
| return (NVS_STATUS_SUCCESS); |
| } |
| |
| /* |
| * ======== NVSRAM_unlock ======= |
| */ |
| void NVSRAM_unlock(NVS_Handle handle) |
| { |
| SemaphoreP_post(writeSem); |
| } |
| |
| /* |
| * ======== NVSRAM_write ======= |
| */ |
| int_fast16_t NVSRAM_write(NVS_Handle handle, size_t offset, void *buffer, |
| size_t bufferSize, uint_fast16_t flags) |
| { |
| size_t i; |
| uint8_t *dstBuf; |
| uint8_t *srcBuf; |
| int_fast16_t result; |
| NVSRAM_Object *object = handle->object; |
| NVSRAM_HWAttrs const *hwAttrs = handle->hwAttrs; |
| |
| /* 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); |
| |
| /* If erase is set, erase destination sector(s) first */ |
| if (flags & NVS_WRITE_ERASE) { |
| result = doErase(handle, offset & object->sectorBaseMask, |
| (bufferSize + hwAttrs->sectorSize) & object->sectorBaseMask); |
| if (result != NVS_STATUS_SUCCESS) { |
| SemaphoreP_post(writeSem); |
| |
| return (result); |
| } |
| } |
| else if (flags & NVS_WRITE_PRE_VERIFY) { |
| /* |
| * If pre-verify, each destination byte must be able to be changed to the |
| * source byte (1s to 0s, not 0s to 1s). |
| * this is satisfied by the following test: |
| * src == (src & dst) |
| */ |
| dstBuf = (uint8_t *)((uint32_t)(hwAttrs->regionBase) + offset); |
| srcBuf = buffer; |
| for (i = 0; i < bufferSize; i++) { |
| if (srcBuf[i] != (srcBuf[i] & dstBuf[i])) { |
| SemaphoreP_post(writeSem); |
| return (NVS_STATUS_INV_WRITE); |
| } |
| } |
| } |
| |
| dstBuf = (uint8_t *)((uint32_t)(hwAttrs->regionBase) + offset); |
| srcBuf = buffer; |
| memcpy((void *) dstBuf, (void *) srcBuf, bufferSize); |
| |
| SemaphoreP_post(writeSem); |
| |
| return (NVS_STATUS_SUCCESS); |
| } |
| |
| /* |
| * ======== checkEraseRange ======== |
| */ |
| static int_fast16_t checkEraseRange(NVS_Handle handle, size_t offset, size_t size) |
| { |
| NVSRAM_Object *object = handle->object; |
| NVSRAM_HWAttrs const *hwAttrs = handle->hwAttrs; |
| |
| if (offset != (offset & object->sectorBaseMask)) { |
| /* poorly aligned start address */ |
| return (NVS_STATUS_INV_ALIGNMENT); |
| } |
| |
| if (offset >= hwAttrs->regionSize) { |
| /* offset is past end of region */ |
| return (NVS_STATUS_INV_OFFSET); |
| } |
| |
| if (offset + size > hwAttrs->regionSize) { |
| /* size is too big */ |
| return (NVS_STATUS_INV_SIZE); |
| } |
| |
| if (size != (size & object->sectorBaseMask)) { |
| /* size is not a multiple of sector size */ |
| return (NVS_STATUS_INV_SIZE); |
| } |
| |
| return (NVS_STATUS_SUCCESS); |
| } |
| |
| /* |
| * ======== doErase ======== |
| */ |
| static int_fast16_t doErase(NVS_Handle handle, size_t offset, size_t size) |
| { |
| void * sectorBase; |
| int_fast16_t rangeStatus; |
| NVSRAM_HWAttrs const *hwAttrs = handle->hwAttrs; |
| |
| /* sanity test the erase args */ |
| rangeStatus = checkEraseRange(handle, offset, size); |
| if (rangeStatus != NVS_STATUS_SUCCESS) { |
| return (rangeStatus); |
| } |
| |
| sectorBase = (void *) ((uint32_t) hwAttrs->regionBase + offset); |
| |
| memset(sectorBase, 0xFF, size); |
| |
| return (NVS_STATUS_SUCCESS); |
| } |