Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[linux-2.6.git] / drivers / ssb / pci.c
index 5bb1278..a00b35f 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <linux/ssb/ssb.h>
 #include <linux/ssb/ssb_regs.h>
+#include <linux/slab.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
 
@@ -167,7 +168,7 @@ err_pci:
 }
 
 /* Get the word-offset for a SSB_SPROM_XXX define. */
-#define SPOFF(offset)  (((offset) - SSB_SPROM_BASE1) / 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))
@@ -405,6 +406,46 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
        out->antenna_gain.ghz5.a3 = gain;
 }
 
+/* Revs 4 5 and 8 have partially shared layout */
+static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in)
+{
+       SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01,
+            SSB_SPROM4_TXPID2G0, SSB_SPROM4_TXPID2G0_SHIFT);
+       SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01,
+            SSB_SPROM4_TXPID2G1, SSB_SPROM4_TXPID2G1_SHIFT);
+       SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23,
+            SSB_SPROM4_TXPID2G2, SSB_SPROM4_TXPID2G2_SHIFT);
+       SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23,
+            SSB_SPROM4_TXPID2G3, SSB_SPROM4_TXPID2G3_SHIFT);
+
+       SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01,
+            SSB_SPROM4_TXPID5GL0, SSB_SPROM4_TXPID5GL0_SHIFT);
+       SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01,
+            SSB_SPROM4_TXPID5GL1, SSB_SPROM4_TXPID5GL1_SHIFT);
+       SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23,
+            SSB_SPROM4_TXPID5GL2, SSB_SPROM4_TXPID5GL2_SHIFT);
+       SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23,
+            SSB_SPROM4_TXPID5GL3, SSB_SPROM4_TXPID5GL3_SHIFT);
+
+       SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01,
+            SSB_SPROM4_TXPID5G0, SSB_SPROM4_TXPID5G0_SHIFT);
+       SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01,
+            SSB_SPROM4_TXPID5G1, SSB_SPROM4_TXPID5G1_SHIFT);
+       SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23,
+            SSB_SPROM4_TXPID5G2, SSB_SPROM4_TXPID5G2_SHIFT);
+       SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23,
+            SSB_SPROM4_TXPID5G3, SSB_SPROM4_TXPID5G3_SHIFT);
+
+       SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01,
+            SSB_SPROM4_TXPID5GH0, SSB_SPROM4_TXPID5GH0_SHIFT);
+       SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01,
+            SSB_SPROM4_TXPID5GH1, SSB_SPROM4_TXPID5GH1_SHIFT);
+       SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23,
+            SSB_SPROM4_TXPID5GH2, SSB_SPROM4_TXPID5GH2_SHIFT);
+       SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23,
+            SSB_SPROM4_TXPID5GH3, SSB_SPROM4_TXPID5GH3_SHIFT);
+}
+
 static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
 {
        int i;
@@ -427,10 +468,14 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
                SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0);
                SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0);
                SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0);
+               SPEX(boardflags2_lo, SSB_SPROM4_BFL2LO, 0xFFFF, 0);
+               SPEX(boardflags2_hi, SSB_SPROM4_BFL2HI, 0xFFFF, 0);
        } else {
                SPEX(country_code, SSB_SPROM5_CCODE, 0xFFFF, 0);
                SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0);
                SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0);
+               SPEX(boardflags2_lo, SSB_SPROM5_BFL2LO, 0xFFFF, 0);
+               SPEX(boardflags2_hi, SSB_SPROM5_BFL2HI, 0xFFFF, 0);
        }
        SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A,
             SSB_SPROM4_ANTAVAIL_A_SHIFT);
@@ -470,6 +515,8 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
        memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24,
               sizeof(out->antenna_gain.ghz5));
 
+       sprom_extract_r458(out, in);
+
        /* TODO - get remaining rev 4 stuff needed */
 }
 
@@ -560,6 +607,8 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
        memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24,
               sizeof(out->antenna_gain.ghz5));
 
+       sprom_extract_r458(out, in);
+
        /* TODO - get remaining rev 8 stuff needed */
 }
 
@@ -572,37 +621,34 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
        ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision);
        memset(out->et0mac, 0xFF, 6);           /* preset et0 and et1 mac */
        memset(out->et1mac, 0xFF, 6);
