Commit ea158563 authored by Christoph Fritz's avatar Christoph Fritz Committed by Greg Kroah-Hartman

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
da1fdb02 with some pieces from other
mainline changes so that it can be applied to stable 2.6.34.Y.
Signed-off-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent edc62dda
...@@ -233,6 +233,9 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc) ...@@ -233,6 +233,9 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
{ {
if (!cc->dev) if (!cc->dev)
return; /* We don't have a ChipCommon */ return; /* We don't have a ChipCommon */
if (cc->dev->id.revision >= 11)
cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT);
ssb_dprintk(KERN_INFO PFX "chipcommon status is 0x%x\n", cc->status);
ssb_pmu_init(cc); ssb_pmu_init(cc);
chipco_powercontrol_init(cc); chipco_powercontrol_init(cc);
ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST); ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
......
...@@ -495,9 +495,9 @@ static void ssb_pmu_resources_init(struct ssb_chipcommon *cc) ...@@ -495,9 +495,9 @@ static void ssb_pmu_resources_init(struct ssb_chipcommon *cc)
chipco_write32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, max_msk); chipco_write32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, max_msk);
} }
/* http://bcm-v4.sipsolutions.net/802.11/SSB/PmuInit */
void ssb_pmu_init(struct ssb_chipcommon *cc) void ssb_pmu_init(struct ssb_chipcommon *cc)
{ {
struct ssb_bus *bus = cc->dev->bus;
u32 pmucap; u32 pmucap;
if (!(cc->capabilities & SSB_CHIPCO_CAP_PMU)) if (!(cc->capabilities & SSB_CHIPCO_CAP_PMU))
...@@ -509,15 +509,12 @@ void ssb_pmu_init(struct ssb_chipcommon *cc) ...@@ -509,15 +509,12 @@ void ssb_pmu_init(struct ssb_chipcommon *cc)
ssb_dprintk(KERN_DEBUG PFX "Found rev %u PMU (capabilities 0x%08X)\n", ssb_dprintk(KERN_DEBUG PFX "Found rev %u PMU (capabilities 0x%08X)\n",
cc->pmu.rev, pmucap); cc->pmu.rev, pmucap);
if (cc->pmu.rev >= 1) { if (cc->pmu.rev == 1)
if ((bus->chip_id == 0x4325) && (bus->chip_rev < 2)) { chipco_mask32(cc, SSB_CHIPCO_PMU_CTL,
chipco_mask32(cc, SSB_CHIPCO_PMU_CTL, ~SSB_CHIPCO_PMU_CTL_NOILPONW);
~SSB_CHIPCO_PMU_CTL_NOILPONW); else
} else { chipco_set32(cc, SSB_CHIPCO_PMU_CTL,
chipco_set32(cc, SSB_CHIPCO_PMU_CTL, SSB_CHIPCO_PMU_CTL_NOILPONW);
SSB_CHIPCO_PMU_CTL_NOILPONW);
}
}
ssb_pmu_pll_init(cc); ssb_pmu_pll_init(cc);
ssb_pmu_resources_init(cc); ssb_pmu_resources_init(cc);
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "ssb_private.h" #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 the following to 1 to enable a printk on each coreswitch. */
#define SSB_VERBOSE_PCICORESWITCH_DEBUG 0 #define SSB_VERBOSE_PCICORESWITCH_DEBUG 0
...@@ -167,7 +168,7 @@ int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on) ...@@ -167,7 +168,7 @@ int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on)
} }
/* Get the word-offset for a SSB_SPROM_XXX define. */ /* 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. */ /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
#define SPEX16(_outvar, _offset, _mask, _shift) \ #define SPEX16(_outvar, _offset, _mask, _shift) \
out->_outvar = ((in[SPOFF(_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) ...@@ -252,8 +253,13 @@ static int sprom_do_read(struct ssb_bus *bus, u16 *sprom)
{ {
int i; 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++) 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; return 0;
} }
...@@ -284,7 +290,7 @@ static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) ...@@ -284,7 +290,7 @@ static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
ssb_printk("75%%"); ssb_printk("75%%");
else if (i % 2) else if (i % 2)
ssb_printk("."); ssb_printk(".");
writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2)); writew(sprom[i], bus->mmio + bus->sprom_offset + (i * 2));
mmiowb(); mmiowb();
msleep(20); msleep(20);
} }
...@@ -620,21 +626,49 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus, ...@@ -620,21 +626,49 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
int err = -ENOMEM; int err = -ENOMEM;
u16 *buf; 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); buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
if (!buf) if (!buf)
goto out; goto out;
bus->sprom_size = SSB_SPROMSIZE_WORDS_R123; 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); err = sprom_check_crc(buf, bus->sprom_size);
if (err) { if (err) {
/* try for a 440 byte SPROM - revision 4 and higher */ /* try for a 440 byte SPROM - revision 4 and higher */
kfree(buf); kfree(buf);
buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
GFP_KERNEL); GFP_KERNEL);
if (!buf) if (!buf) {
err = -ENOMEM;
goto out; goto out;
}
bus->sprom_size = SSB_SPROMSIZE_WORDS_R4; 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); err = sprom_check_crc(buf, bus->sprom_size);
if (err) { if (err) {
/* All CRC attempts failed. /* All CRC attempts failed.
......
...@@ -179,3 +179,18 @@ const struct ssb_sprom *ssb_get_fallback_sprom(void) ...@@ -179,3 +179,18 @@ const struct ssb_sprom *ssb_get_fallback_sprom(void)
{ {
return fallback_sprom; return fallback_sprom;
} }
/* http://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */
bool ssb_is_sprom_available(struct ssb_bus *bus)
{
/* status register only exists on chipcomon rev >= 11 and we need check
for >= 31 only */
/* this routine differs from specs as we do not access SPROM directly
on PCMCIA */
if (bus->bustype == SSB_BUSTYPE_PCI &&
bus->chipco.dev && /* can be unavailible! */
bus->chipco.dev->id.revision >= 31)
return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
return true;
}
...@@ -302,6 +302,7 @@ struct ssb_bus { ...@@ -302,6 +302,7 @@ struct ssb_bus {
u16 chip_id; u16 chip_id;
u16 chip_rev; u16 chip_rev;
u16 sprom_size; /* number of words in sprom */ u16 sprom_size; /* number of words in sprom */
u16 sprom_offset;
u8 chip_package; u8 chip_package;
/* List of devices (cores) on the backplane. */ /* List of devices (cores) on the backplane. */
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#define SSB_PLLTYPE_7 0x00038000 /* 25Mhz, 4 dividers */ #define SSB_PLLTYPE_7 0x00038000 /* 25Mhz, 4 dividers */
#define SSB_CHIPCO_CAP_PCTL 0x00040000 /* Power Control */ #define SSB_CHIPCO_CAP_PCTL 0x00040000 /* Power Control */
#define SSB_CHIPCO_CAP_OTPS 0x00380000 /* OTP size */ #define SSB_CHIPCO_CAP_OTPS 0x00380000 /* OTP size */
#define SSB_CHIPCO_CAP_SPROM 0x40000000 /* SPROM present */
#define SSB_CHIPCO_CAP_OTPS_SHIFT 19 #define SSB_CHIPCO_CAP_OTPS_SHIFT 19
#define SSB_CHIPCO_CAP_OTPS_BASE 5 #define SSB_CHIPCO_CAP_OTPS_BASE 5
#define SSB_CHIPCO_CAP_JTAGM 0x00400000 /* JTAG master present */ #define SSB_CHIPCO_CAP_JTAGM 0x00400000 /* JTAG master present */
...@@ -564,6 +565,7 @@ struct ssb_chipcommon_pmu { ...@@ -564,6 +565,7 @@ struct ssb_chipcommon_pmu {
struct ssb_chipcommon { struct ssb_chipcommon {
struct ssb_device *dev; struct ssb_device *dev;
u32 capabilities; u32 capabilities;
u32 status;
/* Fast Powerup Delay constant */ /* Fast Powerup Delay constant */
u16 fast_pwrup_delay; u16 fast_pwrup_delay;
struct ssb_chipcommon_pmu pmu; struct ssb_chipcommon_pmu pmu;
......
...@@ -170,7 +170,8 @@ ...@@ -170,7 +170,8 @@
#define SSB_SPROMSIZE_WORDS_R4 220 #define SSB_SPROMSIZE_WORDS_R4 220
#define SSB_SPROMSIZE_BYTES_R123 (SSB_SPROMSIZE_WORDS_R123 * sizeof(u16)) #define SSB_SPROMSIZE_BYTES_R123 (SSB_SPROMSIZE_WORDS_R123 * sizeof(u16))
#define SSB_SPROMSIZE_BYTES_R4 (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16)) #define SSB_SPROMSIZE_BYTES_R4 (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16))
#define SSB_SPROM_BASE 0x1000 #define SSB_SPROM_BASE1 0x1000
#define SSB_SPROM_BASE31 0x0800
#define SSB_SPROM_REVISION 0x107E #define SSB_SPROM_REVISION 0x107E
#define SSB_SPROM_REVISION_REV 0x00FF /* SPROM Revision number */ #define SSB_SPROM_REVISION_REV 0x00FF /* SPROM Revision number */
#define SSB_SPROM_REVISION_CRC 0xFF00 /* SPROM CRC8 value */ #define SSB_SPROM_REVISION_CRC 0xFF00 /* SPROM CRC8 value */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment