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