From: Luca Casati <luk00casati@gmail.com>
To: linux-wireless@vger.kernel.org
Cc: johannes@sipsolutions.net, arend.vanspriel@broadcom.com,
b43-dev@lists.infradead.org, Luca Casati <luk00casati@gmail.com>
Subject: [PATCH wireless-next 2/5] b43: N-PHY: fix TX IQ/LO calibration
Date: Mon, 26 Oct 2026 19:15:40 +0100 [thread overview]
Message-ID: <20261026181543.77763-3-luk00casati@gmail.com> (raw)
In-Reply-To: <20261026181543.77763-1-luk00casati@gmail.com>
Several bugs kept the TX IQ/LO calibration from working, and on rev7+
it left TX dead afterwards:
- The busy poll on IQLOCAL_CMD was inverted: bits 15:14 are set while a
step runs, so it stopped waiting as soon as the step started.
- The final copy was reversed: it wrote the stale buffer over the best
coefficients (table 15 at 96) and read the active ones (at 80), so the
result was never applied.
- TxCalPhyCleanup lacked the rev7+ part of brcmsmac's
wlc_phy_txcal_phycleanup_nphy(): the 0x80 and 0x8 (PA power-up) RF
overrides set up for the cal were left in place.
- There was no counterpart to b43_nphy_tx_cal_radio_setup_rev7(), so the
TSSI/IQ-cal path stayed switched in. Add one, following
wlc_phy_txcal_radio_cleanup_nphy().
- The TRSW override for the second core used 8, which does not fit the
2-bit field on rev7+; use 2 as wl does.
With these, on a BCM43228 (N-PHY rev 16, radio 2057 rev 9) the per-step
coefficients match the proprietary wl driver's.
Found and fixed with the help of an AI coding assistant (Claude Opus 5.5),
by comparing mmiotraces of b43 and wl on the same card.
Signed-off-by: Luca Casati <luk00casati@gmail.com>
---
drivers/net/wireless/broadcom/b43/phy_n.c | 89 +++++++++++++++++++++--
1 file changed, 84 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/broadcom/b43/phy_n.c b/drivers/net/wireless/broadcom/b43/phy_n.c
index 66565c14e..cabf61870 100644
--- a/drivers/net/wireless/broadcom/b43/phy_n.c
+++ b/drivers/net/wireless/broadcom/b43/phy_n.c
@@ -4775,6 +4775,37 @@ static void b43_nphy_tx_cal_radio_setup_rev7(struct b43_wldev *dev)
}
}
+/*
+ * Undo b43_nphy_tx_cal_radio_setup_rev7(), as brcmsmac's
+ * wlc_phy_txcal_radio_cleanup_nphy() does. Without it the TSSI/IQ cal
+ * path stays switched in after a TX IQ/LO cal.
+ */
+static void b43_nphy_tx_cal_radio_cleanup(struct b43_wldev *dev)
+{
+ struct b43_phy *phy = &dev->phy;
+ u16 *save = dev->phy.n->tx_rx_cal_radio_saveregs;
+ int core, off;
+ u16 r;
+
+ if (phy->rev < 7 || phy->rev >= 19)
+ return;
+
+ for (core = 0; core < 2; core++) {
+ r = core ? 0x20 : 0;
+ off = core * 11;
+
+ b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, save[off + 0]);
+ b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, save[off + 1]);
+ b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, save[off + 2]);
+ b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, save[off + 3]);
+ b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, save[off + 5]);
+ if (phy->radio_rev != 5)
+ b43_radio_write(dev, r + R2057_TX0_TSSIA, save[off + 6]);
+ b43_radio_write(dev, r + R2057_TX0_TSSIG, save[off + 7]);
+ b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, save[off + 8]);
+ }
+}
+
/* https://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalRadioSetup */
static void b43_nphy_tx_cal_radio_setup(struct b43_wldev *dev)
{
@@ -5033,7 +5064,9 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
/* https://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhyCleanup */
static void b43_nphy_tx_cal_phy_cleanup(struct b43_wldev *dev)
{
- u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs;
+ struct b43_phy *phy = &dev->phy;
+ struct b43_phy_n *nphy = dev->phy.n;
+ u16 *regs = nphy->tx_rx_cal_phy_saveregs;
if (dev->phy.rev >= 3) {
b43_phy_write(dev, B43_NPHY_AFECTL_C1, regs[0]);
@@ -5047,7 +5080,42 @@ static void b43_nphy_tx_cal_phy_cleanup(struct b43_wldev *dev)
b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[8]);
b43_phy_write(dev, B43_NPHY_PAPD_EN0, regs[9]);
b43_phy_write(dev, B43_NPHY_PAPD_EN1, regs[10]);
+
+ /*
+ * Drop the overrides b43_nphy_tx_cal_phy_setup() set on rev7+
+ * (as brcmsmac's wlc_phy_txcal_phycleanup_nphy()). Leaving
+ * the 0x8 (PA power-up) override in place kills TX.
+ */
+ if (phy->rev >= 19)
+ b43_nphy_rf_ctl_override_rev19(dev, 0x80, 0, 0, true,
+ 1);
+ else if (phy->rev >= 7)
+ b43_nphy_rf_ctl_override_rev7(dev, 0x80, 0, 0, true, 1);
+
b43_nphy_reset_cca(dev);
+
+ if (nphy->use_int_tx_iq_lo_cal) {
+ if (phy->rev >= 19) {
+ b43_nphy_rf_ctl_override_rev19(dev, 0x8, 0, 0x3,
+ true, 0);
+ } else if (phy->rev >= 8) {
+ b43_nphy_rf_ctl_override_rev7(dev, 0x8, 0, 0x3,
+ true, 0);
+ } else if (phy->rev == 7) {
+ bool ghz2 = b43_current_band(dev->wl) ==
+ NL80211_BAND_2GHZ;
+
+ b43_radio_maskset(dev, ghz2 ?
+ R2057_PAD2G_TUNE_PUS_CORE0 :
+ R2057_IPA5G_CASCOFFV_PU_CORE0,
+ ~1, 1);
+ b43_radio_maskset(dev, ghz2 ?
+ R2057_PAD2G_TUNE_PUS_CORE1 :
+ R2057_IPA5G_CASCOFFV_PU_CORE1,
+ ~1, 1);
+ b43_radio_mask(dev, R2057_OVR_REG0, ~(1 << 4));
+ }
+ }
} else {
b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, regs[0]);
b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, regs[1]);
@@ -5102,7 +5170,12 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA,
0, 3);
b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 2, 1);
- b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 8, 2);
+ /*
+ * On rev7+ the TRSW field is 2 bits wide, so the legacy value
+ * 8 for the second core ends up as 0; wl uses 2 there.
+ */
+ b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW,
+ phy->rev >= 7 ? 2 : 8, 2);
regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0);
regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1);
@@ -5478,12 +5551,16 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
}
b43_phy_write(dev, B43_NPHY_IQLOCAL_CMD, cmd);
+ /* bits 15:14 stay set while the cal step runs */
for (i = 0; i < 2000; i++) {
tmp = b43_phy_read(dev, B43_NPHY_IQLOCAL_CMD);
- if (tmp & 0xC000)
+ if (!(tmp & 0xC000))
break;
udelay(10);
}
+ if (i == 2000)
+ b43err(dev->wl, "TX IQ/LO cal step 0x%04x timed out\n",
+ cmd);
b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length,
buffer);
@@ -5500,8 +5577,9 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
last = (dev->phy.rev < 3) ? 6 : 7;
if (!mphase || nphy->mphase_cal_phase_id == last) {
- b43_ntab_write_bulk(dev, B43_NTAB16(15, 96), 4, buffer);
- b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 4, buffer);
+ /* apply the result: best coeffs (96) -> active (80) */
+ b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), 4, buffer);
+ b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4, buffer);
if (dev->phy.rev < 3) {
buffer[0] = 0;
buffer[1] = 0;
@@ -5540,6 +5618,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
b43_nphy_tx_cal_phy_cleanup(dev);
b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, save);
+ b43_nphy_tx_cal_radio_cleanup(dev);
if (dev->phy.rev < 2 && (!mphase || nphy->mphase_cal_phase_id == last))
b43_nphy_tx_iq_workaround(dev);
--
2.43.0
next prev parent reply other threads:[~2026-10-26 18:15 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-26 18:15 [PATCH wireless-next 0/5] b43: N-PHY: fix calibrations on rev7+ (BCM43228) Luca Casati
2026-10-26 18:15 ` [PATCH wireless-next 1/5] b43: N-PHY: fix the TX tone sample table Luca Casati
2026-10-26 18:15 ` Luca Casati [this message]
2026-10-26 18:15 ` [PATCH wireless-next 3/5] b43: N-PHY: fix rev3+ RSSI calibration Luca Casati
2026-10-26 18:15 ` [PATCH wireless-next 4/5] b43: N-PHY: wait for the first 2057 RC calibration run Luca Casati
2026-10-26 18:15 ` [PATCH wireless-next 5/5] b43: N-PHY: add the missing override to the rev7 TX_PU command Luca Casati
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261026181543.77763-3-luk00casati@gmail.com \
--to=luk00casati@gmail.com \
--cc=arend.vanspriel@broadcom.com \
--cc=b43-dev@lists.infradead.org \
--cc=johannes@sipsolutions.net \
--cc=linux-wireless@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox