2 * Copyright (c) 2010 Broadcom Corporation
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <linux/etherdevice.h>
20 #include <linux/module.h>
21 #include <linux/pci.h>
34 #define SROM_OFFSET(sih) ((sih->ccrev > 31) ? \
35 (((sih->cccaps & CC_CAP_SROM) == 0) ? NULL : \
36 ((u8 *)curmap + PCI_16KB0_CCREGS_OFFSET + CC_SROM_OTP)) : \
37 ((u8 *)curmap + PCI_BAR0_SPROM_OFFSET))
40 #define WRITE_ENABLE_DELAY 500 /* 500 ms after write enable/disable toggle */
41 #define WRITE_WORD_DELAY 20 /* 20 ms between each word write */
44 /* SROM flags (see sromvar_t) */
45 #define SRFL_MORE 1 /* value continues as described by the next entry */
46 #define SRFL_NOFFS 2 /* value bits can't be all one's */
47 #define SRFL_PRHEX 4 /* value is in hexdecimal format */
48 #define SRFL_PRSIGN 8 /* value is in signed decimal format */
49 #define SRFL_CCODE 0x10 /* value is in country code format */
50 #define SRFL_ETHADDR 0x20 /* value is an Ethernet address */
51 #define SRFL_LEDDC 0x40 /* value is an LED duty cycle */
52 #define SRFL_NOVAR 0x80 /* do not generate a nvram param, entry is for mfgc */
62 typedef struct varbuf {
63 char *base; /* pointer to buffer base */
64 char *buf; /* pointer to current position */
65 unsigned int size; /* current (residual) size in bytes */
71 * - Ethernet address spans across 3 consective words
74 * - Add multiple entries next to each other if a value spans across multiple words
75 * (even multiple fields in the same word) with each entry except the last having
76 * it's SRFL_MORE bit set.
77 * - Ethernet address entry does not follow above rule and must not have SRFL_MORE
78 * bit set. Its SRFL_ETHADDR bit implies it takes multiple words.
79 * - The last entry's name field must be NULL to indicate the end of the table. Other
80 * entries must have non-NULL name.
82 static const sromvar_t pci_sromvars[] = {
83 {"devid", 0xffffff00, SRFL_PRHEX | SRFL_NOVAR, PCI_F0DEVID, 0xffff},
84 {"boardrev", 0x0000000e, SRFL_PRHEX, SROM_AABREV, SROM_BR_MASK},
85 {"boardrev", 0x000000f0, SRFL_PRHEX, SROM4_BREV, 0xffff},
86 {"boardrev", 0xffffff00, SRFL_PRHEX, SROM8_BREV, 0xffff},
87 {"boardflags", 0x00000002, SRFL_PRHEX, SROM_BFL, 0xffff},
88 {"boardflags", 0x00000004, SRFL_PRHEX | SRFL_MORE, SROM_BFL, 0xffff},
89 {"", 0, 0, SROM_BFL2, 0xffff},
90 {"boardflags", 0x00000008, SRFL_PRHEX | SRFL_MORE, SROM_BFL, 0xffff},
91 {"", 0, 0, SROM3_BFL2, 0xffff},
92 {"boardflags", 0x00000010, SRFL_PRHEX | SRFL_MORE, SROM4_BFL0, 0xffff},
93 {"", 0, 0, SROM4_BFL1, 0xffff},
94 {"boardflags", 0x000000e0, SRFL_PRHEX | SRFL_MORE, SROM5_BFL0, 0xffff},
95 {"", 0, 0, SROM5_BFL1, 0xffff},
96 {"boardflags", 0xffffff00, SRFL_PRHEX | SRFL_MORE, SROM8_BFL0, 0xffff},
97 {"", 0, 0, SROM8_BFL1, 0xffff},
98 {"boardflags2", 0x00000010, SRFL_PRHEX | SRFL_MORE, SROM4_BFL2, 0xffff},
99 {"", 0, 0, SROM4_BFL3, 0xffff},
100 {"boardflags2", 0x000000e0, SRFL_PRHEX | SRFL_MORE, SROM5_BFL2, 0xffff},
101 {"", 0, 0, SROM5_BFL3, 0xffff},
102 {"boardflags2", 0xffffff00, SRFL_PRHEX | SRFL_MORE, SROM8_BFL2, 0xffff},
103 {"", 0, 0, SROM8_BFL3, 0xffff},
104 {"boardtype", 0xfffffffc, SRFL_PRHEX, SROM_SSID, 0xffff},
105 {"boardnum", 0x00000006, 0, SROM_MACLO_IL0, 0xffff},
106 {"boardnum", 0x00000008, 0, SROM3_MACLO, 0xffff},
107 {"boardnum", 0x00000010, 0, SROM4_MACLO, 0xffff},
108 {"boardnum", 0x000000e0, 0, SROM5_MACLO, 0xffff},
109 {"boardnum", 0xffffff00, 0, SROM8_MACLO, 0xffff},
110 {"cc", 0x00000002, 0, SROM_AABREV, SROM_CC_MASK},
111 {"regrev", 0x00000008, 0, SROM_OPO, 0xff00},
112 {"regrev", 0x00000010, 0, SROM4_REGREV, 0x00ff},
113 {"regrev", 0x000000e0, 0, SROM5_REGREV, 0x00ff},
114 {"regrev", 0xffffff00, 0, SROM8_REGREV, 0x00ff},
115 {"ledbh0", 0x0000000e, SRFL_NOFFS, SROM_LEDBH10, 0x00ff},
116 {"ledbh1", 0x0000000e, SRFL_NOFFS, SROM_LEDBH10, 0xff00},
117 {"ledbh2", 0x0000000e, SRFL_NOFFS, SROM_LEDBH32, 0x00ff},
118 {"ledbh3", 0x0000000e, SRFL_NOFFS, SROM_LEDBH32, 0xff00},
119 {"ledbh0", 0x00000010, SRFL_NOFFS, SROM4_LEDBH10, 0x00ff},
120 {"ledbh1", 0x00000010, SRFL_NOFFS, SROM4_LEDBH10, 0xff00},
121 {"ledbh2", 0x00000010, SRFL_NOFFS, SROM4_LEDBH32, 0x00ff},
122 {"ledbh3", 0x00000010, SRFL_NOFFS, SROM4_LEDBH32, 0xff00},
123 {"ledbh0", 0x000000e0, SRFL_NOFFS, SROM5_LEDBH10, 0x00ff},
124 {"ledbh1", 0x000000e0, SRFL_NOFFS, SROM5_LEDBH10, 0xff00},
125 {"ledbh2", 0x000000e0, SRFL_NOFFS, SROM5_LEDBH32, 0x00ff},
126 {"ledbh3", 0x000000e0, SRFL_NOFFS, SROM5_LEDBH32, 0xff00},
127 {"ledbh0", 0xffffff00, SRFL_NOFFS, SROM8_LEDBH10, 0x00ff},
128 {"ledbh1", 0xffffff00, SRFL_NOFFS, SROM8_LEDBH10, 0xff00},
129 {"ledbh2", 0xffffff00, SRFL_NOFFS, SROM8_LEDBH32, 0x00ff},
130 {"ledbh3", 0xffffff00, SRFL_NOFFS, SROM8_LEDBH32, 0xff00},
131 {"pa0b0", 0x0000000e, SRFL_PRHEX, SROM_WL0PAB0, 0xffff},
132 {"pa0b1", 0x0000000e, SRFL_PRHEX, SROM_WL0PAB1, 0xffff},
133 {"pa0b2", 0x0000000e, SRFL_PRHEX, SROM_WL0PAB2, 0xffff},
134 {"pa0itssit", 0x0000000e, 0, SROM_ITT, 0x00ff},
135 {"pa0maxpwr", 0x0000000e, 0, SROM_WL10MAXP, 0x00ff},
136 {"pa0b0", 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB0, 0xffff},
137 {"pa0b1", 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB1, 0xffff},
138 {"pa0b2", 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB2, 0xffff},
139 {"pa0itssit", 0xffffff00, 0, SROM8_W0_ITTMAXP, 0xff00},
140 {"pa0maxpwr", 0xffffff00, 0, SROM8_W0_ITTMAXP, 0x00ff},
141 {"opo", 0x0000000c, 0, SROM_OPO, 0x00ff},
142 {"opo", 0xffffff00, 0, SROM8_2G_OFDMPO, 0x00ff},
143 {"aa2g", 0x0000000e, 0, SROM_AABREV, SROM_AA0_MASK},
144 {"aa2g", 0x000000f0, 0, SROM4_AA, 0x00ff},
145 {"aa2g", 0xffffff00, 0, SROM8_AA, 0x00ff},
146 {"aa5g", 0x0000000e, 0, SROM_AABREV, SROM_AA1_MASK},
147 {"aa5g", 0x000000f0, 0, SROM4_AA, 0xff00},
148 {"aa5g", 0xffffff00, 0, SROM8_AA, 0xff00},
149 {"ag0", 0x0000000e, 0, SROM_AG10, 0x00ff},
150 {"ag1", 0x0000000e, 0, SROM_AG10, 0xff00},
151 {"ag0", 0x000000f0, 0, SROM4_AG10, 0x00ff},
152 {"ag1", 0x000000f0, 0, SROM4_AG10, 0xff00},
153 {"ag2", 0x000000f0, 0, SROM4_AG32, 0x00ff},
154 {"ag3", 0x000000f0, 0, SROM4_AG32, 0xff00},
155 {"ag0", 0xffffff00, 0, SROM8_AG10, 0x00ff},
156 {"ag1", 0xffffff00, 0, SROM8_AG10, 0xff00},
157 {"ag2", 0xffffff00, 0, SROM8_AG32, 0x00ff},
158 {"ag3", 0xffffff00, 0, SROM8_AG32, 0xff00},
159 {"pa1b0", 0x0000000e, SRFL_PRHEX, SROM_WL1PAB0, 0xffff},
160 {"pa1b1", 0x0000000e, SRFL_PRHEX, SROM_WL1PAB1, 0xffff},
161 {"pa1b2", 0x0000000e, SRFL_PRHEX, SROM_WL1PAB2, 0xffff},
162 {"pa1lob0", 0x0000000c, SRFL_PRHEX, SROM_WL1LPAB0, 0xffff},
163 {"pa1lob1", 0x0000000c, SRFL_PRHEX, SROM_WL1LPAB1, 0xffff},
164 {"pa1lob2", 0x0000000c, SRFL_PRHEX, SROM_WL1LPAB2, 0xffff},
165 {"pa1hib0", 0x0000000c, SRFL_PRHEX, SROM_WL1HPAB0, 0xffff},
166 {"pa1hib1", 0x0000000c, SRFL_PRHEX, SROM_WL1HPAB1, 0xffff},
167 {"pa1hib2", 0x0000000c, SRFL_PRHEX, SROM_WL1HPAB2, 0xffff},
168 {"pa1itssit", 0x0000000e, 0, SROM_ITT, 0xff00},
169 {"pa1maxpwr", 0x0000000e, 0, SROM_WL10MAXP, 0xff00},
170 {"pa1lomaxpwr", 0x0000000c, 0, SROM_WL1LHMAXP, 0xff00},
171 {"pa1himaxpwr", 0x0000000c, 0, SROM_WL1LHMAXP, 0x00ff},
172 {"pa1b0", 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB0, 0xffff},
173 {"pa1b1", 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB1, 0xffff},
174 {"pa1b2", 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB2, 0xffff},
175 {"pa1lob0", 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB0_LC, 0xffff},
176 {"pa1lob1", 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB1_LC, 0xffff},
177 {"pa1lob2", 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB2_LC, 0xffff},
178 {"pa1hib0", 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB0_HC, 0xffff},
179 {"pa1hib1", 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB1_HC, 0xffff},
180 {"pa1hib2", 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB2_HC, 0xffff},
181 {"pa1itssit", 0xffffff00, 0, SROM8_W1_ITTMAXP, 0xff00},
182 {"pa1maxpwr", 0xffffff00, 0, SROM8_W1_ITTMAXP, 0x00ff},
183 {"pa1lomaxpwr", 0xffffff00, 0, SROM8_W1_MAXP_LCHC, 0xff00},
184 {"pa1himaxpwr", 0xffffff00, 0, SROM8_W1_MAXP_LCHC, 0x00ff},
185 {"bxa2g", 0x00000008, 0, SROM_BXARSSI2G, 0x1800},
186 {"rssisav2g", 0x00000008, 0, SROM_BXARSSI2G, 0x0700},
187 {"rssismc2g", 0x00000008, 0, SROM_BXARSSI2G, 0x00f0},
188 {"rssismf2g", 0x00000008, 0, SROM_BXARSSI2G, 0x000f},
189 {"bxa2g", 0xffffff00, 0, SROM8_BXARSSI2G, 0x1800},
190 {"rssisav2g", 0xffffff00, 0, SROM8_BXARSSI2G, 0x0700},
191 {"rssismc2g", 0xffffff00, 0, SROM8_BXARSSI2G, 0x00f0},
192 {"rssismf2g", 0xffffff00, 0, SROM8_BXARSSI2G, 0x000f},
193 {"bxa5g", 0x00000008, 0, SROM_BXARSSI5G, 0x1800},
194 {"rssisav5g", 0x00000008, 0, SROM_BXARSSI5G, 0x0700},
195 {"rssismc5g", 0x00000008, 0, SROM_BXARSSI5G, 0x00f0},
196 {"rssismf5g", 0x00000008, 0, SROM_BXARSSI5G, 0x000f},
197 {"bxa5g", 0xffffff00, 0, SROM8_BXARSSI5G, 0x1800},
198 {"rssisav5g", 0xffffff00, 0, SROM8_BXARSSI5G, 0x0700},
199 {"rssismc5g", 0xffffff00, 0, SROM8_BXARSSI5G, 0x00f0},
200 {"rssismf5g", 0xffffff00, 0, SROM8_BXARSSI5G, 0x000f},
201 {"tri2g", 0x00000008, 0, SROM_TRI52G, 0x00ff},
202 {"tri5g", 0x00000008, 0, SROM_TRI52G, 0xff00},
203 {"tri5gl", 0x00000008, 0, SROM_TRI5GHL, 0x00ff},
204 {"tri5gh", 0x00000008, 0, SROM_TRI5GHL, 0xff00},
205 {"tri2g", 0xffffff00, 0, SROM8_TRI52G, 0x00ff},
206 {"tri5g", 0xffffff00, 0, SROM8_TRI52G, 0xff00},
207 {"tri5gl", 0xffffff00, 0, SROM8_TRI5GHL, 0x00ff},
208 {"tri5gh", 0xffffff00, 0, SROM8_TRI5GHL, 0xff00},
209 {"rxpo2g", 0x00000008, SRFL_PRSIGN, SROM_RXPO52G, 0x00ff},
210 {"rxpo5g", 0x00000008, SRFL_PRSIGN, SROM_RXPO52G, 0xff00},
211 {"rxpo2g", 0xffffff00, SRFL_PRSIGN, SROM8_RXPO52G, 0x00ff},
212 {"rxpo5g", 0xffffff00, SRFL_PRSIGN, SROM8_RXPO52G, 0xff00},
213 {"txchain", 0x000000f0, SRFL_NOFFS, SROM4_TXRXC, SROM4_TXCHAIN_MASK},
214 {"rxchain", 0x000000f0, SRFL_NOFFS, SROM4_TXRXC, SROM4_RXCHAIN_MASK},
215 {"antswitch", 0x000000f0, SRFL_NOFFS, SROM4_TXRXC, SROM4_SWITCH_MASK},
216 {"txchain", 0xffffff00, SRFL_NOFFS, SROM8_TXRXC, SROM4_TXCHAIN_MASK},
217 {"rxchain", 0xffffff00, SRFL_NOFFS, SROM8_TXRXC, SROM4_RXCHAIN_MASK},
218 {"antswitch", 0xffffff00, SRFL_NOFFS, SROM8_TXRXC, SROM4_SWITCH_MASK},
219 {"tssipos2g", 0xffffff00, 0, SROM8_FEM2G, SROM8_FEM_TSSIPOS_MASK},
220 {"extpagain2g", 0xffffff00, 0, SROM8_FEM2G, SROM8_FEM_EXTPA_GAIN_MASK},
221 {"pdetrange2g", 0xffffff00, 0, SROM8_FEM2G, SROM8_FEM_PDET_RANGE_MASK},
222 {"triso2g", 0xffffff00, 0, SROM8_FEM2G, SROM8_FEM_TR_ISO_MASK},
223 {"antswctl2g", 0xffffff00, 0, SROM8_FEM2G, SROM8_FEM_ANTSWLUT_MASK},
224 {"tssipos5g", 0xffffff00, 0, SROM8_FEM5G, SROM8_FEM_TSSIPOS_MASK},
225 {"extpagain5g", 0xffffff00, 0, SROM8_FEM5G, SROM8_FEM_EXTPA_GAIN_MASK},
226 {"pdetrange5g", 0xffffff00, 0, SROM8_FEM5G, SROM8_FEM_PDET_RANGE_MASK},
227 {"triso5g", 0xffffff00, 0, SROM8_FEM5G, SROM8_FEM_TR_ISO_MASK},
228 {"antswctl5g", 0xffffff00, 0, SROM8_FEM5G, SROM8_FEM_ANTSWLUT_MASK},
229 {"tempthresh", 0xffffff00, 0, SROM8_THERMAL, 0xff00},
230 {"tempoffset", 0xffffff00, 0, SROM8_THERMAL, 0x00ff},
231 {"txpid2ga0", 0x000000f0, 0, SROM4_TXPID2G, 0x00ff},
232 {"txpid2ga1", 0x000000f0, 0, SROM4_TXPID2G, 0xff00},
233 {"txpid2ga2", 0x000000f0, 0, SROM4_TXPID2G + 1, 0x00ff},
234 {"txpid2ga3", 0x000000f0, 0, SROM4_TXPID2G + 1, 0xff00},
235 {"txpid5ga0", 0x000000f0, 0, SROM4_TXPID5G, 0x00ff},
236 {"txpid5ga1", 0x000000f0, 0, SROM4_TXPID5G, 0xff00},
237 {"txpid5ga2", 0x000000f0, 0, SROM4_TXPID5G + 1, 0x00ff},
238 {"txpid5ga3", 0x000000f0, 0, SROM4_TXPID5G + 1, 0xff00},
239 {"txpid5gla0", 0x000000f0, 0, SROM4_TXPID5GL, 0x00ff},
240 {"txpid5gla1", 0x000000f0, 0, SROM4_TXPID5GL, 0xff00},
241 {"txpid5gla2", 0x000000f0, 0, SROM4_TXPID5GL + 1, 0x00ff},
242 {"txpid5gla3", 0x000000f0, 0, SROM4_TXPID5GL + 1, 0xff00},
243 {"txpid5gha0", 0x000000f0, 0, SROM4_TXPID5GH, 0x00ff},
244 {"txpid5gha1", 0x000000f0, 0, SROM4_TXPID5GH, 0xff00},
245 {"txpid5gha2", 0x000000f0, 0, SROM4_TXPID5GH + 1, 0x00ff},
246 {"txpid5gha3", 0x000000f0, 0, SROM4_TXPID5GH + 1, 0xff00},
248 {"ccode", 0x0000000f, SRFL_CCODE, SROM_CCODE, 0xffff},
249 {"ccode", 0x00000010, SRFL_CCODE, SROM4_CCODE, 0xffff},
250 {"ccode", 0x000000e0, SRFL_CCODE, SROM5_CCODE, 0xffff},
251 {"ccode", 0xffffff00, SRFL_CCODE, SROM8_CCODE, 0xffff},
252 {"macaddr", 0xffffff00, SRFL_ETHADDR, SROM8_MACHI, 0xffff},
253 {"macaddr", 0x000000e0, SRFL_ETHADDR, SROM5_MACHI, 0xffff},
254 {"macaddr", 0x00000010, SRFL_ETHADDR, SROM4_MACHI, 0xffff},
255 {"macaddr", 0x00000008, SRFL_ETHADDR, SROM3_MACHI, 0xffff},
256 {"il0macaddr", 0x00000007, SRFL_ETHADDR, SROM_MACHI_IL0, 0xffff},
257 {"et1macaddr", 0x00000007, SRFL_ETHADDR, SROM_MACHI_ET1, 0xffff},
258 {"leddc", 0xffffff00, SRFL_NOFFS | SRFL_LEDDC, SROM8_LEDDC, 0xffff},
259 {"leddc", 0x000000e0, SRFL_NOFFS | SRFL_LEDDC, SROM5_LEDDC, 0xffff},
260 {"leddc", 0x00000010, SRFL_NOFFS | SRFL_LEDDC, SROM4_LEDDC, 0xffff},
261 {"leddc", 0x00000008, SRFL_NOFFS | SRFL_LEDDC, SROM3_LEDDC, 0xffff},
262 {"rawtempsense", 0xffffff00, SRFL_PRHEX, SROM8_MPWR_RAWTS, 0x01ff},
263 {"measpower", 0xffffff00, SRFL_PRHEX, SROM8_MPWR_RAWTS, 0xfe00},
264 {"tempsense_slope", 0xffffff00, SRFL_PRHEX, SROM8_TS_SLP_OPT_CORRX,
266 {"tempcorrx", 0xffffff00, SRFL_PRHEX, SROM8_TS_SLP_OPT_CORRX, 0xfc00},
267 {"tempsense_option", 0xffffff00, SRFL_PRHEX, SROM8_TS_SLP_OPT_CORRX,
269 {"freqoffset_corr", 0xffffff00, SRFL_PRHEX, SROM8_FOC_HWIQ_IQSWP,
271 {"iqcal_swp_dis", 0xffffff00, SRFL_PRHEX, SROM8_FOC_HWIQ_IQSWP, 0x0010},
272 {"hw_iqcal_en", 0xffffff00, SRFL_PRHEX, SROM8_FOC_HWIQ_IQSWP, 0x0020},
273 {"phycal_tempdelta", 0xffffff00, 0, SROM8_PHYCAL_TEMPDELTA, 0x00ff},
275 {"cck2gpo", 0x000000f0, 0, SROM4_2G_CCKPO, 0xffff},
276 {"cck2gpo", 0x00000100, 0, SROM8_2G_CCKPO, 0xffff},
277 {"ofdm2gpo", 0x000000f0, SRFL_MORE, SROM4_2G_OFDMPO, 0xffff},
278 {"", 0, 0, SROM4_2G_OFDMPO + 1, 0xffff},
279 {"ofdm5gpo", 0x000000f0, SRFL_MORE, SROM4_5G_OFDMPO, 0xffff},
280 {"", 0, 0, SROM4_5G_OFDMPO + 1, 0xffff},
281 {"ofdm5glpo", 0x000000f0, SRFL_MORE, SROM4_5GL_OFDMPO, 0xffff},
282 {"", 0, 0, SROM4_5GL_OFDMPO + 1, 0xffff},
283 {"ofdm5ghpo", 0x000000f0, SRFL_MORE, SROM4_5GH_OFDMPO, 0xffff},
284 {"", 0, 0, SROM4_5GH_OFDMPO + 1, 0xffff},
285 {"ofdm2gpo", 0x00000100, SRFL_MORE, SROM8_2G_OFDMPO, 0xffff},
286 {"", 0, 0, SROM8_2G_OFDMPO + 1, 0xffff},
287 {"ofdm5gpo", 0x00000100, SRFL_MORE, SROM8_5G_OFDMPO, 0xffff},
288 {"", 0, 0, SROM8_5G_OFDMPO + 1, 0xffff},
289 {"ofdm5glpo", 0x00000100, SRFL_MORE, SROM8_5GL_OFDMPO, 0xffff},
290 {"", 0, 0, SROM8_5GL_OFDMPO + 1, 0xffff},
291 {"ofdm5ghpo", 0x00000100, SRFL_MORE, SROM8_5GH_OFDMPO, 0xffff},
292 {"", 0, 0, SROM8_5GH_OFDMPO + 1, 0xffff},
293 {"mcs2gpo0", 0x000000f0, 0, SROM4_2G_MCSPO, 0xffff},
294 {"mcs2gpo1", 0x000000f0, 0, SROM4_2G_MCSPO + 1, 0xffff},
295 {"mcs2gpo2", 0x000000f0, 0, SROM4_2G_MCSPO + 2, 0xffff},
296 {"mcs2gpo3", 0x000000f0, 0, SROM4_2G_MCSPO + 3, 0xffff},
297 {"mcs2gpo4", 0x000000f0, 0, SROM4_2G_MCSPO + 4, 0xffff},
298 {"mcs2gpo5", 0x000000f0, 0, SROM4_2G_MCSPO + 5, 0xffff},
299 {"mcs2gpo6", 0x000000f0, 0, SROM4_2G_MCSPO + 6, 0xffff},
300 {"mcs2gpo7", 0x000000f0, 0, SROM4_2G_MCSPO + 7, 0xffff},
301 {"mcs5gpo0", 0x000000f0, 0, SROM4_5G_MCSPO, 0xffff},
302 {"mcs5gpo1", 0x000000f0, 0, SROM4_5G_MCSPO + 1, 0xffff},
303 {"mcs5gpo2", 0x000000f0, 0, SROM4_5G_MCSPO + 2, 0xffff},
304 {"mcs5gpo3", 0x000000f0, 0, SROM4_5G_MCSPO + 3, 0xffff},
305 {"mcs5gpo4", 0x000000f0, 0, SROM4_5G_MCSPO + 4, 0xffff},
306 {"mcs5gpo5", 0x000000f0, 0, SROM4_5G_MCSPO + 5, 0xffff},
307 {"mcs5gpo6", 0x000000f0, 0, SROM4_5G_MCSPO + 6, 0xffff},
308 {"mcs5gpo7", 0x000000f0, 0, SROM4_5G_MCSPO + 7, 0xffff},
309 {"mcs5glpo0", 0x000000f0, 0, SROM4_5GL_MCSPO, 0xffff},
310 {"mcs5glpo1", 0x000000f0, 0, SROM4_5GL_MCSPO + 1, 0xffff},
311 {"mcs5glpo2", 0x000000f0, 0, SROM4_5GL_MCSPO + 2, 0xffff},
312 {"mcs5glpo3", 0x000000f0, 0, SROM4_5GL_MCSPO + 3, 0xffff},
313 {"mcs5glpo4", 0x000000f0, 0, SROM4_5GL_MCSPO + 4, 0xffff},
314 {"mcs5glpo5", 0x000000f0, 0, SROM4_5GL_MCSPO + 5, 0xffff},
315 {"mcs5glpo6", 0x000000f0, 0, SROM4_5GL_MCSPO + 6, 0xffff},
316 {"mcs5glpo7", 0x000000f0, 0, SROM4_5GL_MCSPO + 7, 0xffff},
317 {"mcs5ghpo0", 0x000000f0, 0, SROM4_5GH_MCSPO, 0xffff},
318 {"mcs5ghpo1", 0x000000f0, 0, SROM4_5GH_MCSPO + 1, 0xffff},
319 {"mcs5ghpo2", 0x000000f0, 0, SROM4_5GH_MCSPO + 2, 0xffff},
320 {"mcs5ghpo3", 0x000000f0, 0, SROM4_5GH_MCSPO + 3, 0xffff},
321 {"mcs5ghpo4", 0x000000f0, 0, SROM4_5GH_MCSPO + 4, 0xffff},
322 {"mcs5ghpo5", 0x000000f0, 0, SROM4_5GH_MCSPO + 5, 0xffff},
323 {"mcs5ghpo6", 0x000000f0, 0, SROM4_5GH_MCSPO + 6, 0xffff},
324 {"mcs5ghpo7", 0x000000f0, 0, SROM4_5GH_MCSPO + 7, 0xffff},
325 {"mcs2gpo0", 0x00000100, 0, SROM8_2G_MCSPO, 0xffff},
326 {"mcs2gpo1", 0x00000100, 0, SROM8_2G_MCSPO + 1, 0xffff},
327 {"mcs2gpo2", 0x00000100, 0, SROM8_2G_MCSPO + 2, 0xffff},
328 {"mcs2gpo3", 0x00000100, 0, SROM8_2G_MCSPO + 3, 0xffff},
329 {"mcs2gpo4", 0x00000100, 0, SROM8_2G_MCSPO + 4, 0xffff},
330 {"mcs2gpo5", 0x00000100, 0, SROM8_2G_MCSPO + 5, 0xffff},
331 {"mcs2gpo6", 0x00000100, 0, SROM8_2G_MCSPO + 6, 0xffff},
332 {"mcs2gpo7", 0x00000100, 0, SROM8_2G_MCSPO + 7, 0xffff},
333 {"mcs5gpo0", 0x00000100, 0, SROM8_5G_MCSPO, 0xffff},
334 {"mcs5gpo1", 0x00000100, 0, SROM8_5G_MCSPO + 1, 0xffff},
335 {"mcs5gpo2", 0x00000100, 0, SROM8_5G_MCSPO + 2, 0xffff},
336 {"mcs5gpo3", 0x00000100, 0, SROM8_5G_MCSPO + 3, 0xffff},
337 {"mcs5gpo4", 0x00000100, 0, SROM8_5G_MCSPO + 4, 0xffff},
338 {"mcs5gpo5", 0x00000100, 0, SROM8_5G_MCSPO + 5, 0xffff},
339 {"mcs5gpo6", 0x00000100, 0, SROM8_5G_MCSPO + 6, 0xffff},
340 {"mcs5gpo7", 0x00000100, 0, SROM8_5G_MCSPO + 7, 0xffff},
341 {"mcs5glpo0", 0x00000100, 0, SROM8_5GL_MCSPO, 0xffff},
342 {"mcs5glpo1", 0x00000100, 0, SROM8_5GL_MCSPO + 1, 0xffff},
343 {"mcs5glpo2", 0x00000100, 0, SROM8_5GL_MCSPO + 2, 0xffff},
344 {"mcs5glpo3", 0x00000100, 0, SROM8_5GL_MCSPO + 3, 0xffff},
345 {"mcs5glpo4", 0x00000100, 0, SROM8_5GL_MCSPO + 4, 0xffff},
346 {"mcs5glpo5", 0x00000100, 0, SROM8_5GL_MCSPO + 5, 0xffff},
347 {"mcs5glpo6", 0x00000100, 0, SROM8_5GL_MCSPO + 6, 0xffff},
348 {"mcs5glpo7", 0x00000100, 0, SROM8_5GL_MCSPO + 7, 0xffff},
349 {"mcs5ghpo0", 0x00000100, 0, SROM8_5GH_MCSPO, 0xffff},
350 {"mcs5ghpo1", 0x00000100, 0, SROM8_5GH_MCSPO + 1, 0xffff},
351 {"mcs5ghpo2", 0x00000100, 0, SROM8_5GH_MCSPO + 2, 0xffff},
352 {"mcs5ghpo3", 0x00000100, 0, SROM8_5GH_MCSPO + 3, 0xffff},
353 {"mcs5ghpo4", 0x00000100, 0, SROM8_5GH_MCSPO + 4, 0xffff},
354 {"mcs5ghpo5", 0x00000100, 0, SROM8_5GH_MCSPO + 5, 0xffff},
355 {"mcs5ghpo6", 0x00000100, 0, SROM8_5GH_MCSPO + 6, 0xffff},
356 {"mcs5ghpo7", 0x00000100, 0, SROM8_5GH_MCSPO + 7, 0xffff},
357 {"cddpo", 0x000000f0, 0, SROM4_CDDPO, 0xffff},
358 {"stbcpo", 0x000000f0, 0, SROM4_STBCPO, 0xffff},
359 {"bw40po", 0x000000f0, 0, SROM4_BW40PO, 0xffff},
360 {"bwduppo", 0x000000f0, 0, SROM4_BWDUPPO, 0xffff},
361 {"cddpo", 0x00000100, 0, SROM8_CDDPO, 0xffff},
362 {"stbcpo", 0x00000100, 0, SROM8_STBCPO, 0xffff},
363 {"bw40po", 0x00000100, 0, SROM8_BW40PO, 0xffff},
364 {"bwduppo", 0x00000100, 0, SROM8_BWDUPPO, 0xffff},
366 /* power per rate from sromrev 9 */
367 {"cckbw202gpo", 0xfffffe00, 0, SROM9_2GPO_CCKBW20, 0xffff},
368 {"cckbw20ul2gpo", 0xfffffe00, 0, SROM9_2GPO_CCKBW20UL, 0xffff},
369 {"legofdmbw202gpo", 0xfffffe00, SRFL_MORE, SROM9_2GPO_LOFDMBW20,
371 {"", 0, 0, SROM9_2GPO_LOFDMBW20 + 1, 0xffff},
372 {"legofdmbw20ul2gpo", 0xfffffe00, SRFL_MORE, SROM9_2GPO_LOFDMBW20UL,
374 {"", 0, 0, SROM9_2GPO_LOFDMBW20UL + 1, 0xffff},
375 {"legofdmbw205glpo", 0xfffffe00, SRFL_MORE, SROM9_5GLPO_LOFDMBW20,
377 {"", 0, 0, SROM9_5GLPO_LOFDMBW20 + 1, 0xffff},
378 {"legofdmbw20ul5glpo", 0xfffffe00, SRFL_MORE, SROM9_5GLPO_LOFDMBW20UL,
380 {"", 0, 0, SROM9_5GLPO_LOFDMBW20UL + 1, 0xffff},
381 {"legofdmbw205gmpo", 0xfffffe00, SRFL_MORE, SROM9_5GMPO_LOFDMBW20,
383 {"", 0, 0, SROM9_5GMPO_LOFDMBW20 + 1, 0xffff},
384 {"legofdmbw20ul5gmpo", 0xfffffe00, SRFL_MORE, SROM9_5GMPO_LOFDMBW20UL,
386 {"", 0, 0, SROM9_5GMPO_LOFDMBW20UL + 1, 0xffff},
387 {"legofdmbw205ghpo", 0xfffffe00, SRFL_MORE, SROM9_5GHPO_LOFDMBW20,
389 {"", 0, 0, SROM9_5GHPO_LOFDMBW20 + 1, 0xffff},
390 {"legofdmbw20ul5ghpo", 0xfffffe00, SRFL_MORE, SROM9_5GHPO_LOFDMBW20UL,
392 {"", 0, 0, SROM9_5GHPO_LOFDMBW20UL + 1, 0xffff},
393 {"mcsbw202gpo", 0xfffffe00, SRFL_MORE, SROM9_2GPO_MCSBW20, 0xffff},
394 {"", 0, 0, SROM9_2GPO_MCSBW20 + 1, 0xffff},
395 {"mcsbw20ul2gpo", 0xfffffe00, SRFL_MORE, SROM9_2GPO_MCSBW20UL, 0xffff},
396 {"", 0, 0, SROM9_2GPO_MCSBW20UL + 1, 0xffff},
397 {"mcsbw402gpo", 0xfffffe00, SRFL_MORE, SROM9_2GPO_MCSBW40, 0xffff},
398 {"", 0, 0, SROM9_2GPO_MCSBW40 + 1, 0xffff},
399 {"mcsbw205glpo", 0xfffffe00, SRFL_MORE, SROM9_5GLPO_MCSBW20, 0xffff},
400 {"", 0, 0, SROM9_5GLPO_MCSBW20 + 1, 0xffff},
401 {"mcsbw20ul5glpo", 0xfffffe00, SRFL_MORE, SROM9_5GLPO_MCSBW20UL,
403 {"", 0, 0, SROM9_5GLPO_MCSBW20UL + 1, 0xffff},
404 {"mcsbw405glpo", 0xfffffe00, SRFL_MORE, SROM9_5GLPO_MCSBW40, 0xffff},
405 {"", 0, 0, SROM9_5GLPO_MCSBW40 + 1, 0xffff},
406 {"mcsbw205gmpo", 0xfffffe00, SRFL_MORE, SROM9_5GMPO_MCSBW20, 0xffff},
407 {"", 0, 0, SROM9_5GMPO_MCSBW20 + 1, 0xffff},
408 {"mcsbw20ul5gmpo", 0xfffffe00, SRFL_MORE, SROM9_5GMPO_MCSBW20UL,
410 {"", 0, 0, SROM9_5GMPO_MCSBW20UL + 1, 0xffff},
411 {"mcsbw405gmpo", 0xfffffe00, SRFL_MORE, SROM9_5GMPO_MCSBW40, 0xffff},
412 {"", 0, 0, SROM9_5GMPO_MCSBW40 + 1, 0xffff},
413 {"mcsbw205ghpo", 0xfffffe00, SRFL_MORE, SROM9_5GHPO_MCSBW20, 0xffff},
414 {"", 0, 0, SROM9_5GHPO_MCSBW20 + 1, 0xffff},
415 {"mcsbw20ul5ghpo", 0xfffffe00, SRFL_MORE, SROM9_5GHPO_MCSBW20UL,
417 {"", 0, 0, SROM9_5GHPO_MCSBW20UL + 1, 0xffff},
418 {"mcsbw405ghpo", 0xfffffe00, SRFL_MORE, SROM9_5GHPO_MCSBW40, 0xffff},
419 {"", 0, 0, SROM9_5GHPO_MCSBW40 + 1, 0xffff},
420 {"mcs32po", 0xfffffe00, 0, SROM9_PO_MCS32, 0xffff},
421 {"legofdm40duppo", 0xfffffe00, 0, SROM9_PO_LOFDM40DUP, 0xffff},
426 static const sromvar_t perpath_pci_sromvars[] = {
427 {"maxp2ga", 0x000000f0, 0, SROM4_2G_ITT_MAXP, 0x00ff},
428 {"itt2ga", 0x000000f0, 0, SROM4_2G_ITT_MAXP, 0xff00},
429 {"itt5ga", 0x000000f0, 0, SROM4_5G_ITT_MAXP, 0xff00},
430 {"pa2gw0a", 0x000000f0, SRFL_PRHEX, SROM4_2G_PA, 0xffff},
431 {"pa2gw1a", 0x000000f0, SRFL_PRHEX, SROM4_2G_PA + 1, 0xffff},
432 {"pa2gw2a", 0x000000f0, SRFL_PRHEX, SROM4_2G_PA + 2, 0xffff},
433 {"pa2gw3a", 0x000000f0, SRFL_PRHEX, SROM4_2G_PA + 3, 0xffff},
434 {"maxp5ga", 0x000000f0, 0, SROM4_5G_ITT_MAXP, 0x00ff},
435 {"maxp5gha", 0x000000f0, 0, SROM4_5GLH_MAXP, 0x00ff},
436 {"maxp5gla", 0x000000f0, 0, SROM4_5GLH_MAXP, 0xff00},
437 {"pa5gw0a", 0x000000f0, SRFL_PRHEX, SROM4_5G_PA, 0xffff},
438 {"pa5gw1a", 0x000000f0, SRFL_PRHEX, SROM4_5G_PA + 1, 0xffff},
439 {"pa5gw2a", 0x000000f0, SRFL_PRHEX, SROM4_5G_PA + 2, 0xffff},
440 {"pa5gw3a", 0x000000f0, SRFL_PRHEX, SROM4_5G_PA + 3, 0xffff},
441 {"pa5glw0a", 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA, 0xffff},
442 {"pa5glw1a", 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA + 1, 0xffff},
443 {"pa5glw2a", 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA + 2, 0xffff},
444 {"pa5glw3a", 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA + 3, 0xffff},
445 {"pa5ghw0a", 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA, 0xffff},
446 {"pa5ghw1a", 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA + 1, 0xffff},
447 {"pa5ghw2a", 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA + 2, 0xffff},
448 {"pa5ghw3a", 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA + 3, 0xffff},
449 {"maxp2ga", 0xffffff00, 0, SROM8_2G_ITT_MAXP, 0x00ff},
450 {"itt2ga", 0xffffff00, 0, SROM8_2G_ITT_MAXP, 0xff00},
451 {"itt5ga", 0xffffff00, 0, SROM8_5G_ITT_MAXP, 0xff00},
452 {"pa2gw0a", 0xffffff00, SRFL_PRHEX, SROM8_2G_PA, 0xffff},
453 {"pa2gw1a", 0xffffff00, SRFL_PRHEX, SROM8_2G_PA + 1, 0xffff},
454 {"pa2gw2a", 0xffffff00, SRFL_PRHEX, SROM8_2G_PA + 2, 0xffff},
455 {"maxp5ga", 0xffffff00, 0, SROM8_5G_ITT_MAXP, 0x00ff},
456 {"maxp5gha", 0xffffff00, 0, SROM8_5GLH_MAXP, 0x00ff},
457 {"maxp5gla", 0xffffff00, 0, SROM8_5GLH_MAXP, 0xff00},
458 {"pa5gw0a", 0xffffff00, SRFL_PRHEX, SROM8_5G_PA, 0xffff},
459 {"pa5gw1a", 0xffffff00, SRFL_PRHEX, SROM8_5G_PA + 1, 0xffff},
460 {"pa5gw2a", 0xffffff00, SRFL_PRHEX, SROM8_5G_PA + 2, 0xffff},
461 {"pa5glw0a", 0xffffff00, SRFL_PRHEX, SROM8_5GL_PA, 0xffff},
462 {"pa5glw1a", 0xffffff00, SRFL_PRHEX, SROM8_5GL_PA + 1, 0xffff},
463 {"pa5glw2a", 0xffffff00, SRFL_PRHEX, SROM8_5GL_PA + 2, 0xffff},
464 {"pa5ghw0a", 0xffffff00, SRFL_PRHEX, SROM8_5GH_PA, 0xffff},
465 {"pa5ghw1a", 0xffffff00, SRFL_PRHEX, SROM8_5GH_PA + 1, 0xffff},
466 {"pa5ghw2a", 0xffffff00, SRFL_PRHEX, SROM8_5GH_PA + 2, 0xffff},
470 static int initvars_srom_si(si_t *sih, void *curmap, char **vars, uint *count);
471 static void _initvars_srom_pci(u8 sromrev, u16 *srom, uint off, varbuf_t *b);
472 static int initvars_srom_pci(si_t *sih, void *curmap, char **vars, uint *count);
473 static int initvars_flash_si(si_t *sih, char **vars, uint *count);
474 static int sprom_read_pci(si_t *sih, u16 *sprom,
475 uint wordoff, u16 *buf, uint nwords, bool check_crc);
476 #if defined(BCMNVRAMR)
477 static int otp_read_pci(si_t *sih, u16 *buf, uint bufsz);
479 static u16 srom_cc_cmd(si_t *sih, void *ccregs, u32 cmd,
480 uint wordoff, u16 data);
482 static int initvars_table(char *start, char *end,
483 char **vars, uint *count);
484 static int initvars_flash(si_t *sih, char **vp,
487 /* Initialization of varbuf structure */
488 static void varbuf_init(varbuf_t *b, char *buf, uint size)
491 b->base = b->buf = buf;
494 /* append a null terminated var=value string */
495 static int varbuf_append(varbuf_t *b, const char *fmt, ...)
506 r = vsnprintf(b->buf, b->size, fmt, ap);
509 /* C99 snprintf behavior returns r >= size on overflow,
510 * others return -1 on overflow.
511 * All return -1 on format error.
512 * We need to leave room for 2 null terminations, one for the current var
513 * string, and one for final null of the var table. So check that the
514 * strlen written, r, leaves room for 2 chars.
516 if ((r == -1) || (r > (int)(b->size - 2))) {
521 /* Remove any earlier occurrence of the same variable */
522 s = strchr(b->buf, '=');
524 len = (size_t) (s - b->buf);
525 for (s = b->base; s < b->buf;) {
526 if ((memcmp(s, b->buf, len) == 0) && s[len] == '=') {
528 memmove(s, (s + len),
529 ((b->buf + r + 1) - (s + len)));
531 b->size += (unsigned int)len;
540 /* skip over this string's null termination */
549 * Initialize local vars from the right source for this platform.
550 * Return 0 on success, nonzero on error.
552 int srom_var_init(si_t *sih, uint bustype, void *curmap,
553 char **vars, uint *count)
559 if (vars == NULL || count == NULL)
568 return initvars_srom_si(sih, curmap, vars, count);
574 return initvars_srom_pci(sih, curmap, vars, count);
582 /* In chips with chipcommon rev 32 and later, the srom is in chipcommon,
583 * not in the bus cores.
586 srom_cc_cmd(si_t *sih, void *ccregs, u32 cmd,
587 uint wordoff, u16 data)
589 chipcregs_t *cc = (chipcregs_t *) ccregs;
590 uint wait_cnt = 1000;
592 if ((cmd == SRC_OP_READ) || (cmd == SRC_OP_WRITE)) {
593 W_REG(&cc->sromaddress, wordoff * 2);
594 if (cmd == SRC_OP_WRITE)
595 W_REG(&cc->sromdata, data);
598 W_REG(&cc->sromcontrol, SRC_START | cmd);
601 if ((R_REG(&cc->sromcontrol) & SRC_BUSY) == 0)
608 if (cmd == SRC_OP_READ)
609 return (u16) R_REG(&cc->sromdata);
614 static inline void ltoh16_buf(u16 *buf, unsigned int size)
616 for (size /= 2; size; size--)
617 *(buf + size) = le16_to_cpu(*(buf + size));
620 static inline void htol16_buf(u16 *buf, unsigned int size)
622 for (size /= 2; size; size--)
623 *(buf + size) = cpu_to_le16(*(buf + size));
627 * Read in and validate sprom.
628 * Return 0 on success, nonzero on error.
631 sprom_read_pci(si_t *sih, u16 *sprom, uint wordoff,
632 u16 *buf, uint nwords, bool check_crc)
639 for (i = 0; i < nwords; i++) {
641 if (sih->ccrev > 31 && ISSIM_ENAB(sih)) {
642 /* use indirect since direct is too slow on QT */
643 if ((sih->cccaps & CC_CAP_SROM) == 0)
646 ccregs = (void *)((u8 *) sprom - CC_SROM_OTP);
648 srom_cc_cmd(sih, ccregs, SRC_OP_READ,
653 buf[i] = R_REG(&sprom[wordoff + i]);
655 buf[i] = R_REG(&sprom[wordoff + i]);
660 /* bypass crc checking for simulation to allow srom hack */
666 if (buf[0] == 0xffff) {
667 /* The hardware thinks that an srom that starts with 0xffff
668 * is blank, regardless of the rest of the content, so declare
674 /* fixup the endianness so crc8 will pass */
675 htol16_buf(buf, nwords * 2);
676 if (bcm_crc8((u8 *) buf, nwords * 2, CRC8_INIT_VALUE) !=
678 /* DBG only pci always read srom4 first, then srom8/9 */
681 /* now correct the endianness of the byte array */
682 ltoh16_buf(buf, nwords * 2);
687 #if defined(BCMNVRAMR)
688 static int otp_read_pci(si_t *sih, u16 *buf, uint bufsz)
691 uint sz = OTP_SZ_MAX / 2; /* size in words */
694 otp = kzalloc(OTP_SZ_MAX, GFP_ATOMIC);
699 err = otp_read_region(sih, OTP_HW_RGN, (u16 *) otp, &sz);
701 memcpy(buf, otp, bufsz);
706 if (buf[0] == 0xffff) {
707 /* The hardware thinks that an srom that starts with 0xffff
708 * is blank, regardless of the rest of the content, so declare
714 /* fixup the endianness so crc8 will pass */
715 htol16_buf(buf, bufsz);
716 if (bcm_crc8((u8 *) buf, SROM4_WORDS * 2, CRC8_INIT_VALUE) !=
720 /* now correct the endianness of the byte array */
721 ltoh16_buf(buf, bufsz);
725 #endif /* defined(BCMNVRAMR) */
727 * Create variable table from memory.
728 * Return 0 on success, nonzero on error.
730 static int initvars_table(char *start, char *end,
731 char **vars, uint *count)
733 int c = (int)(end - start);
735 /* do it only when there is more than just the null string */
737 char *vp = kmalloc(c, GFP_ATOMIC);
740 memcpy(vp, start, c);
752 * Find variables with <devpath> from flash. 'base' points to the beginning
753 * of the table upon enter and to the end of the table upon exit when success.
754 * Return 0 on success, nonzero on error.
756 static int initvars_flash(si_t *sih, char **base, uint len)
762 uint l, dl, copy_len;
763 char devpath[SI_DEVPATH_BUFSZ];
765 /* allocate memory and read in flash */
766 flash = kmalloc(NVRAM_SPACE, GFP_ATOMIC);
769 err = nvram_getall(flash, NVRAM_SPACE);
773 ai_devpath(sih, devpath, sizeof(devpath));
775 /* grab vars with the <devpath> prefix in name */
776 dl = strlen(devpath);
777 for (s = flash; s && *s; s += l + 1) {
780 /* skip non-matching variable */
781 if (strncmp(s, devpath, dl))
784 /* is there enough room to copy? */
785 copy_len = l - dl + 1;
786 if (len < copy_len) {
791 /* no prefix, just the name=value */
792 strncpy(vp, &s[dl], copy_len);
797 /* add null string as terminator */
811 * Initialize nonvolatile variable table from flash.
812 * Return 0 on success, nonzero on error.
814 static int initvars_flash_si(si_t *sih, char **vars, uint *count)
819 base = vp = kmalloc(MAXSZ_NVRAM_VARS, GFP_ATOMIC);
823 err = initvars_flash(sih, &vp, MAXSZ_NVRAM_VARS);
825 err = initvars_table(base, vp, vars, count);
832 /* Parse SROM and create name=value pairs. 'srom' points to
833 * the SROM word array. 'off' specifies the offset of the
834 * first word 'srom' points to, which should be either 0 or
835 * SROM3_SWRG_OFF (full SROM or software region).
838 static uint mask_shift(u16 mask)
841 for (i = 0; i < (sizeof(mask) << 3); i++) {
848 static uint mask_width(u16 mask)
851 for (i = (sizeof(mask) << 3) - 1; i >= 0; i--) {
853 return (uint) (i - mask_shift(mask) + 1);
858 static void _initvars_srom_pci(u8 sromrev, u16 *srom, uint off, varbuf_t *b)
862 const sromvar_t *srv;
865 u32 sr = (1 << sromrev);
867 varbuf_append(b, "sromrev=%d", sromrev);
869 for (srv = pci_sromvars; srv->name != NULL; srv++) {
872 if ((srv->revmask & sr) == 0)
881 /* This entry is for mfgc only. Don't generate param for it, */
882 if (flags & SRFL_NOVAR)
885 if (flags & SRFL_ETHADDR) {
888 ea[0] = (srom[srv->off - off] >> 8) & 0xff;
889 ea[1] = srom[srv->off - off] & 0xff;
890 ea[2] = (srom[srv->off + 1 - off] >> 8) & 0xff;
891 ea[3] = srom[srv->off + 1 - off] & 0xff;
892 ea[4] = (srom[srv->off + 2 - off] >> 8) & 0xff;
893 ea[5] = srom[srv->off + 2 - off] & 0xff;
895 varbuf_append(b, "%s=%pM", name, ea);
897 w = srom[srv->off - off];
898 val = (w & srv->mask) >> mask_shift(srv->mask);
899 width = mask_width(srv->mask);
901 while (srv->flags & SRFL_MORE) {
903 if (srv->off == 0 || srv->off < off)
906 w = srom[srv->off - off];
908 ((w & srv->mask) >> mask_shift(srv->
911 width += mask_width(srv->mask);
914 if ((flags & SRFL_NOFFS)
915 && ((int)val == (1 << width) - 1))
918 if (flags & SRFL_CCODE) {
920 varbuf_append(b, "ccode=");
922 varbuf_append(b, "ccode=%c%c",
923 (val >> 8), (val & 0xff));
925 /* LED Powersave duty cycle has to be scaled:
926 *(oncount >> 24) (offcount >> 8)
928 else if (flags & SRFL_LEDDC) {
929 u32 w32 = (((val >> 8) & 0xff) << 24) | /* oncount */
930 (((val & 0xff)) << 8); /* offcount */
931 varbuf_append(b, "leddc=%d", w32);
932 } else if (flags & SRFL_PRHEX)
933 varbuf_append(b, "%s=0x%x", name, val);
934 else if ((flags & SRFL_PRSIGN)
935 && (val & (1 << (width - 1))))
936 varbuf_append(b, "%s=%d", name,
937 (int)(val | (~0 << width)));
939 varbuf_append(b, "%s=%u", name, val);
944 /* Do per-path variables */
949 psz = SROM8_PATH1 - SROM8_PATH0;
952 psz = SROM4_PATH1 - SROM4_PATH0;
955 for (p = 0; p < MAX_PATH_SROM; p++) {
956 for (srv = perpath_pci_sromvars; srv->name != NULL;
958 if ((srv->revmask & sr) == 0)
961 if (pb + srv->off < off)
964 /* This entry is for mfgc only. Don't generate param for it, */
965 if (srv->flags & SRFL_NOVAR)
968 w = srom[pb + srv->off - off];
969 val = (w & srv->mask) >> mask_shift(srv->mask);
970 width = mask_width(srv->mask);
972 /* Cheating: no per-path var is more than 1 word */
974 if ((srv->flags & SRFL_NOFFS)
975 && ((int)val == (1 << width) - 1))
978 if (srv->flags & SRFL_PRHEX)
979 varbuf_append(b, "%s%d=0x%x", srv->name,
982 varbuf_append(b, "%s%d=%d", srv->name,
991 * Initialize nonvolatile variable table from sprom.
992 * Return 0 on success, nonzero on error.
994 static int initvars_srom_pci(si_t *sih, void *curmap, char **vars, uint *count)
996 u16 *srom, *sromwindow;
1000 char *vp, *base = NULL;
1005 * Apply CRC over SROM content regardless SROM is present or not,
1006 * and use variable <devpath>sromrev's existence in flash to decide
1007 * if we should return an error when CRC fails or read SROM variables
1010 srom = kmalloc(SROM_MAX, GFP_ATOMIC);
1014 sromwindow = (u16 *) SROM_OFFSET(sih);
1015 if (ai_is_sprom_available(sih)) {
1017 sprom_read_pci(sih, sromwindow, 0, srom, SROM_WORDS,
1020 if ((srom[SROM4_SIGN] == SROM4_SIGNATURE) ||
1021 (((sih->buscoretype == PCIE_CORE_ID)
1022 && (sih->buscorerev >= 6))
1023 || ((sih->buscoretype == PCI_CORE_ID)
1024 && (sih->buscorerev >= 0xe)))) {
1025 /* sromrev >= 4, read more */
1027 sprom_read_pci(sih, sromwindow, 0, srom,
1029 sromrev = srom[SROM4_CRCREV] & 0xff;
1030 } else if (err == 0) {
1031 /* srom is good and is rev < 4 */
1032 /* top word of sprom contains version and crc8 */
1033 sromrev = srom[SROM_CRCREV] & 0xff;
1034 /* bcm4401 sroms misprogrammed */
1035 if (sromrev == 0x10)
1039 #if defined(BCMNVRAMR)
1040 /* Use OTP if SPROM not available */
1042 err = otp_read_pci(sih, srom, SROM_MAX);
1044 /* OTP only contain SROM rev8/rev9 for now */
1045 sromrev = srom[SROM4_CRCREV] & 0xff;
1055 * We want internal/wltest driver to come up with default
1056 * sromvars so we can program a blank SPROM/OTP.
1063 value = ai_getdevpathvar(sih, "sromrev");
1065 sromrev = (u8) simple_strtoul(value, NULL, 0);
1070 value = ai_getnvramflvar(sih, "sromrev");
1083 /* Bitmask for the sromrev */
1086 /* srom version check: Current valid versions: 1, 2, 3, 4, 5, 8, 9 */
1087 if ((sr & 0x33e) == 0) {
1092 base = vp = kmalloc(MAXSZ_NVRAM_VARS, GFP_ATOMIC);
1098 /* read variables from flash */
1100 err = initvars_flash(sih, &vp, MAXSZ_NVRAM_VARS);
1106 varbuf_init(&b, base, MAXSZ_NVRAM_VARS);
1108 /* parse SROM into name=value pairs. */
1109 _initvars_srom_pci(sromrev, srom, 0, &b);
1111 /* final nullbyte terminator */
1116 err = initvars_table(base, vp, vars, count);
1127 static int initvars_srom_si(si_t *sih, void *curmap, char **vars, uint *varsz)
1129 /* Search flash nvram section for srom variables */
1130 return initvars_flash_si(sih, vars, varsz);