]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ssb: Handle Netbook devices where the SPROM address is changed
authorChristoph Fritz <chf.fritz@googlemail.com>
Sun, 11 Jul 2010 23:26:15 +0000 (18:26 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 2 Aug 2010 17:20:43 +0000 (10:20 -0700)
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>
drivers/ssb/driver_chipcommon.c
drivers/ssb/driver_chipcommon_pmu.c
drivers/ssb/pci.c
drivers/ssb/sprom.c
include/linux/ssb/ssb.h
include/linux/ssb/ssb_driver_chipcommon.h
include/linux/ssb/ssb_regs.h

index 9681536163caa5bdf7a36fc1dcfff9f8da3a66ec..bbf1cb21a7d3509d00d1461a29759927ee157e1a 100644 (file)
@@ -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_dprintk(KERN_INFO PFX "chipcommon status is 0x%x\n", cc->status);
        ssb_pmu_init(cc);
        chipco_powercontrol_init(cc);
        ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
index 64abd11f6fbbef64184011c9b4b7d0e0fb182ea8..8e194d557cace47e5efa2e12c1411544d410ba69 100644 (file)
@@ -495,9 +495,9 @@ static void ssb_pmu_resources_init(struct ssb_chipcommon *cc)
                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)
 {
-       struct ssb_bus *bus = cc->dev->bus;
        u32 pmucap;
 
        if (!(cc->capabilities & SSB_CHIPCO_CAP_PMU))
@@ -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",
                    cc->pmu.rev, pmucap);
 
-       if (cc->pmu.rev >= 1) {
-               if ((bus->chip_id == 0x4325) && (bus->chip_rev < 2)) {
-                       chipco_mask32(cc, SSB_CHIPCO_PMU_CTL,
-                                     ~SSB_CHIPCO_PMU_CTL_NOILPONW);
-               } else {
-                       chipco_set32(cc, SSB_CHIPCO_PMU_CTL,
-                                    SSB_CHIPCO_PMU_CTL_NOILPONW);
-               }
-       }
+       if (cc->pmu.rev == 1)
+               chipco_mask32(cc, SSB_CHIPCO_PMU_CTL,
+                             ~SSB_CHIPCO_PMU_CTL_NOILPONW);
+       else
+               chipco_set32(cc, SSB_CHIPCO_PMU_CTL,
+                            SSB_CHIPCO_PMU_CTL_NOILPONW);
        ssb_pmu_pll_init(cc);
        ssb_pmu_resources_init(cc);
 }
index 9e50896233aa969e5fa145bcf630dd0ba9055363..17a178139548c781b4c78e55641b6e0455165b45 100644 (file)
@@ -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.
index eb708431cb964d36e57ab80b9a9d18b1e5f7f7cb..5f7154d9d04e09fc0ef2c892bfac73cac03b0f53 100644 (file)
@@ -179,3 +179,18 @@ const struct ssb_sprom *ssb_get_fallback_sprom(void)
 {
        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;
+}
index 3d0a9ff24f01220f2957d8f3665eb3467dcafa6d..a28c37d9dd9ec9185f65348da841ccf87a747f04 100644 (file)
@@ -302,6 +302,7 @@ struct ssb_bus {
        u16 chip_id;
        u16 chip_rev;
        u16 sprom_size;         /* number of words in sprom */
+       u16 sprom_offset;
        u8 chip_package;
 
        /* List of devices (cores) on the backplane. */
index 4e27acf0a92f91f3f3052f96dcdfa4d142ca369e..7600f38c5aa96b9d70bed32150bbc5c776ad8613 100644 (file)
@@ -46,6 +46,7 @@
 #define   SSB_PLLTYPE_7                        0x00038000      /* 25Mhz, 4 dividers */
 #define  SSB_CHIPCO_CAP_PCTL           0x00040000      /* Power Control */
 #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_BASE      5
 #define  SSB_CHIPCO_CAP_JTAGM          0x00400000      /* JTAG master present */
@@ -564,6 +565,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;
index 9ae9082eaeb46eb2f9d9573a2706d339ab8c06ab..b8be23ce191547126ea00f54fcf7a12a6b742fad 100644 (file)
 #define SSB_SPROMSIZE_WORDS_R4         220
 #define SSB_SPROMSIZE_BYTES_R123       (SSB_SPROMSIZE_WORDS_R123 * 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_REV                0x00FF  /* SPROM Revision number */
 #define  SSB_SPROM_REVISION_CRC                0xFF00  /* SPROM CRC8 value */