1121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb/*
2121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * BCM947xx nvram variable access
3121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb *
4121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * Copyright (C) 2005 Broadcom Corporation
5121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
6f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de>
7121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb *
87034228792cc561e79ff8600f02884bd4c80e287Ralf Baechle * This program is free software; you can redistribute	it and/or modify it
97034228792cc561e79ff8600f02884bd4c80e287Ralf Baechle * under  the terms of	the GNU General	 Public License as published by the
10121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * Free Software Foundation;  either version 2 of the  License, or (at your
11121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * option) any later version.
12121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb */
13121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
14121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <linux/types.h>
15121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <linux/module.h>
16121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <linux/ssb/ssb.h>
17121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <linux/kernel.h>
18121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <linux/string.h>
19121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <asm/addrspace.h>
20111bd981e2216827aab95503596501ab71bc7a7dHauke Mehrtens#include <bcm47xx_nvram.h>
21121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <asm/mach-bcm47xx/bcm47xx.h>
22121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
23121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorbstatic char nvram_buf[NVRAM_SPACE];
24f4c4d589d5c1046bb5a9c17d98afcda43e04a315Hauke Mehrtensstatic const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000};
25121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
26f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtensstatic u32 find_nvram_size(u32 end)
27f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens{
28f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	struct nvram_header *header;
29f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	int i;
30f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens
31f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) {
32f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens		header = (struct nvram_header *)KSEG1ADDR(end - nvram_sizes[i]);
33f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens		if (header->magic == NVRAM_HEADER)
34f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens			return nvram_sizes[i];
35f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	}
36f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens
37f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	return 0;
38f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens}
39f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens
40121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb/* Probe for NVRAM header */
41cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtensstatic int nvram_find_and_copy(u32 base, u32 lim)
42121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb{
43121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	struct nvram_header *header;
44121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	int i;
4508ccf57283f89adbc2ff897ad82d6ad4560db7cdHauke Mehrtens	u32 off;
46121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	u32 *src, *dst;
47f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	u32 size;
48121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
49c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens	/* TODO: when nvram is on nand flash check for bad blocks first. */
50121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	off = FLASH_MIN;
51121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	while (off <= lim) {
52121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		/* Windowed flash access */
53f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens		size = find_nvram_size(base + off);
54f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens		if (size) {
55f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens			header = (struct nvram_header *)KSEG1ADDR(base + off -
56f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens								  size);
57121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb			goto found;
58f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens		}
59121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		off <<= 1;
60121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	}
61121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
62121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	/* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
63121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	header = (struct nvram_header *) KSEG1ADDR(base + 4096);
64f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	if (header->magic == NVRAM_HEADER) {
65f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens		size = NVRAM_SPACE;
66121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		goto found;
67f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	}
68121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
69121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	header = (struct nvram_header *) KSEG1ADDR(base + 1024);
70f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	if (header->magic == NVRAM_HEADER) {
71f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens		size = NVRAM_SPACE;
72121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		goto found;
73f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	}
74121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
75f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	pr_err("no nvram found\n");
76cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens	return -ENXIO;
77121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
78121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorbfound:
79f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens
80f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	if (header->len > size)
81f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens		pr_err("The nvram size accoridng to the header seems to be bigger than the partition on flash\n");
82f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	if (header->len > NVRAM_SPACE)
83f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens		pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n",
84f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens		       header->len, NVRAM_SPACE);
85f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens
86121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	src = (u32 *) header;
87121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	dst = (u32 *) nvram_buf;
88121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	for (i = 0; i < sizeof(struct nvram_header); i += 4)
89121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		*dst++ = *src++;
90f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	for (; i < header->len && i < NVRAM_SPACE && i < size; i += 4)
91121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		*dst++ = le32_to_cpu(*src++);
92f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens	memset(dst, 0x0, NVRAM_SPACE - i);
93cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens
94cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens	return 0;
95121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb}
96121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
97bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#ifdef CONFIG_BCM47XX_SSB
98cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtensstatic int nvram_init_ssb(void)
99bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki{
100bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	struct ssb_mipscore *mcore = &bcm47xx_bus.ssb.mipscore;
101bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	u32 base;
102bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	u32 lim;
103bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki
104bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	if (mcore->pflash.present) {
105bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki		base = mcore->pflash.window;
106bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki		lim = mcore->pflash.window_size;
107bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	} else {
108bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki		pr_err("Couldn't find supported flash memory\n");
109cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens		return -ENXIO;
110bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	}
111bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki
112cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens	return nvram_find_and_copy(base, lim);
113bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki}
114bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#endif
115bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki
116bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#ifdef CONFIG_BCM47XX_BCMA
117cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtensstatic int nvram_init_bcma(void)
118bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki{
119bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	struct bcma_drv_cc *cc = &bcm47xx_bus.bcma.bus.drv_cc;
120bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	u32 base;
121bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	u32 lim;
122bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki
123c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens#ifdef CONFIG_BCMA_NFLASH
124c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens	if (cc->nflash.boot) {
125c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens		base = BCMA_SOC_FLASH1;
126c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens		lim = BCMA_SOC_FLASH1_SZ;
127c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens	} else
128c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens#endif
129bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	if (cc->pflash.present) {
130bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki		base = cc->pflash.window;
131bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki		lim = cc->pflash.window_size;
132bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#ifdef CONFIG_BCMA_SFLASH
133bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	} else if (cc->sflash.present) {
134bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki		base = cc->sflash.window;
135bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki		lim = cc->sflash.size;
136bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#endif
137bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	} else {
138bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki		pr_err("Couldn't find supported flash memory\n");
139cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens		return -ENXIO;
140bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	}
141bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki
142cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens	return nvram_find_and_copy(base, lim);
143bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki}
144bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#endif
145bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki
146e58da16f716c0e7822e64a5d8a1f413041bc912eHauke Mehrtensstatic int nvram_init(void)
147bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki{
148bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	switch (bcm47xx_bus_type) {
149bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#ifdef CONFIG_BCM47XX_SSB
150bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	case BCM47XX_BUS_TYPE_SSB:
151cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens		return nvram_init_ssb();
152bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#endif
153bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#ifdef CONFIG_BCM47XX_BCMA
154bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	case BCM47XX_BUS_TYPE_BCMA:
155cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens		return nvram_init_bcma();
156bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#endif
157bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki	}
158cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens	return -ENXIO;
159bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki}
160bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki
161111bd981e2216827aab95503596501ab71bc7a7dHauke Mehrtensint bcm47xx_nvram_getenv(char *name, char *val, size_t val_len)
162121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb{
163121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	char *var, *value, *end, *eq;
164cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens	int err;
165121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
166121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	if (!name)
167ee7e2f3c235f43d815c0be285101b5b91a3bcaa5Hauke Mehrtens		return -EINVAL;
168121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
169cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens	if (!nvram_buf[0]) {
170e58da16f716c0e7822e64a5d8a1f413041bc912eHauke Mehrtens		err = nvram_init();
171cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens		if (err)
172cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens			return err;
173cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens	}
174121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb
175121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	/* Look for name=value and return value */
176121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	var = &nvram_buf[sizeof(struct nvram_header)];
177121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	end = nvram_buf + sizeof(nvram_buf) - 2;
178121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	end[0] = end[1] = '\0';
179121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	for (; *var; var = value + strlen(value) + 1) {
180121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		eq = strchr(var, '=');
181121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		if (!eq)
182121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb			break;
183121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		value = eq + 1;
184121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		if ((eq - var) == strlen(name) &&
185121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb			strncmp(var, name, (eq - var)) == 0) {
18644d4b2ae94b19080d6d033a1f5cf2cc47443be3bHauke Mehrtens			return snprintf(val, val_len, "%s", value);
187121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb		}
188121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb	}
189ee7e2f3c235f43d815c0be285101b5b91a3bcaa5Hauke Mehrtens	return -ENOENT;
190121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb}
191111bd981e2216827aab95503596501ab71bc7a7dHauke MehrtensEXPORT_SYMBOL(bcm47xx_nvram_getenv);
19262cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens
19362cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtensint bcm47xx_nvram_gpio_pin(const char *name)
19462cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens{
19562cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens	int i, err;
19662cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens	char nvram_var[10];
19762cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens	char buf[30];
19862cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens
1994fe2169acecb6e62821dfe14bc5c5852870b516fRafał Miłecki	for (i = 0; i < 32; i++) {
20062cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens		err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i);
20162cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens		if (err <= 0)
20262cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens			continue;
20362cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens		err = bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf));
20462cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens		if (err <= 0)
20562cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens			continue;
20662cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens		if (!strcmp(name, buf))
20762cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens			return i;
20862cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens	}
20962cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens	return -ENOENT;
21062cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens}
21162cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke MehrtensEXPORT_SYMBOL(bcm47xx_nvram_gpio_pin);
212