1/*
2 * NVRAM variable manipulation (common)
3 *
4 * Copyright 2007, 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.
11 *
12 * $Id: nvram.c,v 1.1.1.1 2008/10/15 03:31:34 james26_jang Exp $
13 */
14
15#include <typedefs.h>
16#include <bcmdefs.h>
17#include <osl.h>
18#include <bcmutils.h>
19#include <sbutils.h>
20#include <bcmendian.h>
21#include <bcmnvram.h>
22#include <sbsdram.h>
23
24extern struct nvram_tuple * _nvram_realloc(struct nvram_tuple *t, const char *name,
25                                           const char *value);
26extern void _nvram_free(struct nvram_tuple *t);
27extern int _nvram_read(void *buf);
28
29char * _nvram_get(const char *name);
30int _nvram_set(const char *name, const char *value);
31int _nvram_unset(const char *name);
32int _nvram_getall(char *buf, int count);
33int _nvram_commit(struct nvram_header *header);
34int _nvram_init(void *sb);
35void _nvram_exit(void);
36uint8 nvram_calc_crc(struct nvram_header * nvh);
37
38static struct nvram_tuple * BCMINITDATA(nvram_hash)[257];
39static struct nvram_tuple * nvram_dead;
40
41/* Free all tuples. Should be locked. */
42static void
43BCMINITFN(nvram_free)(void)
44{
45	uint i;
46	struct nvram_tuple *t, *next;
47
48	/* Free hash table */
49	for (i = 0; i < ARRAYSIZE(nvram_hash); i++) {
50		for (t = nvram_hash[i]; t; t = next) {
51			next = t->next;
52			_nvram_free(t);
53		}
54		nvram_hash[i] = NULL;
55	}
56
57	/* Free dead table */
58	for (t = nvram_dead; t; t = next) {
59		next = t->next;
60		_nvram_free(t);
61	}
62	nvram_dead = NULL;
63
64	/* Indicate to per-port code that all tuples have been freed */
65	_nvram_free(NULL);
66}
67
68/* String hash */
69static INLINE uint
70hash(const char *s)
71{
72	uint hash = 0;
73
74	while (*s)
75		hash = 31 * hash + *s++;
76
77	return hash;
78}
79
80/* (Re)initialize the hash table. Should be locked. */
81static int
82BCMINITFN(nvram_rehash)(struct nvram_header *header)
83{
84	char buf[] = "0xXXXXXXXX", *name, *value, *end, *eq;
85
86	/* (Re)initialize hash table */
87	nvram_free();
88
89	/* Parse and set "name=value\0 ... \0\0" */
90	name = (char *) &header[1];
91	end = (char *) header + NVRAM_SPACE - 2;
92	end[0] = end[1] = '\0';
93	for (; *name; name = value + strlen(value) + 1) {
94		if (!(eq = strchr(name, '=')))
95			break;
96		*eq = '\0';
97		value = eq + 1;
98		_nvram_set(name, value);
99		*eq = '=';
100	}
101
102	/* Set special SDRAM parameters */
103	if (!_nvram_get("sdram_init")) {
104		sprintf(buf, "0x%04X", (uint16)(header->crc_ver_init >> 16));
105		_nvram_set("sdram_init", buf);
106	}
107	if (!_nvram_get("sdram_config")) {
108		sprintf(buf, "0x%04X", (uint16)(header->config_refresh & 0xffff));
109		_nvram_set("sdram_config", buf);
110	}
111	if (!_nvram_get("sdram_refresh")) {
112		sprintf(buf, "0x%04X", (uint16)((header->config_refresh >> 16) & 0xffff));
113		_nvram_set("sdram_refresh", buf);
114	}
115	if (!_nvram_get("sdram_ncdl")) {
116		sprintf(buf, "0x%08X", header->config_ncdl);
117		_nvram_set("sdram_ncdl", buf);
118	}
119
120	return 0;
121}
122
123/* Get the value of an NVRAM variable. Should be locked. */
124char *
125_nvram_get(const char *name)
126{
127	uint i;
128	struct nvram_tuple *t;
129	char *value;
130
131	if (!name)
132		return NULL;
133
134	/* Hash the name */
135	i = hash(name) % ARRAYSIZE(nvram_hash);
136
137	/* Find the associated tuple in the hash table */
138	for (t = nvram_hash[i]; t && strcmp(t->name, name); t = t->next);
139
140	value = t ? t->value : NULL;
141
142	return value;
143}
144
145/* Set the value of an NVRAM variable. Should be locked. */
146int
147BCMINITFN(_nvram_set)(const char *name, const char *value)
148{
149	uint i;
150	struct nvram_tuple *t, *u, **prev;
151
152	/* Hash the name */
153	i = hash(name) % ARRAYSIZE(nvram_hash);
154
155	/* Find the associated tuple in the hash table */
156	for (prev = &nvram_hash[i], t = *prev; t && strcmp(t->name, name);
157	     prev = &t->next, t = *prev);
158
159	/* (Re)allocate tuple */
160	if (!(u = _nvram_realloc(t, name, value)))
161		return -12; /* -ENOMEM */
162
163	/* Value reallocated */
164	if (t && t == u)
165		return 0;
166
167	/* Move old tuple to the dead table */
168	if (t) {
169		*prev = t->next;
170		t->next = nvram_dead;
171		nvram_dead = t;
172	}
173
174	/* Add new tuple to the hash table */
175	u->next = nvram_hash[i];
176	nvram_hash[i] = u;
177
178	return 0;
179}
180
181/* Unset the value of an NVRAM variable. Should be locked. */
182int
183BCMINITFN(_nvram_unset)(const char *name)
184{
185	uint i;
186	struct nvram_tuple *t, **prev;
187
188	if (!name)
189		return 0;
190
191	/* Hash the name */
192	i = hash(name) % ARRAYSIZE(nvram_hash);
193
194	/* Find the associated tuple in the hash table */
195	for (prev = &nvram_hash[i], t = *prev; t && strcmp(t->name, name);
196	     prev = &t->next, t = *prev);
197
198	/* Move it to the dead table */
199	if (t) {
200		*prev = t->next;
201		t->next = nvram_dead;
202		nvram_dead = t;
203	}
204
205	return 0;
206}
207
208/* Get all NVRAM variables. Should be locked. */
209int
210_nvram_getall(char *buf, int count)
211{
212	uint i;
213	struct nvram_tuple *t;
214	int len = 0;
215
216	bzero(buf, count);
217
218	/* Write name=value\0 ... \0\0 */
219	for (i = 0; i < ARRAYSIZE(nvram_hash); i++) {
220		for (t = nvram_hash[i]; t; t = t->next) {
221			if ((count - len) > (strlen(t->name) + 1 + strlen(t->value) + 1))
222				len += sprintf(buf + len, "%s=%s", t->name, t->value) + 1;
223			else
224				break;
225		}
226	}
227
228	return 0;
229}
230
231/* Regenerate NVRAM. Should be locked. */
232int
233BCMINITFN(_nvram_commit)(struct nvram_header *header)
234{
235	char *init, *config, *refresh, *ncdl;
236	char *ptr, *end;
237	int i;
238	struct nvram_tuple *t;
239
240	/* Regenerate header */
241	header->magic = NVRAM_MAGIC;
242	header->crc_ver_init = (NVRAM_VERSION << 8);
243	if (!(init = _nvram_get("sdram_init")) ||
244	    !(config = _nvram_get("sdram_config")) ||
245	    !(refresh = _nvram_get("sdram_refresh")) ||
246	    !(ncdl = _nvram_get("sdram_ncdl"))) {
247		header->crc_ver_init |= SDRAM_INIT << 16;
248		header->config_refresh = SDRAM_CONFIG;
249		header->config_refresh |= SDRAM_REFRESH << 16;
250		header->config_ncdl = 0;
251	} else {
252		header->crc_ver_init |= (bcm_strtoul(init, NULL, 0) & 0xffff) << 16;
253		header->config_refresh = bcm_strtoul(config, NULL, 0) & 0xffff;
254		header->config_refresh |= (bcm_strtoul(refresh, NULL, 0) & 0xffff) << 16;
255		header->config_ncdl = bcm_strtoul(ncdl, NULL, 0);
256	}
257
258	/* Clear data area */
259	ptr = (char *) header + sizeof(struct nvram_header);
260	bzero(ptr, NVRAM_SPACE - sizeof(struct nvram_header));
261
262	/* Leave space for a double NUL at the end */
263	end = (char *) header + NVRAM_SPACE - 2;
264
265	/* Write out all tuples */
266	for (i = 0; i < ARRAYSIZE(nvram_hash); i++) {
267		for (t = nvram_hash[i]; t; t = t->next) {
268			if ((ptr + strlen(t->name) + 1 + strlen(t->value) + 1) > end)
269				break;
270			ptr += sprintf(ptr, "%s=%s", t->name, t->value) + 1;
271		}
272	}
273
274	/* End with a double NUL */
275	ptr += 2;
276
277	/* Set new length */
278	header->len = ROUNDUP(ptr - (char *) header, 4);
279
280	/* Set new CRC8 */
281	header->crc_ver_init |= nvram_calc_crc(header);
282
283	/* Reinitialize hash table */
284	return nvram_rehash(header);
285}
286
287/* Initialize hash table. Should be locked. */
288int
289BCMINITFN(_nvram_init)(void *sb)
290{
291	struct nvram_header *header;
292	int ret;
293
294
295	if (!(header = (struct nvram_header *) MALLOC(sb_osh(sb), NVRAM_SPACE))) {
296		printf("nvram_init: out of memory\n");
297		return -12; /* -ENOMEM */
298	}
299
300	if ((ret = _nvram_read(header)) == 0 &&
301	    header->magic == NVRAM_MAGIC)
302		nvram_rehash(header);
303
304	MFREE(sb_osh(sb), header, NVRAM_SPACE);
305	return ret;
306}
307
308/* Free hash table. Should be locked. */
309void
310BCMINITFN(_nvram_exit)(void)
311{
312	nvram_free();
313}
314
315/* returns the CRC8 of the nvram */
316uint8
317BCMINITFN(nvram_calc_crc)(struct nvram_header * nvh)
318{
319	struct nvram_header tmp;
320	uint8 crc;
321
322	/* Little-endian CRC8 over the last 11 bytes of the header */
323	tmp.crc_ver_init = htol32((nvh->crc_ver_init & NVRAM_CRC_VER_MASK));
324	tmp.config_refresh = htol32(nvh->config_refresh);
325	tmp.config_ncdl = htol32(nvh->config_ncdl);
326
327	crc = hndcrc8((uint8 *) &tmp + NVRAM_CRC_START_POSITION,
328		sizeof(struct nvram_header) - NVRAM_CRC_START_POSITION,
329		CRC8_INIT_VALUE);
330
331	/* Continue CRC8 over data bytes */
332	crc = hndcrc8((uint8 *) &nvh[1], nvh->len - sizeof(struct nvram_header), crc);
333
334	return crc;
335}
336