* [PATCH wireless-next v2 0/8] bcma-ac-rev1: support 4352/4360 rev3 chips
@ 2026-10-26 23:53 Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 1/8] bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC Alessio Ferri
` (7 more replies)
0 siblings, 8 replies; 15+ messages in thread
From: Alessio Ferri @ 2026-10-26 23:53 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch, vit9696
Cc: linux-wireless, linux-kernel, linux-mips, Alessio Ferri
Preparatory work in the bring up of AC family rev1 chips, in particular,
the bcm4352 rev3 and bcm4360 rev3. I don't own nor I can find any mmio
captures for other revs, so everything is gated on rev3 of the two chips
except for the CORECTL_ASYNCGPIO taht is gated on cc rev >= 21.
Initialize the PMU, the PCIe2 core and chipcommon of the BCM4352/BCM4360
family on chip rev 3, parse SPROM revision 11, read the NVRAM variables
the family's boards set on top of the SPROM, and bind bcma-pci-bridge to
the BCM4352.
Signed-off-by: Alessio Ferri <alessio.ferri@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>
Cc: linux-wireless@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@vger.kernel.org
---
Alessio Ferri (8):
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 ledbh4..ledbh15 from NVRAM
firmware: bcm47xx_sprom: read boardflags3 and AvVmid_c0..2 from NVRAM
bcma: add the BCM4352 PCI ID
drivers/bcma/core.c | 2 +-
drivers/bcma/driver_chipcommon.c | 3 +
drivers/bcma/driver_chipcommon_pmu.c | 41 ++++++
drivers/bcma/driver_pcie2.c | 3 +
drivers/bcma/host_pci.c | 1 +
drivers/bcma/sprom.c | 204 +++++++++++++++++++++++++++-
drivers/firmware/broadcom/bcm47xx_sprom.c | 118 +++++++++++++---
include/linux/bcm47xx_sprom.h | 15 ++
include/linux/bcma/bcma_driver_chipcommon.h | 5 +
include/linux/ssb/ssb.h | 62 +++++++++
include/linux/ssb/ssb_regs.h | 94 +++++++++++++
11 files changed, 524 insertions(+), 24 deletions(-)
---
base-commit: 0fcb05b72344f40806cbe810de8872f1698bf053
change-id: 20261014-bcma-ac-rev1-da894142790f
Best regards,
--
Alessio Ferri <alessio.ferri@mythread.it>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH wireless-next v2 1/8] bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC
2026-10-26 23:53 [PATCH wireless-next v2 0/8] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
@ 2026-10-26 23:53 ` Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 2/8] bcma: pmu: init PLL and resources on BCM4352/BCM4360 Alessio Ferri
` (6 subsequent siblings)
7 siblings, 0 replies; 15+ messages in thread
From: Alessio Ferri @ 2026-10-26 23:53 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch, vit9696
Cc: linux-wireless, linux-kernel, linux-mips, Alessio Ferri
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 related [flat|nested] 15+ messages in thread
* [PATCH wireless-next v2 2/8] bcma: pmu: init PLL and resources on BCM4352/BCM4360
2026-10-26 23:53 [PATCH wireless-next v2 0/8] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 1/8] bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC Alessio Ferri
@ 2026-10-26 23:53 ` Alessio Ferri
2026-10-28 0:44 ` vit9696
2026-10-26 23:53 ` [PATCH wireless-next v2 3/8] bcma: pcie2: request HT " Alessio Ferri
` (5 subsequent siblings)
7 siblings, 1 reply; 15+ messages in thread
From: Alessio Ferri @ 2026-10-26 23:53 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch, vit9696
Cc: linux-wireless, linux-kernel, linux-mips, Alessio Ferri
On chip rev 3:
- program PLLCTL2/3 for a 960.0098 MHz fractional VCO from the 40 MHz
crystal, except on a BCM4360 strapped for USB;
- set the up/down time of resource 6;
- with chipstatus RSRC_INIT_MODE0 clear, program PLLCTL6/14 and set
max_res_mask to 0x1ff.
The values are those of Broadcom's driver on rev 3; other revisions are
left as they are.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/bcma/driver_chipcommon_pmu.c | 41 +++++++++++++++++++++++++++++
include/linux/bcma/bcma_driver_chipcommon.h | 4 +++
2 files changed, 45 insertions(+)
diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
index 263ef6fa1..ab2b2701d 100644
--- a/drivers/bcma/driver_chipcommon_pmu.c
+++ b/drivers/bcma/driver_chipcommon_pmu.c
@@ -145,6 +145,39 @@ 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;
+
+ if (bus->chipinfo.rev != 3)
+ return;
+ if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4360 &&
+ cc->status & BCMA_CC_CHIPST_4360_MODE_USB)
+ return;
+
+ /* Fractional VCO, 960.0098 MHz from a 40 MHz crystal. */
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x0c31);
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, 0x100e);
+ bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
+}
+
+/* Returns the max resource mask to program, 0 to leave it alone. */
+static u32 bcma_pmu_4360_resources_init(struct bcma_drv_cc *cc)
+{
+ if (cc->core->bus->chipinfo.rev != 3)
+ return 0;
+
+ bcma_pmu_write32(cc, BCMA_CC_PMU_RES_TABSEL, 6);
+ bcma_pmu_write32(cc, BCMA_CC_PMU_RES_UPDNTM, 0x00200001);
+
+ if (cc->status & BCMA_CC_CHIPST_4360_RSRC_INIT_MODE0)
+ return 0;
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL6, 0x09048562);
+ bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL14, 0x09048562);
+ bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
+ return 0x1ff;
+}
+
static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
{
struct bcma_bus *bus = cc->core->bus;
@@ -156,6 +189,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 +225,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:
+ max_msk = bcma_pmu_4360_resources_init(cc);
+ 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..45c37b6bd 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -107,6 +107,8 @@
#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_CHIPST_4360_MODE_USB 0x00000100
#define BCMA_CC_JCMD 0x0030 /* Rev >= 10 only */
#define BCMA_CC_JCMD_START 0x80000000
#define BCMA_CC_JCMD_BUSY 0x80000000
@@ -500,6 +502,8 @@
#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_CTL14 14
#define BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000
#define BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT 20
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH wireless-next v2 3/8] bcma: pcie2: request HT on BCM4352/BCM4360
2026-10-26 23:53 [PATCH wireless-next v2 0/8] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 1/8] bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 2/8] bcma: pmu: init PLL and resources on BCM4352/BCM4360 Alessio Ferri
@ 2026-10-26 23:53 ` Alessio Ferri
2026-10-28 11:40 ` vit9696
2026-10-26 23:53 ` [PATCH wireless-next v2 4/8] bcma: chipcommon: enable asynchronous GPIO interrupts Alessio Ferri
` (4 subsequent siblings)
7 siblings, 1 reply; 15+ messages in thread
From: Alessio Ferri @ 2026-10-26 23:53 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch, vit9696
Cc: linux-wireless, linux-kernel, linux-mips, Alessio Ferri
Set HAVEHTREQ in the PCIe2 core's clock control on chip rev 3, 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..fd8fab982 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 == 3)
+ pcie2_set32(pcie2, BCMA_CLKCTLST,
+ BCMA_CLKCTLST_HAVEHTREQ);
break;
default:
pcie2->reqsize = 128;
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH wireless-next v2 4/8] bcma: chipcommon: enable asynchronous GPIO interrupts
2026-10-26 23:53 [PATCH wireless-next v2 0/8] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (2 preceding siblings ...)
2026-10-26 23:53 ` [PATCH wireless-next v2 3/8] bcma: pcie2: request HT " Alessio Ferri
@ 2026-10-26 23:53 ` Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 5/8] bcma: extract SPROM revision 11 Alessio Ferri
` (3 subsequent siblings)
7 siblings, 0 replies; 15+ messages in thread
From: Alessio Ferri @ 2026-10-26 23:53 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch, vit9696
Cc: linux-wireless, linux-kernel, linux-mips, Alessio Ferri
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 45c37b6bd..b761801d6 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 related [flat|nested] 15+ messages in thread
* [PATCH wireless-next v2 5/8] bcma: extract SPROM revision 11
2026-10-26 23:53 [PATCH wireless-next v2 0/8] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (3 preceding siblings ...)
2026-10-26 23:53 ` [PATCH wireless-next v2 4/8] bcma: chipcommon: enable asynchronous GPIO interrupts Alessio Ferri
@ 2026-10-26 23:53 ` Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 6/8] firmware: bcm47xx_sprom: read ledbh4..ledbh15 from NVRAM Alessio Ferri
` (2 subsequent siblings)
7 siblings, 0 replies; 15+ messages in thread
From: Alessio Ferri @ 2026-10-26 23:53 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch, vit9696
Cc: linux-wireless, linux-kernel, linux-mips, Alessio Ferri
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 | 201 ++++++++++++++++++++++++++++++++++++++++++-
include/linux/ssb/ssb.h | 58 +++++++++++++
include/linux/ssb/ssb_regs.h | 94 ++++++++++++++++++++
3 files changed, 352 insertions(+), 1 deletion(-)
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index e668ad796..c98b6fb66 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -474,6 +474,202 @@ 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);
+
+ 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);
+
+ /* Whole dB in bits [5:0], sign-extended, quarter dB in bits [7:6]. */
+ for (i = 0; i < 2; i++) {
+ u8 raw = i ? (sprom[SSB_SPROM11_AGBG2A0 / 2] >> 8) & 0xff
+ : sprom[SSB_SPROM11_AGBG10 / 2] & 0xff;
+ s8 gain = (s8)((raw & 0x3f) << 2) >> 2;
+
+ bus->sprom.antenna_gain_qdb[i] = (s8)(4 * gain + (raw >> 6));
+ }
+}
+
/*
* Indicates the presence of external SPROM.
*/
@@ -640,7 +836,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..0548af664 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,6 +205,51 @@ 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];
+ /* 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];
};
/* Information about the PCB the circuitry is soldered on. */
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
index 210f46494..12fa55e91 100644
--- a/include/linux/ssb/ssb_regs.h
+++ b/include/linux/ssb/ssb_regs.h
@@ -606,6 +606,100 @@
#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_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 related [flat|nested] 15+ messages in thread
* [PATCH wireless-next v2 6/8] firmware: bcm47xx_sprom: read ledbh4..ledbh15 from NVRAM
2026-10-26 23:53 [PATCH wireless-next v2 0/8] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (4 preceding siblings ...)
2026-10-26 23:53 ` [PATCH wireless-next v2 5/8] bcma: extract SPROM revision 11 Alessio Ferri
@ 2026-10-26 23:53 ` Alessio Ferri
2026-10-28 12:43 ` vit9696
2026-10-26 23:53 ` [PATCH wireless-next v2 7/8] firmware: bcm47xx_sprom: read boardflags3 and AvVmid_c0..2 " Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 8/8] bcma: add the BCM4352 PCI ID Alessio Ferri
7 siblings, 1 reply; 15+ messages in thread
From: Alessio Ferri @ 2026-10-26 23:53 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch, vit9696
Cc: linux-wireless, linux-kernel, linux-mips, Alessio Ferri
Add gpio_ext[] for the LED behaviours of GPIO 4-15 and fill it from
NVRAM ledbh4..ledbh15, both for a SPROM built from NVRAM and, through
the new bcm47xx_sprom_fill_bcma_nvram(), on top of a SPROM read from a
bcma device. 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 c98b6fb66..ac7622512 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>
@@ -841,6 +842,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 bca03fd85..75604cd70 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>
@@ -172,6 +173,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)) \
@@ -209,6 +224,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);
@@ -647,45 +664,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 0548af664..02e6c9054 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 related [flat|nested] 15+ messages in thread
* [PATCH wireless-next v2 7/8] firmware: bcm47xx_sprom: read boardflags3 and AvVmid_c0..2 from NVRAM
2026-10-26 23:53 [PATCH wireless-next v2 0/8] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (5 preceding siblings ...)
2026-10-26 23:53 ` [PATCH wireless-next v2 6/8] firmware: bcm47xx_sprom: read ledbh4..ledbh15 from NVRAM Alessio Ferri
@ 2026-10-26 23:53 ` Alessio Ferri
2026-10-28 13:27 ` vit9696
2026-10-26 23:53 ` [PATCH wireless-next v2 8/8] bcma: add the BCM4352 PCI ID Alessio Ferri
7 siblings, 1 reply; 15+ messages in thread
From: Alessio Ferri @ 2026-10-26 23:53 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch, vit9696
Cc: linux-wireless, linux-kernel, linux-mips, Alessio Ferri
Read them along with ledbh4..ledbh15. AvVmid_cN holds the Av/Vmid pairs
of chain N's power detector per band; avvmid_valid marks the chains
whose list parsed. A boardflags3 in NVRAM overrides the SPROM word.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/firmware/broadcom/bcm47xx_sprom.c | 32 +++++++++++++++++++++++++++++++
include/linux/ssb/ssb.h | 3 +++
2 files changed, 35 insertions(+)
diff --git a/drivers/firmware/broadcom/bcm47xx_sprom.c b/drivers/firmware/broadcom/bcm47xx_sprom.c
index 75604cd70..581dd867d 100644
--- a/drivers/firmware/broadcom/bcm47xx_sprom.c
+++ b/drivers/firmware/broadcom/bcm47xx_sprom.c
@@ -173,6 +173,32 @@ static void nvram_read_alpha2(const char *prefix, const char *name,
memcpy(val, buf, 2);
}
+/*
+ * AvVmid_c<core>: Av,Vmid of the chain's power detector for 2g, 5gl, 5gml,
+ * 5gmu and 5gh. The chain is marked valid only if all ten values parse.
+ */
+static void nvram_read_avvmid(const char *prefix, int core,
+ struct ssb_sprom *sprom, bool fallback)
+{
+ char name[sizeof("AvVmid_c0")];
+ char buf[64], *p = buf, *tok;
+ u8 v[10];
+ int n = 0;
+
+ snprintf(name, sizeof(name), "AvVmid_c%d", core);
+ if (get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback) < 0)
+ return;
+ while (n < ARRAY_SIZE(v) && (tok = strsep(&p, ",")) != NULL) {
+ if (kstrtou8(strim(tok), 0, &v[n]))
+ return;
+ n++;
+ }
+ if (n != ARRAY_SIZE(v))
+ return;
+ memcpy(sprom->avvmid[core], v, sizeof(v));
+ sprom->avvmid_valid |= BIT(core);
+}
+
/* 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)
@@ -185,6 +211,12 @@ 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);
+ sprom->avvmid_valid = 0;
+ for (i = 0; i < ARRAY_SIZE(sprom->avvmid); i++)
+ nvram_read_avvmid(prefix, i, sprom, fallback);
}
/* This is one-function-only macro, it uses local "sprom" variable! */
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index 02e6c9054..5a4daa76d 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -77,6 +77,9 @@ struct ssb_sprom {
u8 gpio2; /* GPIO pin 2 */
u8 gpio3; /* GPIO pin 3 */
u8 gpio_ext[12]; /* GPIO pins 4-15, NVRAM ledbh4-15 only */
+ /* NVRAM AvVmid_c0..2: {Av, Vmid} for 2g, 5gl, 5gml, 5gmu, 5gh */
+ u8 avvmid[3][5][2];
+ u8 avvmid_valid; /* bit per chain whose AvVmid_cN parsed */
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 related [flat|nested] 15+ messages in thread
* [PATCH wireless-next v2 8/8] bcma: add the BCM4352 PCI ID
2026-10-26 23:53 [PATCH wireless-next v2 0/8] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
` (6 preceding siblings ...)
2026-10-26 23:53 ` [PATCH wireless-next v2 7/8] firmware: bcm47xx_sprom: read boardflags3 and AvVmid_c0..2 " Alessio Ferri
@ 2026-10-26 23:53 ` Alessio Ferri
7 siblings, 0 replies; 15+ messages in thread
From: Alessio Ferri @ 2026-10-26 23:53 UTC (permalink / raw)
To: Rafał Miłecki, Michael Buesch, vit9696
Cc: linux-wireless, linux-kernel, linux-mips, Alessio Ferri
Bind bcma-pci-bridge to the BCM4352, 0x43b3.
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
drivers/bcma/host_pci.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 3dc298506..f6cf722a3 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -298,6 +298,7 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = {
{ 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, 0x43b3) },
{ 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 related [flat|nested] 15+ messages in thread
* Re: [PATCH wireless-next v2 2/8] bcma: pmu: init PLL and resources on BCM4352/BCM4360
2026-10-26 23:53 ` [PATCH wireless-next v2 2/8] bcma: pmu: init PLL and resources on BCM4352/BCM4360 Alessio Ferri
@ 2026-10-28 0:44 ` vit9696
2026-10-28 9:14 ` Alessio Ferri
0 siblings, 1 reply; 15+ messages in thread
From: vit9696 @ 2026-10-28 0:44 UTC (permalink / raw)
To: Alessio Ferri
Cc: Rafał Miłecki, Michael Buesch, linux-wireless,
linux-kernel, linux-mips
[-- Attachment #1: Type: text/plain, Size: 5920 bytes --]
Hello,
XTAL frequency on BCM4360 and BCM4352 apparently does not only depend on the revision number. It is determined by bit #0 presence in chipstatus. If bit #0 is set, then it is 40 MHz, otherwise it is 20 MHz. This logic is actually already present in the bcma driver[1], and looks correct to me. FVCO frequency being 960 MHz for all BCM4360 and BCM4352 chips is also more or less known from the public open-source part of the Broadcom driver[2].
The nitpick here is that whereas PLL seems indeed to be only programmed on revision 3 and newer[3] (with the code there assuming 40 MHz XTAL frequency, probably because Broadcom migrated to 40 MHz at a certain point for all newer chips), but there is a special case for HT clk_ctl_st bit[4], i.e. backplane is running on HT clock.
With this in mind I believe the patch:
- could handle revision 3 or newer, not just 3 in CTL2/3 logic;
- could bug on BCMA_CC_CHIPST_4360_XTAL_40MZ not being set in CTL2/3 logic;
- could check for CCS_HTAVAIL bit not being set in CTL2/3 logic;
- perhaps could calculate resulting CTL2/3 logic values in a slightly cleaner way, after all si_set_bb_vcofreq_frac is open-source;
- perhaps could handle CTL6/14 for other revisions, si_pmu_res_init is also open-source, and to me it makes little sense to ignore it.
Best,
Vitaly
[1] https://github.com/torvalds/linux/blob/8a36f53fd5ec3eff01f1b637d42cbdc3821d0493/drivers/bcma/driver_chipcommon_pmu.c#L336-L342
[2] https://github.com/RMerl/asuswrt-merlin.ng/blob/386_x/release/src-rt-6.x.4708/shared/hndpmu.c#L3450-L3460
[3] https://github.com/RMerl/asuswrt-merlin.ng/blob/386_x/release/src-rt-6.x.4708/shared/hndpmu.c#L5142-L5143
[4] https://github.com/RMerl/asuswrt-merlin.ng/blob/386_x/release/src-rt-6.x.4708/shared/hndpmu.c#L4961C37-L4964
> On 27 Oct 2026, at 02:53, Alessio Ferri <alessio.ferri@mythread.it> wrote:
>
> On chip rev 3:
>
> - program PLLCTL2/3 for a 960.0098 MHz fractional VCO from the 40 MHz
> crystal, except on a BCM4360 strapped for USB;
> - set the up/down time of resource 6;
> - with chipstatus RSRC_INIT_MODE0 clear, program PLLCTL6/14 and set
> max_res_mask to 0x1ff.
>
> The values are those of Broadcom's driver on rev 3; other revisions are
> left as they are.
>
> Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
> ---
> drivers/bcma/driver_chipcommon_pmu.c | 41 +++++++++++++++++++++++++++++
> include/linux/bcma/bcma_driver_chipcommon.h | 4 +++
> 2 files changed, 45 insertions(+)
>
> diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
> index 263ef6fa1..ab2b2701d 100644
> --- a/drivers/bcma/driver_chipcommon_pmu.c
> +++ b/drivers/bcma/driver_chipcommon_pmu.c
> @@ -145,6 +145,39 @@ 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;
> +
> + if (bus->chipinfo.rev != 3)
> + return;
> + if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4360 &&
> + cc->status & BCMA_CC_CHIPST_4360_MODE_USB)
> + return;
> +
> + /* Fractional VCO, 960.0098 MHz from a 40 MHz crystal. */
> + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x0c31);
> + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, 0x100e);
> + bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
> +}
> +
> +/* Returns the max resource mask to program, 0 to leave it alone. */
> +static u32 bcma_pmu_4360_resources_init(struct bcma_drv_cc *cc)
> +{
> + if (cc->core->bus->chipinfo.rev != 3)
> + return 0;
> +
> + bcma_pmu_write32(cc, BCMA_CC_PMU_RES_TABSEL, 6);
> + bcma_pmu_write32(cc, BCMA_CC_PMU_RES_UPDNTM, 0x00200001);
> +
> + if (cc->status & BCMA_CC_CHIPST_4360_RSRC_INIT_MODE0)
> + return 0;
> + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL6, 0x09048562);
> + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL14, 0x09048562);
> + bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
> + return 0x1ff;
> +}
> +
> static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
> {
> struct bcma_bus *bus = cc->core->bus;
> @@ -156,6 +189,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 +225,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:
> + max_msk = bcma_pmu_4360_resources_init(cc);
> + 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..45c37b6bd 100644
> --- a/include/linux/bcma/bcma_driver_chipcommon.h
> +++ b/include/linux/bcma/bcma_driver_chipcommon.h
> @@ -107,6 +107,8 @@
> #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_CHIPST_4360_MODE_USB 0x00000100
> #define BCMA_CC_JCMD 0x0030 /* Rev >= 10 only */
> #define BCMA_CC_JCMD_START 0x80000000
> #define BCMA_CC_JCMD_BUSY 0x80000000
> @@ -500,6 +502,8 @@
> #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_CTL14 14
>
> #define BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000
> #define BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT 20
>
> --
> 2.34.1
>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 583 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH wireless-next v2 2/8] bcma: pmu: init PLL and resources on BCM4352/BCM4360
2026-10-28 0:44 ` vit9696
@ 2026-10-28 9:14 ` Alessio Ferri
2026-10-28 13:34 ` vit9696
0 siblings, 1 reply; 15+ messages in thread
From: Alessio Ferri @ 2026-10-28 9:14 UTC (permalink / raw)
To: vit9696
Cc: Rafał Miłecki, Michael Buesch, linux-wireless,
linux-kernel, linux-mips
Il giorno Wed, 28 Oct 2026 00:44:31 +0000
vit9696 <vit9696@protonmail.com> ha scritto:
> Hello,
>
> XTAL frequency on BCM4360 and BCM4352 apparently does not only depend
> on the revision number. It is determined by bit #0 presence in
> chipstatus. If bit #0 is set, then it is 40 MHz, otherwise it is 20
> MHz. This logic is actually already present in the bcma driver[1],
> and looks correct to me. FVCO frequency being 960 MHz for all BCM4360
> and BCM4352 chips is also more or less known from the public
> open-source part of the Broadcom driver[2].
>
> The nitpick here is that whereas PLL seems indeed to be only
> programmed on revision 3 and newer[3] (with the code there assuming
> 40 MHz XTAL frequency, probably because Broadcom migrated to 40 MHz
> at a certain point for all newer chips), but there is a special case
> for HT clk_ctl_st bit[4], i.e. backplane is running on HT clock.
>
> With this in mind I believe the patch:
> - could handle revision 3 or newer, not just 3 in CTL2/3 logic;
> - could bug on BCMA_CC_CHIPST_4360_XTAL_40MZ not being set in CTL2/3
> logic;
> - could check for CCS_HTAVAIL bit not being set in CTL2/3 logic;
> - perhaps could calculate resulting CTL2/3 logic values in a slightly
> cleaner way, after all si_set_bb_vcofreq_frac is open-source;
> - perhaps could handle CTL6/14 for other revisions, si_pmu_res_init
> is also open-source, and to me it makes little sense to ignore it.
>
> Best,
> Vitaly
>
Hi Vitaly
Ok, it seems i missed it, i'll fix. In the meantime would you mind
looking in depth at rest of the series before i resend with this change?
Best,
Alessio
> [1]
> https://github.com/torvalds/linux/blob/8a36f53fd5ec3eff01f1b637d42cbdc3821d0493/drivers/bcma/driver_chipcommon_pmu.c#L336-L342
> [2]
> https://github.com/RMerl/asuswrt-merlin.ng/blob/386_x/release/src-rt-6.x.4708/shared/hndpmu.c#L3450-L3460
> [3]
> https://github.com/RMerl/asuswrt-merlin.ng/blob/386_x/release/src-rt-6.x.4708/shared/hndpmu.c#L5142-L5143
> [4]
> https://github.com/RMerl/asuswrt-merlin.ng/blob/386_x/release/src-rt-6.x.4708/shared/hndpmu.c#L4961C37-L4964
>
> > On 27 Oct 2026, at 02:53, Alessio Ferri <alessio.ferri@mythread.it>
> > wrote:
> >
> > On chip rev 3:
> >
> > - program PLLCTL2/3 for a 960.0098 MHz fractional VCO from the 40
> > MHz crystal, except on a BCM4360 strapped for USB;
> > - set the up/down time of resource 6;
> > - with chipstatus RSRC_INIT_MODE0 clear, program PLLCTL6/14 and set
> > max_res_mask to 0x1ff.
> >
> > The values are those of Broadcom's driver on rev 3; other revisions
> > are left as they are.
> >
> > Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
> > ---
> > drivers/bcma/driver_chipcommon_pmu.c | 41
> > +++++++++++++++++++++++++++++
> > include/linux/bcma/bcma_driver_chipcommon.h | 4 +++ 2 files
> > changed, 45 insertions(+)
> >
> > diff --git a/drivers/bcma/driver_chipcommon_pmu.c
> > b/drivers/bcma/driver_chipcommon_pmu.c index 263ef6fa1..ab2b2701d
> > 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c
> > +++ b/drivers/bcma/driver_chipcommon_pmu.c
> > @@ -145,6 +145,39 @@ 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;
> > +
> > + if (bus->chipinfo.rev != 3)
> > + return;
> > + if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4360 &&
> > + cc->status & BCMA_CC_CHIPST_4360_MODE_USB)
> > + return;
> > +
> > + /* Fractional VCO, 960.0098 MHz from a 40 MHz crystal. */
> > + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x0c31);
> > + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, 0x100e);
> > + bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
> > +}
> > +
> > +/* Returns the max resource mask to program, 0 to leave it alone.
> > */ +static u32 bcma_pmu_4360_resources_init(struct bcma_drv_cc *cc)
> > +{
> > + if (cc->core->bus->chipinfo.rev != 3)
> > + return 0;
> > +
> > + bcma_pmu_write32(cc, BCMA_CC_PMU_RES_TABSEL, 6);
> > + bcma_pmu_write32(cc, BCMA_CC_PMU_RES_UPDNTM, 0x00200001);
> > +
> > + if (cc->status & BCMA_CC_CHIPST_4360_RSRC_INIT_MODE0)
> > + return 0;
> > + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL6, 0x09048562);
> > + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL14, 0x09048562);
> > + bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
> > + return 0x1ff;
> > +}
> > +
> > static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
> > {
> > struct bcma_bus *bus = cc->core->bus;
> > @@ -156,6 +189,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 +225,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:
> > + max_msk = bcma_pmu_4360_resources_init(cc);
> > + 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..45c37b6bd 100644 ---
> > a/include/linux/bcma/bcma_driver_chipcommon.h +++
> > b/include/linux/bcma/bcma_driver_chipcommon.h @@ -107,6 +107,8 @@
> > #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_CHIPST_4360_MODE_USB 0x00000100 #define BCMA_CC_JCMD 0x0030
> > /* Rev >= 10 only */ #define BCMA_CC_JCMD_START 0x80000000
> > #define BCMA_CC_JCMD_BUSY 0x80000000
> > @@ -500,6 +502,8 @@
> > #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_CTL14 14
> >
> > #define BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000
> > #define BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT 20
> >
> > --
> > 2.34.1
> >
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH wireless-next v2 3/8] bcma: pcie2: request HT on BCM4352/BCM4360
2026-10-26 23:53 ` [PATCH wireless-next v2 3/8] bcma: pcie2: request HT " Alessio Ferri
@ 2026-10-28 11:40 ` vit9696
0 siblings, 0 replies; 15+ messages in thread
From: vit9696 @ 2026-10-28 11:40 UTC (permalink / raw)
To: Alessio Ferri
Cc: Rafał Miłecki, Michael Buesch, linux-wireless,
linux-kernel, linux-mips
[-- Attachment #1: Type: text/plain, Size: 1158 bytes --]
This should apply for all revisions below 4, not just 3 as per open-source Broadcom driver[1].
Best,
Vitaly
[1] https://github.com/RMerl/asuswrt-merlin.ng/blob/ca72a61f8bc5ef920e29d63d53d9864262208f0c/release/src-rt-5.02axhnd.675x/bcmdrivers/broadcom/net/wl/impl69/main/src/shared/hndpmu.c#L2059-L2067
> On 27 Oct 2026, at 02:53, Alessio Ferri <alessio.ferri@mythread.it> wrote:
>
> Set HAVEHTREQ in the PCIe2 core's clock control on chip rev 3, 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..fd8fab982 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 == 3)
> + pcie2_set32(pcie2, BCMA_CLKCTLST,
> + BCMA_CLKCTLST_HAVEHTREQ);
> break;
> default:
> pcie2->reqsize = 128;
>
> --
> 2.34.1
>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 583 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH wireless-next v2 6/8] firmware: bcm47xx_sprom: read ledbh4..ledbh15 from NVRAM
2026-10-26 23:53 ` [PATCH wireless-next v2 6/8] firmware: bcm47xx_sprom: read ledbh4..ledbh15 from NVRAM Alessio Ferri
@ 2026-10-28 12:43 ` vit9696
0 siblings, 0 replies; 15+ messages in thread
From: vit9696 @ 2026-10-28 12:43 UTC (permalink / raw)
To: Alessio Ferri
Cc: Rafał Miłecki, Michael Buesch, linux-wireless,
linux-kernel, linux-mips
[-- Attachment #1: Type: text/plain, Size: 8503 bytes --]
This change looks confusing to me.
While I believe it not clearly documented in public, from my knowledge the general approach is as follows:
- Broadcom supplies SPROM with configuration defaults.
- Vendor overrides these defaults via NVRAM where SDK allows it.
I believe all currently known SPROMs have configuration for [0, 3] GPIO pins whereas NVRAM overrides cover the whole [0, 15] GPIO range.
What I entirely miss is why this patch modifies bcma_sprom_get and not just bcm47xx_sprom_fill_auto (called from bcm47xx_fill_sprom ← bcm47xx_get_sprom_bcma). I believe you can safely process the remaining overrides there next to currently processed [0, 3] overrides.
In my opinion, it also makes sense convert gpio0/gpio1/gpio2/gpio3/gpio_ext[] to a unified array, because this is actually a 16-byte or a 32-byte array depending on the chip.
Best,
Vitaly
> On 27 Oct 2026, at 02:53, Alessio Ferri <alessio.ferri@mythread.it> wrote:
>
> Add gpio_ext[] for the LED behaviours of GPIO 4-15 and fill it from
> NVRAM ledbh4..ledbh15, both for a SPROM built from NVRAM and, through
> the new bcm47xx_sprom_fill_bcma_nvram(), on top of a SPROM read from a
> bcma device. 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 c98b6fb66..ac7622512 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>
> @@ -841,6 +842,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 bca03fd85..75604cd70 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>
> @@ -172,6 +173,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)) \
> @@ -209,6 +224,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);
> @@ -647,45 +664,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 0548af664..02e6c9054 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
>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 583 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH wireless-next v2 7/8] firmware: bcm47xx_sprom: read boardflags3 and AvVmid_c0..2 from NVRAM
2026-10-26 23:53 ` [PATCH wireless-next v2 7/8] firmware: bcm47xx_sprom: read boardflags3 and AvVmid_c0..2 " Alessio Ferri
@ 2026-10-28 13:27 ` vit9696
0 siblings, 0 replies; 15+ messages in thread
From: vit9696 @ 2026-10-28 13:27 UTC (permalink / raw)
To: Alessio Ferri
Cc: Rafał Miłecki, Michael Buesch, linux-wireless,
linux-kernel, linux-mips
[-- Attachment #1: Type: text/plain, Size: 4713 bytes --]
I think my previous message regarding 6/8 also relates to the change here in 7/8.
You are right to observe that NVRAM overrides are generally used to set not just GPIO leds but also PHY properties. Some of these variables are actually present in the open-source driver[1].
Whether or not you need to support them generally depends on the hardware. I personally do not remember seeing AvVmid_cX being set on BCM4360, I think it is generally set only for BCM4354. The proprietary driver might observe these variables regardless of the general configuration presence perhaps for unification purposes, but I am not sure this is needed for any BCM4360 in the wild.
Do you have an NVRAM dump with AvVmid_cX set for any BCM4360 hardware? If not, the AvVmid part is probably a no-op and I would not rush adding that. The amount of overrides supported by Broadcom drivers in general is quite large, dozens of vars, and it is probably best to add them based on NVRAM dumps to reduce the amount of potential format parsing bugs.
In general defaultsromvars_4360 array[2] is a good reference of which variables are generally found in the NVRAM. You can see e.g. boardflags3 there.
Best,
Vitaly
[1] https://github.com/RMerl/asuswrt-merlin.ng/blob/ca72a61f8bc5ef920e29d63d53d9864262208f0c/release/src-rt-5.04behnd.4916/bcmdrivers/broadcom/net/wl/impl103/main/src/shared/bcmsrom.c#L2823
[2] https://github.com/RMerl/asuswrt-merlin.ng/blob/ca72a61f8bc5ef920e29d63d53d9864262208f0c/release/src-rt-5.02axhnd/bcmdrivers/broadcom/net/wl/impl51/main/src/shared/bcmsrom.c#L598
> On 27 Oct 2026, at 02:53, Alessio Ferri <alessio.ferri@mythread.it> wrote:
>
> Read them along with ledbh4..ledbh15. AvVmid_cN holds the Av/Vmid pairs
> of chain N's power detector per band; avvmid_valid marks the chains
> whose list parsed. A boardflags3 in NVRAM overrides the SPROM word.
>
> Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
> ---
> drivers/firmware/broadcom/bcm47xx_sprom.c | 32 +++++++++++++++++++++++++++++++
> include/linux/ssb/ssb.h | 3 +++
> 2 files changed, 35 insertions(+)
>
> diff --git a/drivers/firmware/broadcom/bcm47xx_sprom.c b/drivers/firmware/broadcom/bcm47xx_sprom.c
> index 75604cd70..581dd867d 100644
> --- a/drivers/firmware/broadcom/bcm47xx_sprom.c
> +++ b/drivers/firmware/broadcom/bcm47xx_sprom.c
> @@ -173,6 +173,32 @@ static void nvram_read_alpha2(const char *prefix, const char *name,
> memcpy(val, buf, 2);
> }
>
> +/*
> + * AvVmid_c<core>: Av,Vmid of the chain's power detector for 2g, 5gl, 5gml,
> + * 5gmu and 5gh. The chain is marked valid only if all ten values parse.
> + */
> +static void nvram_read_avvmid(const char *prefix, int core,
> + struct ssb_sprom *sprom, bool fallback)
> +{
> + char name[sizeof("AvVmid_c0")];
> + char buf[64], *p = buf, *tok;
> + u8 v[10];
> + int n = 0;
> +
> + snprintf(name, sizeof(name), "AvVmid_c%d", core);
> + if (get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback) < 0)
> + return;
> + while (n < ARRAY_SIZE(v) && (tok = strsep(&p, ",")) != NULL) {
> + if (kstrtou8(strim(tok), 0, &v[n]))
> + return;
> + n++;
> + }
> + if (n != ARRAY_SIZE(v))
> + return;
> + memcpy(sprom->avvmid[core], v, sizeof(v));
> + sprom->avvmid_valid |= BIT(core);
> +}
> +
> /* 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)
> @@ -185,6 +211,12 @@ 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);
> + sprom->avvmid_valid = 0;
> + for (i = 0; i < ARRAY_SIZE(sprom->avvmid); i++)
> + nvram_read_avvmid(prefix, i, sprom, fallback);
> }
>
> /* This is one-function-only macro, it uses local "sprom" variable! */
> diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
> index 02e6c9054..5a4daa76d 100644
> --- a/include/linux/ssb/ssb.h
> +++ b/include/linux/ssb/ssb.h
> @@ -77,6 +77,9 @@ struct ssb_sprom {
> u8 gpio2; /* GPIO pin 2 */
> u8 gpio3; /* GPIO pin 3 */
> u8 gpio_ext[12]; /* GPIO pins 4-15, NVRAM ledbh4-15 only */
> + /* NVRAM AvVmid_c0..2: {Av, Vmid} for 2g, 5gl, 5gml, 5gmu, 5gh */
> + u8 avvmid[3][5][2];
> + u8 avvmid_valid; /* bit per chain whose AvVmid_cN parsed */
> 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
>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 583 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH wireless-next v2 2/8] bcma: pmu: init PLL and resources on BCM4352/BCM4360
2026-10-28 9:14 ` Alessio Ferri
@ 2026-10-28 13:34 ` vit9696
0 siblings, 0 replies; 15+ messages in thread
From: vit9696 @ 2026-10-28 13:34 UTC (permalink / raw)
To: Alessio Ferri
Cc: Rafał Miłecki, Michael Buesch, linux-wireless,
linux-kernel, linux-mips
[-- Attachment #1: Type: text/plain, Size: 7186 bytes --]
Hi Alessio,
I checked the other patches and e-mailed my thoughts where I saw I could provide some help.
Although I do not have expertise in Broadcom hardware design, I co-maintain complementary set of extensions for macOS Broadcom drivers for the purpose of broader hardware compatibility and happen to have some sparse information here and there.
In general I would recommend to rely more on the open-source part of the Broadcom drivers, especially for Northstar boards, because it often contains useful information.
Best,
Vitaly
> On 28 Oct 2026, at 12:14, Alessio Ferri <alessio.ferri@mythread.it> wrote:
>
> Il giorno Wed, 28 Oct 2026 00:44:31 +0000
> vit9696 <vit9696@protonmail.com> ha scritto:
>
>> Hello,
>>
>> XTAL frequency on BCM4360 and BCM4352 apparently does not only depend
>> on the revision number. It is determined by bit #0 presence in
>> chipstatus. If bit #0 is set, then it is 40 MHz, otherwise it is 20
>> MHz. This logic is actually already present in the bcma driver[1],
>> and looks correct to me. FVCO frequency being 960 MHz for all BCM4360
>> and BCM4352 chips is also more or less known from the public
>> open-source part of the Broadcom driver[2].
>>
>> The nitpick here is that whereas PLL seems indeed to be only
>> programmed on revision 3 and newer[3] (with the code there assuming
>> 40 MHz XTAL frequency, probably because Broadcom migrated to 40 MHz
>> at a certain point for all newer chips), but there is a special case
>> for HT clk_ctl_st bit[4], i.e. backplane is running on HT clock.
>>
>> With this in mind I believe the patch:
>> - could handle revision 3 or newer, not just 3 in CTL2/3 logic;
>> - could bug on BCMA_CC_CHIPST_4360_XTAL_40MZ not being set in CTL2/3
>> logic;
>> - could check for CCS_HTAVAIL bit not being set in CTL2/3 logic;
>> - perhaps could calculate resulting CTL2/3 logic values in a slightly
>> cleaner way, after all si_set_bb_vcofreq_frac is open-source;
>> - perhaps could handle CTL6/14 for other revisions, si_pmu_res_init
>> is also open-source, and to me it makes little sense to ignore it.
>>
>> Best,
>> Vitaly
>>
>
> Hi Vitaly
> Ok, it seems i missed it, i'll fix. In the meantime would you mind
> looking in depth at rest of the series before i resend with this change?
>
> Best,
> Alessio
>
>> [1]
>> https://github.com/torvalds/linux/blob/8a36f53fd5ec3eff01f1b637d42cbdc3821d0493/drivers/bcma/driver_chipcommon_pmu.c#L336-L342
>> [2]
>> https://github.com/RMerl/asuswrt-merlin.ng/blob/386_x/release/src-rt-6.x.4708/shared/hndpmu.c#L3450-L3460
>> [3]
>> https://github.com/RMerl/asuswrt-merlin.ng/blob/386_x/release/src-rt-6.x.4708/shared/hndpmu.c#L5142-L5143
>> [4]
>> https://github.com/RMerl/asuswrt-merlin.ng/blob/386_x/release/src-rt-6.x.4708/shared/hndpmu.c#L4961C37-L4964
>>
>>> On 27 Oct 2026, at 02:53, Alessio Ferri <alessio.ferri@mythread.it>
>>> wrote:
>>>
>>> On chip rev 3:
>>>
>>> - program PLLCTL2/3 for a 960.0098 MHz fractional VCO from the 40
>>> MHz crystal, except on a BCM4360 strapped for USB;
>>> - set the up/down time of resource 6;
>>> - with chipstatus RSRC_INIT_MODE0 clear, program PLLCTL6/14 and set
>>> max_res_mask to 0x1ff.
>>>
>>> The values are those of Broadcom's driver on rev 3; other revisions
>>> are left as they are.
>>>
>>> Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
>>> ---
>>> drivers/bcma/driver_chipcommon_pmu.c | 41
>>> +++++++++++++++++++++++++++++
>>> include/linux/bcma/bcma_driver_chipcommon.h | 4 +++ 2 files
>>> changed, 45 insertions(+)
>>>
>>> diff --git a/drivers/bcma/driver_chipcommon_pmu.c
>>> b/drivers/bcma/driver_chipcommon_pmu.c index 263ef6fa1..ab2b2701d
>>> 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c
>>> +++ b/drivers/bcma/driver_chipcommon_pmu.c
>>> @@ -145,6 +145,39 @@ 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;
>>> +
>>> + if (bus->chipinfo.rev != 3)
>>> + return;
>>> + if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4360 &&
>>> + cc->status & BCMA_CC_CHIPST_4360_MODE_USB)
>>> + return;
>>> +
>>> + /* Fractional VCO, 960.0098 MHz from a 40 MHz crystal. */
>>> + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x0c31);
>>> + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, 0x100e);
>>> + bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
>>> +}
>>> +
>>> +/* Returns the max resource mask to program, 0 to leave it alone.
>>> */ +static u32 bcma_pmu_4360_resources_init(struct bcma_drv_cc *cc)
>>> +{
>>> + if (cc->core->bus->chipinfo.rev != 3)
>>> + return 0;
>>> +
>>> + bcma_pmu_write32(cc, BCMA_CC_PMU_RES_TABSEL, 6);
>>> + bcma_pmu_write32(cc, BCMA_CC_PMU_RES_UPDNTM, 0x00200001);
>>> +
>>> + if (cc->status & BCMA_CC_CHIPST_4360_RSRC_INIT_MODE0)
>>> + return 0;
>>> + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL6, 0x09048562);
>>> + bcma_chipco_pll_write(cc, BCMA_CC_PMU_PLL_CTL14, 0x09048562);
>>> + bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
>>> + return 0x1ff;
>>> +}
>>> +
>>> static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
>>> {
>>> struct bcma_bus *bus = cc->core->bus;
>>> @@ -156,6 +189,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 +225,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:
>>> + max_msk = bcma_pmu_4360_resources_init(cc);
>>> + 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..45c37b6bd 100644 ---
>>> a/include/linux/bcma/bcma_driver_chipcommon.h +++
>>> b/include/linux/bcma/bcma_driver_chipcommon.h @@ -107,6 +107,8 @@
>>> #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_CHIPST_4360_MODE_USB 0x00000100 #define BCMA_CC_JCMD 0x0030
>>> /* Rev >= 10 only */ #define BCMA_CC_JCMD_START 0x80000000
>>> #define BCMA_CC_JCMD_BUSY 0x80000000
>>> @@ -500,6 +502,8 @@
>>> #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_CTL14 14
>>>
>>> #define BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000
>>> #define BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT 20
>>>
>>> --
>>> 2.34.1
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 583 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-10-28 13:34 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-26 23:53 [PATCH wireless-next v2 0/8] bcma-ac-rev1: support 4352/4360 rev3 chips Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 1/8] bcma: core: clear only FORCEHT for BCMA_CLKMODE_DYNAMIC Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 2/8] bcma: pmu: init PLL and resources on BCM4352/BCM4360 Alessio Ferri
2026-10-28 0:44 ` vit9696
2026-10-28 9:14 ` Alessio Ferri
2026-10-28 13:34 ` vit9696
2026-10-26 23:53 ` [PATCH wireless-next v2 3/8] bcma: pcie2: request HT " Alessio Ferri
2026-10-28 11:40 ` vit9696
2026-10-26 23:53 ` [PATCH wireless-next v2 4/8] bcma: chipcommon: enable asynchronous GPIO interrupts Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 5/8] bcma: extract SPROM revision 11 Alessio Ferri
2026-10-26 23:53 ` [PATCH wireless-next v2 6/8] firmware: bcm47xx_sprom: read ledbh4..ledbh15 from NVRAM Alessio Ferri
2026-10-28 12:43 ` vit9696
2026-10-26 23:53 ` [PATCH wireless-next v2 7/8] firmware: bcm47xx_sprom: read boardflags3 and AvVmid_c0..2 " Alessio Ferri
2026-10-28 13:27 ` vit9696
2026-10-26 23:53 ` [PATCH wireless-next v2 8/8] bcma: add the BCM4352 PCI ID Alessio Ferri
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.