blob: 1282ef7eb922ed419948603878663f0743b1f943 [file] [log] [blame]
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
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.
7 *
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.
15 */
Andy Shevchenko48c51a82010-09-15 12:47:18 +030016#include <linux/kernel.h>
Brett Rudley33279892010-10-01 18:03:27 -070017#include <linux/string.h>
Greg Kroah-Hartmana1c16ed2010-10-21 11:17:44 -070018#include <bcmdefs.h>
19#include <osl.h>
Brett Rudley33279892010-10-01 18:03:27 -070020#include <linuxver.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070021#include <stdarg.h>
22#include <bcmutils.h>
23#include <hndsoc.h>
24#include <sbchipc.h>
25#include <bcmdevs.h>
26#include <bcmendian.h>
27#include <pcicfg.h>
28#include <siutils.h>
29#include <bcmsrom.h>
30#include <bcmsrom_tbl.h>
31#ifdef BCMSDIO
32#include <bcmsdh.h>
33#include <sdio.h>
34#endif
35
36#include <bcmnvram.h>
37#include <bcmotp.h>
38
39#if defined(BCMSDIO)
40#include <sbsdio.h>
41#include <sbhnddma.h>
42#include <sbsdpcmdev.h>
43#endif
44
45#include <proto/ethernet.h> /* for sprom content groking */
46
47#define BS_ERROR(args)
48
49#define SROM_OFFSET(sih) ((sih->ccrev > 31) ? \
50 (((sih->cccaps & CC_CAP_SROM) == 0) ? NULL : \
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -070051 ((u8 *)curmap + PCI_16KB0_CCREGS_OFFSET + CC_SROM_OTP)) : \
52 ((u8 *)curmap + PCI_BAR0_SPROM_OFFSET))
Henry Ptasinskia9533e72010-09-08 21:04:42 -070053
54#if defined(BCMDBG)
55#define WRITE_ENABLE_DELAY 500 /* 500 ms after write enable/disable toggle */
56#define WRITE_WORD_DELAY 20 /* 20 ms between each word write */
57#endif
58
59typedef struct varbuf {
60 char *base; /* pointer to buffer base */
61 char *buf; /* pointer to current position */
62 unsigned int size; /* current (residual) size in bytes */
63} varbuf_t;
64extern char *_vars;
65extern uint _varsz;
66
67#define SROM_CIS_SINGLE 1
68
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040069static int initvars_srom_si(si_t *sih, osl_t *osh, void *curmap, char **vars,
70 uint *count);
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070071static void _initvars_srom_pci(u8 sromrev, u16 *srom, uint off,
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040072 varbuf_t *b);
73static int initvars_srom_pci(si_t *sih, void *curmap, char **vars,
74 uint *count);
75static int initvars_flash_si(si_t *sih, char **vars, uint *count);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070076#ifdef BCMSDIO
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040077static int initvars_cis_sdio(osl_t *osh, char **vars, uint *count);
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -070078static int sprom_cmd_sdio(osl_t *osh, u8 cmd);
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070079static int sprom_read_sdio(osl_t *osh, u16 addr, u16 *data);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070080#endif /* BCMSDIO */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070081static int sprom_read_pci(osl_t *osh, si_t *sih, u16 *sprom, uint wordoff,
82 u16 *buf, uint nwords, bool check_crc);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070083#if defined(BCMNVRAMR)
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070084static int otp_read_pci(osl_t *osh, si_t *sih, u16 *buf, uint bufsz);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070085#endif
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -070086static u16 srom_cc_cmd(si_t *sih, osl_t *osh, void *ccregs, u32 cmd,
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070087 uint wordoff, u16 data);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070088
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040089static int initvars_table(osl_t *osh, char *start, char *end, char **vars,
90 uint *count);
91static int initvars_flash(si_t *sih, osl_t *osh, char **vp, uint len);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070092
93/* Initialization of varbuf structure */
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -070094static void varbuf_init(varbuf_t *b, char *buf, uint size)
Jason Coopera2627bc2010-09-14 09:45:31 -040095{
Henry Ptasinskia9533e72010-09-08 21:04:42 -070096 b->size = size;
97 b->base = b->buf = buf;
98}
99
100/* append a null terminated var=value string */
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -0700101static int varbuf_append(varbuf_t *b, const char *fmt, ...)
Jason Coopera2627bc2010-09-14 09:45:31 -0400102{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700103 va_list ap;
104 int r;
105 size_t len;
106 char *s;
107
108 if (b->size < 2)
109 return 0;
110
111 va_start(ap, fmt);
112 r = vsnprintf(b->buf, b->size, fmt, ap);
113 va_end(ap);
114
115 /* C99 snprintf behavior returns r >= size on overflow,
116 * others return -1 on overflow.
117 * All return -1 on format error.
118 * We need to leave room for 2 null terminations, one for the current var
119 * string, and one for final null of the var table. So check that the
120 * strlen written, r, leaves room for 2 chars.
121 */
122 if ((r == -1) || (r > (int)(b->size - 2))) {
123 b->size = 0;
124 return 0;
125 }
126
127 /* Remove any earlier occurrence of the same variable */
Jason Cooperca8c1e52010-09-14 09:45:33 -0400128 s = strchr(b->buf, '=');
129 if (s != NULL) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700130 len = (size_t) (s - b->buf);
131 for (s = b->base; s < b->buf;) {
132 if ((bcmp(s, b->buf, len) == 0) && s[len] == '=') {
133 len = strlen(s) + 1;
134 memmove(s, (s + len),
135 ((b->buf + r + 1) - (s + len)));
136 b->buf -= len;
137 b->size += (unsigned int)len;
138 break;
139 }
140
Jason Cooper62145822010-09-14 09:45:34 -0400141 while (*s++)
142 ;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700143 }
144 }
145
146 /* skip over this string's null termination */
147 r++;
148 b->size -= r;
149 b->buf += r;
150
151 return r;
152}
153
154/*
155 * Initialize local vars from the right source for this platform.
156 * Return 0 on success, nonzero on error.
157 */
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -0700158int srom_var_init(si_t *sih, uint bustype, void *curmap, osl_t *osh,
159 char **vars, uint *count)
160{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700161 uint len;
162
163 len = 0;
164
165 ASSERT(bustype == BUSTYPE(bustype));
166 if (vars == NULL || count == NULL)
Jason Cooper90ea2292010-09-14 09:45:32 -0400167 return 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700168
169 *vars = NULL;
170 *count = 0;
171
172 switch (BUSTYPE(bustype)) {
173 case SI_BUS:
174 case JTAG_BUS:
175 return initvars_srom_si(sih, osh, curmap, vars, count);
176
177 case PCI_BUS:
178 ASSERT(curmap != NULL);
179 if (curmap == NULL)
Jason Cooper90ea2292010-09-14 09:45:32 -0400180 return -1;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700181
182 return initvars_srom_pci(sih, curmap, vars, count);
183
184#ifdef BCMSDIO
185 case SDIO_BUS:
186 return initvars_cis_sdio(osh, vars, count);
187#endif /* BCMSDIO */
188
189 default:
190 ASSERT(0);
191 }
Jason Cooper90ea2292010-09-14 09:45:32 -0400192 return -1;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700193}
194
195/* support only 16-bit word read from srom */
196int
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400197srom_read(si_t *sih, uint bustype, void *curmap, osl_t *osh,
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700198 uint byteoff, uint nbytes, u16 *buf, bool check_crc)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700199{
200 uint off, nw;
201#ifdef BCMSDIO
202 uint i;
203#endif /* BCMSDIO */
204
205 ASSERT(bustype == BUSTYPE(bustype));
206
207 /* check input - 16-bit access only */
208 if (byteoff & 1 || nbytes & 1 || (byteoff + nbytes) > SROM_MAX)
209 return 1;
210
211 off = byteoff / 2;
212 nw = nbytes / 2;
213
214 if (BUSTYPE(bustype) == PCI_BUS) {
215 if (!curmap)
216 return 1;
217
218 if (si_is_sprom_available(sih)) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700219 u16 *srom;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700220
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700221 srom = (u16 *) SROM_OFFSET(sih);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700222 if (srom == NULL)
223 return 1;
224
225 if (sprom_read_pci
226 (osh, sih, srom, off, buf, nw, check_crc))
227 return 1;
228 }
229#if defined(BCMNVRAMR)
230 else {
231 if (otp_read_pci(osh, sih, buf, SROM_MAX))
232 return 1;
233 }
234#endif
235#ifdef BCMSDIO
236 } else if (BUSTYPE(bustype) == SDIO_BUS) {
237 off = byteoff / 2;
238 nw = nbytes / 2;
239 for (i = 0; i < nw; i++) {
240 if (sprom_read_sdio
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700241 (osh, (u16) (off + i), (u16 *) (buf + i)))
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700242 return 1;
243 }
244#endif /* BCMSDIO */
245 } else if (BUSTYPE(bustype) == SI_BUS) {
246 return 1;
247 } else {
248 return 1;
249 }
250
251 return 0;
252}
253
Greg Kroah-Hartman17c4da12010-10-08 14:11:20 -0700254static const char vstr_manf[] = "manf=%s";
255static const char vstr_productname[] = "productname=%s";
256static const char vstr_manfid[] = "manfid=0x%x";
257static const char vstr_prodid[] = "prodid=0x%x";
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700258#ifdef BCMSDIO
Greg Kroah-Hartman17c4da12010-10-08 14:11:20 -0700259static const char vstr_sdmaxspeed[] = "sdmaxspeed=%d";
260static const char vstr_sdmaxblk[][13] = {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700261"sdmaxblk0=%d", "sdmaxblk1=%d", "sdmaxblk2=%d"};
262#endif
Greg Kroah-Hartman17c4da12010-10-08 14:11:20 -0700263static const char vstr_regwindowsz[] = "regwindowsz=%d";
264static const char vstr_sromrev[] = "sromrev=%d";
265static const char vstr_chiprev[] = "chiprev=%d";
266static const char vstr_subvendid[] = "subvendid=0x%x";
267static const char vstr_subdevid[] = "subdevid=0x%x";
268static const char vstr_boardrev[] = "boardrev=0x%x";
269static const char vstr_aa2g[] = "aa2g=0x%x";
270static const char vstr_aa5g[] = "aa5g=0x%x";
271static const char vstr_ag[] = "ag%d=0x%x";
272static const char vstr_cc[] = "cc=%d";
273static const char vstr_opo[] = "opo=%d";
274static const char vstr_pa0b[][9] = {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700275"pa0b0=%d", "pa0b1=%d", "pa0b2=%d"};
276
Greg Kroah-Hartman17c4da12010-10-08 14:11:20 -0700277static const char vstr_pa0itssit[] = "pa0itssit=%d";
278static const char vstr_pa0maxpwr[] = "pa0maxpwr=%d";
279static const char vstr_pa1b[][9] = {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700280"pa1b0=%d", "pa1b1=%d", "pa1b2=%d"};
281
Greg Kroah-Hartman17c4da12010-10-08 14:11:20 -0700282static const char vstr_pa1lob[][11] = {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700283"pa1lob0=%d", "pa1lob1=%d", "pa1lob2=%d"};
284
Greg Kroah-Hartman17c4da12010-10-08 14:11:20 -0700285static const char vstr_pa1hib[][11] = {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700286"pa1hib0=%d", "pa1hib1=%d", "pa1hib2=%d"};
287
Greg Kroah-Hartman17c4da12010-10-08 14:11:20 -0700288static const char vstr_pa1itssit[] = "pa1itssit=%d";
289static const char vstr_pa1maxpwr[] = "pa1maxpwr=%d";
290static const char vstr_pa1lomaxpwr[] = "pa1lomaxpwr=%d";
291static const char vstr_pa1himaxpwr[] = "pa1himaxpwr=%d";
292static const char vstr_oem[] =
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700293 "oem=%02x%02x%02x%02x%02x%02x%02x%02x";
Greg Kroah-Hartman17c4da12010-10-08 14:11:20 -0700294static const char vstr_boardflags[] = "boardflags=0x%x";
295static const char vstr_boardflags2[] = "boardflags2=0x%x";
296static const char vstr_ledbh[] = "ledbh%d=0x%x";
297static const char vstr_noccode[] = "ccode=0x0";
298static const char vstr_ccode[] = "ccode=%c%c";
299static const char vstr_cctl[] = "cctl=0x%x";
300static const char vstr_cckpo[] = "cckpo=0x%x";
301static const char vstr_ofdmpo[] = "ofdmpo=0x%x";
302static const char vstr_rdlid[] = "rdlid=0x%x";
303static const char vstr_rdlrndis[] = "rdlrndis=%d";
304static const char vstr_rdlrwu[] = "rdlrwu=%d";
305static const char vstr_usbfs[] = "usbfs=%d";
306static const char vstr_wpsgpio[] = "wpsgpio=%d";
307static const char vstr_wpsled[] = "wpsled=%d";
308static const char vstr_rdlsn[] = "rdlsn=%d";
309static const char vstr_rssismf2g[] = "rssismf2g=%d";
310static const char vstr_rssismc2g[] = "rssismc2g=%d";
311static const char vstr_rssisav2g[] = "rssisav2g=%d";
312static const char vstr_bxa2g[] = "bxa2g=%d";
313static const char vstr_rssismf5g[] = "rssismf5g=%d";
314static const char vstr_rssismc5g[] = "rssismc5g=%d";
315static const char vstr_rssisav5g[] = "rssisav5g=%d";
316static const char vstr_bxa5g[] = "bxa5g=%d";
317static const char vstr_tri2g[] = "tri2g=%d";
318static const char vstr_tri5gl[] = "tri5gl=%d";
319static const char vstr_tri5g[] = "tri5g=%d";
320static const char vstr_tri5gh[] = "tri5gh=%d";
321static const char vstr_rxpo2g[] = "rxpo2g=%d";
322static const char vstr_rxpo5g[] = "rxpo5g=%d";
323static const char vstr_boardtype[] = "boardtype=0x%x";
324static const char vstr_leddc[] = "leddc=0x%04x";
325static const char vstr_vendid[] = "vendid=0x%x";
326static const char vstr_devid[] = "devid=0x%x";
327static const char vstr_xtalfreq[] = "xtalfreq=%d";
328static const char vstr_txchain[] = "txchain=0x%x";
329static const char vstr_rxchain[] = "rxchain=0x%x";
330static const char vstr_antswitch[] = "antswitch=0x%x";
331static const char vstr_regrev[] = "regrev=0x%x";
332static const char vstr_antswctl2g[] = "antswctl2g=0x%x";
333static const char vstr_triso2g[] = "triso2g=0x%x";
334static const char vstr_pdetrange2g[] = "pdetrange2g=0x%x";
335static const char vstr_extpagain2g[] = "extpagain2g=0x%x";
336static const char vstr_tssipos2g[] = "tssipos2g=0x%x";
337static const char vstr_antswctl5g[] = "antswctl5g=0x%x";
338static const char vstr_triso5g[] = "triso5g=0x%x";
339static const char vstr_pdetrange5g[] = "pdetrange5g=0x%x";
340static const char vstr_extpagain5g[] = "extpagain5g=0x%x";
341static const char vstr_tssipos5g[] = "tssipos5g=0x%x";
342static const char vstr_maxp2ga0[] = "maxp2ga0=0x%x";
343static const char vstr_itt2ga0[] = "itt2ga0=0x%x";
344static const char vstr_pa[] = "pa%dgw%da%d=0x%x";
345static const char vstr_pahl[] = "pa%dg%cw%da%d=0x%x";
346static const char vstr_maxp5ga0[] = "maxp5ga0=0x%x";
347static const char vstr_itt5ga0[] = "itt5ga0=0x%x";
348static const char vstr_maxp5gha0[] = "maxp5gha0=0x%x";
349static const char vstr_maxp5gla0[] = "maxp5gla0=0x%x";
350static const char vstr_maxp2ga1[] = "maxp2ga1=0x%x";
351static const char vstr_itt2ga1[] = "itt2ga1=0x%x";
352static const char vstr_maxp5ga1[] = "maxp5ga1=0x%x";
353static const char vstr_itt5ga1[] = "itt5ga1=0x%x";
354static const char vstr_maxp5gha1[] = "maxp5gha1=0x%x";
355static const char vstr_maxp5gla1[] = "maxp5gla1=0x%x";
356static const char vstr_cck2gpo[] = "cck2gpo=0x%x";
357static const char vstr_ofdm2gpo[] = "ofdm2gpo=0x%x";
358static const char vstr_ofdm5gpo[] = "ofdm5gpo=0x%x";
359static const char vstr_ofdm5glpo[] = "ofdm5glpo=0x%x";
360static const char vstr_ofdm5ghpo[] = "ofdm5ghpo=0x%x";
361static const char vstr_cddpo[] = "cddpo=0x%x";
362static const char vstr_stbcpo[] = "stbcpo=0x%x";
363static const char vstr_bw40po[] = "bw40po=0x%x";
364static const char vstr_bwduppo[] = "bwduppo=0x%x";
365static const char vstr_mcspo[] = "mcs%dgpo%d=0x%x";
366static const char vstr_mcspohl[] = "mcs%dg%cpo%d=0x%x";
367static const char vstr_custom[] = "customvar%d=0x%x";
368static const char vstr_cckdigfilttype[] = "cckdigfilttype=%d";
369static const char vstr_boardnum[] = "boardnum=%d";
370static const char vstr_macaddr[] = "macaddr=%s";
371static const char vstr_usbepnum[] = "usbepnum=0x%x";
372static const char vstr_end[] = "END\0";
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700373
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700374u8 patch_pair;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700375
376/* For dongle HW, accept partial calibration parameters */
377#define BCMDONGLECASE(n)
378
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -0700379int srom_parsecis(osl_t *osh, u8 *pcis[], uint ciscnt, char **vars, uint *count)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700380{
381 char eabuf[32];
382 char *base;
383 varbuf_t b;
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700384 u8 *cis, tup, tlen, sromrev = 1;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700385 int i, j;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700386 bool ag_init = false;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700387 u32 w32;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700388 uint funcid;
389 uint cisnum;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700390 s32 boardnum;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700391 int err;
392 bool standard_cis;
393
394 ASSERT(vars != NULL);
395 ASSERT(count != NULL);
396
397 boardnum = -1;
398
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +0200399 base = kmalloc(MAXSZ_NVRAM_VARS, GFP_ATOMIC);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700400 ASSERT(base != NULL);
401 if (!base)
402 return -2;
403
404 varbuf_init(&b, base, MAXSZ_NVRAM_VARS);
405 bzero(base, MAXSZ_NVRAM_VARS);
406 eabuf[0] = '\0';
407 for (cisnum = 0; cisnum < ciscnt; cisnum++) {
408 cis = *pcis++;
409 i = 0;
410 funcid = 0;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700411 standard_cis = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700412 do {
413 if (standard_cis) {
414 tup = cis[i++];
415 if (tup == CISTPL_NULL || tup == CISTPL_END)
416 tlen = 0;
417 else
418 tlen = cis[i++];
419 } else {
420 if (cis[i] == CISTPL_NULL
421 || cis[i] == CISTPL_END) {
422 tlen = 0;
423 tup = cis[i];
424 } else {
425 tlen = cis[i];
426 tup = CISTPL_BRCM_HNBU;
427 }
428 ++i;
429 }
430 if ((i + tlen) >= CIS_SIZE)
431 break;
432
433 switch (tup) {
434 case CISTPL_VERS_1:
435 /* assume the strings are good if the version field checks out */
436 if (((cis[i + 1] << 8) + cis[i]) >= 0x0008) {
437 varbuf_append(&b, vstr_manf,
438 &cis[i + 2]);
439 varbuf_append(&b, vstr_productname,
440 &cis[i + 3 +
441 strlen((char *)
442 &cis[i +
443 2])]);
444 break;
445 }
446
447 case CISTPL_MANFID:
448 varbuf_append(&b, vstr_manfid,
449 (cis[i + 1] << 8) + cis[i]);
450 varbuf_append(&b, vstr_prodid,
451 (cis[i + 3] << 8) + cis[i + 2]);
452 break;
453
454 case CISTPL_FUNCID:
455 funcid = cis[i];
456 break;
457
458 case CISTPL_FUNCE:
459 switch (funcid) {
460 case CISTPL_FID_SDIO:
461#ifdef BCMSDIO
462 if (cis[i] == 0) {
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700463 u8 spd = cis[i + 3];
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700464 static int base[] = {
465 -1, 10, 12, 13, 15, 20,
466 25, 30,
467 35, 40, 45, 50, 55, 60,
468 70, 80
469 };
470 static int mult[] = {
471 10, 100, 1000, 10000,
472 -1, -1, -1, -1
473 };
474 ASSERT((mult[spd & 0x7] != -1)
475 &&
476 (base
477 [(spd >> 3) & 0x0f]));
478 varbuf_append(&b,
479 vstr_sdmaxblk[0],
480 (cis[i + 2] << 8)
481 + cis[i + 1]);
482 varbuf_append(&b,
483 vstr_sdmaxspeed,
484 (mult[spd & 0x7] *
485 base[(spd >> 3) &
486 0x0f]));
487 } else if (cis[i] == 1) {
488 varbuf_append(&b,
489 vstr_sdmaxblk
490 [cisnum],
491 (cis[i + 13] << 8)
492 | cis[i + 12]);
493 }
494#endif /* BCMSDIO */
495 funcid = 0;
496 break;
497 default:
498 /* set macaddr if HNBU_MACADDR not seen yet */
499 if (eabuf[0] == '\0'
500 && cis[i] == LAN_NID
501 && !(ETHER_ISNULLADDR(&cis[i + 2]))
502 && !(ETHER_ISMULTI(&cis[i + 2]))) {
503 ASSERT(cis[i + 1] ==
504 ETHER_ADDR_LEN);
Andy Shevchenkoba07d0c2010-10-11 16:58:33 +0300505 snprintf(eabuf, sizeof(eabuf),
506 "%pM", &cis[i + 2]);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700507
508 /* set boardnum if HNBU_BOARDNUM not seen yet */
509 if (boardnum == -1)
510 boardnum =
511 (cis[i + 6] << 8) +
512 cis[i + 7];
513 }
514 break;
515 }
516 break;
517
518 case CISTPL_CFTABLE:
519 varbuf_append(&b, vstr_regwindowsz,
520 (cis[i + 7] << 8) | cis[i + 6]);
521 break;
522
523 case CISTPL_BRCM_HNBU:
524 switch (cis[i]) {
525 case HNBU_SROMREV:
526 sromrev = cis[i + 1];
527 varbuf_append(&b, vstr_sromrev,
528 sromrev);
529 break;
530
531 case HNBU_XTALFREQ:
532 varbuf_append(&b, vstr_xtalfreq,
533 (cis[i + 4] << 24) |
534 (cis[i + 3] << 16) |
535 (cis[i + 2] << 8) |
536 cis[i + 1]);
537 break;
538
539 case HNBU_CHIPID:
540 varbuf_append(&b, vstr_vendid,
541 (cis[i + 2] << 8) +
542 cis[i + 1]);
543 varbuf_append(&b, vstr_devid,
544 (cis[i + 4] << 8) +
545 cis[i + 3]);
546 if (tlen >= 7) {
547 varbuf_append(&b, vstr_chiprev,
548 (cis[i + 6] << 8)
549 + cis[i + 5]);
550 }
551 if (tlen >= 9) {
552 varbuf_append(&b,
553 vstr_subvendid,
554 (cis[i + 8] << 8)
555 + cis[i + 7]);
556 }
557 if (tlen >= 11) {
558 varbuf_append(&b, vstr_subdevid,
559 (cis[i + 10] << 8)
560 + cis[i + 9]);
561 /* subdevid doubles for boardtype */
562 varbuf_append(&b,
563 vstr_boardtype,
564 (cis[i + 10] << 8)
565 + cis[i + 9]);
566 }
567 break;
568
569 case HNBU_BOARDNUM:
570 boardnum =
571 (cis[i + 2] << 8) + cis[i + 1];
572 break;
573
574 case HNBU_PATCH:
575 {
576 char vstr_paddr[16];
577 char vstr_pdata[16];
578
579 /* retrieve the patch pairs
580 * from tlen/6; where 6 is
581 * sizeof(patch addr(2)) +
582 * sizeof(patch data(4)).
583 */
584 patch_pair = tlen / 6;
585
586 for (j = 0; j < patch_pair; j++) {
587 snprintf(vstr_paddr,
588 sizeof
589 (vstr_paddr),
590 "pa%d=0x%%x",
591 j);
592 snprintf(vstr_pdata,
593 sizeof
594 (vstr_pdata),
595 "pd%d=0x%%x",
596 j);
597
598 varbuf_append(&b,
599 vstr_paddr,
600 (cis
601 [i +
602 (j *
603 6) +
604 2] << 8)
605 | cis[i +
606 (j *
607 6)
608 +
609 1]);
610
611 varbuf_append(&b,
612 vstr_pdata,
613 (cis
614 [i +
615 (j *
616 6) +
617 6] <<
618 24) |
619 (cis
620 [i +
621 (j *
622 6) +
623 5] <<
624 16) |
625 (cis
626 [i +
627 (j *
628 6) +
629 4] << 8)
630 | cis[i +
631 (j *
632 6)
633 +
634 3]);
635 }
636 }
637 break;
638
639 case HNBU_BOARDREV:
640 if (tlen == 2)
641 varbuf_append(&b, vstr_boardrev,
642 cis[i + 1]);
643 else
644 varbuf_append(&b, vstr_boardrev,
645 (cis[i + 2] << 8)
646 + cis[i + 1]);
647 break;
648
649 case HNBU_BOARDFLAGS:
650 w32 = (cis[i + 2] << 8) + cis[i + 1];
651 if (tlen >= 5)
652 w32 |=
653 ((cis[i + 4] << 24) +
654 (cis[i + 3] << 16));
655 varbuf_append(&b, vstr_boardflags, w32);
656
657 if (tlen >= 7) {
658 w32 =
659 (cis[i + 6] << 8) + cis[i +
660 5];
661 if (tlen >= 9)
662 w32 |=
663 ((cis[i + 8] << 24)
664 +
665 (cis[i + 7] <<
666 16));
667 varbuf_append(&b,
668 vstr_boardflags2,
669 w32);
670 }
671 break;
672
673 case HNBU_USBFS:
674 varbuf_append(&b, vstr_usbfs,
675 cis[i + 1]);
676 break;
677
678 case HNBU_BOARDTYPE:
679 varbuf_append(&b, vstr_boardtype,
680 (cis[i + 2] << 8) +
681 cis[i + 1]);
682 break;
683
684 case HNBU_HNBUCIS:
685 /*
686 * what follows is a nonstandard HNBU CIS
687 * that lacks CISTPL_BRCM_HNBU tags
688 *
689 * skip 0xff (end of standard CIS)
690 * after this tuple
691 */
692 tlen++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700693 standard_cis = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700694 break;
695
696 case HNBU_USBEPNUM:
697 varbuf_append(&b, vstr_usbepnum,
698 (cis[i + 2] << 8) | cis[i
699 +
700 1]);
701 break;
702
703 case HNBU_AA:
704 varbuf_append(&b, vstr_aa2g,
705 cis[i + 1]);
706 if (tlen >= 3)
707 varbuf_append(&b, vstr_aa5g,
708 cis[i + 2]);
709 break;
710
711 case HNBU_AG:
712 varbuf_append(&b, vstr_ag, 0,
713 cis[i + 1]);
714 if (tlen >= 3)
715 varbuf_append(&b, vstr_ag, 1,
716 cis[i + 2]);
717 if (tlen >= 4)
718 varbuf_append(&b, vstr_ag, 2,
719 cis[i + 3]);
720 if (tlen >= 5)
721 varbuf_append(&b, vstr_ag, 3,
722 cis[i + 4]);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700723 ag_init = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700724 break;
725
726 case HNBU_ANT5G:
727 varbuf_append(&b, vstr_aa5g,
728 cis[i + 1]);
729 varbuf_append(&b, vstr_ag, 1,
730 cis[i + 2]);
731 break;
732
733 case HNBU_CC:
734 ASSERT(sromrev == 1);
735 varbuf_append(&b, vstr_cc, cis[i + 1]);
736 break;
737
738 case HNBU_PAPARMS:
739 switch (tlen) {
740 case 2:
741 ASSERT(sromrev == 1);
742 varbuf_append(&b,
743 vstr_pa0maxpwr,
744 cis[i + 1]);
745 break;
746 case 10:
747 ASSERT(sromrev >= 2);
748 varbuf_append(&b, vstr_opo,
749 cis[i + 9]);
750 /* FALLTHROUGH */
751 case 9:
752 varbuf_append(&b,
753 vstr_pa0maxpwr,
754 cis[i + 8]);
755 /* FALLTHROUGH */
756 BCMDONGLECASE(8)
757 varbuf_append(&b,
758 vstr_pa0itssit,
759 cis[i + 7]);
760 /* FALLTHROUGH */
761 BCMDONGLECASE(7)
762 for (j = 0; j < 3; j++) {
763 varbuf_append(&b,
764 vstr_pa0b
765 [j],
766 (cis
767 [i +
768 (j *
769 2) +
770 2] << 8)
771 + cis[i +
772 (j *
773 2)
774 +
775 1]);
776 }
777 break;
778 default:
779 ASSERT((tlen == 2)
780 || (tlen == 9)
781 || (tlen == 10));
782 break;
783 }
784 break;
785
786 case HNBU_PAPARMS5G:
787 ASSERT((sromrev == 2)
788 || (sromrev == 3));
789 switch (tlen) {
790 case 23:
791 varbuf_append(&b,
792 vstr_pa1himaxpwr,
793 cis[i + 22]);
794 varbuf_append(&b,
795 vstr_pa1lomaxpwr,
796 cis[i + 21]);
797 varbuf_append(&b,
798 vstr_pa1maxpwr,
799 cis[i + 20]);
800 /* FALLTHROUGH */
801 case 20:
802 varbuf_append(&b,
803 vstr_pa1itssit,
804 cis[i + 19]);
805 /* FALLTHROUGH */
806 case 19:
807 for (j = 0; j < 3; j++) {
808 varbuf_append(&b,
809 vstr_pa1b
810 [j],
811 (cis
812 [i +
813 (j *
814 2) +
815 2] << 8)
816 + cis[i +
817 (j *
818 2)
819 +
820 1]);
821 }
822 for (j = 3; j < 6; j++) {
823 varbuf_append(&b,
824 vstr_pa1lob
825 [j - 3],
826 (cis
827 [i +
828 (j *
829 2) +
830 2] << 8)
831 + cis[i +
832 (j *
833 2)
834 +
835 1]);
836 }
837 for (j = 6; j < 9; j++) {
838 varbuf_append(&b,
839 vstr_pa1hib
840 [j - 6],
841 (cis
842 [i +
843 (j *
844 2) +
845 2] << 8)
846 + cis[i +
847 (j *
848 2)
849 +
850 1]);
851 }
852 break;
853 default:
854 ASSERT((tlen == 19) ||
855 (tlen == 20)
856 || (tlen == 23));
857 break;
858 }
859 break;
860
861 case HNBU_OEM:
862 ASSERT(sromrev == 1);
863 varbuf_append(&b, vstr_oem,
864 cis[i + 1], cis[i + 2],
865 cis[i + 3], cis[i + 4],
866 cis[i + 5], cis[i + 6],
867 cis[i + 7], cis[i + 8]);
868 break;
869
870 case HNBU_LEDS:
871 for (j = 1; j <= 4; j++) {
872 if (cis[i + j] != 0xff) {
873 varbuf_append(&b,
874 vstr_ledbh,
875 j - 1,
876 cis[i +
877 j]);
878 }
879 }
880 break;
881
882 case HNBU_CCODE:
883 ASSERT(sromrev > 1);
884 if ((cis[i + 1] == 0)
885 || (cis[i + 2] == 0))
886 varbuf_append(&b, vstr_noccode);
887 else
888 varbuf_append(&b, vstr_ccode,
889 cis[i + 1],
890 cis[i + 2]);
891 varbuf_append(&b, vstr_cctl,
892 cis[i + 3]);
893 break;
894
895 case HNBU_CCKPO:
896 ASSERT(sromrev > 2);
897 varbuf_append(&b, vstr_cckpo,
898 (cis[i + 2] << 8) | cis[i
899 +
900 1]);
901 break;
902
903 case HNBU_OFDMPO:
904 ASSERT(sromrev > 2);
905 varbuf_append(&b, vstr_ofdmpo,
906 (cis[i + 4] << 24) |
907 (cis[i + 3] << 16) |
908 (cis[i + 2] << 8) |
909 cis[i + 1]);
910 break;
911
912 case HNBU_WPS:
913 varbuf_append(&b, vstr_wpsgpio,
914 cis[i + 1]);
915 if (tlen >= 3)
916 varbuf_append(&b, vstr_wpsled,
917 cis[i + 2]);
918 break;
919
920 case HNBU_RSSISMBXA2G:
921 ASSERT(sromrev == 3);
922 varbuf_append(&b, vstr_rssismf2g,
923 cis[i + 1] & 0xf);
924 varbuf_append(&b, vstr_rssismc2g,
925 (cis[i + 1] >> 4) & 0xf);
926 varbuf_append(&b, vstr_rssisav2g,
927 cis[i + 2] & 0x7);
928 varbuf_append(&b, vstr_bxa2g,
929 (cis[i + 2] >> 3) & 0x3);
930 break;
931
932 case HNBU_RSSISMBXA5G:
933 ASSERT(sromrev == 3);
934 varbuf_append(&b, vstr_rssismf5g,
935 cis[i + 1] & 0xf);
936 varbuf_append(&b, vstr_rssismc5g,
937 (cis[i + 1] >> 4) & 0xf);
938 varbuf_append(&b, vstr_rssisav5g,
939 cis[i + 2] & 0x7);
940 varbuf_append(&b, vstr_bxa5g,
941 (cis[i + 2] >> 3) & 0x3);
942 break;
943
944 case HNBU_TRI2G:
945 ASSERT(sromrev == 3);
946 varbuf_append(&b, vstr_tri2g,
947 cis[i + 1]);
948 break;
949
950 case HNBU_TRI5G:
951 ASSERT(sromrev == 3);
952 varbuf_append(&b, vstr_tri5gl,
953 cis[i + 1]);
954 varbuf_append(&b, vstr_tri5g,
955 cis[i + 2]);
956 varbuf_append(&b, vstr_tri5gh,
957 cis[i + 3]);
958 break;
959
960 case HNBU_RXPO2G:
961 ASSERT(sromrev == 3);
962 varbuf_append(&b, vstr_rxpo2g,
963 cis[i + 1]);
964 break;
965
966 case HNBU_RXPO5G:
967 ASSERT(sromrev == 3);
968 varbuf_append(&b, vstr_rxpo5g,
969 cis[i + 1]);
970 break;
971
972 case HNBU_MACADDR:
973 if (!(ETHER_ISNULLADDR(&cis[i + 1])) &&
974 !(ETHER_ISMULTI(&cis[i + 1]))) {
Andy Shevchenkoba07d0c2010-10-11 16:58:33 +0300975 snprintf(eabuf, sizeof(eabuf),
976 "%pM", &cis[i + 1]);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700977
978 /* set boardnum if HNBU_BOARDNUM not seen yet */
979 if (boardnum == -1)
980 boardnum =
981 (cis[i + 5] << 8) +
982 cis[i + 6];
983 }
984 break;
985
986 case HNBU_LEDDC:
987 /* CIS leddc only has 16bits, convert it to 32bits */
988 w32 = ((cis[i + 2] << 24) | /* oncount */
989 (cis[i + 1] << 8)); /* offcount */
990 varbuf_append(&b, vstr_leddc, w32);
991 break;
992
993 case HNBU_CHAINSWITCH:
994 varbuf_append(&b, vstr_txchain,
995 cis[i + 1]);
996 varbuf_append(&b, vstr_rxchain,
997 cis[i + 2]);
998 varbuf_append(&b, vstr_antswitch,
999 (cis[i + 4] << 8) +
1000 cis[i + 3]);
1001 break;
1002
1003 case HNBU_REGREV:
1004 varbuf_append(&b, vstr_regrev,
1005 cis[i + 1]);
1006 break;
1007
1008 case HNBU_FEM:{
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001009 u16 fem =
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001010 (cis[i + 2] << 8) + cis[i +
1011 1];
1012 varbuf_append(&b,
1013 vstr_antswctl2g,
1014 (fem &
1015 SROM8_FEM_ANTSWLUT_MASK)
1016 >>
1017 SROM8_FEM_ANTSWLUT_SHIFT);
1018 varbuf_append(&b, vstr_triso2g,
1019 (fem &
1020 SROM8_FEM_TR_ISO_MASK)
1021 >>
1022 SROM8_FEM_TR_ISO_SHIFT);
1023 varbuf_append(&b,
1024 vstr_pdetrange2g,
1025 (fem &
1026 SROM8_FEM_PDET_RANGE_MASK)
1027 >>
1028 SROM8_FEM_PDET_RANGE_SHIFT);
1029 varbuf_append(&b,
1030 vstr_extpagain2g,
1031 (fem &
1032 SROM8_FEM_EXTPA_GAIN_MASK)
1033 >>
1034 SROM8_FEM_EXTPA_GAIN_SHIFT);
1035 varbuf_append(&b,
1036 vstr_tssipos2g,
1037 (fem &
1038 SROM8_FEM_TSSIPOS_MASK)
1039 >>
1040 SROM8_FEM_TSSIPOS_SHIFT);
1041 if (tlen < 5)
1042 break;
1043
1044 fem =
1045 (cis[i + 4] << 8) + cis[i +
1046 3];
1047 varbuf_append(&b,
1048 vstr_antswctl5g,
1049 (fem &
1050 SROM8_FEM_ANTSWLUT_MASK)
1051 >>
1052 SROM8_FEM_ANTSWLUT_SHIFT);
1053 varbuf_append(&b, vstr_triso5g,
1054 (fem &
1055 SROM8_FEM_TR_ISO_MASK)
1056 >>
1057 SROM8_FEM_TR_ISO_SHIFT);
1058 varbuf_append(&b,
1059 vstr_pdetrange5g,
1060 (fem &
1061 SROM8_FEM_PDET_RANGE_MASK)
1062 >>
1063 SROM8_FEM_PDET_RANGE_SHIFT);
1064 varbuf_append(&b,
1065 vstr_extpagain5g,
1066 (fem &
1067 SROM8_FEM_EXTPA_GAIN_MASK)
1068 >>
1069 SROM8_FEM_EXTPA_GAIN_SHIFT);
1070 varbuf_append(&b,
1071 vstr_tssipos5g,
1072 (fem &
1073 SROM8_FEM_TSSIPOS_MASK)
1074 >>
1075 SROM8_FEM_TSSIPOS_SHIFT);
1076 break;
1077 }
1078
1079 case HNBU_PAPARMS_C0:
1080 varbuf_append(&b, vstr_maxp2ga0,
1081 cis[i + 1]);
1082 varbuf_append(&b, vstr_itt2ga0,
1083 cis[i + 2]);
1084 varbuf_append(&b, vstr_pa, 2, 0, 0,
1085 (cis[i + 4] << 8) +
1086 cis[i + 3]);
1087 varbuf_append(&b, vstr_pa, 2, 1, 0,
1088 (cis[i + 6] << 8) +
1089 cis[i + 5]);
1090 varbuf_append(&b, vstr_pa, 2, 2, 0,
1091 (cis[i + 8] << 8) +
1092 cis[i + 7]);
1093 if (tlen < 31)
1094 break;
1095
1096 varbuf_append(&b, vstr_maxp5ga0,
1097 cis[i + 9]);
1098 varbuf_append(&b, vstr_itt5ga0,
1099 cis[i + 10]);
1100 varbuf_append(&b, vstr_maxp5gha0,
1101 cis[i + 11]);
1102 varbuf_append(&b, vstr_maxp5gla0,
1103 cis[i + 12]);
1104 varbuf_append(&b, vstr_pa, 5, 0, 0,
1105 (cis[i + 14] << 8) +
1106 cis[i + 13]);
1107 varbuf_append(&b, vstr_pa, 5, 1, 0,
1108 (cis[i + 16] << 8) +
1109 cis[i + 15]);
1110 varbuf_append(&b, vstr_pa, 5, 2, 0,
1111 (cis[i + 18] << 8) +
1112 cis[i + 17]);
1113 varbuf_append(&b, vstr_pahl, 5, 'l', 0,
1114 0,
1115 (cis[i + 20] << 8) +
1116 cis[i + 19]);
1117 varbuf_append(&b, vstr_pahl, 5, 'l', 1,
1118 0,
1119 (cis[i + 22] << 8) +
1120 cis[i + 21]);
1121 varbuf_append(&b, vstr_pahl, 5, 'l', 2,
1122 0,
1123 (cis[i + 24] << 8) +
1124 cis[i + 23]);
1125 varbuf_append(&b, vstr_pahl, 5, 'h', 0,
1126 0,
1127 (cis[i + 26] << 8) +
1128 cis[i + 25]);
1129 varbuf_append(&b, vstr_pahl, 5, 'h', 1,
1130 0,
1131 (cis[i + 28] << 8) +
1132 cis[i + 27]);
1133 varbuf_append(&b, vstr_pahl, 5, 'h', 2,
1134 0,
1135 (cis[i + 30] << 8) +
1136 cis[i + 29]);
1137 break;
1138
1139 case HNBU_PAPARMS_C1:
1140 varbuf_append(&b, vstr_maxp2ga1,
1141 cis[i + 1]);
1142 varbuf_append(&b, vstr_itt2ga1,
1143 cis[i + 2]);
1144 varbuf_append(&b, vstr_pa, 2, 0, 1,
1145 (cis[i + 4] << 8) +
1146 cis[i + 3]);
1147 varbuf_append(&b, vstr_pa, 2, 1, 1,
1148 (cis[i + 6] << 8) +
1149 cis[i + 5]);
1150 varbuf_append(&b, vstr_pa, 2, 2, 1,
1151 (cis[i + 8] << 8) +
1152 cis[i + 7]);
1153 if (tlen < 31)
1154 break;
1155
1156 varbuf_append(&b, vstr_maxp5ga1,
1157 cis[i + 9]);
1158 varbuf_append(&b, vstr_itt5ga1,
1159 cis[i + 10]);
1160 varbuf_append(&b, vstr_maxp5gha1,
1161 cis[i + 11]);
1162 varbuf_append(&b, vstr_maxp5gla1,
1163 cis[i + 12]);
1164 varbuf_append(&b, vstr_pa, 5, 0, 1,
1165 (cis[i + 14] << 8) +
1166 cis[i + 13]);
1167 varbuf_append(&b, vstr_pa, 5, 1, 1,
1168 (cis[i + 16] << 8) +
1169 cis[i + 15]);
1170 varbuf_append(&b, vstr_pa, 5, 2, 1,
1171 (cis[i + 18] << 8) +
1172 cis[i + 17]);
1173 varbuf_append(&b, vstr_pahl, 5, 'l', 0,
1174 1,
1175 (cis[i + 20] << 8) +
1176 cis[i + 19]);
1177 varbuf_append(&b, vstr_pahl, 5, 'l', 1,
1178 1,
1179 (cis[i + 22] << 8) +
1180 cis[i + 21]);
1181 varbuf_append(&b, vstr_pahl, 5, 'l', 2,
1182 1,
1183 (cis[i + 24] << 8) +
1184 cis[i + 23]);
1185 varbuf_append(&b, vstr_pahl, 5, 'h', 0,
1186 1,
1187 (cis[i + 26] << 8) +
1188 cis[i + 25]);
1189 varbuf_append(&b, vstr_pahl, 5, 'h', 1,
1190 1,
1191 (cis[i + 28] << 8) +
1192 cis[i + 27]);
1193 varbuf_append(&b, vstr_pahl, 5, 'h', 2,
1194 1,
1195 (cis[i + 30] << 8) +
1196 cis[i + 29]);
1197 break;
1198
1199 case HNBU_PO_CCKOFDM:
1200 varbuf_append(&b, vstr_cck2gpo,
1201 (cis[i + 2] << 8) +
1202 cis[i + 1]);
1203 varbuf_append(&b, vstr_ofdm2gpo,
1204 (cis[i + 6] << 24) +
1205 (cis[i + 5] << 16) +
1206 (cis[i + 4] << 8) +
1207 cis[i + 3]);
1208 if (tlen < 19)
1209 break;
1210
1211 varbuf_append(&b, vstr_ofdm5gpo,
1212 (cis[i + 10] << 24) +
1213 (cis[i + 9] << 16) +
1214 (cis[i + 8] << 8) +
1215 cis[i + 7]);
1216 varbuf_append(&b, vstr_ofdm5glpo,
1217 (cis[i + 14] << 24) +
1218 (cis[i + 13] << 16) +
1219 (cis[i + 12] << 8) +
1220 cis[i + 11]);
1221 varbuf_append(&b, vstr_ofdm5ghpo,
1222 (cis[i + 18] << 24) +
1223 (cis[i + 17] << 16) +
1224 (cis[i + 16] << 8) +
1225 cis[i + 15]);
1226 break;
1227
1228 case HNBU_PO_MCS2G:
1229 for (j = 0; j <= (tlen / 2); j++) {
1230 varbuf_append(&b, vstr_mcspo, 2,
1231 j,
1232 (cis
1233 [i + 2 +
1234 2 * j] << 8) +
1235 cis[i + 1 +
1236 2 * j]);
1237 }
1238 break;
1239
1240 case HNBU_PO_MCS5GM:
1241 for (j = 0; j <= (tlen / 2); j++) {
1242 varbuf_append(&b, vstr_mcspo, 5,
1243 j,
1244 (cis
1245 [i + 2 +
1246 2 * j] << 8) +
1247 cis[i + 1 +
1248 2 * j]);
1249 }
1250 break;
1251
1252 case HNBU_PO_MCS5GLH:
1253 for (j = 0; j <= (tlen / 4); j++) {
1254 varbuf_append(&b, vstr_mcspohl,
1255 5, 'l', j,
1256 (cis
1257 [i + 2 +
1258 2 * j] << 8) +
1259 cis[i + 1 +
1260 2 * j]);
1261 }
1262
1263 for (j = 0; j <= (tlen / 4); j++) {
1264 varbuf_append(&b, vstr_mcspohl,
1265 5, 'h', j,
1266 (cis
1267 [i +
1268 ((tlen / 2) +
1269 2) +
1270 2 * j] << 8) +
1271 cis[i +
1272 ((tlen / 2) +
1273 1) + 2 * j]);
1274 }
1275
1276 break;
1277
1278 case HNBU_PO_CDD:
1279 varbuf_append(&b, vstr_cddpo,
1280 (cis[i + 2] << 8) +
1281 cis[i + 1]);
1282 break;
1283
1284 case HNBU_PO_STBC:
1285 varbuf_append(&b, vstr_stbcpo,
1286 (cis[i + 2] << 8) +
1287 cis[i + 1]);
1288 break;
1289
1290 case HNBU_PO_40M:
1291 varbuf_append(&b, vstr_bw40po,
1292 (cis[i + 2] << 8) +
1293 cis[i + 1]);
1294 break;
1295
1296 case HNBU_PO_40MDUP:
1297 varbuf_append(&b, vstr_bwduppo,
1298 (cis[i + 2] << 8) +
1299 cis[i + 1]);
1300 break;
1301
1302 case HNBU_OFDMPO5G:
1303 varbuf_append(&b, vstr_ofdm5gpo,
1304 (cis[i + 4] << 24) +
1305 (cis[i + 3] << 16) +
1306 (cis[i + 2] << 8) +
1307 cis[i + 1]);
1308 varbuf_append(&b, vstr_ofdm5glpo,
1309 (cis[i + 8] << 24) +
1310 (cis[i + 7] << 16) +
1311 (cis[i + 6] << 8) +
1312 cis[i + 5]);
1313 varbuf_append(&b, vstr_ofdm5ghpo,
1314 (cis[i + 12] << 24) +
1315 (cis[i + 11] << 16) +
1316 (cis[i + 10] << 8) +
1317 cis[i + 9]);
1318 break;
1319
1320 case HNBU_CUSTOM1:
1321 varbuf_append(&b, vstr_custom, 1,
1322 ((cis[i + 4] << 24) +
1323 (cis[i + 3] << 16) +
1324 (cis[i + 2] << 8) +
1325 cis[i + 1]));
1326 break;
1327
1328#if defined(BCMSDIO)
1329 case HNBU_SROM3SWRGN:
1330 if (tlen >= 73) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001331 u16 srom[35];
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07001332 u8 srev = cis[i + 1 + 70];
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001333 ASSERT(srev == 3);
1334 /* make tuple value 16-bit aligned and parse it */
1335 bcopy(&cis[i + 1], srom,
1336 sizeof(srom));
1337 _initvars_srom_pci(srev, srom,
1338 SROM3_SWRGN_OFF,
1339 &b);
1340 /* 2.4G antenna gain is included in SROM */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001341 ag_init = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001342 /* Ethernet MAC address is included in SROM */
1343 eabuf[0] = 0;
1344 boardnum = -1;
1345 }
1346 /* create extra variables */
1347 if (tlen >= 75)
1348 varbuf_append(&b, vstr_vendid,
1349 (cis[i + 1 + 73]
1350 << 8) + cis[i +
1351 1 +
1352 72]);
1353 if (tlen >= 77)
1354 varbuf_append(&b, vstr_devid,
1355 (cis[i + 1 + 75]
1356 << 8) + cis[i +
1357 1 +
1358 74]);
1359 if (tlen >= 79)
1360 varbuf_append(&b, vstr_xtalfreq,
1361 (cis[i + 1 + 77]
1362 << 8) + cis[i +
1363 1 +
1364 76]);
1365 break;
1366#endif /* defined(BCMSDIO) */
1367
1368 case HNBU_CCKFILTTYPE:
1369 varbuf_append(&b, vstr_cckdigfilttype,
1370 (cis[i + 1]));
1371 break;
1372 }
1373
1374 break;
1375 }
1376 i += tlen;
1377 } while (tup != CISTPL_END);
1378 }
1379
1380 if (boardnum != -1) {
1381 varbuf_append(&b, vstr_boardnum, boardnum);
1382 }
1383
1384 if (eabuf[0]) {
1385 varbuf_append(&b, vstr_macaddr, eabuf);
1386 }
1387
1388 /* if there is no antenna gain field, set default */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001389 if (getvar(NULL, "ag0") == NULL && ag_init == false) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001390 varbuf_append(&b, vstr_ag, 0, 0xff);
1391 }
1392
1393 /* final nullbyte terminator */
1394 ASSERT(b.size >= 1);
1395 *b.buf++ = '\0';
1396
1397 ASSERT(b.buf - base <= MAXSZ_NVRAM_VARS);
1398 err = initvars_table(osh, base, b.buf, vars, count);
1399
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02001400 kfree(base);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001401 return err;
1402}
1403
1404/* In chips with chipcommon rev 32 and later, the srom is in chipcommon,
1405 * not in the bus cores.
1406 */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001407static u16
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001408srom_cc_cmd(si_t *sih, osl_t *osh, void *ccregs, u32 cmd, uint wordoff,
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001409 u16 data)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001410{
1411 chipcregs_t *cc = (chipcregs_t *) ccregs;
1412 uint wait_cnt = 1000;
1413
1414 if ((cmd == SRC_OP_READ) || (cmd == SRC_OP_WRITE)) {
1415 W_REG(osh, &cc->sromaddress, wordoff * 2);
1416 if (cmd == SRC_OP_WRITE)
1417 W_REG(osh, &cc->sromdata, data);
1418 }
1419
1420 W_REG(osh, &cc->sromcontrol, SRC_START | cmd);
1421
1422 while (wait_cnt--) {
1423 if ((R_REG(osh, &cc->sromcontrol) & SRC_BUSY) == 0)
1424 break;
1425 }
1426
1427 if (!wait_cnt) {
1428 BS_ERROR(("%s: Command 0x%x timed out\n", __func__, cmd));
1429 return 0xffff;
1430 }
1431 if (cmd == SRC_OP_READ)
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001432 return (u16) R_REG(osh, &cc->sromdata);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001433 else
1434 return 0xffff;
1435}
1436
1437/*
1438 * Read in and validate sprom.
1439 * Return 0 on success, nonzero on error.
1440 */
1441static int
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001442sprom_read_pci(osl_t *osh, si_t *sih, u16 *sprom, uint wordoff,
1443 u16 *buf, uint nwords, bool check_crc)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001444{
1445 int err = 0;
1446 uint i;
1447 void *ccregs = NULL;
1448
1449 /* read the sprom */
1450 for (i = 0; i < nwords; i++) {
1451
1452 if (sih->ccrev > 31 && ISSIM_ENAB(sih)) {
1453 /* use indirect since direct is too slow on QT */
1454 if ((sih->cccaps & CC_CAP_SROM) == 0)
1455 return 1;
1456
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07001457 ccregs = (void *)((u8 *) sprom - CC_SROM_OTP);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001458 buf[i] =
1459 srom_cc_cmd(sih, osh, ccregs, SRC_OP_READ,
1460 wordoff + i, 0);
1461
1462 } else {
1463 if (ISSIM_ENAB(sih))
1464 buf[i] = R_REG(osh, &sprom[wordoff + i]);
1465
1466 buf[i] = R_REG(osh, &sprom[wordoff + i]);
1467 }
1468
1469 }
1470
1471 /* bypass crc checking for simulation to allow srom hack */
1472 if (ISSIM_ENAB(sih))
1473 return err;
1474
1475 if (check_crc) {
1476
1477 if (buf[0] == 0xffff) {
1478 /* The hardware thinks that an srom that starts with 0xffff
1479 * is blank, regardless of the rest of the content, so declare
1480 * it bad.
1481 */
1482 BS_ERROR(("%s: buf[0] = 0x%x, returning bad-crc\n",
1483 __func__, buf[0]));
1484 return 1;
1485 }
1486
1487 /* fixup the endianness so crc8 will pass */
1488 htol16_buf(buf, nwords * 2);
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07001489 if (hndcrc8((u8 *) buf, nwords * 2, CRC8_INIT_VALUE) !=
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001490 CRC8_GOOD_VALUE) {
1491 /* DBG only pci always read srom4 first, then srom8/9 */
1492 /* BS_ERROR(("%s: bad crc\n", __func__)); */
1493 err = 1;
1494 }
1495 /* now correct the endianness of the byte array */
1496 ltoh16_buf(buf, nwords * 2);
1497 }
1498 return err;
1499}
1500
1501#if defined(BCMNVRAMR)
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001502static int otp_read_pci(osl_t *osh, si_t *sih, u16 *buf, uint bufsz)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001503{
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07001504 u8 *otp;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001505 uint sz = OTP_SZ_MAX / 2; /* size in words */
1506 int err = 0;
1507
1508 ASSERT(bufsz <= OTP_SZ_MAX);
1509
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001510 otp = kzalloc(OTP_SZ_MAX, GFP_ATOMIC);
Jason Cooperca8c1e52010-09-14 09:45:33 -04001511 if (otp == NULL) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001512 return BCME_ERROR;
1513 }
1514
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001515 err = otp_read_region(sih, OTP_HW_RGN, (u16 *) otp, &sz);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001516
1517 bcopy(otp, buf, bufsz);
1518
1519 if (otp)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02001520 kfree(otp);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001521
1522 /* Check CRC */
1523 if (buf[0] == 0xffff) {
1524 /* The hardware thinks that an srom that starts with 0xffff
1525 * is blank, regardless of the rest of the content, so declare
1526 * it bad.
1527 */
1528 BS_ERROR(("%s: buf[0] = 0x%x, returning bad-crc\n", __func__,
1529 buf[0]));
1530 return 1;
1531 }
1532
1533 /* fixup the endianness so crc8 will pass */
1534 htol16_buf(buf, bufsz);
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07001535 if (hndcrc8((u8 *) buf, SROM4_WORDS * 2, CRC8_INIT_VALUE) !=
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001536 CRC8_GOOD_VALUE) {
1537 BS_ERROR(("%s: bad crc\n", __func__));
1538 err = 1;
1539 }
1540 /* now correct the endianness of the byte array */
1541 ltoh16_buf(buf, bufsz);
1542
1543 return err;
1544}
1545#endif /* defined(BCMNVRAMR) */
1546/*
1547* Create variable table from memory.
1548* Return 0 on success, nonzero on error.
1549*/
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -07001550static int initvars_table(osl_t *osh, char *start, char *end, char **vars,
1551 uint *count)
1552{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001553 int c = (int)(end - start);
1554
1555 /* do it only when there is more than just the null string */
1556 if (c > 1) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001557 char *vp = kmalloc(c, GFP_ATOMIC);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001558 ASSERT(vp != NULL);
1559 if (!vp)
1560 return BCME_NOMEM;
1561 bcopy(start, vp, c);
1562 *vars = vp;
1563 *count = c;
1564 } else {
1565 *vars = NULL;
1566 *count = 0;
1567 }
1568
1569 return 0;
1570}
1571
1572/*
1573 * Find variables with <devpath> from flash. 'base' points to the beginning
1574 * of the table upon enter and to the end of the table upon exit when success.
1575 * Return 0 on success, nonzero on error.
1576 */
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -07001577static int initvars_flash(si_t *sih, osl_t *osh, char **base, uint len)
Jason Coopera2627bc2010-09-14 09:45:31 -04001578{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001579 char *vp = *base;
1580 char *flash;
1581 int err;
1582 char *s;
1583 uint l, dl, copy_len;
1584 char devpath[SI_DEVPATH_BUFSZ];
1585
1586 /* allocate memory and read in flash */
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001587 flash = kmalloc(NVRAM_SPACE, GFP_ATOMIC);
Jason Cooperca8c1e52010-09-14 09:45:33 -04001588 if (!flash)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001589 return BCME_NOMEM;
Jason Cooperca8c1e52010-09-14 09:45:33 -04001590 err = nvram_getall(flash, NVRAM_SPACE);
1591 if (err)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001592 goto exit;
1593
1594 si_devpath(sih, devpath, sizeof(devpath));
1595
1596 /* grab vars with the <devpath> prefix in name */
1597 dl = strlen(devpath);
1598 for (s = flash; s && *s; s += l + 1) {
1599 l = strlen(s);
1600
1601 /* skip non-matching variable */
1602 if (strncmp(s, devpath, dl))
1603 continue;
1604
1605 /* is there enough room to copy? */
1606 copy_len = l - dl + 1;
1607 if (len < copy_len) {
1608 err = BCME_BUFTOOSHORT;
1609 goto exit;
1610 }
1611
1612 /* no prefix, just the name=value */
1613 strncpy(vp, &s[dl], copy_len);
1614 vp += copy_len;
1615 len -= copy_len;
1616 }
1617
1618 /* add null string as terminator */
1619 if (len < 1) {
1620 err = BCME_BUFTOOSHORT;
1621 goto exit;
1622 }
1623 *vp++ = '\0';
1624
1625 *base = vp;
1626
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02001627 exit: kfree(flash);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001628 return err;
1629}
1630
1631/*
1632 * Initialize nonvolatile variable table from flash.
1633 * Return 0 on success, nonzero on error.
1634 */
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -07001635static int initvars_flash_si(si_t *sih, char **vars, uint *count)
Jason Coopera2627bc2010-09-14 09:45:31 -04001636{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001637 osl_t *osh = si_osh(sih);
1638 char *vp, *base;
1639 int err;
1640
1641 ASSERT(vars != NULL);
1642 ASSERT(count != NULL);
1643
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001644 base = vp = kmalloc(MAXSZ_NVRAM_VARS, GFP_ATOMIC);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001645 ASSERT(vp != NULL);
1646 if (!vp)
1647 return BCME_NOMEM;
1648
Jason Cooperca8c1e52010-09-14 09:45:33 -04001649 err = initvars_flash(sih, osh, &vp, MAXSZ_NVRAM_VARS);
1650 if (err == 0)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001651 err = initvars_table(osh, base, vp, vars, count);
1652
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02001653 kfree(base);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001654
1655 return err;
1656}
1657
1658/* Parse SROM and create name=value pairs. 'srom' points to
1659 * the SROM word array. 'off' specifies the offset of the
1660 * first word 'srom' points to, which should be either 0 or
1661 * SROM3_SWRG_OFF (full SROM or software region).
1662 */
1663
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001664static uint mask_shift(u16 mask)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001665{
1666 uint i;
1667 for (i = 0; i < (sizeof(mask) << 3); i++) {
1668 if (mask & (1 << i))
1669 return i;
1670 }
1671 ASSERT(mask);
1672 return 0;
1673}
1674
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001675static uint mask_width(u16 mask)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001676{
1677 int i;
1678 for (i = (sizeof(mask) << 3) - 1; i >= 0; i--) {
1679 if (mask & (1 << i))
1680 return (uint) (i - mask_shift(mask) + 1);
1681 }
1682 ASSERT(mask);
1683 return 0;
1684}
1685
1686#if defined(BCMDBG)
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001687static bool mask_valid(u16 mask)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001688{
1689 uint shift = mask_shift(mask);
1690 uint width = mask_width(mask);
1691 return mask == ((~0 << shift) & ~(~0 << (shift + width)));
1692}
1693#endif /* BCMDBG */
1694
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -07001695static void _initvars_srom_pci(u8 sromrev, u16 *srom, uint off, varbuf_t *b)
1696{
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001697 u16 w;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001698 u32 val;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001699 const sromvar_t *srv;
1700 uint width;
1701 uint flags;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001702 u32 sr = (1 << sromrev);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001703
1704 varbuf_append(b, "sromrev=%d", sromrev);
1705
1706 for (srv = pci_sromvars; srv->name != NULL; srv++) {
1707 const char *name;
1708
1709 if ((srv->revmask & sr) == 0)
1710 continue;
1711
1712 if (srv->off < off)
1713 continue;
1714
1715 flags = srv->flags;
1716 name = srv->name;
1717
1718 /* This entry is for mfgc only. Don't generate param for it, */
1719 if (flags & SRFL_NOVAR)
1720 continue;
1721
1722 if (flags & SRFL_ETHADDR) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001723 struct ether_addr ea;
1724
1725 ea.octet[0] = (srom[srv->off - off] >> 8) & 0xff;
1726 ea.octet[1] = srom[srv->off - off] & 0xff;
1727 ea.octet[2] = (srom[srv->off + 1 - off] >> 8) & 0xff;
1728 ea.octet[3] = srom[srv->off + 1 - off] & 0xff;
1729 ea.octet[4] = (srom[srv->off + 2 - off] >> 8) & 0xff;
1730 ea.octet[5] = srom[srv->off + 2 - off] & 0xff;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001731
Andy Shevchenkoba07d0c2010-10-11 16:58:33 +03001732 varbuf_append(b, "%s=%pM", name, ea.octet);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001733 } else {
1734 ASSERT(mask_valid(srv->mask));
1735 ASSERT(mask_width(srv->mask));
1736
1737 w = srom[srv->off - off];
1738 val = (w & srv->mask) >> mask_shift(srv->mask);
1739 width = mask_width(srv->mask);
1740
1741 while (srv->flags & SRFL_MORE) {
1742 srv++;
1743 ASSERT(srv->name != NULL);
1744
1745 if (srv->off == 0 || srv->off < off)
1746 continue;
1747
1748 ASSERT(mask_valid(srv->mask));
1749 ASSERT(mask_width(srv->mask));
1750
1751 w = srom[srv->off - off];
1752 val +=
1753 ((w & srv->mask) >> mask_shift(srv->
1754 mask)) <<
1755 width;
1756 width += mask_width(srv->mask);
1757 }
1758
1759 if ((flags & SRFL_NOFFS)
1760 && ((int)val == (1 << width) - 1))
1761 continue;
1762
1763 if (flags & SRFL_CCODE) {
1764 if (val == 0)
1765 varbuf_append(b, "ccode=");
1766 else
1767 varbuf_append(b, "ccode=%c%c",
1768 (val >> 8), (val & 0xff));
1769 }
1770 /* LED Powersave duty cycle has to be scaled:
1771 *(oncount >> 24) (offcount >> 8)
1772 */
1773 else if (flags & SRFL_LEDDC) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001774 u32 w32 = (((val >> 8) & 0xff) << 24) | /* oncount */
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001775 (((val & 0xff)) << 8); /* offcount */
1776 varbuf_append(b, "leddc=%d", w32);
1777 } else if (flags & SRFL_PRHEX)
1778 varbuf_append(b, "%s=0x%x", name, val);
1779 else if ((flags & SRFL_PRSIGN)
1780 && (val & (1 << (width - 1))))
1781 varbuf_append(b, "%s=%d", name,
1782 (int)(val | (~0 << width)));
1783 else
1784 varbuf_append(b, "%s=%u", name, val);
1785 }
1786 }
1787
1788 if (sromrev >= 4) {
1789 /* Do per-path variables */
1790 uint p, pb, psz;
1791
1792 if (sromrev >= 8) {
1793 pb = SROM8_PATH0;
1794 psz = SROM8_PATH1 - SROM8_PATH0;
1795 } else {
1796 pb = SROM4_PATH0;
1797 psz = SROM4_PATH1 - SROM4_PATH0;
1798 }
1799
1800 for (p = 0; p < MAX_PATH_SROM; p++) {
1801 for (srv = perpath_pci_sromvars; srv->name != NULL;
1802 srv++) {
1803 if ((srv->revmask & sr) == 0)
1804 continue;
1805
1806 if (pb + srv->off < off)
1807 continue;
1808
1809 /* This entry is for mfgc only. Don't generate param for it, */
1810 if (srv->flags & SRFL_NOVAR)
1811 continue;
1812
1813 w = srom[pb + srv->off - off];
1814
1815 ASSERT(mask_valid(srv->mask));
1816 val = (w & srv->mask) >> mask_shift(srv->mask);
1817 width = mask_width(srv->mask);
1818
1819 /* Cheating: no per-path var is more than 1 word */
1820
1821 if ((srv->flags & SRFL_NOFFS)
1822 && ((int)val == (1 << width) - 1))
1823 continue;
1824
1825 if (srv->flags & SRFL_PRHEX)
1826 varbuf_append(b, "%s%d=0x%x", srv->name,
1827 p, val);
1828 else
1829 varbuf_append(b, "%s%d=%d", srv->name,
1830 p, val);
1831 }
1832 pb += psz;
1833 }
1834 }
1835}
1836
1837/*
1838 * Initialize nonvolatile variable table from sprom.
1839 * Return 0 on success, nonzero on error.
1840 */
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -07001841static int initvars_srom_pci(si_t *sih, void *curmap, char **vars, uint *count)
1842{
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001843 u16 *srom, *sromwindow;
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07001844 u8 sromrev = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001845 u32 sr;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001846 varbuf_t b;
1847 char *vp, *base = NULL;
1848 osl_t *osh = si_osh(sih);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001849 bool flash = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001850 int err = 0;
1851
1852 /*
1853 * Apply CRC over SROM content regardless SROM is present or not,
1854 * and use variable <devpath>sromrev's existance in flash to decide
1855 * if we should return an error when CRC fails or read SROM variables
1856 * from flash.
1857 */
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001858 srom = kmalloc(SROM_MAX, GFP_ATOMIC);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001859 ASSERT(srom != NULL);
1860 if (!srom)
1861 return -2;
1862
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001863 sromwindow = (u16 *) SROM_OFFSET(sih);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001864 if (si_is_sprom_available(sih)) {
1865 err =
1866 sprom_read_pci(osh, sih, sromwindow, 0, srom, SROM_WORDS,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001867 true);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001868
1869 if ((srom[SROM4_SIGN] == SROM4_SIGNATURE) ||
1870 (((sih->buscoretype == PCIE_CORE_ID)
1871 && (sih->buscorerev >= 6))
1872 || ((sih->buscoretype == PCI_CORE_ID)
1873 && (sih->buscorerev >= 0xe)))) {
1874 /* sromrev >= 4, read more */
1875 err =
1876 sprom_read_pci(osh, sih, sromwindow, 0, srom,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001877 SROM4_WORDS, true);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001878 sromrev = srom[SROM4_CRCREV] & 0xff;
1879 if (err)
1880 BS_ERROR(("%s: srom %d, bad crc\n", __func__,
1881 sromrev));
1882
1883 } else if (err == 0) {
1884 /* srom is good and is rev < 4 */
1885 /* top word of sprom contains version and crc8 */
1886 sromrev = srom[SROM_CRCREV] & 0xff;
1887 /* bcm4401 sroms misprogrammed */
1888 if (sromrev == 0x10)
1889 sromrev = 1;
1890 }
1891 }
1892#if defined(BCMNVRAMR)
1893 /* Use OTP if SPROM not available */
1894 else if ((err = otp_read_pci(osh, sih, srom, SROM_MAX)) == 0) {
1895 /* OTP only contain SROM rev8/rev9 for now */
1896 sromrev = srom[SROM4_CRCREV] & 0xff;
1897 }
1898#endif
1899 else {
1900 err = 1;
1901 BS_ERROR(("Neither SPROM nor OTP has valid image\n"));
1902 }
1903
1904 /* We want internal/wltest driver to come up with default sromvars so we can
1905 * program a blank SPROM/OTP.
1906 */
1907 if (err) {
1908 char *value;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001909 u32 val;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001910 val = 0;
1911
Jason Cooperca8c1e52010-09-14 09:45:33 -04001912 value = si_getdevpathvar(sih, "sromrev");
1913 if (value) {
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07001914 sromrev = (u8) simple_strtoul(value, NULL, 0);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001915 flash = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001916 goto varscont;
1917 }
1918
1919 BS_ERROR(("%s, SROM CRC Error\n", __func__));
1920
Jason Cooperca8c1e52010-09-14 09:45:33 -04001921 value = si_getnvramflvar(sih, "sromrev");
1922 if (value) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001923 err = 0;
1924 goto errout;
1925 }
1926
1927 {
1928 err = -1;
1929 goto errout;
1930 }
1931 }
1932
1933 varscont:
1934 /* Bitmask for the sromrev */
1935 sr = 1 << sromrev;
1936
1937 /* srom version check: Current valid versions: 1, 2, 3, 4, 5, 8, 9 */
1938 if ((sr & 0x33e) == 0) {
1939 err = -2;
1940 goto errout;
1941 }
1942
1943 ASSERT(vars != NULL);
1944 ASSERT(count != NULL);
1945
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001946 base = vp = kmalloc(MAXSZ_NVRAM_VARS, GFP_ATOMIC);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001947 ASSERT(vp != NULL);
1948 if (!vp) {
1949 err = -2;
1950 goto errout;
1951 }
1952
1953 /* read variables from flash */
1954 if (flash) {
Jason Cooperca8c1e52010-09-14 09:45:33 -04001955 err = initvars_flash(sih, osh, &vp, MAXSZ_NVRAM_VARS);
1956 if (err)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001957 goto errout;
1958 goto varsdone;
1959 }
1960
1961 varbuf_init(&b, base, MAXSZ_NVRAM_VARS);
1962
1963 /* parse SROM into name=value pairs. */
1964 _initvars_srom_pci(sromrev, srom, 0, &b);
1965
1966 /* final nullbyte terminator */
1967 ASSERT(b.size >= 1);
1968 vp = b.buf;
1969 *vp++ = '\0';
1970
1971 ASSERT((vp - base) <= MAXSZ_NVRAM_VARS);
1972
1973 varsdone:
1974 err = initvars_table(osh, base, vp, vars, count);
1975
1976 errout:
1977 if (base)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02001978 kfree(base);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001979
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02001980 kfree(srom);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001981 return err;
1982}
1983
1984#ifdef BCMSDIO
1985/*
1986 * Read the SDIO cis and call parsecis to initialize the vars.
1987 * Return 0 on success, nonzero on error.
1988 */
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -07001989static int initvars_cis_sdio(osl_t *osh, char **vars, uint *count)
Jason Coopera2627bc2010-09-14 09:45:31 -04001990{
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07001991 u8 *cis[SBSDIO_NUM_FUNCTION + 1];
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001992 uint fn, numfn;
1993 int rc = 0;
1994
1995 numfn = bcmsdh_query_iofnum(NULL);
1996 ASSERT(numfn <= SDIOD_MAX_IOFUNCS);
1997
1998 for (fn = 0; fn <= numfn; fn++) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001999 cis[fn] = kzalloc(SBSDIO_CIS_SIZE_LIMIT, GFP_ATOMIC);
Jason Cooperca8c1e52010-09-14 09:45:33 -04002000 if (cis[fn] == NULL) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -07002001 rc = -1;
2002 break;
2003 }
2004
Henry Ptasinskia9533e72010-09-08 21:04:42 -07002005 if (bcmsdh_cis_read(NULL, fn, cis[fn], SBSDIO_CIS_SIZE_LIMIT) !=
2006 0) {
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02002007 kfree(cis[fn]);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07002008 rc = -2;
2009 break;
2010 }
2011 }
2012
2013 if (!rc)
2014 rc = srom_parsecis(osh, cis, fn, vars, count);
2015
2016 while (fn-- > 0)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02002017 kfree(cis[fn]);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07002018
Jason Cooper90ea2292010-09-14 09:45:32 -04002019 return rc;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07002020}
2021
2022/* set SDIO sprom command register */
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -07002023static int sprom_cmd_sdio(osl_t *osh, u8 cmd)
Jason Coopera2627bc2010-09-14 09:45:31 -04002024{
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07002025 u8 status = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07002026 uint wait_cnt = 1000;
2027
2028 /* write sprom command register */
2029 bcmsdh_cfg_write(NULL, SDIO_FUNC_1, SBSDIO_SPROM_CS, cmd, NULL);
2030
2031 /* wait status */
2032 while (wait_cnt--) {
2033 status =
2034 bcmsdh_cfg_read(NULL, SDIO_FUNC_1, SBSDIO_SPROM_CS, NULL);
2035 if (status & SBSDIO_SPROM_DONE)
2036 return 0;
2037 }
2038
2039 return 1;
2040}
2041
2042/* read a word from the SDIO srom */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07002043static int sprom_read_sdio(osl_t *osh, u16 addr, u16 *data)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07002044{
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07002045 u8 addr_l, addr_h, data_l, data_h;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07002046
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -07002047 addr_l = (u8) ((addr * 2) & 0xff);
2048 addr_h = (u8) (((addr * 2) >> 8) & 0xff);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07002049
2050 /* set address */
2051 bcmsdh_cfg_write(NULL, SDIO_FUNC_1, SBSDIO_SPROM_ADDR_HIGH, addr_h,
2052 NULL);
2053 bcmsdh_cfg_write(NULL, SDIO_FUNC_1, SBSDIO_SPROM_ADDR_LOW, addr_l,
2054 NULL);
2055
2056 /* do read */
2057 if (sprom_cmd_sdio(osh, SBSDIO_SPROM_READ))
2058 return 1;
2059
2060 /* read data */
2061 data_h =
2062 bcmsdh_cfg_read(NULL, SDIO_FUNC_1, SBSDIO_SPROM_DATA_HIGH, NULL);
2063 data_l =
2064 bcmsdh_cfg_read(NULL, SDIO_FUNC_1, SBSDIO_SPROM_DATA_LOW, NULL);
2065
2066 *data = (data_h << 8) | data_l;
2067 return 0;
2068}
2069#endif /* BCMSDIO */
2070
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -07002071static int initvars_srom_si(si_t *sih, osl_t *osh, void *curmap, char **vars,
2072 uint *varsz)
2073{
Henry Ptasinskia9533e72010-09-08 21:04:42 -07002074 /* Search flash nvram section for srom variables */
2075 return initvars_flash_si(sih, vars, varsz);
2076}