bcma: add support for on-chip OTP memory used for SPROM storage
authorArend van Spriel <arend@broadcom.com>
Tue, 6 Mar 2012 14:50:48 +0000 (15:50 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 6 Mar 2012 20:16:18 +0000 (15:16 -0500)
Wireless Broadcom chips can have either their SPROM data stored
on either external SPROM or on-chip OTP memory. Both are accessed
through the same register space. This patch adds support for the
on-chip OTP memory.

Tested with:
BCM43224 OTP and SPROM
BCM4331 SPROM
BCM4313 OTP

This patch is in response to linux-wireless thread [1].

[1] http://article.gmane.org/gmane.linux.kernel.wireless.general/85426

Tested-by: Saul St. John <saul.stjohn@gmail.com>
Tested-by: Rafal Milecki <zajec5@gmail.com>
Tested-by: Hauke Mehrtens <hauke@hauke-m.de>
Cc: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/bcma/sprom.c
include/linux/bcma/bcma_driver_chipcommon.h

index fba8066857d233de48cf3eff9055f1cceff85f47..cdcf75c0954febe06c4ac04f030568470284c160 100644 (file)
@@ -300,37 +300,128 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
             SSB_SROM8_FEM_ANTSWLUT_SHIFT);
 }
 
-static bool bcma_is_sprom_available(struct bcma_bus *bus)
+/*
+ * Indicates the presence of external SPROM.
+ */
+static bool bcma_sprom_ext_available(struct bcma_bus *bus)
 {
-       u32 sromctrl;
+       u32 chip_status;
+       u32 srom_control;
+       u32 present_mask;
 
-       if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
-               return false;
+       if (bus->drv_cc.core->id.rev >= 31) {
+               if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
+                       return false;
 
-       if (bus->drv_cc.core->id.rev >= 32) {
-               sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL);
-               return sromctrl & BCMA_CC_SROM_CONTROL_PRESENT;
+               srom_control = bcma_read32(bus->drv_cc.core,
+                                          BCMA_CC_SROM_CONTROL);
+               return srom_control & BCMA_CC_SROM_CONTROL_PRESENT;
        }
-       return true;
+
+       /* older chipcommon revisions use chip status register */
+       chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
+       switch (bus->chipinfo.id) {
+       case 0x4313:
+               present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT;
+               break;
+
+       case 0x4331:
+               present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT;
+               break;
+
+       default:
+               return true;
+       }
+
+       return chip_status & present_mask;
+}
+
+/*
+ * Indicates that on-chip OTP memory is present and enabled.
+ */
+static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
+{
+       u32 chip_status;
+       u32 otpsize = 0;
+       bool present;
+
+       chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
+       switch (bus->chipinfo.id) {
+       case 0x4313:
+               present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT;
+               break;
+
+       case 0x4331:
+               present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
+               break;
+
+       case 43224:
+       case 43225:
+               /* for these chips OTP is always available */
+               present = true;
+               break;
+
+       default:
+               present = false;
+               break;
+       }
+
+       if (present) {
+               otpsize = bus->drv_cc.capabilities & BCMA_CC_CAP_OTPS;
+               otpsize >>= BCMA_CC_CAP_OTPS_SHIFT;
+       }
+
+       return otpsize != 0;
+}
+
+/*
+ * Verify OTP is filled and determine the byte
+ * offset where SPROM data is located.
+ *
+ * On error, returns 0; byte offset otherwise.
+ */
+static int bcma_sprom_onchip_offset(struct bcma_bus *bus)
+{
+       struct bcma_device *cc = bus->drv_cc.core;
+       u32 offset;
+
+       /* verify OTP status */
+       if ((bcma_read32(cc, BCMA_CC_OTPS) & BCMA_CC_OTPS_GU_PROG_HW) == 0)
+               return 0;
+
+       /* obtain bit offset from otplayout register */
+       offset = (bcma_read32(cc, BCMA_CC_OTPL) & BCMA_CC_OTPL_GURGN_OFFSET);
+       return BCMA_CC_SPROM + (offset >> 3);
 }
 
 int bcma_sprom_get(struct bcma_bus *bus)
 {
-       u16 offset;
+       u16 offset = BCMA_CC_SPROM;
        u16 *sprom;
        int err = 0;
 
        if (!bus->drv_cc.core)
                return -EOPNOTSUPP;
 
-       if (!bcma_is_sprom_available(bus)) {
+       if (!bcma_sprom_ext_available(bus)) {
                /*
-                * Maybe there is no SPROM on the device?
-                * Now we ask the arch code if there is some sprom
-                * available for this device in some other storage.
+                * External SPROM takes precedence so check
+                * on-chip OTP only when no external SPROM
+                * is present.
                 */
-               err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
-               return err;
+               if (bcma_sprom_onchip_available(bus)) {
+                       /* determine offset */
+                       offset = bcma_sprom_onchip_offset(bus);
+               }
+               if (!offset) {
+                       /*
+                        * Maybe there is no SPROM on the device?
+                        * Now we ask the arch code if there is some sprom
+                        * available for this device in some other storage.
+                        */
+                       err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
+                       return err;
+               }
        }
 
        sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
@@ -341,11 +432,6 @@ int bcma_sprom_get(struct bcma_bus *bus)
        if (bus->chipinfo.id == 0x4331)
                bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
 
-       /* Most cards have SPROM moved by additional offset 0x30 (48 dwords).
-        * According to brcm80211 this applies to cards with PCIe rev >= 6
-        * TODO: understand this condition and use it */
-       offset = (bus->chipinfo.id == 0x4331) ? BCMA_CC_SPROM :
-               BCMA_CC_SPROM_PCIE6;
        pr_debug("SPROM offset 0x%x\n", offset);
        bcma_sprom_read(bus, offset, sprom);
 
index e72938b1071431dcbef56e80d2bbd9fff5bfc228..8bbfe31fbac864db2fa60e9edeef149097d3a32e 100644 (file)
@@ -56,6 +56,9 @@
 #define         BCMA_CC_OTPS_HW_PROTECT        0x00000001
 #define         BCMA_CC_OTPS_SW_PROTECT        0x00000002
 #define         BCMA_CC_OTPS_CID_PROTECT       0x00000004
+#define  BCMA_CC_OTPS_GU_PROG_IND      0x00000F00      /* General Use programmed indication */
+#define  BCMA_CC_OTPS_GU_PROG_IND_SHIFT        8
+#define  BCMA_CC_OTPS_GU_PROG_HW       0x00000100      /* HW region programmed */
 #define BCMA_CC_OTPC                   0x0014          /* OTP control */
 #define         BCMA_CC_OTPC_RECWAIT           0xFF000000
 #define         BCMA_CC_OTPC_PROGWAIT          0x00FFFF00
@@ -72,6 +75,8 @@
 #define         BCMA_CC_OTPP_READ              0x40000000
 #define         BCMA_CC_OTPP_START             0x80000000
 #define         BCMA_CC_OTPP_BUSY              0x80000000
+#define BCMA_CC_OTPL                   0x001C          /* OTP layout */
+#define  BCMA_CC_OTPL_GURGN_OFFSET     0x00000FFF      /* offset of general use region */
 #define BCMA_CC_IRQSTAT                        0x0020
 #define BCMA_CC_IRQMASK                        0x0024
 #define         BCMA_CC_IRQ_GPIO               0x00000001      /* gpio intr */
 #define         BCMA_CC_IRQ_WDRESET            0x80000000      /* watchdog reset occurred */
 #define BCMA_CC_CHIPCTL                        0x0028          /* Rev >= 11 only */
 #define BCMA_CC_CHIPSTAT               0x002C          /* Rev >= 11 only */
+#define  BCMA_CC_CHIPST_4313_SPROM_PRESENT     1
+#define  BCMA_CC_CHIPST_4313_OTP_PRESENT       2
+#define  BCMA_CC_CHIPST_4331_SPROM_PRESENT     2
+#define  BCMA_CC_CHIPST_4331_OTP_PRESENT       4
 #define BCMA_CC_JCMD                   0x0030          /* Rev >= 10 only */
 #define  BCMA_CC_JCMD_START            0x80000000
 #define  BCMA_CC_JCMD_BUSY             0x80000000
 #define BCMA_CC_PLLCTL_ADDR            0x0660
 #define BCMA_CC_PLLCTL_DATA            0x0664
 #define BCMA_CC_SPROM                  0x0800 /* SPROM beginning */
-#define BCMA_CC_SPROM_PCIE6            0x0830 /* SPROM beginning on PCIe rev >= 6 */
 
 /* Divider allocation in 4716/47162/5356 */
 #define BCMA_CC_PMU5_MAINPLL_CPU       1