Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sprom.c
Go to the documentation of this file.
1 /*
2  * Broadcom specific AMBA
3  * SPROM reading
4  *
5  * Copyright 2011, 2012, Hauke Mehrtens <[email protected]>
6  *
7  * Licensed under the GNU/GPL. See COPYING for details.
8  */
9 
10 #include "bcma_private.h"
11 
12 #include <linux/bcma/bcma.h>
13 #include <linux/bcma/bcma_regs.h>
14 #include <linux/pci.h>
15 #include <linux/io.h>
16 #include <linux/dma-mapping.h>
17 #include <linux/slab.h>
18 
19 static int(*get_fallback_sprom)(struct bcma_bus *dev, struct ssb_sprom *out);
20 
39 int bcma_arch_register_fallback_sprom(int (*sprom_callback)(struct bcma_bus *bus,
40  struct ssb_sprom *out))
41 {
42  if (get_fallback_sprom)
43  return -EEXIST;
44  get_fallback_sprom = sprom_callback;
45 
46  return 0;
47 }
48 
49 static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus,
50  struct ssb_sprom *out)
51 {
52  int err;
53 
54  if (!get_fallback_sprom) {
55  err = -ENOENT;
56  goto fail;
57  }
58 
59  err = get_fallback_sprom(bus, out);
60  if (err)
61  goto fail;
62 
63  bcma_debug(bus, "Using SPROM revision %d provided by platform.\n",
64  bus->sprom.revision);
65  return 0;
66 fail:
67  bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err);
68  return err;
69 }
70 
71 /**************************************************
72  * R/W ops.
73  **************************************************/
74 
75 static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
76 {
77  int i;
78  for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
79  sprom[i] = bcma_read16(bus->drv_cc.core,
80  offset + (i * 2));
81 }
82 
83 /**************************************************
84  * Validation.
85  **************************************************/
86 
87 static inline u8 bcma_crc8(u8 crc, u8 data)
88 {
89  /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */
90  static const u8 t[] = {
91  0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
92  0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
93  0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
94  0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
95  0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
96  0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
97  0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
98  0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
99  0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
100  0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
101  0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
102  0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
103  0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
104  0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
105  0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
106  0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
107  0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
108  0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
109  0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
110  0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
111  0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
112  0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
113  0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
114  0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
115  0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
116  0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
117  0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
118  0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
119  0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
120  0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
121  0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
122  0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
123  };
124  return t[crc ^ data];
125 }
126 
127 static u8 bcma_sprom_crc(const u16 *sprom)
128 {
129  int word;
130  u8 crc = 0xFF;
131 
132  for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
133  crc = bcma_crc8(crc, sprom[word] & 0x00FF);
134  crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
135  }
136  crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
137  crc ^= 0xFF;
138 
139  return crc;
140 }
141 
142 static int bcma_sprom_check_crc(const u16 *sprom)
143 {
144  u8 crc;
145  u8 expected_crc;
146  u16 tmp;
147 
148  crc = bcma_sprom_crc(sprom);
149  tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
150  expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
151  if (crc != expected_crc)
152  return -EPROTO;
153 
154  return 0;
155 }
156 
157 static int bcma_sprom_valid(const u16 *sprom)
158 {
159  u16 revision;
160  int err;
161 
162  err = bcma_sprom_check_crc(sprom);
163  if (err)
164  return err;
165 
166  revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
167  if (revision != 8 && revision != 9) {
168  pr_err("Unsupported SPROM revision: %d\n", revision);
169  return -ENOENT;
170  }
171 
172  return 0;
173 }
174 
175 /**************************************************
176  * SPROM extraction.
177  **************************************************/
178 
179 #define SPOFF(offset) ((offset) / sizeof(u16))
180 
181 #define SPEX(_field, _offset, _mask, _shift) \
182  bus->sprom._field = ((sprom[SPOFF(_offset)] & (_mask)) >> (_shift))
183 
184 #define SPEX32(_field, _offset, _mask, _shift) \
185  bus->sprom._field = ((((u32)sprom[SPOFF((_offset)+2)] << 16 | \
186  sprom[SPOFF(_offset)]) & (_mask)) >> (_shift))
187 
188 #define SPEX_ARRAY8(_field, _offset, _mask, _shift) \
189  do { \
190  SPEX(_field[0], _offset + 0, _mask, _shift); \
191  SPEX(_field[1], _offset + 2, _mask, _shift); \
192  SPEX(_field[2], _offset + 4, _mask, _shift); \
193  SPEX(_field[3], _offset + 6, _mask, _shift); \
194  SPEX(_field[4], _offset + 8, _mask, _shift); \
195  SPEX(_field[5], _offset + 10, _mask, _shift); \
196  SPEX(_field[6], _offset + 12, _mask, _shift); \
197  SPEX(_field[7], _offset + 14, _mask, _shift); \
198  } while (0)
199 
200 static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
201 {
202  u16 v, o;
203  int i;
204  u16 pwr_info_offset[] = {
207  };
208  BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
209  ARRAY_SIZE(bus->sprom.core_pwr_info));
210 
211  bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
213 
214  for (i = 0; i < 3; i++) {
215  v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
216  *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
217  }
218 
220 
229 
238 
247 
256 
261 
262  SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
263  SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
264 
265  /* Extract cores power info info */
266  for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
267  o = pwr_info_offset[i];
268  SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
270  SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
271  SSB_SPROM8_2G_MAXP, 0);
272 
273  SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
274  SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
275  SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
276 
277  SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
279  SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
280  SSB_SPROM8_5G_MAXP, 0);
281  SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
283  SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
285 
286  SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
287  SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
288  SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
289  SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
290  SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
291  SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
292  SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
293  SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
294  SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
295  }
296 
307 
318 
362 
363  SPEX(pa0b0, SSB_SPROM8_PA0B0, ~0, 0);
364  SPEX(pa0b1, SSB_SPROM8_PA0B1, ~0, 0);
365  SPEX(pa0b2, SSB_SPROM8_PA0B2, ~0, 0);
366  SPEX(pa1b0, SSB_SPROM8_PA1B0, ~0, 0);
367  SPEX(pa1b1, SSB_SPROM8_PA1B1, ~0, 0);
368  SPEX(pa1b2, SSB_SPROM8_PA1B2, ~0, 0);
380 
381  /* Extract the antenna gain values. */
390 
395 
402 
403  SPEX(opo, SSB_SPROM8_OFDM2GPO, 0x00ff, 0);
404 
409 
430 
431  SPEX(bw40po, SSB_SPROM8_BW40PO, ~0, 0);
432  SPEX(cddpo, SSB_SPROM8_CDDPO, ~0, 0);
433  SPEX(stbcpo, SSB_SPROM8_STBCPO, ~0, 0);
435 
448 }
449 
450 /*
451  * Indicates the presence of external SPROM.
452  */
453 static bool bcma_sprom_ext_available(struct bcma_bus *bus)
454 {
455  u32 chip_status;
456  u32 srom_control;
457  u32 present_mask;
458 
459  if (bus->drv_cc.core->id.rev >= 31) {
460  if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
461  return false;
462 
463  srom_control = bcma_read32(bus->drv_cc.core,
465  return srom_control & BCMA_CC_SROM_CONTROL_PRESENT;
466  }
467 
468  /* older chipcommon revisions use chip status register */
469  chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
470  switch (bus->chipinfo.id) {
472  present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT;
473  break;
474 
476  present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT;
477  break;
478 
479  default:
480  return true;
481  }
482 
483  return chip_status & present_mask;
484 }
485 
486 /*
487  * Indicates that on-chip OTP memory is present and enabled.
488  */
489 static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
490 {
491  u32 chip_status;
492  u32 otpsize = 0;
493  bool present;
494 
495  chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
496  switch (bus->chipinfo.id) {
498  present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT;
499  break;
500 
502  present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
503  break;
504 
507  /* for these chips OTP is always available */
508  present = true;
509  break;
513  present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT;
514  break;
515  default:
516  present = false;
517  break;
518  }
519 
520  if (present) {
521  otpsize = bus->drv_cc.capabilities & BCMA_CC_CAP_OTPS;
522  otpsize >>= BCMA_CC_CAP_OTPS_SHIFT;
523  }
524 
525  return otpsize != 0;
526 }
527 
528 /*
529  * Verify OTP is filled and determine the byte
530  * offset where SPROM data is located.
531  *
532  * On error, returns 0; byte offset otherwise.
533  */
534 static int bcma_sprom_onchip_offset(struct bcma_bus *bus)
535 {
536  struct bcma_device *cc = bus->drv_cc.core;
537  u32 offset;
538 
539  /* verify OTP status */
540  if ((bcma_read32(cc, BCMA_CC_OTPS) & BCMA_CC_OTPS_GU_PROG_HW) == 0)
541  return 0;
542 
543  /* obtain bit offset from otplayout register */
544  offset = (bcma_read32(cc, BCMA_CC_OTPL) & BCMA_CC_OTPL_GURGN_OFFSET);
545  return BCMA_CC_SPROM + (offset >> 3);
546 }
547 
548 int bcma_sprom_get(struct bcma_bus *bus)
549 {
550  u16 offset = BCMA_CC_SPROM;
551  u16 *sprom;
552  int err = 0;
553 
554  if (!bus->drv_cc.core)
555  return -EOPNOTSUPP;
556 
557  if (!bcma_sprom_ext_available(bus)) {
558  bool sprom_onchip;
559 
560  /*
561  * External SPROM takes precedence so check
562  * on-chip OTP only when no external SPROM
563  * is present.
564  */
565  sprom_onchip = bcma_sprom_onchip_available(bus);
566  if (sprom_onchip) {
567  /* determine offset */
568  offset = bcma_sprom_onchip_offset(bus);
569  }
570  if (!offset || !sprom_onchip) {
571  /*
572  * Maybe there is no SPROM on the device?
573  * Now we ask the arch code if there is some sprom
574  * available for this device in some other storage.
575  */
576  err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
577  return err;
578  }
579  }
580 
581  sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
582  GFP_KERNEL);
583  if (!sprom)
584  return -ENOMEM;
585 
586  if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
587  bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
589 
590  bcma_debug(bus, "SPROM offset 0x%x\n", offset);
591  bcma_sprom_read(bus, offset, sprom);
592 
593  if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
594  bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
596 
597  err = bcma_sprom_valid(sprom);
598  if (err)
599  goto out;
600 
601  bcma_sprom_extract_r8(bus, sprom);
602 
603 out:
604  kfree(sprom);
605  return err;
606 }