+
        if ((bus->chip_id & 0xFF00) == 0x4400) {
                /* Workaround: The BCM44XX chip has a stupid revision
                 * number stored in the SPROM.
                 * Always extract r1. */
                out->revision = 1;
+               ssb_dprintk(KERN_DEBUG PFX "SPROM treated as revision %d\n", out->revision);
+       }
+
+       switch (out->revision) {
+       case 1:
+       case 2:
+       case 3:
                sprom_extract_r123(out, in);
-       } else if (bus->chip_id == 0x4321) {
-               /* the BCM4328 has a chipid == 0x4321 and a rev 4 SPROM */
-               out->revision = 4;
+               break;
+       case 4:
+       case 5:
                sprom_extract_r45(out, in);
-       } else {
-               switch (out->revision) {
-               case 1:
-               case 2:
-               case 3:
-                       sprom_extract_r123(out, in);
-                       break;
-               case 4:
-               case 5:
-                       sprom_extract_r45(out, in);
-                       break;
-               case 8:
-                       sprom_extract_r8(out, in);
-                       break;
-               default:
-                       ssb_printk(KERN_WARNING PFX "Unsupported SPROM"
-                                  "  revision %d detected. Will extract"
-                                  " v1\n", out->revision);
-                       out->revision = 1;
-                       sprom_extract_r123(out, in);
-               }
+               break;
+       case 8:
+               sprom_extract_r8(out, in);
+               break;
+       default:
+               ssb_printk(KERN_WARNING PFX "Unsupported SPROM"
+                          " revision %d detected. Will extract"
+                          " v1\n", out->revision);
+               out->revision = 1;
+               sprom_extract_r123(out, in);
        }
 
        if (out->boardflags_lo == 0xFFFF)
@@ -616,21 +662,34 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
 static int ssb_pci_sprom_get(struct ssb_bus *bus,
                             struct ssb_sprom *sprom)
 {
-       const struct ssb_sprom *fallback;
-       int err = -ENOMEM;
+       int err;
        u16 *buf;
 
        if (!ssb_is_sprom_available(bus)) {
                ssb_printk(KERN_ERR PFX "No SPROM available!\n");
                return -ENODEV;
        }
-
-       bus->sprom_offset = (bus->chipco.dev->id.revision < 31) ?
-               SSB_SPROM_BASE1 : SSB_SPROM_BASE31;
+       if (bus->chipco.dev) {  /* can be unavailable! */
+               /*
+                * 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;
+               return -ENOMEM;
        bus->sprom_size = SSB_SPROMSIZE_WORDS_R123;
        sprom_do_read(bus, buf);
        err = sprom_check_crc(buf, bus->sprom_size);
@@ -640,17 +699,24 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
                buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
                              GFP_KERNEL);
                if (!buf)
-                       goto out;
+                       return -ENOMEM;
                bus->sprom_size = SSB_SPROMSIZE_WORDS_R4;
                sprom_do_read(bus, buf);
                err = sprom_check_crc(buf, bus->sprom_size);
                if (err) {
                        /* All CRC attempts failed.
                         * Maybe there is no SPROM on the device?
-                        * If we have a fallback, use that. */
-                       fallback = ssb_get_fallback_sprom();
-                       if (fallback) {
-                               memcpy(sprom, fallback, sizeof(*sprom));
+                        * Now we ask the arch code if there is some sprom
+                        * available for this device in some other storage */
+                       err = ssb_fill_sprom_with_fallback(bus, sprom);
+                       if (err) {
+                               ssb_printk(KERN_WARNING PFX "WARNING: Using"
+                                          " fallback SPROM failed (err %d)\n",
+                                          err);
+                       } else {
+                               ssb_dprintk(KERN_DEBUG PFX "Using SPROM"
+                                           " revision %d provided by"
+                                           " platform.\n", sprom->revision);
                                err = 0;
                                goto out_free;
                        }
@@ -662,19 +728,15 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
 
 out_free:
        kfree(buf);
-out:
        return err;
 }
 
 static void ssb_pci_get_boardinfo(struct ssb_bus *bus,
                                  struct ssb_boardinfo *bi)
 {
-       pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID,
-                            &bi->vendor);
-       pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID,
-                            &bi->type);
-       pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
-                            &bi->rev);
+       bi->vendor = bus->host_pci->subsystem_vendor;
+       bi->type = bus->host_pci->subsystem_device;
+       bi->rev = bus->host_pci->revision;
 }
 
 int ssb_pci_get_invariants(struct ssb_bus *bus,