• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * BCM947xx nvram variable access
3  *
4  * Copyright (C) 2005 Broadcom Corporation
5  * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
6  * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de>
7  *
8  * This program is free software; you can redistribute	it and/or modify it
9  * under  the terms of	the GNU General	 Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  */
13 
14 #include <linux/types.h>
15 #include <linux/module.h>
16 #include <linux/ssb/ssb.h>
17 #include <linux/kernel.h>
18 #include <linux/string.h>
19 #include <asm/addrspace.h>
20 #include <bcm47xx_nvram.h>
21 #include <asm/mach-bcm47xx/bcm47xx.h>
22 
23 static char nvram_buf[NVRAM_SPACE];
24 static const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000};
25 
find_nvram_size(u32 end)26 static u32 find_nvram_size(u32 end)
27 {
28 	struct nvram_header *header;
29 	int i;
30 
31 	for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) {
32 		header = (struct nvram_header *)KSEG1ADDR(end - nvram_sizes[i]);
33 		if (header->magic == NVRAM_HEADER)
34 			return nvram_sizes[i];
35 	}
36 
37 	return 0;
38 }
39 
40 /* Probe for NVRAM header */
nvram_find_and_copy(u32 base,u32 lim)41 static int nvram_find_and_copy(u32 base, u32 lim)
42 {
43 	struct nvram_header *header;
44 	int i;
45 	u32 off;
46 	u32 *src, *dst;
47 	u32 size;
48 
49 	/* TODO: when nvram is on nand flash check for bad blocks first. */
50 	off = FLASH_MIN;
51 	while (off <= lim) {
52 		/* Windowed flash access */
53 		size = find_nvram_size(base + off);
54 		if (size) {
55 			header = (struct nvram_header *)KSEG1ADDR(base + off -
56 								  size);
57 			goto found;
58 		}
59 		off <<= 1;
60 	}
61 
62 	/* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
63 	header = (struct nvram_header *) KSEG1ADDR(base + 4096);
64 	if (header->magic == NVRAM_HEADER) {
65 		size = NVRAM_SPACE;
66 		goto found;
67 	}
68 
69 	header = (struct nvram_header *) KSEG1ADDR(base + 1024);
70 	if (header->magic == NVRAM_HEADER) {
71 		size = NVRAM_SPACE;
72 		goto found;
73 	}
74 
75 	pr_err("no nvram found\n");
76 	return -ENXIO;
77 
78 found:
79 
80 	if (header->len > size)
81 		pr_err("The nvram size accoridng to the header seems to be bigger than the partition on flash\n");
82 	if (header->len > NVRAM_SPACE)
83 		pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n",
84 		       header->len, NVRAM_SPACE);
85 
86 	src = (u32 *) header;
87 	dst = (u32 *) nvram_buf;
88 	for (i = 0; i < sizeof(struct nvram_header); i += 4)
89 		*dst++ = *src++;
90 	for (; i < header->len && i < NVRAM_SPACE && i < size; i += 4)
91 		*dst++ = le32_to_cpu(*src++);
92 	memset(dst, 0x0, NVRAM_SPACE - i);
93 
94 	return 0;
95 }
96 
97 #ifdef CONFIG_BCM47XX_SSB
nvram_init_ssb(void)98 static int nvram_init_ssb(void)
99 {
100 	struct ssb_mipscore *mcore = &bcm47xx_bus.ssb.mipscore;
101 	u32 base;
102 	u32 lim;
103 
104 	if (mcore->pflash.present) {
105 		base = mcore->pflash.window;
106 		lim = mcore->pflash.window_size;
107 	} else {
108 		pr_err("Couldn't find supported flash memory\n");
109 		return -ENXIO;
110 	}
111 
112 	return nvram_find_and_copy(base, lim);
113 }
114 #endif
115 
116 #ifdef CONFIG_BCM47XX_BCMA
nvram_init_bcma(void)117 static int nvram_init_bcma(void)
118 {
119 	struct bcma_drv_cc *cc = &bcm47xx_bus.bcma.bus.drv_cc;
120 	u32 base;
121 	u32 lim;
122 
123 #ifdef CONFIG_BCMA_NFLASH
124 	if (cc->nflash.boot) {
125 		base = BCMA_SOC_FLASH1;
126 		lim = BCMA_SOC_FLASH1_SZ;
127 	} else
128 #endif
129 	if (cc->pflash.present) {
130 		base = cc->pflash.window;
131 		lim = cc->pflash.window_size;
132 #ifdef CONFIG_BCMA_SFLASH
133 	} else if (cc->sflash.present) {
134 		base = cc->sflash.window;
135 		lim = cc->sflash.size;
136 #endif
137 	} else {
138 		pr_err("Couldn't find supported flash memory\n");
139 		return -ENXIO;
140 	}
141 
142 	return nvram_find_and_copy(base, lim);
143 }
144 #endif
145 
nvram_init(void)146 static int nvram_init(void)
147 {
148 	switch (bcm47xx_bus_type) {
149 #ifdef CONFIG_BCM47XX_SSB
150 	case BCM47XX_BUS_TYPE_SSB:
151 		return nvram_init_ssb();
152 #endif
153 #ifdef CONFIG_BCM47XX_BCMA
154 	case BCM47XX_BUS_TYPE_BCMA:
155 		return nvram_init_bcma();
156 #endif
157 	}
158 	return -ENXIO;
159 }
160 
bcm47xx_nvram_getenv(char * name,char * val,size_t val_len)161 int bcm47xx_nvram_getenv(char *name, char *val, size_t val_len)
162 {
163 	char *var, *value, *end, *eq;
164 	int err;
165 
166 	if (!name)
167 		return -EINVAL;
168 
169 	if (!nvram_buf[0]) {
170 		err = nvram_init();
171 		if (err)
172 			return err;
173 	}
174 
175 	/* Look for name=value and return value */
176 	var = &nvram_buf[sizeof(struct nvram_header)];
177 	end = nvram_buf + sizeof(nvram_buf) - 2;
178 	end[0] = end[1] = '\0';
179 	for (; *var; var = value + strlen(value) + 1) {
180 		eq = strchr(var, '=');
181 		if (!eq)
182 			break;
183 		value = eq + 1;
184 		if ((eq - var) == strlen(name) &&
185 			strncmp(var, name, (eq - var)) == 0) {
186 			return snprintf(val, val_len, "%s", value);
187 		}
188 	}
189 	return -ENOENT;
190 }
191 EXPORT_SYMBOL(bcm47xx_nvram_getenv);
192 
bcm47xx_nvram_gpio_pin(const char * name)193 int bcm47xx_nvram_gpio_pin(const char *name)
194 {
195 	int i, err;
196 	char nvram_var[10];
197 	char buf[30];
198 
199 	for (i = 0; i < 32; i++) {
200 		err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i);
201 		if (err <= 0)
202 			continue;
203 		err = bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf));
204 		if (err <= 0)
205 			continue;
206 		if (!strcmp(name, buf))
207 			return i;
208 	}
209 	return -ENOENT;
210 }
211 EXPORT_SYMBOL(bcm47xx_nvram_gpio_pin);
212