Jesse Keating 7a32965
From 4d9d1ff88f920e9fcdde155c0a1366b7e0462d14 Mon Sep 17 00:00:00 2001
Jesse Keating 7a32965
From: John W. Linville <linville@tuxdriver.com>
Jesse Keating 7a32965
Date: Fri, 19 Mar 2010 14:58:01 -0400
Jesse Keating 7a32965
Subject: [PATCH v4] ssb: do not read SPROM if it does not exist
Jesse Keating 7a32965
Jesse Keating 7a32965
Attempting to read registers that don't exist on the SSB bus can cause
Jesse Keating 7a32965
hangs on some boxes.  At least some b43 devices are 'in the wild' that
Jesse Keating 7a32965
don't have SPROMs at all.  When the SSB bus support loads, it attempts
Jesse Keating 7a32965
to read these (non-existant) SPROMs and causes hard hangs on the box --
Jesse Keating 7a32965
no console output, etc.
Jesse Keating 7a32965
Jesse Keating 7a32965
This patch adds some intelligence to determine whether or not the SPROM
Jesse Keating 7a32965
is present before attempting to read it.  This avoids those hard hangs
Jesse Keating 7a32965
on those devices with no SPROM attached to their SSB bus.  The
Jesse Keating 7a32965
SSB-attached devices (e.g. b43, et al.) won't work, but at least the box
Jesse Keating 7a32965
will survive to test further patches. :-)
Jesse Keating 7a32965
Jesse Keating 7a32965
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Jesse Keating 7a32965
Cc: Larry Finger <Larry.Finger@lwfinger.net>
Jesse Keating 7a32965
Cc: Michael Buesch <mb@bu3sch.de>
Jesse Keating 7a32965
Cc: stable@kernel.org
Jesse Keating 7a32965
---
Jesse Keating 7a32965
 drivers/ssb/driver_chipcommon.c           |    3 +++
Jesse Keating 7a32965
 drivers/ssb/pci.c                         |    3 +++
Jesse Keating 7a32965
 drivers/ssb/sprom.c                       |   26 ++++++++++++++++++++++++++
Jesse Keating 7a32965
 include/linux/ssb/ssb.h                   |    3 +++
Jesse Keating 7a32965
 include/linux/ssb/ssb_driver_chipcommon.h |   15 +++++++++++++++
Jesse Keating 7a32965
 5 files changed, 50 insertions(+), 0 deletions(-)
Jesse Keating 7a32965
Jesse Keating 7a32965
diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
Jesse Keating 7a32965
index 9681536..6cf288d 100644
Jesse Keating 7a32965
--- a/drivers/ssb/driver_chipcommon.c
Jesse Keating 7a32965
+++ b/drivers/ssb/driver_chipcommon.c
Jesse Keating 7a32965
@@ -233,6 +233,9 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
Jesse Keating 7a32965
 {
Jesse Keating 7a32965
 	if (!cc->dev)
Jesse Keating 7a32965
 		return; /* We don't have a ChipCommon */
Jesse Keating 7a32965
+	if (cc->dev->id.revision >= 11) {
Jesse Keating 7a32965
+		cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT);
Jesse Keating 7a32965
+	}
Jesse Keating 7a32965
 	ssb_pmu_init(cc);
Jesse Keating 7a32965
 	chipco_powercontrol_init(cc);
Jesse Keating 7a32965
 	ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
Jesse Keating 7a32965
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
Jesse Keating 7a32965
index a8dbb06..89d7ab1 100644
Jesse Keating 7a32965
--- a/drivers/ssb/pci.c
Jesse Keating 7a32965
+++ b/drivers/ssb/pci.c
Jesse Keating 7a32965
@@ -621,6 +621,9 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
Jesse Keating 7a32965
 	int err = -ENOMEM;
Jesse Keating 7a32965
 	u16 *buf;
Jesse Keating 7a32965
 
