EVE 1.0
eeprom.c
1 #include <stddef.h>
2 #include <contiki.h>
3 #include <core/work.h>
4 #include <core/event-monitor.h>
5 #include <dev/eeprom.h>
6 #include <dev/arch-eeprom.h>
7 #include <stdbool.h>
8 #include <string.h>
9 #include <alloca.h>
10 #include <lib/assert.h>
11 #include <dev/resources.h>
12 #include <dev/spi-memory.h>
13 #include <dev/eve-module.h>
14 
15 /*** Typedef's and defines. ***/
16 
17 #define EEPROM_BUFFER_SIZE(e) (((e)->PageSize < 64) ? 64 : ((e)->PageSize))
18 
19 #define EEPROM_BIT_PRESENT (1 << 0)
20 #define EEPROM_BIT_USED (1 << 1)
21 #define EEPROM_BIT_VALID (1 << 2)
22 #define EEPROM_BITS(bits) ((uint8_t) ~(bits))
23 
24 /* XXX: Product-specific */
25 extern const struct spi_memory_t ExtFlash;
26 
27 /** EEPROM bank id */
28 enum {
29  BANK_1, /**< First bank */
30  BANK_2, /**< Second bank */
31  BANK__COUNT /**< Amount of banks */
32 };
33 
34 /** EEPROM bank descriptor */
35 struct eeprom_bank_t {
36  uint16_t read; /**< Read index, the first valid element in the bank */
37  uint16_t write; /**< Write index, the first free entry in the bank */
38 };
39 
40 /*** Function prototypes ***/
41 static uint32_t eeprom_sector(const struct eeprom_t *e, uint8_t bank);
42 static uint32_t eeprom_page(const struct eeprom_t *e, uint8_t bank, uint16_t page);
43 static uint32_t eeprom_addr(const struct eeprom_t *e, uint8_t bank, uint16_t page, uint32_t offset);
44 static void eeprom_bank_init(const struct eeprom_t *e, uint8_t *buffer, uint8_t bank);
45 static bool eeprom_bank_open(const struct eeprom_t *e, uint8_t *buffer, unsigned bank, struct eeprom_bank_t *cache);
46 static void eeprom_read_impl(eeprom_addr_t addr, unsigned char *buf, int size, const struct eeprom_t *e);
47 static void eeprom_write_impl(eeprom_addr_t addr, const unsigned char *buf, int size, const struct eeprom_t *e);
48 static void eeprom_init_impl(const struct eeprom_t *e);
49 
50 static bool eeprom_ext_power(bool On);
51 static void eeprom_ext_erase(uint32_t SectorIndex);
52 static void eeprom_ext_write(uint32_t DstAddress, const void *SrcBuffer, uint32_t ByteCount);
53 static void eeprom_ext_read(void *DstBuffer, uint32_t SrcAddress, uint32_t ByteCount);
54 
55 static bool eeprom_int_power(bool On);
56 static void eeprom_int_erase(uint32_t SectorIndex);
57 static void eeprom_int_write(uint32_t DstAddress, const void *SrcBuffer, uint32_t ByteCount);
58 static void eeprom_int_read(void *DstBuffer, uint32_t SrcAddress, uint32_t ByteCount);
59 
60 /*** Variables ***/
61 const struct eeprom_vtbl_t EepromInExternalFlash =
62 {
63  .Power = eeprom_ext_power,
64  .Erase = eeprom_ext_erase,
65  .Write = eeprom_ext_write,
66  .Read = eeprom_ext_read,
67 };
68 
69 const struct eeprom_vtbl_t EepromInInternalFlash =
70 {
71  .Power = eeprom_int_power,
72  .Erase = eeprom_int_erase,
73  .Write = eeprom_int_write,
74  .Read = eeprom_int_read,
75 };
76 
77 /*** Static functions ***/
78 
79 /** Returns flash sector address by bank id */
80 static uint32_t eeprom_sector(const struct eeprom_t *e, uint8_t bank)
81 {
82  return e->StartSector + bank;
83 }
84 
85 /** Returns flash page address by bank id and page index*/
86 static uint32_t eeprom_page(const struct eeprom_t *e, uint8_t bank, uint16_t page)
87 {
88  return eeprom_sector(e, bank) * e->PagesPerSector + page;
89 }
90 
91 /** Returns flash address by bank id, page index and offset within the page */
92 static uint32_t eeprom_addr(const struct eeprom_t *e, uint8_t bank, uint16_t page, uint32_t offset)
93 {
94  return eeprom_page(e, bank, page) * e->PageSize + offset;
95 }
96 
97 /** Initializes a EEPROM bank on flash */
98 static void eeprom_bank_init(const struct eeprom_t *e, uint8_t *buffer, uint8_t bank)
99 {
100  const struct eeprom_vtbl_t *v = e->Vtbl;
101  uint16_t pos, chunk;
102 
103  assert(v != NULL);
104 
105  /* Erase the sector */
106  v->Erase(eeprom_sector(e, bank));
107 
108  for (pos = 0; pos < e->PagesPerSector; pos += chunk) {
109  chunk = e->PagesPerSector - pos;
110  if (chunk > EEPROM_BUFFER_SIZE(e))
111  chunk = EEPROM_BUFFER_SIZE(e);
112 
113  /* Write the header. Do not set VALID bit for the first time */
114  memset(&buffer[0], EEPROM_BITS(EEPROM_BIT_PRESENT), chunk);
115  if (pos == 0)
116  buffer[0] = EEPROM_BITS(EEPROM_BIT_PRESENT | EEPROM_BIT_USED);
117 
118  v->Write(eeprom_addr(e, bank, 0, pos), buffer, chunk);
119  }
120 
121  /* And now, when write is finished we can mark the header as VALID */
122  buffer[0] = EEPROM_BITS(EEPROM_BIT_PRESENT | EEPROM_BIT_USED | EEPROM_BIT_VALID);
123  v->Write(eeprom_addr(e, bank, 0, 0), buffer, 1);
124 }
125 
126 /** Reads bank header and fills bank descriptor with read and write indexes */
127 static bool eeprom_bank_open(const struct eeprom_t *e, uint8_t *buffer, unsigned bank, struct eeprom_bank_t *cache)
128 {
129  const struct eeprom_vtbl_t *v = e->Vtbl;
130  uint16_t pos, chunk;
131  uint16_t idx = (e->PagesPerSector + e->PageSize - 1) / e->PageSize;
132 
133  assert (v != NULL);
134 
135  cache->read = cache->write = 0;
136 
137  for (pos = 0; pos < e->PagesPerSector; pos += chunk) {
138  chunk = e->PagesPerSector - pos;
139  if (chunk > EEPROM_BUFFER_SIZE(e))
140  chunk = EEPROM_BUFFER_SIZE(e);
141 
142  v->Read(buffer, eeprom_addr(e, bank, 0, pos), chunk);
143 
144  /* Sanity check */
145  if (pos == 0
146  && buffer[0] != EEPROM_BITS(EEPROM_BIT_PRESENT | EEPROM_BIT_USED | EEPROM_BIT_VALID))
147  return false;
148 
149  /* Iterate through the header */
150  while (idx < pos + chunk) {
151  /* If VALID bit is set, update read index */
152  if (buffer[idx - pos] == EEPROM_BITS(EEPROM_BIT_PRESENT | EEPROM_BIT_USED | EEPROM_BIT_VALID)) {
153  cache->read = idx;
154  /* Else if not used, update write index */
155  } else if (buffer[idx - pos] == EEPROM_BITS(EEPROM_BIT_PRESENT)) {
156  if (cache->write == 0)
157  cache->write = idx;
158  /* Else check for valid value in the header */
159  } else if (buffer[idx - pos] != EEPROM_BITS(EEPROM_BIT_PRESENT | EEPROM_BIT_USED)) {
160  return false;
161  }
162  idx += e->PagesPerEeprom;
163  }
164  }
165 
166  /* Done */
167  return true;
168 }
169 
170 /** Reads data from the eeprom */
171 static void eeprom_read_impl(eeprom_addr_t addr, unsigned char *buf, int size, const struct eeprom_t *e)
172 {
173  const struct eeprom_vtbl_t *v = e->Vtbl;
174  struct eeprom_state_t *s = e->State;
175  const eeprom_addr_t eeprom_size = e->PageSize * e->PagesPerEeprom;
176 
177  assert(v != NULL);
178 
179  SWINT_AUTO_LOCK();
180 
181  /* Sanity check */
182  if (addr > eeprom_size || (addr + size) > eeprom_size || size <= 0)
183  return;
184 
185  /* If no valid data exist, fill buffer with 0xFF */
186  if (!s->opened || s->read_page == 0 || !v->Power(true)) {
187  memset(buf, 0xFF, size);
188  /* Else read the page from flash */
189  } else {
190  v->Read(buf, eeprom_addr(e, s->read_bank, s->read_page, addr), size);
191  /* Release flash resource */
192  v->Power(false);
193  }
194 }
195 
196 /** Writes buffer content to the eeprom */
197 static void eeprom_write_impl(eeprom_addr_t addr, const unsigned char *buf, int size, const struct eeprom_t *e)
198 {
199  const struct eeprom_vtbl_t *v = e->Vtbl;
200  struct eeprom_state_t *s = e->State;
201  const eeprom_addr_t eeprom_size = e->PageSize * e->PagesPerEeprom;
202  uint8_t *buffer = alloca(EEPROM_BUFFER_SIZE(e));
203  unsigned page;
204 
205  assert(v != NULL);
206 
207  SWINT_AUTO_LOCK();
208 
209  /* Sanity check */
210  if (addr > eeprom_size || (addr + size) > eeprom_size || size <= 0)
211  {
212  assert(0);
213  return;
214  }
215 
216  /* Sanity check */
217  if (!s->opened)
218  {
219  assert(0);
220  return;
221  }
222 
223  /* Open flash */
224  if (!v->Power(true))
225  {
226  assert(0);
227  return;
228  }
229 
230  /* When the bank is full, wrap to the next one */
231  if (s->write_page + e->PagesPerEeprom > e->PagesPerSector) {
232  s->write_page = (e->PagesPerSector + e->PageSize - 1) / e->PageSize;
233  s->write_bank ^= BANK_1 ^ BANK_2;
234  }
235 
236  /* First write USED bit to mark the page as dirty */
237  assert(EEPROM_BUFFER_SIZE(e) >= e->PagesPerEeprom);
238  memset(buffer, EEPROM_BITS(EEPROM_BIT_PRESENT | EEPROM_BIT_USED), e->PagesPerEeprom);
239  v->Write(eeprom_addr(e, s->write_bank, 0, s->write_page), buffer, e->PagesPerEeprom);
240 
241  for (page = 0; page < e->PagesPerEeprom; ++page) {
242  /* Read and update content of the EEPROM page */
243  eeprom_read_impl(page * e->PageSize, buffer, e->PageSize, e);
244 
245  if (addr < (page + 1) * e->PageSize && size > 0) {
246  unsigned offset = addr & (e->PageSize - 1);
247  unsigned chunk = e->PageSize - offset;
248  if (chunk > size)
249  chunk = size;
250 
251  memcpy(&buffer[offset], buf, chunk);
252  addr += chunk;
253  buf += chunk;
254  size -= chunk;
255  }
256 
257  /* Write the page back to the flash */
258  v->Write(eeprom_addr(e, s->write_bank, s->write_page, page * e->PageSize), buffer, e->PageSize);
259  }
260 
261  /* Now write the VALID bit indicating that content is synced */
262  memset(buffer, EEPROM_BITS(EEPROM_BIT_PRESENT | EEPROM_BIT_USED | EEPROM_BIT_VALID), e->PagesPerEeprom);
263  v->Write(eeprom_addr(e, s->write_bank, 0, s->write_page), buffer, e->PagesPerEeprom);
264 
265  /* Now we have a valid copy on flash, so erase former read bank */
266  if (s->write_bank != s->read_bank)
267  eeprom_bank_init(e, buffer, s->read_bank);
268 
269  /* Update indexes */
270  s->read_bank = s->write_bank;
271  s->read_page = s->write_page;
272  s->write_page += e->PagesPerEeprom;
273 
274  /* Release flash */
275  v->Power(false);
276 }
277 
278 /** Inializes the eeprom */
279 static void eeprom_init_impl(const struct eeprom_t *e)
280 {
281  const struct eeprom_vtbl_t *v = e->Vtbl;
282  struct eeprom_state_t *s = e->State;
283  const uint16_t header_size = (e->PagesPerSector + e->PageSize - 1) / e->PageSize;
284  uint8_t *buffer = alloca(EEPROM_BUFFER_SIZE(e));
285  struct eeprom_bank_t bank[BANK__COUNT];
286  struct eeprom_bank_t idx;
287  int i;
288 
289  assert(v != NULL);
290 
291  SWINT_AUTO_LOCK();
292 
293  assert(e->PagesPerEeprom + 1 <= e->PagesPerSector);
294 
295  s->opened = false;
296 
297  /* Request flash resource */
298  if (!v->Power(true))
299  return;
300 
301  /* Open both banks */
302  for (i = 0; i < BANK__COUNT; ++i) {
303  if (!eeprom_bank_open(e, buffer, i, &bank[i])) {
304  eeprom_bank_init(e, buffer, i);
305  eeprom_bank_open(e, buffer, i, &bank[i]);
306  }
307  }
308 
309 retry:
310  /* Find current read bank */
311  if (bank[BANK_1].read == 0 && bank[BANK_2].read == 0)
312  idx.read = BANK_1;
313  else if (bank[BANK_1].read == 0)
314  idx.read = BANK_2;
315  else if (bank[BANK_2].read == 0)
316  idx.read = BANK_1;
317  else if (bank[BANK_1].read < bank[BANK_2].read)
318  idx.read = BANK_1;
319  else
320  idx.read = BANK_2;
321 
322  /* Find current write bank */
323  if (bank[BANK_1].write == 0 && bank[BANK_2].write == 0) {
324  eeprom_bank_init(e, buffer, BANK_1);
325  eeprom_bank_open(e, buffer, BANK_1, &bank[BANK_1]);
326  eeprom_bank_init(e, buffer, BANK_2);
327  eeprom_bank_open(e, buffer, BANK_2, &bank[BANK_2]);
328  goto retry;
329  } else if (bank[BANK_2].write == 0)
330  idx.write = BANK_1;
331  else if (bank[BANK_1].write == 0)
332  idx.write = BANK_2;
333  else if (bank[BANK_2].write == header_size)
334  idx.write = BANK_1;
335  else if (bank[BANK_1].write == header_size)
336  idx.write = BANK_2;
337  else if (bank[BANK_1].write > bank[BANK_2].write)
338  idx.write = BANK_2;
339  else
340  idx.write = BANK_1;
341 
342  /* Initialize possibly dirty other bank */
343  if (idx.read == idx.write) {
344  uint8_t other = idx.read ^ (BANK_1 ^ BANK_2);
345 
346  if (bank[other].write != header_size || bank[other].read != 0) {
347  eeprom_bank_init(e, buffer, other);
348  eeprom_bank_open(e, buffer, other, &bank[other]);
349  goto retry;
350  }
351  }
352 
353  /* Save state */
354  s->read_bank = idx.read;
355  s->read_page = bank[idx.read].read;
356  s->write_bank = idx.write;
357  s->write_page = bank[idx.write].write;
358 
359  s->opened = true;
360 
361  /* Release flash resource */
362  v->Power(false);
363 }
364 
365 /*** External flash vtbl impl ***/
366 
367 static bool eeprom_ext_power(bool On)
368 {
369  if (On)
370  {
371  return (RequestExtFlash() == 0);
372  }
373  else
374  {
375  ReleaseExtFlash();
376  return true;
377  }
378 }
379 
380 static void eeprom_ext_erase(uint32_t SectorIndex)
381 {
382  SpiMemoryEraseSector(&ExtFlash, SectorIndex);
383 }
384 
385 static void eeprom_ext_write(uint32_t DstAddress, const void *SrcBuffer, uint32_t ByteCount)
386 {
387  SpiMemoryWrite(&ExtFlash, DstAddress, SrcBuffer, ByteCount);
388 }
389 
390 static void eeprom_ext_read(void *DstBuffer, uint32_t SrcAddress, uint32_t ByteCount)
391 {
392  SpiMemoryRead(&ExtFlash, DstBuffer, SrcAddress, ByteCount);
393 }
394 
395 /*** Internal flash vtbl impl ***/
396 
397 static bool eeprom_int_power(bool On)
398 {
399  return true;
400 }
401 
402 static void eeprom_int_erase(uint32_t SectorIndex)
403 {
404  DECLARE_FLASH_OP_MONITOR(Monitor);
405  sd_flash_page_erase(SectorIndex);
406  FLASH_OP_WAIT(Monitor);
407 }
408 
409 static void eeprom_int_write(uint32_t DstAddress, const void *SrcBuffer, uint32_t ByteCount)
410 {
411  if (DstAddress & 3)
412  {
413  uint8_t Data[4] = {0xFF, 0xFF, 0xFF, 0xFF};
414  unsigned Chunk = 4 - (DstAddress & 3);
415 
416  if (Chunk > ByteCount)
417  Chunk = ByteCount;
418 
419  memcpy(&Data[DstAddress & 3], SrcBuffer, Chunk);
420 
421  DECLARE_FLASH_OP_MONITOR(Monitor);
422  sd_flash_write((void *) (DstAddress & 3), (const uint32_t *) &Data, 1);
423  FLASH_OP_WAIT(Monitor);
424 
425  SrcBuffer = (uint8_t *) SrcBuffer + Chunk;
426  DstAddress += Chunk;
427  ByteCount -= Chunk;
428  }
429 
430  if (ByteCount >= 4)
431  {
432  DECLARE_FLASH_OP_MONITOR(Monitor);
433  sd_flash_write((void *) DstAddress, (const uint32_t *) SrcBuffer, ByteCount / 4);
434  FLASH_OP_WAIT(Monitor);
435 
436  SrcBuffer = (uint8_t *) SrcBuffer + (ByteCount & ~3);
437  DstAddress += (ByteCount & ~3);
438  ByteCount &= 3;
439  }
440 
441  if (ByteCount > 0)
442  {
443  uint8_t Data[4] = {0xFF, 0xFF, 0xFF, 0xFF};
444 
445  memcpy(&Data[0], SrcBuffer, ByteCount);
446 
447  DECLARE_FLASH_OP_MONITOR(Monitor);
448  sd_flash_write((void *) DstAddress, (const uint32_t *) &Data, 1);
449  FLASH_OP_WAIT(Monitor);
450  }
451 }
452 
453 static void eeprom_int_read(void *DstBuffer, uint32_t SrcAddress, uint32_t ByteCount)
454 {
455  memcpy(DstBuffer, (void *) SrcAddress, ByteCount);
456 }
457 
458 /*** Module API ***/
459 
460 void eeprom_write(eeprom_addr_t addr, unsigned char *buf, int size)
461 {
462  int i;
463  for (i = 0; i < EepromTableSize; ++i) {
464  if (EepromTable[i].BeginAddr <= addr && EepromTable[i].EndAddr >= addr + size) {
465  eeprom_write_impl(addr - EepromTable[i].BeginAddr, buf, size, &EepromTable[i]);
466  break;
467  }
468  }
469 }
470 
471 void eeprom_read(eeprom_addr_t addr, unsigned char *buf, int size)
472 {
473  int i;
474  for (i = 0; i < EepromTableSize; ++i) {
475  if (EepromTable[i].BeginAddr <= addr && EepromTable[i].EndAddr >= addr + size) {
476  eeprom_read_impl(addr - EepromTable[i].BeginAddr, buf, size, &EepromTable[i]);
477  break;
478  }
479  }
480 }
481 
482 void eeprom_init(void)
483 {
484  int i;
485  for (i = 0; i < EepromTableSize; ++i) {
486  eeprom_init_impl(&EepromTable[i]);
487  }
488 }
Resource manager.
EVE module stub.
uint16_t StartSector
Definition: arch-eeprom.h:71
const struct eeprom_vtbl_t * Vtbl
Definition: arch-eeprom.h:68
bool SpiMemoryWrite(const struct spi_memory_t *Memory, uint32_t DstAddress, const void *SrcBuffer, uint32_t ByteCount)
Name: SpiMemoryWrite Writes bytes from SrcBuffer to SPI memory. It the SPI memory is page based...
uint16_t PagesPerSector
Definition: arch-eeprom.h:73
uint8_t read_bank
Definition: arch-eeprom.h:52
uint8_t PagesPerEeprom
Definition: arch-eeprom.h:74
#define SWINT_AUTO_LOCK()
Definition: work.h:113
uint16_t PageSize
Definition: arch-eeprom.h:72
void eeprom_init(void)
Definition: eeprom.c:482
Header file for the EVE work scheduling.
struct eeprom_state_t * State
Definition: arch-eeprom.h:67
uint8_t write_bank
Definition: arch-eeprom.h:53
void eeprom_read(eeprom_addr_t addr, unsigned char *buf, int size)
Definition: eeprom.c:471
uint16_t write_page
Definition: arch-eeprom.h:51
Header file for the EVE event monitor primitive.
void eeprom_write(eeprom_addr_t addr, unsigned char *buf, int size)
Definition: eeprom.c:460
Driver for SPI-based memories.
bool SpiMemoryEraseSector(const struct spi_memory_t *Memory, uint32_t SectorIndex)
Name: SpiMemoryEraseSector Erases given sector in SPI memory and waits for completion. Called from main program level.
bool SpiMemoryRead(const struct spi_memory_t *Memory, void *DstBuffer, uint32_t SrcAddress, uint32_t ByteCount)
Name: SpiMemoryRead Reads bytes from SPI memory to DstBuffer. Called from main program level...
Driver for EEPROM emulation layer.
uint16_t read_page
Definition: arch-eeprom.h:50