1 /*
2 * Copyright (c) 2023 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "eeprom_emulation.h"
13 #include "hpm_crc32.h"
14
15 #define E2P_OFFSET(TYPE, MEMBER) ((uint32_t)(&(((TYPE *)0)->MEMBER)))
16 #define E2P_SWAP(a, b)\
17 do {\
18 uint32_t tmp = (a);\
19 (a) = (b);\
20 (b) = tmp;\
21 } while (0)
22
23 static e2p_block e2p_info_table[E2P_MAX_VAR_CNT];
24
e2p_print_info(e2p_t * e2p)25 static void e2p_print_info(e2p_t *e2p)
26 {
27 uint32_t info_count;
28 uint32_t valid_count = E2P_MAX_VAR_CNT;
29
30 info_count = (e2p->config.start_addr + e2p->config.sector_cnt * e2p->config.erase_size - e2p->p_info) / sizeof(e2p_block) - 2;
31 for (int i = 0; i < E2P_MAX_VAR_CNT; i++) {
32 if (e2p_info_table[i].block_id == E2P_EARSED_ID) {
33 valid_count = i;
34 break;
35 }
36 }
37
38 e2p_info("------------ flash->eeprom init ok -----------\n");
39 e2p_info("start address: 0x%08x", e2p->config.start_addr);
40 e2p_info("sector count: %u", e2p->config.sector_cnt);
41 e2p_info("flash earse granularity: %u", e2p->config.erase_size);
42 e2p_info("version: 0x%x", e2p->config.version);
43 e2p_info("end address: 0x%08x", e2p->config.start_addr + e2p->config.sector_cnt * e2p->config.erase_size);
44 e2p_info("data write addr = 0x%08x, info write addr = 0x%08x, remain flash size = 0x%x\n", \
45 e2p->p_data, e2p->p_info, e2p->remain_size);
46 e2p_info("valid count percent info count( %u / %u )\n", valid_count, info_count);
47 e2p_info("----------------------------------------------\n");
48 }
49
e2p_table_update(e2p_block * block)50 static hpm_stat_t e2p_table_update(e2p_block *block)
51 {
52 uint32_t i = 0;
53
54 if (block->valid_state == e2p_invalid)
55 return E2P_STATUS_OK;
56
57 for ( ; i < E2P_MAX_VAR_CNT; i++) {
58 if (e2p_info_table[i].block_id == E2P_EARSED_ID)
59 break;
60 if (e2p_info_table[i].block_id == block->block_id) {
61 e2p_trace("block_id[0x%08x] multiple write, flush api solve repeat\n", block->block_id);
62 break;
63 }
64 }
65
66 if (i == E2P_MAX_VAR_CNT)
67 return E2P_ERROR_MUL_VAR;
68
69 memcpy(&e2p_info_table[i], block, sizeof(e2p_block));
70 return E2P_STATUS_OK;
71 }
72
e2p_config_info(e2p_t * e2p)73 static void e2p_config_info(e2p_t *e2p)
74 {
75 e2p_block block;
76 e2p_config_t *cfg = &e2p->config;
77 uint32_t end_addr = cfg->start_addr + cfg->sector_cnt * cfg->erase_size;
78
79 memset(&block, E2P_EARSED_VAR, sizeof(e2p_block));
80 block.block_id = cfg->version;
81 block.data_addr = E2P_MAGIC_ID;
82 cfg->flash_write((uint8_t *)&block, end_addr - sizeof(e2p_block), sizeof(e2p_block));
83 e2p_trace("e2p info has been config\n");
84 }
85
e2p_data_crc_calc(uint16_t length,uint8_t * data)86 static uint32_t e2p_data_crc_calc(uint16_t length, uint8_t *data)
87 {
88 return crc32(data, (uint32_t)length);
89 }
90
e2p_retrieve_info(uint32_t block_id,e2p_block * block)91 static hpm_stat_t e2p_retrieve_info(uint32_t block_id, e2p_block *block)
92 {
93 int i = 0;
94
95 if (block_id == E2P_EARSED_ID)
96 return E2P_ERROR_BAD_ID;
97
98 for ( ; i < E2P_MAX_VAR_CNT; i++) {
99 if (e2p_info_table[i].block_id == block_id) {
100 e2p_trace("find read block, pos at table[%u]\n", i);
101 memcpy(block, &e2p_info_table[i], sizeof(e2p_block));
102 break;
103 }
104 }
105
106 return (i == E2P_MAX_VAR_CNT ? E2P_ERROR_BAD_ID : E2P_STATUS_OK);
107 }
108
109
e2p_write_private(e2p_t * e2p,uint32_t block_id,uint16_t length,uint8_t * data)110 static hpm_stat_t e2p_write_private(e2p_t *e2p, uint32_t block_id, uint16_t length, uint8_t *data)
111 {
112 int ret = 0;
113 e2p_block block;
114 e2p_config_t *cfg = &e2p->config;
115
116 if (e2p->remain_size < length + sizeof(e2p_block)) {
117 e2p_trace("no enough flash\n");
118 return E2P_ERROR_NO_MEM;
119 }
120
121 block.block_id = block_id;
122 block.data_addr = e2p->p_data;
123 block.length = length;
124 block.valid_state = e2p_valid;
125 block.crc = e2p_data_crc_calc(length, data);
126
127 if (E2P_STATUS_OK != cfg->flash_write(data, e2p->p_data, length)) {
128 e2p_trace("flash write data error\n");
129 return E2P_ERROR;
130 }
131 e2p->p_data += length;
132 e2p->remain_size -= length;
133
134 if (E2P_STATUS_OK != cfg->flash_write((uint8_t *)&block, e2p->p_info, sizeof(e2p_block))) {
135 e2p_trace("flash write info error\n");
136 return E2P_ERROR;
137 }
138 e2p->p_info -= sizeof(e2p_block);
139 e2p->remain_size -= sizeof(e2p_block);
140
141 ret = e2p_table_update(&block);
142 if (E2P_STATUS_OK != ret)
143 return ret;
144
145 e2p_info("block_id[0x%08x] success write, data addr=0x%08x, remain size=0x%08x crc=0x%08x\n", block.block_id, block.data_addr, e2p->remain_size, block.crc);
146 return E2P_STATUS_OK;
147 }
148
e2p_info_table_sort(void)149 static int e2p_info_table_sort(void)
150 {
151 int count = 0;
152 e2p_block block;
153
154 do {
155 if (e2p_info_table[count].block_id == E2P_EARSED_ID) {
156 break;
157 }
158 count++;
159 } while (count < E2P_MAX_VAR_CNT);
160
161 if (count == 0)
162 return count;
163
164 for (int i = 0; i < count; i++) {
165 memcpy(&block, &e2p_info_table[i], sizeof(e2p_block));
166 for (int j = i + 1; j < count; j++) {
167 if (e2p_info_table[j].data_addr < block.data_addr) {
168 E2P_SWAP(e2p_info_table[j].block_id, block.block_id);
169 E2P_SWAP(e2p_info_table[j].data_addr, block.data_addr);
170 E2P_SWAP(e2p_info_table[j].length, block.length);
171 E2P_SWAP(e2p_info_table[j].valid_state, block.valid_state);
172 E2P_SWAP(e2p_info_table[j].crc, block.crc);
173 }
174 }
175 memcpy(&e2p_info_table[i], &block, sizeof(e2p_block));
176 }
177
178 return count;
179 }
180
e2p_earse_info_sector(e2p_t * e2p)181 static void e2p_earse_info_sector(e2p_t *e2p)
182 {
183 uint32_t data_sec_num, info_sec_num;
184 uint32_t addr, size;
185 e2p_config_t *cfg = &e2p->config;
186
187 data_sec_num = (e2p->p_data - cfg->start_addr) / cfg->erase_size;
188 info_sec_num = (e2p->p_info - cfg->start_addr) / cfg->erase_size;
189 addr = e2p->p_info - (e2p->p_info % cfg->erase_size);
190 size = (cfg->sector_cnt - info_sec_num) * cfg->erase_size;
191
192 e2p_trace("data_sector=%u, info_sector=%u, addr=0x%08x, size=0x%08x", data_sec_num, info_sec_num, addr, size);
193
194 if (data_sec_num < info_sec_num) {
195 cfg->flash_erase(addr, size);
196 } else {
197 uint8_t data_buf[cfg->erase_size];
198 uint32_t remain_size = e2p->p_info % cfg->erase_size;
199 memset(data_buf, E2P_EARSED_VAR, sizeof(data_buf));
200
201 e2p_trace("data tail[0x%08x] and info tail[0x%08x] in same sector\n", e2p->p_data, e2p->p_info);
202 cfg->flash_read(data_buf, addr, remain_size);
203 cfg->flash_erase(addr, size);
204 cfg->flash_write(data_buf, addr, sizeof(data_buf));
205 }
206 }
207
e2p_config(e2p_t * e2p)208 hpm_stat_t e2p_config(e2p_t *e2p)
209 {
210 if (e2p->config.erase_size == 0 || e2p->config.sector_cnt == 0) {
211 e2p_info("config error erase_size = %u, sector_cnt = %u\n", e2p->config.erase_size, e2p->config.sector_cnt);
212 return E2P_ERROR_INIT_ERR;
213 }
214
215 if (e2p->config.flash_read == NULL || e2p->config.flash_write == NULL || e2p->config.flash_erase == NULL) {
216 e2p_info("Not register operate function read = %p, write = %p, erase = %p", \
217 e2p->config.flash_read, e2p->config.flash_write, e2p->config.flash_erase);
218 return E2P_ERROR_INIT_ERR;
219 }
220
221 e2p_block block;
222 e2p_config_t *cfg = &e2p->config;
223
224 e2p->p_data = cfg->start_addr;
225 e2p->p_info = cfg->start_addr + cfg->sector_cnt * cfg->erase_size - 2 * sizeof(e2p_block);
226 e2p->remain_size = e2p->p_info - e2p->p_data;
227 memset(e2p_info_table, E2P_EARSED_VAR, sizeof(e2p_info_table));
228
229 cfg->flash_read((uint8_t *)&block, e2p->p_info + sizeof(e2p_block), sizeof(e2p_block));
230 e2p_trace("read data, block_id=%x, addr=%x, length=%x, valid_state=%x, crc=%x\n", \
231 block.block_id, block.data_addr, block.length, block.valid_state, block.crc);
232
233 if (block.block_id != cfg->version || block.data_addr != E2P_MAGIC_ID) {
234 e2p_info("check version failed, begin earse all sector, it will take some time\n");
235 cfg->flash_erase(e2p->p_data, cfg->sector_cnt * cfg->erase_size);
236 e2p_config_info(e2p);
237 e2p_print_info(e2p);
238 return E2P_STATUS_OK;
239 }
240
241 while (1) {
242 block.data_addr = e2p->p_info;
243 block.length = sizeof(e2p_block);
244
245 cfg->flash_read((uint8_t *)&block, block.data_addr, block.length);
246 if (block.block_id == E2P_EARSED_ID)
247 break;
248
249 int ret = e2p_table_update(&block);
250 if (E2P_STATUS_OK != ret)
251 return ret;
252
253 e2p->p_data += block.length;
254 e2p->p_info -= sizeof(e2p_block);
255 e2p->remain_size -= (block.length + sizeof(e2p_block));
256
257 if (e2p->remain_size < 2 * sizeof(e2p_block)) {
258 e2p_trace("remain flash is not enough\n");
259 return E2P_ERROR_NO_MEM;
260 }
261 }
262
263 e2p_print_info(e2p);
264 return E2P_STATUS_OK;
265 }
266
e2p_flush(e2p_t * e2p,uint8_t flag)267 hpm_stat_t e2p_flush(e2p_t *e2p, uint8_t flag)
268 {
269 if (e2p->config.erase_size < e2p->remain_size && flag == 0) {
270 e2p_trace("no need arrange flash\n");
271 return E2P_STATUS_OK;
272 }
273 e2p_config_t *cfg = &e2p->config;
274
275 e2p_earse_info_sector(e2p);
276
277 e2p->p_data = cfg->start_addr;
278 e2p->p_info = cfg->start_addr + cfg->sector_cnt * cfg->erase_size - 2 * sizeof(e2p_block);
279 e2p->remain_size = e2p->p_info - e2p->p_data;
280
281 int read_len = 0;
282 int count = 0;
283 uint32_t head, tail;
284 uint8_t read_buf[cfg->erase_size * 2];
285 int valid_num = e2p_info_table_sort();
286
287 tail = e2p->p_data;
288 for (int i = 0; i < valid_num;) {
289 head = tail;
290
291 while (1) {
292 if (e2p_info_table[i].block_id == E2P_EARSED_ID || i >= valid_num) {
293 e2p_trace("e2p blank[%u], need flush num[%u]\n", i, valid_num);
294 break;
295 }
296 cfg->flash_read(read_buf + read_len, e2p_info_table[i].data_addr, e2p_info_table[i].length);
297 read_len += e2p_info_table[i].length;
298 tail = e2p_info_table[i].data_addr + e2p_info_table[i].length;
299 i++;
300 if (read_len >= cfg->erase_size)
301 break;
302 }
303
304 e2p_trace("---- transfer data to buffer: len=%u\n", read_len);
305
306 do {
307 cfg->flash_erase(head, cfg->erase_size);
308 head += cfg->erase_size;
309 } while (head + cfg->erase_size <= tail);
310
311 tail = head;
312 uint8_t *pdata = read_buf;
313 while (count < i) {
314 if (e2p_info_table[count].block_id == E2P_EARSED_ID || e2p->p_data + e2p_info_table[i].length >= tail) {
315 e2p_trace("write back suspend, write stop at 0x%08x/0x0%x\n", e2p->p_data, read_len);
316 break;
317 }
318
319 e2p_write_private(e2p, e2p_info_table[count].block_id, e2p_info_table[count].length, pdata);
320 pdata += e2p_info_table[count].length;
321 count++;
322 }
323
324 e2p_trace("-----write data back: len=%u\n", pdata - read_buf);
325
326 read_len -= (pdata - read_buf);
327 if (read_len)
328 memmove(read_buf, pdata, read_len);
329 }
330
331 while (tail < e2p->p_info - cfg->erase_size) {
332 cfg->flash_erase(tail, cfg->erase_size);
333 tail += cfg->erase_size;
334 }
335
336 uint8_t *ptr = read_buf;
337 while (read_len) {
338 e2p_trace("remain write back[%u], block_id[%x], data_addr[%x], length[%u], valid_state[%u], crc[%x]\n", \
339 count, e2p_info_table[count].block_id, e2p_info_table[count].data_addr, e2p_info_table[count].length, e2p_info_table[count].valid_state, e2p_info_table[count].crc);
340 e2p_write_private(e2p, e2p_info_table[count].block_id, e2p_info_table[count].length, ptr);
341 read_len -= e2p_info_table[count].length;
342 ptr += e2p_info_table[count].length;
343 count++;
344 }
345
346 e2p_config_info(e2p);
347 return E2P_STATUS_OK;
348 }
349
e2p_write(e2p_t * e2p,uint32_t block_id,uint16_t length,uint8_t * data)350 hpm_stat_t e2p_write(e2p_t *e2p, uint32_t block_id, uint16_t length, uint8_t *data)
351 {
352 if (e2p->remain_size < length + sizeof(e2p_block)) {
353 e2p_flush(e2p, E2P_FLUSH_BEGIN);
354 if (e2p->remain_size < length + sizeof(e2p_block)) {
355 e2p_trace("no enough flash write\n");
356 return E2P_ERROR_NO_MEM;
357 }
358 }
359
360 return e2p_write_private(e2p, block_id, length, data);
361 }
362
e2p_read(e2p_t * e2p,uint32_t block_id,uint16_t length,uint8_t * data)363 hpm_stat_t e2p_read(e2p_t *e2p, uint32_t block_id, uint16_t length, uint8_t *data)
364 {
365 e2p_block block;
366 int ret = 0;
367
368 ret = e2p_retrieve_info(block_id, &block);
369 if (ret != E2P_STATUS_OK)
370 return ret;
371
372 uint8_t tmp[block.length];
373 e2p->config.flash_read(tmp, block.data_addr, block.length);
374
375 if (block.crc != e2p_data_crc_calc(block.length, tmp)) {
376 e2p_trace("crc check error, data addr = 0x%08x, crc = 0x%08x", block.data_addr, block.crc);
377 return E2P_ERROR;
378 }
379
380 length > block.length ? (length=block.length) : length;
381 memmove(data, tmp, length);
382 return E2P_STATUS_OK;
383 }
384
e2p_format(e2p_t * e2p)385 void e2p_format(e2p_t *e2p)
386 {
387 e2p_config_t *cfg = &e2p->config;
388
389 cfg->flash_erase(cfg->start_addr, cfg->sector_cnt * cfg->erase_size);
390 }
391
e2p_generate_id(const char * name)392 uint32_t e2p_generate_id(const char *name)
393 {
394 return (name[0] << 24) | (name[1] << 16) | (name[2] << 8) | (name[3]);
395 }
396
397
e2p_show_info(e2p_t * e2p)398 void e2p_show_info(e2p_t *e2p)
399 {
400 e2p_print_info(e2p);
401 }
402