aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware/broadcom
diff options
context:
space:
mode:
authorRafał Miłecki <zajec5@gmail.com>2015-06-10 17:05:08 -0400
committerRalf Baechle <ralf@linux-mips.org>2015-06-21 15:55:33 -0400
commitf6e734a8c162297953d7bfc0f3f6bf4f8c33d72f (patch)
tree285ec89e3f7d713f42f66cd1ba16dc637976c26f /drivers/firmware/broadcom
parent1e51714c81e11bd0ffbb2b0724a1f66ce58608a5 (diff)
MIPS: BCM47xx: Move NVRAM driver to the drivers/firmware/
After Broadcom switched from MIPS to ARM for their home routers we need to have NVRAM driver in some common place (not arch/mips/). As explained in Kconfig, this driver is responsible for parsing SoC configuration data that is passed to the kernel in flash from the bootloader firmware called "CFE". We were thinking about putting it in bus directory, however there are two possible buses for MIPS: drivers/ssb/ and drivers/bcma/. So this won't fit there and this is why I would like to move this driver to the drivers/firmware/. Signed-off-by: Rafał Miłecki <zajec5@gmail.com> Reviewed-by: Paul Walmsley <paul@pwsan.com> Cc: linux-mips@linux-mips.org Cc: Hauke Mehrtens <hauke@hauke-m.de> Cc: Seiji Aguchi <seiji.aguchi@hds.com> Cc: Greg Kroah-Hartman <gregkh@suse.de> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Mike Waychison <mikew@google.com> Cc: Roy Franz <roy.franz@linaro.org> Cc: Matt Fleming <matt.fleming@intel.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Patchwork: https://patchwork.linux-mips.org/patch/10544/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'drivers/firmware/broadcom')
-rw-r--r--drivers/firmware/broadcom/Kconfig11
-rw-r--r--drivers/firmware/broadcom/Makefile1
-rw-r--r--drivers/firmware/broadcom/bcm47xx_nvram.c248
3 files changed, 260 insertions, 0 deletions
diff --git a/drivers/firmware/broadcom/Kconfig b/drivers/firmware/broadcom/Kconfig
new file mode 100644
index 000000000000..6bed119930dd
--- /dev/null
+++ b/drivers/firmware/broadcom/Kconfig
@@ -0,0 +1,11 @@
1config BCM47XX_NVRAM
2 bool "Broadcom NVRAM driver"
3 depends on BCM47XX || ARCH_BCM_5301X
4 help
5 Broadcom home routers contain flash partition called "nvram" with all
6 important hardware configuration as well as some minor user setup.
7 NVRAM partition contains a text-like data representing name=value
8 pairs.
9 This driver provides an easy way to get value of requested parameter.
10 It simply reads content of NVRAM and parses it. It doesn't control any
11 hardware part itself.
diff --git a/drivers/firmware/broadcom/Makefile b/drivers/firmware/broadcom/Makefile
new file mode 100644
index 000000000000..d0e683583cd6
--- /dev/null
+++ b/drivers/firmware/broadcom/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx_nvram.o
diff --git a/drivers/firmware/broadcom/bcm47xx_nvram.c b/drivers/firmware/broadcom/bcm47xx_nvram.c
new file mode 100644
index 000000000000..87add3fdce52
--- /dev/null
+++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
@@ -0,0 +1,248 @@
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/io.h>
15#include <linux/types.h>
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/string.h>
19#include <linux/mtd/mtd.h>
20#include <linux/bcm47xx_nvram.h>
21
22#define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */
23#define NVRAM_SPACE 0x10000
24#define NVRAM_MAX_GPIO_ENTRIES 32
25#define NVRAM_MAX_GPIO_VALUE_LEN 30
26
27#define FLASH_MIN 0x00020000 /* Minimum flash size */
28
29struct nvram_header {
30 u32 magic;
31 u32 len;
32 u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */
33 u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */
34 u32 config_ncdl; /* ncdl values for memc */
35};
36
37static char nvram_buf[NVRAM_SPACE];
38static size_t nvram_len;
39static const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000};
40
41static u32 find_nvram_size(void __iomem *end)
42{
43 struct nvram_header __iomem *header;
44 int i;
45
46 for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) {
47 header = (struct nvram_header *)(end - nvram_sizes[i]);
48 if (header->magic == NVRAM_MAGIC)
49 return nvram_sizes[i];
50 }
51
52 return 0;
53}
54
55/* Probe for NVRAM header */
56static int nvram_find_and_copy(void __iomem *iobase, u32 lim)
57{
58 struct nvram_header __iomem *header;
59 int i;
60 u32 off;
61 u32 *src, *dst;
62 u32 size;
63
64 if (nvram_len) {
65 pr_warn("nvram already initialized\n");
66 return -EEXIST;
67 }
68
69 /* TODO: when nvram is on nand flash check for bad blocks first. */
70 off = FLASH_MIN;
71 while (off <= lim) {
72 /* Windowed flash access */
73 size = find_nvram_size(iobase + off);
74 if (size) {
75 header = (struct nvram_header *)(iobase + off - size);
76 goto found;
77 }
78 off <<= 1;
79 }
80
81 /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
82 header = (struct nvram_header *)(iobase + 4096);
83 if (header->magic == NVRAM_MAGIC) {
84 size = NVRAM_SPACE;
85 goto found;
86 }
87
88 header = (struct nvram_header *)(iobase + 1024);
89 if (header->magic == NVRAM_MAGIC) {
90 size = NVRAM_SPACE;
91 goto found;
92 }
93
94 pr_err("no nvram found\n");
95 return -ENXIO;
96
97found:
98 src = (u32 *)header;
99 dst = (u32 *)nvram_buf;
100 for (i = 0; i < sizeof(struct nvram_header); i += 4)
101 *dst++ = __raw_readl(src++);
102 header = (struct nvram_header *)nvram_buf;
103 nvram_len = header->len;
104 if (nvram_len > size) {
105 pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n");
106 nvram_len = size;
107 }
108 if (nvram_len >= NVRAM_SPACE) {
109 pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n",
110 header->len, NVRAM_SPACE - 1);
111 nvram_len = NVRAM_SPACE - 1;
112 }
113 /* proceed reading data after header */
114 for (; i < nvram_len; i += 4)
115 *dst++ = readl(src++);
116 nvram_buf[NVRAM_SPACE - 1] = '\0';
117
118 return 0;
119}
120
121/*
122 * On bcm47xx we need access to the NVRAM very early, so we can't use mtd
123 * subsystem to access flash. We can't even use platform device / driver to
124 * store memory offset.
125 * To handle this we provide following symbol. It's supposed to be called as
126 * soon as we get info about flash device, before any NVRAM entry is needed.
127 */
128int bcm47xx_nvram_init_from_mem(u32 base, u32 lim)
129{
130 void __iomem *iobase;
131 int err;
132
133 iobase = ioremap_nocache(base, lim);
134 if (!iobase)
135 return -ENOMEM;
136
137 err = nvram_find_and_copy(iobase, lim);
138
139 iounmap(iobase);
140
141 return err;
142}
143
144static int nvram_init(void)
145{
146#ifdef CONFIG_MTD
147 struct mtd_info *mtd;
148 struct nvram_header header;
149 size_t bytes_read;
150 int err;
151
152 mtd = get_mtd_device_nm("nvram");
153 if (IS_ERR(mtd))
154 return -ENODEV;
155
156 err = mtd_read(mtd, 0, sizeof(header), &bytes_read, (uint8_t *)&header);
157 if (!err && header.magic == NVRAM_MAGIC &&
158 header.len > sizeof(header)) {
159 nvram_len = header.len;
160 if (nvram_len >= NVRAM_SPACE) {
161 pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n",
162 header.len, NVRAM_SPACE);
163 nvram_len = NVRAM_SPACE - 1;
164 }
165
166 err = mtd_read(mtd, 0, nvram_len, &nvram_len,
167 (u8 *)nvram_buf);
168 return err;
169 }
170#endif
171
172 return -ENXIO;
173}
174
175int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len)
176{
177 char *var, *value, *end, *eq;
178 int err;
179
180 if (!name)
181 return -EINVAL;
182
183 if (!nvram_len) {
184 err = nvram_init();
185 if (err)
186 return err;
187 }
188
189 /* Look for name=value and return value */
190 var = &nvram_buf[sizeof(struct nvram_header)];
191 end = nvram_buf + sizeof(nvram_buf);
192 while (var < end && *var) {
193 eq = strchr(var, '=');
194 if (!eq)
195 break;
196 value = eq + 1;
197 if (eq - var == strlen(name) &&
198 strncmp(var, name, eq - var) == 0)
199 return snprintf(val, val_len, "%s", value);
200 var = value + strlen(value) + 1;
201 }
202 return -ENOENT;
203}
204EXPORT_SYMBOL(bcm47xx_nvram_getenv);
205
206int bcm47xx_nvram_gpio_pin(const char *name)
207{
208 int i, err;
209 char nvram_var[] = "gpioXX";
210 char buf[NVRAM_MAX_GPIO_VALUE_LEN];
211
212 /* TODO: Optimize it to don't call getenv so many times */
213 for (i = 0; i < NVRAM_MAX_GPIO_ENTRIES; i++) {
214 err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i);
215 if (err <= 0)
216 continue;
217 err = bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf));
218 if (err <= 0)
219 continue;
220 if (!strcmp(name, buf))
221 return i;
222 }
223 return -ENOENT;
224}
225EXPORT_SYMBOL(bcm47xx_nvram_gpio_pin);
226
227char *bcm47xx_nvram_get_contents(size_t *nvram_size)
228{
229 int err;
230 char *nvram;
231
232 if (!nvram_len) {
233 err = nvram_init();
234 if (err)
235 return NULL;
236 }
237
238 *nvram_size = nvram_len - sizeof(struct nvram_header);
239 nvram = vmalloc(*nvram_size);
240 if (!nvram)
241 return NULL;
242 memcpy(nvram, &nvram_buf[sizeof(struct nvram_header)], *nvram_size);
243
244 return nvram;
245}
246EXPORT_SYMBOL(bcm47xx_nvram_get_contents);
247
248MODULE_LICENSE("GPLv2");