Commit 10d8493c authored by Arend van Spriel's avatar Arend van Spriel Committed by John W. Linville

bcma: add support for on-chip OTP memory used for SPROM storage

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/85426Tested-by: default avatarSaul St. John <saul.stjohn@gmail.com>
Tested-by: default avatarRafal Milecki <zajec5@gmail.com>
Tested-by: default avatarHauke Mehrtens <hauke@hauke-m.de>
Cc: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 4ac887cf
...@@ -300,30 +300,120 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) ...@@ -300,30 +300,120 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
SSB_SROM8_FEM_ANTSWLUT_SHIFT); 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.core->id.rev >= 31) {
if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
return false; return false;
if (bus->drv_cc.core->id.rev >= 32) { srom_control = bcma_read32(bus->drv_cc.core,
sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); BCMA_CC_SROM_CONTROL);
return sromctrl & BCMA_CC_SROM_CONTROL_PRESENT; return srom_control & BCMA_CC_SROM_CONTROL_PRESENT;
} }
/* 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 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) int bcma_sprom_get(struct bcma_bus *bus)
{ {
u16 offset; u16 offset = BCMA_CC_SPROM;
u16 *sprom; u16 *sprom;
int err = 0; int err = 0;
if (!bus->drv_cc.core) if (!bus->drv_cc.core)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (!bcma_is_sprom_available(bus)) { if (!bcma_sprom_ext_available(bus)) {
/*
* External SPROM takes precedence so check
* on-chip OTP only when no external SPROM
* is present.
*/
if (bcma_sprom_onchip_available(bus)) {
/* determine offset */
offset = bcma_sprom_onchip_offset(bus);
}
if (!offset) {
/* /*
* Maybe there is no SPROM on the device? * Maybe there is no SPROM on the device?
* Now we ask the arch code if there is some sprom * Now we ask the arch code if there is some sprom
...@@ -332,6 +422,7 @@ int bcma_sprom_get(struct bcma_bus *bus) ...@@ -332,6 +422,7 @@ int bcma_sprom_get(struct bcma_bus *bus)
err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
return err; return err;
} }
}
sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
GFP_KERNEL); GFP_KERNEL);
...@@ -341,11 +432,6 @@ int bcma_sprom_get(struct bcma_bus *bus) ...@@ -341,11 +432,6 @@ int bcma_sprom_get(struct bcma_bus *bus)
if (bus->chipinfo.id == 0x4331) if (bus->chipinfo.id == 0x4331)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); 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); pr_debug("SPROM offset 0x%x\n", offset);
bcma_sprom_read(bus, offset, sprom); bcma_sprom_read(bus, offset, sprom);
......
...@@ -56,6 +56,9 @@ ...@@ -56,6 +56,9 @@
#define BCMA_CC_OTPS_HW_PROTECT 0x00000001 #define BCMA_CC_OTPS_HW_PROTECT 0x00000001
#define BCMA_CC_OTPS_SW_PROTECT 0x00000002 #define BCMA_CC_OTPS_SW_PROTECT 0x00000002
#define BCMA_CC_OTPS_CID_PROTECT 0x00000004 #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 0x0014 /* OTP control */
#define BCMA_CC_OTPC_RECWAIT 0xFF000000 #define BCMA_CC_OTPC_RECWAIT 0xFF000000
#define BCMA_CC_OTPC_PROGWAIT 0x00FFFF00 #define BCMA_CC_OTPC_PROGWAIT 0x00FFFF00
...@@ -72,6 +75,8 @@ ...@@ -72,6 +75,8 @@
#define BCMA_CC_OTPP_READ 0x40000000 #define BCMA_CC_OTPP_READ 0x40000000
#define BCMA_CC_OTPP_START 0x80000000 #define BCMA_CC_OTPP_START 0x80000000
#define BCMA_CC_OTPP_BUSY 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_IRQSTAT 0x0020
#define BCMA_CC_IRQMASK 0x0024 #define BCMA_CC_IRQMASK 0x0024
#define BCMA_CC_IRQ_GPIO 0x00000001 /* gpio intr */ #define BCMA_CC_IRQ_GPIO 0x00000001 /* gpio intr */
...@@ -79,6 +84,10 @@ ...@@ -79,6 +84,10 @@
#define BCMA_CC_IRQ_WDRESET 0x80000000 /* watchdog reset occurred */ #define BCMA_CC_IRQ_WDRESET 0x80000000 /* watchdog reset occurred */
#define BCMA_CC_CHIPCTL 0x0028 /* Rev >= 11 only */ #define BCMA_CC_CHIPCTL 0x0028 /* Rev >= 11 only */
#define BCMA_CC_CHIPSTAT 0x002C /* 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 0x0030 /* Rev >= 10 only */
#define BCMA_CC_JCMD_START 0x80000000 #define BCMA_CC_JCMD_START 0x80000000
#define BCMA_CC_JCMD_BUSY 0x80000000 #define BCMA_CC_JCMD_BUSY 0x80000000
...@@ -256,7 +265,6 @@ ...@@ -256,7 +265,6 @@
#define BCMA_CC_PLLCTL_ADDR 0x0660 #define BCMA_CC_PLLCTL_ADDR 0x0660
#define BCMA_CC_PLLCTL_DATA 0x0664 #define BCMA_CC_PLLCTL_DATA 0x0664
#define BCMA_CC_SPROM 0x0800 /* SPROM beginning */ #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 */ /* Divider allocation in 4716/47162/5356 */
#define BCMA_CC_PMU5_MAINPLL_CPU 1 #define BCMA_CC_PMU5_MAINPLL_CPU 1
......
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