Jesse Keating 7a32965
+	if (!ssb_is_sprom_available(bus))
Jesse Keating 7a32965
+		return -ENODEV;
Jesse Keating 7a32965
+
Jesse Keating 7a32965
 	buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
Jesse Keating 7a32965
 	if (!buf)
Jesse Keating 7a32965
 		goto out;
Jesse Keating 7a32965
diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c
Jesse Keating 7a32965
index f2f920f..c690f58 100644
Jesse Keating 7a32965
--- a/drivers/ssb/sprom.c
Jesse Keating 7a32965
+++ b/drivers/ssb/sprom.c
Jesse Keating 7a32965
@@ -176,3 +176,29 @@ const struct ssb_sprom *ssb_get_fallback_sprom(void)
Jesse Keating 7a32965
 {
Jesse Keating 7a32965
 	return fallback_sprom;
Jesse Keating 7a32965
 }
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+bool ssb_is_sprom_available(struct ssb_bus *bus)
Jesse Keating 7a32965
+{
Jesse Keating 7a32965
+	/* some older devices don't have chipcommon, but they have sprom */
Jesse Keating 7a32965
+	if (!bus->chipco.dev)
Jesse Keating 7a32965
+		return true;
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+	/* status register only exists on chipcomon rev >= 11 */
Jesse Keating 7a32965
+	if (bus->chipco.dev->id.revision < 11)
Jesse Keating 7a32965
+		return true;
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+	switch (bus->chip_id) {
Jesse Keating 7a32965
+	case 0x4312:
Jesse Keating 7a32965
+		return SSB_CHIPCO_CHST_4312_SPROM_PRESENT(bus->chipco.status);
Jesse Keating 7a32965
+	case 0x4322:
Jesse Keating 7a32965
+		return SSB_CHIPCO_CHST_4322_SPROM_PRESENT(bus->chipco.status);
Jesse Keating 7a32965
+	case 0x4325:
Jesse Keating 7a32965
+		return SSB_CHIPCO_CHST_4325_SPROM_PRESENT(bus->chipco.status);
Jesse Keating 7a32965
+	default:
Jesse Keating 7a32965
+		break;
Jesse Keating 7a32965
+	}
Jesse Keating 7a32965
+	if (bus->chipco.dev->id.revision >= 31)
Jesse Keating 7a32965
+		return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+	return true;
Jesse Keating 7a32965
+}
Jesse Keating 7a32965
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
Jesse Keating 7a32965
index 24f9885..3b4da23 100644
Jesse Keating 7a32965
--- a/include/linux/ssb/ssb.h
Jesse Keating 7a32965
+++ b/include/linux/ssb/ssb.h
Jesse Keating 7a32965
@@ -394,6 +394,9 @@ extern int ssb_bus_sdiobus_register(struct ssb_bus *bus,
Jesse Keating 7a32965
 
Jesse Keating 7a32965
 extern void ssb_bus_unregister(struct ssb_bus *bus);
Jesse Keating 7a32965
 
Jesse Keating 7a32965
+/* Does the device have an SPROM? */
Jesse Keating 7a32965
+extern bool ssb_is_sprom_available(struct ssb_bus *bus);
Jesse Keating 7a32965
+
Jesse Keating 7a32965
 /* Set a fallback SPROM.
Jesse Keating 7a32965
  * See kdoc at the function definition for complete documentation. */
Jesse Keating 7a32965
 extern int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom);
Jesse Keating 7a32965
diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
Jesse Keating 7a32965
index 4e27acf..2cdf249 100644
Jesse Keating 7a32965
--- a/include/linux/ssb/ssb_driver_chipcommon.h
Jesse Keating 7a32965
+++ b/include/linux/ssb/ssb_driver_chipcommon.h
Jesse Keating 7a32965
@@ -53,6 +53,7 @@
Jesse Keating 7a32965
 #define  SSB_CHIPCO_CAP_64BIT		0x08000000	/* 64-bit Backplane */
Jesse Keating 7a32965
 #define  SSB_CHIPCO_CAP_PMU		0x10000000	/* PMU available (rev >= 20) */
