summaryrefslogtreecommitdiffstats
path: root/ssb_check_for_sprom.patch
diff options
context:
space:
mode:
Diffstat (limited to 'ssb_check_for_sprom.patch')
-rw-r--r--ssb_check_for_sprom.patch155
1 files changed, 155 insertions, 0 deletions
diff --git a/ssb_check_for_sprom.patch b/ssb_check_for_sprom.patch
new file mode 100644
index 000000000..9415e1337
--- /dev/null
+++ b/ssb_check_for_sprom.patch
@@ -0,0 +1,155 @@
+From 4d9d1ff88f920e9fcdde155c0a1366b7e0462d14 Mon Sep 17 00:00:00 2001
+From: John W. Linville <linville@tuxdriver.com>
+Date: Fri, 19 Mar 2010 14:58:01 -0400
+Subject: [PATCH v4] 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>
+Cc: Larry Finger <Larry.Finger@lwfinger.net>
+Cc: Michael Buesch <mb@bu3sch.de>
+Cc: stable@kernel.org
+---
+ drivers/ssb/driver_chipcommon.c | 3 +++
+ drivers/ssb/pci.c | 3 +++
+ drivers/ssb/sprom.c | 26 ++++++++++++++++++++++++++
+ include/linux/ssb/ssb.h | 3 +++
+ include/linux/ssb/ssb_driver_chipcommon.h | 15 +++++++++++++++
+ 5 files changed, 50 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
+index 9681536..6cf288d 100644
+--- a/drivers/ssb/driver_chipcommon.c
++++ b/drivers/ssb/driver_chipcommon.c
+@@ -233,6 +233,9 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
+ {
+ if (!cc->dev)
+ return; /* We don't have a ChipCommon */
++ if (cc->dev->id.revision >= 11) {
++ cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT);
++ }
+ ssb_pmu_init(cc);
+ chipco_powercontrol_init(cc);
+ ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
+diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
+index a8dbb06..89d7ab1 100644
+--- a/drivers/ssb/pci.c
++++ b/drivers/ssb/pci.c
+@@ -621,6 +621,9 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
+ int err = -ENOMEM;
+ u16 *buf;
+
++ if (!ssb_is_sprom_available(bus))
++ return -ENODEV;
++
+ buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
+ if (!buf)
+ goto out;
+diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c
+index f2f920f..c690f58 100644
+--- a/drivers/ssb/sprom.c
++++ b/drivers/ssb/sprom.c
+@@ -176,3 +176,29 @@ const struct ssb_sprom *ssb_get_fallback_sprom(void)
+ {
+ return fallback_sprom;
+ }
++
++bool ssb_is_sprom_available(struct ssb_bus *bus)
++{
++ /* some older devices don't have chipcommon, but they have sprom */
++ if (!bus->chipco.dev)
++ return true;
++
++ /* status register only exists on chipcomon rev >= 11 */
++ if (bus->chipco.dev->id.revision < 11)
++ return true;
++
++ switch (bus->chip_id) {
++ case 0x4312:
++ return SSB_CHIPCO_CHST_4312_SPROM_PRESENT(bus->chipco.status);
++ case 0x4322:
++ return SSB_CHIPCO_CHST_4322_SPROM_PRESENT(bus->chipco.status);
++ case 0x4325:
++ return SSB_CHIPCO_CHST_4325_SPROM_PRESENT(bus->chipco.status);
++ default:
++ break;
++ }
++ if (bus->chipco.dev->id.revision >= 31)
++ return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
++
++ return true;
++}
+diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
+index 24f9885..3b4da23 100644
+--- a/include/linux/ssb/ssb.h
++++ b/include/linux/ssb/ssb.h
+@@ -394,6 +394,9 @@ extern int ssb_bus_sdiobus_register(struct ssb_bus *bus,
+
+ extern void ssb_bus_unregister(struct ssb_bus *bus);
+
++/* Does the device have an SPROM? */
++extern bool ssb_is_sprom_available(struct ssb_bus *bus);
++
+ /* Set a fallback SPROM.
+ * See kdoc at the function definition for complete documentation. */
+ extern int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom);
+diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
+index 4e27acf..2cdf249 100644
+--- a/include/linux/ssb/ssb_driver_chipcommon.h
++++ b/include/linux/ssb/ssb_driver_chipcommon.h
+@@ -53,6 +53,7 @@
+ #define SSB_CHIPCO_CAP_64BIT 0x08000000 /* 64-bit Backplane */
+ #define SSB_CHIPCO_CAP_PMU 0x10000000 /* PMU available (rev >= 20) */
+ #define SSB_CHIPCO_CAP_ECI 0x20000000 /* ECI available (rev >= 20) */
++#define SSB_CHIPCO_CAP_SPROM 0x40000000 /* SPROM present */
+ #define SSB_CHIPCO_CORECTL 0x0008
+ #define SSB_CHIPCO_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */
+ #define SSB_CHIPCO_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */
+@@ -385,6 +386,7 @@
+
+
+ /** Chip specific Chip-Status register contents. */
++#define SSB_CHIPCO_CHST_4322_SPROM_EXISTS 0x00000040 /* SPROM present */
+ #define SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL 0x00000003
+ #define SSB_CHIPCO_CHST_4325_DEFCIS_SEL 0 /* OTP is powered up, use def. CIS, no SPROM */
+ #define SSB_CHIPCO_CHST_4325_SPROM_SEL 1 /* OTP is powered up, SPROM is present */
+@@ -398,6 +400,18 @@
+ #define SSB_CHIPCO_CHST_4325_RCAL_VALUE_SHIFT 4
+ #define SSB_CHIPCO_CHST_4325_PMUTOP_2B 0x00000200 /* 1 for 2b, 0 for to 2a */
+
++/** Macros to determine SPROM presence based on Chip-Status register. */
++#define SSB_CHIPCO_CHST_4312_SPROM_PRESENT(status) \
++ ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
++ SSB_CHIPCO_CHST_4325_OTP_SEL)
++#define SSB_CHIPCO_CHST_4322_SPROM_PRESENT(status) \
++ (status & SSB_CHIPCO_CHST_4322_SPROM_EXISTS)
++#define SSB_CHIPCO_CHST_4325_SPROM_PRESENT(status) \
++ (((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
++ SSB_CHIPCO_CHST_4325_DEFCIS_SEL) && \
++ ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
++ SSB_CHIPCO_CHST_4325_OTP_SEL))
++
+
+
+ /** Clockcontrol masks and values **/
+@@ -564,6 +578,7 @@ struct ssb_chipcommon_pmu {
+ struct ssb_chipcommon {
+ struct ssb_device *dev;
+ u32 capabilities;
++ u32 status;
+ /* Fast Powerup Delay constant */
+ u16 fast_pwrup_delay;
+ struct ssb_chipcommon_pmu pmu;
+--
+1.7.0.1
+