1 /*
2  * Copyright (c) 2017, Texas Instruments Incorporated
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * *  Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * *  Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * *  Neither the name of Texas Instruments Incorporated nor the names of
17  *    its contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  *  ======== NVSRAM.c ========
35  */
36 
37 #include <stdbool.h>
38 #include <stdint.h>
39 #include <string.h>
40 
41 #include <ti/drivers/dpl/HwiP.h>
42 #include <ti/drivers/dpl/SemaphoreP.h>
43 
44 #include <ti/drivers/NVS.h>
45 #include <ti/drivers/nvs/NVSRAM.h>
46 
47 static int_fast16_t checkEraseRange(NVS_Handle handle, size_t offset, size_t size);
48 static int_fast16_t doErase(NVS_Handle handle, size_t offset, size_t size);
49 
50 extern NVS_Config NVS_config[];
51 extern const uint8_t NVS_count;
52 
53 /* NVS function table for NVSRAM implementation */
54 const NVS_FxnTable NVSRAM_fxnTable = {
55     NVSRAM_close,
56     NVSRAM_control,
57     NVSRAM_erase,
58     NVSRAM_getAttrs,
59     NVSRAM_init,
60     NVSRAM_lock,
61     NVSRAM_open,
62     NVSRAM_read,
63     NVSRAM_unlock,
64     NVSRAM_write
65 };
66 
67 /*
68  *  Semaphore to synchronize access to the region.
69  */
70 static SemaphoreP_Handle  writeSem;
71 
72 /*
73  *  ======== NVSRAM_close ========
74  */
NVSRAM_close(NVS_Handle handle)75 void NVSRAM_close(NVS_Handle handle)
76 {
77     ((NVSRAM_Object *) handle->object)->isOpen = false;
78 }
79 
80 /*
81  *  ======== NVSRAM_control ========
82  */
NVSRAM_control(NVS_Handle handle,uint_fast16_t cmd,uintptr_t arg)83 int_fast16_t NVSRAM_control(NVS_Handle handle, uint_fast16_t cmd,
84     uintptr_t arg)
85 {
86     return (NVS_STATUS_UNDEFINEDCMD);
87 }
88 
89 /*
90  *  ======== NVSRAM_erase ========
91  */
NVSRAM_erase(NVS_Handle handle,size_t offset,size_t size)92 int_fast16_t NVSRAM_erase(NVS_Handle handle, size_t offset, size_t size)
93 {
94     int_fast16_t status;
95 
96     SemaphoreP_pend(writeSem, SemaphoreP_WAIT_FOREVER);
97 
98     status = doErase(handle, offset, size);
99 
100     SemaphoreP_post(writeSem);
101 
102     return (status);
103 }
104 
105 /*
106  *  ======== NVSRAM_getAttrs ========
107  */
NVSRAM_getAttrs(NVS_Handle handle,NVS_Attrs * attrs)108 void NVSRAM_getAttrs(NVS_Handle handle, NVS_Attrs *attrs)
109 {
110     NVSRAM_HWAttrs const *hwAttrs = handle->hwAttrs;
111 
112     attrs->regionBase = hwAttrs->regionBase;
113     attrs->regionSize = hwAttrs->regionSize;
114     attrs->sectorSize = hwAttrs->sectorSize;
115 }
116 
117 /*
118  *  ======== NVSRAM_init ========
119  */
NVSRAM_init()120 void NVSRAM_init()
121 {
122     uintptr_t         key;
123     SemaphoreP_Handle sem;
124 
125     /* speculatively create a binary semaphore for thread safety */
126     sem = SemaphoreP_createBinary(1);
127     /* sem == NULL will be detected in 'open' */
128 
129     key = HwiP_disable();
130 
131     if (writeSem == NULL) {
132         /* use the binary sem created above */
133         writeSem = sem;
134         HwiP_restore(key);
135     }
136     else {
137         /* init already called */
138         HwiP_restore(key);
139 
140         /* delete unused Semaphore */
141         if (sem) {
142             SemaphoreP_delete(sem);
143         }
144     }
145 }
146 
147 /*
148  *  ======== NVSRAM_lock =======
149  */
NVSRAM_lock(NVS_Handle handle,uint32_t timeout)150 int_fast16_t NVSRAM_lock(NVS_Handle handle, uint32_t timeout)
151 {
152     if (SemaphoreP_pend(writeSem, timeout) != SemaphoreP_OK) {
153         return (NVS_STATUS_TIMEOUT);
154     }
155     return (NVS_STATUS_SUCCESS);
156 }
157 
158 /*
159  *  ======== NVSRAM_open =======
160  */
NVSRAM_open(uint_least8_t index,NVS_Params * params)161 NVS_Handle NVSRAM_open(uint_least8_t index, NVS_Params *params)
162 {
163     NVS_Handle            handle;
164     NVSRAM_Object        *object;
165     NVSRAM_HWAttrs const *hwAttrs;
166 
167     /* Confirm that 'init' has successfully completed */
168     if (writeSem == NULL) {
169         NVSRAM_init();
170         if (writeSem == NULL) {
171             return (NULL);
172         }
173     }
174 
175     /* verify NVS region index */
176     if (index >= NVS_count) {
177         return (NULL);
178     }
179 
180     handle = &NVS_config[index];
181     object = NVS_config[index].object;
182     hwAttrs = NVS_config[index].hwAttrs;
183 
184     /* for efficient argument checking */
185     object->sectorBaseMask = ~(hwAttrs->sectorSize - 1);
186 
187     SemaphoreP_pend(writeSem, SemaphoreP_WAIT_FOREVER);
188 
189     if (object->isOpen) {
190         SemaphoreP_post(writeSem);
191 
192         return (NULL);
193     }
194 
195     /* The regionBase must be aligned on a page boundary */
196     if ((size_t) (hwAttrs->regionBase) & (hwAttrs->sectorSize - 1)) {
197         SemaphoreP_post(writeSem);
198 
199         return (NULL);
200     }
201 
202     /* The region cannot be smaller than a sector size */
203     if (hwAttrs->regionSize < hwAttrs->sectorSize) {
204         SemaphoreP_post(writeSem);
205 
206         return (NULL);
207     }
208 
209     /* The region size must be a multiple of sector size */
210     if (hwAttrs->regionSize !=
211         (hwAttrs->regionSize & object->sectorBaseMask)) {
212         SemaphoreP_post(writeSem);
213         return (NULL);
214     }
215 
216     object->isOpen = true;
217 
218     SemaphoreP_post(writeSem);
219 
220     return (handle);
221 }
222 
223 /*
224  *  ======== NVSRAM_read =======
225  */
NVSRAM_read(NVS_Handle handle,size_t offset,void * buffer,size_t bufferSize)226 int_fast16_t NVSRAM_read(NVS_Handle handle, size_t offset, void *buffer,
227     size_t bufferSize)
228 {
229     NVSRAM_HWAttrs const *hwAttrs = handle->hwAttrs;
230 
231     /* Validate offset and bufferSize */
232     if (offset + bufferSize > hwAttrs->regionSize) {
233         return (NVS_STATUS_INV_OFFSET);
234     }
235 
236     /*
237      *  Get exclusive access to the region.  We don't want someone
238      *  else to erase the region while we are reading it.
239      */
240     SemaphoreP_pend(writeSem, SemaphoreP_WAIT_FOREVER);
241 
242     memcpy(buffer, (char *)(hwAttrs->regionBase) + offset, bufferSize);
243 
244     SemaphoreP_post(writeSem);
245 
246     return (NVS_STATUS_SUCCESS);
247 }
248 
249 /*
250  *  ======== NVSRAM_unlock =======
251  */
NVSRAM_unlock(NVS_Handle handle)252 void NVSRAM_unlock(NVS_Handle handle)
253 {
254     SemaphoreP_post(writeSem);
255 }
256 
257 /*
258  *  ======== NVSRAM_write =======
259  */
NVSRAM_write(NVS_Handle handle,size_t offset,void * buffer,size_t bufferSize,uint_fast16_t flags)260 int_fast16_t NVSRAM_write(NVS_Handle handle, size_t offset, void *buffer,
261     size_t bufferSize, uint_fast16_t flags)
262 {
263     size_t                i;
264     uint8_t              *dstBuf;
265     uint8_t              *srcBuf;
266     int_fast16_t          result;
267     size_t                size;
268     NVSRAM_Object        *object = handle->object;
269     NVSRAM_HWAttrs const *hwAttrs = handle->hwAttrs;
270 
271     /* Validate offset and bufferSize */
272     if (offset + bufferSize > hwAttrs->regionSize) {
273         return (NVS_STATUS_INV_OFFSET);
274     }
275 
276     /* Get exclusive access to the Flash region */
277     SemaphoreP_pend(writeSem, SemaphoreP_WAIT_FOREVER);
278 
279     /* If erase is set, erase destination sector(s) first */
280     if (flags & NVS_WRITE_ERASE) {
281         size = bufferSize & object->sectorBaseMask;
282         if (bufferSize & (~object->sectorBaseMask)) {
283             size += hwAttrs->sectorSize;
284         }
285 
286         result = doErase(handle, offset & object->sectorBaseMask, size);
287         if (result != NVS_STATUS_SUCCESS) {
288             SemaphoreP_post(writeSem);
289 
290             return (result);
291         }
292     }
293     else if (flags & NVS_WRITE_PRE_VERIFY) {
294         /*
295          *  If pre-verify, each destination byte must be able to be changed to the
296          *  source byte (1s to 0s, not 0s to 1s).
297          *  this is satisfied by the following test:
298          *     src == (src & dst)
299          */
300         dstBuf = (uint8_t *)((uint32_t)(hwAttrs->regionBase) + offset);
301         srcBuf = buffer;
302         for (i = 0; i < bufferSize; i++) {
303             if (srcBuf[i] != (srcBuf[i] & dstBuf[i])) {
304                 SemaphoreP_post(writeSem);
305                 return (NVS_STATUS_INV_WRITE);
306             }
307         }
308     }
309 
310     dstBuf = (uint8_t *)((uint32_t)(hwAttrs->regionBase) + offset);
311     srcBuf = buffer;
312     memcpy((void *) dstBuf, (void *) srcBuf, bufferSize);
313 
314     SemaphoreP_post(writeSem);
315 
316     return (NVS_STATUS_SUCCESS);
317 }
318 
319 /*
320  *  ======== checkEraseRange ========
321  */
checkEraseRange(NVS_Handle handle,size_t offset,size_t size)322 static int_fast16_t checkEraseRange(NVS_Handle handle, size_t offset, size_t size)
323 {
324     NVSRAM_Object        *object = handle->object;
325     NVSRAM_HWAttrs const *hwAttrs = handle->hwAttrs;
326 
327     if (offset != (offset & object->sectorBaseMask)) {
328         /* poorly aligned start address */
329         return (NVS_STATUS_INV_ALIGNMENT);
330     }
331 
332     if (offset >= hwAttrs->regionSize) {
333         /* offset is past end of region */
334         return (NVS_STATUS_INV_OFFSET);
335     }
336 
337     if (offset + size > hwAttrs->regionSize) {
338         /* size is too big */
339         return (NVS_STATUS_INV_SIZE);
340     }
341 
342     if (size != (size & object->sectorBaseMask)) {
343         /* size is not a multiple of sector size */
344         return (NVS_STATUS_INV_SIZE);
345     }
346 
347     return (NVS_STATUS_SUCCESS);
348 }
349 
350 /*
351  *  ======== doErase ========
352  */
doErase(NVS_Handle handle,size_t offset,size_t size)353 static int_fast16_t doErase(NVS_Handle handle, size_t offset, size_t size)
354 {
355     void *                sectorBase;
356     int_fast16_t          rangeStatus;
357     NVSRAM_HWAttrs const *hwAttrs = handle->hwAttrs;
358 
359     /* sanity test the erase args */
360     rangeStatus = checkEraseRange(handle, offset, size);
361     if (rangeStatus != NVS_STATUS_SUCCESS) {
362         return (rangeStatus);
363     }
364 
365     sectorBase = (void *) ((uint32_t) hwAttrs->regionBase + offset);
366 
367     memset(sectorBase, 0xFF, size);
368 
369     return (NVS_STATUS_SUCCESS);
370 }
371