1/*
2 *  Routines to access SPROM and to parse SROM/CIS variables.
3 *
4 * Copyright 2007, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
11 * $Id: bcmsrom.c,v 1.1.1.1 2008/10/15 03:31:34 james26_jang Exp $
12 */
13
14#include <typedefs.h>
15#include <bcmdefs.h>
16#include <osl.h>
17#include <stdarg.h>
18#include <bcmutils.h>
19#include <sbchipc.h>
20#include <bcmdevs.h>
21#include <bcmendian.h>
22#include <sbpcmcia.h>
23#include <pcicfg.h>
24#include <sbconfig.h>
25#include <sbutils.h>
26#include <bcmsrom.h>
27
28#include <bcmnvram.h>
29#include <bcmotp.h>
30
31#if defined(BCMUSBDEV)
32#include <sbsdio.h>
33#include <sbhnddma.h>
34#include <sbsdpcmdev.h>
35#endif
36
37#ifdef WLTEST
38#include <sbsprom.h>
39#endif /* WLTEST */
40#include <proto/ethernet.h>	/* for sprom content groking */
41
42/* debug/trace */
43#if defined(WLTEST)
44#define	BS_ERROR(args)	printf args
45#else
46#define	BS_ERROR(args)
47#endif
48
49#define WRITE_ENABLE_DELAY	500	/* 500 ms after write enable/disable toggle */
50#define WRITE_WORD_DELAY	20	/* 20 ms between each word write */
51
52typedef struct varbuf {
53	char *buf;		/* pointer to current position */
54	unsigned int size;	/* current (residual) size in bytes */
55} varbuf_t;
56
57static int initvars_srom_sb(sb_t *sbh, osl_t *osh, void *curmap, char **vars, uint *count);
58static void _initvars_srom_pci(uint8 sromrev, uint16 *srom, uint off, varbuf_t *b);
59static int initvars_srom_pci(sb_t *sbh, void *curmap, char **vars, uint *count);
60static int initvars_cis_pcmcia(sb_t *sbh, osl_t *osh, char **vars, uint *count);
61#if !defined(BCMUSBDEV)
62static int initvars_flash_sb(sb_t *sbh, char **vars, uint *count);
63#endif
64static int sprom_cmd_pcmcia(osl_t *osh, uint8 cmd);
65static int sprom_read_pcmcia(osl_t *osh, uint16 addr, uint16 *data);
66static int sprom_write_pcmcia(osl_t *osh, uint16 addr, uint16 data);
67static int sprom_read_pci(osl_t *osh, uint16 *sprom, uint wordoff, uint16 *buf, uint nwords,
68                          bool check_crc);
69
70static int initvars_table(osl_t *osh, char *start, char *end, char **vars, uint *count);
71static int initvars_flash(sb_t *sbh, osl_t *osh, char **vp, uint len);
72
73#ifdef BCMUSBDEV
74static int get_sb_pcmcia_srom(sb_t *sbh, osl_t *osh, uint8 *pcmregs,
75                              uint boff, uint16 *srom, uint bsz);
76static int set_sb_pcmcia_srom(sb_t *sbh, osl_t *osh, uint8 *pcmregs,
77                              uint boff, uint16 *srom, uint bsz);
78static uint srom_size(sb_t *sbh, osl_t *osh);
79#endif /* def BCMUSBDEV */
80
81/* Initialization of varbuf structure */
82static void
83varbuf_init(varbuf_t *b, char *buf, uint size)
84{
85	b->size = size;
86	b->buf = buf;
87}
88
89/* append a null terminated var=value string */
90static int
91varbuf_append(varbuf_t *b, const char *fmt, ...)
92{
93	va_list ap;
94	int r;
95
96	if (b->size < 2)
97	  return 0;
98
99	va_start(ap, fmt);
100	r = vsnprintf(b->buf, b->size, fmt, ap);
101	va_end(ap);
102
103	/* C99 snprintf behavior returns r >= size on overflow,
104	 * others return -1 on overflow.
105	 * All return -1 on format error.
106	 * We need to leave room for 2 null terminations, one for the current var
107	 * string, and one for final null of the var table. So check that the
108	 * strlen written, r, leaves room for 2 chars.
109	 */
110	if ((r == -1) || (r > (int)(b->size - 2))) {
111		b->size = 0;
112		return 0;
113	}
114
115	/* skip over this string's null termination */
116	r++;
117	b->size -= r;
118	b->buf += r;
119
120	return r;
121}
122
123/*
124 * Initialize local vars from the right source for this platform.
125 * Return 0 on success, nonzero on error.
126 */
127int
128BCMINITFN(srom_var_init)(sb_t *sbh, uint bustype, void *curmap, osl_t *osh,
129	char **vars, uint *count)
130{
131	ASSERT(bustype == BUSTYPE(bustype));
132	if (vars == NULL || count == NULL)
133		return (0);
134
135	*vars = NULL;
136	*count = 0;
137
138	switch (BUSTYPE(bustype)) {
139	case SB_BUS:
140	case JTAG_BUS:
141		return initvars_srom_sb(sbh, osh, curmap, vars, count);
142
143	case PCI_BUS:
144		ASSERT(curmap);	/* can not be NULL */
145		return initvars_srom_pci(sbh, curmap, vars, count);
146
147	case PCMCIA_BUS:
148		return initvars_cis_pcmcia(sbh, osh, vars, count);
149
150
151	default:
152		ASSERT(0);
153	}
154	return (-1);
155}
156
157/* support only 16-bit word read from srom */
158int
159srom_read(sb_t *sbh, uint bustype, void *curmap, osl_t *osh,
160          uint byteoff, uint nbytes, uint16 *buf)
161{
162	void *srom;
163	uint i, off, nw;
164
165	ASSERT(bustype == BUSTYPE(bustype));
166
167	/* check input - 16-bit access only */
168	if (byteoff & 1 || nbytes & 1 || (byteoff + nbytes) > (SPROM_SIZE * 2))
169		return 1;
170
171	off = byteoff / 2;
172	nw = nbytes / 2;
173
174	if (BUSTYPE(bustype) == PCI_BUS) {
175		if (!curmap)
176			return 1;
177		srom = (uchar*)curmap + PCI_BAR0_SPROM_OFFSET;
178		if (sprom_read_pci(osh, srom, off, buf, nw, FALSE))
179			return 1;
180	} else if (BUSTYPE(bustype) == PCMCIA_BUS) {
181		for (i = 0; i < nw; i++) {
182			if (sprom_read_pcmcia(osh, (uint16)(off + i), (uint16 *)(buf + i)))
183				return 1;
184		}
185	} else if (BUSTYPE(bustype) == SB_BUS) {
186#ifdef BCMUSBDEV
187		if (SPROMBUS == PCMCIA_BUS) {
188			uint origidx;
189			void *regs;
190			int rc;
191			bool wasup;
192
193			origidx = sb_coreidx(sbh);
194			regs = sb_setcore(sbh, SB_PCMCIA, 0);
195			ASSERT(regs != NULL);
196
197			if (!(wasup = sb_iscoreup(sbh)))
198				sb_core_reset(sbh, 0, 0);
199
200			rc = get_sb_pcmcia_srom(sbh, osh, regs, byteoff, buf, nbytes);
201
202			if (!wasup)
203				sb_core_disable(sbh, 0);
204
205			sb_setcoreidx(sbh, origidx);
206			return rc;
207		}
208#endif /* def BCMUSBDEV */
209
210		return 1;
211	} else {
212		return 1;
213	}
214
215	return 0;
216}
217
218/* support only 16-bit word write into srom */
219int
220srom_write(sb_t *sbh, uint bustype, void *curmap, osl_t *osh,
221           uint byteoff, uint nbytes, uint16 *buf)
222{
223	uint16 *srom;
224	uint i, nw, crc_range;
225	uint16 image[SPROM_SIZE];
226	uint8 crc;
227	volatile uint32 val32;
228
229	ASSERT(bustype == BUSTYPE(bustype));
230
231	/* check input - 16-bit access only */
232	if ((byteoff & 1) || (nbytes & 1))
233		return 1;
234
235	if (byteoff == 0x55aa) {
236		/* Erase request */
237		crc_range = 0;
238		memset((void *)image, 0xff, nbytes);
239		nw = nbytes / 2;
240	} else if ((byteoff == 0) &&
241	           ((nbytes == SPROM_SIZE * 2) ||
242	            (nbytes == (SPROM_CRC_RANGE * 2)) ||
243	            (nbytes == (SROM4_WORDS * 2)))) {
244		/* Are we writing the whole thing at once? */
245		crc_range = nbytes;
246		bcopy((void *)buf, (void *)image, nbytes);
247		nw = nbytes / 2;
248	} else {
249		if ((byteoff + nbytes) > (SPROM_SIZE * 2))
250			return 1;
251
252		if (BUSTYPE(bustype) == PCMCIA_BUS) {
253			crc_range = SPROM_SIZE * 2;
254		}
255		else {
256			crc_range = SPROM_CRC_RANGE * 2;	/* Tentative */
257		}
258
259		nw = crc_range / 2;
260		/* read first 64 words from srom */
261		if (srom_read(sbh, bustype, curmap, osh, 0, crc_range, image))
262			return 1;
263		if (image[SROM4_SIGN] == SROM4_SIGNATURE) {
264			nw = SROM4_WORDS;
265			crc_range = nw * 2;
266			if (srom_read(sbh, bustype, curmap, osh, 0, crc_range, image))
267				return 1;
268		}
269		/* make changes */
270		bcopy((void *)buf, (void *)&image[byteoff / 2], nbytes);
271	}
272
273	if (crc_range) {
274		/* calculate crc */
275		htol16_buf(image, crc_range);
276		crc = ~hndcrc8((uint8 *)image, crc_range - 1, CRC8_INIT_VALUE);
277		ltoh16_buf(image, crc_range);
278		image[nw - 1] = (crc << 8) | (image[nw - 1] & 0xff);
279	}
280
281	if (BUSTYPE(bustype) == PCI_BUS) {
282		srom = (uint16 *)((uchar*)curmap + PCI_BAR0_SPROM_OFFSET);
283		/* enable writes to the SPROM */
284		val32 = OSL_PCI_READ_CONFIG(osh, PCI_SPROM_CONTROL, sizeof(uint32));
285		val32 |= SPROM_WRITEEN;
286		OSL_PCI_WRITE_CONFIG(osh, PCI_SPROM_CONTROL, sizeof(uint32), val32);
287		bcm_mdelay(WRITE_ENABLE_DELAY);
288		/* write srom */
289		for (i = 0; i < nw; i++) {
290			W_REG(osh, &srom[i], image[i]);
291			bcm_mdelay(WRITE_WORD_DELAY);
292		}
293		/* disable writes to the SPROM */
294		OSL_PCI_WRITE_CONFIG(osh, PCI_SPROM_CONTROL, sizeof(uint32), val32 &
295		                     ~SPROM_WRITEEN);
296	} else if (BUSTYPE(bustype) == PCMCIA_BUS) {
297		/* enable writes to the SPROM */
298		if (sprom_cmd_pcmcia(osh, SROM_WEN))
299			return 1;
300		bcm_mdelay(WRITE_ENABLE_DELAY);
301		/* write srom */
302		for (i = 0; i < nw; i++) {
303			sprom_write_pcmcia(osh, (uint16)(i), image[i]);
304			bcm_mdelay(WRITE_WORD_DELAY);
305		}
306		/* disable writes to the SPROM */
307		if (sprom_cmd_pcmcia(osh, SROM_WDS))
308			return 1;
309	} else if (BUSTYPE(bustype) == SB_BUS) {
310#ifdef BCMUSBDEV
311		if (SPROMBUS == PCMCIA_BUS) {
312			uint origidx;
313			void *regs;
314			int rc;
315			bool wasup;
316
317			origidx = sb_coreidx(sbh);
318			regs = sb_setcore(sbh, SB_PCMCIA, 0);
319			ASSERT(regs != NULL);
320
321			if (!(wasup = sb_iscoreup(sbh)))
322				sb_core_reset(sbh, 0, 0);
323
324			rc = set_sb_pcmcia_srom(sbh, osh, regs, byteoff, buf, nbytes);
325
326			if (!wasup)
327				sb_core_disable(sbh, 0);
328
329			sb_setcoreidx(sbh, origidx);
330			return rc;
331		}
332#endif /* def BCMUSBDEV */
333		return 1;
334	} else {
335		return 1;
336	}
337
338	bcm_mdelay(WRITE_ENABLE_DELAY);
339	return 0;
340}
341
342#ifdef BCMUSBDEV
343#define SB_PCMCIA_READ(osh, regs, fcr) \
344		R_REG(osh, (volatile uint8 *)(regs) + 0x600 + (fcr) - 0x700 / 2)
345#define SB_PCMCIA_WRITE(osh, regs, fcr, v) \
346		W_REG(osh, (volatile uint8 *)(regs) + 0x600 + (fcr) - 0x700 / 2, v)
347
348/* set PCMCIA srom command register */
349static int
350srom_cmd_sb_pcmcia(osl_t *osh, uint8 *pcmregs, uint8 cmd)
351{
352	uint8 status = 0;
353	uint wait_cnt = 0;
354
355	/* write srom command register */
356	SB_PCMCIA_WRITE(osh, pcmregs, SROM_CS, cmd);
357
358	/* wait status */
359	while (++wait_cnt < 1000000) {
360		status = SB_PCMCIA_READ(osh, pcmregs, SROM_CS);
361		if (status & SROM_DONE)
362			return 0;
363		OSL_DELAY(1);
364	}
365
366	BS_ERROR(("sr_cmd: Give up after %d tries, stat = 0x%x\n", wait_cnt, status));
367	return 1;
368}
369
370/* read a word from the PCMCIA srom over SB */
371static int
372srom_read_sb_pcmcia(osl_t *osh, uint8 *pcmregs, uint16 addr, uint16 *data)
373{
374	uint8 addr_l, addr_h,  data_l, data_h;
375
376	addr_l = (uint8)((addr * 2) & 0xff);
377	addr_h = (uint8)(((addr * 2) >> 8) & 0xff);
378
379	/* set address */
380	SB_PCMCIA_WRITE(osh, pcmregs, SROM_ADDRH, addr_h);
381	SB_PCMCIA_WRITE(osh, pcmregs, SROM_ADDRL, addr_l);
382
383	/* do read */
384	if (srom_cmd_sb_pcmcia(osh, pcmregs, SROM_READ))
385		return 1;
386
387	/* read data */
388	data_h = SB_PCMCIA_READ(osh, pcmregs, SROM_DATAH);
389	data_l = SB_PCMCIA_READ(osh, pcmregs, SROM_DATAL);
390	*data = ((uint16)data_h << 8) | data_l;
391
392	return 0;
393}
394
395/* write a word to the PCMCIA srom over SB */
396static int
397srom_write_sb_pcmcia(osl_t *osh, uint8 *pcmregs, uint16 addr, uint16 data)
398{
399	uint8 addr_l, addr_h, data_l, data_h;
400	int rc;
401
402	addr_l = (uint8)((addr * 2) & 0xff);
403	addr_h = (uint8)(((addr * 2) >> 8) & 0xff);
404
405	/* set address */
406	SB_PCMCIA_WRITE(osh, pcmregs, SROM_ADDRH, addr_h);
407	SB_PCMCIA_WRITE(osh, pcmregs, SROM_ADDRL, addr_l);
408
409	data_l = (uint8)(data & 0xff);
410	data_h = (uint8)((data >> 8) & 0xff);
411
412	/* write data */
413	SB_PCMCIA_WRITE(osh, pcmregs, SROM_DATAH, data_h);
414	SB_PCMCIA_WRITE(osh, pcmregs, SROM_DATAL, data_l);
415
416	/* do write */
417	rc = srom_cmd_sb_pcmcia(osh, pcmregs, SROM_WRITE);
418	OSL_DELAY(20000);
419	return rc;
420}
421
422/*
423 * Read the srom for the pcmcia-srom over sb case.
424 * Return 0 on success, nonzero on error.
425 */
426static int
427get_sb_pcmcia_srom(sb_t *sbh, osl_t *osh, uint8 *pcmregs,
428                   uint boff, uint16 *srom, uint bsz)
429{
430	uint i, nw, woff, wsz;
431	int err = 0;
432
433	/* read must be at word boundary */
434	ASSERT((boff & 1) == 0 && (bsz & 1) == 0);
435
436	/* read sprom size and validate the parms */
437	if ((nw = srom_size(sbh, osh)) == 0) {
438		BS_ERROR(("get_sb_pcmcia_srom: sprom size unknown\n"));
439		err = -1;
440		goto out;
441	}
442	if (boff + bsz > 2 * nw) {
443		BS_ERROR(("get_sb_pcmcia_srom: sprom size exceeded\n"));
444		err = -2;
445		goto out;
446	}
447
448	/* read in sprom contents */
449	for (woff = boff / 2, wsz = bsz / 2, i = 0;
450	     woff < nw && i < wsz; woff ++, i ++) {
451		if (srom_read_sb_pcmcia(osh, pcmregs, (uint16)woff, &srom[i])) {
452			BS_ERROR(("get_sb_pcmcia_srom: sprom read failed\n"));
453			err = -3;
454			goto out;
455		}
456	}
457
458out:
459	return err;
460}
461
462/*
463 * Write the srom for the pcmcia-srom over sb case.
464 * Return 0 on success, nonzero on error.
465 */
466static int
467set_sb_pcmcia_srom(sb_t *sbh, osl_t *osh, uint8 *pcmregs,
468                   uint boff, uint16 *srom, uint bsz)
469{
470	uint i, nw, woff, wsz;
471	uint16 word;
472	uint8 crc;
473	int err = 0;
474
475	/* write must be at word boundary */
476	ASSERT((boff & 1) == 0 && (bsz & 1) == 0);
477
478	/* read sprom size and validate the parms */
479	if ((nw = srom_size(sbh, osh)) == 0) {
480		BS_ERROR(("set_sb_pcmcia_srom: sprom size unknown\n"));
481		err = -1;
482		goto out;
483	}
484	if (boff + bsz > 2 * nw) {
485		BS_ERROR(("set_sb_pcmcia_srom: sprom size exceeded\n"));
486		err = -2;
487		goto out;
488	}
489
490	/* enable write */
491	if (srom_cmd_sb_pcmcia(osh, pcmregs, SROM_WEN)) {
492		BS_ERROR(("set_sb_pcmcia_srom: sprom wen failed\n"));
493		err = -3;
494		goto out;
495	}
496
497	/* write buffer to sprom */
498	for (woff = boff / 2, wsz = bsz / 2, i = 0;
499	     woff < nw && i < wsz; woff ++, i ++) {
500		if (srom_write_sb_pcmcia(osh, pcmregs, (uint16)woff, srom[i])) {
501			BS_ERROR(("set_sb_pcmcia_srom: sprom write failed\n"));
502			err = -4;
503			goto out;
504		}
505	}
506
507	/* fix crc */
508	crc = CRC8_INIT_VALUE;
509	for (woff = 0; woff < nw; woff ++) {
510		if (srom_read_sb_pcmcia(osh, pcmregs, (uint16)woff, &word)) {
511			BS_ERROR(("set_sb_pcmcia_srom: sprom fix crc read failed\n"));
512			err = -5;
513			goto out;
514		}
515		word = htol16(word);
516		crc = hndcrc8((uint8 *)&word, woff != nw - 1 ? 2 : 1, crc);
517	}
518	word = (~crc << 8) + (ltoh16(word) & 0xff);
519	if (srom_write_sb_pcmcia(osh, pcmregs, (uint16)(woff - 1), word)) {
520		BS_ERROR(("set_sb_pcmcia_srom: sprom fix crc write failed\n"));
521		err = -6;
522		goto out;
523	}
524
525	/* disable write */
526	if (srom_cmd_sb_pcmcia(osh, pcmregs, SROM_WDS)) {
527		BS_ERROR(("set_sb_pcmcia_srom: sprom wds failed\n"));
528		err = -7;
529		goto out;
530	}
531
532out:
533	return err;
534}
535#endif /* def BCMUSBDEV */
536
537int
538srom_parsecis(osl_t *osh, uint8 *pcis[], uint ciscnt, char **vars, uint *count)
539{
540	char eabuf[32];
541	char *base;
542	varbuf_t b;
543	uint8 *cis, tup, tlen, sromrev = 1;
544	int i, j;
545	uint varsize;
546	bool ag_init = FALSE;
547	uint32 w32;
548	uint funcid;
549	uint cisnum;
550	int32 boardnum = -1;
551
552	ASSERT(vars);
553	ASSERT(count);
554
555	base = MALLOC(osh, MAXSZ_NVRAM_VARS);
556	ASSERT(base);
557	if (!base)
558		return -2;
559
560	varbuf_init(&b, base, MAXSZ_NVRAM_VARS);
561
562	eabuf[0] = '\0';
563	for (cisnum = 0; cisnum < ciscnt; cisnum++) {
564		cis = *pcis++;
565		i = 0;
566		funcid = 0;
567		do {
568			tup = cis[i++];
569			tlen = cis[i++];
570			if ((i + tlen) >= CIS_SIZE)
571				break;
572
573			switch (tup) {
574			case CISTPL_VERS_1:
575				/* assume the strings are good if the version field checks out */
576				if (((cis[i + 1] << 8) + cis[i]) >= 0x0008) {
577					varbuf_append(&b, "manf=%s", &cis[i + 2]);
578					varbuf_append(&b, "productname=%s",
579					              &cis[i + 3 + strlen((char *)&cis[i + 2])]);
580					break;
581				}
582
583			case CISTPL_MANFID:
584				varbuf_append(&b, "manfid=0x%x", (cis[i + 1] << 8) + cis[i]);
585				varbuf_append(&b, "prodid=0x%x", (cis[i + 3] << 8) + cis[i + 2]);
586				break;
587
588			case CISTPL_FUNCID:
589				funcid = cis[i];
590				break;
591
592			case CISTPL_FUNCE:
593				switch (funcid) {
594				default:
595					/* set macaddr if HNBU_MACADDR not seen yet */
596					if (eabuf[0] == '\0' && cis[i] == LAN_NID) {
597						ASSERT(cis[i + 1] == ETHER_ADDR_LEN);
598						bcm_ether_ntoa((struct ether_addr *)&cis[i + 2],
599						               eabuf);
600					}
601					/* set boardnum if HNBU_BOARDNUM not seen yet */
602					if (boardnum == -1)
603						boardnum = (cis[i + 6] << 8) + cis[i + 7];
604					break;
605				}
606				break;
607
608			case CISTPL_CFTABLE:
609				varbuf_append(&b, "regwindowsz=%d", (cis[i + 7] << 8) | cis[i + 6]);
610				break;
611
612			case CISTPL_BRCM_HNBU:
613				switch (cis[i]) {
614				case HNBU_SROMREV:
615					sromrev = cis[i + 1];
616					varbuf_append(&b, "sromrev=%d", sromrev);
617					break;
618
619				case HNBU_CHIPID:
620					varbuf_append(&b, "vendid=0x%x", (cis[i + 2] << 8) +
621					              cis[i + 1]);
622					varbuf_append(&b, "devid=0x%x", (cis[i + 4] << 8) +
623					              cis[i + 3]);
624					if (tlen >= 7) {
625						varbuf_append(&b, "chiprev=%d",
626						              (cis[i + 6] << 8) + cis[i + 5]);
627					}
628					if (tlen >= 9) {
629						varbuf_append(&b, "subvendid=0x%x",
630						              (cis[i + 8] << 8) + cis[i + 7]);
631					}
632					if (tlen >= 11) {
633						varbuf_append(&b, "subdevid=0x%x",
634						              (cis[i + 10] << 8) + cis[i + 9]);
635						/* subdevid doubles for boardtype */
636						varbuf_append(&b, "boardtype=0x%x",
637						              (cis[i + 10] << 8) + cis[i + 9]);
638					}
639					break;
640
641				case HNBU_BOARDREV:
642					varbuf_append(&b, "boardrev=0x%x", cis[i + 1]);
643					break;
644
645				case HNBU_AA:
646					varbuf_append(&b, "aa2g=%d", cis[i + 1]);
647					break;
648
649				case HNBU_AG:
650					varbuf_append(&b, "ag0=%d", cis[i + 1]);
651					ag_init = TRUE;
652					break;
653
654				case HNBU_ANT5G:
655					varbuf_append(&b, "aa5g=%d", cis[i + 1]);
656					varbuf_append(&b, "ag1=%d", cis[i + 2]);
657					break;
658
659				case HNBU_CC:
660					ASSERT(sromrev == 1);
661					varbuf_append(&b, "cc=%d", cis[i + 1]);
662					break;
663
664				case HNBU_PAPARMS:
665					if (tlen == 2) {
666						ASSERT(sromrev == 1);
667						varbuf_append(&b, "pa0maxpwr=%d", cis[i + 1]);
668					} else if (tlen >= 9) {
669						if (tlen == 10) {
670							ASSERT(sromrev >= 2);
671							varbuf_append(&b, "opo=%d", cis[i + 9]);
672						} else
673							ASSERT(tlen == 9);
674
675						for (j = 0; j < 3; j++) {
676							varbuf_append(&b, "pa0b%d=%d", j,
677							              (cis[i + (j * 2) + 2] << 8) +
678							              cis[i + (j * 2) + 1]);
679						}
680						varbuf_append(&b, "pa0itssit=%d", cis[i + 7]);
681						varbuf_append(&b, "pa0maxpwr=%d", cis[i + 8]);
682					} else
683						ASSERT(tlen >= 9);
684					break;
685
686				case HNBU_PAPARMS5G:
687					ASSERT((sromrev == 2) || (sromrev == 3));
688					for (j = 0; j < 3; j++) {
689						varbuf_append(&b, "pa1b%d=%d", j,
690							(cis[i + (j * 2) + 2] << 8) +
691							cis[i + (j * 2) + 1]);
692					}
693					for (j = 3; j < 6; j++) {
694						varbuf_append(&b, "pa1lob%d=%d", j - 3,
695							(cis[i + (j * 2) + 2] << 8) +
696							cis[i + (j * 2) + 1]);
697					}
698					for (j = 6; j < 9; j++) {
699						varbuf_append(&b, "pa1hib%d=%d", j - 6,
700							(cis[i + (j * 2) + 2] << 8) +
701							cis[i + (j * 2) + 1]);
702					}
703					varbuf_append(&b, "pa1itssit=%d", cis[i + 19]);
704					varbuf_append(&b, "pa1maxpwr=%d", cis[i + 20]);
705					varbuf_append(&b, "pa1lomaxpwr=%d", cis[i + 21]);
706					varbuf_append(&b, "pa1himaxpwr=%d", cis[i + 22]);
707					break;
708
709				case HNBU_OEM:
710					ASSERT(sromrev == 1);
711					varbuf_append(&b, "oem=%02x%02x%02x%02x%02x%02x%02x%02x",
712					              cis[i + 1], cis[i + 2],
713					              cis[i + 3], cis[i + 4],
714					              cis[i + 5], cis[i + 6],
715					              cis[i + 7], cis[i + 8]);
716					break;
717
718				case HNBU_BOARDFLAGS:
719					w32 = (cis[i + 2] << 8) + cis[i + 1];
720					if (tlen == 5)
721						w32 |= (cis[i + 4] << 24) + (cis[i + 3] << 16);
722					varbuf_append(&b, "boardflags=0x%x", w32);
723					break;
724
725				case HNBU_LEDS:
726					if (cis[i + 1] != 0xff) {
727						varbuf_append(&b, "ledbh0=%d", cis[i + 1]);
728					}
729					if (cis[i + 2] != 0xff) {
730						varbuf_append(&b, "ledbh1=%d", cis[i + 2]);
731					}
732					if (cis[i + 3] != 0xff) {
733						varbuf_append(&b, "ledbh2=%d", cis[i + 3]);
734					}
735					if (cis[i + 4] != 0xff) {
736						varbuf_append(&b, "ledbh3=%d", cis[i + 4]);
737					}
738					break;
739
740				case HNBU_CCODE:
741					ASSERT(sromrev > 1);
742					if ((cis[i + 1] == 0) || (cis[i + 2] == 0))
743						varbuf_append(&b, "ccode=");
744					else
745						varbuf_append(&b, "ccode=%c%c",
746						              cis[i + 1], cis[i + 2]);
747					varbuf_append(&b, "cctl=0x%x", cis[i + 3]);
748					break;
749
750				case HNBU_CCKPO:
751					ASSERT(sromrev > 2);
752					varbuf_append(&b, "cckpo=0x%x",
753					              (cis[i + 2] << 8) | cis[i + 1]);
754					break;
755
756				case HNBU_OFDMPO:
757					ASSERT(sromrev > 2);
758					varbuf_append(&b, "ofdmpo=0x%x",
759					              (cis[i + 4] << 24) |
760					              (cis[i + 3] << 16) |
761					              (cis[i + 2] << 8) |
762					              cis[i + 1]);
763					break;
764
765				case HNBU_RDLID:
766					varbuf_append(&b, "rdlid=0x%x",
767					              (cis[i + 2] << 8) | cis[i + 1]);
768					break;
769
770				case HNBU_RDLRNDIS:
771					varbuf_append(&b, "rdlrndis=%d", cis[i + 1]);
772					break;
773
774				case HNBU_RDLRWU:
775					varbuf_append(&b, "rdlrwu=%d", cis[i + 1]);
776					break;
777
778				case HNBU_RDLSN:
779					varbuf_append(&b, "rdlsn=%d",
780					              (cis[i + 2] << 8) | cis[i + 1]);
781					break;
782
783				case HNBU_XTALFREQ:
784					varbuf_append(&b, "xtalfreq=%d",
785					              (cis[i + 4] << 24) |
786					              (cis[i + 3] << 16) |
787					              (cis[i + 2] << 8) |
788					              cis[i + 1]);
789					break;
790
791				case HNBU_RSSISMBXA2G:
792					ASSERT(sromrev == 3);
793					varbuf_append(&b, "rssismf2g=%d", cis[i + 1] & 0xf);
794					varbuf_append(&b, "rssismc2g=%d", (cis[i + 1] >> 4) & 0xf);
795					varbuf_append(&b, "rssisav2g=%d", cis[i + 2] & 0x7);
796					varbuf_append(&b, "bxa2g=%d", (cis[i + 2] >> 3) & 0x3);
797					break;
798
799				case HNBU_RSSISMBXA5G:
800					ASSERT(sromrev == 3);
801					varbuf_append(&b, "rssismf5g=%d", cis[i + 1] & 0xf);
802					varbuf_append(&b, "rssismc5g=%d", (cis[i + 1] >> 4) & 0xf);
803					varbuf_append(&b, "rssisav5g=%d", cis[i + 2] & 0x7);
804					varbuf_append(&b, "bxa5g=%d", (cis[i + 2] >> 3) & 0x3);
805					break;
806
807				case HNBU_TRI2G:
808					ASSERT(sromrev == 3);
809					varbuf_append(&b, "tri2g=%d", cis[i + 1]);
810					break;
811
812				case HNBU_TRI5G:
813					ASSERT(sromrev == 3);
814					varbuf_append(&b, "tri5gl=%d", cis[i + 1]);
815					varbuf_append(&b, "tri5g=%d", cis[i + 2]);
816					varbuf_append(&b, "tri5gh=%d", cis[i + 3]);
817					break;
818
819				case HNBU_RXPO2G:
820					ASSERT(sromrev == 3);
821					varbuf_append(&b, "rxpo2g=%d", cis[i + 1]);
822					break;
823
824				case HNBU_RXPO5G:
825					ASSERT(sromrev == 3);
826					varbuf_append(&b, "rxpo5g=%d", cis[i + 1]);
827					break;
828
829				case HNBU_BOARDNUM:
830					boardnum = (cis[i + 2] << 8) + cis[i + 1];
831					break;
832
833				case HNBU_MACADDR:
834					bcm_ether_ntoa((struct ether_addr *)&cis[i + 1],
835					               eabuf);
836					break;
837
838				case HNBU_BOARDTYPE:
839					varbuf_append(&b, "boardtype=0x%x",
840					              (cis[i + 2] << 8) + cis[i + 1]);
841					break;
842
843#if defined(BCMCCISSR3)
844				case HNBU_SROM3SWRGN: {
845					uint16 srom[35];
846					uint8 srev = cis[i + 1 + 70];
847					ASSERT(srev == 3);
848					/* make tuple value 16-bit aligned and parse it */
849					bcopy(&cis[i + 1], srom, sizeof(srom));
850					_initvars_srom_pci(srev, srom, SROM3_SWRGN_OFF, &b);
851					/* create extra variables */
852					varbuf_append(&b, "vendid=0x%x",
853					              (cis[i + 1 + 73] << 8) + cis[i + 1 + 72]);
854					varbuf_append(&b, "devid=0x%x",
855					              (cis[i + 1 + 75] << 8) + cis[i + 1 + 74]);
856					varbuf_append(&b, "xtalfreq=%d",
857					              (cis[i + 1 + 77] << 8) + cis[i + 1 + 76]);
858					/* 2.4G antenna gain is included in SROM */
859					ag_init = TRUE;
860					/* Ethernet MAC address is included in SROM */
861					eabuf[0] = 0;
862					boardnum = -1;
863					break;
864				}
865#endif
866				}
867				break;
868			}
869			i += tlen;
870		} while (tup != CISTPL_END);
871	}
872
873	if (boardnum != -1) {
874		varbuf_append(&b, "boardnum=%d", boardnum);
875	}
876
877	if (eabuf[0]) {
878		varbuf_append(&b, "macaddr=%s", eabuf);
879	}
880
881	/* if there is no antenna gain field, set default */
882	if (ag_init == FALSE) {
883		varbuf_append(&b, "ag0=%d", 0xff);
884	}
885
886	/* final nullbyte terminator */
887	ASSERT(b.size >= 1);
888	*b.buf++ = '\0';
889	varsize = (uint)(b.buf - base);
890	ASSERT(varsize < MAXSZ_NVRAM_VARS);
891	if (varsize < MAXSZ_NVRAM_VARS) {
892		char* new_buf;
893		new_buf = (char*)MALLOC(osh, varsize);
894		ASSERT(new_buf);
895		if (new_buf) {
896			bcopy(base, new_buf, varsize);
897			MFREE(osh, base, MAXSZ_NVRAM_VARS);
898			base = new_buf;
899		}
900	}
901
902	*vars = base;
903	*count = varsize;
904
905	return (0);
906}
907
908
909/* set PCMCIA sprom command register */
910static int
911sprom_cmd_pcmcia(osl_t *osh, uint8 cmd)
912{
913	uint8 status = 0;
914	uint wait_cnt = 1000;
915
916	/* write sprom command register */
917	OSL_PCMCIA_WRITE_ATTR(osh, SROM_CS, &cmd, 1);
918
919	/* wait status */
920	while (wait_cnt--) {
921		OSL_PCMCIA_READ_ATTR(osh, SROM_CS, &status, 1);
922		if (status & SROM_DONE)
923			return 0;
924	}
925
926	return 1;
927}
928
929/* read a word from the PCMCIA srom */
930static int
931sprom_read_pcmcia(osl_t *osh, uint16 addr, uint16 *data)
932{
933	uint8 addr_l, addr_h, data_l, data_h;
934
935	addr_l = (uint8)((addr * 2) & 0xff);
936	addr_h = (uint8)(((addr * 2) >> 8) & 0xff);
937
938	/* set address */
939	OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRH, &addr_h, 1);
940	OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRL, &addr_l, 1);
941
942	/* do read */
943	if (sprom_cmd_pcmcia(osh, SROM_READ))
944		return 1;
945
946	/* read data */
947	data_h = data_l = 0;
948	OSL_PCMCIA_READ_ATTR(osh, SROM_DATAH, &data_h, 1);
949	OSL_PCMCIA_READ_ATTR(osh, SROM_DATAL, &data_l, 1);
950
951	*data = (data_h << 8) | data_l;
952	return 0;
953}
954
955/* write a word to the PCMCIA srom */
956static int
957sprom_write_pcmcia(osl_t *osh, uint16 addr, uint16 data)
958{
959	uint8 addr_l, addr_h, data_l, data_h;
960
961	addr_l = (uint8)((addr * 2) & 0xff);
962	addr_h = (uint8)(((addr * 2) >> 8) & 0xff);
963	data_l = (uint8)(data & 0xff);
964	data_h = (uint8)((data >> 8) & 0xff);
965
966	/* set address */
967	OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRH, &addr_h, 1);
968	OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRL, &addr_l, 1);
969
970	/* write data */
971	OSL_PCMCIA_WRITE_ATTR(osh, SROM_DATAH, &data_h, 1);
972	OSL_PCMCIA_WRITE_ATTR(osh, SROM_DATAL, &data_l, 1);
973
974	/* do write */
975	return sprom_cmd_pcmcia(osh, SROM_WRITE);
976}
977
978/*
979 * Read in and validate sprom.
980 * Return 0 on success, nonzero on error.
981 */
982static int
983sprom_read_pci(osl_t *osh, uint16 *sprom, uint wordoff, uint16 *buf, uint nwords, bool check_crc)
984{
985	int err = 0;
986	uint i;
987
988	/* read the sprom */
989	for (i = 0; i < nwords; i++) {
990#ifdef BCMQT
991		buf[i] = R_REG(osh, &sprom[wordoff + i]);
992#endif
993		buf[i] = R_REG(osh, &sprom[wordoff + i]);
994	}
995
996	if (check_crc) {
997		if (buf[0] == 0xffff) {
998			/* The hardware thinks that an srom that starts with 0xffff
999			 * is blank, regardless of the rest of the content, so declare
1000			 * it bad.
1001			 */
1002			BS_ERROR(("%s: buf[0] = 0x%x, returning bad-crc\n", __FUNCTION__, buf[0]));
1003			return 1;
1004		}
1005
1006		/* fixup the endianness so crc8 will pass */
1007		htol16_buf(buf, nwords * 2);
1008		if (hndcrc8((uint8 *)buf, nwords * 2, CRC8_INIT_VALUE) != CRC8_GOOD_VALUE)
1009			err = 1;
1010		/* now correct the endianness of the byte array */
1011		ltoh16_buf(buf, nwords * 2);
1012	}
1013
1014	return err;
1015}
1016
1017/*
1018* Create variable table from memory.
1019* Return 0 on success, nonzero on error.
1020*/
1021static int
1022BCMINITFN(initvars_table)(osl_t *osh, char *start, char *end, char **vars, uint *count)
1023{
1024	int c = (int)(end - start);
1025
1026	/* do it only when there is more than just the null string */
1027	if (c > 1) {
1028		char *vp = MALLOC(osh, c);
1029		ASSERT(vp);
1030		if (!vp)
1031			return BCME_NOMEM;
1032		bcopy(start, vp, c);
1033		*vars = vp;
1034		*count = c;
1035	}
1036	else {
1037		*vars = NULL;
1038		*count = 0;
1039	}
1040
1041	return 0;
1042}
1043
1044/*
1045 * Find variables with <devpath> from flash. 'base' points to the beginning
1046 * of the table upon enter and to the end of the table upon exit when success.
1047 * Return 0 on success, nonzero on error.
1048 */
1049static int
1050initvars_flash(sb_t *sbh, osl_t *osh, char **base, uint len)
1051{
1052	char *vp = *base;
1053	char *flash;
1054	int err;
1055	char *s;
1056	uint l, dl, copy_len;
1057	char devpath[SB_DEVPATH_BUFSZ];
1058
1059	/* allocate memory and read in flash */
1060	if (!(flash = MALLOC(osh, NVRAM_SPACE)))
1061		return BCME_NOMEM;
1062	if ((err = nvram_getall(flash, NVRAM_SPACE)))
1063		goto exit;
1064
1065	sb_devpath(sbh, devpath, sizeof(devpath));
1066
1067	/* grab vars with the <devpath> prefix in name */
1068	dl = strlen(devpath);
1069	for (s = flash; s && *s; s += l + 1) {
1070		l = strlen(s);
1071
1072		/* skip non-matching variable */
1073		if (strncmp(s, devpath, dl))
1074			continue;
1075
1076		/* is there enough room to copy? */
1077		copy_len = l - dl + 1;
1078		if (len < copy_len) {
1079			err = BCME_BUFTOOSHORT;
1080			goto exit;
1081		}
1082
1083		/* no prefix, just the name=value */
1084		strncpy(vp, &s[dl], copy_len);
1085		vp += copy_len;
1086		len -= copy_len;
1087	}
1088
1089	/* add null string as terminator */
1090	if (len < 1) {
1091		err = BCME_BUFTOOSHORT;
1092		goto exit;
1093	}
1094	*vp++ = '\0';
1095
1096	*base = vp;
1097
1098exit:	MFREE(osh, flash, NVRAM_SPACE);
1099	return err;
1100}
1101
1102#if !defined(BCMUSBDEV)
1103/*
1104 * Initialize nonvolatile variable table from flash.
1105 * Return 0 on success, nonzero on error.
1106 */
1107static int
1108initvars_flash_sb(sb_t *sbh, char **vars, uint *count)
1109{
1110	osl_t *osh = sb_osh(sbh);
1111	char *vp, *base;
1112	int err;
1113
1114	ASSERT(vars);
1115	ASSERT(count);
1116
1117	base = vp = MALLOC(osh, MAXSZ_NVRAM_VARS);
1118	ASSERT(vp);
1119	if (!vp)
1120		return BCME_NOMEM;
1121
1122	if ((err = initvars_flash(sbh, osh, &vp, MAXSZ_NVRAM_VARS)) == 0)
1123		err = initvars_table(osh, base, vp, vars, count);
1124
1125	MFREE(osh, base, MAXSZ_NVRAM_VARS);
1126
1127	return err;
1128}
1129#endif
1130
1131#ifdef WLTEST
1132char mfgsromvars[256];
1133char *defaultsromvars = "il0macaddr=00:11:22:33:44:51\0"
1134		"et0macaddr=00:11:22:33:44:52\0"
1135		"et1macaddr=00:11:22:33:44:53\0"
1136		"boardtype=0xffff\0"
1137		"boardrev=0x10\0"
1138		"boardflags=8\0"
1139		"sromrev=2\0"
1140		"aa2g=3\0"
1141		"\0";
1142#define	MFGSROM_DEFVARSLEN	149 /* default srom len */
1143#endif /* WL_TEST */
1144
1145/*
1146 * Initialize nonvolatile variable table from sprom.
1147 * Return 0 on success, nonzero on error.
1148 */
1149
1150typedef struct {
1151	const char *name;
1152	uint32	revmask;
1153	uint32	flags;
1154	uint16	off;
1155	uint16	mask;
1156} sromvar_t;
1157
1158#define SRFL_MORE	1		/* value continues as described by the next entry */
1159#define	SRFL_NOFFS	2		/* value bits can't be all one's */
1160#define	SRFL_PRHEX	4		/* value is in hexdecimal format */
1161#define	SRFL_PRSIGN	8		/* value is in signed decimal format */
1162#define	SRFL_CCODE	0x10		/* value is in country code format */
1163#define	SRFL_ETHADDR	0x20		/* value is an Ethernet address */
1164#define SRFL_LEDDC	0x40		/* value is an LED duty cycle */
1165
1166/* Assumptions:
1167 * - Ethernet address spins across 3 consective words
1168 *
1169 * Table rules:
1170 * - Add multiple entries next to each other if a value spins across multiple words
1171 *   (even multiple fields in the same word) with each entry except the last having
1172 *   it's SRFL_MORE bit set.
1173 * - Ethernet address entry does not follow above rule and must not have SRFL_MORE
1174 *   bit set. Its SRFL_ETHADDR bit implies it takes multiple words.
1175 * - The last entry's name field must be NULL to indicate the end of the table. Other
1176 *   entries must have non-NULL name.
1177 */
1178
1179static const sromvar_t pci_sromvars[] = {
1180	{"boardrev",	0x0000000e,	SRFL_PRHEX,	SROM_AABREV, SROM_BR_MASK},
1181	{"boardrev",	0x000000f0,	SRFL_PRHEX,	SROM4_BREV, 0xffff},
1182	{"boardrev",	0xffffff00,	SRFL_PRHEX,	SROM8_BREV, 0xffff},
1183	{"boardflags",	0x00000002,	SRFL_PRHEX,	SROM_BFL, 0xffff},
1184	{"boardflags",	0x00000004,	SRFL_PRHEX|SRFL_MORE,	SROM_BFL, 0xffff},
1185	{"",		0,		0,			SROM_BFL2, 0xffff},
1186	{"boardflags",	0x00000008,	SRFL_PRHEX|SRFL_MORE,	SROM_BFL, 0xffff},
1187	{"",		0,		0,			SROM3_BFL2, 0xffff},
1188	{"boardflags",	0x00000010,	SRFL_PRHEX|SRFL_MORE,	SROM4_BFL0, 0xffff},
1189	{"",		0,		0,			SROM4_BFL1, 0xffff},
1190	{"boardflags",	0x000000e0,	SRFL_PRHEX|SRFL_MORE,	SROM5_BFL0, 0xffff},
1191	{"",		0,		0,			SROM5_BFL1, 0xffff},
1192	{"boardflags",	0xffffff00,	SRFL_PRHEX|SRFL_MORE,	SROM8_BFL0, 0xffff},
1193	{"",		0,		0,			SROM8_BFL1, 0xffff},
1194	{"boardflags2", 0x00000010,	SRFL_PRHEX|SRFL_MORE,	SROM4_BFL2, 0xffff},
1195	{"",		0,		0,			SROM4_BFL3, 0xffff},
1196	{"boardflags2", 0x000000e0,	SRFL_PRHEX|SRFL_MORE,	SROM5_BFL2, 0xffff},
1197	{"",		0,		0,			SROM5_BFL3, 0xffff},
1198	{"boardflags2", 0xffffff00,	SRFL_PRHEX|SRFL_MORE,	SROM8_BFL2, 0xffff},
1199	{"",		0,		0,			SROM8_BFL3, 0xffff},
1200	{"boardtype",	0xfffffffc,	SRFL_PRHEX,	SROM_SSID, 0xffff},
1201	{"boardnum",	0x00000006,	0,		SROM_MACLO_IL0, 0xffff},
1202	{"boardnum",	0x00000008,	0,		SROM3_MACLO, 0xffff},
1203	{"boardnum",	0x00000010,	0,		SROM4_MACLO, 0xffff},
1204	{"boardnum",	0x000000e0,	0,		SROM5_MACLO, 0xffff},
1205	{"boardnum",	0xffffff00,	0,		SROM8_MACLO, 0xffff},
1206	{"cc",		0x00000002,	0,		SROM_AABREV, SROM_CC_MASK},
1207	{"regrev",	0x00000008,	0,		SROM_OPO, 0xff00},
1208	{"regrev",	0x00000010,	0,		SROM4_REGREV, 0xff},
1209	{"regrev",	0x000000e0,	0,		SROM5_REGREV, 0xff},
1210	{"regrev",	0xffffff00,	0,		SROM8_REGREV, 0xff},
1211	{"ledbh0",	0x0000000e,	SRFL_NOFFS,	SROM_LEDBH10, 0xff},
1212	{"ledbh1",	0x0000000e,	SRFL_NOFFS,	SROM_LEDBH10, 0xff00},
1213	{"ledbh2",	0x0000000e,	SRFL_NOFFS,	SROM_LEDBH32, 0xff},
1214	{"ledbh3",	0x0000000e,	SRFL_NOFFS,	SROM_LEDBH32, 0xff00},
1215	{"ledbh0",	0x00000010,	SRFL_NOFFS,	SROM4_LEDBH10, 0xff},
1216	{"ledbh1",	0x00000010,	SRFL_NOFFS,	SROM4_LEDBH10, 0xff00},
1217	{"ledbh2",	0x00000010,	SRFL_NOFFS,	SROM4_LEDBH32, 0xff},
1218	{"ledbh3",	0x00000010,	SRFL_NOFFS,	SROM4_LEDBH32, 0xff00},
1219	{"ledbh0",	0x000000e0,	SRFL_NOFFS,	SROM5_LEDBH10, 0xff},
1220	{"ledbh1",	0x000000e0,	SRFL_NOFFS,	SROM5_LEDBH10, 0xff00},
1221	{"ledbh2",	0x000000e0,	SRFL_NOFFS,	SROM5_LEDBH32, 0xff},
1222	{"ledbh3",	0x000000e0,	SRFL_NOFFS,	SROM5_LEDBH32, 0xff00},
1223	{"ledbh0",	0xffffff00,	SRFL_NOFFS,	SROM8_LEDBH10, 0xff},
1224	{"ledbh1",	0xffffff00,	SRFL_NOFFS,	SROM8_LEDBH10, 0xff00},
1225	{"ledbh2",	0xffffff00,	SRFL_NOFFS,	SROM8_LEDBH32, 0xff},
1226	{"ledbh3",	0xffffff00,	SRFL_NOFFS,	SROM8_LEDBH32, 0xff00},
1227	{"pa0b0",	0x0000000e,	SRFL_PRHEX,	SROM_WL0PAB0, 0xffff},
1228	{"pa0b1",	0x0000000e,	SRFL_PRHEX,	SROM_WL0PAB1, 0xffff},
1229	{"pa0b2",	0x0000000e,	SRFL_PRHEX,	SROM_WL0PAB2, 0xffff},
1230	{"pa0itssit",	0x0000000e,	0,		SROM_ITT, 0xff},
1231	{"pa0maxpwr",	0x0000000e,	0,		SROM_WL10MAXP, 0xff},
1232	{"pa0b0",	0xffffff00,	SRFL_PRHEX,	SROM8_W0_PAB0, 0xffff},
1233	{"pa0b1",	0xffffff00,	SRFL_PRHEX,	SROM8_W0_PAB1, 0xffff},
1234	{"pa0b2",	0xffffff00,	SRFL_PRHEX,	SROM8_W0_PAB2, 0xffff},
1235	{"pa0itssit",	0xffffff00,	0,		SROM8_W0_ITTMAXP, 0xff00},
1236	{"pa0maxpwr",	0xffffff00,	0,		SROM8_W0_ITTMAXP, 0xff},
1237	{"opo",		0x0000000c,	0,		SROM_OPO, 0xff},
1238	{"opo",		0xffffff00,	0,		SROM8_2G_OFDMPO, 0xff},
1239	{"aa2g",	0x0000000e,	0,		SROM_AABREV, SROM_AA0_MASK},
1240	{"aa2g",	0x000000f0,	0,		SROM4_AA, 0xff},
1241	{"aa2g",	0xffffff00,	0,		SROM8_AA, 0xff},
1242	{"aa5g",	0x0000000e,	0,		SROM_AABREV, SROM_AA1_MASK},
1243	{"aa5g",	0x000000f0,	0,		SROM4_AA, 0xff00},
1244	{"aa5g",	0xffffff00,	0,		SROM8_AA, 0xff00},
1245	{"ag0",		0x0000000e,	0,		SROM_AG10, 0xff},
1246	{"ag1",		0x0000000e,	0,		SROM_AG10, 0xff00},
1247	{"ag0",		0x000000f0,	0,		SROM4_AG10, 0xff},
1248	{"ag1",		0x000000f0,	0,		SROM4_AG10, 0xff00},
1249	{"ag2",		0x000000f0,	0,		SROM4_AG32, 0xff},
1250	{"ag3",		0x000000f0,	0,		SROM4_AG32, 0xff00},
1251	{"ag0",		0xffffff00,	0,		SROM8_AG10, 0xff},
1252	{"ag1",		0xffffff00,	0,		SROM8_AG10, 0xff00},
1253	{"ag2",		0xffffff00,	0,		SROM8_AG32, 0xff},
1254	{"ag3",		0xffffff00,	0,		SROM8_AG32, 0xff00},
1255	{"pa1b0",	0x0000000e,	SRFL_PRHEX,	SROM_WL1PAB0, 0xffff},
1256	{"pa1b1",	0x0000000e,	SRFL_PRHEX,	SROM_WL1PAB1, 0xffff},
1257	{"pa1b2",	0x0000000e,	SRFL_PRHEX,	SROM_WL1PAB2, 0xffff},
1258	{"pa1lob0",	0x0000000c,	SRFL_PRHEX,	SROM_WL1LPAB0, 0xffff},
1259	{"pa1lob1",	0x0000000c,	SRFL_PRHEX,	SROM_WL1LPAB1, 0xffff},
1260	{"pa1lob2",	0x0000000c,	SRFL_PRHEX,	SROM_WL1LPAB2, 0xffff},
1261	{"pa1hib0",	0x0000000c,	SRFL_PRHEX,	SROM_WL1HPAB0, 0xffff},
1262	{"pa1hib1",	0x0000000c,	SRFL_PRHEX,	SROM_WL1HPAB1, 0xffff},
1263	{"pa1hib2",	0x0000000c,	SRFL_PRHEX,	SROM_WL1HPAB2, 0xffff},
1264	{"pa1itssit",	0x0000000e,	0,		SROM_ITT, 0xff00},
1265	{"pa1maxpwr",	0x0000000e,	0,		SROM_WL10MAXP, 0xff00},
1266	{"pa1lomaxpwr",	0x0000000c,	0,		SROM_WL1LHMAXP, 0xff00},
1267	{"pa1himaxpwr",	0x0000000c,	0,		SROM_WL1LHMAXP, 0xff},
1268	{"pa1b0",	0xffffff00,	SRFL_PRHEX,	SROM8_W1_PAB0, 0xffff},
1269	{"pa1b1",	0xffffff00,	SRFL_PRHEX,	SROM8_W1_PAB1, 0xffff},
1270	{"pa1b2",	0xffffff00,	SRFL_PRHEX,	SROM8_W1_PAB2, 0xffff},
1271	{"pa1lob0",	0xffffff00,	SRFL_PRHEX,	SROM8_W1_PAB0_LC, 0xffff},
1272	{"pa1lob1",	0xffffff00,	SRFL_PRHEX,	SROM8_W1_PAB1_LC, 0xffff},
1273	{"pa1lob2",	0xffffff00,	SRFL_PRHEX,	SROM8_W1_PAB2_LC, 0xffff},
1274	{"pa1hib0",	0xffffff00,	SRFL_PRHEX,	SROM8_W1_PAB0_HC, 0xffff},
1275	{"pa1hib1",	0xffffff00,	SRFL_PRHEX,	SROM8_W1_PAB1_HC, 0xffff},
1276	{"pa1hib2",	0xffffff00,	SRFL_PRHEX,	SROM8_W1_PAB2_HC, 0xffff},
1277	{"pa1itssit",	0xffffff00,	0,		SROM8_W1_ITTMAXP, 0xff00},
1278	{"pa1maxpwr",	0xffffff00,	0,		SROM8_W1_ITTMAXP, 0xff},
1279	{"pa1lomaxpwr",	0xffffff00,	0,		SROM8_W1_MAXP_LCHC, 0xff00},
1280	{"pa1himaxpwr",	0xffffff00,	0,		SROM8_W1_MAXP_LCHC, 0xff},
1281	{"bxa2g",	0x00000008,	0,		SROM_BXARSSI2G, 0x1800},
1282	{"rssisav2g",	0x00000008,	0,		SROM_BXARSSI2G, 0x0700},
1283	{"rssismc2g",	0x00000008,	0,		SROM_BXARSSI2G, 0x00f0},
1284	{"rssismf2g",	0x00000008,	0,		SROM_BXARSSI2G, 0x000f},
1285	{"bxa2g",	0xffffff00,	0,		SROM8_BXARSSI2G, 0x1800},
1286	{"rssisav2g",	0xffffff00,	0,		SROM8_BXARSSI2G, 0x0700},
1287	{"rssismc2g",	0xffffff00,	0,		SROM8_BXARSSI2G, 0x00f0},
1288	{"rssismf2g",	0xffffff00,	0,		SROM8_BXARSSI2G, 0x000f},
1289	{"bxa5g",	0x00000008,	0,		SROM_BXARSSI5G, 0x1800},
1290	{"rssisav5g",	0x00000008,	0,		SROM_BXARSSI5G, 0x0700},
1291	{"rssismc5g",	0x00000008,	0,		SROM_BXARSSI5G, 0x00f0},
1292	{"rssismf5g",	0x00000008,	0,		SROM_BXARSSI5G, 0x000f},
1293	{"bxa5g",	0xffffff00,	0,		SROM8_BXARSSI5G, 0x1800},
1294	{"rssisav5g",	0xffffff00,	0,		SROM8_BXARSSI5G, 0x0700},
1295	{"rssismc5g",	0xffffff00,	0,		SROM8_BXARSSI5G, 0x00f0},
1296	{"rssismf5g",	0xffffff00,	0,		SROM8_BXARSSI5G, 0x000f},
1297	{"tri2g",	0x00000008,	0,		SROM_TRI52G, 0xff},
1298	{"tri5g",	0x00000008,	0,		SROM_TRI52G, 0xff00},
1299	{"tri5gl",	0x00000008,	0,		SROM_TRI5GHL, 0xff},
1300	{"tri5gh",	0x00000008,	0,		SROM_TRI5GHL, 0xff00},
1301	{"tri2g",	0xffffff00,	0,		SROM8_TRI52G, 0xff},
1302	{"tri5g",	0xffffff00,	0,		SROM8_TRI52G, 0xff00},
1303	{"tri5gl",	0xffffff00,	0,		SROM8_TRI5GHL, 0xff},
1304	{"tri5gh",	0xffffff00,	0,		SROM8_TRI5GHL, 0xff00},
1305	{"rxpo2g",	0x00000008,	SRFL_PRSIGN,	SROM_RXPO52G, 0xff},
1306	{"rxpo5g",	0x00000008,	SRFL_PRSIGN,	SROM_RXPO52G, 0xff00},
1307	{"rxpo2g",	0xffffff00,	SRFL_PRSIGN,	SROM8_RXPO52G, 0xff},
1308	{"rxpo5g",	0xffffff00,	SRFL_PRSIGN,	SROM8_RXPO52G, 0xff00},
1309	{"txchain",	0x000000f0,	SRFL_NOFFS,	SROM4_TXRXC, SROM4_TXCHAIN_MASK},
1310	{"rxchain",	0x000000f0,	SRFL_NOFFS,	SROM4_TXRXC, SROM4_RXCHAIN_MASK},
1311	{"antswitch",	0x000000f0,	SRFL_NOFFS,	SROM4_TXRXC, SROM4_SWITCH_MASK},
1312	{"txchain",	0xffffff00,	SRFL_NOFFS,	SROM8_TXRXC, SROM4_TXCHAIN_MASK},
1313	{"rxchain",	0xffffff00,	SRFL_NOFFS,	SROM8_TXRXC, SROM4_RXCHAIN_MASK},
1314	{"antswitch",	0xffffff00,	SRFL_NOFFS,	SROM8_TXRXC, SROM4_SWITCH_MASK},
1315	{"txpid2ga0",	0x000000f0,	0,		SROM4_TXPID2G, 0xff},
1316	{"txpid2ga1",	0x000000f0,	0,		SROM4_TXPID2G, 0xff00},
1317	{"txpid2ga2",	0x000000f0,	0,		SROM4_TXPID2G + 1, 0xff},
1318	{"txpid2ga3",	0x000000f0,	0,		SROM4_TXPID2G + 1, 0xff00},
1319	{"txpid5ga0",	0x000000f0,	0,		SROM4_TXPID5G, 0xff},
1320	{"txpid5ga1",	0x000000f0,	0,		SROM4_TXPID5G, 0xff00},
1321	{"txpid5ga2",	0x000000f0,	0,		SROM4_TXPID5G + 1, 0xff},
1322	{"txpid5ga3",	0x000000f0,	0,		SROM4_TXPID5G + 1, 0xff00},
1323	{"txpid5gla0",	0x000000f0,	0,		SROM4_TXPID5GL, 0xff},
1324	{"txpid5gla1",	0x000000f0,	0,		SROM4_TXPID5GL, 0xff00},
1325	{"txpid5gla2",	0x000000f0,	0,		SROM4_TXPID5GL + 1, 0xff},
1326	{"txpid5gla3",	0x000000f0,	0,		SROM4_TXPID5GL + 1, 0xff00},
1327	{"txpid5gha0",	0x000000f0,	0,		SROM4_TXPID5GH, 0xff},
1328	{"txpid5gha1",	0x000000f0,	0,		SROM4_TXPID5GH, 0xff00},
1329	{"txpid5gha2",	0x000000f0,	0,		SROM4_TXPID5GH + 1, 0xff},
1330	{"txpid5gha3",	0x000000f0,	0,		SROM4_TXPID5GH + 1, 0xff00},
1331	{"cck2gpo",	0x000000f0,	0,		SROM4_2G_CCKPO, 0xffff},
1332	{"cck2gpo",	0xffffff00,	0,		SROM8_2G_CCKPO, 0xffff},
1333	{"ofdm2gpo",	0x000000f0,	SRFL_MORE,	SROM4_2G_OFDMPO, 0xffff},
1334	{"",		0,		0,		SROM4_2G_OFDMPO + 1, 0xffff},
1335	{"ofdm5gpo",	0x000000f0,	SRFL_MORE,	SROM4_5G_OFDMPO, 0xffff},
1336	{"",		0,		0,		SROM4_5G_OFDMPO + 1, 0xffff},
1337	{"ofdm5glpo",	0x000000f0,	SRFL_MORE,	SROM4_5GL_OFDMPO, 0xffff},
1338	{"",		0,		0,		SROM4_5GL_OFDMPO + 1, 0xffff},
1339	{"ofdm5ghpo",	0x000000f0,	SRFL_MORE,	SROM4_5GH_OFDMPO, 0xffff},
1340	{"",		0,		0,		SROM4_5GH_OFDMPO + 1, 0xffff},
1341	{"ofdm2gpo",	0xffffff00,	SRFL_MORE,	SROM8_2G_OFDMPO, 0xffff},
1342	{"",		0,		0,		SROM8_2G_OFDMPO + 1, 0xffff},
1343	{"ofdm5gpo",	0xffffff00,	SRFL_MORE,	SROM8_5G_OFDMPO, 0xffff},
1344	{"",		0,		0,		SROM8_5G_OFDMPO + 1, 0xffff},
1345	{"ofdm5glpo",	0xffffff00,	SRFL_MORE,	SROM8_5GL_OFDMPO, 0xffff},
1346	{"",		0,		0,		SROM8_5GL_OFDMPO + 1, 0xffff},
1347	{"ofdm5ghpo",	0xffffff00,	SRFL_MORE,	SROM8_5GH_OFDMPO, 0xffff},
1348	{"",		0,		0,		SROM8_5GH_OFDMPO + 1, 0xffff},
1349	{"mcs2gpo0",	0x000000f0,	0,		SROM4_2G_MCSPO, 0xffff},
1350	{"mcs2gpo1",	0x000000f0,	0,		SROM4_2G_MCSPO + 1, 0xffff},
1351	{"mcs2gpo2",	0x000000f0,	0,		SROM4_2G_MCSPO + 2, 0xffff},
1352	{"mcs2gpo3",	0x000000f0,	0,		SROM4_2G_MCSPO + 3, 0xffff},
1353	{"mcs2gpo4",	0x000000f0,	0,		SROM4_2G_MCSPO + 4, 0xffff},
1354	{"mcs2gpo5",	0x000000f0,	0,		SROM4_2G_MCSPO + 5, 0xffff},
1355	{"mcs2gpo6",	0x000000f0,	0,		SROM4_2G_MCSPO + 6, 0xffff},
1356	{"mcs2gpo7",	0x000000f0,	0,		SROM4_2G_MCSPO + 7, 0xffff},
1357	{"mcs5gpo0",	0x000000f0,	0,		SROM4_5G_MCSPO, 0xffff},
1358	{"mcs5gpo1",	0x000000f0,	0,		SROM4_5G_MCSPO + 1, 0xffff},
1359	{"mcs5gpo2",	0x000000f0,	0,		SROM4_5G_MCSPO + 2, 0xffff},
1360	{"mcs5gpo3",	0x000000f0,	0,		SROM4_5G_MCSPO + 3, 0xffff},
1361	{"mcs5gpo4",	0x000000f0,	0,		SROM4_5G_MCSPO + 4, 0xffff},
1362	{"mcs5gpo5",	0x000000f0,	0,		SROM4_5G_MCSPO + 5, 0xffff},
1363	{"mcs5gpo6",	0x000000f0,	0,		SROM4_5G_MCSPO + 6, 0xffff},
1364	{"mcs5gpo7",	0x000000f0,	0,		SROM4_5G_MCSPO + 7, 0xffff},
1365	{"mcs5glpo0",	0x000000f0,	0,		SROM4_5GL_MCSPO, 0xffff},
1366	{"mcs5glpo1",	0x000000f0,	0,		SROM4_5GL_MCSPO + 1, 0xffff},
1367	{"mcs5glpo2",	0x000000f0,	0,		SROM4_5GL_MCSPO + 2, 0xffff},
1368	{"mcs5glpo3",	0x000000f0,	0,		SROM4_5GL_MCSPO + 3, 0xffff},
1369	{"mcs5glpo4",	0x000000f0,	0,		SROM4_5GL_MCSPO + 4, 0xffff},
1370	{"mcs5glpo5",	0x000000f0,	0,		SROM4_5GL_MCSPO + 5, 0xffff},
1371	{"mcs5glpo6",	0x000000f0,	0,		SROM4_5GL_MCSPO + 6, 0xffff},
1372	{"mcs5glpo7",	0x000000f0,	0,		SROM4_5GL_MCSPO + 7, 0xffff},
1373	{"mcs5ghpo0",	0x000000f0,	0,		SROM4_5GH_MCSPO, 0xffff},
1374	{"mcs5ghpo1",	0x000000f0,	0,		SROM4_5GH_MCSPO + 1, 0xffff},
1375	{"mcs5ghpo2",	0x000000f0,	0,		SROM4_5GH_MCSPO + 2, 0xffff},
1376	{"mcs5ghpo3",	0x000000f0,	0,		SROM4_5GH_MCSPO + 3, 0xffff},
1377	{"mcs5ghpo4",	0x000000f0,	0,		SROM4_5GH_MCSPO + 4, 0xffff},
1378	{"mcs5ghpo5",	0x000000f0,	0,		SROM4_5GH_MCSPO + 5, 0xffff},
1379	{"mcs5ghpo6",	0x000000f0,	0,		SROM4_5GH_MCSPO + 6, 0xffff},
1380	{"mcs5ghpo7",	0x000000f0,	0,		SROM4_5GH_MCSPO + 7, 0xffff},
1381	{"mcs2gpo0",	0xffffff00,	0,		SROM8_2G_MCSPO, 0xffff},
1382	{"mcs2gpo1",	0xffffff00,	0,		SROM8_2G_MCSPO + 1, 0xffff},
1383	{"mcs2gpo2",	0xffffff00,	0,		SROM8_2G_MCSPO + 2, 0xffff},
1384	{"mcs2gpo3",	0xffffff00,	0,		SROM8_2G_MCSPO + 3, 0xffff},
1385	{"mcs2gpo4",	0xffffff00,	0,		SROM8_2G_MCSPO + 4, 0xffff},
1386	{"mcs2gpo5",	0xffffff00,	0,		SROM8_2G_MCSPO + 5, 0xffff},
1387	{"mcs2gpo6",	0xffffff00,	0,		SROM8_2G_MCSPO + 6, 0xffff},
1388	{"mcs2gpo7",	0xffffff00,	0,		SROM8_2G_MCSPO + 7, 0xffff},
1389	{"mcs5gpo0",	0xffffff00,	0,		SROM8_5G_MCSPO, 0xffff},
1390	{"mcs5gpo1",	0xffffff00,	0,		SROM8_5G_MCSPO + 1, 0xffff},
1391	{"mcs5gpo2",	0xffffff00,	0,		SROM8_5G_MCSPO + 2, 0xffff},
1392	{"mcs5gpo3",	0xffffff00,	0,		SROM8_5G_MCSPO + 3, 0xffff},
1393	{"mcs5gpo4",	0xffffff00,	0,		SROM8_5G_MCSPO + 4, 0xffff},
1394	{"mcs5gpo5",	0xffffff00,	0,		SROM8_5G_MCSPO + 5, 0xffff},
1395	{"mcs5gpo6",	0xffffff00,	0,		SROM8_5G_MCSPO + 6, 0xffff},
1396	{"mcs5gpo7",	0xffffff00,	0,		SROM8_5G_MCSPO + 7, 0xffff},
1397	{"mcs5glpo0",	0xffffff00,	0,		SROM8_5GL_MCSPO, 0xffff},
1398	{"mcs5glpo1",	0xffffff00,	0,		SROM8_5GL_MCSPO + 1, 0xffff},
1399	{"mcs5glpo2",	0xffffff00,	0,		SROM8_5GL_MCSPO + 2, 0xffff},
1400	{"mcs5glpo3",	0xffffff00,	0,		SROM8_5GL_MCSPO + 3, 0xffff},
1401	{"mcs5glpo4",	0xffffff00,	0,		SROM8_5GL_MCSPO + 4, 0xffff},
1402	{"mcs5glpo5",	0xffffff00,	0,		SROM8_5GL_MCSPO + 5, 0xffff},
1403	{"mcs5glpo6",	0xffffff00,	0,		SROM8_5GL_MCSPO + 6, 0xffff},
1404	{"mcs5glpo7",	0xffffff00,	0,		SROM8_5GL_MCSPO + 7, 0xffff},
1405	{"mcs5ghpo0",	0xffffff00,	0,		SROM8_5GH_MCSPO, 0xffff},
1406	{"mcs5ghpo1",	0xffffff00,	0,		SROM8_5GH_MCSPO + 1, 0xffff},
1407	{"mcs5ghpo2",	0xffffff00,	0,		SROM8_5GH_MCSPO + 2, 0xffff},
1408	{"mcs5ghpo3",	0xffffff00,	0,		SROM8_5GH_MCSPO + 3, 0xffff},
1409	{"mcs5ghpo4",	0xffffff00,	0,		SROM8_5GH_MCSPO + 4, 0xffff},
1410	{"mcs5ghpo5",	0xffffff00,	0,		SROM8_5GH_MCSPO + 5, 0xffff},
1411	{"mcs5ghpo6",	0xffffff00,	0,		SROM8_5GH_MCSPO + 6, 0xffff},
1412	{"mcs5ghpo7",	0xffffff00,	0,		SROM8_5GH_MCSPO + 7, 0xffff},
1413	{"cddpo",	0x000000f0,	0,		SROM4_CDDPO, 0xffff},
1414	{"stbcpo",	0x000000f0,	0,		SROM4_STBCPO, 0xffff},
1415	{"bw40po",	0x000000f0,	0,		SROM4_BW40PO, 0xffff},
1416	{"bwduppo",	0x000000f0,	0,		SROM4_BWDUPPO, 0xffff},
1417	{"cddpo",	0xffffff00,	0,		SROM8_CDDPO, 0xffff},
1418	{"stbcpo",	0xffffff00,	0,		SROM8_STBCPO, 0xffff},
1419	{"bw40po",	0xffffff00,	0,		SROM8_BW40PO, 0xffff},
1420	{"bwduppo",	0xffffff00,	0,		SROM8_BWDUPPO, 0xffff},
1421	{"ccode",	0x0000000f,	SRFL_CCODE,	SROM_CCODE, 0xffff},
1422	{"ccode",	0x00000010,	SRFL_CCODE,	SROM4_CCODE, 0xffff},
1423	{"ccode",	0x000000e0,	SRFL_CCODE,	SROM5_CCODE, 0xffff},
1424	{"ccode",	0xffffff00,	SRFL_CCODE,	SROM8_CCODE, 0xffff},
1425	{"macaddr",	0xffffff00,	SRFL_ETHADDR,	SROM8_MACHI, 0xffff},
1426	{"macaddr",	0x000000e0,	SRFL_ETHADDR,	SROM5_MACHI, 0xffff},
1427	{"macaddr",	0x00000010,	SRFL_ETHADDR,	SROM4_MACHI, 0xffff},
1428	{"macaddr",	0x00000008,	SRFL_ETHADDR,	SROM3_MACHI, 0xffff},
1429	{"il0macaddr",	0x00000007,	SRFL_ETHADDR,	SROM_MACHI_IL0, 0xffff},
1430	{"et1macaddr",	0x00000007,	SRFL_ETHADDR,	SROM_MACHI_ET1, 0xffff},
1431	{"leddc",	0xffffff00,	SRFL_NOFFS|SRFL_LEDDC,	SROM8_LEDDC, 0xffff},
1432	{"leddc",	0x000000e0,	SRFL_NOFFS|SRFL_LEDDC,	SROM5_LEDDC, 0xffff},
1433	{"leddc",	0x00000010,	SRFL_NOFFS|SRFL_LEDDC,	SROM4_LEDDC, 0xffff},
1434	{"leddc",	0x00000008,	SRFL_NOFFS|SRFL_LEDDC,	SROM3_LEDDC, 0xffff},
1435	{NULL,		0,		0,		0, 0}
1436};
1437
1438static const sromvar_t perpath_pci_sromvars[] = {
1439	{"maxp2ga",	0x000000f0,	0,		SROM4_2G_ITT_MAXP, 0xff},
1440	{"itt2ga",	0x000000f0,	0,		SROM4_2G_ITT_MAXP, 0xff00},
1441	{"itt5ga",	0x000000f0,	0,		SROM4_5G_ITT_MAXP, 0xff00},
1442	{"pa2gw0a",	0x000000f0,	SRFL_PRHEX,	SROM4_2G_PA, 0xffff},
1443	{"pa2gw1a",	0x000000f0,	SRFL_PRHEX,	SROM4_2G_PA + 1, 0xffff},
1444	{"pa2gw2a",	0x000000f0,	SRFL_PRHEX,	SROM4_2G_PA + 2, 0xffff},
1445	{"pa2gw3a",	0x000000f0,	SRFL_PRHEX,	SROM4_2G_PA + 3, 0xffff},
1446	{"maxp5ga",	0x000000f0,	0,		SROM4_5G_ITT_MAXP, 0xff},
1447	{"maxp5gha",	0x000000f0,	0,		SROM4_5GLH_MAXP, 0xff},
1448	{"maxp5gla",	0x000000f0,	0,		SROM4_5GLH_MAXP, 0xff00},
1449	{"pa5gw0a",	0x000000f0,	SRFL_PRHEX,	SROM4_5G_PA, 0xffff},
1450	{"pa5gw1a",	0x000000f0,	SRFL_PRHEX,	SROM4_5G_PA + 1, 0xffff},
1451	{"pa5gw2a",	0x000000f0,	SRFL_PRHEX,	SROM4_5G_PA + 2, 0xffff},
1452	{"pa5gw3a",	0x000000f0,	SRFL_PRHEX,	SROM4_5G_PA + 3, 0xffff},
1453	{"pa5glw0a",	0x000000f0,	SRFL_PRHEX,	SROM4_5GL_PA, 0xffff},
1454	{"pa5glw1a",	0x000000f0,	SRFL_PRHEX,	SROM4_5GL_PA + 1, 0xffff},
1455	{"pa5glw2a",	0x000000f0,	SRFL_PRHEX,	SROM4_5GL_PA + 2, 0xffff},
1456	{"pa5glw3a",	0x000000f0,	SRFL_PRHEX,	SROM4_5GL_PA + 3, 0xffff},
1457	{"pa5ghw0a",	0x000000f0,	SRFL_PRHEX,	SROM4_5GH_PA, 0xffff},
1458	{"pa5ghw1a",	0x000000f0,	SRFL_PRHEX,	SROM4_5GH_PA + 1, 0xffff},
1459	{"pa5ghw2a",	0x000000f0,	SRFL_PRHEX,	SROM4_5GH_PA + 2, 0xffff},
1460	{"pa5ghw3a",	0x000000f0,	SRFL_PRHEX,	SROM4_5GH_PA + 3, 0xffff},
1461	{"maxp2ga",	0xffffff00,	0,		SROM8_2G_ITT_MAXP, 0xff},
1462	{"itt2ga",	0xffffff00,	0,		SROM8_2G_ITT_MAXP, 0xff00},
1463	{"itt5ga",	0xffffff00,	0,		SROM8_5G_ITT_MAXP, 0xff00},
1464	{"pa2gw0a",	0xffffff00,	SRFL_PRHEX,	SROM8_2G_PA, 0xffff},
1465	{"pa2gw1a",	0xffffff00,	SRFL_PRHEX,	SROM8_2G_PA + 1, 0xffff},
1466	{"pa2gw2a",	0xffffff00,	SRFL_PRHEX,	SROM8_2G_PA + 2, 0xffff},
1467	{"maxp5ga",	0xffffff00,	0,		SROM8_5G_ITT_MAXP, 0xff},
1468	{"maxp5gha",	0xffffff00,	0,		SROM8_5GLH_MAXP, 0xff},
1469	{"maxp5gla",	0xffffff00,	0,		SROM8_5GLH_MAXP, 0xff00},
1470	{"pa5gw0a",	0xffffff00,	SRFL_PRHEX,	SROM8_5G_PA, 0xffff},
1471	{"pa5gw1a",	0xffffff00,	SRFL_PRHEX,	SROM8_5G_PA + 1, 0xffff},
1472	{"pa5gw2a",	0xffffff00,	SRFL_PRHEX,	SROM8_5G_PA + 2, 0xffff},
1473	{"pa5glw0a",	0xffffff00,	SRFL_PRHEX,	SROM8_5GL_PA, 0xffff},
1474	{"pa5glw1a",	0xffffff00,	SRFL_PRHEX,	SROM8_5GL_PA + 1, 0xffff},
1475	{"pa5glw2a",	0xffffff00,	SRFL_PRHEX,	SROM8_5GL_PA + 2, 0xffff},
1476	{"pa5ghw0a",	0xffffff00,	SRFL_PRHEX,	SROM8_5GH_PA, 0xffff},
1477	{"pa5ghw1a",	0xffffff00,	SRFL_PRHEX,	SROM8_5GH_PA + 1, 0xffff},
1478	{"pa5ghw2a",	0xffffff00,	SRFL_PRHEX,	SROM8_5GH_PA + 2, 0xffff},
1479	{NULL,		0,		0,		0, 0}
1480};
1481
1482/* Parse SROM and create name=value pairs. 'srom' points to
1483 * the SROM word array. 'off' specifies the offset of the
1484 * first word 'srom' points to, which should be either 0 or
1485 * SROM3_SWRG_OFF (full SROM or software region).
1486 */
1487
1488static uint
1489mask_shift(uint16 mask)
1490{
1491	uint i;
1492	for (i = 0; i < (sizeof(mask) << 3); i ++) {
1493		if (mask & (1 << i))
1494			return i;
1495	}
1496	ASSERT(mask);
1497	return 0;
1498}
1499
1500static uint
1501mask_width(uint16 mask)
1502{
1503	int i;
1504	for (i = (sizeof(mask) << 3) - 1; i >= 0; i --) {
1505		if (mask & (1 << i))
1506			return (uint)(i - mask_shift(mask) + 1);
1507	}
1508	ASSERT(mask);
1509	return 0;
1510}
1511
1512#ifdef BCMDBG_ASSERT
1513static bool
1514mask_valid(uint16 mask)
1515{
1516	uint shift = mask_shift(mask);
1517	uint width = mask_width(mask);
1518	return mask == ((~0 << shift) & ~(~0 << (shift + width)));
1519}
1520#endif
1521
1522static void
1523_initvars_srom_pci(uint8 sromrev, uint16 *srom, uint off, varbuf_t *b)
1524{
1525	uint16 w;
1526	uint32 val;
1527	const sromvar_t *srv;
1528	uint width;
1529	uint flags;
1530	uint32 sr = (1 << sromrev);
1531
1532	varbuf_append(b, "sromrev=%d", sromrev);
1533
1534	for (srv = pci_sromvars; srv->name != NULL; srv ++) {
1535		const char *name;
1536
1537		if ((srv->revmask & sr) == 0)
1538			continue;
1539
1540		if (srv->off < off)
1541			continue;
1542
1543		flags = srv->flags;
1544		name = srv->name;
1545
1546		if (flags & SRFL_ETHADDR) {
1547			char eabuf[ETHER_ADDR_STR_LEN];
1548			struct ether_addr ea;
1549
1550			ea.octet[0] = (srom[srv->off - off] >> 8) & 0xff;
1551			ea.octet[1] = srom[srv->off - off] & 0xff;
1552			ea.octet[2] = (srom[srv->off + 1 - off] >> 8) & 0xff;
1553			ea.octet[3] = srom[srv->off + 1 - off] & 0xff;
1554			ea.octet[4] = (srom[srv->off + 2 - off] >> 8) & 0xff;
1555			ea.octet[5] = srom[srv->off + 2 - off] & 0xff;
1556			bcm_ether_ntoa(&ea, eabuf);
1557
1558			varbuf_append(b, "%s=%s", name, eabuf);
1559		}
1560		else {
1561			ASSERT(mask_valid(srv->mask));
1562			ASSERT(mask_width(srv->mask));
1563
1564			w = srom[srv->off - off];
1565			val = (w & srv->mask) >> mask_shift(srv->mask);
1566			width = mask_width(srv->mask);
1567
1568			while (srv->flags & SRFL_MORE) {
1569				srv ++;
1570				ASSERT(srv->name);
1571
1572				if (srv->off == 0 || srv->off < off)
1573					continue;
1574
1575				ASSERT(mask_valid(srv->mask));
1576				ASSERT(mask_width(srv->mask));
1577
1578				w = srom[srv->off - off];
1579				val += ((w & srv->mask) >> mask_shift(srv->mask)) << width;
1580				width += mask_width(srv->mask);
1581			}
1582
1583			if ((flags & SRFL_NOFFS) && ((int)val == (1 << width) - 1))
1584				continue;
1585
1586			if (flags & SRFL_CCODE) {
1587				if (val == 0)
1588					varbuf_append(b, "ccode=");
1589				else
1590					varbuf_append(b, "ccode=%c%c", (val >> 8), (val & 0xff));
1591			}
1592			/* LED Powersave duty cycle has to be scaled:
1593			 *(oncount >> 24) (offcount >> 8)
1594			 */
1595			else if (flags & SRFL_LEDDC) {
1596				uint32 w32 = (((val >> 8) & 0xff) << 24) | /* oncount */
1597					     (((val & 0xff)) << 8); /* offcount */
1598				varbuf_append(b, "leddc=%d", w32);
1599			}
1600			else if (flags & SRFL_PRHEX)
1601				varbuf_append(b, "%s=0x%x", name, val);
1602			else if ((flags & SRFL_PRSIGN) && (val & (1 << (width - 1))))
1603				varbuf_append(b, "%s=%d", name, (int)(val | (~0 << width)));
1604			else
1605				varbuf_append(b, "%s=%u", name, val);
1606		}
1607	}
1608
1609	if (sromrev >= 4) {
1610		/* Do per-path variables */
1611		uint p, pb, psz;
1612
1613		if (sromrev >= 8) {
1614			pb = SROM8_PATH0;
1615			psz = SROM8_PATH1 - SROM8_PATH0;
1616		} else {
1617			pb = SROM4_PATH0;
1618			psz = SROM4_PATH1 - SROM4_PATH0;
1619		}
1620
1621		for (p = 0; p < MAX_PATH; p++) {
1622			for (srv = perpath_pci_sromvars; srv->name != NULL; srv ++) {
1623				if ((srv->revmask & sr) == 0)
1624					continue;
1625
1626				if (pb + srv->off < off)
1627					continue;
1628
1629				w = srom[pb + srv->off - off];
1630				ASSERT(mask_valid(srv->mask));
1631				val = (w & srv->mask) >> mask_shift(srv->mask);
1632				width = mask_width(srv->mask);
1633
1634				/* Cheating: no per-path var is more than 1 word */
1635
1636				if ((srv->flags & SRFL_NOFFS) && ((int)val == (1 << width) - 1))
1637					continue;
1638
1639				if (srv->flags & SRFL_PRHEX)
1640					varbuf_append(b, "%s%d=0x%x", srv->name, p, val);
1641				else
1642					varbuf_append(b, "%s%d=%d", srv->name, p, val);
1643			}
1644			pb += psz;
1645		}
1646	}
1647}
1648
1649static int
1650initvars_srom_pci(sb_t *sbh, void *curmap, char **vars, uint *count)
1651{
1652	uint16 *srom;
1653	uint8 sromrev = 0;
1654	uint32 sr;
1655	varbuf_t b;
1656	char *vp, *base = NULL;
1657	osl_t *osh = sb_osh(sbh);
1658	bool flash = FALSE;
1659	char *value;
1660	int err;
1661
1662	/*
1663	 * Apply CRC over SROM content regardless SROM is present or not,
1664	 * and use variable <devpath>sromrev's existance in flash to decide
1665	 * if we should return an error when CRC fails or read SROM variables
1666	 * from flash.
1667	 */
1668	srom = MALLOC(osh, SROM_MAX);
1669	ASSERT(srom);
1670	if (!srom)
1671		return -2;
1672
1673	err = sprom_read_pci(osh, (void *)((int8 *)curmap + PCI_BAR0_SPROM_OFFSET), 0, srom,
1674	                     SROM_WORDS, TRUE);
1675
1676	if ((srom[SROM4_SIGN] == SROM4_SIGNATURE) ||
1677	    ((sbh->buscoretype == SB_PCIE) && (sbh->buscorerev >= 6))) {
1678		/* sromrev >= 4, read more */
1679		err = sprom_read_pci(osh, (void *)((int8 *)curmap + PCI_BAR0_SPROM_OFFSET), 0,
1680		                     srom, SROM4_WORDS, TRUE);
1681		sromrev = srom[SROM4_CRCREV] & 0xff;
1682	} else if (err == 0) {
1683		/* srom is good and is rev < 4 */
1684		/* top word of sprom contains version and crc8 */
1685		sromrev = srom[SROM_CRCREV] & 0xff;
1686		/* bcm4401 sroms misprogrammed */
1687		if (sromrev == 0x10)
1688			sromrev = 1;
1689	}
1690
1691	if (err) {
1692#ifdef WLTEST
1693		uint32 val;
1694
1695		BS_ERROR(("SROM Crc Error, so see if we could use a default\n"));
1696		val = OSL_PCI_READ_CONFIG(osh, PCI_SPROM_CONTROL, sizeof(uint32));
1697		if (val & SPROM_OTPIN_USE) {
1698			BS_ERROR(("srom crc failed with OTP, use default vars....\n"));
1699			vp = base = mfgsromvars;
1700			if (sb_chip(sbh) == BCM4311_CHIP_ID) {
1701				const char *devid = "devid=0x4311";
1702				const size_t devid_strlen = strlen(devid);
1703				BS_ERROR(("setting the devid to be 4311\n"));
1704				bcopy(devid, vp, devid_strlen + 1);
1705				vp += devid_strlen + 1;
1706			}
1707			bcopy(defaultsromvars, vp, MFGSROM_DEFVARSLEN);
1708			vp += MFGSROM_DEFVARSLEN;
1709			goto varsdone;
1710		} else {
1711#endif /* WLTEST */
1712			BS_ERROR(("srom crc failed with SPROM....\n"));
1713			if (!(value = sb_getdevpathvar(sbh, "sromrev"))) {
1714				err = -1;
1715				goto errout;
1716			}
1717			sromrev = (uint8)bcm_strtoul(value, NULL, 0);
1718			flash = TRUE;
1719#ifdef WLTEST
1720		}
1721#endif /* WLTEST */
1722	}
1723
1724	/* Bitmask for the sromrev */
1725	sr = 1 << sromrev;
1726
1727	/* srom version check
1728	 * Current valid versions: 1, 2, 3, 4, 5, 8
1729	 */
1730	if ((sr & 0x13e) == 0) {
1731		err = -2;
1732		goto errout;
1733	}
1734
1735	ASSERT(vars);
1736	ASSERT(count);
1737
1738	base = vp = MALLOC(osh, MAXSZ_NVRAM_VARS);
1739	ASSERT(vp);
1740	if (!vp) {
1741		err = -2;
1742		goto errout;
1743	}
1744
1745	/* read variables from flash */
1746	if (flash) {
1747		if ((err = initvars_flash(sbh, osh, &vp, MAXSZ_NVRAM_VARS)))
1748			goto errout;
1749		goto varsdone;
1750	}
1751
1752	varbuf_init(&b, base, MAXSZ_NVRAM_VARS);
1753
1754	/* parse SROM into name=value pairs. */
1755	_initvars_srom_pci(sromrev, srom, 0, &b);
1756
1757	/* final nullbyte terminator */
1758	ASSERT(b.size >= 1);
1759	vp = b.buf;
1760	*vp++ = '\0';
1761
1762	ASSERT((vp - base) <= MAXSZ_NVRAM_VARS);
1763
1764varsdone:
1765	err = initvars_table(osh, base, vp, vars, count);
1766
1767errout:
1768#ifdef WLTEST
1769	if (base && (base != mfgsromvars))
1770#else
1771	if (base)
1772#endif
1773		MFREE(osh, base, MAXSZ_NVRAM_VARS);
1774
1775	MFREE(osh, srom, SROM_MAX);
1776	return err;
1777}
1778
1779/*
1780 * Read the cis and call parsecis to initialize the vars.
1781 * Return 0 on success, nonzero on error.
1782 */
1783static int
1784initvars_cis_pcmcia(sb_t *sbh, osl_t *osh, char **vars, uint *count)
1785{
1786	uint8 *cis = NULL;
1787	int rc;
1788	uint data_sz;
1789
1790	data_sz = (sb_pcmciarev(sbh) == 1) ? (SPROM_SIZE * 2) : CIS_SIZE;
1791
1792	if ((cis = MALLOC(osh, data_sz)) == NULL)
1793		return (-2);
1794
1795	if (sb_pcmciarev(sbh) == 1) {
1796		if (srom_read(sbh, PCMCIA_BUS, (void *)NULL, osh, 0, data_sz, (uint16 *)cis)) {
1797			MFREE(osh, cis, data_sz);
1798			return (-1);
1799		}
1800		/* fix up endianess for 16-bit data vs 8-bit parsing */
1801		htol16_buf((uint16 *)cis, data_sz);
1802	} else
1803		OSL_PCMCIA_READ_ATTR(osh, 0, cis, data_sz);
1804
1805	rc = srom_parsecis(osh, &cis, 1, vars, count);
1806
1807	MFREE(osh, cis, data_sz);
1808
1809	return (rc);
1810}
1811
1812
1813static int
1814BCMINITFN(initvars_srom_sb)(sb_t *sbh, osl_t *osh, void *curmap, char **vars, uint *varsz)
1815{
1816#if defined(BCMUSBDEV)
1817	static bool srvars = FALSE;	/* Use OTP/SPROM as global variables */
1818
1819	int sel = 0;	/* where to read the srom. 0 - nowhere, 1 - otp, 2 - sprom */
1820	uint sz = 0;	/* srom size in bytes */
1821	void *oh = NULL;
1822	int rc = BCME_OK;
1823
1824	/* Bail out if we've dealt with OTP/SPROM before! */
1825	if (srvars)
1826		return 0;
1827
1828#if defined(BCM4328)
1829	if (sbh->chip == BCM4328_CHIP_ID) {
1830		/* Access the SPROM if it is present */
1831		if ((sz = srom_size(sbh, osh)) != 0) {
1832			sz <<= 1;
1833			sel = 2;
1834		}
1835	}
1836#endif
1837#if defined(BCM4325)
1838	if (sbh->chip == BCM4325_CHIP_ID) {
1839		uint32 cst = sbh->chipst & CST4325_SPROM_OTP_SEL_MASK;
1840
1841		/* Access OTP if it is present, powered on, and programmed */
1842		if ((oh = otp_init(sbh)) != NULL && (otp_status(oh) & OTPS_GUP_SW)) {
1843			sz = otp_size(oh);
1844			sel = 1;
1845		}
1846		/* Access the SPROM if it is present and allow to be accessed */
1847		else if ((cst == CST4325_OTP_PWRDN || cst == CST4325_SPROM_SEL) &&
1848		         (sz = srom_size(sbh, osh)) != 0) {
1849			sz <<= 1;
1850			sel = 2;
1851		}
1852	}
1853#endif	/* BCM4325 */
1854
1855	/* Read CIS in OTP/SPROM */
1856	if (sel != 0) {
1857		uint16 *srom;
1858		uint8 *body = NULL;
1859
1860		ASSERT(sz);
1861
1862		/* Allocate memory */
1863		if ((srom = (uint16 *)MALLOC(osh, sz)) == NULL)
1864			return BCME_NOMEM;
1865
1866		/* Read CIS */
1867		switch (sel) {
1868		case 1:
1869			rc = otp_read_region(oh, OTP_SW_RGN, srom, sz);
1870			body = (uint8 *)srom;
1871			break;
1872		case 2:
1873			rc = srom_read(sbh, SB_BUS, curmap, osh, 0, sz, srom);
1874			/* sprom has 8 byte h/w header */
1875			body = (uint8 *)srom + SBSDIO_SPROM_CIS_OFFSET;
1876			break;
1877		default:
1878			/* impossible to come here */
1879			ASSERT(0);
1880			break;
1881		}
1882
1883		/* Parse CIS */
1884		if (rc == BCME_OK) {
1885			uint i, tpls = 0xffffffff;
1886			/* # sdiod fns + common + extra */
1887			uint8 *cis[SBSDIO_NUM_FUNCTION + 2];
1888			uint ciss = 0;
1889
1890			/* each word is in host endian */
1891			htol16_buf((uint8 *)srom, sz);
1892
1893			ASSERT(body);
1894
1895			/* count cis tuple chains */
1896			for (i = 0; i < sz && ciss < ARRAYSIZE(cis) && tpls != 0; i ++) {
1897				cis[ciss++] = &body[i];
1898				for (tpls = 0; i < sz - 1; tpls ++) {
1899					if (body[i++] == CISTPL_END)
1900						break;
1901					i += body[i] + 1;
1902				}
1903			}
1904
1905			/* call parser routine only when there are tuple chains */
1906			if (ciss > 1)
1907				rc = srom_parsecis(osh, cis, ciss, vars, varsz);
1908		}
1909
1910		/* Clean up */
1911		MFREE(osh, srom, sz);
1912
1913		/* Make SROM variables global */
1914		if (rc == BCME_OK) {
1915			rc = nvram_append((void *)sbh, *vars, *varsz);
1916			srvars = TRUE;
1917
1918			/* Tell the caller there is no individual SROM variables */
1919			*vars = NULL;
1920			*varsz = 0;
1921		}
1922	}
1923
1924	return rc;
1925#else	/* !BCMUSBDEV && !BCMSDIODEV */
1926	/* Search flash nvram section for srom variables */
1927	return initvars_flash_sb(sbh, vars, varsz);
1928#endif
1929}
1930
1931#ifdef BCMUSBDEV
1932/* Return sprom size in 16-bit words */
1933static uint
1934srom_size(sb_t *sbh, osl_t *osh)
1935{
1936	uint size = 0;
1937	if (SPROMBUS == PCMCIA_BUS) {
1938		uint32 origidx;
1939		sdpcmd_regs_t *pcmregs;
1940		bool wasup;
1941
1942		origidx = sb_coreidx(sbh);
1943		pcmregs = sb_setcore(sbh, SB_PCMCIA, 0);
1944		ASSERT(pcmregs);
1945
1946		if (!(wasup = sb_iscoreup(sbh)))
1947			sb_core_reset(sbh, 0, 0);
1948
1949		/* not worry about earlier core revs */
1950		if (sb_corerev(sbh) < 8)
1951			goto done;
1952
1953		/* SPROM is accessible only in PCMCIA mode unless there is SDIO clock */
1954		if (!(R_REG(osh, &pcmregs->corestatus) & CS_PCMCIAMODE))
1955			goto done;
1956
1957		switch (SB_PCMCIA_READ(osh, pcmregs, SROM_INFO) & SRI_SZ_MASK) {
1958		case 1:
1959			size = 256;	/* SROM_INFO == 1 means 4kbit */
1960			break;
1961		case 2:
1962			size = 1024;	/* SROM_INFO == 2 means 16kbit */
1963			break;
1964		default:
1965			break;
1966		}
1967
1968	done:
1969		if (!wasup)
1970			sb_core_disable(sbh, 0);
1971
1972		sb_setcoreidx(sbh, origidx);
1973	}
1974	return size;
1975}
1976#endif /* def BCMUSBDEV */
1977