diff options
author | Arend van Spriel <arend@broadcom.com> | 2012-03-06 09:50:48 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-03-06 15:16:18 -0500 |
commit | 10d8493cd9efd38b1947b7a74276dbdc8311aa1a (patch) | |
tree | 69caa6570a2582667783dd03c8ce5c204881f398 /drivers/bcma/sprom.c | |
parent | 4ac887cfdad909f16ee1886fe4fa19b452fc7fd3 (diff) |
bcma: add support for on-chip OTP memory used for SPROM storage
Wireless Broadcom chips can have either their SPROM data stored
on either external SPROM or on-chip OTP memory. Both are accessed
through the same register space. This patch adds support for the
on-chip OTP memory.
Tested with:
BCM43224 OTP and SPROM
BCM4331 SPROM
BCM4313 OTP
This patch is in response to linux-wireless thread [1].
[1] http://article.gmane.org/gmane.linux.kernel.wireless.general/85426
Tested-by: Saul St. John <saul.stjohn@gmail.com>
Tested-by: Rafal Milecki <zajec5@gmail.com>
Tested-by: Hauke Mehrtens <hauke@hauke-m.de>
Cc: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/bcma/sprom.c')
-rw-r--r-- | drivers/bcma/sprom.c | 126 |
1 files changed, 106 insertions, 20 deletions
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index fba8066857d2..cdcf75c0954f 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c | |||
@@ -300,37 +300,128 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) | |||
300 | SSB_SROM8_FEM_ANTSWLUT_SHIFT); | 300 | SSB_SROM8_FEM_ANTSWLUT_SHIFT); |
301 | } | 301 | } |
302 | 302 | ||
303 | static bool bcma_is_sprom_available(struct bcma_bus *bus) | 303 | /* |
304 | * Indicates the presence of external SPROM. | ||
305 | */ | ||
306 | static bool bcma_sprom_ext_available(struct bcma_bus *bus) | ||
304 | { | 307 | { |
305 | u32 sromctrl; | 308 | u32 chip_status; |
309 | u32 srom_control; | ||
310 | u32 present_mask; | ||
306 | 311 | ||
307 | if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) | 312 | if (bus->drv_cc.core->id.rev >= 31) { |
308 | return false; | 313 | if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) |
314 | return false; | ||
309 | 315 | ||
310 | if (bus->drv_cc.core->id.rev >= 32) { | 316 | srom_control = bcma_read32(bus->drv_cc.core, |
311 | sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); | 317 | BCMA_CC_SROM_CONTROL); |
312 | return sromctrl & BCMA_CC_SROM_CONTROL_PRESENT; | 318 | return srom_control & BCMA_CC_SROM_CONTROL_PRESENT; |
313 | } | 319 | } |
314 | return true; | 320 | |
321 | /* older chipcommon revisions use chip status register */ | ||
322 | chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT); | ||
323 | switch (bus->chipinfo.id) { | ||
324 | case 0x4313: | ||
325 | present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT; | ||
326 | break; | ||
327 | |||
328 | case 0x4331: | ||
329 | present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT; | ||
330 | break; | ||
331 | |||
332 | default: | ||
333 | return true; | ||
334 | } | ||
335 | |||
336 | return chip_status & present_mask; | ||
337 | } | ||
338 | |||
339 | /* | ||
340 | * Indicates that on-chip OTP memory is present and enabled. | ||
341 | */ | ||
342 | static bool bcma_sprom_onchip_available(struct bcma_bus *bus) | ||
343 | { | ||
344 | u32 chip_status; | ||
345 | u32 otpsize = 0; | ||
346 | bool present; | ||
347 | |||
348 | chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT); | ||
349 | switch (bus->chipinfo.id) { | ||
350 | case 0x4313: | ||
351 | present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT; | ||
352 | break; | ||
353 | |||
354 | case 0x4331: | ||
355 | present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT; | ||
356 | break; | ||
357 | |||
358 | case 43224: | ||
359 | case 43225: | ||
360 | /* for these chips OTP is always available */ | ||
361 | present = true; | ||
362 | break; | ||
363 | |||
364 | default: | ||
365 | present = false; | ||
366 | break; | ||
367 | } | ||
368 | |||
369 | if (present) { | ||
370 | otpsize = bus->drv_cc.capabilities & BCMA_CC_CAP_OTPS; | ||
371 | otpsize >>= BCMA_CC_CAP_OTPS_SHIFT; | ||
372 | } | ||
373 | |||
374 | return otpsize != 0; | ||
375 | } | ||
376 | |||
377 | /* | ||
378 | * Verify OTP is filled and determine the byte | ||
379 | * offset where SPROM data is located. | ||
380 | * | ||
381 | * On error, returns 0; byte offset otherwise. | ||
382 | */ | ||
383 | static int bcma_sprom_onchip_offset(struct bcma_bus *bus) | ||
384 | { | ||
385 | struct bcma_device *cc = bus->drv_cc.core; | ||
386 | u32 offset; | ||
387 | |||
388 | /* verify OTP status */ | ||
389 | if ((bcma_read32(cc, BCMA_CC_OTPS) & BCMA_CC_OTPS_GU_PROG_HW) == 0) | ||
390 | return 0; | ||
391 | |||
392 | /* obtain bit offset from otplayout register */ | ||
393 | offset = (bcma_read32(cc, BCMA_CC_OTPL) & BCMA_CC_OTPL_GURGN_OFFSET); | ||
394 | return BCMA_CC_SPROM + (offset >> 3); | ||
315 | } | 395 | } |
316 | 396 | ||
317 | int bcma_sprom_get(struct bcma_bus *bus) | 397 | int bcma_sprom_get(struct bcma_bus *bus) |
318 | { | 398 | { |
319 | u16 offset; | 399 | u16 offset = BCMA_CC_SPROM; |
320 | u16 *sprom; | 400 | u16 *sprom; |
321 | int err = 0; | 401 | int err = 0; |
322 | 402 | ||
323 | if (!bus->drv_cc.core) | 403 | if (!bus->drv_cc.core) |
324 | return -EOPNOTSUPP; | 404 | return -EOPNOTSUPP; |
325 | 405 | ||
326 | if (!bcma_is_sprom_available(bus)) { | 406 | if (!bcma_sprom_ext_available(bus)) { |
327 | /* | 407 | /* |
328 | * Maybe there is no SPROM on the device? | 408 | * External SPROM takes precedence so check |
329 | * Now we ask the arch code if there is some sprom | 409 | * on-chip OTP only when no external SPROM |
330 | * available for this device in some other storage. | 410 | * is present. |
331 | */ | 411 | */ |
332 | err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); | 412 | if (bcma_sprom_onchip_available(bus)) { |
333 | return err; | 413 | /* determine offset */ |
414 | offset = bcma_sprom_onchip_offset(bus); | ||
415 | } | ||
416 | if (!offset) { | ||
417 | /* | ||
418 | * Maybe there is no SPROM on the device? | ||
419 | * Now we ask the arch code if there is some sprom | ||
420 | * available for this device in some other storage. | ||
421 | */ | ||
422 | err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); | ||
423 | return err; | ||
424 | } | ||
334 | } | 425 | } |
335 | 426 | ||
336 | sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), | 427 | sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), |
@@ -341,11 +432,6 @@ int bcma_sprom_get(struct bcma_bus *bus) | |||
341 | if (bus->chipinfo.id == 0x4331) | 432 | if (bus->chipinfo.id == 0x4331) |
342 | bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); | 433 | bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); |
343 | 434 | ||
344 | /* Most cards have SPROM moved by additional offset 0x30 (48 dwords). | ||
345 | * According to brcm80211 this applies to cards with PCIe rev >= 6 | ||
346 | * TODO: understand this condition and use it */ | ||
347 | offset = (bus->chipinfo.id == 0x4331) ? BCMA_CC_SPROM : | ||
348 | BCMA_CC_SPROM_PCIE6; | ||
349 | pr_debug("SPROM offset 0x%x\n", offset); | 435 | pr_debug("SPROM offset 0x%x\n", offset); |
350 | bcma_sprom_read(bus, offset, sprom); | 436 | bcma_sprom_read(bus, offset, sprom); |
351 | 437 | ||