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