Jesse Keating 7a32965
 #define  SSB_CHIPCO_CAP_ECI		0x20000000	/* ECI available (rev >= 20) */
Jesse Keating 7a32965
+#define  SSB_CHIPCO_CAP_SPROM		0x40000000	/* SPROM present */
Jesse Keating 7a32965
 #define SSB_CHIPCO_CORECTL		0x0008
Jesse Keating 7a32965
 #define  SSB_CHIPCO_CORECTL_UARTCLK0	0x00000001	/* Drive UART with internal clock */
Jesse Keating 7a32965
 #define	 SSB_CHIPCO_CORECTL_SE		0x00000002	/* sync clk out enable (corerev >= 3) */
Jesse Keating 7a32965
@@ -385,6 +386,7 @@
Jesse Keating 7a32965
 
Jesse Keating 7a32965
 
Jesse Keating 7a32965
 /** Chip specific Chip-Status register contents. */
Jesse Keating 7a32965
+#define SSB_CHIPCO_CHST_4322_SPROM_EXISTS	0x00000040 /* SPROM present */
Jesse Keating 7a32965
 #define SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL	0x00000003
Jesse Keating 7a32965
 #define SSB_CHIPCO_CHST_4325_DEFCIS_SEL		0 /* OTP is powered up, use def. CIS, no SPROM */
Jesse Keating 7a32965
 #define SSB_CHIPCO_CHST_4325_SPROM_SEL		1 /* OTP is powered up, SPROM is present */
Jesse Keating 7a32965
@@ -398,6 +400,18 @@
Jesse Keating 7a32965
 #define SSB_CHIPCO_CHST_4325_RCAL_VALUE_SHIFT	4
Jesse Keating 7a32965
 #define SSB_CHIPCO_CHST_4325_PMUTOP_2B 		0x00000200 /* 1 for 2b, 0 for to 2a */
Jesse Keating 7a32965
 
Jesse Keating 7a32965
+/** Macros to determine SPROM presence based on Chip-Status register. */
Jesse Keating 7a32965
+#define SSB_CHIPCO_CHST_4312_SPROM_PRESENT(status) \
Jesse Keating 7a32965
+	((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
Jesse Keating 7a32965
+		SSB_CHIPCO_CHST_4325_OTP_SEL)
Jesse Keating 7a32965
+#define SSB_CHIPCO_CHST_4322_SPROM_PRESENT(status) \
Jesse Keating 7a32965
+	(status & SSB_CHIPCO_CHST_4322_SPROM_EXISTS)
Jesse Keating 7a32965
+#define SSB_CHIPCO_CHST_4325_SPROM_PRESENT(status) \
Jesse Keating 7a32965
+	(((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
Jesse Keating 7a32965
+		SSB_CHIPCO_CHST_4325_DEFCIS_SEL) && \
Jesse Keating 7a32965
+	 ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
Jesse Keating 7a32965
+		SSB_CHIPCO_CHST_4325_OTP_SEL))
Jesse Keating 7a32965
+
Jesse Keating 7a32965
 
Jesse Keating 7a32965
 
Jesse Keating 7a32965
 /** Clockcontrol masks and values **/
Jesse Keating 7a32965
@@ -564,6 +578,7 @@ struct ssb_chipcommon_pmu {
Jesse Keating 7a32965
 struct ssb_chipcommon {
Jesse Keating 7a32965
 	struct ssb_device *dev;
Jesse Keating 7a32965
 	u32 capabilities;
Jesse Keating 7a32965
+	u32 status;
Jesse Keating 7a32965
 	/* Fast Powerup Delay constant */
Jesse Keating 7a32965
 	u16 fast_pwrup_delay;
Jesse Keating 7a32965
 	struct ssb_chipcommon_pmu pmu;
Jesse Keating 7a32965
-- 
Jesse Keating 7a32965
1.7.0.1
Jesse Keating 7a32965