Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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/85426



Tested-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
Loading
Loading
Loading
Loading
+106 −20
Original line number Diff line number Diff line
@@ -300,30 +300,120 @@ 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.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;
	}

	/* 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)) {
		/*
		 * 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?
			 * Now we ask the arch code if there is some sprom
@@ -332,6 +422,7 @@ int bcma_sprom_get(struct bcma_bus *bus)
			err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
			return err;
		}
	}

	sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
			GFP_KERNEL);
@@ -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);

+9 −1
Original line number Diff line number Diff line
@@ -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 */
@@ -79,6 +84,10 @@
#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
@@ -256,7 +265,6 @@
#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