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

ssb: do not read SPROM if it does not exist

Attempting to read registers that don't exist on the SSB bus can cause
hangs on some boxes. At least some b43 devices are 'in the wild' that
don't have SPROMs at all. When the SSB bus support loads, it attempts
to read these (non-existant) SPROMs and causes hard hangs on the box --
no console output, etc.

This patch adds some intelligence to determine whether or not the SPROM
is present before attempting to read it. This avoids those hard hangs
on those devices with no SPROM attached to their SSB bus. The
SSB-attached devices (e.g. b43, et al.) won't work, but at least the box
will survive to test further patches. :-)

Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Cc: Larry Finger <Larry.Finger@lwfinger.net>
Cc: Michael Buesch <mb@bu3sch.de>

+39
+2
drivers/ssb/driver_chipcommon.c
··· 233 233 { 234 234 if (!cc->dev) 235 235 return; /* We don't have a ChipCommon */ 236 + if (cc->dev->id.revision >= 11) 237 + cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT); 236 238 ssb_pmu_init(cc); 237 239 chipco_powercontrol_init(cc); 238 240 ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
+5
drivers/ssb/pci.c
··· 620 620 int err = -ENOMEM; 621 621 u16 *buf; 622 622 623 + if (!ssb_is_sprom_available(bus)) { 624 + ssb_printk(KERN_ERR PFX "No SPROM available!\n"); 625 + return -ENODEV; 626 + } 627 + 623 628 buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL); 624 629 if (!buf) 625 630 goto out;
+14
drivers/ssb/sprom.c
··· 175 175 { 176 176 return fallback_sprom; 177 177 } 178 + 179 + /* http://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */ 180 + bool ssb_is_sprom_available(struct ssb_bus *bus) 181 + { 182 + /* status register only exists on chipcomon rev >= 11 and we need check 183 + for >= 31 only */ 184 + /* this routine differs from specs as we do not access SPROM directly 185 + on PCMCIA */ 186 + if (bus->bustype == SSB_BUSTYPE_PCI && 187 + bus->chipco.dev->id.revision >= 31) 188 + return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM; 189 + 190 + return true; 191 + }
+3
include/linux/ssb/ssb.h
··· 394 394 395 395 extern void ssb_bus_unregister(struct ssb_bus *bus); 396 396 397 + /* Does the device have an SPROM? */ 398 + extern bool ssb_is_sprom_available(struct ssb_bus *bus); 399 + 397 400 /* Set a fallback SPROM. 398 401 * See kdoc at the function definition for complete documentation. */ 399 402 extern int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom);
+15
include/linux/ssb/ssb_driver_chipcommon.h
··· 53 53 #define SSB_CHIPCO_CAP_64BIT 0x08000000 /* 64-bit Backplane */ 54 54 #define SSB_CHIPCO_CAP_PMU 0x10000000 /* PMU available (rev >= 20) */ 55 55 #define SSB_CHIPCO_CAP_ECI 0x20000000 /* ECI available (rev >= 20) */ 56 + #define SSB_CHIPCO_CAP_SPROM 0x40000000 /* SPROM present */ 56 57 #define SSB_CHIPCO_CORECTL 0x0008 57 58 #define SSB_CHIPCO_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */ 58 59 #define SSB_CHIPCO_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */ ··· 386 385 387 386 388 387 /** Chip specific Chip-Status register contents. */ 388 + #define SSB_CHIPCO_CHST_4322_SPROM_EXISTS 0x00000040 /* SPROM present */ 389 389 #define SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL 0x00000003 390 390 #define SSB_CHIPCO_CHST_4325_DEFCIS_SEL 0 /* OTP is powered up, use def. CIS, no SPROM */ 391 391 #define SSB_CHIPCO_CHST_4325_SPROM_SEL 1 /* OTP is powered up, SPROM is present */ ··· 399 397 #define SSB_CHIPCO_CHST_4325_RCAL_VALUE 0x000001F0 400 398 #define SSB_CHIPCO_CHST_4325_RCAL_VALUE_SHIFT 4 401 399 #define SSB_CHIPCO_CHST_4325_PMUTOP_2B 0x00000200 /* 1 for 2b, 0 for to 2a */ 400 + 401 + /** Macros to determine SPROM presence based on Chip-Status register. */ 402 + #define SSB_CHIPCO_CHST_4312_SPROM_PRESENT(status) \ 403 + ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ 404 + SSB_CHIPCO_CHST_4325_OTP_SEL) 405 + #define SSB_CHIPCO_CHST_4322_SPROM_PRESENT(status) \ 406 + (status & SSB_CHIPCO_CHST_4322_SPROM_EXISTS) 407 + #define SSB_CHIPCO_CHST_4325_SPROM_PRESENT(status) \ 408 + (((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ 409 + SSB_CHIPCO_CHST_4325_DEFCIS_SEL) && \ 410 + ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ 411 + SSB_CHIPCO_CHST_4325_OTP_SEL)) 402 412 403 413 404 414 ··· 578 564 struct ssb_chipcommon { 579 565 struct ssb_device *dev; 580 566 u32 capabilities; 567 + u32 status; 581 568 /* Fast Powerup Delay constant */ 582 569 u16 fast_pwrup_delay; 583 570 struct ssb_chipcommon_pmu pmu;