linux/drivers/net/wireless/brcm80211/brcmsmac/srom.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010 Broadcom Corporation
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#include <linux/kernel.h>
  18#include <linux/string.h>
  19#include <linux/io.h>
  20#include <linux/etherdevice.h>
  21#include <linux/crc8.h>
  22#include <stdarg.h>
  23
  24#include <chipcommon.h>
  25#include <brcmu_utils.h>
  26#include "pub.h"
  27#include "nicpci.h"
  28#include "aiutils.h"
  29#include "otp.h"
  30#include "srom.h"
  31#include "soc.h"
  32
  33/*
  34 * SROM CRC8 polynomial value:
  35 *
  36 * x^8 + x^7 +x^6 + x^4 + x^2 + 1
  37 */
  38#define SROM_CRC8_POLY          0xAB
  39
  40/* Maximum srom: 6 Kilobits == 768 bytes */
  41#define SROM_MAX                768
  42
  43/* PCI fields */
  44#define PCI_F0DEVID             48
  45
  46#define SROM_WORDS              64
  47
  48#define SROM_SSID               2
  49
  50#define SROM_WL1LHMAXP          29
  51
  52#define SROM_WL1LPAB0           30
  53#define SROM_WL1LPAB1           31
  54#define SROM_WL1LPAB2           32
  55
  56#define SROM_WL1HPAB0           33
  57#define SROM_WL1HPAB1           34
  58#define SROM_WL1HPAB2           35
  59
  60#define SROM_MACHI_IL0          36
  61#define SROM_MACMID_IL0         37
  62#define SROM_MACLO_IL0          38
  63#define SROM_MACHI_ET1          42
  64#define SROM_MACMID_ET1         43
  65#define SROM_MACLO_ET1          44
  66
  67#define SROM_BXARSSI2G          40
  68#define SROM_BXARSSI5G          41
  69
  70#define SROM_TRI52G             42
  71#define SROM_TRI5GHL            43
  72
  73#define SROM_RXPO52G            45
  74
  75#define SROM_AABREV             46
  76/* Fields in AABREV */
  77#define SROM_BR_MASK            0x00ff
  78#define SROM_CC_MASK            0x0f00
  79#define SROM_CC_SHIFT           8
  80#define SROM_AA0_MASK           0x3000
  81#define SROM_AA0_SHIFT          12
  82#define SROM_AA1_MASK           0xc000
  83#define SROM_AA1_SHIFT          14
  84
  85#define SROM_WL0PAB0            47
  86#define SROM_WL0PAB1            48
  87#define SROM_WL0PAB2            49
  88
  89#define SROM_LEDBH10            50
  90#define SROM_LEDBH32            51
  91
  92#define SROM_WL10MAXP           52
  93
  94#define SROM_WL1PAB0            53
  95#define SROM_WL1PAB1            54
  96#define SROM_WL1PAB2            55
  97
  98#define SROM_ITT                56
  99
 100#define SROM_BFL                57
 101#define SROM_BFL2               28
 102
 103#define SROM_AG10               58
 104
 105#define SROM_CCODE              59
 106
 107#define SROM_OPO                60
 108
 109#define SROM_CRCREV             63
 110
 111#define SROM4_WORDS             220
 112
 113#define SROM4_TXCHAIN_MASK      0x000f
 114#define SROM4_RXCHAIN_MASK      0x00f0
 115#define SROM4_SWITCH_MASK       0xff00
 116
 117/* Per-path fields */
 118#define MAX_PATH_SROM           4
 119
 120#define SROM4_CRCREV            219
 121
 122/* SROM Rev 8: Make space for a 48word hardware header for PCIe rev >= 6.
 123 * This is acombined srom for both MIMO and SISO boards, usable in
 124 * the .130 4Kilobit OTP with hardware redundancy.
 125 */
 126#define SROM8_BREV              65
 127
 128#define SROM8_BFL0              66
 129#define SROM8_BFL1              67
 130#define SROM8_BFL2              68
 131#define SROM8_BFL3              69
 132
 133#define SROM8_MACHI             70
 134#define SROM8_MACMID            71
 135#define SROM8_MACLO             72
 136
 137#define SROM8_CCODE             73
 138#define SROM8_REGREV            74
 139
 140#define SROM8_LEDBH10           75
 141#define SROM8_LEDBH32           76
 142
 143#define SROM8_LEDDC             77
 144
 145#define SROM8_AA                78
 146
 147#define SROM8_AG10              79
 148#define SROM8_AG32              80
 149
 150#define SROM8_TXRXC             81
 151
 152#define SROM8_BXARSSI2G         82
 153#define SROM8_BXARSSI5G         83
 154#define SROM8_TRI52G            84
 155#define SROM8_TRI5GHL           85
 156#define SROM8_RXPO52G           86
 157
 158#define SROM8_FEM2G             87
 159#define SROM8_FEM5G             88
 160#define SROM8_FEM_ANTSWLUT_MASK         0xf800
 161#define SROM8_FEM_ANTSWLUT_SHIFT        11
 162#define SROM8_FEM_TR_ISO_MASK           0x0700
 163#define SROM8_FEM_TR_ISO_SHIFT          8
 164#define SROM8_FEM_PDET_RANGE_MASK       0x00f8
 165#define SROM8_FEM_PDET_RANGE_SHIFT      3
 166#define SROM8_FEM_EXTPA_GAIN_MASK       0x0006
 167#define SROM8_FEM_EXTPA_GAIN_SHIFT      1
 168#define SROM8_FEM_TSSIPOS_MASK          0x0001
 169#define SROM8_FEM_TSSIPOS_SHIFT         0
 170
 171#define SROM8_THERMAL           89
 172
 173/* Temp sense related entries */
 174#define SROM8_MPWR_RAWTS                90
 175#define SROM8_TS_SLP_OPT_CORRX  91
 176/* FOC: freiquency offset correction, HWIQ: H/W IOCAL enable,
 177 * IQSWP: IQ CAL swap disable */
 178#define SROM8_FOC_HWIQ_IQSWP    92
 179
 180/* Temperature delta for PHY calibration */
 181#define SROM8_PHYCAL_TEMPDELTA  93
 182
 183/* Per-path offsets & fields */
 184#define SROM8_PATH0             96
 185#define SROM8_PATH1             112
 186#define SROM8_PATH2             128
 187#define SROM8_PATH3             144
 188
 189#define SROM8_2G_ITT_MAXP       0
 190#define SROM8_2G_PA             1
 191#define SROM8_5G_ITT_MAXP       4
 192#define SROM8_5GLH_MAXP         5
 193#define SROM8_5G_PA             6
 194#define SROM8_5GL_PA            9
 195#define SROM8_5GH_PA            12
 196
 197/* All the miriad power offsets */
 198#define SROM8_2G_CCKPO          160
 199
 200#define SROM8_2G_OFDMPO         161
 201#define SROM8_5G_OFDMPO         163
 202#define SROM8_5GL_OFDMPO        165
 203#define SROM8_5GH_OFDMPO        167
 204
 205#define SROM8_2G_MCSPO          169
 206#define SROM8_5G_MCSPO          177
 207#define SROM8_5GL_MCSPO         185
 208#define SROM8_5GH_MCSPO         193
 209
 210#define SROM8_CDDPO             201
 211#define SROM8_STBCPO            202
 212#define SROM8_BW40PO            203
 213#define SROM8_BWDUPPO           204
 214
 215/* SISO PA parameters are in the path0 spaces */
 216#define SROM8_SISO              96
 217
 218/* Legacy names for SISO PA paramters */
 219#define SROM8_W0_ITTMAXP        (SROM8_SISO + SROM8_2G_ITT_MAXP)
 220#define SROM8_W0_PAB0           (SROM8_SISO + SROM8_2G_PA)
 221#define SROM8_W0_PAB1           (SROM8_SISO + SROM8_2G_PA + 1)
 222#define SROM8_W0_PAB2           (SROM8_SISO + SROM8_2G_PA + 2)
 223#define SROM8_W1_ITTMAXP        (SROM8_SISO + SROM8_5G_ITT_MAXP)
 224#define SROM8_W1_MAXP_LCHC      (SROM8_SISO + SROM8_5GLH_MAXP)
 225#define SROM8_W1_PAB0           (SROM8_SISO + SROM8_5G_PA)
 226#define SROM8_W1_PAB1           (SROM8_SISO + SROM8_5G_PA + 1)
 227#define SROM8_W1_PAB2           (SROM8_SISO + SROM8_5G_PA + 2)
 228#define SROM8_W1_PAB0_LC        (SROM8_SISO + SROM8_5GL_PA)
 229#define SROM8_W1_PAB1_LC        (SROM8_SISO + SROM8_5GL_PA + 1)
 230#define SROM8_W1_PAB2_LC        (SROM8_SISO + SROM8_5GL_PA + 2)
 231#define SROM8_W1_PAB0_HC        (SROM8_SISO + SROM8_5GH_PA)
 232#define SROM8_W1_PAB1_HC        (SROM8_SISO + SROM8_5GH_PA + 1)
 233#define SROM8_W1_PAB2_HC        (SROM8_SISO + SROM8_5GH_PA + 2)
 234
 235/* SROM REV 9 */
 236#define SROM9_2GPO_CCKBW20      160
 237#define SROM9_2GPO_CCKBW20UL    161
 238#define SROM9_2GPO_LOFDMBW20    162
 239#define SROM9_2GPO_LOFDMBW20UL  164
 240
 241#define SROM9_5GLPO_LOFDMBW20   166
 242#define SROM9_5GLPO_LOFDMBW20UL 168
 243#define SROM9_5GMPO_LOFDMBW20   170
 244#define SROM9_5GMPO_LOFDMBW20UL 172
 245#define SROM9_5GHPO_LOFDMBW20   174
 246#define SROM9_5GHPO_LOFDMBW20UL 176
 247
 248#define SROM9_2GPO_MCSBW20      178
 249#define SROM9_2GPO_MCSBW20UL    180
 250#define SROM9_2GPO_MCSBW40      182
 251
 252#define SROM9_5GLPO_MCSBW20     184
 253#define SROM9_5GLPO_MCSBW20UL   186
 254#define SROM9_5GLPO_MCSBW40     188
 255#define SROM9_5GMPO_MCSBW20     190
 256#define SROM9_5GMPO_MCSBW20UL   192
 257#define SROM9_5GMPO_MCSBW40     194
 258#define SROM9_5GHPO_MCSBW20     196
 259#define SROM9_5GHPO_MCSBW20UL   198
 260#define SROM9_5GHPO_MCSBW40     200
 261
 262#define SROM9_PO_MCS32          202
 263#define SROM9_PO_LOFDM40DUP     203
 264
 265/* SROM flags (see sromvar_t) */
 266
 267/* value continues as described by the next entry */
 268#define SRFL_MORE       1
 269#define SRFL_NOFFS      2       /* value bits can't be all one's */
 270#define SRFL_PRHEX      4       /* value is in hexdecimal format */
 271#define SRFL_PRSIGN     8       /* value is in signed decimal format */
 272#define SRFL_CCODE      0x10    /* value is in country code format */
 273#define SRFL_ETHADDR    0x20    /* value is an Ethernet address */
 274#define SRFL_LEDDC      0x40    /* value is an LED duty cycle */
 275/* do not generate a nvram param, entry is for mfgc */
 276#define SRFL_NOVAR      0x80
 277
 278/* Max. nvram variable table size */
 279#define MAXSZ_NVRAM_VARS        4096
 280
 281/*
 282 * indicates type of value.
 283 */
 284enum brcms_srom_var_type {
 285        BRCMS_SROM_STRING,
 286        BRCMS_SROM_SNUMBER,
 287        BRCMS_SROM_UNUMBER
 288};
 289
 290/*
 291 * storage type for srom variable.
 292 *
 293 * var_list: for linked list operations.
 294 * varid: identifier of the variable.
 295 * var_type: type of variable.
 296 * buf: variable value when var_type == BRCMS_SROM_STRING.
 297 * uval: unsigned variable value when var_type == BRCMS_SROM_UNUMBER.
 298 * sval: signed variable value when var_type == BRCMS_SROM_SNUMBER.
 299 */
 300struct brcms_srom_list_head {
 301        struct list_head var_list;
 302        enum brcms_srom_id varid;
 303        enum brcms_srom_var_type var_type;
 304        union {
 305                char buf[0];
 306                u32 uval;
 307                s32 sval;
 308        };
 309};
 310
 311struct brcms_sromvar {
 312        enum brcms_srom_id varid;
 313        u32 revmask;
 314        u32 flags;
 315        u16 off;
 316        u16 mask;
 317};
 318
 319struct brcms_varbuf {
 320        char *base;             /* pointer to buffer base */
 321        char *buf;              /* pointer to current position */
 322        unsigned int size;      /* current (residual) size in bytes */
 323};
 324
 325/*
 326 * Assumptions:
 327 * - Ethernet address spans across 3 consecutive words
 328 *
 329 * Table rules:
 330 * - Add multiple entries next to each other if a value spans across multiple
 331 *   words (even multiple fields in the same word) with each entry except the
 332 *   last having it's SRFL_MORE bit set.
 333 * - Ethernet address entry does not follow above rule and must not have
 334 *   SRFL_MORE bit set. Its SRFL_ETHADDR bit implies it takes multiple words.
 335 * - The last entry's name field must be NULL to indicate the end of the table.
 336 *   Other entries must have non-NULL name.
 337 */
 338static const struct brcms_sromvar pci_sromvars[] = {
 339        {BRCMS_SROM_DEVID, 0xffffff00, SRFL_PRHEX | SRFL_NOVAR, PCI_F0DEVID,
 340         0xffff},
 341        {BRCMS_SROM_BOARDREV, 0xffffff00, SRFL_PRHEX, SROM8_BREV, 0xffff},
 342        {BRCMS_SROM_BOARDFLAGS, 0xffffff00, SRFL_PRHEX | SRFL_MORE, SROM8_BFL0,
 343         0xffff},
 344        {BRCMS_SROM_CONT, 0, 0, SROM8_BFL1, 0xffff},
 345        {BRCMS_SROM_BOARDFLAGS2, 0xffffff00, SRFL_PRHEX | SRFL_MORE, SROM8_BFL2,
 346         0xffff},
 347        {BRCMS_SROM_CONT, 0, 0, SROM8_BFL3, 0xffff},
 348        {BRCMS_SROM_BOARDTYPE, 0xfffffffc, SRFL_PRHEX, SROM_SSID, 0xffff},
 349        {BRCMS_SROM_BOARDNUM, 0xffffff00, 0, SROM8_MACLO, 0xffff},
 350        {BRCMS_SROM_REGREV, 0xffffff00, 0, SROM8_REGREV, 0x00ff},
 351        {BRCMS_SROM_LEDBH0, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH10, 0x00ff},
 352        {BRCMS_SROM_LEDBH1, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH10, 0xff00},
 353        {BRCMS_SROM_LEDBH2, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH32, 0x00ff},
 354        {BRCMS_SROM_LEDBH3, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH32, 0xff00},
 355        {BRCMS_SROM_PA0B0, 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB0, 0xffff},
 356        {BRCMS_SROM_PA0B1, 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB1, 0xffff},
 357        {BRCMS_SROM_PA0B2, 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB2, 0xffff},
 358        {BRCMS_SROM_PA0ITSSIT, 0xffffff00, 0, SROM8_W0_ITTMAXP, 0xff00},
 359        {BRCMS_SROM_PA0MAXPWR, 0xffffff00, 0, SROM8_W0_ITTMAXP, 0x00ff},
 360        {BRCMS_SROM_OPO, 0xffffff00, 0, SROM8_2G_OFDMPO, 0x00ff},
 361        {BRCMS_SROM_AA2G, 0xffffff00, 0, SROM8_AA, 0x00ff},
 362        {BRCMS_SROM_AA5G, 0xffffff00, 0, SROM8_AA, 0xff00},
 363        {BRCMS_SROM_AG0, 0xffffff00, 0, SROM8_AG10, 0x00ff},
 364        {BRCMS_SROM_AG1, 0xffffff00, 0, SROM8_AG10, 0xff00},
 365        {BRCMS_SROM_AG2, 0xffffff00, 0, SROM8_AG32, 0x00ff},
 366        {BRCMS_SROM_AG3, 0xffffff00, 0, SROM8_AG32, 0xff00},
 367        {BRCMS_SROM_PA1B0, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB0, 0xffff},
 368        {BRCMS_SROM_PA1B1, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB1, 0xffff},
 369        {BRCMS_SROM_PA1B2, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB2, 0xffff},
 370        {BRCMS_SROM_PA1LOB0, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB0_LC, 0xffff},
 371        {BRCMS_SROM_PA1LOB1, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB1_LC, 0xffff},
 372        {BRCMS_SROM_PA1LOB2, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB2_LC, 0xffff},
 373        {BRCMS_SROM_PA1HIB0, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB0_HC, 0xffff},
 374        {BRCMS_SROM_PA1HIB1, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB1_HC, 0xffff},
 375        {BRCMS_SROM_PA1HIB2, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB2_HC, 0xffff},
 376        {BRCMS_SROM_PA1ITSSIT, 0xffffff00, 0, SROM8_W1_ITTMAXP, 0xff00},
 377        {BRCMS_SROM_PA1MAXPWR, 0xffffff00, 0, SROM8_W1_ITTMAXP, 0x00ff},
 378        {BRCMS_SROM_PA1LOMAXPWR, 0xffffff00, 0, SROM8_W1_MAXP_LCHC, 0xff00},
 379        {BRCMS_SROM_PA1HIMAXPWR, 0xffffff00, 0, SROM8_W1_MAXP_LCHC, 0x00ff},
 380        {BRCMS_SROM_BXA2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x1800},
 381        {BRCMS_SROM_RSSISAV2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x0700},
 382        {BRCMS_SROM_RSSISMC2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x00f0},
 383        {BRCMS_SROM_RSSISMF2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x000f},
 384        {BRCMS_SROM_BXA5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x1800},
 385        {BRCMS_SROM_RSSISAV5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x0700},
 386        {BRCMS_SROM_RSSISMC5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x00f0},
 387        {BRCMS_SROM_RSSISMF5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x000f},
 388        {BRCMS_SROM_TRI2G, 0xffffff00, 0, SROM8_TRI52G, 0x00ff},
 389        {BRCMS_SROM_TRI5G, 0xffffff00, 0, SROM8_TRI52G, 0xff00},
 390        {BRCMS_SROM_TRI5GL, 0xffffff00, 0, SROM8_TRI5GHL, 0x00ff},
 391        {BRCMS_SROM_TRI5GH, 0xffffff00, 0, SROM8_TRI5GHL, 0xff00},
 392        {BRCMS_SROM_RXPO2G, 0xffffff00, SRFL_PRSIGN, SROM8_RXPO52G, 0x00ff},
 393        {BRCMS_SROM_RXPO5G, 0xffffff00, SRFL_PRSIGN, SROM8_RXPO52G, 0xff00},
 394        {BRCMS_SROM_TXCHAIN, 0xffffff00, SRFL_NOFFS, SROM8_TXRXC,
 395         SROM4_TXCHAIN_MASK},
 396        {BRCMS_SROM_RXCHAIN, 0xffffff00, SRFL_NOFFS, SROM8_TXRXC,
 397         SROM4_RXCHAIN_MASK},
 398        {BRCMS_SROM_ANTSWITCH, 0xffffff00, SRFL_NOFFS, SROM8_TXRXC,
 399         SROM4_SWITCH_MASK},
 400        {BRCMS_SROM_TSSIPOS2G, 0xffffff00, 0, SROM8_FEM2G,
 401         SROM8_FEM_TSSIPOS_MASK},
 402        {BRCMS_SROM_EXTPAGAIN2G, 0xffffff00, 0, SROM8_FEM2G,
 403         SROM8_FEM_EXTPA_GAIN_MASK},
 404        {BRCMS_SROM_PDETRANGE2G, 0xffffff00, 0, SROM8_FEM2G,
 405         SROM8_FEM_PDET_RANGE_MASK},
 406        {BRCMS_SROM_TRISO2G, 0xffffff00, 0, SROM8_FEM2G, SROM8_FEM_TR_ISO_MASK},
 407        {BRCMS_SROM_ANTSWCTL2G, 0xffffff00, 0, SROM8_FEM2G,
 408         SROM8_FEM_ANTSWLUT_MASK},
 409        {BRCMS_SROM_TSSIPOS5G, 0xffffff00, 0, SROM8_FEM5G,
 410         SROM8_FEM_TSSIPOS_MASK},
 411        {BRCMS_SROM_EXTPAGAIN5G, 0xffffff00, 0, SROM8_FEM5G,
 412         SROM8_FEM_EXTPA_GAIN_MASK},
 413        {BRCMS_SROM_PDETRANGE5G, 0xffffff00, 0, SROM8_FEM5G,
 414         SROM8_FEM_PDET_RANGE_MASK},
 415        {BRCMS_SROM_TRISO5G, 0xffffff00, 0, SROM8_FEM5G, SROM8_FEM_TR_ISO_MASK},
 416        {BRCMS_SROM_ANTSWCTL5G, 0xffffff00, 0, SROM8_FEM5G,
 417         SROM8_FEM_ANTSWLUT_MASK},
 418        {BRCMS_SROM_TEMPTHRESH, 0xffffff00, 0, SROM8_THERMAL, 0xff00},
 419        {BRCMS_SROM_TEMPOFFSET, 0xffffff00, 0, SROM8_THERMAL, 0x00ff},
 420
 421        {BRCMS_SROM_CCODE, 0xffffff00, SRFL_CCODE, SROM8_CCODE, 0xffff},
 422        {BRCMS_SROM_MACADDR, 0xffffff00, SRFL_ETHADDR, SROM8_MACHI, 0xffff},
 423        {BRCMS_SROM_LEDDC, 0xffffff00, SRFL_NOFFS | SRFL_LEDDC, SROM8_LEDDC,
 424         0xffff},
 425        {BRCMS_SROM_RAWTEMPSENSE, 0xffffff00, SRFL_PRHEX, SROM8_MPWR_RAWTS,
 426         0x01ff},
 427        {BRCMS_SROM_MEASPOWER, 0xffffff00, SRFL_PRHEX, SROM8_MPWR_RAWTS,
 428         0xfe00},
 429        {BRCMS_SROM_TEMPSENSE_SLOPE, 0xffffff00, SRFL_PRHEX,
 430         SROM8_TS_SLP_OPT_CORRX, 0x00ff},
 431        {BRCMS_SROM_TEMPCORRX, 0xffffff00, SRFL_PRHEX, SROM8_TS_SLP_OPT_CORRX,
 432         0xfc00},
 433        {BRCMS_SROM_TEMPSENSE_OPTION, 0xffffff00, SRFL_PRHEX,
 434         SROM8_TS_SLP_OPT_CORRX, 0x0300},
 435        {BRCMS_SROM_FREQOFFSET_CORR, 0xffffff00, SRFL_PRHEX,
 436         SROM8_FOC_HWIQ_IQSWP, 0x000f},
 437        {BRCMS_SROM_IQCAL_SWP_DIS, 0xffffff00, SRFL_PRHEX, SROM8_FOC_HWIQ_IQSWP,
 438         0x0010},
 439        {BRCMS_SROM_HW_IQCAL_EN, 0xffffff00, SRFL_PRHEX, SROM8_FOC_HWIQ_IQSWP,
 440         0x0020},
 441        {BRCMS_SROM_PHYCAL_TEMPDELTA, 0xffffff00, 0, SROM8_PHYCAL_TEMPDELTA,
 442         0x00ff},
 443
 444        {BRCMS_SROM_CCK2GPO, 0x00000100, 0, SROM8_2G_CCKPO, 0xffff},
 445        {BRCMS_SROM_OFDM2GPO, 0x00000100, SRFL_MORE, SROM8_2G_OFDMPO, 0xffff},
 446        {BRCMS_SROM_CONT, 0, 0, SROM8_2G_OFDMPO + 1, 0xffff},
 447        {BRCMS_SROM_OFDM5GPO, 0x00000100, SRFL_MORE, SROM8_5G_OFDMPO, 0xffff},
 448        {BRCMS_SROM_CONT, 0, 0, SROM8_5G_OFDMPO + 1, 0xffff},
 449        {BRCMS_SROM_OFDM5GLPO, 0x00000100, SRFL_MORE, SROM8_5GL_OFDMPO, 0xffff},
 450        {BRCMS_SROM_CONT, 0, 0, SROM8_5GL_OFDMPO + 1, 0xffff},
 451        {BRCMS_SROM_OFDM5GHPO, 0x00000100, SRFL_MORE, SROM8_5GH_OFDMPO, 0xffff},
 452        {BRCMS_SROM_CONT, 0, 0, SROM8_5GH_OFDMPO + 1, 0xffff},
 453        {BRCMS_SROM_MCS2GPO0, 0x00000100, 0, SROM8_2G_MCSPO, 0xffff},
 454        {BRCMS_SROM_MCS2GPO1, 0x00000100, 0, SROM8_2G_MCSPO + 1, 0xffff},
 455        {BRCMS_SROM_MCS2GPO2, 0x00000100, 0, SROM8_2G_MCSPO + 2, 0xffff},
 456        {BRCMS_SROM_MCS2GPO3, 0x00000100, 0, SROM8_2G_MCSPO + 3, 0xffff},
 457        {BRCMS_SROM_MCS2GPO4, 0x00000100, 0, SROM8_2G_MCSPO + 4, 0xffff},
 458        {BRCMS_SROM_MCS2GPO5, 0x00000100, 0, SROM8_2G_MCSPO + 5, 0xffff},
 459        {BRCMS_SROM_MCS2GPO6, 0x00000100, 0, SROM8_2G_MCSPO + 6, 0xffff},
 460        {BRCMS_SROM_MCS2GPO7, 0x00000100, 0, SROM8_2G_MCSPO + 7, 0xffff},
 461        {BRCMS_SROM_MCS5GPO0, 0x00000100, 0, SROM8_5G_MCSPO, 0xffff},
 462        {BRCMS_SROM_MCS5GPO1, 0x00000100, 0, SROM8_5G_MCSPO + 1, 0xffff},
 463        {BRCMS_SROM_MCS5GPO2, 0x00000100, 0, SROM8_5G_MCSPO + 2, 0xffff},
 464        {BRCMS_SROM_MCS5GPO3, 0x00000100, 0, SROM8_5G_MCSPO + 3, 0xffff},
 465        {BRCMS_SROM_MCS5GPO4, 0x00000100, 0, SROM8_5G_MCSPO + 4, 0xffff},
 466        {BRCMS_SROM_MCS5GPO5, 0x00000100, 0, SROM8_5G_MCSPO + 5, 0xffff},
 467        {BRCMS_SROM_MCS5GPO6, 0x00000100, 0, SROM8_5G_MCSPO + 6, 0xffff},
 468        {BRCMS_SROM_MCS5GPO7, 0x00000100, 0, SROM8_5G_MCSPO + 7, 0xffff},
 469        {BRCMS_SROM_MCS5GLPO0, 0x00000100, 0, SROM8_5GL_MCSPO, 0xffff},
 470        {BRCMS_SROM_MCS5GLPO1, 0x00000100, 0, SROM8_5GL_MCSPO + 1, 0xffff},
 471        {BRCMS_SROM_MCS5GLPO2, 0x00000100, 0, SROM8_5GL_MCSPO + 2, 0xffff},
 472        {BRCMS_SROM_MCS5GLPO3, 0x00000100, 0, SROM8_5GL_MCSPO + 3, 0xffff},
 473        {BRCMS_SROM_MCS5GLPO4, 0x00000100, 0, SROM8_5GL_MCSPO + 4, 0xffff},
 474        {BRCMS_SROM_MCS5GLPO5, 0x00000100, 0, SROM8_5GL_MCSPO + 5, 0xffff},
 475        {BRCMS_SROM_MCS5GLPO6, 0x00000100, 0, SROM8_5GL_MCSPO + 6, 0xffff},
 476        {BRCMS_SROM_MCS5GLPO7, 0x00000100, 0, SROM8_5GL_MCSPO + 7, 0xffff},
 477        {BRCMS_SROM_MCS5GHPO0, 0x00000100, 0, SROM8_5GH_MCSPO, 0xffff},
 478        {BRCMS_SROM_MCS5GHPO1, 0x00000100, 0, SROM8_5GH_MCSPO + 1, 0xffff},
 479        {BRCMS_SROM_MCS5GHPO2, 0x00000100, 0, SROM8_5GH_MCSPO + 2, 0xffff},
 480        {BRCMS_SROM_MCS5GHPO3, 0x00000100, 0, SROM8_5GH_MCSPO + 3, 0xffff},
 481        {BRCMS_SROM_MCS5GHPO4, 0x00000100, 0, SROM8_5GH_MCSPO + 4, 0xffff},
 482        {BRCMS_SROM_MCS5GHPO5, 0x00000100, 0, SROM8_5GH_MCSPO + 5, 0xffff},
 483        {BRCMS_SROM_MCS5GHPO6, 0x00000100, 0, SROM8_5GH_MCSPO + 6, 0xffff},
 484        {BRCMS_SROM_MCS5GHPO7, 0x00000100, 0, SROM8_5GH_MCSPO + 7, 0xffff},
 485        {BRCMS_SROM_CDDPO, 0x00000100, 0, SROM8_CDDPO, 0xffff},
 486        {BRCMS_SROM_STBCPO, 0x00000100, 0, SROM8_STBCPO, 0xffff},
 487        {BRCMS_SROM_BW40PO, 0x00000100, 0, SROM8_BW40PO, 0xffff},
 488        {BRCMS_SROM_BWDUPPO, 0x00000100, 0, SROM8_BWDUPPO, 0xffff},
 489
 490        /* power per rate from sromrev 9 */
 491        {BRCMS_SROM_CCKBW202GPO, 0xfffffe00, 0, SROM9_2GPO_CCKBW20, 0xffff},
 492        {BRCMS_SROM_CCKBW20UL2GPO, 0xfffffe00, 0, SROM9_2GPO_CCKBW20UL, 0xffff},
 493        {BRCMS_SROM_LEGOFDMBW202GPO, 0xfffffe00, SRFL_MORE,
 494         SROM9_2GPO_LOFDMBW20, 0xffff},
 495        {BRCMS_SROM_CONT, 0, 0, SROM9_2GPO_LOFDMBW20 + 1, 0xffff},
 496        {BRCMS_SROM_LEGOFDMBW20UL2GPO, 0xfffffe00, SRFL_MORE,
 497         SROM9_2GPO_LOFDMBW20UL, 0xffff},
 498        {BRCMS_SROM_CONT, 0, 0, SROM9_2GPO_LOFDMBW20UL + 1, 0xffff},
 499        {BRCMS_SROM_LEGOFDMBW205GLPO, 0xfffffe00, SRFL_MORE,
 500         SROM9_5GLPO_LOFDMBW20, 0xffff},
 501        {BRCMS_SROM_CONT, 0, 0, SROM9_5GLPO_LOFDMBW20 + 1, 0xffff},
 502        {BRCMS_SROM_LEGOFDMBW20UL5GLPO, 0xfffffe00, SRFL_MORE,
 503         SROM9_5GLPO_LOFDMBW20UL, 0xffff},
 504        {BRCMS_SROM_CONT, 0, 0, SROM9_5GLPO_LOFDMBW20UL + 1, 0xffff},
 505        {BRCMS_SROM_LEGOFDMBW205GMPO, 0xfffffe00, SRFL_MORE,
 506         SROM9_5GMPO_LOFDMBW20, 0xffff},
 507        {BRCMS_SROM_CONT, 0, 0, SROM9_5GMPO_LOFDMBW20 + 1, 0xffff},
 508        {BRCMS_SROM_LEGOFDMBW20UL5GMPO, 0xfffffe00, SRFL_MORE,
 509         SROM9_5GMPO_LOFDMBW20UL, 0xffff},
 510        {BRCMS_SROM_CONT, 0, 0, SROM9_5GMPO_LOFDMBW20UL + 1, 0xffff},
 511        {BRCMS_SROM_LEGOFDMBW205GHPO, 0xfffffe00, SRFL_MORE,
 512         SROM9_5GHPO_LOFDMBW20, 0xffff},
 513        {BRCMS_SROM_CONT, 0, 0, SROM9_5GHPO_LOFDMBW20 + 1, 0xffff},
 514        {BRCMS_SROM_LEGOFDMBW20UL5GHPO, 0xfffffe00, SRFL_MORE,
 515         SROM9_5GHPO_LOFDMBW20UL, 0xffff},
 516        {BRCMS_SROM_CONT, 0, 0, SROM9_5GHPO_LOFDMBW20UL + 1, 0xffff},
 517        {BRCMS_SROM_MCSBW202GPO, 0xfffffe00, SRFL_MORE, SROM9_2GPO_MCSBW20,
 518         0xffff},
 519        {BRCMS_SROM_CONT, 0, 0, SROM9_2GPO_MCSBW20 + 1, 0xffff},
 520        {BRCMS_SROM_MCSBW20UL2GPO, 0xfffffe00, SRFL_MORE, SROM9_2GPO_MCSBW20UL,
 521         0xffff},
 522        {BRCMS_SROM_CONT, 0, 0, SROM9_2GPO_MCSBW20UL + 1, 0xffff},
 523        {BRCMS_SROM_MCSBW402GPO, 0xfffffe00, SRFL_MORE, SROM9_2GPO_MCSBW40,
 524         0xffff},
 525        {BRCMS_SROM_CONT, 0, 0, SROM9_2GPO_MCSBW40 + 1, 0xffff},
 526        {BRCMS_SROM_MCSBW205GLPO, 0xfffffe00, SRFL_MORE, SROM9_5GLPO_MCSBW20,
 527         0xffff},
 528        {BRCMS_SROM_CONT, 0, 0, SROM9_5GLPO_MCSBW20 + 1, 0xffff},
 529        {BRCMS_SROM_MCSBW20UL5GLPO, 0xfffffe00, SRFL_MORE,
 530         SROM9_5GLPO_MCSBW20UL, 0xffff},
 531        {BRCMS_SROM_CONT, 0, 0, SROM9_5GLPO_MCSBW20UL + 1, 0xffff},
 532        {BRCMS_SROM_MCSBW405GLPO, 0xfffffe00, SRFL_MORE, SROM9_5GLPO_MCSBW40,
 533         0xffff},
 534        {BRCMS_SROM_CONT, 0, 0, SROM9_5GLPO_MCSBW40 + 1, 0xffff},
 535        {BRCMS_SROM_MCSBW205GMPO, 0xfffffe00, SRFL_MORE, SROM9_5GMPO_MCSBW20,
 536         0xffff},
 537        {BRCMS_SROM_CONT, 0, 0, SROM9_5GMPO_MCSBW20 + 1, 0xffff},
 538        {BRCMS_SROM_MCSBW20UL5GMPO, 0xfffffe00, SRFL_MORE,
 539         SROM9_5GMPO_MCSBW20UL, 0xffff},
 540        {BRCMS_SROM_CONT, 0, 0, SROM9_5GMPO_MCSBW20UL + 1, 0xffff},
 541        {BRCMS_SROM_MCSBW405GMPO, 0xfffffe00, SRFL_MORE, SROM9_5GMPO_MCSBW40,
 542         0xffff},
 543        {BRCMS_SROM_CONT, 0, 0, SROM9_5GMPO_MCSBW40 + 1, 0xffff},
 544        {BRCMS_SROM_MCSBW205GHPO, 0xfffffe00, SRFL_MORE, SROM9_5GHPO_MCSBW20,
 545         0xffff},
 546        {BRCMS_SROM_CONT, 0, 0, SROM9_5GHPO_MCSBW20 + 1, 0xffff},
 547        {BRCMS_SROM_MCSBW20UL5GHPO, 0xfffffe00, SRFL_MORE,
 548         SROM9_5GHPO_MCSBW20UL, 0xffff},
 549        {BRCMS_SROM_CONT, 0, 0, SROM9_5GHPO_MCSBW20UL + 1, 0xffff},
 550        {BRCMS_SROM_MCSBW405GHPO, 0xfffffe00, SRFL_MORE, SROM9_5GHPO_MCSBW40,
 551         0xffff},
 552        {BRCMS_SROM_CONT, 0, 0, SROM9_5GHPO_MCSBW40 + 1, 0xffff},
 553        {BRCMS_SROM_MCS32PO, 0xfffffe00, 0, SROM9_PO_MCS32, 0xffff},
 554        {BRCMS_SROM_LEGOFDM40DUPPO, 0xfffffe00, 0, SROM9_PO_LOFDM40DUP, 0xffff},
 555
 556        {BRCMS_SROM_NULL, 0, 0, 0, 0}
 557};
 558
 559static const struct brcms_sromvar perpath_pci_sromvars[] = {
 560        {BRCMS_SROM_MAXP2GA0, 0xffffff00, 0, SROM8_2G_ITT_MAXP, 0x00ff},
 561        {BRCMS_SROM_ITT2GA0, 0xffffff00, 0, SROM8_2G_ITT_MAXP, 0xff00},
 562        {BRCMS_SROM_ITT5GA0, 0xffffff00, 0, SROM8_5G_ITT_MAXP, 0xff00},
 563        {BRCMS_SROM_PA2GW0A0, 0xffffff00, SRFL_PRHEX, SROM8_2G_PA, 0xffff},
 564        {BRCMS_SROM_PA2GW1A0, 0xffffff00, SRFL_PRHEX, SROM8_2G_PA + 1, 0xffff},
 565        {BRCMS_SROM_PA2GW2A0, 0xffffff00, SRFL_PRHEX, SROM8_2G_PA + 2, 0xffff},
 566        {BRCMS_SROM_MAXP5GA0, 0xffffff00, 0, SROM8_5G_ITT_MAXP, 0x00ff},
 567        {BRCMS_SROM_MAXP5GHA0, 0xffffff00, 0, SROM8_5GLH_MAXP, 0x00ff},
 568        {BRCMS_SROM_MAXP5GLA0, 0xffffff00, 0, SROM8_5GLH_MAXP, 0xff00},
 569        {BRCMS_SROM_PA5GW0A0, 0xffffff00, SRFL_PRHEX, SROM8_5G_PA, 0xffff},
 570        {BRCMS_SROM_PA5GW1A0, 0xffffff00, SRFL_PRHEX, SROM8_5G_PA + 1, 0xffff},
 571        {BRCMS_SROM_PA5GW2A0, 0xffffff00, SRFL_PRHEX, SROM8_5G_PA + 2, 0xffff},
 572        {BRCMS_SROM_PA5GLW0A0, 0xffffff00, SRFL_PRHEX, SROM8_5GL_PA, 0xffff},
 573        {BRCMS_SROM_PA5GLW1A0, 0xffffff00, SRFL_PRHEX, SROM8_5GL_PA + 1,
 574         0xffff},
 575        {BRCMS_SROM_PA5GLW2A0, 0xffffff00, SRFL_PRHEX, SROM8_5GL_PA + 2,
 576         0xffff},
 577        {BRCMS_SROM_PA5GHW0A0, 0xffffff00, SRFL_PRHEX, SROM8_5GH_PA, 0xffff},
 578        {BRCMS_SROM_PA5GHW1A0, 0xffffff00, SRFL_PRHEX, SROM8_5GH_PA + 1,
 579         0xffff},
 580        {BRCMS_SROM_PA5GHW2A0, 0xffffff00, SRFL_PRHEX, SROM8_5GH_PA + 2,
 581         0xffff},
 582        {BRCMS_SROM_NULL, 0, 0, 0, 0}
 583};
 584
 585/* crc table has the same contents for every device instance, so it can be
 586 * shared between devices. */
 587static u8 brcms_srom_crc8_table[CRC8_TABLE_SIZE];
 588
 589static uint mask_shift(u16 mask)
 590{
 591        uint i;
 592        for (i = 0; i < (sizeof(mask) << 3); i++) {
 593                if (mask & (1 << i))
 594                        return i;
 595        }
 596        return 0;
 597}
 598
 599static uint mask_width(u16 mask)
 600{
 601        int i;
 602        for (i = (sizeof(mask) << 3) - 1; i >= 0; i--) {
 603                if (mask & (1 << i))
 604                        return (uint) (i - mask_shift(mask) + 1);
 605        }
 606        return 0;
 607}
 608
 609static inline void le16_to_cpu_buf(u16 *buf, uint nwords)
 610{
 611        while (nwords--)
 612                *(buf + nwords) = le16_to_cpu(*(__le16 *)(buf + nwords));
 613}
 614
 615static inline void cpu_to_le16_buf(u16 *buf, uint nwords)
 616{
 617        while (nwords--)
 618                *(__le16 *)(buf + nwords) = cpu_to_le16(*(buf + nwords));
 619}
 620
 621/*
 622 * convert binary srom data into linked list of srom variable items.
 623 */
 624static void
 625_initvars_srom_pci(u8 sromrev, u16 *srom, struct list_head *var_list)
 626{
 627        struct brcms_srom_list_head *entry;
 628        enum brcms_srom_id id;
 629        u16 w;
 630        u32 val = 0;
 631        const struct brcms_sromvar *srv;
 632        uint width;
 633        uint flags;
 634        u32 sr = (1 << sromrev);
 635        uint p;
 636        uint pb =  SROM8_PATH0;
 637        const uint psz = SROM8_PATH1 - SROM8_PATH0;
 638
 639        /* first store the srom revision */
 640        entry = kzalloc(sizeof(struct brcms_srom_list_head), GFP_KERNEL);
 641        entry->varid = BRCMS_SROM_REV;
 642        entry->var_type = BRCMS_SROM_UNUMBER;
 643        entry->uval = sromrev;
 644        list_add(&entry->var_list, var_list);
 645
 646        for (srv = pci_sromvars; srv->varid != BRCMS_SROM_NULL; srv++) {
 647                enum brcms_srom_var_type type;
 648                u8 ea[ETH_ALEN];
 649                u8 extra_space = 0;
 650
 651                if ((srv->revmask & sr) == 0)
 652                        continue;
 653
 654                flags = srv->flags;
 655                id = srv->varid;
 656
 657                /* This entry is for mfgc only. Don't generate param for it, */
 658                if (flags & SRFL_NOVAR)
 659                        continue;
 660
 661                if (flags & SRFL_ETHADDR) {
 662                        /*
 663                         * stored in string format XX:XX:XX:XX:XX:XX (17 chars)
 664                         */
 665                        ea[0] = (srom[srv->off] >> 8) & 0xff;
 666                        ea[1] = srom[srv->off] & 0xff;
 667                        ea[2] = (srom[srv->off + 1] >> 8) & 0xff;
 668                        ea[3] = srom[srv->off + 1] & 0xff;
 669                        ea[4] = (srom[srv->off + 2] >> 8) & 0xff;
 670                        ea[5] = srom[srv->off + 2] & 0xff;
 671                        /* 17 characters + string terminator - union size */
 672                        extra_space = 18 - sizeof(s32);
 673                        type = BRCMS_SROM_STRING;
 674                } else {
 675                        w = srom[srv->off];
 676                        val = (w & srv->mask) >> mask_shift(srv->mask);
 677                        width = mask_width(srv->mask);
 678
 679                        while (srv->flags & SRFL_MORE) {
 680                                srv++;
 681                                if (srv->off == 0)
 682                                        continue;
 683
 684                                w = srom[srv->off];
 685                                val +=
 686                                    ((w & srv->mask) >> mask_shift(srv->
 687                                                                   mask)) <<
 688                                    width;
 689                                width += mask_width(srv->mask);
 690                        }
 691
 692                        if ((flags & SRFL_NOFFS)
 693                            && ((int)val == (1 << width) - 1))
 694                                continue;
 695
 696                        if (flags & SRFL_CCODE) {
 697                                type = BRCMS_SROM_STRING;
 698                        } else if (flags & SRFL_LEDDC) {
 699                                /* LED Powersave duty cycle has to be scaled:
 700                                 *(oncount >> 24) (offcount >> 8)
 701                                 */
 702                                u32 w32 = /* oncount */
 703                                          (((val >> 8) & 0xff) << 24) |
 704                                          /* offcount */
 705                                          (((val & 0xff)) << 8);
 706                                type = BRCMS_SROM_UNUMBER;
 707                                val = w32;
 708                        } else if ((flags & SRFL_PRSIGN)
 709                                 && (val & (1 << (width - 1)))) {
 710                                type = BRCMS_SROM_SNUMBER;
 711                                val |= ~0 << width;
 712                        } else
 713                                type = BRCMS_SROM_UNUMBER;
 714                }
 715
 716                entry = kzalloc(sizeof(struct brcms_srom_list_head) +
 717                                extra_space, GFP_KERNEL);
 718                entry->varid = id;
 719                entry->var_type = type;
 720                if (flags & SRFL_ETHADDR) {
 721                        snprintf(entry->buf, 18, "%pM", ea);
 722                } else if (flags & SRFL_CCODE) {
 723                        if (val == 0)
 724                                entry->buf[0] = '\0';
 725                        else
 726                                snprintf(entry->buf, 3, "%c%c",
 727                                         (val >> 8), (val & 0xff));
 728                } else {
 729                        entry->uval = val;
 730                }
 731
 732                list_add(&entry->var_list, var_list);
 733        }
 734
 735        for (p = 0; p < MAX_PATH_SROM; p++) {
 736                for (srv = perpath_pci_sromvars;
 737                     srv->varid != BRCMS_SROM_NULL; srv++) {
 738                        if ((srv->revmask & sr) == 0)
 739                                continue;
 740
 741                        if (srv->flags & SRFL_NOVAR)
 742                                continue;
 743
 744                        w = srom[pb + srv->off];
 745                        val = (w & srv->mask) >> mask_shift(srv->mask);
 746                        width = mask_width(srv->mask);
 747
 748                        /* Cheating: no per-path var is more than
 749                         * 1 word */
 750                        if ((srv->flags & SRFL_NOFFS)
 751                            && ((int)val == (1 << width) - 1))
 752                                continue;
 753
 754                        entry =
 755                            kzalloc(sizeof(struct brcms_srom_list_head),
 756                                    GFP_KERNEL);
 757                        entry->varid = srv->varid+p;
 758                        entry->var_type = BRCMS_SROM_UNUMBER;
 759                        entry->uval = val;
 760                        list_add(&entry->var_list, var_list);
 761                }
 762                pb += psz;
 763        }
 764}
 765
 766/*
 767 * The crc check is done on a little-endian array, we need
 768 * to switch the bytes around before checking crc (and
 769 * then switch it back).
 770 */
 771static int do_crc_check(u16 *buf, unsigned nwords)
 772{
 773        u8 crc;
 774
 775        cpu_to_le16_buf(buf, nwords);
 776        crc = crc8(brcms_srom_crc8_table, (void *)buf, nwords << 1, CRC8_INIT_VALUE);
 777        le16_to_cpu_buf(buf, nwords);
 778
 779        return crc == CRC8_GOOD_VALUE(brcms_srom_crc8_table);
 780}
 781
 782/*
 783 * Read in and validate sprom.
 784 * Return 0 on success, nonzero on error.
 785 */
 786static int
 787sprom_read_pci(struct si_pub *sih, u16 *buf, uint nwords, bool check_crc)
 788{
 789        int err = 0;
 790        uint i;
 791        struct bcma_device *core;
 792        uint sprom_offset;
 793
 794        /* determine core to read */
 795        if (ai_get_ccrev(sih) < 32) {
 796                core = ai_findcore(sih, BCMA_CORE_80211, 0);
 797                sprom_offset = PCI_BAR0_SPROM_OFFSET;
 798        } else {
 799                core = ai_findcore(sih, BCMA_CORE_CHIPCOMMON, 0);
 800                sprom_offset = CHIPCREGOFFS(sromotp);
 801        }
 802
 803        /* read the sprom */
 804        for (i = 0; i < nwords; i++)
 805                buf[i] = bcma_read16(core, sprom_offset+i*2);
 806
 807        if (buf[0] == 0xffff)
 808                /*
 809                 * The hardware thinks that an srom that starts with
 810                 * 0xffff is blank, regardless of the rest of the
 811                 * content, so declare it bad.
 812                 */
 813                return -ENODATA;
 814
 815        if (check_crc && !do_crc_check(buf, nwords))
 816                err = -EIO;
 817
 818        return err;
 819}
 820
 821static int otp_read_pci(struct si_pub *sih, u16 *buf, uint nwords)
 822{
 823        u8 *otp;
 824        uint sz = OTP_SZ_MAX / 2;       /* size in words */
 825        int err = 0;
 826
 827        otp = kzalloc(OTP_SZ_MAX, GFP_ATOMIC);
 828        if (otp == NULL)
 829                return -ENOMEM;
 830
 831        err = otp_read_region(sih, OTP_HW_RGN, (u16 *) otp, &sz);
 832
 833        sz = min_t(uint, sz, nwords);
 834        memcpy(buf, otp, sz * 2);
 835
 836        kfree(otp);
 837
 838        /* Check CRC */
 839        if (buf[0] == 0xffff)
 840                /* The hardware thinks that an srom that starts with 0xffff
 841                 * is blank, regardless of the rest of the content, so declare
 842                 * it bad.
 843                 */
 844                return -ENODATA;
 845
 846        /* fixup the endianness so crc8 will pass */
 847        cpu_to_le16_buf(buf, sz);
 848        if (crc8(brcms_srom_crc8_table, (u8 *) buf, sz * 2,
 849                 CRC8_INIT_VALUE) != CRC8_GOOD_VALUE(brcms_srom_crc8_table))
 850                err = -EIO;
 851        else
 852                /* now correct the endianness of the byte array */
 853                le16_to_cpu_buf(buf, sz);
 854
 855        return err;
 856}
 857
 858/*
 859 * Initialize nonvolatile variable table from sprom.
 860 * Return 0 on success, nonzero on error.
 861 */
 862int srom_var_init(struct si_pub *sih)
 863{
 864        u16 *srom;
 865        u8 sromrev = 0;
 866        u32 sr;
 867        int err = 0;
 868
 869        /*
 870         * Apply CRC over SROM content regardless SROM is present or not.
 871         */
 872        srom = kmalloc(SROM_MAX, GFP_ATOMIC);
 873        if (!srom)
 874                return -ENOMEM;
 875
 876        crc8_populate_lsb(brcms_srom_crc8_table, SROM_CRC8_POLY);
 877        if (ai_is_sprom_available(sih)) {
 878                err = sprom_read_pci(sih, srom, SROM4_WORDS, true);
 879
 880                if (err == 0)
 881                        /* srom read and passed crc */
 882                        /* top word of sprom contains version and crc8 */
 883                        sromrev = srom[SROM4_CRCREV] & 0xff;
 884        } else {
 885                /* Use OTP if SPROM not available */
 886                err = otp_read_pci(sih, srom, SROM4_WORDS);
 887                if (err == 0)
 888                        /* OTP only contain SROM rev8/rev9 for now */
 889                        sromrev = srom[SROM4_CRCREV] & 0xff;
 890        }
 891
 892        if (!err) {
 893                struct si_info *sii = (struct si_info *)sih;
 894
 895                /* Bitmask for the sromrev */
 896                sr = 1 << sromrev;
 897
 898                /*
 899                 * srom version check: Current valid versions: 8, 9
 900                 */
 901                if ((sr & 0x300) == 0) {
 902                        err = -EINVAL;
 903                        goto errout;
 904                }
 905
 906                INIT_LIST_HEAD(&sii->var_list);
 907
 908                /* parse SROM into name=value pairs. */
 909                _initvars_srom_pci(sromrev, srom, &sii->var_list);
 910        }
 911
 912errout:
 913        kfree(srom);
 914        return err;
 915}
 916
 917void srom_free_vars(struct si_pub *sih)
 918{
 919        struct si_info *sii;
 920        struct brcms_srom_list_head *entry, *next;
 921
 922        sii = (struct si_info *)sih;
 923        list_for_each_entry_safe(entry, next, &sii->var_list, var_list) {
 924                list_del(&entry->var_list);
 925                kfree(entry);
 926        }
 927}
 928
 929/*
 930 * Search the name=value vars for a specific one and return its value.
 931 * Returns NULL if not found.
 932 */
 933char *getvar(struct si_pub *sih, enum brcms_srom_id id)
 934{
 935        struct si_info *sii;
 936        struct brcms_srom_list_head *entry;
 937
 938        sii = (struct si_info *)sih;
 939
 940        list_for_each_entry(entry, &sii->var_list, var_list)
 941                if (entry->varid == id)
 942                        return &entry->buf[0];
 943
 944        /* nothing found */
 945        return NULL;
 946}
 947
 948/*
 949 * Search the vars for a specific one and return its value as
 950 * an integer. Returns 0 if not found.-
 951 */
 952int getintvar(struct si_pub *sih, enum brcms_srom_id id)
 953{
 954        struct si_info *sii;
 955        struct brcms_srom_list_head *entry;
 956        unsigned long res;
 957
 958        sii = (struct si_info *)sih;
 959
 960        list_for_each_entry(entry, &sii->var_list, var_list)
 961                if (entry->varid == id) {
 962                        if (entry->var_type == BRCMS_SROM_SNUMBER ||
 963                            entry->var_type == BRCMS_SROM_UNUMBER)
 964                                return (int)entry->sval;
 965                        else if (!kstrtoul(&entry->buf[0], 0, &res))
 966                                return (int)res;
 967                }
 968
 969        return 0;
 970}
 971
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.