summaryrefslogtreecommitdiffstats
path: root/drivers/staging/brcm80211/util/bcmsrom.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/brcm80211/util/bcmsrom.c')
-rw-r--r--drivers/staging/brcm80211/util/bcmsrom.c2081
1 files changed, 2081 insertions, 0 deletions
diff --git a/drivers/staging/brcm80211/util/bcmsrom.c b/drivers/staging/brcm80211/util/bcmsrom.c
new file mode 100644
index 0000000..d5527f7
--- /dev/null
+++ b/drivers/staging/brcm80211/util/bcmsrom.c
@@ -0,0 +1,2081 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <stdarg.h>
+#include <bcmutils.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <pcicfg.h>
+#include <siutils.h>
+#include <bcmsrom.h>
+#include <bcmsrom_tbl.h>
+#ifdef BCMSDIO
+#include <bcmsdh.h>
+#include <sdio.h>
+#endif
+
+#include <bcmnvram.h>
+#include <bcmotp.h>
+
+#if defined(BCMSDIO)
+#include <sbsdio.h>
+#include <sbhnddma.h>
+#include <sbsdpcmdev.h>
+#endif
+
+#include <proto/ethernet.h> /* for sprom content groking */
+
+#define BS_ERROR(args)
+
+#define SROM_OFFSET(sih) ((sih->ccrev > 31) ? \
+ (((sih->cccaps & CC_CAP_SROM) == 0) ? NULL : \
+ ((uint8 *)curmap + PCI_16KB0_CCREGS_OFFSET + CC_SROM_OTP)) : \
+ ((uint8 *)curmap + PCI_BAR0_SPROM_OFFSET))
+
+#if defined(BCMDBG)
+#define WRITE_ENABLE_DELAY 500 /* 500 ms after write enable/disable toggle */
+#define WRITE_WORD_DELAY 20 /* 20 ms between each word write */
+#endif
+
+typedef struct varbuf {
+ char *base; /* pointer to buffer base */
+ char *buf; /* pointer to current position */
+ unsigned int size; /* current (residual) size in bytes */
+} varbuf_t;
+extern char *_vars;
+extern uint _varsz;
+
+#define SROM_CIS_SINGLE 1
+
+static int initvars_srom_si(si_t * sih, osl_t * osh, void *curmap, char **vars,
+ uint * count);
+static void _initvars_srom_pci(uint8 sromrev, uint16 * srom, uint off,
+ varbuf_t * b);
+static int initvars_srom_pci(si_t * sih, void *curmap, char **vars,
+ uint * count);
+static int initvars_flash_si(si_t * sih, char **vars, uint * count);
+#ifdef BCMSDIO
+static int initvars_cis_sdio(osl_t * osh, char **vars, uint * count);
+static int sprom_cmd_sdio(osl_t * osh, uint8 cmd);
+static int sprom_read_sdio(osl_t * osh, uint16 addr, uint16 * data);
+#endif /* BCMSDIO */
+static int sprom_read_pci(osl_t * osh, si_t * sih, uint16 * sprom, uint wordoff,
+ uint16 * buf, uint nwords, bool check_crc);
+#if defined(BCMNVRAMR)
+static int otp_read_pci(osl_t * osh, si_t * sih, uint16 * buf, uint bufsz);
+#endif
+static uint16 srom_cc_cmd(si_t * sih, osl_t * osh, void *ccregs, uint32 cmd,
+ uint wordoff, uint16 data);
+
+static int initvars_table(osl_t * osh, char *start, char *end, char **vars,
+ uint * count);
+static int initvars_flash(si_t * sih, osl_t * osh, char **vp, uint len);
+
+/* Initialization of varbuf structure */
+static void BCMATTACHFN(varbuf_init) (varbuf_t * b, char *buf, uint size) {
+ b->size = size;
+ b->base = b->buf = buf;
+}
+
+/* append a null terminated var=value string */
+static int BCMATTACHFN(varbuf_append) (varbuf_t * b, const char *fmt, ...) {
+ va_list ap;
+ int r;
+ size_t len;
+ char *s;
+
+ if (b->size < 2)
+ return 0;
+
+ va_start(ap, fmt);
+ r = vsnprintf(b->buf, b->size, fmt, ap);
+ va_end(ap);
+
+ /* C99 snprintf behavior returns r >= size on overflow,
+ * others return -1 on overflow.
+ * All return -1 on format error.
+ * We need to leave room for 2 null terminations, one for the current var
+ * string, and one for final null of the var table. So check that the
+ * strlen written, r, leaves room for 2 chars.
+ */
+ if ((r == -1) || (r > (int)(b->size - 2))) {
+ b->size = 0;
+ return 0;
+ }
+
+ /* Remove any earlier occurrence of the same variable */
+ if ((s = strchr(b->buf, '=')) != NULL) {
+ len = (size_t) (s - b->buf);
+ for (s = b->base; s < b->buf;) {
+ if ((bcmp(s, b->buf, len) == 0) && s[len] == '=') {
+ len = strlen(s) + 1;
+ memmove(s, (s + len),
+ ((b->buf + r + 1) - (s + len)));
+ b->buf -= len;
+ b->size += (unsigned int)len;
+ break;
+ }
+
+ while (*s++) ;
+ }
+ }
+
+ /* skip over this string's null termination */
+ r++;
+ b->size -= r;
+ b->buf += r;
+
+ return r;
+}
+
+/*
+ * Initialize local vars from the right source for this platform.
+ * Return 0 on success, nonzero on error.
+ */
+int
+BCMATTACHFN(srom_var_init) (si_t * sih, uint bustype, void *curmap, osl_t * osh,
+ char **vars, uint * count) {
+ uint len;
+
+ len = 0;
+
+ ASSERT(bustype == BUSTYPE(bustype));
+ if (vars == NULL || count == NULL)
+ return (0);
+
+ *vars = NULL;
+ *count = 0;
+
+ switch (BUSTYPE(bustype)) {
+ case SI_BUS:
+ case JTAG_BUS:
+ return initvars_srom_si(sih, osh, curmap, vars, count);
+
+ case PCI_BUS:
+ ASSERT(curmap != NULL);
+ if (curmap == NULL)
+ return (-1);
+
+ return initvars_srom_pci(sih, curmap, vars, count);
+
+#ifdef BCMSDIO
+ case SDIO_BUS:
+ return initvars_cis_sdio(osh, vars, count);
+#endif /* BCMSDIO */
+
+ default:
+ ASSERT(0);
+ }
+ return (-1);
+}
+
+/* support only 16-bit word read from srom */
+int
+srom_read(si_t * sih, uint bustype, void *curmap, osl_t * osh,
+ uint byteoff, uint nbytes, uint16 * buf, bool check_crc)
+{
+ uint off, nw;
+#ifdef BCMSDIO
+ uint i;
+#endif /* BCMSDIO */
+
+ ASSERT(bustype == BUSTYPE(bustype));
+
+ /* check input - 16-bit access only */
+ if (byteoff & 1 || nbytes & 1 || (byteoff + nbytes) > SROM_MAX)
+ return 1;
+
+ off = byteoff / 2;
+ nw = nbytes / 2;
+
+ if (BUSTYPE(bustype) == PCI_BUS) {
+ if (!curmap)
+ return 1;
+
+ if (si_is_sprom_available(sih)) {
+ uint16 *srom;
+
+ srom = (uint16 *) SROM_OFFSET(sih);
+ if (srom == NULL)
+ return 1;
+
+ if (sprom_read_pci
+ (osh, sih, srom, off, buf, nw, check_crc))
+ return 1;
+ }
+#if defined(BCMNVRAMR)
+ else {
+ if (otp_read_pci(osh, sih, buf, SROM_MAX))
+ return 1;
+ }
+#endif
+#ifdef BCMSDIO
+ } else if (BUSTYPE(bustype) == SDIO_BUS) {
+ off = byteoff / 2;
+ nw = nbytes / 2;
+ for (i = 0; i < nw; i++) {
+ if (sprom_read_sdio
+ (osh, (uint16) (off + i), (uint16 *) (buf + i)))
+ return 1;
+ }
+#endif /* BCMSDIO */
+ } else if (BUSTYPE(bustype) == SI_BUS) {
+ return 1;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+static const char BCMATTACHDATA(vstr_manf)[] = "manf=%s";
+static const char BCMATTACHDATA(vstr_productname)[] = "productname=%s";
+static const char BCMATTACHDATA(vstr_manfid)[] = "manfid=0x%x";
+static const char BCMATTACHDATA(vstr_prodid)[] = "prodid=0x%x";
+#ifdef BCMSDIO
+static const char BCMATTACHDATA(vstr_sdmaxspeed)[] = "sdmaxspeed=%d";
+static const char BCMATTACHDATA(vstr_sdmaxblk)[][13] =
+{
+"sdmaxblk0=%d", "sdmaxblk1=%d", "sdmaxblk2=%d"};
+#endif
+static const char BCMATTACHDATA(vstr_regwindowsz)[] = "regwindowsz=%d";
+static const char BCMATTACHDATA(vstr_sromrev)[] = "sromrev=%d";
+static const char BCMATTACHDATA(vstr_chiprev)[] = "chiprev=%d";
+static const char BCMATTACHDATA(vstr_subvendid)[] = "subvendid=0x%x";
+static const char BCMATTACHDATA(vstr_subdevid)[] = "subdevid=0x%x";
+static const char BCMATTACHDATA(vstr_boardrev)[] = "boardrev=0x%x";
+static const char BCMATTACHDATA(vstr_aa2g)[] = "aa2g=0x%x";
+static const char BCMATTACHDATA(vstr_aa5g)[] = "aa5g=0x%x";
+static const char BCMATTACHDATA(vstr_ag)[] = "ag%d=0x%x";
+static const char BCMATTACHDATA(vstr_cc)[] = "cc=%d";
+static const char BCMATTACHDATA(vstr_opo)[] = "opo=%d";
+static const char BCMATTACHDATA(vstr_pa0b)[][9] =
+{
+"pa0b0=%d", "pa0b1=%d", "pa0b2=%d"};
+
+static const char BCMATTACHDATA(vstr_pa0itssit)[] = "pa0itssit=%d";
+static const char BCMATTACHDATA(vstr_pa0maxpwr)[] = "pa0maxpwr=%d";
+static const char BCMATTACHDATA(vstr_pa1b)[][9] =
+{
+"pa1b0=%d", "pa1b1=%d", "pa1b2=%d"};
+
+static const char BCMATTACHDATA(vstr_pa1lob)[][11] =
+{
+"pa1lob0=%d", "pa1lob1=%d", "pa1lob2=%d"};
+
+static const char BCMATTACHDATA(vstr_pa1hib)[][11] =
+{
+"pa1hib0=%d", "pa1hib1=%d", "pa1hib2=%d"};
+
+static const char BCMATTACHDATA(vstr_pa1itssit)[] = "pa1itssit=%d";
+static const char BCMATTACHDATA(vstr_pa1maxpwr)[] = "pa1maxpwr=%d";
+static const char BCMATTACHDATA(vstr_pa1lomaxpwr)[] = "pa1lomaxpwr=%d";
+static const char BCMATTACHDATA(vstr_pa1himaxpwr)[] = "pa1himaxpwr=%d";
+static const char BCMATTACHDATA(vstr_oem)[] =
+ "oem=%02x%02x%02x%02x%02x%02x%02x%02x";
+static const char BCMATTACHDATA(vstr_boardflags)[] = "boardflags=0x%x";
+static const char BCMATTACHDATA(vstr_boardflags2)[] = "boardflags2=0x%x";
+static const char BCMATTACHDATA(vstr_ledbh)[] = "ledbh%d=0x%x";
+static const char BCMATTACHDATA(vstr_noccode)[] = "ccode=0x0";
+static const char BCMATTACHDATA(vstr_ccode)[] = "ccode=%c%c";
+static const char BCMATTACHDATA(vstr_cctl)[] = "cctl=0x%x";
+static const char BCMATTACHDATA(vstr_cckpo)[] = "cckpo=0x%x";
+static const char BCMATTACHDATA(vstr_ofdmpo)[] = "ofdmpo=0x%x";
+static const char BCMATTACHDATA(vstr_rdlid)[] = "rdlid=0x%x";
+static const char BCMATTACHDATA(vstr_rdlrndis)[] = "rdlrndis=%d";
+static const char BCMATTACHDATA(vstr_rdlrwu)[] = "rdlrwu=%d";
+static const char BCMATTACHDATA(vstr_usbfs)[] = "usbfs=%d";
+static const char BCMATTACHDATA(vstr_wpsgpio)[] = "wpsgpio=%d";
+static const char BCMATTACHDATA(vstr_wpsled)[] = "wpsled=%d";
+static const char BCMATTACHDATA(vstr_rdlsn)[] = "rdlsn=%d";
+static const char BCMATTACHDATA(vstr_rssismf2g)[] = "rssismf2g=%d";
+static const char BCMATTACHDATA(vstr_rssismc2g)[] = "rssismc2g=%d";
+static const char BCMATTACHDATA(vstr_rssisav2g)[] = "rssisav2g=%d";
+static const char BCMATTACHDATA(vstr_bxa2g)[] = "bxa2g=%d";
+static const char BCMATTACHDATA(vstr_rssismf5g)[] = "rssismf5g=%d";
+static const char BCMATTACHDATA(vstr_rssismc5g)[] = "rssismc5g=%d";
+static const char BCMATTACHDATA(vstr_rssisav5g)[] = "rssisav5g=%d";
+static const char BCMATTACHDATA(vstr_bxa5g)[] = "bxa5g=%d";
+static const char BCMATTACHDATA(vstr_tri2g)[] = "tri2g=%d";
+static const char BCMATTACHDATA(vstr_tri5gl)[] = "tri5gl=%d";
+static const char BCMATTACHDATA(vstr_tri5g)[] = "tri5g=%d";
+static const char BCMATTACHDATA(vstr_tri5gh)[] = "tri5gh=%d";
+static const char BCMATTACHDATA(vstr_rxpo2g)[] = "rxpo2g=%d";
+static const char BCMATTACHDATA(vstr_rxpo5g)[] = "rxpo5g=%d";
+static const char BCMATTACHDATA(vstr_boardtype)[] = "boardtype=0x%x";
+static const char BCMATTACHDATA(vstr_leddc)[] = "leddc=0x%04x";
+static const char BCMATTACHDATA(vstr_vendid)[] = "vendid=0x%x";
+static const char BCMATTACHDATA(vstr_devid)[] = "devid=0x%x";
+static const char BCMATTACHDATA(vstr_xtalfreq)[] = "xtalfreq=%d";
+static const char BCMATTACHDATA(vstr_txchain)[] = "txchain=0x%x";
+static const char BCMATTACHDATA(vstr_rxchain)[] = "rxchain=0x%x";
+static const char BCMATTACHDATA(vstr_antswitch)[] = "antswitch=0x%x";
+static const char BCMATTACHDATA(vstr_regrev)[] = "regrev=0x%x";
+static const char BCMATTACHDATA(vstr_antswctl2g)[] = "antswctl2g=0x%x";
+static const char BCMATTACHDATA(vstr_triso2g)[] = "triso2g=0x%x";
+static const char BCMATTACHDATA(vstr_pdetrange2g)[] = "pdetrange2g=0x%x";
+static const char BCMATTACHDATA(vstr_extpagain2g)[] = "extpagain2g=0x%x";
+static const char BCMATTACHDATA(vstr_tssipos2g)[] = "tssipos2g=0x%x";
+static const char BCMATTACHDATA(vstr_antswctl5g)[] = "antswctl5g=0x%x";
+static const char BCMATTACHDATA(vstr_triso5g)[] = "triso5g=0x%x";
+static const char BCMATTACHDATA(vstr_pdetrange5g)[] = "pdetrange5g=0x%x";
+static const char BCMATTACHDATA(vstr_extpagain5g)[] = "extpagain5g=0x%x";
+static const char BCMATTACHDATA(vstr_tssipos5g)[] = "tssipos5g=0x%x";
+static const char BCMATTACHDATA(vstr_maxp2ga0)[] = "maxp2ga0=0x%x";
+static const char BCMATTACHDATA(vstr_itt2ga0)[] = "itt2ga0=0x%x";
+static const char BCMATTACHDATA(vstr_pa)[] = "pa%dgw%da%d=0x%x";
+static const char BCMATTACHDATA(vstr_pahl)[] = "pa%dg%cw%da%d=0x%x";
+static const char BCMATTACHDATA(vstr_maxp5ga0)[] = "maxp5ga0=0x%x";
+static const char BCMATTACHDATA(vstr_itt5ga0)[] = "itt5ga0=0x%x";
+static const char BCMATTACHDATA(vstr_maxp5gha0)[] = "maxp5gha0=0x%x";
+static const char BCMATTACHDATA(vstr_maxp5gla0)[] = "maxp5gla0=0x%x";
+static const char BCMATTACHDATA(vstr_maxp2ga1)[] = "maxp2ga1=0x%x";
+static const char BCMATTACHDATA(vstr_itt2ga1)[] = "itt2ga1=0x%x";
+static const char BCMATTACHDATA(vstr_maxp5ga1)[] = "maxp5ga1=0x%x";
+static const char BCMATTACHDATA(vstr_itt5ga1)[] = "itt5ga1=0x%x";
+static const char BCMATTACHDATA(vstr_maxp5gha1)[] = "maxp5gha1=0x%x";
+static const char BCMATTACHDATA(vstr_maxp5gla1)[] = "maxp5gla1=0x%x";
+static const char BCMATTACHDATA(vstr_cck2gpo)[] = "cck2gpo=0x%x";
+static const char BCMATTACHDATA(vstr_ofdm2gpo)[] = "ofdm2gpo=0x%x";
+static const char BCMATTACHDATA(vstr_ofdm5gpo)[] = "ofdm5gpo=0x%x";
+static const char BCMATTACHDATA(vstr_ofdm5glpo)[] = "ofdm5glpo=0x%x";
+static const char BCMATTACHDATA(vstr_ofdm5ghpo)[] = "ofdm5ghpo=0x%x";
+static const char BCMATTACHDATA(vstr_cddpo)[] = "cddpo=0x%x";
+static const char BCMATTACHDATA(vstr_stbcpo)[] = "stbcpo=0x%x";
+static const char BCMATTACHDATA(vstr_bw40po)[] = "bw40po=0x%x";
+static const char BCMATTACHDATA(vstr_bwduppo)[] = "bwduppo=0x%x";
+static const char BCMATTACHDATA(vstr_mcspo)[] = "mcs%dgpo%d=0x%x";
+static const char BCMATTACHDATA(vstr_mcspohl)[] = "mcs%dg%cpo%d=0x%x";
+static const char BCMATTACHDATA(vstr_custom)[] = "customvar%d=0x%x";
+static const char BCMATTACHDATA(vstr_cckdigfilttype)[] = "cckdigfilttype=%d";
+static const char BCMATTACHDATA(vstr_boardnum)[] = "boardnum=%d";
+static const char BCMATTACHDATA(vstr_macaddr)[] = "macaddr=%s";
+static const char BCMATTACHDATA(vstr_usbepnum)[] = "usbepnum=0x%x";
+static const char BCMATTACHDATA(vstr_end)[] = "END\0";
+
+uint8 patch_pair = 0;
+
+/* For dongle HW, accept partial calibration parameters */
+#define BCMDONGLECASE(n)
+
+int
+BCMATTACHFN(srom_parsecis) (osl_t * osh, uint8 * pcis[], uint ciscnt,
+ char **vars, uint * count)
+{
+ char eabuf[32];
+ char *base;
+ varbuf_t b;
+ uint8 *cis, tup, tlen, sromrev = 1;
+ int i, j;
+ bool ag_init = FALSE;
+ uint32 w32;
+ uint funcid;
+ uint cisnum;
+ int32 boardnum;
+ int err;
+ bool standard_cis;
+
+ ASSERT(vars != NULL);
+ ASSERT(count != NULL);
+
+ boardnum = -1;
+
+ base = MALLOC(osh, MAXSZ_NVRAM_VARS);
+ ASSERT(base != NULL);
+ if (!base)
+ return -2;
+
+ varbuf_init(&b, base, MAXSZ_NVRAM_VARS);
+ bzero(base, MAXSZ_NVRAM_VARS);
+ eabuf[0] = '\0';
+ for (cisnum = 0; cisnum < ciscnt; cisnum++) {
+ cis = *pcis++;
+ i = 0;
+ funcid = 0;
+ standard_cis = TRUE;
+ do {
+ if (standard_cis) {
+ tup = cis[i++];
+ if (tup == CISTPL_NULL || tup == CISTPL_END)
+ tlen = 0;
+ else
+ tlen = cis[i++];
+ } else {
+ if (cis[i] == CISTPL_NULL
+ || cis[i] == CISTPL_END) {
+ tlen = 0;
+ tup = cis[i];
+ } else {
+ tlen = cis[i];
+ tup = CISTPL_BRCM_HNBU;
+ }
+ ++i;
+ }
+ if ((i + tlen) >= CIS_SIZE)
+ break;
+
+ switch (tup) {
+ case CISTPL_VERS_1:
+ /* assume the strings are good if the version field checks out */
+ if (((cis[i + 1] << 8) + cis[i]) >= 0x0008) {
+ varbuf_append(&b, vstr_manf,
+ &cis[i + 2]);
+ varbuf_append(&b, vstr_productname,
+ &cis[i + 3 +
+ strlen((char *)
+ &cis[i +
+ 2])]);
+ break;
+ }
+
+ case CISTPL_MANFID:
+ varbuf_append(&b, vstr_manfid,
+ (cis[i + 1] << 8) + cis[i]);
+ varbuf_append(&b, vstr_prodid,
+ (cis[i + 3] << 8) + cis[i + 2]);
+ break;
+
+ case CISTPL_FUNCID:
+ funcid = cis[i];
+ break;
+
+ case CISTPL_FUNCE:
+ switch (funcid) {
+ case CISTPL_FID_SDIO:
+#ifdef BCMSDIO
+ if (cis[i] == 0) {
+ uint8 spd = cis[i + 3];
+ static int base[] = {
+ -1, 10, 12, 13, 15, 20,
+ 25, 30,
+ 35, 40, 45, 50, 55, 60,
+ 70, 80
+ };
+ static int mult[] = {
+ 10, 100, 1000, 10000,
+ -1, -1, -1, -1
+ };
+ ASSERT((mult[spd & 0x7] != -1)
+ &&
+ (base
+ [(spd >> 3) & 0x0f]));
+ varbuf_append(&b,
+ vstr_sdmaxblk[0],
+ (cis[i + 2] << 8)
+ + cis[i + 1]);
+ varbuf_append(&b,
+ vstr_sdmaxspeed,
+ (mult[spd & 0x7] *
+ base[(spd >> 3) &
+ 0x0f]));
+ } else if (cis[i] == 1) {
+ varbuf_append(&b,
+ vstr_sdmaxblk
+ [cisnum],
+ (cis[i + 13] << 8)
+ | cis[i + 12]);
+ }
+#endif /* BCMSDIO */
+ funcid = 0;
+ break;
+ default:
+ /* set macaddr if HNBU_MACADDR not seen yet */
+ if (eabuf[0] == '\0'
+ && cis[i] == LAN_NID
+ && !(ETHER_ISNULLADDR(&cis[i + 2]))
+ && !(ETHER_ISMULTI(&cis[i + 2]))) {
+ ASSERT(cis[i + 1] ==
+ ETHER_ADDR_LEN);
+ bcm_ether_ntoa((struct
+ ether_addr *)
+ &cis[i + 2],
+ eabuf);
+
+ /* set boardnum if HNBU_BOARDNUM not seen yet */
+ if (boardnum == -1)
+ boardnum =
+ (cis[i + 6] << 8) +
+ cis[i + 7];
+ }
+ break;
+ }
+ break;
+
+ case CISTPL_CFTABLE:
+ varbuf_append(&b, vstr_regwindowsz,
+ (cis[i + 7] << 8) | cis[i + 6]);
+ break;
+
+ case CISTPL_BRCM_HNBU:
+ switch (cis[i]) {
+ case HNBU_SROMREV:
+ sromrev = cis[i + 1];
+ varbuf_append(&b, vstr_sromrev,
+ sromrev);
+ break;
+
+ case HNBU_XTALFREQ:
+ varbuf_append(&b, vstr_xtalfreq,
+ (cis[i + 4] << 24) |
+ (cis[i + 3] << 16) |
+ (cis[i + 2] << 8) |
+ cis[i + 1]);
+ break;
+
+ case HNBU_CHIPID:
+ varbuf_append(&b, vstr_vendid,
+ (cis[i + 2] << 8) +
+ cis[i + 1]);
+ varbuf_append(&b, vstr_devid,
+ (cis[i + 4] << 8) +
+ cis[i + 3]);
+ if (tlen >= 7) {
+ varbuf_append(&b, vstr_chiprev,
+ (cis[i + 6] << 8)
+ + cis[i + 5]);
+ }
+ if (tlen >= 9) {
+ varbuf_append(&b,
+ vstr_subvendid,
+ (cis[i + 8] << 8)
+ + cis[i + 7]);
+ }
+ if (tlen >= 11) {
+ varbuf_append(&b, vstr_subdevid,
+ (cis[i + 10] << 8)
+ + cis[i + 9]);
+ /* subdevid doubles for boardtype */
+ varbuf_append(&b,
+ vstr_boardtype,
+ (cis[i + 10] << 8)
+ + cis[i + 9]);
+ }
+ break;
+
+ case HNBU_BOARDNUM:
+ boardnum =
+ (cis[i + 2] << 8) + cis[i + 1];
+ break;
+
+ case HNBU_PATCH:
+ {
+ char vstr_paddr[16];
+ char vstr_pdata[16];
+
+ /* retrieve the patch pairs
+ * from tlen/6; where 6 is
+ * sizeof(patch addr(2)) +
+ * sizeof(patch data(4)).
+ */
+ patch_pair = tlen / 6;
+
+ for (j = 0; j < patch_pair; j++) {
+ snprintf(vstr_paddr,
+ sizeof
+ (vstr_paddr),
+ "pa%d=0x%%x",
+ j);
+ snprintf(vstr_pdata,
+ sizeof
+ (vstr_pdata),
+ "pd%d=0x%%x",
+ j);
+
+ varbuf_append(&b,
+ vstr_paddr,
+ (cis
+ [i +
+ (j *
+ 6) +
+ 2] << 8)
+ | cis[i +
+ (j *
+ 6)
+ +
+ 1]);
+
+ varbuf_append(&b,
+ vstr_pdata,
+ (cis
+ [i +
+ (j *
+ 6) +
+ 6] <<
+ 24) |
+ (cis
+ [i +
+ (j *
+ 6) +
+ 5] <<
+ 16) |
+ (cis
+ [i +
+ (j *
+ 6) +
+ 4] << 8)
+ | cis[i +
+ (j *
+ 6)
+ +
+ 3]);
+ }
+ }
+ break;
+
+ case HNBU_BOARDREV:
+ if (tlen == 2)
+ varbuf_append(&b, vstr_boardrev,
+ cis[i + 1]);
+ else
+ varbuf_append(&b, vstr_boardrev,
+ (cis[i + 2] << 8)
+ + cis[i + 1]);
+ break;
+
+ case HNBU_BOARDFLAGS:
+ w32 = (cis[i + 2] << 8) + cis[i + 1];
+ if (tlen >= 5)
+ w32 |=
+ ((cis[i + 4] << 24) +
+ (cis[i + 3] << 16));
+ varbuf_append(&b, vstr_boardflags, w32);
+
+ if (tlen >= 7) {
+ w32 =
+ (cis[i + 6] << 8) + cis[i +
+ 5];
+ if (tlen >= 9)
+ w32 |=
+ ((cis[i + 8] << 24)
+ +
+ (cis[i + 7] <<
+ 16));
+ varbuf_append(&b,
+ vstr_boardflags2,
+ w32);
+ }
+ break;
+
+ case HNBU_USBFS:
+ varbuf_append(&b, vstr_usbfs,
+ cis[i + 1]);
+ break;
+
+ case HNBU_BOARDTYPE:
+ varbuf_append(&b, vstr_boardtype,
+ (cis[i + 2] << 8) +
+ cis[i + 1]);
+ break;
+
+ case HNBU_HNBUCIS:
+ /*
+ * what follows is a nonstandard HNBU CIS
+ * that lacks CISTPL_BRCM_HNBU tags
+ *
+ * skip 0xff (end of standard CIS)
+ * after this tuple
+ */
+ tlen++;
+ standard_cis = FALSE;
+ break;
+
+ case HNBU_USBEPNUM:
+ varbuf_append(&b, vstr_usbepnum,
+ (cis[i + 2] << 8) | cis[i
+ +
+ 1]);
+ break;
+
+ case HNBU_AA:
+ varbuf_append(&b, vstr_aa2g,
+ cis[i + 1]);
+ if (tlen >= 3)
+ varbuf_append(&b, vstr_aa5g,
+ cis[i + 2]);
+ break;
+
+ case HNBU_AG:
+ varbuf_append(&b, vstr_ag, 0,
+ cis[i + 1]);
+ if (tlen >= 3)
+ varbuf_append(&b, vstr_ag, 1,
+ cis[i + 2]);
+ if (tlen >= 4)
+ varbuf_append(&b, vstr_ag, 2,
+ cis[i + 3]);
+ if (tlen >= 5)
+ varbuf_append(&b, vstr_ag, 3,
+ cis[i + 4]);
+ ag_init = TRUE;
+ break;
+
+ case HNBU_ANT5G:
+ varbuf_append(&b, vstr_aa5g,
+ cis[i + 1]);
+ varbuf_append(&b, vstr_ag, 1,
+ cis[i + 2]);
+ break;
+
+ case HNBU_CC:
+ ASSERT(sromrev == 1);
+ varbuf_append(&b, vstr_cc, cis[i + 1]);
+ break;
+
+ case HNBU_PAPARMS:
+ switch (tlen) {
+ case 2:
+ ASSERT(sromrev == 1);
+ varbuf_append(&b,
+ vstr_pa0maxpwr,
+ cis[i + 1]);
+ break;
+ case 10:
+ ASSERT(sromrev >= 2);
+ varbuf_append(&b, vstr_opo,
+ cis[i + 9]);
+ /* FALLTHROUGH */
+ case 9:
+ varbuf_append(&b,
+ vstr_pa0maxpwr,
+ cis[i + 8]);
+ /* FALLTHROUGH */
+ BCMDONGLECASE(8)
+ varbuf_append(&b,
+ vstr_pa0itssit,
+ cis[i + 7]);
+ /* FALLTHROUGH */
+ BCMDONGLECASE(7)
+ for (j = 0; j < 3; j++) {
+ varbuf_append(&b,
+ vstr_pa0b
+ [j],
+ (cis
+ [i +
+ (j *
+ 2) +
+ 2] << 8)
+ + cis[i +
+ (j *
+ 2)
+ +
+ 1]);
+ }
+ break;
+ default:
+ ASSERT((tlen == 2)
+ || (tlen == 9)
+ || (tlen == 10));
+ break;
+ }
+ break;
+
+ case HNBU_PAPARMS5G:
+ ASSERT((sromrev == 2)
+ || (sromrev == 3));
+ switch (tlen) {
+ case 23:
+ varbuf_append(&b,
+ vstr_pa1himaxpwr,
+ cis[i + 22]);
+ varbuf_append(&b,
+ vstr_pa1lomaxpwr,
+ cis[i + 21]);
+ varbuf_append(&b,
+ vstr_pa1maxpwr,
+ cis[i + 20]);
+ /* FALLTHROUGH */
+ case 20:
+ varbuf_append(&b,
+ vstr_pa1itssit,
+ cis[i + 19]);
+ /* FALLTHROUGH */
+ case 19:
+ for (j = 0; j < 3; j++) {
+ varbuf_append(&b,
+ vstr_pa1b
+ [j],
+ (cis
+ [i +
+ (j *
+ 2) +
+ 2] << 8)
+ + cis[i +
+ (j *
+ 2)
+ +
+ 1]);
+ }
+ for (j = 3; j < 6; j++) {
+ varbuf_append(&b,
+ vstr_pa1lob
+ [j - 3],
+ (cis
+ [i +
+ (j *
+ 2) +
+ 2] << 8)
+ + cis[i +
+ (j *
+ 2)
+ +
+ 1]);
+ }
+ for (j = 6; j < 9; j++) {
+ varbuf_append(&b,
+ vstr_pa1hib
+ [j - 6],
+ (cis
+ [i +
+ (j *
+ 2) +
+ 2] << 8)
+ + cis[i +
+ (j *
+ 2)
+ +
+ 1]);
+ }
+ break;
+ default:
+ ASSERT((tlen == 19) ||
+ (tlen == 20)
+ || (tlen == 23));
+ break;
+ }
+ break;
+
+ case HNBU_OEM:
+ ASSERT(sromrev == 1);
+ varbuf_append(&b, vstr_oem,
+ cis[i + 1], cis[i + 2],
+ cis[i + 3], cis[i + 4],
+ cis[i + 5], cis[i + 6],
+ cis[i + 7], cis[i + 8]);
+ break;
+
+ case HNBU_LEDS:
+ for (j = 1; j <= 4; j++) {
+ if (cis[i + j] != 0xff) {
+ varbuf_append(&b,
+ vstr_ledbh,
+ j - 1,
+ cis[i +
+ j]);
+ }
+ }
+ break;
+
+ case HNBU_CCODE:
+ ASSERT(sromrev > 1);
+ if ((cis[i + 1] == 0)
+ || (cis[i + 2] == 0))
+ varbuf_append(&b, vstr_noccode);
+ else
+ varbuf_append(&b, vstr_ccode,
+ cis[i + 1],
+ cis[i + 2]);
+ varbuf_append(&b, vstr_cctl,
+ cis[i + 3]);
+ break;
+
+ case HNBU_CCKPO:
+ ASSERT(sromrev > 2);
+ varbuf_append(&b, vstr_cckpo,
+ (cis[i + 2] << 8) | cis[i
+ +
+ 1]);
+ break;
+
+ case HNBU_OFDMPO:
+ ASSERT(sromrev > 2);
+ varbuf_append(&b, vstr_ofdmpo,
+ (cis[i + 4] << 24) |
+ (cis[i + 3] << 16) |
+ (cis[i + 2] << 8) |
+ cis[i + 1]);
+ break;
+
+ case HNBU_WPS:
+ varbuf_append(&b, vstr_wpsgpio,
+ cis[i + 1]);
+ if (tlen >= 3)
+ varbuf_append(&b, vstr_wpsled,
+ cis[i + 2]);
+ break;
+
+ case HNBU_RSSISMBXA2G:
+ ASSERT(sromrev == 3);
+ varbuf_append(&b, vstr_rssismf2g,
+ cis[i + 1] & 0xf);
+ varbuf_append(&b, vstr_rssismc2g,
+ (cis[i + 1] >> 4) & 0xf);
+ varbuf_append(&b, vstr_rssisav2g,
+ cis[i + 2] & 0x7);
+ varbuf_append(&b, vstr_bxa2g,
+ (cis[i + 2] >> 3) & 0x3);
+ break;
+
+ case HNBU_RSSISMBXA5G:
+ ASSERT(sromrev == 3);
+ varbuf_append(&b, vstr_rssismf5g,
+ cis[i + 1] & 0xf);
+ varbuf_append(&b, vstr_rssismc5g,
+ (cis[i + 1] >> 4) & 0xf);
+ varbuf_append(&b, vstr_rssisav5g,
+ cis[i + 2] & 0x7);
+ varbuf_append(&b, vstr_bxa5g,
+ (cis[i + 2] >> 3) & 0x3);
+ break;
+
+ case HNBU_TRI2G:
+ ASSERT(sromrev == 3);
+ varbuf_append(&b, vstr_tri2g,
+ cis[i + 1]);
+ break;
+
+ case HNBU_TRI5G:
+ ASSERT(sromrev == 3);
+ varbuf_append(&b, vstr_tri5gl,
+ cis[i + 1]);
+ varbuf_append(&b, vstr_tri5g,
+ cis[i + 2]);
+ varbuf_append(&b, vstr_tri5gh,
+ cis[i + 3]);
+ break;
+
+ case HNBU_RXPO2G:
+ ASSERT(sromrev == 3);
+ varbuf_append(&b, vstr_rxpo2g,
+ cis[i + 1]);
+ break;
+
+ case HNBU_RXPO5G:
+ ASSERT(sromrev == 3);
+ varbuf_append(&b, vstr_rxpo5g,
+ cis[i + 1]);
+ break;
+
+ case HNBU_MACADDR:
+ if (!(ETHER_ISNULLADDR(&cis[i + 1])) &&
+ !(ETHER_ISMULTI(&cis[i + 1]))) {
+ bcm_ether_ntoa((struct
+ ether_addr *)
+ &cis[i + 1],
+ eabuf);
+
+ /* set boardnum if HNBU_BOARDNUM not seen yet */
+ if (boardnum == -1)
+ boardnum =
+ (cis[i + 5] << 8) +
+ cis[i + 6];
+ }
+ break;
+
+ case HNBU_LEDDC:
+ /* CIS leddc only has 16bits, convert it to 32bits */
+ w32 = ((cis[i + 2] << 24) | /* oncount */
+ (cis[i + 1] << 8)); /* offcount */
+ varbuf_append(&b, vstr_leddc, w32);
+ break;
+
+ case HNBU_CHAINSWITCH:
+ varbuf_append(&b, vstr_txchain,
+ cis[i + 1]);
+ varbuf_append(&b, vstr_rxchain,
+ cis[i + 2]);
+ varbuf_append(&b, vstr_antswitch,
+ (cis[i + 4] << 8) +
+ cis[i + 3]);
+ break;
+
+ case HNBU_REGREV:
+ varbuf_append(&b, vstr_regrev,
+ cis[i + 1]);
+ break;
+
+ case HNBU_FEM:{
+ uint16 fem =
+ (cis[i + 2] << 8) + cis[i +
+ 1];
+ varbuf_append(&b,
+ vstr_antswctl2g,
+ (fem &
+ SROM8_FEM_ANTSWLUT_MASK)
+ >>
+ SROM8_FEM_ANTSWLUT_SHIFT);
+ varbuf_append(&b, vstr_triso2g,
+ (fem &
+ SROM8_FEM_TR_ISO_MASK)
+ >>
+ SROM8_FEM_TR_ISO_SHIFT);
+ varbuf_append(&b,
+ vstr_pdetrange2g,
+ (fem &
+ SROM8_FEM_PDET_RANGE_MASK)
+ >>
+ SROM8_FEM_PDET_RANGE_SHIFT);
+ varbuf_append(&b,
+ vstr_extpagain2g,
+ (fem &
+ SROM8_FEM_EXTPA_GAIN_MASK)
+ >>
+ SROM8_FEM_EXTPA_GAIN_SHIFT);
+ varbuf_append(&b,
+ vstr_tssipos2g,
+ (fem &
+ SROM8_FEM_TSSIPOS_MASK)
+ >>
+ SROM8_FEM_TSSIPOS_SHIFT);
+ if (tlen < 5)
+ break;
+
+ fem =
+ (cis[i + 4] << 8) + cis[i +
+ 3];
+ varbuf_append(&b,
+ vstr_antswctl5g,
+ (fem &
+ SROM8_FEM_ANTSWLUT_MASK)
+ >>
+ SROM8_FEM_ANTSWLUT_SHIFT);
+ varbuf_append(&b, vstr_triso5g,
+ (fem &
+ SROM8_FEM_TR_ISO_MASK)
+ >>
+ SROM8_FEM_TR_ISO_SHIFT);
+ varbuf_append(&b,
+ vstr_pdetrange5g,
+ (fem &
+ SROM8_FEM_PDET_RANGE_MASK)
+ >>
+ SROM8_FEM_PDET_RANGE_SHIFT);
+ varbuf_append(&b,
+ vstr_extpagain5g,
+ (fem &
+ SROM8_FEM_EXTPA_GAIN_MASK)
+ >>
+ SROM8_FEM_EXTPA_GAIN_SHIFT);
+ varbuf_append(&b,
+ vstr_tssipos5g,
+ (fem &
+ SROM8_FEM_TSSIPOS_MASK)
+ >>
+ SROM8_FEM_TSSIPOS_SHIFT);
+ break;
+ }
+
+ case HNBU_PAPARMS_C0:
+ varbuf_append(&b, vstr_maxp2ga0,
+ cis[i + 1]);
+ varbuf_append(&b, vstr_itt2ga0,
+ cis[i + 2]);
+ varbuf_append(&b, vstr_pa, 2, 0, 0,
+ (cis[i + 4] << 8) +
+ cis[i + 3]);
+ varbuf_append(&b, vstr_pa, 2, 1, 0,
+ (cis[i + 6] << 8) +
+ cis[i + 5]);
+ varbuf_append(&b, vstr_pa, 2, 2, 0,
+ (cis[i + 8] << 8) +
+ cis[i + 7]);
+ if (tlen < 31)
+ break;
+
+ varbuf_append(&b, vstr_maxp5ga0,
+ cis[i + 9]);
+ varbuf_append(&b, vstr_itt5ga0,
+ cis[i + 10]);
+ varbuf_append(&b, vstr_maxp5gha0,
+ cis[i + 11]);
+ varbuf_append(&b, vstr_maxp5gla0,
+ cis[i + 12]);
+ varbuf_append(&b, vstr_pa, 5, 0, 0,
+ (cis[i + 14] << 8) +
+ cis[i + 13]);
+ varbuf_append(&b, vstr_pa, 5, 1, 0,
+ (cis[i + 16] << 8) +
+ cis[i + 15]);
+ varbuf_append(&b, vstr_pa, 5, 2, 0,
+ (cis[i + 18] << 8) +
+ cis[i + 17]);
+ varbuf_append(&b, vstr_pahl, 5, 'l', 0,
+ 0,
+ (cis[i + 20] << 8) +
+ cis[i + 19]);
+ varbuf_append(&b, vstr_pahl, 5, 'l', 1,
+ 0,
+ (cis[i + 22] << 8) +
+ cis[i + 21]);
+ varbuf_append(&b, vstr_pahl, 5, 'l', 2,
+ 0,
+ (cis[i + 24] << 8) +
+ cis[i + 23]);
+ varbuf_append(&b, vstr_pahl, 5, 'h', 0,
+ 0,
+ (cis[i + 26] << 8) +
+ cis[i + 25]);
+ varbuf_append(&b, vstr_pahl, 5, 'h', 1,
+ 0,
+ (cis[i + 28] << 8) +
+ cis[i + 27]);
+ varbuf_append(&b, vstr_pahl, 5, 'h', 2,
+ 0,
+ (cis[i + 30] << 8) +
+ cis[i + 29]);
+ break;
+
+ case HNBU_PAPARMS_C1:
+ varbuf_append(&b, vstr_maxp2ga1,
+ cis[i + 1]);
+ varbuf_append(&b, vstr_itt2ga1,
+ cis[i + 2]);
+ varbuf_append(&b, vstr_pa, 2, 0, 1,
+ (cis[i + 4] << 8) +
+ cis[i + 3]);
+ varbuf_append(&b, vstr_pa, 2, 1, 1,
+ (cis[i + 6] << 8) +
+ cis[i + 5]);
+ varbuf_append(&b, vstr_pa, 2, 2, 1,
+ (cis[i + 8] << 8) +
+ cis[i + 7]);
+ if (tlen < 31)
+ break;
+
+ varbuf_append(&b, vstr_maxp5ga1,
+ cis[i + 9]);
+ varbuf_append(&b, vstr_itt5ga1,
+ cis[i + 10]);
+ varbuf_append(&b, vstr_maxp5gha1,
+ cis[i + 11]);
+ varbuf_append(&b, vstr_maxp5gla1,
+ cis[i + 12]);
+ varbuf_append(&b, vstr_pa, 5, 0, 1,
+ (cis[i + 14] << 8) +
+ cis[i + 13]);
+ varbuf_append(&b, vstr_pa, 5, 1, 1,
+ (cis[i + 16] << 8) +
+ cis[i + 15]);
+ varbuf_append(&b, vstr_pa, 5, 2, 1,
+ (cis[i + 18] << 8) +
+ cis[i + 17]);
+ varbuf_append(&b, vstr_pahl, 5, 'l', 0,
+ 1,
+ (cis[i + 20] << 8) +
+ cis[i + 19]);
+ varbuf_append(&b, vstr_pahl, 5, 'l', 1,
+ 1,
+ (cis[i + 22] << 8) +
+ cis[i + 21]);
+ varbuf_append(&b, vstr_pahl, 5, 'l', 2,
+ 1,
+ (cis[i + 24] << 8) +
+ cis[i + 23]);
+ varbuf_append(&b, vstr_pahl, 5, 'h', 0,
+ 1,
+ (cis[i + 26] << 8) +
+ cis[i + 25]);
+ varbuf_append(&b, vstr_pahl, 5, 'h', 1,
+ 1,
+ (cis[i + 28] << 8) +
+ cis[i + 27]);
+ varbuf_append(&b, vstr_pahl, 5, 'h', 2,
+ 1,
+ (cis[i + 30] << 8) +
+ cis[i + 29]);
+ break;
+
+ case HNBU_PO_CCKOFDM:
+ varbuf_append(&b, vstr_cck2gpo,
+ (cis[i + 2] << 8) +
+ cis[i + 1]);
+ varbuf_append(&b, vstr_ofdm2gpo,
+ (cis[i + 6] << 24) +
+ (cis[i + 5] << 16) +
+ (cis[i + 4] << 8) +
+ cis[i + 3]);
+ if (tlen < 19)
+ break;
+
+ varbuf_append(&b, vstr_ofdm5gpo,
+ (cis[i + 10] << 24) +
+ (cis[i + 9] << 16) +
+ (cis[i + 8] << 8) +
+ cis[i + 7]);
+ varbuf_append(&b, vstr_ofdm5glpo,
+ (cis[i + 14] << 24) +
+ (cis[i + 13] << 16) +
+ (cis[i + 12] << 8) +
+ cis[i + 11]);
+ varbuf_append(&b, vstr_ofdm5ghpo,
+ (cis[i + 18] << 24) +
+ (cis[i + 17] << 16) +
+ (cis[i + 16] << 8) +
+ cis[i + 15]);
+ break;
+
+ case HNBU_PO_MCS2G:
+ for (j = 0; j <= (tlen / 2); j++) {
+ varbuf_append(&b, vstr_mcspo, 2,
+ j,
+ (cis
+ [i + 2 +
+ 2 * j] << 8) +
+ cis[i + 1 +
+ 2 * j]);
+ }
+ break;
+
+ case HNBU_PO_MCS5GM:
+ for (j = 0; j <= (tlen / 2); j++) {
+ varbuf_append(&b, vstr_mcspo, 5,
+ j,
+ (cis
+ [i + 2 +
+ 2 * j] << 8) +
+ cis[i + 1 +
+ 2 * j]);
+ }
+ break;
+
+ case HNBU_PO_MCS5GLH:
+ for (j = 0; j <= (tlen / 4); j++) {
+ varbuf_append(&b, vstr_mcspohl,
+ 5, 'l', j,
+ (cis
+ [i + 2 +
+ 2 * j] << 8) +
+ cis[i + 1 +
+ 2 * j]);
+ }
+
+ for (j = 0; j <= (tlen / 4); j++) {
+ varbuf_append(&b, vstr_mcspohl,
+ 5, 'h', j,
+ (cis
+ [i +
+ ((tlen / 2) +
+ 2) +
+ 2 * j] << 8) +
+ cis[i +
+ ((tlen / 2) +
+ 1) + 2 * j]);
+ }
+
+ break;
+
+ case HNBU_PO_CDD:
+ varbuf_append(&b, vstr_cddpo,
+ (cis[i + 2] << 8) +
+ cis[i + 1]);
+ break;
+
+ case HNBU_PO_STBC:
+ varbuf_append(&b, vstr_stbcpo,
+ (cis[i + 2] << 8) +
+ cis[i + 1]);
+ break;
+
+ case HNBU_PO_40M:
+ varbuf_append(&b, vstr_bw40po,
+ (cis[i + 2] << 8) +
+ cis[i + 1]);
+ break;
+
+ case HNBU_PO_40MDUP:
+ varbuf_append(&b, vstr_bwduppo,
+ (cis[i + 2] << 8) +
+ cis[i + 1]);
+ break;
+
+ case HNBU_OFDMPO5G:
+ varbuf_append(&b, vstr_ofdm5gpo,
+ (cis[i + 4] << 24) +
+ (cis[i + 3] << 16) +
+ (cis[i + 2] << 8) +
+ cis[i + 1]);
+ varbuf_append(&b, vstr_ofdm5glpo,
+ (cis[i + 8] << 24) +
+ (cis[i + 7] << 16) +
+ (cis[i + 6] << 8) +
+ cis[i + 5]);
+ varbuf_append(&b, vstr_ofdm5ghpo,
+ (cis[i + 12] << 24) +
+ (cis[i + 11] << 16) +
+ (cis[i + 10] << 8) +
+ cis[i + 9]);
+ break;
+
+ case HNBU_CUSTOM1:
+ varbuf_append(&b, vstr_custom, 1,
+ ((cis[i + 4] << 24) +
+ (cis[i + 3] << 16) +
+ (cis[i + 2] << 8) +
+ cis[i + 1]));
+ break;
+
+#if defined(BCMSDIO)
+ case HNBU_SROM3SWRGN:
+ if (tlen >= 73) {
+ uint16 srom[35];
+ uint8 srev = cis[i + 1 + 70];
+ ASSERT(srev == 3);
+ /* make tuple value 16-bit aligned and parse it */
+ bcopy(&cis[i + 1], srom,
+ sizeof(srom));
+ _initvars_srom_pci(srev, srom,
+ SROM3_SWRGN_OFF,
+ &b);
+ /* 2.4G antenna gain is included in SROM */
+ ag_init = TRUE;
+ /* Ethernet MAC address is included in SROM */
+ eabuf[0] = 0;
+ boardnum = -1;
+ }
+ /* create extra variables */
+ if (tlen >= 75)
+ varbuf_append(&b, vstr_vendid,
+ (cis[i + 1 + 73]
+ << 8) + cis[i +
+ 1 +
+ 72]);
+ if (tlen >= 77)
+ varbuf_append(&b, vstr_devid,
+ (cis[i + 1 + 75]
+ << 8) + cis[i +
+ 1 +
+ 74]);
+ if (tlen >= 79)
+ varbuf_append(&b, vstr_xtalfreq,
+ (cis[i + 1 + 77]
+ << 8) + cis[i +
+ 1 +
+ 76]);
+ break;
+#endif /* defined(BCMSDIO) */
+
+ case HNBU_CCKFILTTYPE:
+ varbuf_append(&b, vstr_cckdigfilttype,
+ (cis[i + 1]));
+ break;
+ }
+
+ break;
+ }
+ i += tlen;
+ } while (tup != CISTPL_END);
+ }
+
+ if (boardnum != -1) {
+ varbuf_append(&b, vstr_boardnum, boardnum);
+ }
+
+ if (eabuf[0]) {
+ varbuf_append(&b, vstr_macaddr, eabuf);
+ }
+
+ /* if there is no antenna gain field, set default */
+ if (getvar(NULL, "ag0") == NULL && ag_init == FALSE) {
+ varbuf_append(&b, vstr_ag, 0, 0xff);
+ }
+
+ /* final nullbyte terminator */
+ ASSERT(b.size >= 1);
+ *b.buf++ = '\0';
+
+ ASSERT(b.buf - base <= MAXSZ_NVRAM_VARS);
+ err = initvars_table(osh, base, b.buf, vars, count);
+
+ MFREE(osh, base, MAXSZ_NVRAM_VARS);
+ return err;
+}
+
+/* In chips with chipcommon rev 32 and later, the srom is in chipcommon,
+ * not in the bus cores.
+ */
+static uint16
+srom_cc_cmd(si_t * sih, osl_t * osh, void *ccregs, uint32 cmd, uint wordoff,
+ uint16 data)
+{
+ chipcregs_t *cc = (chipcregs_t *) ccregs;
+ uint wait_cnt = 1000;
+
+ if ((cmd == SRC_OP_READ) || (cmd == SRC_OP_WRITE)) {
+ W_REG(osh, &cc->sromaddress, wordoff * 2);
+ if (cmd == SRC_OP_WRITE)
+ W_REG(osh, &cc->sromdata, data);
+ }
+
+ W_REG(osh, &cc->sromcontrol, SRC_START | cmd);
+
+ while (wait_cnt--) {
+ if ((R_REG(osh, &cc->sromcontrol) & SRC_BUSY) == 0)
+ break;
+ }
+
+ if (!wait_cnt) {
+ BS_ERROR(("%s: Command 0x%x timed out\n", __func__, cmd));
+ return 0xffff;
+ }
+ if (cmd == SRC_OP_READ)
+ return (uint16) R_REG(osh, &cc->sromdata);
+ else
+ return 0xffff;
+}
+
+/*
+ * Read in and validate sprom.
+ * Return 0 on success, nonzero on error.
+ */
+static int
+sprom_read_pci(osl_t * osh, si_t * sih, uint16 * sprom, uint wordoff,
+ uint16 * buf, uint nwords, bool check_crc)
+{
+ int err = 0;
+ uint i;
+ void *ccregs = NULL;
+
+ /* read the sprom */
+ for (i = 0; i < nwords; i++) {
+
+ if (sih->ccrev > 31 && ISSIM_ENAB(sih)) {
+ /* use indirect since direct is too slow on QT */
+ if ((sih->cccaps & CC_CAP_SROM) == 0)
+ return 1;
+
+ ccregs = (void *)((uint8 *) sprom - CC_SROM_OTP);
+ buf[i] =
+ srom_cc_cmd(sih, osh, ccregs, SRC_OP_READ,
+ wordoff + i, 0);
+
+ } else {
+ if (ISSIM_ENAB(sih))
+ buf[i] = R_REG(osh, &sprom[wordoff + i]);
+
+ buf[i] = R_REG(osh, &sprom[wordoff + i]);
+ }
+
+ }
+
+ /* bypass crc checking for simulation to allow srom hack */
+ if (ISSIM_ENAB(sih))
+ return err;
+
+ if (check_crc) {
+
+ if (buf[0] == 0xffff) {
+ /* The hardware thinks that an srom that starts with 0xffff
+ * is blank, regardless of the rest of the content, so declare
+ * it bad.
+ */
+ BS_ERROR(("%s: buf[0] = 0x%x, returning bad-crc\n",
+ __func__, buf[0]));
+ return 1;
+ }
+
+ /* fixup the endianness so crc8 will pass */
+ htol16_buf(buf, nwords * 2);
+ if (hndcrc8((uint8 *) buf, nwords * 2, CRC8_INIT_VALUE) !=
+ CRC8_GOOD_VALUE) {
+ /* DBG only pci always read srom4 first, then srom8/9 */
+ /* BS_ERROR(("%s: bad crc\n", __func__)); */
+ err = 1;
+ }
+ /* now correct the endianness of the byte array */
+ ltoh16_buf(buf, nwords * 2);
+ }
+ return err;
+}
+
+#if defined(BCMNVRAMR)
+static int otp_read_pci(osl_t * osh, si_t * sih, uint16 * buf, uint bufsz)
+{
+ uint8 *otp;
+ uint sz = OTP_SZ_MAX / 2; /* size in words */
+ int err = 0;
+
+ ASSERT(bufsz <= OTP_SZ_MAX);
+
+ if ((otp = MALLOC(osh, OTP_SZ_MAX)) == NULL) {
+ return BCME_ERROR;
+ }
+
+ bzero(otp, OTP_SZ_MAX);
+
+ err = otp_read_region(sih, OTP_HW_RGN, (uint16 *) otp, &sz);
+
+ bcopy(otp, buf, bufsz);
+
+ if (otp)
+ MFREE(osh, otp, OTP_SZ_MAX);
+
+ /* Check CRC */
+ if (buf[0] == 0xffff) {
+ /* The hardware thinks that an srom that starts with 0xffff
+ * is blank, regardless of the rest of the content, so declare
+ * it bad.
+ */
+ BS_ERROR(("%s: buf[0] = 0x%x, returning bad-crc\n", __func__,
+ buf[0]));
+ return 1;
+ }
+
+ /* fixup the endianness so crc8 will pass */
+ htol16_buf(buf, bufsz);
+ if (hndcrc8((uint8 *) buf, SROM4_WORDS * 2, CRC8_INIT_VALUE) !=
+ CRC8_GOOD_VALUE) {
+ BS_ERROR(("%s: bad crc\n", __func__));
+ err = 1;
+ }
+ /* now correct the endianness of the byte array */
+ ltoh16_buf(buf, bufsz);
+
+ return err;
+}
+#endif /* defined(BCMNVRAMR) */
+/*
+* Create variable table from memory.
+* Return 0 on success, nonzero on error.
+*/
+static int
+BCMATTACHFN(initvars_table) (osl_t * osh, char *start, char *end, char **vars,
+ uint * count) {
+ int c = (int)(end - start);
+
+ /* do it only when there is more than just the null string */
+ if (c > 1) {
+ char *vp = MALLOC(osh, c);
+ ASSERT(vp != NULL);
+ if (!vp)
+ return BCME_NOMEM;
+ bcopy(start, vp, c);
+ *vars = vp;
+ *count = c;
+ } else {
+ *vars = NULL;
+ *count = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Find variables with <devpath> from flash. 'base' points to the beginning
+ * of the table upon enter and to the end of the table upon exit when success.
+ * Return 0 on success, nonzero on error.
+ */
+static int
+BCMATTACHFN(initvars_flash) (si_t * sih, osl_t * osh, char **base, uint len) {
+ char *vp = *base;
+ char *flash;
+ int err;
+ char *s;
+ uint l, dl, copy_len;
+ char devpath[SI_DEVPATH_BUFSZ];
+
+ /* allocate memory and read in flash */
+ if (!(flash = MALLOC(osh, NVRAM_SPACE)))
+ return BCME_NOMEM;
+ if ((err = nvram_getall(flash, NVRAM_SPACE)))
+ goto exit;
+
+ si_devpath(sih, devpath, sizeof(devpath));
+
+ /* grab vars with the <devpath> prefix in name */
+ dl = strlen(devpath);
+ for (s = flash; s && *s; s += l + 1) {
+ l = strlen(s);
+
+ /* skip non-matching variable */
+ if (strncmp(s, devpath, dl))
+ continue;
+
+ /* is there enough room to copy? */
+ copy_len = l - dl + 1;
+ if (len < copy_len) {
+ err = BCME_BUFTOOSHORT;
+ goto exit;
+ }
+
+ /* no prefix, just the name=value */
+ strncpy(vp, &s[dl], copy_len);
+ vp += copy_len;
+ len -= copy_len;
+ }
+
+ /* add null string as terminator */
+ if (len < 1) {
+ err = BCME_BUFTOOSHORT;
+ goto exit;
+ }
+ *vp++ = '\0';
+
+ *base = vp;
+
+ exit: MFREE(osh, flash, NVRAM_SPACE);
+ return err;
+}
+
+/*
+ * Initialize nonvolatile variable table from flash.
+ * Return 0 on success, nonzero on error.
+ */
+static int
+BCMATTACHFN(initvars_flash_si) (si_t * sih, char **vars, uint * count) {
+ osl_t *osh = si_osh(sih);
+ char *vp, *base;
+ int err;
+
+ ASSERT(vars != NULL);
+ ASSERT(count != NULL);
+
+ base = vp = MALLOC(osh, MAXSZ_NVRAM_VARS);
+ ASSERT(vp != NULL);
+ if (!vp)
+ return BCME_NOMEM;
+
+ if ((err = initvars_flash(sih, osh, &vp, MAXSZ_NVRAM_VARS)) == 0)
+ err = initvars_table(osh, base, vp, vars, count);
+
+ MFREE(osh, base, MAXSZ_NVRAM_VARS);
+
+ return err;
+}
+
+/* Parse SROM and create name=value pairs. 'srom' points to
+ * the SROM word array. 'off' specifies the offset of the
+ * first word 'srom' points to, which should be either 0 or
+ * SROM3_SWRG_OFF (full SROM or software region).
+ */
+
+static uint mask_shift(uint16 mask)
+{
+ uint i;
+ for (i = 0; i < (sizeof(mask) << 3); i++) {
+ if (mask & (1 << i))
+ return i;
+ }
+ ASSERT(mask);
+ return 0;
+}
+
+static uint mask_width(uint16 mask)
+{
+ int i;
+ for (i = (sizeof(mask) << 3) - 1; i >= 0; i--) {
+ if (mask & (1 << i))
+ return (uint) (i - mask_shift(mask) + 1);
+ }
+ ASSERT(mask);
+ return 0;
+}
+
+#if defined(BCMDBG)
+static bool mask_valid(uint16 mask)
+{
+ uint shift = mask_shift(mask);
+ uint width = mask_width(mask);
+ return mask == ((~0 << shift) & ~(~0 << (shift + width)));
+}
+#endif /* BCMDBG */
+
+static void
+BCMATTACHFN(_initvars_srom_pci) (uint8 sromrev, uint16 * srom, uint off,
+ varbuf_t * b) {
+ uint16 w;
+ uint32 val;
+ const sromvar_t *srv;
+ uint width;
+ uint flags;
+ uint32 sr = (1 << sromrev);
+
+ varbuf_append(b, "sromrev=%d", sromrev);
+
+ for (srv = pci_sromvars; srv->name != NULL; srv++) {
+ const char *name;
+
+ if ((srv->revmask & sr) == 0)
+ continue;
+
+ if (srv->off < off)
+ continue;
+
+ flags = srv->flags;
+ name = srv->name;
+
+ /* This entry is for mfgc only. Don't generate param for it, */
+ if (flags & SRFL_NOVAR)
+ continue;
+
+ if (flags & SRFL_ETHADDR) {
+ char eabuf[ETHER_ADDR_STR_LEN];
+ struct ether_addr ea;
+
+ ea.octet[0] = (srom[srv->off - off] >> 8) & 0xff;
+ ea.octet[1] = srom[srv->off - off] & 0xff;
+ ea.octet[2] = (srom[srv->off + 1 - off] >> 8) & 0xff;
+ ea.octet[3] = srom[srv->off + 1 - off] & 0xff;
+ ea.octet[4] = (srom[srv->off + 2 - off] >> 8) & 0xff;
+ ea.octet[5] = srom[srv->off + 2 - off] & 0xff;
+ bcm_ether_ntoa(&ea, eabuf);
+
+ varbuf_append(b, "%s=%s", name, eabuf);
+ } else {
+ ASSERT(mask_valid(srv->mask));
+ ASSERT(mask_width(srv->mask));
+
+ w = srom[srv->off - off];
+ val = (w & srv->mask) >> mask_shift(srv->mask);
+ width = mask_width(srv->mask);
+
+ while (srv->flags & SRFL_MORE) {
+ srv++;
+ ASSERT(srv->name != NULL);
+
+ if (srv->off == 0 || srv->off < off)
+ continue;
+
+ ASSERT(mask_valid(srv->mask));
+ ASSERT(mask_width(srv->mask));
+
+ w = srom[srv->off - off];
+ val +=
+ ((w & srv->mask) >> mask_shift(srv->
+ mask)) <<
+ width;
+ width += mask_width(srv->mask);
+ }
+
+ if ((flags & SRFL_NOFFS)
+ && ((int)val == (1 << width) - 1))
+ continue;
+
+ if (flags & SRFL_CCODE) {
+ if (val == 0)
+ varbuf_append(b, "ccode=");
+ else
+ varbuf_append(b, "ccode=%c%c",
+ (val >> 8), (val & 0xff));
+ }
+ /* LED Powersave duty cycle has to be scaled:
+ *(oncount >> 24) (offcount >> 8)
+ */
+ else if (flags & SRFL_LEDDC) {
+ uint32 w32 = (((val >> 8) & 0xff) << 24) | /* oncount */
+ (((val & 0xff)) << 8); /* offcount */
+ varbuf_append(b, "leddc=%d", w32);
+ } else if (flags & SRFL_PRHEX)
+ varbuf_append(b, "%s=0x%x", name, val);
+ else if ((flags & SRFL_PRSIGN)
+ && (val & (1 << (width - 1))))
+ varbuf_append(b, "%s=%d", name,
+ (int)(val | (~0 << width)));
+ else
+ varbuf_append(b, "%s=%u", name, val);
+ }
+ }
+
+ if (sromrev >= 4) {
+ /* Do per-path variables */
+ uint p, pb, psz;
+
+ if (sromrev >= 8) {
+ pb = SROM8_PATH0;
+ psz = SROM8_PATH1 - SROM8_PATH0;
+ } else {
+ pb = SROM4_PATH0;
+ psz = SROM4_PATH1 - SROM4_PATH0;
+ }
+
+ for (p = 0; p < MAX_PATH_SROM; p++) {
+ for (srv = perpath_pci_sromvars; srv->name != NULL;
+ srv++) {
+ if ((srv->revmask & sr) == 0)
+ continue;
+
+ if (pb + srv->off < off)
+ continue;
+
+ /* This entry is for mfgc only. Don't generate param for it, */
+ if (srv->flags & SRFL_NOVAR)
+ continue;
+
+ w = srom[pb + srv->off - off];
+
+ ASSERT(mask_valid(srv->mask));
+ val = (w & srv->mask) >> mask_shift(srv->mask);
+ width = mask_width(srv->mask);
+
+ /* Cheating: no per-path var is more than 1 word */
+
+ if ((srv->flags & SRFL_NOFFS)
+ && ((int)val == (1 << width) - 1))
+ continue;
+
+ if (srv->flags & SRFL_PRHEX)
+ varbuf_append(b, "%s%d=0x%x", srv->name,
+ p, val);
+ else
+ varbuf_append(b, "%s%d=%d", srv->name,
+ p, val);
+ }
+ pb += psz;
+ }
+ }
+}
+
+/*
+ * Initialize nonvolatile variable table from sprom.
+ * Return 0 on success, nonzero on error.
+ */
+static int
+BCMATTACHFN(initvars_srom_pci) (si_t * sih, void *curmap, char **vars,
+ uint * count) {
+ uint16 *srom, *sromwindow;
+ uint8 sromrev = 0;
+ uint32 sr;
+ varbuf_t b;
+ char *vp, *base = NULL;
+ osl_t *osh = si_osh(sih);
+ bool flash = FALSE;
+ int err = 0;
+
+ /*
+ * Apply CRC over SROM content regardless SROM is present or not,
+ * and use variable <devpath>sromrev's existance in flash to decide
+ * if we should return an error when CRC fails or read SROM variables
+ * from flash.
+ */
+ srom = MALLOC(osh, SROM_MAX);
+ ASSERT(srom != NULL);
+ if (!srom)
+ return -2;
+
+ sromwindow = (uint16 *) SROM_OFFSET(sih);
+ if (si_is_sprom_available(sih)) {
+ err =
+ sprom_read_pci(osh, sih, sromwindow, 0, srom, SROM_WORDS,
+ TRUE);
+
+ if ((srom[SROM4_SIGN] == SROM4_SIGNATURE) ||
+ (((sih->buscoretype == PCIE_CORE_ID)
+ && (sih->buscorerev >= 6))
+ || ((sih->buscoretype == PCI_CORE_ID)
+ && (sih->buscorerev >= 0xe)))) {
+ /* sromrev >= 4, read more */
+ err =
+ sprom_read_pci(osh, sih, sromwindow, 0, srom,
+ SROM4_WORDS, TRUE);
+ sromrev = srom[SROM4_CRCREV] & 0xff;
+ if (err)
+ BS_ERROR(("%s: srom %d, bad crc\n", __func__,
+ sromrev));
+
+ } else if (err == 0) {
+ /* srom is good and is rev < 4 */
+ /* top word of sprom contains version and crc8 */
+ sromrev = srom[SROM_CRCREV] & 0xff;
+ /* bcm4401 sroms misprogrammed */
+ if (sromrev == 0x10)
+ sromrev = 1;
+ }
+ }
+#if defined(BCMNVRAMR)
+ /* Use OTP if SPROM not available */
+ else if ((err = otp_read_pci(osh, sih, srom, SROM_MAX)) == 0) {
+ /* OTP only contain SROM rev8/rev9 for now */
+ sromrev = srom[SROM4_CRCREV] & 0xff;
+ }
+#endif
+ else {
+ err = 1;
+ BS_ERROR(("Neither SPROM nor OTP has valid image\n"));
+ }
+
+ /* We want internal/wltest driver to come up with default sromvars so we can
+ * program a blank SPROM/OTP.
+ */
+ if (err) {
+ char *value;
+ uint32 val;
+ val = 0;
+
+ if ((value = si_getdevpathvar(sih, "sromrev"))) {
+ sromrev = (uint8) bcm_strtoul(value, NULL, 0);
+ flash = TRUE;
+ goto varscont;
+ }
+
+ BS_ERROR(("%s, SROM CRC Error\n", __func__));
+
+ if ((value = si_getnvramflvar(sih, "sromrev"))) {
+ err = 0;
+ goto errout;
+ }
+
+ {
+ err = -1;
+ goto errout;
+ }
+ }
+
+ varscont:
+ /* Bitmask for the sromrev */
+ sr = 1 << sromrev;
+
+ /* srom version check: Current valid versions: 1, 2, 3, 4, 5, 8, 9 */
+ if ((sr & 0x33e) == 0) {
+ err = -2;
+ goto errout;
+ }
+
+ ASSERT(vars != NULL);
+ ASSERT(count != NULL);
+
+ base = vp = MALLOC(osh, MAXSZ_NVRAM_VARS);
+ ASSERT(vp != NULL);
+ if (!vp) {
+ err = -2;
+ goto errout;
+ }
+
+ /* read variables from flash */
+ if (flash) {
+ if ((err = initvars_flash(sih, osh, &vp, MAXSZ_NVRAM_VARS)))
+ goto errout;
+ goto varsdone;
+ }
+
+ varbuf_init(&b, base, MAXSZ_NVRAM_VARS);
+
+ /* parse SROM into name=value pairs. */
+ _initvars_srom_pci(sromrev, srom, 0, &b);
+
+ /* final nullbyte terminator */
+ ASSERT(b.size >= 1);
+ vp = b.buf;
+ *vp++ = '\0';
+
+ ASSERT((vp - base) <= MAXSZ_NVRAM_VARS);
+
+ varsdone:
+ err = initvars_table(osh, base, vp, vars, count);
+
+ errout:
+ if (base)
+ MFREE(osh, base, MAXSZ_NVRAM_VARS);
+
+ MFREE(osh, srom, SROM_MAX);
+ return err;
+}
+
+#ifdef BCMSDIO
+/*
+ * Read the SDIO cis and call parsecis to initialize the vars.
+ * Return 0 on success, nonzero on error.
+ */
+static int
+BCMATTACHFN(initvars_cis_sdio) (osl_t * osh, char **vars, uint * count) {
+ uint8 *cis[SBSDIO_NUM_FUNCTION + 1];
+ uint fn, numfn;
+ int rc = 0;
+
+ numfn = bcmsdh_query_iofnum(NULL);
+ ASSERT(numfn <= SDIOD_MAX_IOFUNCS);
+
+ for (fn = 0; fn <= numfn; fn++) {
+ if ((cis[fn] = MALLOC(osh, SBSDIO_CIS_SIZE_LIMIT)) == NULL) {
+ rc = -1;
+ break;
+ }
+
+ bzero(cis[fn], SBSDIO_CIS_SIZE_LIMIT);
+
+ if (bcmsdh_cis_read(NULL, fn, cis[fn], SBSDIO_CIS_SIZE_LIMIT) !=
+ 0) {
+ MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT);
+ rc = -2;
+ break;
+ }
+ }
+
+ if (!rc)
+ rc = srom_parsecis(osh, cis, fn, vars, count);
+
+ while (fn-- > 0)
+ MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT);
+
+ return (rc);
+}
+
+/* set SDIO sprom command register */
+static int BCMATTACHFN(sprom_cmd_sdio) (osl_t * osh, uint8 cmd) {
+ uint8 status = 0;
+ uint wait_cnt = 1000;
+
+ /* write sprom command register */
+ bcmsdh_cfg_write(NULL, SDIO_FUNC_1, SBSDIO_SPROM_CS, cmd, NULL);
+
+ /* wait status */
+ while (wait_cnt--) {
+ status =
+ bcmsdh_cfg_read(NULL, SDIO_FUNC_1, SBSDIO_SPROM_CS, NULL);
+ if (status & SBSDIO_SPROM_DONE)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* read a word from the SDIO srom */
+static int sprom_read_sdio(osl_t * osh, uint16 addr, uint16 * data)
+{
+ uint8 addr_l, addr_h, data_l, data_h;
+
+ addr_l = (uint8) ((addr * 2) & 0xff);
+ addr_h = (uint8) (((addr * 2) >> 8) & 0xff);
+
+ /* set address */
+ bcmsdh_cfg_write(NULL, SDIO_FUNC_1, SBSDIO_SPROM_ADDR_HIGH, addr_h,
+ NULL);
+ bcmsdh_cfg_write(NULL, SDIO_FUNC_1, SBSDIO_SPROM_ADDR_LOW, addr_l,
+ NULL);
+
+ /* do read */
+ if (sprom_cmd_sdio(osh, SBSDIO_SPROM_READ))
+ return 1;
+
+ /* read data */
+ data_h =
+ bcmsdh_cfg_read(NULL, SDIO_FUNC_1, SBSDIO_SPROM_DATA_HIGH, NULL);
+ data_l =
+ bcmsdh_cfg_read(NULL, SDIO_FUNC_1, SBSDIO_SPROM_DATA_LOW, NULL);
+
+ *data = (data_h << 8) | data_l;
+ return 0;
+}
+#endif /* BCMSDIO */
+
+static int
+BCMATTACHFN(initvars_srom_si) (si_t * sih, osl_t * osh, void *curmap,
+ char **vars, uint * varsz) {
+ /* Search flash nvram section for srom variables */
+ return initvars_flash_si(sih, vars, varsz);
+}
OpenPOWER on IntegriCloud