aboutsummaryrefslogtreecommitdiff
path: root/drivers/ssb/pci.c
diff options
context:
space:
mode:
authorChristoph Fritz <chf.fritz@googlemail.com>2010-07-11 18:26:15 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-02 10:20:43 -0700
commitea158563643b3c28c5dd3f286ab61aadddfd7b88 (patch)
tree53dca771f4266f7911c1b5b17669a3d260f6dc87 /drivers/ssb/pci.c
parentedc62dda419e4f6ee40548fa3deb9baf8b369e29 (diff)
ssb: Handle Netbook devices where the SPROM address is changed
For some Netbook computers with Broadcom BCM4312 wireless interfaces, the SPROM has been moved to a new location. When the ssb driver tries to read the old location, the systems hangs when trying to read a non-existent location. Such freezes are particularly bad as they do not log the failure. This patch is modified from commit da1fdb02d9200ff28b6f3a380d21930335fe5429 with some pieces from other mainline changes so that it can be applied to stable 2.6.34.Y. Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/ssb/pci.c')
-rw-r--r--drivers/ssb/pci.c46
1 files changed, 40 insertions, 6 deletions
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
index 9e50896233a..17a17813954 100644
--- a/drivers/ssb/pci.c
+++ b/drivers/ssb/pci.c
@@ -22,6 +22,7 @@
#include "ssb_private.h"
+bool ssb_is_sprom_available(struct ssb_bus *bus);
/* Define the following to 1 to enable a printk on each coreswitch. */
#define SSB_VERBOSE_PCICORESWITCH_DEBUG 0
@@ -167,7 +168,7 @@ err_pci:
}
/* Get the word-offset for a SSB_SPROM_XXX define. */
-#define SPOFF(offset) (((offset) - SSB_SPROM_BASE) / sizeof(u16))
+#define SPOFF(offset) ((offset) / sizeof(u16))
/* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
#define SPEX16(_outvar, _offset, _mask, _shift) \
out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
@@ -252,8 +253,13 @@ static int sprom_do_read(struct ssb_bus *bus, u16 *sprom)
{
int i;
+ /* Check if SPROM can be read */
+ if (ioread16(bus->mmio + bus->sprom_offset) == 0xFFFF) {
+ ssb_printk(KERN_ERR PFX "Unable to read SPROM\n");
+ return -ENODEV;
+ }
for (i = 0; i < bus->sprom_size; i++)
- sprom[i] = ioread16(bus->mmio + SSB_SPROM_BASE + (i * 2));
+ sprom[i] = ioread16(bus->mmio + bus->sprom_offset + (i * 2));
return 0;
}
@@ -284,7 +290,7 @@ static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
ssb_printk("75%%");
else if (i % 2)
ssb_printk(".");
- writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2));
+ writew(sprom[i], bus->mmio + bus->sprom_offset + (i * 2));
mmiowb();
msleep(20);
}
@@ -620,21 +626,49 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
int err = -ENOMEM;
u16 *buf;
+ if (!ssb_is_sprom_available(bus)) {
+ ssb_printk(KERN_ERR PFX "No SPROM available!\n");
+ return -ENODEV;
+ }
+ if (bus->chipco.dev) { /* can be unavailible! */
+ /*
+ * get SPROM offset: SSB_SPROM_BASE1 except for
+ * chipcommon rev >= 31 or chip ID is 0x4312 and
+ * chipcommon status & 3 == 2
+ */
+ if (bus->chipco.dev->id.revision >= 31)
+ bus->sprom_offset = SSB_SPROM_BASE31;
+ else if (bus->chip_id == 0x4312 &&
+ (bus->chipco.status & 0x03) == 2)
+ bus->sprom_offset = SSB_SPROM_BASE31;
+ else
+ bus->sprom_offset = SSB_SPROM_BASE1;
+ } else {
+ bus->sprom_offset = SSB_SPROM_BASE1;
+ }
+ ssb_dprintk(KERN_INFO PFX "SPROM offset is 0x%x\n", bus->sprom_offset);
+
buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
if (!buf)
goto out;
bus->sprom_size = SSB_SPROMSIZE_WORDS_R123;
- sprom_do_read(bus, buf);
+ err = sprom_do_read(bus, buf);
+ if (err)
+ goto out_free;
err = sprom_check_crc(buf, bus->sprom_size);
if (err) {
/* try for a 440 byte SPROM - revision 4 and higher */
kfree(buf);
buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
GFP_KERNEL);
- if (!buf)
+ if (!buf) {
+ err = -ENOMEM;
goto out;
+ }
bus->sprom_size = SSB_SPROMSIZE_WORDS_R4;
- sprom_do_read(bus, buf);
+ err = sprom_do_read(bus, buf);
+ if (err)
+ goto out_free;
err = sprom_check_crc(buf, bus->sprom_size);
if (err) {
/* All CRC attempts failed.