1 /*
2 * Copyright (c) 2020 Laczen
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * This driver emulates an EEPROM device in flash.
9 *
10 * The emulation represents the EEPROM in flash as a region that is a direct
11 * map of the EEPROM data (EEPROM data) followed by a region where changes to
12 * the EEPROM data (EEPROM changes) are stored. The combination of EEPROM data
13 * and EEPROM changes form a EEPROM page (see drawing below). Changes to EEPROM
14 * data are written as address-data combinations. The size of such a combination
15 * is determined by the flash write block size and the size of the EEPROM
16 * (required address space), with a minimum of 4 byte.
17 *
18 * When there is no more space to store changes a new EEPROM page is taken into
19 * use. This copies the existing data to the EEPROM data area of the new page.
20 * During this copying the write that is performed is applied at the same time.
21 * The old page is then invalidated.
22 *
23 * The EEPROM page needs to be a multiple of a flash page size. Multiple EEPROM
24 * pages are also supported and increases the number of writes that can be
25 * performed.
26 *
27 * The representation of the EEPROM on flash is shown in the next graph.
28 *
29 * |-----------------------------------------------------------------------|
30 * ||----------------------| |----------------------| |-------------------||
31 * || EEPROM data | | | |-Flash page--------||
32 * || | | | |
33 * || size = EEPROM size | | | |
34 * ||----------------------| |----------------------| ... |
35 * || EEPROM changes: | | | |
36 * || (address, new data) | | | |
37 * || | | | |
38 * || XX| | XX| |
39 * ||--EEPROM page 0-------| |--EEPROM page 1-------| |
40 * |------------------------------------------------------------Partition--|
41 * XX: page validity marker: all 0x00: page invalid
42 *
43 * Internally the address of an EEPROM byte is represented by a uint32_t (this
44 * should be sufficient in all cases). In case the EEPROM size is smaller than
45 * 64kB only a uint16_t is used to store changes. In this case the change stored
46 * for a 4 byte flash write block size are a combination of 2 byte address and
47 * 2 byte data.
48 *
49 * The EEPROM size, pagesize and the flash partition used for the EEPROM are
50 * defined in the dts. The flash partition should allow at least two EEPROM
51 * pages.
52 *
53 */
54
55 #define DT_DRV_COMPAT zephyr_emu_eeprom
56
57 #define EEPROM_EMU_VERSION 0
58 #define EEPROM_EMU_MAGIC 0x45454d55 /* EEMU in hex */
59
60 #include <zephyr/drivers/eeprom.h>
61 #include <zephyr/drivers/flash.h>
62 #include <zephyr/kernel.h>
63 #define LOG_LEVEL CONFIG_EEPROM_LOG_LEVEL
64 #include <zephyr/logging/log.h>
65 LOG_MODULE_REGISTER(eeprom_emulator);
66
67 struct eeprom_emu_config {
68 /* EEPROM size */
69 size_t size;
70 /* EEPROM is read-only */
71 bool readonly;
72 /* Page size used to emulate the EEPROM, contains one area of EEPROM
73 * size and a area to store changes.
74 */
75 size_t page_size;
76 /* Offset of the flash partition used to emulate the EEPROM */
77 off_t flash_offset;
78 /* Size of the flash partition to emulate the EEPROM */
79 size_t flash_size;
80 /* Delay the erase of EEPROM pages until the complete partition is used.
81 */
82 bool partitionerase;
83 /* Size of a change block */
84 uint8_t flash_cbs;
85 uint8_t *rambuf;
86 /* Device of the flash partition used to emulate the EEPROM */
87 const struct device *flash_dev;
88 };
89
90 struct eeprom_emu_data {
91 /* Offset in current (EEPROM) page where next change is written */
92 off_t write_offset;
93 /* Offset of the current (EEPROM) page */
94 off_t page_offset;
95 struct k_mutex lock;
96 };
97
98 /* read/write context */
99 struct eeprom_emu_ctx {
100 const void *data; /* pointer to data */
101 const size_t len; /* data length */
102 const off_t address; /* eeprom address */
103 size_t rlen; /* data remaining (unprocessed) length */
104 };
105
106 /*
107 * basic flash read, only used with offset aligned to flash write block size
108 */
eeprom_emu_flash_read(const struct device * dev,off_t offset,uint8_t * blk,size_t len)109 static inline int eeprom_emu_flash_read(const struct device *dev, off_t offset,
110 uint8_t *blk, size_t len)
111 {
112 const struct eeprom_emu_config *dev_config = dev->config;
113
114 return flash_read(dev_config->flash_dev, dev_config->flash_offset +
115 offset, blk, len);
116 }
117
118 /*
119 * basic flash write, only used with offset aligned to flash write block size
120 */
eeprom_emu_flash_write(const struct device * dev,off_t offset,const uint8_t * blk,size_t len)121 static inline int eeprom_emu_flash_write(const struct device *dev, off_t offset,
122 const uint8_t *blk, size_t len)
123 {
124 const struct eeprom_emu_config *dev_config = dev->config;
125 int rc;
126
127 rc = flash_write(dev_config->flash_dev, dev_config->flash_offset +
128 offset, blk, len);
129 return rc;
130 }
131
132 /*
133 * basic flash erase, only used with offset aligned to flash page and len a
134 * multiple of the flash page size
135 */
eeprom_emu_flash_erase(const struct device * dev,off_t offset,size_t len)136 static inline int eeprom_emu_flash_erase(const struct device *dev, off_t offset,
137 size_t len)
138 {
139 const struct eeprom_emu_config *dev_config = dev->config;
140 int rc;
141
142 rc = flash_erase(dev_config->flash_dev, dev_config->flash_offset +
143 offset, len);
144 return rc;
145 }
146
147 /*
148 * eeprom_emu_page_invalidate: invalidate a page by writing all zeros at the end
149 */
eeprom_emu_page_invalidate(const struct device * dev,off_t offset)150 static int eeprom_emu_page_invalidate(const struct device *dev, off_t offset)
151 {
152 const struct eeprom_emu_config *dev_config = dev->config;
153 uint8_t buf[dev_config->flash_cbs];
154
155 LOG_DBG("Invalidating page at [0x%tx]", (ptrdiff_t)offset);
156
157 memset(buf, 0x00, sizeof(buf));
158
159 offset += (dev_config->page_size - sizeof(buf));
160 return eeprom_emu_flash_write(dev, offset, buf, sizeof(buf));
161 }
162
163 /*
164 * eeprom_emu_get_address: read the address from a change block
165 */
eeprom_emu_get_address(const struct device * dev,const uint8_t * blk)166 static uint32_t eeprom_emu_get_address(const struct device *dev,
167 const uint8_t *blk)
168 {
169 const struct eeprom_emu_config *dev_config = dev->config;
170 uint32_t address = 0U;
171
172 blk += dev_config->flash_cbs / 2;
173 for (int i = 0; i < sizeof(address); i++) {
174 if (2 * i == dev_config->flash_cbs) {
175 break;
176 }
177
178 address += ((uint32_t)(*blk) << (8 * i));
179 blk++;
180 }
181
182 return address;
183 }
184
185 /*
186 * eeprom_emu_set_change: create change blocks from data in blk and address
187 */
eeprom_emu_set_change(const struct device * dev,const uint32_t address,const uint8_t * data,uint8_t * blk)188 static void eeprom_emu_set_change(const struct device *dev,
189 const uint32_t address, const uint8_t *data,
190 uint8_t *blk)
191 {
192 const struct eeprom_emu_config *dev_config = dev->config;
193
194 for (int i = 0; i < (dev_config->flash_cbs / 2); i++) {
195 (*blk++) = (*data++);
196 }
197
198 for (int i = 0; i < (dev_config->flash_cbs / 2); i++) {
199 if (i < sizeof(address)) {
200 (*blk++) = (uint8_t)(((address >> (8 * i)) & 0xff));
201 } else {
202 (*blk++) = 0xff;
203 }
204
205 }
206
207 }
208
209 /*
210 * eeprom_emu_is_word_used: check if word is not empty
211 */
eeprom_emu_is_word_used(const struct device * dev,const uint8_t * blk)212 static int eeprom_emu_is_word_used(const struct device *dev, const uint8_t *blk)
213 {
214 const struct eeprom_emu_config *dev_config = dev->config;
215
216 for (int i = 0; i < dev_config->flash_cbs; i++) {
217 if ((*blk++) != 0xff) {
218 return 1;
219 }
220
221 }
222
223 return 0;
224 }
225
226 /*
227 * eeprom_emu_word_read: read basic word (cbs byte of data) item from
228 * address directly from flash.
229 */
eeprom_emu_word_read(const struct device * dev,off_t address,uint8_t * data)230 static int eeprom_emu_word_read(const struct device *dev, off_t address,
231 uint8_t *data)
232 {
233 const struct eeprom_emu_config *dev_config = dev->config;
234 const struct eeprom_emu_data *dev_data = dev->data;
235 uint8_t buf[dev_config->flash_cbs];
236 off_t direct_address;
237 int rc;
238
239 direct_address = dev_data->page_offset + address;
240
241 /* Direct flash read */
242 rc = eeprom_emu_flash_read(dev, direct_address, data, sizeof(buf));
243 if (rc) {
244 return rc;
245 }
246
247 /* Process changes written to flash */
248 off_t offset, ch_address;
249 bool mc1 = false, mc2 = false;
250
251 offset = dev_data->write_offset;
252 while (((!mc1) || (!mc2)) && (offset > dev_config->size)) {
253 offset -= sizeof(buf);
254 /* read the change */
255 rc = eeprom_emu_flash_read(dev, dev_data->page_offset + offset,
256 buf, sizeof(buf));
257 if (rc) {
258 return rc;
259 }
260
261 /* get the address from a change block */
262 ch_address = eeprom_emu_get_address(dev, buf);
263 if ((!mc1) && (ch_address == address)) {
264 memcpy(data, buf, sizeof(buf)/2);
265 mc1 = true;
266 }
267
268 if ((!mc2) && (ch_address == (address + sizeof(buf)/2))) {
269 memcpy(data + sizeof(buf)/2, buf, sizeof(buf)/2);
270 mc2 = true;
271 }
272
273 }
274
275 return rc;
276 }
277
278 /* Update data specified in ctx from flash */
eeprom_emu_flash_get(const struct device * dev,struct eeprom_emu_ctx * ctx)279 static int eeprom_emu_flash_get(const struct device *dev,
280 struct eeprom_emu_ctx *ctx)
281 {
282 const struct eeprom_emu_config *dev_config = dev->config;
283 off_t address = ctx->address + ctx->len - ctx->rlen;
284 uint8_t *data8 = (uint8_t *)(ctx->data);
285 uint8_t buf[dev_config->flash_cbs];
286 const off_t addr_jmp = address & (sizeof(buf) - 1);
287 size_t len;
288 int rc;
289
290 data8 += (ctx->len - ctx->rlen);
291 len = MIN((sizeof(buf) - addr_jmp), ctx->rlen);
292 rc = eeprom_emu_word_read(dev, address - addr_jmp, buf);
293 if (rc) {
294 return rc;
295 }
296
297 memcpy(data8, buf + addr_jmp, len);
298 ctx->rlen -= len;
299
300 return rc;
301 }
302
303 /*
304 * eeprom_emu_compactor: start a new EEPROM page and copy existing data to the
305 * new page. During copy update the data with present write data. Invalidate
306 * the old page.
307 */
eeprom_emu_compactor(const struct device * dev,struct eeprom_emu_ctx * ctx)308 static int eeprom_emu_compactor(const struct device *dev,
309 struct eeprom_emu_ctx *ctx)
310 {
311 const struct eeprom_emu_config *dev_config = dev->config;
312 struct eeprom_emu_data *dev_data = dev->data;
313 off_t next_page_offset;
314 int rc = 0;
315
316 LOG_DBG("Compactor called for page at [0x%tx]",
317 (ptrdiff_t)dev_data->page_offset);
318
319 next_page_offset = dev_data->page_offset + dev_config->page_size;
320 if (next_page_offset >= dev_config->flash_size) {
321 next_page_offset = 0;
322 }
323
324 if (!dev_config->partitionerase) {
325 /* erase the new page */
326 rc = eeprom_emu_flash_erase(dev, next_page_offset,
327 dev_config->page_size);
328 } else if (next_page_offset == 0) {
329 /* erase the entire partition */
330 rc = eeprom_emu_flash_erase(dev, next_page_offset,
331 dev_config->flash_size);
332 } else {
333 rc = 0;
334 }
335
336 if (rc) {
337 return rc;
338 }
339
340 if (dev_config->rambuf && (ctx != NULL)) {
341 rc = eeprom_emu_flash_write(dev, next_page_offset,
342 dev_config->rambuf,
343 dev_config->size);
344 if (rc) {
345 return rc;
346 }
347
348 ctx->rlen = 0;
349 } else {
350 off_t rd_offset = 0;
351 uint8_t buf[dev_config->flash_cbs];
352
353 /* reset the context if available */
354 if (ctx != NULL) {
355 ctx->rlen = ctx->len;
356 }
357
358 /* copy existing data */
359 while (rd_offset < dev_config->size) {
360
361 rc = eeprom_emu_word_read(dev, rd_offset, buf);
362 if (rc) {
363 return rc;
364 }
365
366 if ((ctx != NULL) && (ctx->len) &&
367 (rd_offset > (ctx->address - sizeof(buf)))) {
368 /* overwrite buf data with context data */
369 uint8_t *data8 = (uint8_t *)(ctx->data);
370 off_t address, addr_jmp;
371 size_t len;
372
373 address = ctx->address + ctx->len - ctx->rlen;
374 addr_jmp = address & (sizeof(buf) - 1);
375 len = MIN((sizeof(buf) - addr_jmp), ctx->rlen);
376 data8 += (ctx->len - ctx->rlen);
377 memcpy(buf + addr_jmp, data8, len);
378 ctx->rlen -= len;
379 }
380
381 if (eeprom_emu_is_word_used(dev, buf)) {
382 rc = eeprom_emu_flash_write(dev,
383 next_page_offset +
384 rd_offset, buf,
385 sizeof(buf));
386 if (rc) {
387 return rc;
388 }
389
390 }
391
392 rd_offset += sizeof(buf);
393 }
394
395 }
396
397 if ((dev_config->partitionerase) && (next_page_offset == 0)) {
398 /* no need to invalidate previous page as it has been deleted */
399 rc = 0;
400 } else {
401 /* invalidate the old page */
402 rc = eeprom_emu_page_invalidate(dev, dev_data->page_offset);
403 }
404
405 if (!rc) {
406 dev_data->write_offset = dev_config->size;
407 dev_data->page_offset = next_page_offset;
408 }
409 return rc;
410 }
411
412 /*
413 * eeprom_emu_word_write: write basic word (cbs bytes of data) item to address,
414 */
eeprom_emu_word_write(const struct device * dev,off_t address,const uint8_t * data,struct eeprom_emu_ctx * ctx)415 static int eeprom_emu_word_write(const struct device *dev, off_t address,
416 const uint8_t *data,
417 struct eeprom_emu_ctx *ctx)
418 {
419 const struct eeprom_emu_config *dev_config = dev->config;
420 struct eeprom_emu_data *dev_data = dev->data;
421 uint8_t buf[dev_config->flash_cbs], tmp[dev_config->flash_cbs];
422 off_t direct_address, wraddr;
423 int rc;
424
425 direct_address = dev_data->page_offset + address;
426
427 rc = eeprom_emu_flash_read(dev, direct_address, buf, sizeof(buf));
428 if (rc) {
429 return rc;
430 }
431
432 if (!eeprom_emu_is_word_used(dev, buf)) {
433 if (eeprom_emu_is_word_used(dev, data)) {
434 rc = eeprom_emu_flash_write(dev, direct_address, data,
435 sizeof(buf));
436 }
437
438 return rc;
439 }
440
441 rc = eeprom_emu_word_read(dev, address, buf);
442 if (rc) {
443 return rc;
444 }
445
446 if (!memcmp(buf, data, sizeof(buf))) {
447 /* data has not changed */
448 return rc;
449 }
450
451 wraddr = address;
452 /* store change */
453 for (uint8_t i = 0; i < 2; i++) {
454 if (memcmp(&buf[i*sizeof(buf)/2], data, sizeof(buf)/2)) {
455 eeprom_emu_set_change(dev, wraddr, data, tmp);
456 rc = eeprom_emu_flash_write(dev, dev_data->page_offset +
457 dev_data->write_offset, tmp,
458 sizeof(buf));
459 if (rc) {
460 return rc;
461 }
462
463 dev_data->write_offset += sizeof(buf);
464 if ((dev_data->write_offset + sizeof(buf)) >=
465 dev_config->page_size) {
466 rc = eeprom_emu_compactor(dev, ctx);
467 return rc;
468
469 }
470
471 }
472
473 data += sizeof(buf)/2;
474 wraddr += sizeof(buf)/2;
475 }
476
477 return rc;
478 }
479
480 /* Update flash with data specified in ctx */
eeprom_emu_flash_set(const struct device * dev,struct eeprom_emu_ctx * ctx)481 static int eeprom_emu_flash_set(const struct device *dev,
482 struct eeprom_emu_ctx *ctx)
483 {
484 const struct eeprom_emu_config *dev_config = dev->config;
485 off_t address = ctx->address + ctx->len - ctx->rlen;
486 uint8_t *data8 = (uint8_t *)(ctx->data);
487 uint8_t buf[dev_config->flash_cbs];
488 const off_t addr_jmp = address & (sizeof(buf) - 1);
489 size_t len;
490 int rc;
491
492 data8 += (ctx->len - ctx->rlen);
493 len = MIN((sizeof(buf) - addr_jmp), ctx->rlen);
494 rc = eeprom_emu_word_read(dev, address - addr_jmp, buf);
495 if (rc) {
496 return rc;
497 }
498
499 memcpy(buf + addr_jmp, data8, len);
500 rc = eeprom_emu_word_write(dev, address - addr_jmp, buf, ctx);
501 if (rc) {
502 return rc;
503 }
504
505 if (ctx->rlen) {
506 ctx->rlen -= len;
507 }
508
509 return rc;
510 }
511
eeprom_emu_range_is_valid(const struct device * dev,off_t address,size_t len)512 static int eeprom_emu_range_is_valid(const struct device *dev, off_t address,
513 size_t len)
514 {
515 const struct eeprom_emu_config *dev_config = dev->config;
516
517 if ((address + len) <= dev_config->size) {
518 return 1;
519 }
520
521 return 0;
522 }
523
eeprom_emu_read(const struct device * dev,off_t address,void * data,size_t len)524 static int eeprom_emu_read(const struct device *dev, off_t address, void *data,
525 size_t len)
526 {
527 const struct eeprom_emu_config *dev_config = dev->config;
528 struct eeprom_emu_data *dev_data = dev->data;
529 struct eeprom_emu_ctx ctx = {
530 .data = data,
531 .len = len,
532 .address = address,
533 .rlen = len,
534 };
535 int rc = 0;
536
537 /* Nothing to do */
538 if (!len) {
539 return 0;
540 }
541
542 /* Error checking */
543 if ((!data) || (!eeprom_emu_range_is_valid(dev, address, len))) {
544 return -EINVAL;
545 }
546
547 if (!device_is_ready(dev_config->flash_dev)) {
548 LOG_ERR("flash device is not ready");
549 return -EIO;
550 }
551
552 /* Handle normal case */
553 LOG_DBG("EEPROM read at [0x%tx] length[%zu]", (ptrdiff_t)address, len);
554 k_mutex_lock(&dev_data->lock, K_FOREVER);
555
556 /* read from rambuffer if possible */
557 if (dev_config->rambuf) {
558 memcpy(data, dev_config->rambuf + address, len);
559 } else {
560 /* read from flash if no rambuffer */
561 while (ctx.rlen) {
562 rc = eeprom_emu_flash_get(dev, &ctx);
563 if (rc) {
564 break;
565 }
566
567 }
568 }
569
570 k_mutex_unlock(&dev_data->lock);
571
572 return rc;
573 }
574
eeprom_emu_write(const struct device * dev,off_t address,const void * data,size_t len)575 static int eeprom_emu_write(const struct device *dev, off_t address,
576 const void *data, size_t len)
577 {
578 const struct eeprom_emu_config *dev_config = dev->config;
579 struct eeprom_emu_data *dev_data = dev->data;
580 struct eeprom_emu_ctx ctx = {
581 .data = data,
582 .len = len,
583 .address = address,
584 .rlen = len,
585 };
586 int rc = 0;
587
588 /* Nothing to do */
589 if (!len) {
590 return 0;
591 }
592
593 /* Error checking */
594 if ((!data) || (!eeprom_emu_range_is_valid(dev, address, len))) {
595 return -EINVAL;
596 }
597
598 if (dev_config->readonly) {
599 LOG_ERR("attempt to write to read-only device");
600 return -EACCES;
601 }
602
603 if (!device_is_ready(dev_config->flash_dev)) {
604 LOG_ERR("flash device is not ready");
605 return -EIO;
606 }
607
608 /* Handle normal case */
609 LOG_DBG("EEPROM write at [0x%tx] length[%zu]", (ptrdiff_t)address, len);
610
611 k_mutex_lock(&dev_data->lock, K_FOREVER);
612
613 /* first update the rambuffer */
614 if (dev_config->rambuf) {
615 memcpy(dev_config->rambuf + address, data, len);
616 }
617
618 /* second update the flash */
619 while (ctx.rlen) {
620 rc = eeprom_emu_flash_set(dev, &ctx);
621 if (rc) {
622 break;
623 }
624
625 }
626
627 k_mutex_unlock(&dev_data->lock);
628
629 return rc;
630 }
631
eeprom_emu_size(const struct device * dev)632 static size_t eeprom_emu_size(const struct device *dev)
633 {
634 const struct eeprom_emu_config *dev_config = dev->config;
635
636 return dev_config->size;
637 }
638
eeprom_emu_init(const struct device * dev)639 static int eeprom_emu_init(const struct device *dev)
640 {
641 const struct eeprom_emu_config *dev_config = dev->config;
642 struct eeprom_emu_data *dev_data = dev->data;
643 off_t offset;
644 uint8_t buf[dev_config->flash_cbs];
645 int rc = 0;
646
647 k_mutex_init(&dev_data->lock);
648 if (!device_is_ready(dev_config->flash_dev)) {
649 __ASSERT(0, "Could not get flash device binding");
650 return -ENODEV;
651 }
652
653 /* Find the page offset */
654 dev_data->page_offset = 0U;
655 dev_data->write_offset = dev_config->page_size - sizeof(buf);
656 while (dev_data->page_offset < dev_config->flash_size) {
657 offset = dev_data->page_offset + dev_data->write_offset;
658 rc = eeprom_emu_flash_read(dev, offset, buf, sizeof(buf));
659 if (rc) {
660 return rc;
661 }
662
663 if (!eeprom_emu_is_word_used(dev, buf)) {
664 break;
665 }
666
667 dev_data->page_offset += dev_config->page_size;
668 }
669
670 if (dev_data->page_offset == dev_config->flash_size) {
671 __ASSERT(0, "All pages are invalid, is this a EEPROM area?");
672 return -EINVAL;
673 }
674
675 dev_data->write_offset = dev_config->size;
676
677 /* Update the write offset */
678 while ((dev_data->write_offset + sizeof(buf)) < dev_config->page_size) {
679 offset = dev_data->page_offset + dev_data->write_offset;
680 rc = eeprom_emu_flash_read(dev, offset, buf, sizeof(buf));
681 if (rc) {
682 return rc;
683 }
684
685 if (!eeprom_emu_is_word_used(dev, buf)) {
686 break;
687 }
688
689 dev_data->write_offset += sizeof(buf);
690 }
691
692 /* dev_data->write_offset reaches last possible location, compaction
693 * might have been interrupted: call eeprom_emu_compactor again, but
694 * only in case we are using a write-enabled eeprom
695 */
696 if ((!dev_config->readonly) &&
697 ((dev_data->write_offset + sizeof(buf)) >= dev_config->page_size)) {
698 rc = eeprom_emu_compactor(dev, NULL);
699 if (rc) {
700 return rc;
701 }
702
703 }
704
705 /* Fill the ram buffer if enabled */
706 if (dev_config->rambuf) {
707 offset = 0;
708 while (offset < dev_config->size) {
709 rc = eeprom_emu_word_read(dev, offset, buf);
710 if (rc) {
711 return rc;
712 }
713
714 memcpy(dev_config->rambuf + offset, buf, sizeof(buf));
715 offset += sizeof(buf);
716 }
717
718 }
719
720 return rc;
721 }
722
723 static const struct eeprom_driver_api eeprom_emu_api = {
724 .read = eeprom_emu_read,
725 .write = eeprom_emu_write,
726 .size = eeprom_emu_size,
727 };
728
729 #define EEPROM_PARTITION(n) DT_INST_PHANDLE_BY_IDX(n, partition, 0)
730
731 #define PART_WBS(part) \
732 DT_PROP(COND_CODE_1(DT_NODE_HAS_COMPAT(DT_GPARENT(part), soc_nv_flash),\
733 (DT_GPARENT(part)), (DT_PARENT(part))), write_block_size)
734
735 #define PART_CBS(part, size) (PART_WBS(part) < 4) ? \
736 ((size > (2^16)) ? 8 : 4) : PART_WBS(part)
737
738 #define PART_DEV_ID(part) \
739 COND_CODE_1(DT_NODE_HAS_COMPAT(DT_GPARENT(part), soc_nv_flash), \
740 (DT_PARENT(DT_GPARENT(part))), (DT_GPARENT(part)))
741
742 #define PART_DEV(part) \
743 DEVICE_DT_GET(PART_DEV_ID(part))
744
745 #define RECALC_SIZE(size, cbs) \
746 (size % cbs) ? ((size + cbs - 1) & ~(cbs - 1)) : size
747
748 #define ASSERT_SIZE_PAGESIZE_VALID(size, pagesize, readonly) \
749 BUILD_ASSERT(readonly ? (size <= pagesize) : (4*size <= 3*pagesize), \
750 "EEPROM size to big for pagesize")
751
752 #define ASSERT_PAGESIZE_PARTSIZE_VALID(pagesize, partsize) \
753 BUILD_ASSERT(partsize % pagesize == 0U, \
754 "Partition size not a multiple of pagesize")
755
756 #define ASSERT_PAGESIZE_SIZE(pagesize, partsize, onepage) \
757 BUILD_ASSERT(onepage ? (partsize >= pagesize) : (partsize > pagesize),\
758 "Partition size to small")
759
760 #define EEPROM_EMU_READ_ONLY(n) \
761 DT_INST_PROP(n, read_only) || \
762 DT_PROP(EEPROM_PARTITION(n), read_only)
763
764 #define EEPROM_EMU_ONEPAGE(n) \
765 EEPROM_EMU_READ_ONLY(n) || DT_INST_PROP(n, partition_erase)
766
767 #define EEPROM_EMU_ENABLE_RAMBUF(n) \
768 COND_CODE_1(DT_INST_PROP(n, rambuf), (1), \
769 (COND_CODE_1(DT_INST_PROP(n, partition_erase), (1), (0))))
770
771 #define EEPROM_EMU_RAMBUF(n) \
772 COND_CODE_0(EEPROM_EMU_ENABLE_RAMBUF(n), (), \
773 (static uint8_t eeprom_emu_##n##_rambuf[DT_INST_PROP(n, size)];))
774
775 #define EEPROM_EMU_RAMBUF_LINK(n) \
776 COND_CODE_0(EEPROM_EMU_ENABLE_RAMBUF(n), (NULL), \
777 (eeprom_emu_##n##_rambuf))
778
779 #define EEPROM_EMU_INIT(n) \
780 ASSERT_SIZE_PAGESIZE_VALID(DT_INST_PROP(n, size), \
781 DT_INST_PROP(n, pagesize), EEPROM_EMU_ONEPAGE(n)); \
782 ASSERT_PAGESIZE_PARTSIZE_VALID(DT_INST_PROP(n, pagesize), \
783 DT_REG_SIZE(EEPROM_PARTITION(n))); \
784 ASSERT_PAGESIZE_SIZE(DT_INST_PROP(n, pagesize), \
785 DT_REG_SIZE(EEPROM_PARTITION(n)), EEPROM_EMU_ONEPAGE(n)); \
786 EEPROM_EMU_RAMBUF(n) \
787 static const struct eeprom_emu_config eeprom_emu_##n##_config = { \
788 .size = RECALC_SIZE( \
789 DT_INST_PROP(n, size), \
790 (PART_CBS(EEPROM_PARTITION(n), DT_INST_PROP(n, size))) \
791 ), \
792 .readonly = EEPROM_EMU_READ_ONLY(n), \
793 .page_size = DT_INST_PROP(n, pagesize), \
794 .flash_offset = DT_REG_ADDR(EEPROM_PARTITION(n)), \
795 .flash_size = DT_REG_SIZE(EEPROM_PARTITION(n)), \
796 .partitionerase = DT_INST_PROP(n, partition_erase), \
797 .flash_cbs = PART_CBS(EEPROM_PARTITION(n), \
798 DT_INST_PROP(n, size)), \
799 .flash_dev = PART_DEV(EEPROM_PARTITION(n)),\
800 .rambuf = EEPROM_EMU_RAMBUF_LINK(n), \
801 }; \
802 static struct eeprom_emu_data eeprom_emu_##n##_data; \
803 DEVICE_DT_INST_DEFINE(n, &eeprom_emu_init, \
804 NULL, &eeprom_emu_##n##_data, \
805 &eeprom_emu_##n##_config, POST_KERNEL, \
806 CONFIG_EEPROM_INIT_PRIORITY, &eeprom_emu_api); \
807
808 DT_INST_FOREACH_STATUS_OKAY(EEPROM_EMU_INIT)
809