nvram [K26 part]: auto-detect the nvram size
[tomato.git] / release / src-rt / shared / nvram / nvram.c
blob1273b92d2d05f440177c5d21e8f5854e8dd11f6a
1 /*
2 * NVRAM variable manipulation (common)
4 * Copyright 2004, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
12 * $Id$
15 #include <typedefs.h>
16 #include <bcmdefs.h>
17 #include <osl.h>
18 #include <bcmutils.h>
19 #include <linux/delay.h>
20 #include <bcmendian.h>
21 #include <bcmnvram.h>
22 #include <sbsdram.h>
24 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
25 #define get_seconds() CURRENT_TIME
26 #define jiffy_time (jiffies)
27 #else
28 #define jiffy_time (jiffies - INITIAL_JIFFIES)
29 #endif
32 extern int _nvram_init_read(void);
33 extern int nvram_space; /* Size of the NVRAM. Generally 32kb */
34 extern char nvram_buf[]; /* The buffer to hold values. */
35 extern char *nvram_commit_buf; /* Buffer for the flash eraseblock(s). */
36 extern int oflow_area_present;
38 char *_nvram_get(const char *name);
39 int _nvram_set(const char *name, const char *value);
40 int _nvram_unset(const char *name);
41 int _nvram_getall(char *buf, int count);
42 int _nvram_commit(struct nvram_header *header);
43 int _nvram_init(void *sb);
44 void _nvram_exit(void);
45 uint8 nvram_calc_crc(struct nvram_header * nvh);
46 int walk_chain(int pr);
47 struct nvram_dbitem *_nvram_realloc(struct nvram_dbitem *t, const char *name,
48 const char *value);
49 void _nvram_free(struct nvram_dbitem *t);
50 void _nvram_valbuf_compactify(void);
52 #define NUM_HLH 257
53 static struct nvram_dbitem *BCMINITDATA(nvram_hash)[NUM_HLH];
54 static struct nvram_dbitem *nvram_dead;
55 static int it_siz, it_cnt;
56 static unsigned nvram_offset = 0;
57 char sbuf[128];
59 /* Prio == write priority (actually: category)
60 * * Entries that must be in the main (original) NVRAM area, visible
61 * to PMON & CFE.
62 * * Entries that can go anywhere. PMON & CFE don't need to see these.
64 * Since there is no spec for what PMON & CFE need to see, and the list
65 * may change for different firmware versions:
66 * Assume that when "restore_defaults" != "0" then CFE cleared
67 * the NVRAM and set its defaults.
68 * Put a special flag item that is physically after the prio 1 items.
69 * When refreshing frm the flash, everything before this is defined as prio 1.
70 * Everything after it is prio 2.
71 * If it is not found, then everything is prio 1.
72 * When writing out (commit), write the prio 1 items, then the flag item,
73 * then everything else.
76 static uint16 prio;
77 #define PRIO_MARK_ITEM "}Marker42"
78 #define PRIO_MAIN 1
79 #define PRIO_ANYWHERE 2
80 #define PRIO_OFLOW 3
82 int prefer_ov = 0; /* Prefer "any" go to oflow area? */
83 int oflow_ok = 1; /* It is okay to go into oflow area? */
84 int alt = 0; // temp
86 /* Free all tuples. Should be locked. */
87 static void
88 BCMINITFN(nvram_free)(void)
90 uint i;
91 struct nvram_dbitem *t, *next;
93 /* Free hash table */
94 for (i = 0; i < NUM_HLH; i++) {
95 for (t = nvram_hash[i]; t; t = next) {
96 next = t->next;
97 _nvram_free(t);
99 nvram_hash[i] = NULL;
102 /* Free dead table */
103 for (t = nvram_dead; t; t = next) {
104 next = t->next;
105 _nvram_free(t);
107 nvram_dead = NULL;
109 /* Indicate to per-port code that all tuples have been freed */
110 _nvram_free(NULL);
113 /* String hash. FNV-1a algorithm */
114 static INLINE uint
115 hash(const char *s)
117 unsigned hval = 0x811c9dc5;
119 while (*s) {
120 hval ^= *s++;
121 hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
123 return hval;
126 /* (Re)initialize the hash table. Should be locked. */
127 static int
128 BCMINITFN(nvram_rehash)(struct nvram_header *header)
130 char buf[20], *name, *value, *end, *eq;
131 int n = 0; //temp
133 /* (Re)initialize hash table */
134 nvram_free();
136 /* Parse and set "name=value\0 ... \0\0" */
137 name = (char *) &header[1];
138 end = (char *) header + nvram_space - 2;
139 end[0] = end[1] = '\0';
140 for (; *name; name = value + strlen(value) + 1) {
141 if (!(eq = strchr(name, '=')))
142 break;
143 ++n;
144 *eq = '\0';
145 value = eq + 1;
146 _nvram_set(name, value);
147 *eq = '=';
150 /* Set special SDRAM parameters */
151 if (!_nvram_get("sdram_init")) {
152 sprintf(buf, "0x%04X", (uint16)(header->crc_ver_init >> 16));
153 _nvram_set("sdram_init", buf);
155 if (!_nvram_get("sdram_config")) {
156 sprintf(buf, "0x%04X", (uint16)(header->config_refresh & 0xffff));
157 _nvram_set("sdram_config", buf);
159 if (!_nvram_get("sdram_refresh")) {
160 sprintf(buf, "0x%04X", (uint16)((header->config_refresh >> 16) & 0xffff));
161 _nvram_set("sdram_refresh", buf);
163 if (!_nvram_get("sdram_ncdl")) {
164 sprintf(buf, "0x%08X", header->config_ncdl);
165 _nvram_set("sdram_ncdl", buf);
167 printk("Item count: %d valsiz: %u\n", n, nvram_offset);
168 return 0;
171 /* Get the value of an NVRAM variable. Should be locked. */
172 char *
173 _nvram_get(const char *name)
175 uint i;
176 struct nvram_dbitem *t, **prev;
178 if (!name)
179 return NULL;
181 if (name[0] == '=') { /* Special dynamic size info */
182 if (strcmp(name, "=nvram_used") == 0) {
183 sprintf(&nvram_buf[nvram_space - 128], "%d",
184 walk_chain(0));
185 return (&nvram_buf[nvram_space - 128]);
187 if (strcmp(name, "=nvram_space") == 0) {
188 i = nvram_space -1; /* Trailing null but no pad. */
189 if (oflow_area_present)
190 i += NVRAM_32K -1;
191 sprintf(&nvram_buf[nvram_space - 128], "%d", i);
192 return (&nvram_buf[nvram_space - 128]);
196 /* Hash the name */
197 i = hash(name) % NUM_HLH;
199 /* Find the item in the hash table */
200 for (prev = &nvram_hash[i], t = *prev;
201 t && (strcmp(t->name, name));
202 prev = &t->next, t = *prev) {}
203 if (t) {
204 *prev = t->next; /* Move it to the top of the chain - MRU. */
205 t->next = nvram_hash[i];
206 nvram_hash[i] = t;
207 return(t->value);
209 return NULL;
212 /* Set the value of an NVRAM variable. Should be locked. */
213 int
214 BCMINITFN(_nvram_set)(const char *name, const char *value)
216 uint i;
217 struct nvram_dbitem *t, *u, **prev;
219 /* Special control items. No name. Value is control command.
220 * prio_main, _any , oflow Says to flag subsequent sets accordingly.
221 * "=name" says to set prio on that name. */
222 if (*name == '\0') {
223 printk("Special: '%s'\n", value);
224 if (strcmp(value, "prio_main") == 0)
225 prio = PRIO_MAIN;
226 else if (strcmp(value, "prio_any") == 0)
227 prio = PRIO_ANYWHERE;
228 else if (strcmp(value, "prio_oflow") == 0)
229 prio = PRIO_OFLOW;
230 else if (*value == '=') { /* Set prio on this. */
231 name = value +1;
232 i = hash(name) % NUM_HLH;
233 for (prev = &nvram_hash[i], t = *prev;
234 t && (strcmp(t->name, name));
235 prev = &t->next, t = *prev) {}
236 if (t)
237 t->prio = prio;
239 else if (strcmp(value, "prefer_oflow-n") == 0)
240 prefer_ov = 0;
241 else if (strcmp(value, "prefer_oflow-y") == 0)
242 prefer_ov = 1;
243 else if (strcmp(value, "prefer_oflow-n") == 0) alt = 0; //temp
244 else if (strcmp(value, "prefer_oflow-a") == 0) alt = 1; //temp
245 else if (strcmp(value, "oflow_ok-n") == 0)
246 oflow_ok = 0;
247 else if (strcmp(value, "oflow_ok-y") == 0)
248 oflow_ok = 1;
249 else if (strcmp(value, "reset_stat") == 0) {
250 _nvram_unset("z-commit");
251 for (i = 0; ++i < 50; ) {
252 sprintf(sbuf, "z-commit_%02u", i);
253 _nvram_unset(sbuf);
256 else
257 printk("nvram: Unknown special value '%s'\n", value);
258 return 0;
261 /* Hash the name */
262 i = hash(name) % NUM_HLH;
264 /* Find the item in the hash table */
265 for (prev = &nvram_hash[i], t = *prev;
266 t && (strcmp(t->name, name));
267 prev = &t->next, t = *prev) {}
269 /* (Re)allocate tuple */
270 if (!(u = _nvram_realloc(t, name, value)))
271 return -12; /* -ENOMEM */
273 /* Value reallocated */
274 if (t && t == u) {
275 *prev = u->next; /* Move it to the top of the chain. */
276 u->next = nvram_hash[i];
277 nvram_hash[i] = u;
278 return 0;
281 #if 0
282 /* Move old tuple to the dead table */
283 // Can never get here!! It would mean that the node existed but
284 // nvram_realloc returned a different one. But it doesn't.
285 if (t) {
286 *prev = t->next;
287 t->next = nvram_dead;
288 nvram_dead = t;
290 #endif
291 /* Add new tuple to the hash table */
292 u->next = nvram_hash[i];
293 nvram_hash[i] = u;
295 return 0;
298 /* Unset the value of an NVRAM variable. Should be locked. */
299 int
300 BCMINITFN(_nvram_unset)(const char *name)
302 uint i;
303 struct nvram_dbitem *t, **prev;
305 if (!name)
306 return 0;
308 /* Hash the name */
309 i = hash(name) % NUM_HLH;
311 /* Find the item in the hash table */
312 for (prev = &nvram_hash[i], t = *prev;
313 t && (strcmp(t->name, name));
314 prev = &t->next, t = *prev) {}
316 /* Move it to the dead table */
317 if (t) {
318 *prev = t->next;
319 t->next = nvram_dead;
320 nvram_dead = t;
322 return 0;
325 /* Get all NVRAM variables. Should be locked. */
326 int
327 _nvram_getall(char *buf, int count)
329 uint i;
330 struct nvram_dbitem *t;
331 int len = 0;
333 bzero(buf, count);
335 /* Write name=value\0 ... \0\0 */
336 for (i = 0; i < NUM_HLH; i++) {
337 for (t = nvram_hash[i]; t; t = t->next) {
338 if ((count - len) > (strlen(t->name) + 1 + strlen(t->value) + 1))
339 len += sprintf(buf + len, "%s=%s", t->name, t->value) + 1;
340 else
341 break;
344 _nvram_valbuf_compactify(); //temp
345 return 0;
348 /* Regenerate NVRAM. Should be locked. */
350 BCMINITFN(_nvram_commit)(struct nvram_header *header)
352 char *init, *config, *refresh, *ncdl;
353 char *ptr;
354 unsigned int i;
355 int rem1, rem2;
356 int siz;
357 int cnt = 0, n = jiffies; // temp
358 struct nvram_dbitem *t;
360 /* Regenerate header */
361 header->magic = NVRAM_MAGIC;
362 header->crc_ver_init = (NVRAM_VERSION << 8);
363 if (!(init = _nvram_get("sdram_init")) ||
364 !(config = _nvram_get("sdram_config")) ||
365 !(refresh = _nvram_get("sdram_refresh")) ||
366 !(ncdl = _nvram_get("sdram_ncdl"))) {
367 header->crc_ver_init |= SDRAM_INIT << 16;
368 header->config_refresh = SDRAM_CONFIG;
369 header->config_refresh |= SDRAM_REFRESH << 16;
370 header->config_ncdl = 0;
371 } else {
372 header->crc_ver_init |= (bcm_strtoul(init, NULL, 0) & 0xffff) << 16;
373 header->config_refresh = bcm_strtoul(config, NULL, 0) & 0xffff;
374 header->config_refresh |= (bcm_strtoul(refresh, NULL, 0) & 0xffff) << 16;
375 header->config_ncdl = bcm_strtoul(ncdl, NULL, 0);
377 #if 1
378 /* Keep info on the commits, for development debugging. */
379 i = n; //temp
380 i = 0;
382 if ((ptr = _nvram_get("z-commit")) != NULL)
383 i = simple_strtol(ptr, NULL, 10);
384 ++i;
385 sprintf(sbuf, "%02u it_cnt, it_siz, uptime (msec), time(sec)", i);
386 _nvram_set("z-commit", sbuf);
387 if (i < 50) {
388 walk_chain(0);
389 sprintf(sbuf, "z-commit_%02u", i);
390 sprintf(sbuf +20, "%4d,%6d,%8u, %lu", it_cnt, it_siz, jiffies_to_msecs(jiffy_time), get_seconds());
391 _nvram_set(sbuf, sbuf +20);
393 #endif
394 /* Clear data area */
395 /* Leave space for a closing NUL & roundup at the end */
396 rem1 = nvram_space -1 -3 - sizeof(struct nvram_header);
397 rem2 = 0;
398 ptr = (char *)header + sizeof(struct nvram_header);
399 memset(ptr, 0xff, nvram_space - sizeof(struct nvram_header));
401 /* Write out all tuples */
402 for (i = 0; i < NUM_HLH; i++) {
403 for (t = nvram_hash[i]; t; t = t->next) {
404 ++cnt;
405 siz = strlen(t->name) + strlen(t->value) + 2;
406 if (siz > rem1) {
407 printk("NVRAM overflow at %s=%-15.15s\n", t->name, t->value);
408 break;
410 ptr += sprintf(ptr, "%s=%s", t->name, t->value) + 1;
411 rem1 -= siz;
415 *ptr++ = 0; /* Ends with an extra NUL */
416 header->len = ROUNDUP(ptr - (char *)header, 4);
417 header->crc_ver_init |= nvram_calc_crc(header);
419 /* Reinitialize hash table. Why?? Just to check. And purge. */
420 nvram_rehash(header);
421 return 0;
424 /* Initialize hash table. Should be locked. */
425 int
426 BCMINITFN(_nvram_init)(void *sb)
428 int ret;
429 struct nvram_header *header;
431 printk("jiffies: %lu msec: %u\n", jiffy_time, jiffies_to_msecs(jiffy_time)); //temp
432 ret = _nvram_init_read();
433 if (ret >= 0) {
434 header = (struct nvram_header *)(nvram_commit_buf + ret);
435 nvram_rehash(header);
436 #if 0
437 if (j >= 32 KB) {
438 oflow_area_present = 1;
439 ov_hdr = (struct nvram_header *)(nvram_commit_buf);
440 nvram_rehash_ov(ov_hdr, header);
442 #endif
444 return(ret);
447 /* Free hash table. Should be locked. */
448 void
449 BCMINITFN(_nvram_exit)(void)
451 nvram_free();
454 /* returns the CRC8 of the nvram */
455 uint8
456 BCMINITFN(nvram_calc_crc)(struct nvram_header * nvh)
458 struct nvram_header tmp;
459 uint8 crc;
461 /* Little-endian CRC8 over the last 11 bytes of the header */
462 tmp.crc_ver_init = htol32((nvh->crc_ver_init & NVRAM_CRC_VER_MASK));
463 tmp.config_refresh = htol32(nvh->config_refresh);
464 tmp.config_ncdl = htol32(nvh->config_ncdl);
466 crc = hndcrc8((uint8 *) &tmp + NVRAM_CRC_START_POSITION,
467 sizeof(struct nvram_header) - NVRAM_CRC_START_POSITION,
468 CRC8_INIT_VALUE);
470 /* Continue CRC8 over data bytes */
471 crc = hndcrc8((uint8 *) &nvh[1], nvh->len - sizeof(struct nvram_header), crc);
473 return crc;
476 /* Purge un-used value items from the value buffer.
477 * Should be locked.
479 void _nvram_valbuf_compactify(void)
481 char *wk_buf;
482 char *nxt;
483 int i;
484 int siz;
485 struct nvram_dbitem *t, *next;
486 int sz = nvram_offset; //temp
488 wk_buf = nvram_commit_buf;
489 memcpy(wk_buf, nvram_buf, nvram_offset);
490 /* Walk all tuples & copy & update value ptrs */
491 nxt = nvram_buf;
492 for (i = 0; i < NUM_HLH; i++) {
493 for (t = nvram_hash[i]; t; t = t->next) {
494 if (t->value) {
495 siz = strlen(t->value - nvram_buf + wk_buf) +1;
496 if (siz + nxt - nvram_buf > NVRAM_VAL_SIZE -4) {
497 printk("Oh crap! Over-ran valbuf during compatify. Can't happen!\n");
498 printk("We're gonna die!\n");
499 break;
501 memcpy(nxt, t->value - nvram_buf + wk_buf, siz);
502 t->value = nxt;
503 nxt += ROUNDUP_P2(siz, 4);
508 /* Free dead table */
509 for (t = nvram_dead; t; t = next) {
510 next = t->next;
511 kfree(t);
513 nvram_dead = NULL;
514 nvram_offset = nxt - nvram_buf;
515 printk("compactify: was: %d now: %d\n", sz, nvram_offset); //temp
518 /* In:
519 * t = existing node to update its value.
520 * If t is NULL, alloc a new node, and set the name & prio.
521 * In either case,
522 * If value not NULL or new value is != existing value, then
523 * copy the value param to the nvram_buf.
524 * Returns the node.
526 struct nvram_dbitem *_nvram_realloc(struct nvram_dbitem *t, const char *name, const char *value)
528 int siz;
530 siz = strlen(value) +1;
531 if ((nvram_offset + siz) >= NVRAM_VAL_SIZE -256) {
532 _nvram_valbuf_compactify();
533 if ((nvram_offset + siz) >= NVRAM_VAL_SIZE -256)
534 return NULL;
537 if (!t) {
538 if (!(t = kmalloc(sizeof(struct nvram_tuple) + strlen(name) + 1, GFP_ATOMIC)))
539 return NULL;
541 strcpy(t->name, name);
542 t->prio = prio;
543 t->value = NULL;
546 /* Copy value. Always on a word boundary.*/
547 if (!t->value || strcmp(t->value, value)) {
548 t->value = &nvram_buf[nvram_offset];
549 memcpy(t->value, value, siz);
550 nvram_offset += ROUNDUP_P2(siz, 4);
552 return t;
555 void
556 _nvram_free(struct nvram_dbitem *t)
558 if (!t)
559 nvram_offset = 0;
560 else
561 kfree(t);
565 /* Return the size the variables will take when written to NVRAM. */
566 int walk_chain(int z)
568 int i;
569 struct nvram_dbitem *t;
571 it_siz = it_cnt = 0;
572 for (i = 0; i < NUM_HLH; i++) {
573 for (t = nvram_hash[i]; t; t = t->next) {
574 it_siz += strlen(t->name) + strlen(t->value) + 2;
575 ++it_cnt;
576 if (z)
577 printk("%3d: %s=%-15.15s\n", i, t->name, t->value);
580 return (it_siz);
583 /* For the emacs code formatting
584 Local Variables:
585 c-basic-offset: 8
586 End: