* [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips
@ 2026-11-09 1:17 Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 01/10] bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC Alessio Ferri
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:17 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, Alessio Ferri, b43-dev
Preparatory work in the bring up of AC family rev1 chips, in particular,
the bcm4352 rev3 and bcm4360 rev3. When public sources were found, the
same gates applied for operations were ported in this code.
Initialize the PMU, the PCIe2 core and chipcommon of the BCM4352/BCM4360
family on chip based on the open source version of broadcom driver, parse
SPROM revision 11, read the NVRAM variables set on top of the SPROM, and
bind bcma-pci-bridge to the BCM4352. Finally, add the missing ids to b43.
Most of the series was tested by Erik Conijin and Ville Takio, both are
cc-ed, but i don't know if i can put their tested by directly in the
commit myself or if it is a very suspicious thing to do.
Concerning the risk of applying the series:
- FORCEHT may change the behaviour of brcmsmac too (can i ask Arend?)
- i do not expect regression from rev11 parsing, especially since no one
use it yet.
- The rest of the series has code mostly gated with the same criteria
used by public code released by Broadcom, so i guess that it is safe.
- b43 marks the ac part BROKEN, so there is very little to preserve.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
Changes in v4:
- Readded the missing signed off
- Added pdoffset2g40ma and pdoffset2g40mvalid to the series, work of
Ville Takio, mostly.
- Link to v3: https://patch.msgid.link/20261031-bcma-ac-rev1-v3-0-1fbe38e8179b@mythread.it
Changes in v3:
- regated most of the checks on the proper version/flag present
instead of forcing everything to be rev3 only, as Vitaly suggested
- Added nvram parsing for all params found in a real northstar router
- Removed AvVmid parsing from nvram, as it doesn't seems to be useful
- Added missing pci ids for 4352/4360 variants
- Link to v2: https://patch.msgid.link/20261027-bcma-ac-rev1-v2-0-b69b61c31920@mythread.it
Changes in v2:
- gated chipcommon behaviour on its own revision instead of chip rev as
pointed out by Vitaly
- Link to v1: https://patch.msgid.link/20261014-bcma-ac-rev1-v1-0-b608e99d3f56@mythread.it
To: Rafał Miłecki <zajec5@gmail.com>
To: Michael Buesch <m@bues.ch>
To: vit9696 <vit9696@protonmail.com>
Cc: linux-wireless@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@vger.kernel.org
Cc: b43-dev@lists.infradead.org
---
Alessio Ferri (10):
bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC
bcma: pmu: init PLL and resources on BCM4352/BCM4360
bcma: pcie2: request HT on BCM4352/BCM4360
bcma: chipcommon: enable asynchronous GPIO interrupts
bcma: extract SPROM revision 11
firmware: bcm47xx_sprom: read the SPROM revision 11 fields from NVRAM
firmware: bcm47xx_sprom: read ledbh4..ledbh15 from NVRAM
firmware: bcm47xx_sprom: read boardflags3 from NVRAM over the SPROM
bcma: add the single-band BCM4352/BCM4360 PCI IDs
wifi: b43: add the 2.4 GHz BCM4352/BCM4360 to the supported bands
drivers/bcma/core.c | 2 +-
drivers/bcma/driver_chipcommon.c | 3 +
drivers/bcma/driver_chipcommon_pmu.c | 81 +++++++++
drivers/bcma/driver_pcie2.c | 3 +
drivers/bcma/host_pci.c | 8 +-
drivers/bcma/sprom.c | 210 ++++++++++++++++++++++-
drivers/firmware/broadcom/bcm47xx_sprom.c | 257 +++++++++++++++++++++++++---
drivers/net/wireless/broadcom/b43/main.c | 6 +
include/linux/bcm47xx_sprom.h | 15 ++
include/linux/bcma/bcma_driver_chipcommon.h | 15 ++
include/linux/ssb/ssb.h | 72 ++++++++
include/linux/ssb/ssb_regs.h | 103 +++++++++++
12 files changed, 747 insertions(+), 28 deletions(-)
---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
change-id: 20261014-bcma-ac-rev1-da894142790f
Best regards,
--
Alessio Ferri <alessio.ferri@mythread.it>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH wireless-next v4 01/10] bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
@ 2026-11-09 1:17 ` Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 02/10] bcma: pmu: init PLL and resources on BCM4352/BCM4360 Alessio Ferri
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:17 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, Alessio Ferri, b43-dev
Clear FORCEHT with bcma_mask32() and leave the other CLKCTLST bits as
they are.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/bcma/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/bcma/core.c b/drivers/bcma/core.c
index 37a5ffe67..f4246689e 100644
--- a/drivers/bcma/core.c
+++ b/drivers/bcma/core.c
@@ -100,7 +100,7 @@ void bcma_core_set_clockmode(struct bcma_device *core,
bcma_err(core->bus, "HT force timeout\n");
break;
case BCMA_CLKMODE_DYNAMIC:
- bcma_set32(core, BCMA_CLKCTLST, ~BCMA_CLKCTLST_FORCEHT);
+ bcma_mask32(core, BCMA_CLKCTLST, ~BCMA_CLKCTLST_FORCEHT);
break;
}
}
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH wireless-next v4 02/10] bcma: pmu: init PLL and resources on BCM4352/BCM4360
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 01/10] bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC Alessio Ferri
@ 2026-11-09 1:17 ` Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 03/10] bcma: pcie2: request HT " Alessio Ferri
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:17 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, Alessio Ferri, b43-dev
Program the PMU as Broadcom's driver does (hndpmu.c):
- on chip rev 3 and later, set the BBPLL to a 960.0098 MHz fractional
VCO from the 40 MHz crystal, unless the backplane runs on HT already;
- set the resource up/down times and, on rev 4 and later, a min
resource mask of 0x103;
- with chipstatus RSRC_INIT_MODE0 clear, program PLL controls 6 and 14,
and on rev 4 and later 7 and 15 with REFCLK enabled in chipcontrol 1;
on rev 3 and later, set max_res_mask to 0x1ff.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/bcma/driver_chipcommon_pmu.c | 81 +++++++++++++++++++++++++++++
include/linux/bcma/bcma_driver_chipcommon.h | 14 +++++
2 files changed, 95 insertions(+)
diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
index 263ef6fa1..7a226fcd2 100644
--- a/drivers/bcma/driver_chipcommon_pmu.c
+++ b/drivers/bcma/driver_chipcommon_pmu.c
@@ -11,6 +11,7 @@
#include "bcma_private.h"
#include <linux/export.h>
+#include <linux/math64.h>
#include <linux/bcma/bcma.h>
u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset)
@@ -145,6 +146,78 @@ static void bcma_pmu2_pll_init0(struct bcma_drv_cc *cc, u32 xtalfreq)
/* TODO: Do we need to update OTP? */
}
+static void bcma_pmu_4360_pll_init(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+ const u32 xtal_hz = 40000000, vco_hz = 960009800;
+ u32 ctl2;
+
+ if (bus->chipinfo.rev < 3)
+ return;
+ /* The backplane may be running from the BBPLL. */
+ if (bcma_cc_read32(cc, BCMA_CLKCTLST) & BCMA_CLKCTLST_HAVEHT)
+ return;
+ if (bcma_pmu_get_alp_clock(cc) != xtal_hz) {
+ bcma_warn(bus, "No 40 MHz crystal, BBPLL left as is\n");
+ return;
+ }
+
+ ctl2 = (vco_hz / xtal_hz) << BCMA_CC_PMU4360_NDIV_INT_SHIFT |
+ BCMA_CC_PMU4360_NDIV_MODE_FRAC << BCMA_CC_PMU4360_NDIV_MODE_SHIFT |
+ 1 << BCMA_CC_PMU4360_P1DIV_SHIFT;
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, ctl2);
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL3,
+ div_u64((u64)(vco_hz % xtal_hz) << 24, xtal_hz));
+ bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
+}
+
+/* Up/down times of resources 0-8 on chip rev 4 and later */
+static const u32 bcma_pmu_4360b1_res_updown[] = {
+ 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00860002,
+ 0x00000000, 0x00020001, 0x00080001, 0x00000000,
+};
+
+static void bcma_pmu_4360_resources_init(struct bcma_drv_cc *cc,
+ u32 *min_msk, u32 *max_msk)
+{
+ u8 rev = cc->core->bus->chipinfo.rev;
+ bool init_mode0 = cc->status & BCMA_CC_CHIPST_4360_RSRC_INIT_MODE0;
+ unsigned int i;
+
+ if (rev < 4) {
+ bcma_pmu_write32(cc, BCMA_CC_PMU_RES_TABSEL, 6);
+ bcma_pmu_write32(cc, BCMA_CC_PMU_RES_UPDNTM, 0x00200001);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(bcma_pmu_4360b1_res_updown); i++) {
+ bcma_pmu_write32(cc, BCMA_CC_PMU_RES_TABSEL, i);
+ bcma_pmu_write32(cc, BCMA_CC_PMU_RES_UPDNTM,
+ bcma_pmu_4360b1_res_updown[i]);
+ }
+ *min_msk = 0x103;
+ /* The max mask must cover the min mask before it is written. */
+ bcma_pmu_set32(cc, BCMA_CC_PMU_MAXRES_MSK, *min_msk);
+ }
+
+ if (init_mode0)
+ return;
+
+ if (rev < 4) {
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL6, 0x09048562);
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL14, 0x09048562);
+ } else {
+ bcma_chipco_chipctl_maskset(cc, 1, ~0,
+ BCMA_CHIPCTL1_4360_REFCLK_EN);
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL6, 0x080004e2);
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL7, 0xe);
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL14, 0x080004e2);
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL15, 0xe);
+ }
+ bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
+
+ if (rev >= 3)
+ *max_msk = 0x1ff;
+}
+
static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
{
struct bcma_bus *bus = cc->core->bus;
@@ -156,6 +229,10 @@ static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
xtalfreq = 20000;
bcma_pmu2_pll_init0(cc, xtalfreq);
break;
+ case BCMA_CHIP_ID_BCM4352:
+ case BCMA_CHIP_ID_BCM4360:
+ bcma_pmu_4360_pll_init(cc);
+ break;
}
}
@@ -188,6 +265,10 @@ static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
BCMA_RES_4314_WL_CORE_READY;
max_msk = 0x3FFFFFFF;
break;
+ case BCMA_CHIP_ID_BCM4352:
+ case BCMA_CHIP_ID_BCM4360:
+ bcma_pmu_4360_resources_init(cc, &min_msk, &max_msk);
+ break;
default:
bcma_debug(bus, "PMU resource config unknown or not needed for device 0x%04X\n",
bus->chipinfo.id);
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 0cb6638b5..525155658 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -107,6 +107,7 @@
#define BCMA_CC_CHIPST_4706_PCIE1_DISABLE BIT(5) /* PCIE1 enable strap pin */
#define BCMA_CC_CHIPST_5357_NAND_BOOT BIT(4) /* NAND boot, valid for CC rev 38 and/or BCM5357 */
#define BCMA_CC_CHIPST_4360_XTAL_40MZ 0x00000001
+#define BCMA_CC_CHIPST_4360_RSRC_INIT_MODE0 0x00000020
#define BCMA_CC_JCMD 0x0030 /* Rev >= 10 only */
#define BCMA_CC_JCMD_START 0x80000000
#define BCMA_CC_JCMD_BUSY 0x80000000
@@ -500,6 +501,16 @@
#define BCMA_CC_PMU_PLL_CTL3 3
#define BCMA_CC_PMU_PLL_CTL4 4
#define BCMA_CC_PMU_PLL_CTL5 5
+#define BCMA_CC_PMU_PLL_CTL6 6
+#define BCMA_CC_PMU_PLL_CTL7 7
+#define BCMA_CC_PMU_PLL_CTL14 14
+#define BCMA_CC_PMU_PLL_CTL15 15
+
+/* BCM4360 PLL control 2 */
+#define BCMA_CC_PMU4360_P1DIV_SHIFT 0
+#define BCMA_CC_PMU4360_NDIV_MODE_SHIFT 4
+#define BCMA_CC_PMU4360_NDIV_MODE_FRAC 3
+#define BCMA_CC_PMU4360_NDIV_INT_SHIFT 7
#define BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000
#define BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT 20
@@ -527,6 +538,9 @@
#define BCMA_CHIPCTL_4331_BT_SHD0_ON_GPIO4 BIT(16) /* enable bt_shd0 at gpio4 */
#define BCMA_CHIPCTL_4331_BT_SHD1_ON_GPIO5 BIT(17) /* enable bt_shd1 at gpio5 */
+/* BCM4360 ChipControl 1 */
+#define BCMA_CHIPCTL1_4360_REFCLK_EN BIT(11)
+
/* 43224 chip-specific ChipControl register bits */
#define BCMA_CCTRL_43224_GPIO_TOGGLE 0x8000 /* gpio[3:0] pins as btcoex or s/w gpio */
#define BCMA_CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 /* 12 mA drive strength */
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH wireless-next v4 03/10] bcma: pcie2: request HT on BCM4352/BCM4360
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 01/10] bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 02/10] bcma: pmu: init PLL and resources on BCM4352/BCM4360 Alessio Ferri
@ 2026-11-09 1:17 ` Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 04/10] bcma: chipcommon: enable asynchronous GPIO interrupts Alessio Ferri
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:17 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, Alessio Ferri, b43-dev
Set HAVEHTREQ in the PCIe2 core's clock control on chip revs before 4,
as Broadcom's driver does.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/bcma/driver_pcie2.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/bcma/driver_pcie2.c b/drivers/bcma/driver_pcie2.c
index cf889fc62..54daeaa43 100644
--- a/drivers/bcma/driver_pcie2.c
+++ b/drivers/bcma/driver_pcie2.c
@@ -170,6 +170,9 @@ void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2)
case BCMA_CHIP_ID_BCM4360:
case BCMA_CHIP_ID_BCM4352:
pcie2->reqsize = 1024;
+ if (ci->rev < 4)
+ pcie2_set32(pcie2, BCMA_CLKCTLST,
+ BCMA_CLKCTLST_HAVEHTREQ);
break;
default:
pcie2->reqsize = 128;
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH wireless-next v4 04/10] bcma: chipcommon: enable asynchronous GPIO interrupts
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (2 preceding siblings ...)
2026-11-09 1:17 ` [PATCH wireless-next v4 03/10] bcma: pcie2: request HT " Alessio Ferri
@ 2026-11-09 1:17 ` Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 05/10] bcma: extract SPROM revision 11 Alessio Ferri
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:17 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, Alessio Ferri, b43-dev
Set CORECTL_ASYNCGPIO on chipcommon rev 21 and later, as Broadcom's
driver does at attach, so that GPIO interrupts are generated without the
backplane clock.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/bcma/driver_chipcommon.c | 3 +++
include/linux/bcma/bcma_driver_chipcommon.h | 1 +
2 files changed, 4 insertions(+)
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index fd91a39f0..f5e1b5415 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -227,6 +227,9 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
((leddc_on << BCMA_CC_GPIOTIMER_ONTIME_SHIFT) |
(leddc_off << BCMA_CC_GPIOTIMER_OFFTIME_SHIFT)));
}
+
+ if (cc->core->id.rev >= 21)
+ bcma_cc_set32(cc, BCMA_CC_CORECTL, BCMA_CC_CORECTL_ASYNCGPIO);
cc->ticks_per_ms = bcma_chipco_watchdog_ticks_per_ms(cc);
cc->setup_done = true;
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 525155658..c7d69d583 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -54,6 +54,7 @@
#define BCMA_CC_CORECTL 0x0008
#define BCMA_CC_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */
#define BCMA_CC_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */
+#define BCMA_CC_CORECTL_ASYNCGPIO 0x00000004 /* GPIO IRQs without backplane clock */
#define BCMA_CC_CORECTL_UARTCLKEN 0x00000008 /* UART clock enable (rev >= 21) */
#define BCMA_CC_BIST 0x000C
#define BCMA_CC_OTPS 0x0010 /* OTP status */
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH wireless-next v4 05/10] bcma: extract SPROM revision 11
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (3 preceding siblings ...)
2026-11-09 1:17 ` [PATCH wireless-next v4 04/10] bcma: chipcommon: enable asynchronous GPIO interrupts Alessio Ferri
@ 2026-11-09 1:17 ` Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 06/10] firmware: bcm47xx_sprom: read the SPROM revision 11 fields from NVRAM Alessio Ferri
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:17 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, Alessio Ferri, b43-dev
Add bcma_sprom_extract_r11() and have bcma_sprom_get() route SPROM
revision 11 to it instead of bcma_sprom_extract_r8(). Add the struct
ssb_sprom fields it fills: per-chain power info and rx gains, FEM
configuration, TSSI floor, rpcal, power detector offsets, per-rate
power offsets, temperature words and antenna gain in quarter dB.
Header fields use the rev 8 masks at their rev 11 offsets. The layout
follows bcmsrom_tbl.h and bcmsrom_fmt.h from bcmdhd; offsets checked
against SPROM dumps of BCM4352 and BCM4360 boards.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/bcma/sprom.c | 207 ++++++++++++++++++++++++++++++++++++++++++-
include/linux/ssb/ssb.h | 71 +++++++++++++++
include/linux/ssb/ssb_regs.h | 103 +++++++++++++++++++++
3 files changed, 380 insertions(+), 1 deletion(-)
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index e668ad796..339111519 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -474,6 +474,208 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
}
+static void bcma_sprom_unpack_rxgains(struct ssb_sprom_rxgains *out,
+ int chain, u8 packed)
+{
+ out->trelnabyp[chain] = (packed >> 7) & 0x01;
+ out->triso[chain] = (packed >> 3) & 0x0f;
+ out->elnagain[chain] = packed & 0x07;
+}
+
+static void bcma_sprom_extract_r11(struct bcma_bus *bus, const u16 *sprom)
+{
+ static const u16 pwr_info_offset[] = {
+ SSB_SPROM11_PWR_INFO_CORE0,
+ SSB_SPROM11_PWR_INFO_CORE1,
+ SSB_SPROM11_PWR_INFO_CORE2,
+ };
+ u16 v;
+ int i, j;
+
+ BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) >
+ ARRAY_SIZE(bus->sprom.core_pwr_info));
+
+ /* Header: rev-11 offsets, rev-8 masks/shifts. */
+ for (i = 0; i < 3; i++) {
+ v = sprom[SPOFF(SSB_SPROM11_IL0MAC) + i];
+ *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
+ }
+
+ SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
+ SPEX(board_type, SSB_SPROM1_SPID, ~0, 0);
+ SPEX(boardflags_lo, SSB_SPROM8_BFLLO, ~0, 0);
+ SPEX(boardflags_hi, SSB_SPROM8_BFLHI, ~0, 0);
+ SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, ~0, 0);
+ SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, ~0, 0);
+ SPEX32(boardflags3, SSB_SPROM11_BFL3, ~0, 0);
+
+ SPEX(country_code, SSB_SPROM11_CCODE, ~0, 0);
+ SPEX(gpio0, SSB_SPROM11_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
+ SPEX(gpio1, SSB_SPROM11_GPIOA, SSB_SPROM8_GPIOA_P1,
+ SSB_SPROM8_GPIOA_P1_SHIFT);
+ SPEX(gpio2, SSB_SPROM11_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
+ SPEX(gpio3, SSB_SPROM11_GPIOB, SSB_SPROM8_GPIOB_P3,
+ SSB_SPROM8_GPIOB_P3_SHIFT);
+ SPEX(ant_available_a, SSB_SPROM11_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
+ SSB_SPROM8_ANTAVAIL_A_SHIFT);
+ SPEX(ant_available_bg, SSB_SPROM11_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
+ SSB_SPROM8_ANTAVAIL_BG_SHIFT);
+ SPEX(txchain, SSB_SPROM11_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN,
+ SSB_SPROM8_TXRXC_TXCHAIN_SHIFT);
+ SPEX(rxchain, SSB_SPROM11_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN,
+ SSB_SPROM8_TXRXC_RXCHAIN_SHIFT);
+ SPEX(antswitch, SSB_SPROM11_TXRXC, SSB_SPROM8_TXRXC_SWITCH,
+ SSB_SPROM8_TXRXC_SWITCH_SHIFT);
+ SPEX(subband5gver, SSB_SPROM11_SUBBAND5GVER, 0x00ff, 0);
+
+ SPEX(tssiposslope2g, SSB_SPROM11_FEM_CFG1,
+ SSB_SPROM11_FEM_TSSIPOSSLOPE, 0);
+ SPEX(epagain2g, SSB_SPROM11_FEM_CFG1, SSB_SPROM11_FEM_EPAGAIN,
+ SSB_SPROM11_FEM_EPAGAIN_SHIFT);
+ SPEX(pdgain2g, SSB_SPROM11_FEM_CFG1, SSB_SPROM11_FEM_PDGAIN,
+ SSB_SPROM11_FEM_PDGAIN_SHIFT);
+ SPEX(tworangetssi2g, SSB_SPROM11_FEM_CFG1,
+ SSB_SPROM11_FEM_TWORANGETSSI, SSB_SPROM11_FEM_TWORANGETSSI_SHIFT);
+ SPEX(papdcap2g, SSB_SPROM11_FEM_CFG1, SSB_SPROM11_FEM_PAPDCAP,
+ SSB_SPROM11_FEM_PAPDCAP_SHIFT);
+ SPEX(femctrl, SSB_SPROM11_FEM_CFG1, SSB_SPROM11_FEM_CTRL,
+ SSB_SPROM11_FEM_CTRL_SHIFT);
+ SPEX(tssiposslope5g, SSB_SPROM11_FEM_CFG2,
+ SSB_SPROM11_FEM_TSSIPOSSLOPE, 0);
+ SPEX(epagain5g, SSB_SPROM11_FEM_CFG2, SSB_SPROM11_FEM_EPAGAIN,
+ SSB_SPROM11_FEM_EPAGAIN_SHIFT);
+ SPEX(pdgain5g, SSB_SPROM11_FEM_CFG2, SSB_SPROM11_FEM_PDGAIN,
+ SSB_SPROM11_FEM_PDGAIN_SHIFT);
+ SPEX(tworangetssi5g, SSB_SPROM11_FEM_CFG2,
+ SSB_SPROM11_FEM_TWORANGETSSI, SSB_SPROM11_FEM_TWORANGETSSI_SHIFT);
+ SPEX(papdcap5g, SSB_SPROM11_FEM_CFG2, SSB_SPROM11_FEM_PAPDCAP,
+ SSB_SPROM11_FEM_PAPDCAP_SHIFT);
+ SPEX(gainctrlsph, SSB_SPROM11_FEM_CFG2,
+ SSB_SPROM11_FEM_GAINCTRLSPH, SSB_SPROM11_FEM_GAINCTRLSPH_SHIFT);
+
+ SPEX(tssifloor2g, SSB_SPROM11_TSSIFLOOR_2G,
+ SSB_SPROM11_TSSIFLOOR_MASK, 0);
+ for (i = 0; i < 4; i++)
+ SPEX(tssifloor5g[i], SSB_SPROM11_TSSIFLOOR_5GL + i * 2,
+ SSB_SPROM11_TSSIFLOOR_MASK, 0);
+
+ for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
+ struct ssb_sprom_core_pwr_info *p = &bus->sprom.core_pwr_info[i];
+ u16 base = pwr_info_offset[i];
+
+ p->maxp2ga = sprom[SPOFF(base + SSB_SPROM11_PWR_MAXP2GA)] & 0xff;
+
+ for (j = 0; j < 3; j++)
+ p->pa2ga[j] = sprom[SPOFF(base + SSB_SPROM11_PWR_PA2GA) + j];
+
+ v = sprom[SPOFF(base + SSB_SPROM11_PWR_MAXP5GA)];
+ p->maxp5ga[0] = v & 0xff;
+ p->maxp5ga[1] = (v >> 8) & 0xff;
+ v = sprom[SPOFF(base + SSB_SPROM11_PWR_MAXP5GA) + 1];
+ p->maxp5ga[2] = v & 0xff;
+ p->maxp5ga[3] = (v >> 8) & 0xff;
+
+ for (j = 0; j < 12; j++)
+ p->pa5ga[j] = sprom[SPOFF(base + SSB_SPROM11_PWR_PA5GA) + j];
+
+ v = sprom[SPOFF(base + SSB_SPROM11_PWR_RXGAINS0)];
+ bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_5gm, i, v & 0xff);
+ bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_5gh, i, (v >> 8) & 0xff);
+ v = sprom[SPOFF(base + SSB_SPROM11_PWR_RXGAINS1)];
+ bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_2g, i, v & 0xff);
+ bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_5gl, i, (v >> 8) & 0xff);
+ }
+
+ SPEX(rpcal2g, SSB_SPROM11_RPCAL_2G, ~0, 0);
+ SPEX(rpcal5gb[0], SSB_SPROM11_RPCAL_5GL, ~0, 0);
+ SPEX(rpcal5gb[1], SSB_SPROM11_RPCAL_5GM, ~0, 0);
+ SPEX(rpcal5gb[2], SSB_SPROM11_RPCAL_5GH, ~0, 0);
+ SPEX(rpcal5gb[3], SSB_SPROM11_RPCAL_5GU, ~0, 0);
+
+ SPEX(pdoffset2g40ma[0], SSB_SPROM11_PDOFFSET2G40M,
+ SSB_SPROM11_PDOFFSET2G40M_MA0, SSB_SPROM11_PDOFFSET2G40M_MA0_SHIFT);
+ SPEX(pdoffset2g40ma[1], SSB_SPROM11_PDOFFSET2G40M,
+ SSB_SPROM11_PDOFFSET2G40M_MA1, SSB_SPROM11_PDOFFSET2G40M_MA1_SHIFT);
+ SPEX(pdoffset2g40ma[2], SSB_SPROM11_PDOFFSET2G40M,
+ SSB_SPROM11_PDOFFSET2G40M_MA2, SSB_SPROM11_PDOFFSET2G40M_MA2_SHIFT);
+ SPEX(pdoffset2g40mvalid, SSB_SPROM11_PDOFFSET2G40M,
+ SSB_SPROM11_PDOFFSET2G40M_VALID,
+ SSB_SPROM11_PDOFFSET2G40M_VALID_SHIFT);
+
+ for (i = 0; i < 3; i++) {
+ bus->sprom.pdoffset40ma[i] =
+ sprom[SPOFF(SSB_SPROM11_PDOFFSET40MA) + i];
+ bus->sprom.pdoffset80ma[i] =
+ sprom[SPOFF(SSB_SPROM11_PDOFFSET80MA) + i];
+ }
+
+ SPEX(cckbw202gpo, SSB_SPROM11_CCKBW202GPO, ~0, 0);
+ SPEX(cckbw20ul2gpo, SSB_SPROM11_CCKBW20UL2GPO, ~0, 0);
+ SPEX32(mcsbw202gpo, SSB_SPROM11_MCSBW202GPO, ~0, 0);
+ SPEX32(mcsbw402gpo, SSB_SPROM11_MCSBW402GPO, ~0, 0);
+ SPEX(dot11agofdmhrbw202gpo, SSB_SPROM11_DOT11AGOFDMHRBW202GPO, ~0, 0);
+ SPEX(ofdmlrbw202gpo, SSB_SPROM11_OFDMLRBW202GPO, ~0, 0);
+ SPEX32(mcsbw205glpo, SSB_SPROM11_MCSBW205GLPO, ~0, 0);
+ SPEX32(mcsbw405glpo, SSB_SPROM11_MCSBW405GLPO, ~0, 0);
+ SPEX32(mcsbw805glpo, SSB_SPROM11_MCSBW805GLPO, ~0, 0);
+ SPEX32(mcsbw205gmpo, SSB_SPROM11_MCSBW205GMPO, ~0, 0);
+ SPEX32(mcsbw405gmpo, SSB_SPROM11_MCSBW405GMPO, ~0, 0);
+ SPEX32(mcsbw805gmpo, SSB_SPROM11_MCSBW805GMPO, ~0, 0);
+ SPEX32(mcsbw205ghpo, SSB_SPROM11_MCSBW205GHPO, ~0, 0);
+ SPEX32(mcsbw405ghpo, SSB_SPROM11_MCSBW405GHPO, ~0, 0);
+ SPEX32(mcsbw805ghpo, SSB_SPROM11_MCSBW805GHPO, ~0, 0);
+
+ SPEX(mcslr5gpo[0], SSB_SPROM11_MCSLR5GLPO, ~0, 0);
+ SPEX(mcslr5gpo[1], SSB_SPROM11_MCSLR5GMPO, ~0, 0);
+ SPEX(mcslr5gpo[2], SSB_SPROM11_MCSLR5GHPO, ~0, 0);
+ SPEX(sb20in40hrpo, SSB_SPROM11_SB20IN40HRPO, ~0, 0);
+ SPEX(sb20in40lrpo, SSB_SPROM11_SB20IN40LRPO, ~0, 0);
+ SPEX(sb20in80and160hr5gpo[0], SSB_SPROM11_SB20IN80AND160HR5GLPO, ~0, 0);
+ SPEX(sb20in80and160hr5gpo[1], SSB_SPROM11_SB20IN80AND160HR5GMPO, ~0, 0);
+ SPEX(sb20in80and160hr5gpo[2], SSB_SPROM11_SB20IN80AND160HR5GHPO, ~0, 0);
+ SPEX(sb20in80and160lr5gpo[0], SSB_SPROM11_SB20IN80AND160LR5GLPO, ~0, 0);
+ SPEX(sb20in80and160lr5gpo[1], SSB_SPROM11_SB20IN80AND160LR5GMPO, ~0, 0);
+ SPEX(sb20in80and160lr5gpo[2], SSB_SPROM11_SB20IN80AND160LR5GHPO, ~0, 0);
+ SPEX(sb40and80hr5gpo[0], SSB_SPROM11_SB40AND80HR5GLPO, ~0, 0);
+ SPEX(sb40and80hr5gpo[1], SSB_SPROM11_SB40AND80HR5GMPO, ~0, 0);
+ SPEX(sb40and80hr5gpo[2], SSB_SPROM11_SB40AND80HR5GHPO, ~0, 0);
+ SPEX(sb40and80lr5gpo[0], SSB_SPROM11_SB40AND80LR5GLPO, ~0, 0);
+ SPEX(sb40and80lr5gpo[1], SSB_SPROM11_SB40AND80LR5GMPO, ~0, 0);
+ SPEX(sb40and80lr5gpo[2], SSB_SPROM11_SB40AND80LR5GHPO, ~0, 0);
+ SPEX(dot11agduphrpo, SSB_SPROM11_DOT11AGDUPHRPO, ~0, 0);
+ SPEX(dot11agduplrpo, SSB_SPROM11_DOT11AGDUPLRPO, ~0, 0);
+
+ SPEX(tempthresh, SSB_SPROM11_THERMAL, SSB_SPROM8_THERMAL_TRESH,
+ SSB_SPROM8_THERMAL_TRESH_SHIFT);
+ SPEX(tempoffset, SSB_SPROM11_THERMAL, SSB_SPROM8_THERMAL_OFFSET,
+ SSB_SPROM8_THERMAL_OFFSET_SHIFT);
+ SPEX(rawtempsense, SSB_SPROM11_RAWTS, SSB_SPROM8_RAWTS_RAWTEMP,
+ SSB_SPROM8_RAWTS_RAWTEMP_SHIFT);
+ SPEX(measpower, SSB_SPROM11_RAWTS, SSB_SPROM8_RAWTS_MEASPOWER,
+ SSB_SPROM8_RAWTS_MEASPOWER_SHIFT);
+ SPEX(tempsense_slope, SSB_SPROM11_OPT_CORRX,
+ SSB_SPROM8_OPT_CORRX_TEMP_SLOPE,
+ SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT);
+ SPEX(tempcorrx, SSB_SPROM11_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX,
+ SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT);
+ SPEX(tempsense_option, SSB_SPROM11_OPT_CORRX,
+ SSB_SPROM8_OPT_CORRX_TEMP_OPTION,
+ SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT);
+ SPEX(phycal_tempdelta, SSB_SPROM11_TEMPDELTA,
+ SSB_SPROM8_TEMPDELTA_PHYCAL,
+ SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT);
+ SPEX(temps_period, SSB_SPROM11_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PERIOD,
+ SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT);
+ SPEX(temps_hysteresis, SSB_SPROM11_TEMPDELTA,
+ SSB_SPROM8_TEMPDELTA_HYSTERESIS,
+ SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
+
+ bus->sprom.antenna_gain_qdb[0] =
+ ssb_sprom_antgain_qdb(sprom[SPOFF(SSB_SPROM11_AGBG10)] & 0xff);
+ bus->sprom.antenna_gain_qdb[1] =
+ ssb_sprom_antgain_qdb(sprom[SPOFF(SSB_SPROM11_AGBG2A0)] >> 8);
+}
+
/*
* Indicates the presence of external SPROM.
*/
@@ -640,7 +842,10 @@ int bcma_sprom_get(struct bcma_bus *bus)
bcma_warn(bus, "Invalid SPROM read from the PCIe card, trying to use fallback SPROM\n");
err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
} else {
- bcma_sprom_extract_r8(bus, sprom);
+ if (bus->sprom.revision == 11)
+ bcma_sprom_extract_r11(bus, sprom);
+ else
+ bcma_sprom_extract_r8(bus, sprom);
kfree(sprom);
}
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index 67cb66f6f..a89bdbf56 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -23,6 +23,19 @@ struct ssb_sprom_core_pwr_info {
u8 itssi_2g, itssi_5g;
u8 maxpwr_2g, maxpwr_5gl, maxpwr_5g, maxpwr_5gh;
u16 pa_2g[4], pa_5gl[4], pa_5g[4], pa_5gh[4];
+
+ /* SROM rev 11 */
+ u8 maxp2ga;
+ u8 maxp5ga[4];
+ u16 pa2ga[3];
+ u16 pa5ga[12];
+};
+
+/* Rx gain settings of a band, indexed by chain. SROM rev 11. */
+struct ssb_sprom_rxgains {
+ u8 elnagain[3];
+ u8 triso[3];
+ u8 trelnabyp[3];
};
struct ssb_sprom {
@@ -192,8 +205,66 @@ struct ssb_sprom {
u16 legofdm40duppo;
u8 sar2g;
u8 sar5g;
+
+ /* SROM rev 11 only. */
+ struct ssb_sprom_rxgains rxgains_2g;
+ struct ssb_sprom_rxgains rxgains_5gl;
+ struct ssb_sprom_rxgains rxgains_5gm;
+ struct ssb_sprom_rxgains rxgains_5gh;
+ u8 subband5gver;
+ /* Antenna gain, quarter dB: [0] agbg0 (2.4 GHz), [1] aga0 (5 GHz). */
+ s8 antenna_gain_qdb[2];
+ u16 dot11agofdmhrbw202gpo;
+ u16 ofdmlrbw202gpo;
+ u32 mcsbw805glpo;
+ u32 mcsbw805gmpo;
+ u32 mcsbw805ghpo;
+ /* TX beamforming RX path calibration, 5 GHz in l, m, h, u order. */
+ u16 rpcal2g;
+ u16 rpcal5gb[4];
+ u16 pdoffset40ma[3];
+ u16 pdoffset80ma[3];
+ u8 pdoffset2g40ma[3]; /* 4 bits, signed */
+ u8 pdoffset2g40mvalid;
+ /* Per-rate-group power offsets, nibbles; [i] is 5 GHz l/m/h. */
+ u16 mcslr5gpo[3];
+ u16 sb20in40hrpo;
+ u16 sb20in40lrpo;
+ u16 sb20in80and160hr5gpo[3];
+ u16 sb20in80and160lr5gpo[3];
+ u16 sb40and80hr5gpo[3];
+ u16 sb40and80lr5gpo[3];
+ u16 dot11agduphrpo;
+ u16 dot11agduplrpo;
+ /* FEM configuration; femctrl selects the FEM control scheme. */
+ u8 tssiposslope2g;
+ u8 epagain2g;
+ u8 pdgain2g;
+ u8 tworangetssi2g;
+ u8 papdcap2g;
+ u8 femctrl;
+ u8 tssiposslope5g;
+ u8 epagain5g;
+ u8 pdgain5g;
+ u8 tworangetssi5g;
+ u8 papdcap5g;
+ u8 gainctrlsph;
+ /* TSSI floor, 0x3ff when not programmed. */
+ u16 tssifloor2g;
+ u16 tssifloor5g[4];
};
+/*
+ * SROM rev 11 antenna gain byte, agbg0 or aga0, in quarter dB: whole dB in
+ * bits [5:0], signed, and quarter dB in bits [7:6].
+ */
+static inline s8 ssb_sprom_antgain_qdb(u8 raw)
+{
+ s8 gain = (s8)((raw & 0x3f) << 2) >> 2;
+
+ return 4 * gain + (raw >> 6);
+}
+
/* Information about the PCB the circuitry is soldered on. */
struct ssb_boardinfo {
u16 vendor;
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
index 210f46494..2546c02c9 100644
--- a/include/linux/ssb/ssb_regs.h
+++ b/include/linux/ssb/ssb_regs.h
@@ -606,6 +606,109 @@
#define SSB_SPROM8_BW40PO 0x0196
#define SSB_SPROM8_BWDUPPO 0x0198
+/* SROM revision 11. Header fields use the SSB_SPROM8 masks and shifts. */
+#define SSB_SPROM11_BFL3 0x008C /* boardflags3, 32 bits */
+#define SSB_SPROM11_IL0MAC 0x0090 /* 6 byte MAC address */
+#define SSB_SPROM11_CCODE 0x0096 /* 2 byte country code */
+#define SSB_SPROM11_GPIOA 0x009A /* ledbh0 low byte, ledbh1 high */
+#define SSB_SPROM11_GPIOB 0x009C /* ledbh2 low byte, ledbh3 high */
+#define SSB_SPROM11_ANTAVAIL 0x00A0
+#define SSB_SPROM11_AGBG10 0x00A2 /* agbg0 low byte, agbg1 high */
+#define SSB_SPROM11_AGBG2A0 0x00A4 /* agbg2 low byte, aga0 high */
+#define SSB_SPROM11_AGA21 0x00A6 /* aga1 low byte, aga2 high */
+#define SSB_SPROM11_TXRXC 0x00A8
+/* FEM configuration, CFG1 for 2.4 GHz and CFG2 for 5 GHz. */
+#define SSB_SPROM11_FEM_CFG1 0x00AA
+#define SSB_SPROM11_FEM_CFG2 0x00AC
+#define SSB_SPROM11_FEM_TSSIPOSSLOPE 0x0001
+#define SSB_SPROM11_FEM_EPAGAIN 0x000E
+#define SSB_SPROM11_FEM_EPAGAIN_SHIFT 1
+#define SSB_SPROM11_FEM_PDGAIN 0x01F0
+#define SSB_SPROM11_FEM_PDGAIN_SHIFT 4
+#define SSB_SPROM11_FEM_TWORANGETSSI 0x0200
+#define SSB_SPROM11_FEM_TWORANGETSSI_SHIFT 9
+#define SSB_SPROM11_FEM_PAPDCAP 0x0400
+#define SSB_SPROM11_FEM_PAPDCAP_SHIFT 10
+#define SSB_SPROM11_FEM_CTRL 0xF800 /* CFG1: femctrl */
+#define SSB_SPROM11_FEM_CTRL_SHIFT 11
+#define SSB_SPROM11_FEM_GAINCTRLSPH 0xF800 /* CFG2: gainctrlsph */
+#define SSB_SPROM11_FEM_GAINCTRLSPH_SHIFT 11
+/* Temperature words, with the SSB_SPROM8 layout. */
+#define SSB_SPROM11_THERMAL 0x00AE
+#define SSB_SPROM11_RAWTS 0x00B0
+#define SSB_SPROM11_OPT_CORRX 0x00B2
+#define SSB_SPROM11_TEMPDELTA 0x00B8
+/* TSSI floor: 2.4 GHz, then 5 GHz l, m, h, u. */
+#define SSB_SPROM11_TSSIFLOOR_2G 0x00BE
+#define SSB_SPROM11_TSSIFLOOR_5GL 0x00C0
+#define SSB_SPROM11_TSSIFLOOR_MASK 0x03FF
+#define SSB_SPROM11_PDOFFSET2G40M 0x00C8 /* 4 bits per chain */
+#define SSB_SPROM11_PDOFFSET2G40M_MA0 0x000F
+#define SSB_SPROM11_PDOFFSET2G40M_MA0_SHIFT 0
+#define SSB_SPROM11_PDOFFSET2G40M_MA1 0x00F0
+#define SSB_SPROM11_PDOFFSET2G40M_MA1_SHIFT 4
+#define SSB_SPROM11_PDOFFSET2G40M_MA2 0x0F00
+#define SSB_SPROM11_PDOFFSET2G40M_MA2_SHIFT 8
+#define SSB_SPROM11_PDOFFSET2G40M_VALID 0x8000
+#define SSB_SPROM11_PDOFFSET2G40M_VALID_SHIFT 15
+#define SSB_SPROM11_PDOFFSET40MA 0x00CA /* 3 x u16, one per chain */
+#define SSB_SPROM11_PDOFFSET80MA 0x00D0 /* 3 x u16, one per chain */
+#define SSB_SPROM11_SUBBAND5GVER 0x00D6
+
+/* Per-chain power info blocks, 0x28 bytes apart; SSB_SPROM11_PWR_* are
+ * offsets within a block.
+ */
+#define SSB_SPROM11_PWR_INFO_CORE0 0x00D8
+#define SSB_SPROM11_PWR_INFO_CORE1 0x0100
+#define SSB_SPROM11_PWR_INFO_CORE2 0x0128
+
+#define SSB_SPROM11_PWR_MAXP2GA 0x0000 /* u8 in low byte */
+#define SSB_SPROM11_PWR_PA2GA 0x0002 /* 3 x u16 */
+#define SSB_SPROM11_PWR_RXGAINS0 0x0008 /* 5gm (lo) / 5gh (hi) */
+#define SSB_SPROM11_PWR_RXGAINS1 0x000A /* 2g (lo) / 5gl (hi) */
+#define SSB_SPROM11_PWR_MAXP5GA 0x000C /* 2 x u16 -> 4 x u8 */
+#define SSB_SPROM11_PWR_PA5GA 0x0010 /* 12 x u16 */
+
+#define SSB_SPROM11_CCKBW202GPO 0x0150
+#define SSB_SPROM11_CCKBW20UL2GPO 0x0152
+#define SSB_SPROM11_MCSBW202GPO 0x0154
+#define SSB_SPROM11_MCSBW402GPO 0x0158
+#define SSB_SPROM11_DOT11AGOFDMHRBW202GPO 0x015C
+#define SSB_SPROM11_OFDMLRBW202GPO 0x015E
+#define SSB_SPROM11_MCSBW205GLPO 0x0160
+#define SSB_SPROM11_MCSBW405GLPO 0x0164
+#define SSB_SPROM11_MCSBW805GLPO 0x0168
+#define SSB_SPROM11_RPCAL_2G 0x016C
+#define SSB_SPROM11_RPCAL_5GL 0x016E
+#define SSB_SPROM11_MCSBW205GMPO 0x0170
+#define SSB_SPROM11_MCSBW405GMPO 0x0174
+#define SSB_SPROM11_MCSBW805GMPO 0x0178
+#define SSB_SPROM11_RPCAL_5GM 0x017C
+#define SSB_SPROM11_RPCAL_5GH 0x017E
+#define SSB_SPROM11_MCSBW205GHPO 0x0180
+#define SSB_SPROM11_MCSBW405GHPO 0x0184
+#define SSB_SPROM11_MCSBW805GHPO 0x0188
+#define SSB_SPROM11_RPCAL_5GU 0x018C
+#define SSB_SPROM11_MCSLR5GLPO 0x0190
+#define SSB_SPROM11_MCSLR5GMPO 0x0192
+#define SSB_SPROM11_MCSLR5GHPO 0x0194
+#define SSB_SPROM11_SB20IN40HRPO 0x0196
+#define SSB_SPROM11_SB20IN80AND160HR5GLPO 0x0198
+#define SSB_SPROM11_SB40AND80HR5GLPO 0x019A
+#define SSB_SPROM11_SB20IN80AND160HR5GMPO 0x019C
+#define SSB_SPROM11_SB40AND80HR5GMPO 0x019E
+#define SSB_SPROM11_SB20IN80AND160HR5GHPO 0x01A0
+#define SSB_SPROM11_SB40AND80HR5GHPO 0x01A2
+#define SSB_SPROM11_SB20IN40LRPO 0x01A4
+#define SSB_SPROM11_SB20IN80AND160LR5GLPO 0x01A6
+#define SSB_SPROM11_SB40AND80LR5GLPO 0x01A8
+#define SSB_SPROM11_SB20IN80AND160LR5GMPO 0x01AA
+#define SSB_SPROM11_SB40AND80LR5GMPO 0x01AC
+#define SSB_SPROM11_SB20IN80AND160LR5GHPO 0x01AE
+#define SSB_SPROM11_SB40AND80LR5GHPO 0x01B0
+#define SSB_SPROM11_DOT11AGDUPHRPO 0x01B2
+#define SSB_SPROM11_DOT11AGDUPLRPO 0x01B4
+
/* Values for boardflags_lo read from SPROM */
#define SSB_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */
#define SSB_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH wireless-next v4 06/10] firmware: bcm47xx_sprom: read the SPROM revision 11 fields from NVRAM
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (4 preceding siblings ...)
2026-11-09 1:17 ` [PATCH wireless-next v4 05/10] bcma: extract SPROM revision 11 Alessio Ferri
@ 2026-11-09 1:17 ` Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 07/10] firmware: bcm47xx_sprom: read ledbh4..ledbh15 " Alessio Ferri
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:17 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, Alessio Ferri, b43-dev
For sromrev=11, read from NVRAM the fields bcma_sprom_extract_r11()
fills, under the names Broadcom's driver uses: the scalars along with
the other revision-dependent entries, and the per-chain power info, rx
gains and power detector offsets, tssifloor5g and the antenna gain in
bcm47xx_fill_sprom_path_r11(). Read there the rx gain errors and noise
levels too, which rev 11 keeps per chain, with one list for the 5 GHz
sub-bands. A list is taken only if it parses whole.
Boards that keep the SPROM of their radios in NVRAM, such as Northstar
routers with BCM4360, get these fields through the SPROM fallback.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/firmware/broadcom/bcm47xx_sprom.c | 168 +++++++++++++++++++++++++++++-
1 file changed, 166 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/broadcom/bcm47xx_sprom.c b/drivers/firmware/broadcom/bcm47xx_sprom.c
index bca03fd85..956ce2099 100644
--- a/drivers/firmware/broadcom/bcm47xx_sprom.c
+++ b/drivers/firmware/broadcom/bcm47xx_sprom.c
@@ -94,6 +94,38 @@ NVRAM_READ_VAL(u32)
#undef NVRAM_READ_VAL
+/* A comma-separated list of n values, taken only if it parses whole. */
+#define NVRAM_READ_ARRAY(type) \
+static bool nvram_read_ ## type ## _array(const char *prefix, \
+ const char *postfix, \
+ const char *name, type val[], \
+ unsigned int n, bool fallback) \
+{ \
+ char buf[128], *p = buf, *tok; \
+ type var[12]; \
+ unsigned int i = 0; \
+ \
+ if (n > ARRAY_SIZE(var)) \
+ return false; \
+ if (get_nvram_var(prefix, postfix, name, buf, sizeof(buf), \
+ fallback) < 0) \
+ return false; \
+ while (i < n && (tok = strsep(&p, ",")) != NULL) { \
+ if (kstrto ## type(strim(tok), 0, &var[i])) \
+ return false; \
+ i++; \
+ } \
+ if (i != n) \
+ return false; \
+ memcpy(val, var, n * sizeof(val[0])); \
+ return true; \
+}
+
+NVRAM_READ_ARRAY(u8)
+NVRAM_READ_ARRAY(u16)
+
+#undef NVRAM_READ_ARRAY
+
static void nvram_read_u32_2(const char *prefix, const char *name,
u16 *val_lo, u16 *val_hi, bool fallback)
{
@@ -368,7 +400,50 @@ static void bcm47xx_sprom_fill_auto(struct ssb_sprom *sprom,
ENTRY(0x00000600, u16, pre, "legofdm40duppo", legofdm40duppo, 0, fb);
ENTRY(0x00000700, u8, pre, "pcieingress_war", pcieingress_war, 0, fb);
- /* TODO: rev 11 support */
+ ENTRY(0xfffff800, u8, pre, "subband5gver", subband5gver, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "tssiposslope2g", tssiposslope2g, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "epagain2g", epagain2g, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "pdgain2g", pdgain2g, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "tworangetssi2g", tworangetssi2g, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "papdcap2g", papdcap2g, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "femctrl", femctrl, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "tssiposslope5g", tssiposslope5g, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "epagain5g", epagain5g, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "pdgain5g", pdgain5g, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "tworangetssi5g", tworangetssi5g, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "papdcap5g", papdcap5g, 0, fb);
+ ENTRY(0xfffff800, u8, pre, "gainctrlsph", gainctrlsph, 0, fb);
+ ENTRY(0xfffff800, u16, pre, "tssifloor2g", tssifloor2g, 0, fb);
+ ENTRY(0xfffff800, u16, pre, "rpcal2g", rpcal2g, 0, fb);
+ ENTRY(0xfffff800, u16, pre, "rpcal5gb0", rpcal5gb[0], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "rpcal5gb1", rpcal5gb[1], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "rpcal5gb2", rpcal5gb[2], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "rpcal5gb3", rpcal5gb[3], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "dot11agofdmhrbw202gpo", dot11agofdmhrbw202gpo, 0, fb);
+ ENTRY(0xfffff800, u16, pre, "ofdmlrbw202gpo", ofdmlrbw202gpo, 0, fb);
+ ENTRY(0xfffff800, u32, pre, "mcsbw805glpo", mcsbw805glpo, 0, fb);
+ ENTRY(0xfffff800, u32, pre, "mcsbw805gmpo", mcsbw805gmpo, 0, fb);
+ ENTRY(0xfffff800, u32, pre, "mcsbw805ghpo", mcsbw805ghpo, 0, fb);
+ ENTRY(0xfffff800, u16, pre, "mcslr5glpo", mcslr5gpo[0], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "mcslr5gmpo", mcslr5gpo[1], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "mcslr5ghpo", mcslr5gpo[2], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb20in40hrpo", sb20in40hrpo, 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb20in40lrpo", sb20in40lrpo, 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb20in80and160hr5glpo", sb20in80and160hr5gpo[0], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb20in80and160hr5gmpo", sb20in80and160hr5gpo[1], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb20in80and160hr5ghpo", sb20in80and160hr5gpo[2], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb20in80and160lr5glpo", sb20in80and160lr5gpo[0], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb20in80and160lr5gmpo", sb20in80and160lr5gpo[1], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb20in80and160lr5ghpo", sb20in80and160lr5gpo[2], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb40and80hr5glpo", sb40and80hr5gpo[0], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb40and80hr5gmpo", sb40and80hr5gpo[1], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb40and80hr5ghpo", sb40and80hr5gpo[2], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb40and80lr5glpo", sb40and80lr5gpo[0], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb40and80lr5gmpo", sb40and80lr5gpo[1], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "sb40and80lr5ghpo", sb40and80lr5gpo[2], 0, fb);
+ ENTRY(0xfffff800, u16, pre, "dot11agduphrpo", dot11agduphrpo, 0, fb);
+ ENTRY(0xfffff800, u16, pre, "dot11agduplrpo", dot11agduplrpo, 0, fb);
+
ENTRY(0x00000700, u8, pre, "rxgainerr2ga0", rxgainerr2ga[0], 0, fb);
ENTRY(0x00000700, u8, pre, "rxgainerr2ga1", rxgainerr2ga[1], 0, fb);
ENTRY(0x00000700, u8, pre, "rxgainerr2ga2", rxgainerr2ga[2], 0, fb);
@@ -388,7 +463,6 @@ static void bcm47xx_sprom_fill_auto(struct ssb_sprom *sprom,
ENTRY(0xfffffe00, u8, pre, "sar2g", sar2g, 0, fb);
ENTRY(0xfffffe00, u8, pre, "sar5g", sar5g, 0, fb);
- /* TODO: rev 11 support */
ENTRY(0x00000700, u8, pre, "noiselvl2ga0", noiselvl2ga[0], 0, fb);
ENTRY(0x00000700, u8, pre, "noiselvl2ga1", noiselvl2ga[1], 0, fb);
ENTRY(0x00000700, u8, pre, "noiselvl2ga2", noiselvl2ga[2], 0, fb);
@@ -481,6 +555,93 @@ static void bcm47xx_fill_sprom_path_r45(struct ssb_sprom *sprom,
}
}
+static void bcm47xx_fill_sprom_path_r11(struct ssb_sprom *sprom,
+ const char *prefix, bool fallback)
+{
+ const struct {
+ const char *band;
+ struct ssb_sprom_rxgains *rx;
+ } rxgains[] = {
+ { "2g", &sprom->rxgains_2g },
+ /* rxgains5g* is the low 5 GHz sub-band */
+ { "5g", &sprom->rxgains_5gl },
+ { "5gm", &sprom->rxgains_5gm },
+ { "5gh", &sprom->rxgains_5gh },
+ };
+ char postfix[2], name[sizeof("rxgains5gmtrelnabypa")];
+ u8 agbg0 = 0, aga0 = 0, v5g[4];
+ int i, b;
+
+ for (i = 0; i < ARRAY_SIZE(sprom->pdoffset40ma); i++) {
+ struct ssb_sprom_core_pwr_info *pwr_info;
+
+ pwr_info = &sprom->core_pwr_info[i];
+
+ snprintf(postfix, sizeof(postfix), "%i", i);
+ nvram_read_u8(prefix, postfix, "maxp2ga",
+ &pwr_info->maxp2ga, 0, fallback);
+ nvram_read_u16_array(prefix, postfix, "pa2ga", pwr_info->pa2ga,
+ ARRAY_SIZE(pwr_info->pa2ga), fallback);
+ nvram_read_u8_array(prefix, postfix, "maxp5ga",
+ pwr_info->maxp5ga,
+ ARRAY_SIZE(pwr_info->maxp5ga), fallback);
+ nvram_read_u16_array(prefix, postfix, "pa5ga", pwr_info->pa5ga,
+ ARRAY_SIZE(pwr_info->pa5ga), fallback);
+ nvram_read_u16(prefix, postfix, "pdoffset40ma",
+ &sprom->pdoffset40ma[i], 0, fallback);
+ nvram_read_u16(prefix, postfix, "pdoffset80ma",
+ &sprom->pdoffset80ma[i], 0, fallback);
+ nvram_read_u8(prefix, postfix, "pdoffset2g40ma",
+ &sprom->pdoffset2g40ma[i], 0, fallback);
+
+ nvram_read_u8(prefix, postfix, "rxgainerr2ga",
+ &sprom->rxgainerr2ga[i], 0, fallback);
+ if (nvram_read_u8_array(prefix, postfix, "rxgainerr5ga", v5g,
+ ARRAY_SIZE(v5g), fallback)) {
+ sprom->rxgainerr5gla[i] = v5g[0];
+ sprom->rxgainerr5gma[i] = v5g[1];
+ sprom->rxgainerr5gha[i] = v5g[2];
+ sprom->rxgainerr5gua[i] = v5g[3];
+ }
+ nvram_read_u8(prefix, postfix, "noiselvl2ga",
+ &sprom->noiselvl2ga[i], 0, fallback);
+ if (nvram_read_u8_array(prefix, postfix, "noiselvl5ga", v5g,
+ ARRAY_SIZE(v5g), fallback)) {
+ sprom->noiselvl5gla[i] = v5g[0];
+ sprom->noiselvl5gma[i] = v5g[1];
+ sprom->noiselvl5gha[i] = v5g[2];
+ sprom->noiselvl5gua[i] = v5g[3];
+ }
+
+ for (b = 0; b < ARRAY_SIZE(rxgains); b++) {
+ struct ssb_sprom_rxgains *rx = rxgains[b].rx;
+
+ snprintf(name, sizeof(name), "rxgains%selnagaina",
+ rxgains[b].band);
+ nvram_read_u8(prefix, postfix, name, &rx->elnagain[i],
+ 0, fallback);
+ snprintf(name, sizeof(name), "rxgains%strisoa",
+ rxgains[b].band);
+ nvram_read_u8(prefix, postfix, name, &rx->triso[i],
+ 0, fallback);
+ snprintf(name, sizeof(name), "rxgains%strelnabypa",
+ rxgains[b].band);
+ nvram_read_u8(prefix, postfix, name, &rx->trelnabyp[i],
+ 0, fallback);
+ }
+ }
+
+ nvram_read_u16_array(prefix, NULL, "tssifloor5g", sprom->tssifloor5g,
+ ARRAY_SIZE(sprom->tssifloor5g), fallback);
+
+ nvram_read_u8(prefix, NULL, "pdoffset2g40mvalid",
+ &sprom->pdoffset2g40mvalid, 0, fallback);
+ nvram_read_u8(prefix, NULL, "agbg0", &agbg0, 0, fallback);
+ nvram_read_u8(prefix, NULL, "aga0", &aga0, 0, fallback);
+ sprom->antenna_gain_qdb[0] = ssb_sprom_antgain_qdb(agbg0);
+ sprom->antenna_gain_qdb[1] = ssb_sprom_antgain_qdb(aga0);
+}
+
static bool bcm47xx_is_valid_mac(u8 *mac)
{
return mac && !(mac[0] == 0x00 && mac[1] == 0x90 && mac[2] == 0x4c);
@@ -586,6 +747,9 @@ void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix,
case 9:
bcm47xx_fill_sprom_path_r4589(sprom, prefix, fallback);
break;
+ case 11:
+ bcm47xx_fill_sprom_path_r11(sprom, prefix, fallback);
+ break;
}
bcm47xx_sprom_fill_auto(sprom, prefix, fallback);
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH wireless-next v4 07/10] firmware: bcm47xx_sprom: read ledbh4..ledbh15 from NVRAM
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (5 preceding siblings ...)
2026-11-09 1:17 ` [PATCH wireless-next v4 06/10] firmware: bcm47xx_sprom: read the SPROM revision 11 fields from NVRAM Alessio Ferri
@ 2026-11-09 1:17 ` Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 08/10] firmware: bcm47xx_sprom: read boardflags3 from NVRAM over the SPROM Alessio Ferri
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:17 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, Alessio Ferri, b43-dev
Add gpio_ext[] for the LED behaviours of GPIO 4-15 and fill it from
NVRAM ledbh4..ledbh15.
bcm47xx_fill_sprom() is reached only through the SPROM fallback, so the
NVRAM of a bcma device with a SPROM of its own is never read. Add
bcm47xx_sprom_fill_bcma_nvram(), which bcma calls after extracting the
SPROM, to read the variables the SPROM has no word for on top of it.
The bcma prefix lookup moves into a helper shared by both paths.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/bcma/sprom.c | 3 ++
drivers/firmware/broadcom/bcm47xx_sprom.c | 86 +++++++++++++++++++++++--------
include/linux/bcm47xx_sprom.h | 15 ++++++
include/linux/ssb/ssb.h | 1 +
4 files changed, 83 insertions(+), 22 deletions(-)
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index 339111519..0d562d5d4 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -11,6 +11,7 @@
#include <linux/bcma/bcma.h>
#include <linux/bcma/bcma_regs.h>
+#include <linux/bcm47xx_sprom.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
@@ -847,6 +848,8 @@ int bcma_sprom_get(struct bcma_bus *bus)
else
bcma_sprom_extract_r8(bus, sprom);
kfree(sprom);
+ /* Most hosts have no NVRAM: ignore the result. */
+ bcm47xx_sprom_fill_bcma_nvram(bus, &bus->sprom);
}
return err;
diff --git a/drivers/firmware/broadcom/bcm47xx_sprom.c b/drivers/firmware/broadcom/bcm47xx_sprom.c
index 956ce2099..e2c4e3307 100644
--- a/drivers/firmware/broadcom/bcm47xx_sprom.c
+++ b/drivers/firmware/broadcom/bcm47xx_sprom.c
@@ -29,6 +29,7 @@
#include <linux/bcm47xx_nvram.h>
#include <linux/bcm47xx_sprom.h>
#include <linux/bcma/bcma.h>
+#include <linux/err.h>
#include <linux/etherdevice.h>
#include <linux/hex.h>
#include <linux/if_ether.h>
@@ -204,6 +205,20 @@ static void nvram_read_alpha2(const char *prefix, const char *name,
memcpy(val, buf, 2);
}
+/* Variables read from NVRAM whether or not the SPROM itself comes from it. */
+static void bcm47xx_sprom_fill_nvram_only(struct ssb_sprom *sprom,
+ const char *prefix, bool fallback)
+{
+ char name[sizeof("ledbh15")];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sprom->gpio_ext); i++) {
+ snprintf(name, sizeof(name), "ledbh%d", i + 4);
+ nvram_read_u8(prefix, NULL, name, &sprom->gpio_ext[i], 0xff,
+ fallback);
+ }
+}
+
/* This is one-function-only macro, it uses local "sprom" variable! */
#define ENTRY(_revmask, _type, _prefix, _name, _val, _allset, _fallback) \
if (_revmask & BIT(sprom->revision)) \
@@ -241,6 +256,8 @@ static void bcm47xx_sprom_fill_auto(struct ssb_sprom *sprom,
ENTRY(0xfffffffe, u8, pre, "ledbh1", gpio1, 0xff, fb);
ENTRY(0xfffffffe, u8, pre, "ledbh2", gpio2, 0xff, fb);
ENTRY(0xfffffffe, u8, pre, "ledbh3", gpio3, 0xff, fb);
+ if (sprom->revision)
+ bcm47xx_sprom_fill_nvram_only(sprom, pre, fb);
ENTRY(0x0000070e, u16, pre, "pa0b0", pa0b0, 0, fb);
ENTRY(0x0000070e, u16, pre, "pa0b1", pa0b1, 0, fb);
@@ -811,45 +828,70 @@ static void bcm47xx_sprom_apply_prefix_alias(char *prefix, size_t prefix_size)
}
}
-static int bcm47xx_get_sprom_bcma(struct bcma_bus *bus, struct ssb_sprom *out)
+/*
+ * The NVRAM prefix of a bcma bus's variables: pci/<bus>/<slot>/ or its
+ * devpathN alias for a PCI device, sb/<core>/ for the SoC's own core.
+ * Returns the prefix, NULL for a SoC bus without an 802.11 core, or an
+ * error pointer for a host type NVRAM knows nothing about. *fallback is
+ * set when the caller may fall back to unprefixed variables.
+ */
+static char *bcm47xx_sprom_bcma_prefix(struct bcma_bus *bus, char *buf,
+ size_t size, bool *fallback)
{
- struct bcma_boardinfo *binfo = &bus->boardinfo;
struct bcma_device *core;
- char buf[10];
- char *prefix;
- bool fallback = false;
+ *fallback = false;
switch (bus->hosttype) {
case BCMA_HOSTTYPE_PCI:
- memset(out, 0, sizeof(struct ssb_sprom));
/* On BCM47XX all PCI buses share the same domain */
if (IS_ENABLED(CONFIG_BCM47XX))
- snprintf(buf, sizeof(buf), "pci/%u/%u/",
+ snprintf(buf, size, "pci/%u/%u/",
bus->host_pci->bus->number + 1,
PCI_SLOT(bus->host_pci->devfn));
else
- snprintf(buf, sizeof(buf), "pci/%u/%u/",
+ snprintf(buf, size, "pci/%u/%u/",
pci_domain_nr(bus->host_pci->bus) + 1,
bus->host_pci->bus->number);
- bcm47xx_sprom_apply_prefix_alias(buf, sizeof(buf));
- prefix = buf;
- break;
+ bcm47xx_sprom_apply_prefix_alias(buf, size);
+ return buf;
case BCMA_HOSTTYPE_SOC:
- memset(out, 0, sizeof(struct ssb_sprom));
core = bcma_find_core(bus, BCMA_CORE_80211);
- if (core) {
- snprintf(buf, sizeof(buf), "sb/%u/",
- core->core_index);
- prefix = buf;
- fallback = true;
- } else {
- prefix = NULL;
- }
- break;
+ if (!core)
+ return NULL;
+ snprintf(buf, size, "sb/%u/", core->core_index);
+ *fallback = true;
+ return buf;
default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+int bcm47xx_sprom_fill_bcma_nvram(struct bcma_bus *bus, struct ssb_sprom *out)
+{
+ char buf[10];
+ char *prefix;
+ bool fallback;
+
+ prefix = bcm47xx_sprom_bcma_prefix(bus, buf, sizeof(buf), &fallback);
+ if (IS_ERR(prefix))
+ return PTR_ERR(prefix);
+ bcm47xx_sprom_fill_nvram_only(out, prefix, fallback);
+ return 0;
+}
+
+static int bcm47xx_get_sprom_bcma(struct bcma_bus *bus, struct ssb_sprom *out)
+{
+ struct bcma_boardinfo *binfo = &bus->boardinfo;
+ char buf[10];
+ char *prefix;
+ bool fallback;
+
+ prefix = bcm47xx_sprom_bcma_prefix(bus, buf, sizeof(buf), &fallback);
+ if (IS_ERR(prefix)) {
pr_warn("Unable to fill SPROM for given bustype.\n");
- return -EINVAL;
+ return PTR_ERR(prefix);
}
+ memset(out, 0, sizeof(struct ssb_sprom));
nvram_read_u16(prefix, NULL, "boardvendor", &binfo->vendor, 0, true);
if (!binfo->vendor)
diff --git a/include/linux/bcm47xx_sprom.h b/include/linux/bcm47xx_sprom.h
index 40a7da3ef..47e9922ec 100644
--- a/include/linux/bcm47xx_sprom.h
+++ b/include/linux/bcm47xx_sprom.h
@@ -9,6 +9,7 @@
#include <linux/types.h>
#include <linux/vmalloc.h>
+struct bcma_bus;
struct ssb_sprom;
#ifdef CONFIG_BCM47XX_SPROM
@@ -28,4 +29,18 @@ static inline int bcm47xx_sprom_register_fallbacks(void)
};
#endif
+/*
+ * Fill the NVRAM-only variables of a bcma bus whose SPROM was read from the
+ * device. Like the SPROM fallback, only with bcma built in.
+ */
+#if defined(CONFIG_BCM47XX_SPROM) && IS_BUILTIN(CONFIG_BCMA)
+int bcm47xx_sprom_fill_bcma_nvram(struct bcma_bus *bus, struct ssb_sprom *out);
+#else
+static inline int bcm47xx_sprom_fill_bcma_nvram(struct bcma_bus *bus,
+ struct ssb_sprom *out)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
#endif /* __BCM47XX_SPROM_H */
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index a89bdbf56..91de70aa3 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -76,6 +76,7 @@ struct ssb_sprom {
u8 gpio1; /* GPIO pin 1 */
u8 gpio2; /* GPIO pin 2 */
u8 gpio3; /* GPIO pin 3 */
+ u8 gpio_ext[12]; /* GPIO pins 4-15, NVRAM ledbh4-15 only */
u8 maxpwr_bg; /* 2.4GHz Amplifier Max Power (in dBm Q5.2) */
u8 maxpwr_al; /* 5.2GHz Amplifier Max Power (in dBm Q5.2) */
u8 maxpwr_a; /* 5.3GHz Amplifier Max Power (in dBm Q5.2) */
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH wireless-next v4 08/10] firmware: bcm47xx_sprom: read boardflags3 from NVRAM over the SPROM
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (6 preceding siblings ...)
2026-11-09 1:17 ` [PATCH wireless-next v4 07/10] firmware: bcm47xx_sprom: read ledbh4..ledbh15 " Alessio Ferri
@ 2026-11-09 1:17 ` Alessio Ferri
2026-11-09 1:25 ` [PATCH wireless-next v4 09/10] bcma: add the single-band BCM4352/BCM4360 PCI IDs Alessio Ferri
2026-11-09 1:26 ` [PATCH wireless-next v4 10/10] wifi: b43: add the 2.4 GHz BCM4352/BCM4360 to the supported bands Alessio Ferri
9 siblings, 0 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:17 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, Alessio Ferri, b43-dev
Read boardflags3 along with ledbh4..ledbh15, so that a value in NVRAM
replaces the word of a SPROM read from the device.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/firmware/broadcom/bcm47xx_sprom.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/firmware/broadcom/bcm47xx_sprom.c b/drivers/firmware/broadcom/bcm47xx_sprom.c
index e2c4e3307..7425526c4 100644
--- a/drivers/firmware/broadcom/bcm47xx_sprom.c
+++ b/drivers/firmware/broadcom/bcm47xx_sprom.c
@@ -217,6 +217,9 @@ static void bcm47xx_sprom_fill_nvram_only(struct ssb_sprom *sprom,
nvram_read_u8(prefix, NULL, name, &sprom->gpio_ext[i], 0xff,
fallback);
}
+
+ nvram_read_u32(prefix, NULL, "boardflags3", &sprom->boardflags3, 0,
+ fallback);
}
/* This is one-function-only macro, it uses local "sprom" variable! */
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH wireless-next v4 09/10] bcma: add the single-band BCM4352/BCM4360 PCI IDs
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (7 preceding siblings ...)
2026-11-09 1:17 ` [PATCH wireless-next v4 08/10] firmware: bcm47xx_sprom: read boardflags3 from NVRAM over the SPROM Alessio Ferri
@ 2026-11-09 1:25 ` Alessio Ferri
2026-11-09 1:26 ` [PATCH wireless-next v4 10/10] wifi: b43: add the 2.4 GHz BCM4352/BCM4360 to the supported bands Alessio Ferri
9 siblings, 0 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:25 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, b43-dev
Bind bcma-pci-bridge to the 2.4 GHz and 5 GHz variants of the BCM4360
(0x43a1, 0x43a2) and BCM4352 (0x43b2, 0x43b3), next to the dual-band
0x43a0 and 0x43b1, and name the chip and band of each.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/bcma/host_pci.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 3dc298506..27f6cc10a 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -294,10 +294,14 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = {
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_DELL, 0x0018) },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_FOXCONN, 0xe092) },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_HP, 0x804a) },
- { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a0) }, /* BCM4360 */
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a1) }, /* BCM4360, 2.4 GHz */
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a2) }, /* BCM4360, 5 GHz */
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
- { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43b1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43b1) }, /* BCM4352 */
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43b2) }, /* BCM4352, 2.4 GHz */
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43b3) }, /* BCM4352, 5 GHz */
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) }, /* 0xa8db, BCM43217 (sic!) */
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43228) }, /* 0xa8dc */
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH wireless-next v4 10/10] wifi: b43: add the 2.4 GHz BCM4352/BCM4360 to the supported bands
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (8 preceding siblings ...)
2026-11-09 1:25 ` [PATCH wireless-next v4 09/10] bcma: add the single-band BCM4352/BCM4360 PCI IDs Alessio Ferri
@ 2026-11-09 1:26 ` Alessio Ferri
9 siblings, 0 replies; 11+ messages in thread
From: Alessio Ferri @ 2026-11-09 1:26 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch
Cc: vit9696, ville+git, egc112, linux-wireless, linux-kernel,
linux-mips, b43-dev
Report the 2.4 GHz BCM4360 (0x43a1) and BCM4352 (0x43b2) as 2.4 GHz
only devices, as their 5 GHz variants are reported as 5 GHz only.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/net/wireless/broadcom/b43/main.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
index 3825b6ad9..350b47b4b 100644
--- a/drivers/net/wireless/broadcom/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
@@ -5366,6 +5366,12 @@ static void b43_supported_bands(struct b43_wldev *dev, bool *have_2ghz_phy,
*have_2ghz_phy = false;
*have_5ghz_phy = true;
return;
+ case 0x43a1: /* BCM4360 */
+ case 0x43b2: /* BCM4352 */
+ /* 2.4 GHz only devices */
+ *have_2ghz_phy = true;
+ *have_5ghz_phy = false;
+ return;
}
/* As a fallback, try to guess using PHY type */
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-11-09 1:26 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-09 1:17 [PATCH wireless-next v4 00/10] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 01/10] bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 02/10] bcma: pmu: init PLL and resources on BCM4352/BCM4360 Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 03/10] bcma: pcie2: request HT " Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 04/10] bcma: chipcommon: enable asynchronous GPIO interrupts Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 05/10] bcma: extract SPROM revision 11 Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 06/10] firmware: bcm47xx_sprom: read the SPROM revision 11 fields from NVRAM Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 07/10] firmware: bcm47xx_sprom: read ledbh4..ledbh15 " Alessio Ferri
2026-11-09 1:17 ` [PATCH wireless-next v4 08/10] firmware: bcm47xx_sprom: read boardflags3 from NVRAM over the SPROM Alessio Ferri
2026-11-09 1:25 ` [PATCH wireless-next v4 09/10] bcma: add the single-band BCM4352/BCM4360 PCI IDs Alessio Ferri
2026-11-09 1:26 ` [PATCH wireless-next v4 10/10] wifi: b43: add the 2.4 GHz BCM4352/BCM4360 to the supported bands Alessio Ferri
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®