Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Arend van Spriel and committed by
John W. Linville
10d8493c 4ac887cf

+115 -21
+106 -20
drivers/bcma/sprom.c
··· 300 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)) 308 - return false; 312 + if (bus->drv_cc.core->id.rev >= 31) { 313 + if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) 314 + return false; 309 315 310 - if (bus->drv_cc.core->id.rev >= 32) { 311 - sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); 312 - return sromctrl & BCMA_CC_SROM_CONTROL_PRESENT; 316 + srom_control = bcma_read32(bus->drv_cc.core, 317 + BCMA_CC_SROM_CONTROL); 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 397 int bcma_sprom_get(struct bcma_bus *bus) 318 398 { 319 - u16 offset; 399 + u16 offset = BCMA_CC_SPROM; 320 400 u16 *sprom; 321 401 int err = 0; 322 402 323 403 if (!bus->drv_cc.core) 324 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? 329 - * Now we ask the arch code if there is some sprom 330 - * available for this device in some other storage. 408 + * External SPROM takes precedence so check 409 + * on-chip OTP only when no external SPROM 410 + * is present. 331 411 */ 332 - err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); 333 - return err; 412 + if (bcma_sprom_onchip_available(bus)) { 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 427 sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), ··· 432 341 if (bus->chipinfo.id == 0x4331) 433 342 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); 434 343 435 - /* Most cards have SPROM moved by additional offset 0x30 (48 dwords). 436 - * According to brcm80211 this applies to cards with PCIe rev >= 6 437 - * TODO: understand this condition and use it */ 438 - offset = (bus->chipinfo.id == 0x4331) ? BCMA_CC_SPROM : 439 - BCMA_CC_SPROM_PCIE6; 440 344 pr_debug("SPROM offset 0x%x\n", offset); 441 345 bcma_sprom_read(bus, offset, sprom); 442 346
+9 -1
include/linux/bcma/bcma_driver_chipcommon.h
··· 56 56 #define BCMA_CC_OTPS_HW_PROTECT 0x00000001 57 57 #define BCMA_CC_OTPS_SW_PROTECT 0x00000002 58 58 #define BCMA_CC_OTPS_CID_PROTECT 0x00000004 59 + #define BCMA_CC_OTPS_GU_PROG_IND 0x00000F00 /* General Use programmed indication */ 60 + #define BCMA_CC_OTPS_GU_PROG_IND_SHIFT 8 61 + #define BCMA_CC_OTPS_GU_PROG_HW 0x00000100 /* HW region programmed */ 59 62 #define BCMA_CC_OTPC 0x0014 /* OTP control */ 60 63 #define BCMA_CC_OTPC_RECWAIT 0xFF000000 61 64 #define BCMA_CC_OTPC_PROGWAIT 0x00FFFF00 ··· 75 72 #define BCMA_CC_OTPP_READ 0x40000000 76 73 #define BCMA_CC_OTPP_START 0x80000000 77 74 #define BCMA_CC_OTPP_BUSY 0x80000000 75 + #define BCMA_CC_OTPL 0x001C /* OTP layout */ 76 + #define BCMA_CC_OTPL_GURGN_OFFSET 0x00000FFF /* offset of general use region */ 78 77 #define BCMA_CC_IRQSTAT 0x0020 79 78 #define BCMA_CC_IRQMASK 0x0024 80 79 #define BCMA_CC_IRQ_GPIO 0x00000001 /* gpio intr */ ··· 84 79 #define BCMA_CC_IRQ_WDRESET 0x80000000 /* watchdog reset occurred */ 85 80 #define BCMA_CC_CHIPCTL 0x0028 /* Rev >= 11 only */ 86 81 #define BCMA_CC_CHIPSTAT 0x002C /* Rev >= 11 only */ 82 + #define BCMA_CC_CHIPST_4313_SPROM_PRESENT 1 83 + #define BCMA_CC_CHIPST_4313_OTP_PRESENT 2 84 + #define BCMA_CC_CHIPST_4331_SPROM_PRESENT 2 85 + #define BCMA_CC_CHIPST_4331_OTP_PRESENT 4 87 86 #define BCMA_CC_JCMD 0x0030 /* Rev >= 10 only */ 88 87 #define BCMA_CC_JCMD_START 0x80000000 89 88 #define BCMA_CC_JCMD_BUSY 0x80000000 ··· 265 256 #define BCMA_CC_PLLCTL_ADDR 0x0660 266 257 #define BCMA_CC_PLLCTL_DATA 0x0664 267 258 #define BCMA_CC_SPROM 0x0800 /* SPROM beginning */ 268 - #define BCMA_CC_SPROM_PCIE6 0x0830 /* SPROM beginning on PCIe rev >= 6 */ 269 259 270 260 /* Divider allocation in 4716/47162/5356 */ 271 261 #define BCMA_CC_PMU5_MAINPLL_CPU 1