Linux wireless drivers development
 help / color / mirror / Atom feed
From: Andreas Wendleder <gonsolo@gmail.com>
To: linux-wireless@vger.kernel.org
Cc: Johannes Berg <johannes@sipsolutions.net>,
	b43-dev@lists.infradead.org, Michael Buesch <m@bues.ch>,
	Alessio Ferri <alessio.ferri@mythread.it>,
	kvalo@kernel.org, julian.calaby@gmail.com, jpeisach@ubuntu.com,
	rosenp@gmail.com, linux-kernel@vger.kernel.org
Subject: [PATCH RFC wireless-next 2/4] wifi: b43: AC-PHY init and 2.4 GHz channel switching
Date: Thu, 12 Nov 2026 18:56:54 +0100	[thread overview]
Message-ID: <20261112175656.40686-3-gonsolo@gmail.com> (raw)
In-Reply-To: <20261112175656.40686-1-gonsolo@gmail.com>

Replace the placeholder AC-PHY code with an initialization that brings
the BCM4360 (PHY AC rev 1, radio 2069 rev 4) up on 2.4 GHz at 20 MHz:

 - radio 2069 power-up (init, resistor and RC calibration) and power-down
   from software_rfkill,
 - PHY init: the table set, the first-init registers, the reset-time
   register blocks, hardware TX power control with the wide tables, and
   the second TX chain, which stays dark unless chipcontrol bit 3 is set,
 - channel switching: tune the synthesizer from the radio table, the
   per-channel Farrow resampler words and BW registers, reset CCA,
   with PHY 0x0003 bit 8 (the band) cleared on 2.4 GHz,
 - a fixed 2.4 GHz TX gain table index as the starting point of the power
   loop,
 - the 15 second work restarts the core when the microcode counts PHY TX
   errors in several fresh inits (only about a third of fresh inits can
   transmit, the rest fail every frame), at most three times,
 - HT capabilities for the 2.4 GHz band.

5 GHz channels are refused for now.

Co-developed-by: Alessio Ferri <alessio.ferri@mythread.it>
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
Signed-off-by: Andreas Wendleder <gonsolo@gmail.com>
---
 drivers/net/wireless/broadcom/b43/main.c      |   23 +-
 drivers/net/wireless/broadcom/b43/phy_ac.c    | 1014 ++++++++++++++++-
 drivers/net/wireless/broadcom/b43/phy_ac.h    |    3 +
 .../net/wireless/broadcom/b43/phy_ac_farrow.h |  260 +++++
 4 files changed, 1295 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/wireless/broadcom/b43/phy_ac_farrow.h

diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
index 6b80b5e..7d2f9ea 100644
--- a/drivers/net/wireless/broadcom/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
@@ -352,6 +352,25 @@ static struct ieee80211_supported_band b43_band_2GHz = {
 	.n_bitrates	= b43_g_ratetable_size,
 };
 
+static struct ieee80211_supported_band b43_band_2GHz_ac = {
+	.band		= NL80211_BAND_2GHZ,
+	.channels	= b43_2ghz_chantable,
+	.n_channels	= ARRAY_SIZE(b43_2ghz_chantable),
+	.bitrates	= b43_g_ratetable,
+	.n_bitrates	= b43_g_ratetable_size,
+	.ht_cap		= {
+		.ht_supported	= true,
+		/* static SM power save disabled */
+		.cap		= IEEE80211_HT_CAP_SM_PS,
+		.ampdu_factor	= IEEE80211_HT_MAX_AMPDU_8K,
+		.ampdu_density	= IEEE80211_HT_MPDU_DENSITY_NONE,
+		.mcs		= {
+			.rx_mask	= { 0xff, 0xff },
+			.tx_params	= IEEE80211_HT_MCS_TX_DEFINED,
+		},
+	},
+};
+
 static struct ieee80211_supported_band b43_band_2ghz_limited = {
 	.band		= NL80211_BAND_2GHZ,
 	.channels	= b43_2ghz_chantable,
@@ -5613,7 +5632,9 @@ static int b43_setup_bands(struct b43_wldev *dev,
 
 	if (have_2ghz_phy)
 		hw->wiphy->bands[NL80211_BAND_2GHZ] = limited_2g ?
-			&b43_band_2ghz_limited : &b43_band_2GHz;
+			&b43_band_2ghz_limited :
+			(dev->phy.type == B43_PHYTYPE_AC ?
+			 &b43_band_2GHz_ac : &b43_band_2GHz);
 	if (dev->phy.type == B43_PHYTYPE_N) {
 		if (have_5ghz_phy)
 			hw->wiphy->bands[NL80211_BAND_5GHZ] = limited_5g ?
diff --git a/drivers/net/wireless/broadcom/b43/phy_ac.c b/drivers/net/wireless/broadcom/b43/phy_ac.c
index fe102ea..4bcf6f8 100644
--- a/drivers/net/wireless/broadcom/b43/phy_ac.c
+++ b/drivers/net/wireless/broadcom/b43/phy_ac.c
@@ -4,10 +4,17 @@
  * IEEE 802.11ac AC-PHY support
  *
  * Copyright (c) 2015 Rafał Miłecki <zajec5@gmail.com>
+ * Copyright (c) 2026 Alessio Ferri <alessio.ferri@mythread.it>
+ * Copyright (c) 2026 Andreas Wendleder <gonsolo@gmail.com>
  */
 
 #include "b43.h"
 #include "phy_ac.h"
+#include "radio_2069.h"
+#include "tables_phy_ac.h"
+#include "dma.h"
+#include "main.h"
+
 
 /**************************************************
  * Basic PHY ops
@@ -42,17 +49,1009 @@ static void b43_phy_ac_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
 		    (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
 }
 
+/*
+ * Unlike the generic b43_phy_write() fallback (two 16-bit writes to CONTROL
+ * and DATA), the vendor driver writes one 32-bit word, (value << 16) | reg,
+ * to the CONTROL address. Do the same.
+ */
+static void b43_phy_ac_op_write(struct b43_wldev *dev, u16 reg, u16 value)
+{
+	b43_write32(dev, B43_MMIO_PHY_CONTROL, ((u32)value << 16) | reg);
+}
+
+/*
+ * Which CONTROL/DATA register pair addresses the radio depends on the core
+ * revision: cores 0x1b and below 0x18 use the legacy RADIO_CONTROL and
+ * RADIO_DATA_LOW pair, newer ones (including the BCM4360's) the RADIO24 pair.
+ */
+static bool b43_phy_ac_use_radio24(struct b43_wldev *dev)
+{
+	u8 rev = dev->dev->core_rev;
+
+	return !(rev == 0x1b || rev < 0x18);
+}
+
 static u16 b43_phy_ac_op_radio_read(struct b43_wldev *dev, u16 reg)
 {
-	b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg);
-	return b43_read16(dev, B43_MMIO_RADIO24_DATA);
+	if (b43_phy_ac_use_radio24(dev)) {
+		b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg);
+		return b43_read16(dev, B43_MMIO_RADIO24_DATA);
+	}
+
+	b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg);
+	return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW);
 }
 
 static void b43_phy_ac_op_radio_write(struct b43_wldev *dev, u16 reg,
 				      u16 value)
 {
-	b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg);
-	b43_write16(dev, B43_MMIO_RADIO24_DATA, value);
+	if (b43_phy_ac_use_radio24(dev)) {
+		b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg);
+		b43_write16(dev, B43_MMIO_RADIO24_DATA, value);
+		return;
+	}
+
+	b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg);
+	b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value);
+}
+
+static u8 b43_phy_ac_num_cores(struct b43_wldev *dev)
+{
+	u8 cores = b43_phy_read(dev, 0x00b) & 0x7;
+
+	if (dev->dev->chip_id == 0x4360 &&
+	    (dev->dev->board_type == 0x137 || dev->dev->board_type == 0x117))
+		cores = 2;
+	return cores;
+}
+
+/* prefregs_2069_rev4 from the vendor driver. */
+static const u16 b43_radio_2069r4_prefregs[][2] = {
+	{ 0x063a, 0x0000 }, { 0x063d, 0x000f }, { 0x0645, 0x3000 },
+	{ 0x0646, 0x0303 }, { 0x0647, 0x36e2 }, { 0x0649, 0x6000 },
+	{ 0x064a, 0x0003 }, { 0x065e, 0x0ff4 }, { 0x0666, 0x0b2d },
+	{ 0x0667, 0x03ff }, { 0x0115, 0x3337 }, { 0x0117, 0x1337 },
+	{ 0x0718, 0x7777 }, { 0x071c, 0x1000 }, { 0x0726, 0x0100 },
+	{ 0x0727, 0x0002 }, { 0x0761, 0x0100 }, { 0x0407, 0x8382 },
+	{ 0x055e, 0x0020 }, { 0x0885, 0x0700 }, { 0x08e9, 0x01ce },
+	{ 0x08ea, 0x00ff }, { 0x08ec, 0x06ff }, { 0x0972, 0x0600 },
+};
+
+/*
+ * Radio 2069 rev 4 init (as the vendor driver does it). Skipped: a board-flag
+ * dependent "radio 0x8ea |= 0x100" whose flag source we haven't identified.
+ */
+static void b43_radio_2069_init(struct b43_wldev *dev)
+{
+	u16 s728, s408;
+	unsigned int i;
+	u8 core, cores;
+
+	s728 = b43_phy_read(dev, 0x728);
+	s408 = b43_phy_read(dev, B43_PHY_AC_RFCTL_CMD) & 0xfc38;
+	b43_phy_write(dev, 0x415, 0);
+	b43_phy_write(dev, 0x40e, 0);
+	b43_phy_write(dev, 0x40c, 0x2000);
+	b43_phy_write(dev, B43_PHY_AC_RFCTL_CMD, s408);
+	b43_phy_write(dev, 0x417, 0);
+	b43_phy_write(dev, 0x416, 0xd);
+	b43_phy_write(dev, 0x728, s728 & 0x7e7f);
+	b43_phy_set(dev, 0x720, 0x180);
+	b43_phy_write(dev, B43_PHY_AC_RFCTL_CMD, s408);
+	b43_phy_write(dev, B43_PHY_AC_RFCTL_CMD, s408 | 1);
+	udelay(1);
+	b43_phy_write(dev, B43_PHY_AC_RFCTL_CMD, s408);
+
+	for (i = 0; i < ARRAY_SIZE(b43_radio_2069r4_prefregs); i++)
+		b43_radio_write(dev, b43_radio_2069r4_prefregs[i][0],
+				b43_radio_2069r4_prefregs[i][1]);
+
+	b43_radio_set(dev, 0x96b, 0x0800);
+	b43_radio_set(dev, 0x96b, 0x4000);
+	b43_radio_set(dev, 0x96c, 0x0800);
+	b43_radio_set(dev, 0x96b, 0x8000);
+	b43_radio_set(dev, 0x96b, 0x1000);
+	b43_radio_set(dev, 0x96b, 0x0004);
+	b43_radio_set(dev, 0x407, 0x0002);
+	b43_radio_set(dev, 0x55e, 0x0010);
+
+	cores = b43_phy_ac_num_cores(dev);
+	for (core = 0; core < cores; core++) {
+		u16 c = core << 9;
+
+		b43_radio_maskset(dev, 0x126 | c, ~0x300, 0x100);
+		b43_radio_maskset(dev, 0x127 | c, ~0x003, 0x002);
+		b43_radio_mask(dev, 0x06f | c, ~0x004);
+		b43_radio_mask(dev, 0x06f | c, ~0x001);
+		b43_radio_mask(dev, 0x06f | c, ~0x002);
+		b43_radio_mask(dev, 0x065 | c, ~0x001);
+	}
+
+	b43_radio_mask(dev, 0x40c, ~0x10);
+	b43_phy_write(dev, B43_PHY_AC_RFCTL_CMD, s408 | 6);
+	udelay(100);
+	b43_radio_set(dev, 0x40c, 0x10);
+	b43_phy_write(dev, 0x417, 0xd);
+	b43_phy_write(dev, B43_PHY_AC_RFCTL_CMD, s408 | 2);
+	b43_phy_write(dev, 0x728, s728 | 0x180);
+	udelay(100);
+	b43_phy_write(dev, 0x417, 4);
+	b43_phy_write(dev, 0x728, s728 & 0xfeff);
+
+	b43dbg(dev->wl, "phy_ac: radio 2069 init done (%u cores)\n", cores);
+}
+
+/*
+ * Resistor calibration: the vendor's default path when SPROM boardflags3
+ * bits 3 and 13 are clear. bcma does not parse boardflags3, so we assume 0.
+ */
+static bool b43_radio_2069_rcal(struct b43_wldev *dev)
+{
+	u16 v = 0;
+	int i;
+
+	b43_radio_set(dev, 0x8ea, 0x40);
+	b43_radio_set(dev, 0x8ea, 0x80);
+	b43_radio_mask(dev, 0x8ed, ~0x600);
+	b43_radio_mask(dev, 0x8ed, ~0x1800);
+	b43_radio_set(dev, 0x548, 0x1);
+	b43_radio_write(dev, 0x549, 0);
+	b43_radio_write(dev, 0x54a, 0);
+	b43_radio_write(dev, 0x54b, 0);
+	b43_radio_write(dev, 0x54c, 0);
+	b43_radio_mask(dev, 0x40b, ~0x1);
+	udelay(1);
+	b43_radio_set(dev, 0x40b, 0x1);
+	for (i = 0; i < 100; i++) {
+		udelay(10);
+		v = b43_radio_read(dev, 0x40b);
+		if (v & 0x8)
+			break;
+	}
+	b43_radio_read(dev, 0x40b);
+	b43_radio_mask(dev, 0x548, ~0x1);
+	b43_radio_mask(dev, 0x8ea, ~0x40);
+	b43_radio_mask(dev, 0x8ea, ~0x80);
+	b43_radio_mask(dev, 0x40b, ~0x1);
+
+	b43dbg(dev->wl, "phy_ac: RCAL %s after %d polls (0x40b=%04x)\n",
+	       (v & 0x8) ? "done" : "TIMEOUT", i, v);
+	return v & 0x8;
+}
+
+/* RC calibration (as the vendor driver does it), radio type 0 values. */
+static void b43_radio_2069_rccal(struct b43_wldev *dev)
+{
+	static const u8 cal_a[3] = { 1, 0, 0 };
+	static const u8 cal_b[3] = { 0, 2, 1 };
+	static const u8 cal_c[3] = { 0x1c, 0x70, 0x40 };
+	static const u16 cal_d[3] = { 0x14a, 0x101, 0x11a };
+	u8 core, cores = b43_phy_ac_num_cores(dev);
+	u16 v, r1, r2;
+	int step, i;
+	bool done;
+
+	b43_radio_set(dev, 0x8ea, 0x80);
+	b43_radio_maskset(dev, 0x8ed, ~0x600, 0x400);
+
+	for (step = 0; step < 3; step++) {
+		b43_radio_maskset(dev, 0x410, ~0x1000, cal_a[step] << 12);
+		b43_radio_maskset(dev, 0x410, ~0x0018, cal_b[step] << 3);
+		b43_radio_maskset(dev, 0x411, 0x00ff, cal_c[step] << 8);
+		b43_radio_write(dev, 0x412, cal_d[step]);
+		if (step == 2) {
+			for (core = 0; core < cores; core++) {
+				b43_radio_mask(dev, 0x11d | (core << 9), ~0x4);
+				b43_radio_set(dev, 0x171 | (core << 9), 0x2000);
+			}
+		}
+		b43_radio_mask(dev, 0x410, ~0x1);
+		udelay(1);
+		b43_radio_set(dev, 0x410, 0x1);
+		udelay(35);
+		b43_radio_set(dev, 0x411, 0x1);
+
+		done = false;
+		for (i = 0; i < 100; i++) {
+			udelay(100);
+			if (b43_radio_read(dev, 0x413) & 0x10) {
+				done = true;
+				break;
+			}
+		}
+		b43_radio_mask(dev, 0x411, ~0x1);
+
+		if (done) {
+			if (step == 0) {
+				r1 = b43_radio_read(dev, 0x414);
+				r2 = b43_radio_read(dev, 0x415);
+				b43dbg(dev->wl,
+				       "phy_ac: RCCAL step 0: %04x %04x -> %02x\n",
+				       r1, r2,
+				       (((u32)(r2 - r1)) * 0xc1 >> 8) & 0xff);
+			} else if (step == 1) {
+				v = b43_radio_read(dev, 0x416);
+				for (core = 0; core < cores; core++) {
+					b43_radio_maskset(dev,
+							  0x126 | (core << 9),
+							  ~0x1f, v & 0x1f);
+					b43_radio_maskset(dev,
+							  0x043 | (core << 9),
+							  ~0x1f, v & 0x1f);
+				}
+				b43dbg(dev->wl, "phy_ac: RCCAL step 1: %04x\n",
+				       v);
+			} else {
+				v = b43_radio_read(dev, 0x416);
+				for (core = 0; core < cores; core++)
+					b43_radio_mask(dev, 0x171 | (core << 9),
+						       ~0x2000);
+				b43dbg(dev->wl, "phy_ac: RCCAL step 2: %04x\n",
+				       v);
+			}
+		} else {
+			b43dbg(dev->wl, "phy_ac: RCCAL step %d TIMEOUT\n",
+			       step);
+		}
+		b43_radio_mask(dev, 0x410, ~0x1);
+	}
+	b43_radio_mask(dev, 0x8ea, ~0x80);
+}
+
+/* Radio power-up (vendor wlc_phy_switch_radio_acphy, on, radio type 0). */
+static void b43_radio_2069_power_up(struct b43_wldev *dev)
+{
+	b43_radio_2069_init(dev);
+	b43_phy_mask(dev, 0x16b, ~0x400);
+	udelay(3);
+	b43_phy_write(dev, 0x175, 0);
+	udelay(3);
+	b43_radio_2069_rcal(dev);
+	b43_radio_2069_rccal(dev);
+}
+
+/* Radio power-down; the power-up is b43_radio_2069_power_up(). */
+static void b43_radio_2069_power_down(struct b43_wldev *dev)
+{
+	b43_phy_write(dev, 0x173e, 0x1c00);
+	b43_phy_write(dev, 0x1739, 0);
+	b43_phy_write(dev, 0x173a, 0);
+	b43_phy_write(dev, 0x1725, 0x1fff);
+	b43_phy_write(dev, 0x1729, 0);
+	b43_phy_write(dev, 0x1721, 0xffff);
+	b43_phy_write(dev, 0x1728, 0);
+	b43_phy_write(dev, 0x1720, 0x3ff);
+	b43_phy_maskset(dev, B43_PHY_AC_RFCTL_CMD, ~2, 0);
+	b43_phy_write(dev, 0x417, 0);
+	b43_phy_write(dev, 0x416, 1);
+	dev->phy.ac->radio_on = false;
+	b43dbg(dev->wl, "phy_ac: radio powered down\n");
+}
+
+/*
+ * The radio is powered up by software_rfkill(); only switching off is done
+ * here.
+ */
+static void b43_phy_ac_op_switch_analog(struct b43_wldev *dev, bool on)
+{
+	if (!on)
+		b43_radio_2069_power_down(dev);
+}
+
+static void b43_phy_ac_op_software_rfkill(struct b43_wldev *dev, bool blocked)
+{
+	struct b43_phy_ac *phy_ac = dev->phy.ac;
+
+	b43dbg(dev->wl, "phy_ac: software_rfkill(blocked=%d)\n", blocked);
+	if (blocked) {
+		b43_radio_2069_power_down(dev);
+		return;
+	}
+	if (phy_ac->radio_on)
+		return;
+	if (dev->phy.radio_ver != 0x2069 || dev->phy.radio_rev != 4) {
+		b43err(dev->wl,
+		       "phy_ac: no power-up sequence for radio %04x rev %u\n",
+		       dev->phy.radio_ver, dev->phy.radio_rev);
+		return;
+	}
+	b43_radio_2069_power_up(dev);
+	phy_ac->radio_on = true;
+}
+
+static void b43_phy_ac_op_prepare_structs(struct b43_wldev *dev)
+{
+	struct b43_phy *phy = &dev->phy;
+	struct b43_phy_ac *phy_ac = phy->ac;
+
+	b43dbg(dev->wl, "phy_ac: prepare_structs\n");
+	memset(phy_ac, 0, sizeof(*phy_ac));
+}
+
+/*
+ * 2.4 GHz TX gain table index set on a channel switch (0 is the highest gain).
+ * The reset value, 64, is too weak beyond a few metres.
+ */
+#define B43_AC_TXIDX_2G	20
+static void b43_phy_ac_txpwr_by_index(struct b43_wldev *dev, unsigned int idx);
+
+/* One 48-bit PHY table entry: three words through the third data port. */
+static void b43_phy_ac_table_write48(struct b43_wldev *dev, u16 id, u16 offset,
+				     const u16 *w)
+{
+	b43_phy_write(dev, B43_PHY_AC_TABLE_ID, id);
+	b43_phy_write(dev, B43_PHY_AC_TABLE_OFFSET, offset);
+	b43_phy_write(dev, 0x011, w[0]);
+	b43_phy_write(dev, 0x011, w[1]);
+	b43_phy_write(dev, 0x011, w[2]);
+}
+
+/*
+ * The 48-bit wide tables of the 2.4 GHz setup: the TX gain table 0x20, which
+ * hardware TX power control indexes; the RF sequencer extension entry 0x33;
+ * and table 0x11: twelve fixed cells, 448 cells of the SROM's rpcal
+ * coefficient (the value for this board on 2.4 GHz), then four zero cells.
+ */
+static void b43_phy_ac_wide_tables_2g(struct b43_wldev *dev)
+{
+	static const u16 ext33[3] = { 0xe800, 0x0084, 0xd351 };
+	static const u16 head11[12][3] = {
+		{ 0x005b, 0, 0 }, { 0x8250, 0, 0 }, { 0xc338, 0, 0 },
+		{ 0x4527, 1, 0 }, { 0xa6a1, 1, 0 }, { 0x081b, 2, 0 },
+		{ 0x8a18, 2, 0 }, { 0x2c96, 3, 0 }, { 0x8e17, 3, 0 },
+		{ 0x101b, 4, 0 }, { 0x0020, 0, 0 }, { 0x0020, 0, 0 },
+	};
+	static const u16 fill11[3] = { 0xd602, 0x007e, 0x4002 };
+	static const u16 zero[3] = { 0, 0, 0 };
+	unsigned int i;
+
+	for (i = 0; i < 128; i++)
+		b43_phy_ac_table_write48(dev, 0x20, i, b43_phy_ac_txgain_2g[i]);
+	b43_phy_ac_table_write48(dev, 0x14, 0x33, ext33);
+	for (i = 0; i < 12; i++)
+		b43_phy_ac_table_write48(dev, 0x11, i, head11[i]);
+	for (i = 12; i < 460; i++)
+		b43_phy_ac_table_write48(dev, 0x11, i, fill11);
+	for (i = 460; i < 464; i++)
+		b43_phy_ac_table_write48(dev, 0x11, i, zero);
+}
+
+/*
+ * PHY register init for 2.4 GHz and 20 MHz, in the blocks of the reset-time
+ * code of Alessio Ferri's b43-ac-wip (mode_init, set_reg_on_reset,
+ * set_pdet_on_reset, coeff_bank_init), plus the few registers the vendor
+ * driver writes in the same window. Read-modify-write is kept where his
+ * code uses it.
+ */
+#define B43_AC_NCORES	2	/* coremask 0x3 on this chip */
+
+/* 0x17xx analog front end page, AFE on */
+static void b43_phy_ac_mode_init(struct b43_wldev *dev)
+{
+	b43_phy_write(dev, 0x173e, 0x0000);
+	b43_phy_write(dev, 0x1725, 0x0000);
+	b43_phy_write(dev, 0x1722, 0x0000);
+	b43_phy_write(dev, 0x1723, 0x0000);
+	b43_phy_write(dev, 0x1724, 0x0000);
+	b43_phy_write(dev, 0x1727, 0x0000);
+	b43_phy_write(dev, 0x1750, 0x0000);
+	b43_phy_write(dev, 0x1728, 0x0080);
+	b43_phy_write(dev, 0x1720, 0x0180);
+	b43_phy_write(dev, 0x1729, 0x0000);
+	b43_phy_write(dev, 0x1721, 0x5000);
+	b43_phy_write(dev, 0x173a, b43_phy_read(dev, 0x073a) | 0x0100);
+	b43_phy_write(dev, 0x1725, b43_phy_read(dev, 0x1725) | 0x0400);
+}
+
+static void b43_phy_ac_set_reg_on_reset(struct b43_wldev *dev)
+{
+	unsigned int c;
+
+	b43_phy_write(dev, 0x01f2, 0x00c8);
+	b43_phy_write(dev, 0x0026, 0x0092);
+	b43_phy_write(dev, 0x01ed, 0x0050);
+	b43_phy_write(dev, 0x0025, 0x0030);
+
+	/* clip mask: low byte 0x55 */
+	b43_phy_maskset(dev, 0x02ef, (u16)~0x00ff, 0x0055);
+	b43_phy_maskset(dev, 0x02eb, (u16)~0x00ff, 0x0055);
+	b43_phy_maskset(dev, 0x02f7, (u16)~0x00ff, 0x0055);
+	b43_phy_maskset(dev, 0x02f3, (u16)~0x00ff, 0x0055);
+
+	b43_phy_maskset(dev, 0x01ca, (u16)~0x1000, 0);
+	b43_phy_maskset(dev, 0x01b0, (u16)~0x0020, 0);
+	b43_phy_maskset(dev, 0x01b1, (u16)~0x1000, 0x1000);
+	b43_phy_maskset(dev, 0x01b6, (u16)~0x8000, 0);
+	for (c = 0; c < B43_AC_NCORES; c++) {
+		b43_phy_maskset(dev, 0x0690 + c * 0x200, (u16)~0x0200, 0x0200);
+		b43_phy_maskset(dev, 0x0690 + c * 0x200, (u16)~0x0400, 0x0400);
+	}
+	b43_phy_write(dev, 0x01e6, 0x0030);
+}
+
+/* Operating width in MHz; the coefficient bank scales with it. */
+static unsigned int b43_phy_ac_bw_mhz(struct b43_wldev *dev)
+{
+	switch (dev->wl->hw->conf.chandef.width) {
+	case NL80211_CHAN_WIDTH_80:
+		return 80;
+	case NL80211_CHAN_WIDTH_40:
+		return 40;
+	default:
+		return 20;
+	}
+}
+
+/*
+ * RX gain / threshold LUT and per-core words. The 20 MHz values are checked
+ * on 2.4 GHz; the 40 and 80 MHz ones are from Alessio Ferri's tree (5 GHz).
+ */
+static void b43_phy_ac_coeff_bank_init(struct b43_wldev *dev)
+{
+	static const u16 lut[3][21] = {
+		{ 0x0015, 0x0146, 0x0088, 0x0146, 0x076e, 0x01a8, 0x00a3,
+		  0x00f4, 0x00a3, 0x0684, 0x00ad, 0x00e5, 0x0068, 0x00e5,
+		  0x06be, 0x019e, 0x0073, 0x00b2, 0x0073, 0x05fe, 0x00cc },
+		{ 0x000b, 0x0181, 0x005a, 0x0181, 0x0793, 0x01b7, 0x00c1,
+		  0x0102, 0x00c1, 0x06c0, 0x00a9, 0x0162, 0x0042, 0x0162,
+		  0x075c, 0x01b3, 0x00b1, 0x00ed, 0x00b1, 0x0692, 0x00af },
+		{ 0x0005, 0x017a, 0x009e, 0x017a, 0x07ca, 0x01b2, 0x00bd,
+		  0x0114, 0x00bd, 0x06d6, 0x00a2, 0x016c, 0x006f, 0x016c,
+		  0x0793, 0x01b2, 0x00b6, 0x00ff, 0x00b6, 0x06b4, 0x00a8 },
+	};
+	const unsigned int bw = b43_phy_ac_bw_mhz(dev);
+	const unsigned int w = bw / 40;		/* 0, 1, 2 */
+	unsigned int i, c;
+
+	/* width index */
+	b43_phy_maskset(dev, 0x0076, (u16)~0x0007, w + 1);
+	b43_phy_maskset(dev, 0x0180, (u16)~0x001f, lut[w][0]);
+	for (i = 1; i < 21; i++)
+		b43_phy_maskset(dev, 0x0180 + i, (u16)~0x07ff, lut[w][i]);
+	b43_phy_maskset(dev, 0x01b5, (u16)~0x00ff, bw == 40 ? 0x008b : 0x0097);
+	b43_phy_maskset(dev, 0x0312, (u16)~0x00ff, bw == 80 ? 0x0009 : 0x0013);
+	b43_phy_maskset(dev, 0x0313, (u16)~0xff00, bw == 80 ? 0x0900 : 0x1300);
+	for (c = 0; c < B43_AC_NCORES; c++) {
+		u16 st = c * 0x200;
+
+		b43_phy_maskset(dev, 0x06ed + st, (u16)~0x00ff,
+				bw == 20 ? 0x000a : 0x0014);
+		b43_phy_maskset(dev, 0x06ef + st, (u16)~0x00ff,
+				bw == 20 ? 0x0017 :
+					   (bw == 40 ? 0x002a : 0x0054));
+		b43_phy_maskset(dev, 0x06ef + st, (u16)~0xff00,
+				bw == 20 ? 0x0e00 :
+					   (bw == 40 ? 0x1600 : 0x2c00));
+		b43_phy_maskset(dev, 0x06ef + st, (u16)~0x00ff, 15 * (bw / 20));
+	}
+}
+
+static void b43_phy_ac_phyinit(struct b43_wldev *dev)
+{
+	/*
+	 * The prologue of b43_radio_2069_init()
+	 * (0x0415/040e/040c/0408/0417/0416) and the end state of the power-on
+	 * (0x0408 = 0x0c02, 0x0417 = 4)
+	 */
+	b43_phy_write(dev, 0x1739, 0x0000);
+	b43_phy_write(dev, 0x0415, 0x0000);
+	b43_phy_write(dev, 0x040e, 0x0000);
+	b43_phy_write(dev, 0x040c, 0x2000);
+	b43_phy_write(dev, 0x0416, 0x000d);
+	b43_phy_write(dev, 0x0408, 0x0c02);
+	b43_phy_write(dev, 0x0417, 0x0004);
+	b43_phy_write(dev, 0x016b, 0x0000);
+	b43_phy_write(dev, 0x0175, 0x0000);
+
+	b43_phy_ac_mode_init(dev);
+	b43_phy_write(dev, 0x1645, 0x025c);	/* init_regs */
+	b43_phy_write(dev, 0x03c4, 0x0668);
+	b43_phy_ac_set_reg_on_reset(dev);
+	b43_phy_write(dev, 0x0358, 0xc07f);	/* set_pdet_on_reset */
+	/* FEM table 2 / sub 1 setup */
+	b43_phy_set(dev, 0x040a, 0x0100);
+	b43_phy_write(dev, 0x0414, 0x0555);
+	/* Second TX chain: without this bit core 1 does not radiate and
+	 * two-stream rates fail completely.
+	 */
+	if (dev->dev->bus_type == B43_BUS_BCMA)
+		bcma_cc_set32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_CHIPCTL,
+			      0x00000008);
+	b43_phy_ac_coeff_bank_init(dev);
+	/* RX gain control, all cores. Without it the
+	 * receive level is 26 dB down: -66 instead of -40 dBm.
+	 */
+	b43_phy_write(dev, 0x1726, 0x000c);
+	b43_phy_write(dev, 0x0197, 0x0014);	/* channel_setup */
+	b43_phy_write(dev, 0x0198, 0x0010);
+	b43_phy_ac_wide_tables_2g(dev);
+}
+
+static void b43_phy_ac_setup_regs(struct b43_wldev *dev)
+{
+	/*
+	 * Radio state the hardware TX power loop needs (TSSI / power detector
+	 * path, per-core 0x01a-0x01f / 0x21a-0x21f). Without it PHY 0x640/0x840
+	 * freeze at index 0 and the loop never reaches its target.
+	 */
+	b43_radio_write(dev, 0x0548, 0x0001);
+	b43_radio_write(dev, 0x0549, 0x0000);
+	b43_radio_write(dev, 0x054a, 0x0000);
+	b43_radio_write(dev, 0x054c, 0x0000);
+	b43_radio_write(dev, 0x040b, 0x0168);
+	b43_radio_write(dev, 0x054b, 0x0101);
+	b43_radio_write(dev, 0x004e, 0x8400);
+	b43_radio_write(dev, 0x0166, 0x0000);
+	b43_radio_write(dev, 0x024e, 0x8600);
+	b43_radio_write(dev, 0x0366, 0x0000);
+	b43_radio_write(dev, 0x001a, 0x0014);
+	b43_radio_write(dev, 0x001b, 0x0280);
+	b43_radio_write(dev, 0x001c, 0x0044);
+	b43_radio_write(dev, 0x001e, 0x0010);
+	b43_radio_write(dev, 0x001f, 0x0000);
+	b43_radio_write(dev, 0x021a, 0x0014);
+	b43_radio_write(dev, 0x021b, 0x0280);
+	b43_radio_write(dev, 0x021c, 0x0044);
+	b43_radio_write(dev, 0x021e, 0x0010);
+	b43_radio_write(dev, 0x021f, 0x0000);
+	b43_radio_write(dev, 0x0170, 0x0100);
+
+	b43_phy_ac_phyinit(dev);
+	/*
+	 * Hardware TX power control: 0x70 bits 15:13 enable it, 0x71 holds the
+	 * averaging window, 0x72 the rest of its configuration. Enabled: with
+	 * the wide tables it no longer fails frames next to the AP (0x0100
+	 * would switch it off).
+	 */
+	b43_phy_write(dev, 0x0070, 0xe500);
+	b43_phy_write(dev, 0x0071, 0x04c8);
+	b43_phy_write(dev, 0x0072, 0x400d);
+}
+
+/*
+ * Radio registers written from entries [2..51] of the 2069 tuning table, in
+ * table order. Resolved for acphychipid 0x4360 from the vendor driver's
+ * channel-set code.
+ */
+static const u16 b43_radio_2069_tune_regs[B43_RADIO_2069_TUNE_REGS] = {
+	0x8e0, 0x8e1, 0x8dd, 0x8dc, 0x8e6, 0x8e7, 0x8c4, 0x8c5, 0x8e5, 0x8eb,
+	0x8d6, 0x113, 0x8db, 0x8da, 0x8d7, 0x885, 0x886, 0x887, 0x8d9, 0x8d8,
+	0x8c9, 0x8ca, 0x8cc, 0x8c7, 0x8c8, 0x892, 0x894, 0x895, 0x896, 0x897,
+	0x899, 0x89a, 0x89b, 0x89c, 0x112, 0x629, 0x65b, 0x65e, 0x668, 0x11a,
+	0x11b, 0x719, 0x630, 0x65c, 0x662, 0x66d, 0x893, 0x145, 0x146, 0x723,
+};
+
+static const struct b43_radio_2069_chan *
+b43_radio_2069_find_chan(struct b43_wldev *dev, unsigned int channel)
+{
+	unsigned int i;
+
+	if (dev->phy.radio_ver != 0x2069 || dev->phy.radio_rev != 4)
+		return NULL;
+	for (i = 0; i < b43_radio_2069r4_chans_n; i++)
+		if (b43_radio_2069r4_chans[i].channel == channel)
+			return &b43_radio_2069r4_chans[i];
+	return NULL;
+}
+
+/* Synthesizer VCO calibration kick. */
+static void b43_radio_2069_vcocal(struct b43_wldev *dev)
+{
+	b43_radio_mask(dev, 0x8e5, ~0x4000);
+	b43_radio_mask(dev, 0x8d0, ~0x0001);
+	b43_radio_mask(dev, 0x8e8, ~0x0040);
+	b43_radio_mask(dev, 0x8dc, ~0x2000);
+	udelay(11);
+	b43_radio_set(dev, 0x8d0, 0x0001);
+	b43_radio_set(dev, 0x8e8, 0x0040);
+	udelay(1);
+	b43_radio_set(dev, 0x8dc, 0x2000);
+}
+
+/* Reset clear-channel assessment (PHY rev 1 variant). */
+static void b43_phy_ac_resetcca(struct b43_wldev *dev)
+{
+	u16 bbcfg;
+
+	b43_phy_force_clock(dev, true);
+	bbcfg = b43_phy_read(dev, B43_PHY_AC_BBCFG);
+	b43_phy_write(dev, B43_PHY_AC_BBCFG, bbcfg | B43_PHY_AC_BBCFG_RSTCCA);
+	udelay(1);
+	b43_phy_write(dev, B43_PHY_AC_BBCFG, bbcfg & ~B43_PHY_AC_BBCFG_RSTCCA);
+	b43_phy_force_clock(dev, false);
+	udelay(2);
+}
+
+/*
+ * Per-core RF control state of the 2.4 GHz setup that the init does not reach.
+ */
+static void b43_phy_ac_rfctrl_2g(struct b43_wldev *dev)
+{
+	static const u16 regs[][2] = {
+		{ 0x645, 0x024d }, { 0x845, 0x025f },
+		{ 0x725, 0x0600 }, { 0x925, 0x0600 },
+		{ 0x727, 0x0004 }, { 0x927, 0x0004 },
+		{ 0x728, 0x0880 }, { 0x928, 0x0880 },
+		{ 0x729, 0x1000 }, { 0x929, 0x1000 },
+		{ 0x73a, 0x0180 }, { 0x93a, 0x0180 },
+	};
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(regs); i++)
+		b43_phy_write(dev, regs[i][0], regs[i][1]);
+}
+
+#include "phy_ac_farrow.h"
+
+/*
+ * The ADC/DAC run from a clock derived from the synthesizer, so the
+ * fractional resampler to the baseband rate depends on the channel. The vendor
+ * driver writes these on every channel switch; without them every channel
+ * kept channel 6's ratio, 3.5% off on channel 1.
+ */
+static void b43_phy_ac_set_farrow(struct b43_wldev *dev, unsigned int channel)
+{
+	const struct b43_phy_ac_farrow *f = NULL;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(b43_phy_ac_farrow_20); i++)
+		if (b43_phy_ac_farrow_20[i].channel == channel)
+			f = &b43_phy_ac_farrow_20[i];
+	if (!f)
+		return;
+
+	b43_phy_write(dev, 0x19a, f->rx[0]);
+	b43_phy_write(dev, 0x19b, f->rx[1]);
+	b43_phy_write(dev, 0x19c, f->rx[2]);
+	b43_phy_write(dev, 0x199, f->rx[3]);
+	b43_phy_write(dev, 0x1a1, f->rx[0]);
+	b43_phy_write(dev, 0x1a2, f->rx[1]);
+	b43_phy_write(dev, 0x1a3, f->rx[2]);
+	b43_phy_write(dev, 0x1a0, f->rx[3]);
+	b43_phy_write(dev, 0x1603, f->tx[0]);
+	b43_phy_write(dev, 0x1602, f->tx[1]);
+	b43_phy_write(dev, 0x1607, f->tx[2]);
+	b43_phy_write(dev, 0x1606, f->tx[3]);
+	b43_phy_write(dev, 0x1601, b43_phy_read(dev, 0x601));
+}
+
+/* Program the synthesizer and the per-channel radio words of one channel. */
+static void b43_phy_ac_tune(struct b43_wldev *dev,
+			    const struct b43_radio_2069_chan *e,
+			    unsigned int channel)
+{
+	int i;
+
+	for (i = 0; i < B43_RADIO_2069_TUNE_REGS; i++)
+		b43_radio_write(dev, b43_radio_2069_tune_regs[i], e->radio[i]);
+	if (channel == 4) {
+		b43_radio_write(dev, 0x8d6, 0x0ce4);
+		b43_radio_maskset(dev, 0x8ec, ~0x0070, 0x0050);
+	}
+	b43_radio_set(dev, 0x645, 0x7000);
+	b43_radio_write(dev, 0x723, 0x83e0);
+	b43_radio_2069_vcocal(dev);
+}
+
+/*
+ * Channel switch for radio 2069 rev 4, following the vendor driver's
+ * channel-set sequence. Not ported: carrier-search suppression during the
+ * switch, the 5 GHz PLL setup and the per-channel PHY tweaks that follow the BW
+ * registers.
+ */
+static int b43_phy_ac_op_switch_channel(struct b43_wldev *dev,
+					unsigned int new_channel)
+{
+	struct b43_phy_ac *phy_ac = dev->phy.ac;
+	const struct b43_radio_2069_chan *e;
+	u16 save;
+	int i;
+
+	if (b43_current_band(dev->wl) != NL80211_BAND_2GHZ)
+		return -EOPNOTSUPP;
+
+	e = b43_radio_2069_find_chan(dev, new_channel);
+	if (!e) {
+		b43err(dev->wl, "phy_ac: no tuning data for channel %u (radio %04x rev %u)\n",
+		       new_channel, dev->phy.radio_ver, dev->phy.radio_rev);
+		return -ESRCH;
+	}
+	b43dbg(dev->wl, "phy_ac: switch_channel(%u) -> %u MHz\n",
+	       new_channel, e->freq);
+
+	save = b43_phy_read(dev, 0x19e);
+	b43_phy_set(dev, 0x19e, 0x3);
+
+	b43_phy_mask(dev, B43_PHY_AC_BANDCTL, ~0x0100);
+
+	if (!phy_ac->chan_set) {
+		b43_phy_set(dev, 0x728, 0x0100);
+		udelay(1);
+		b43_phy_mask(dev, 0x728, ~0x0100);
+		phy_ac->chan_set = true;
+	}
+
+	b43_phy_ac_tune(dev, e, new_channel);
+	b43_phy_maskset(dev, 0x19e, ~0x3, save & 0x3);
+
+	for (i = 0; i < 6; i++)
+		b43_phy_write(dev, B43_PHY_AC_BW1A + i, e->bw[i]);
+
+	b43_phy_ac_rfctrl_2g(dev);
+	b43_phy_ac_set_farrow(dev, new_channel);
+	b43_phy_ac_resetcca(dev);
+	b43_phy_ac_txpwr_by_index(dev, B43_AC_TXIDX_2G);
+
+	return 0;
+}
+
+/* Write a table of 8, 16 or 32 bit entries through the PHY table ports. */
+static void b43_phy_ac_write_table(struct b43_wldev *dev,
+				   const struct b43_phy_ac_tbl *t)
+{
+	unsigned int i;
+	u32 v;
+
+	b43_phy_write(dev, B43_PHY_AC_TABLE_ID, t->id);
+	b43_phy_write(dev, B43_PHY_AC_TABLE_OFFSET, t->offset);
+	for (i = 0; i < t->count; i++) {
+		switch (t->width) {
+		case 8:
+			b43_phy_write(dev, B43_PHY_AC_TABLE_DATA1,
+				      ((const u8 *)t->data)[i]);
+			break;
+		case 16:
+			b43_phy_write(dev, B43_PHY_AC_TABLE_DATA1,
+				      ((const u16 *)t->data)[i]);
+			break;
+		case 32:
+			v = ((const u32 *)t->data)[i];
+			b43_phy_write(dev, B43_PHY_AC_TABLE_DATA2, v >> 16);
+			b43_phy_write(dev, B43_PHY_AC_TABLE_DATA1, v & 0xffff);
+			break;
+		}
+	}
+}
+
+/*
+ * 2.4 GHz power-estimation LUT of one core (tables 0x40 / 0x60), 128 entries,
+ * from the SROM power-detector triple (a1, b0, b1) = pa2ga words. At step j:
+ * num = 512*b0 + 32*b1*j, den = 0x8000 + a1*j, v = (den/2 + num)/den clamped to
+ * [-8, 0x7f] (the same transfer function as b43_nphy_tx_power_ctl_setup(); it
+ * matches the vendor driver's table entry for entry on this board). SROM word
+ * offsets (rev 11): core 0 pa2ga at 0x6d, core 1 at 0x81.
+ */
+static void b43_phy_ac_write_est_pwr(struct b43_wldev *dev)
+{
+	static const struct {
+		u16 id;
+		u16 word;
+	} core_tbl[] = { { 0x40, 0x6d }, { 0x60, 0x81 } };
+	struct bcma_drv_cc *cc;
+	unsigned int c, j;
+
+	if (dev->dev->bus_type != B43_BUS_BCMA)
+		return;
+	cc = &dev->dev->bdev->bus->drv_cc;
+	for (c = 0; c < ARRAY_SIZE(core_tbl) && c < b43_phy_ac_num_cores(dev);
+	     c++) {
+		u16 base = BCMA_CC_SPROM + 2 * core_tbl[c].word;
+		u16 w0 = bcma_read16(cc->core, base);
+		u16 w1 = bcma_read16(cc->core, base + 2);
+		u16 w2 = bcma_read16(cc->core, base + 4);
+		s32 a1 = (s16)w0, b0 = (s16)w1, b1 = (s16)w2, num, den;
+		u16 lut[128];
+		struct b43_phy_ac_tbl tbl = { lut, 128, core_tbl[c].id, 0, 16 };
+
+		if (w0 == 0xffff && w1 == 0xffff && w2 == 0xffff)
+			/* unprogrammed SROM: keep the default table */
+			continue;
+		num = b0 << 9;
+		den = 0x8000;
+		for (j = 0; j < 128; j++) {
+			s32 d = den ? den : 1;
+			s32 v = (d / 2 + num) / d;
+
+			num += b1 * 0x20;
+			den += a1;
+			lut[j] = (u16)(clamp(v, -8, 0x7f) & 0xff);
+		}
+		b43_phy_ac_write_table(dev, &tbl);
+	}
+}
+
+/*
+ * PHY init for PHY rev 0/1: the AFE page, then the table set of the
+ * acphytbl_info_rev0 plus the FEM tables of this board's control pattern.
+ */
+static void b43_phy_ac_tables_init(struct b43_wldev *dev)
+{
+	static const u16 clear_regs[] = {
+		0x173e, 0x1725, 0x1722, 0x1723, 0x1724, 0x1725, 0x1726, 0x1727,
+		0x1750,
+	};
+	unsigned int i;
+	u16 save;
+
+	b43_phy_write(dev, 0x410, 0x77);
+	for (i = 0; i < ARRAY_SIZE(clear_regs); i++)
+		b43_phy_write(dev, clear_regs[i], 0);
+	b43_phy_write(dev, 0x1728, 0x80);
+	b43_phy_write(dev, 0x1720, 0x180);
+	b43_phy_write(dev, 0x1729, 0);
+	b43_phy_write(dev, 0x1721, 0x5000);
+	b43_phy_write(dev, 0x173a, b43_phy_read(dev, 0x73a) | 0x100);
+	b43_phy_write(dev, 0x1725, b43_phy_read(dev, 0x725) | 0x400);
+
+	save = b43_phy_read(dev, 0x19e);
+	b43_phy_set(dev, 0x19e, 0x2);
+	for (i = 0; i < b43_phy_ac_tbls_rev0_n; i++)
+		b43_phy_ac_write_table(dev, &b43_phy_ac_tbls_rev0[i]);
+	for (i = 0; i < b43_phy_ac_rfseq_tbls_n; i++)
+		b43_phy_ac_write_table(dev, &b43_phy_ac_rfseq_tbls[i]);
+	b43_phy_ac_write_est_pwr(dev);
+	for (i = 0; i < b43_phy_ac_misc_tbls_n; i++)
+		b43_phy_ac_write_table(dev, &b43_phy_ac_misc_tbls[i]);
+	for (i = 0; i < b43_phy_ac_ladder_tbls_n; i++)
+		b43_phy_ac_write_table(dev, &b43_phy_ac_ladder_tbls[i]);
+	for (i = 0; i < b43_phy_ac_vendor_tbls_n; i++)
+		b43_phy_ac_write_table(dev, &b43_phy_ac_vendor_tbls[i]);
+	/*
+	 * FEM control pattern: only the femctrl == 2 variant (this card) is
+	 * known. SROM rev 11 word 0x55 bits 15:11.
+	 */
+	if (dev->dev->bus_type == B43_BUS_BCMA &&
+	    (bcma_read16(dev->dev->bdev->bus->drv_cc.core,
+			 BCMA_CC_SPROM + 0xaa) >>
+	     11) == 2)
+		for (i = 0; i < b43_phy_ac_femctrl2_tbls_n; i++)
+			b43_phy_ac_write_table(dev,
+					       &b43_phy_ac_femctrl2_tbls[i]);
+	b43_phy_maskset(dev, 0x19e, ~0x2, save & 0x2);
+
+	b43_phy_write(dev, 0x1645, 0x25c);
+	b43dbg(dev->wl, "phy_ac: wrote %u PHY tables\n",
+	       b43_phy_ac_tbls_rev0_n);
+}
+
+/*
+ * First-init PHY register setup for PHY rev 0/1. Not ported: the hardware
+ * interference mitigation setup.
+ */
+static void b43_phy_ac_first_init(struct b43_wldev *dev)
+{
+	bool is_2ghz = b43_current_band(dev->wl) == NL80211_BAND_2GHZ;
+	u8 core, cores = b43_phy_ac_num_cores(dev);
+	static const u16 core_reg[3] = { 0x690, 0x890, 0xa90 };
+
+	b43_phy_set(dev, 0x19e, 0x1c0);
+	if (is_2ghz)
+		b43_phy_write(dev, 0x3c4, 0x668);
+	b43_phy_set(dev, 0x19e, 0x200);
+	b43_phy_maskset(dev, 0x19e, ~0x3c, 0x10);
+	b43_phy_write(dev, 0x1f2, 0xc8);
+	b43_phy_write(dev, 0x026, 0x92);
+	b43_phy_write(dev, 0x1ed, 0x50);
+	b43_phy_write(dev, 0x025, 0x30);
+
+	if (dev->dev->bus_type == B43_BUS_BCMA)
+		bcma_awrite32(dev->dev->bdev, BCMA_IOCTL,
+			      bcma_aread32(dev->dev->bdev, BCMA_IOCTL) |
+			      B43_BCMA_IOCTL_MACPHYCLKEN);
+
+	b43_phy_mask(dev, 0x40f, ~0x200);
+	b43_phy_mask(dev, 0x2f1, ~0x20);
+	b43_phy_mask(dev, 0x2ed, ~0x20);
+	b43_phy_mask(dev, 0x2f9, ~0x20);
+	b43_phy_mask(dev, 0x2f5, ~0x20);
+	b43_phy_maskset(dev, 0x2ef, ~0xff, 0x55);
+	b43_phy_maskset(dev, 0x2eb, ~0xff, 0x55);
+	b43_phy_maskset(dev, 0x2f7, ~0xff, 0x55);
+	b43_phy_maskset(dev, 0x2f3, ~0xff, 0x55);
+
+	b43_phy_write(dev, 0x400, 0);
+	b43_phy_mask(dev, 0x1ca, ~0x1000);
+	b43_phy_ac_resetcca(dev);
+	b43_phy_set(dev, 0x072, 0x4);
+	b43_phy_mask(dev, 0x1b0, ~0x20);
+	b43_phy_set(dev, 0x1b1, 0x1000);
+	b43_phy_mask(dev, 0x1b6, 0x7fff);
+	for (core = 0; core < cores && core < 3; core++) {
+		b43_phy_set(dev, core_reg[core], 0x200);
+		b43_phy_set(dev, core_reg[core], 0x400);
+	}
+	b43_phy_write(dev, 0x1e6, 0x30);
+	b43_phy_write(dev, 0x358, 0xc07f);
+
+	b43dbg(dev->wl, "phy_ac: first-init registers done\n");
+}
+
+static void b43_phy_ac_table_write16(struct b43_wldev *dev, u16 id,
+				     u16 offset, u16 value)
+{
+	b43_phy_write(dev, B43_PHY_AC_TABLE_ID, id);
+	b43_phy_write(dev, B43_PHY_AC_TABLE_OFFSET, offset);
+	b43_phy_write(dev, B43_PHY_AC_TABLE_DATA1, value);
+}
+
+/* Set the TX gain of both cores from the 2.4 GHz gain table, as the stock
+ * driver's txpwr_by_index does with hardware power control off.
+ */
+static void b43_phy_ac_txpwr_by_index(struct b43_wldev *dev, unsigned int idx)
+{
+	const u16 *e = b43_phy_ac_txgain_2g[min(idx, 127u)];
+	u16 bbmult = e[0] & 0x00ff;
+	u16 g0 = (e[0] >> 8) | ((e[1] & 0x00ff) << 8);
+	u16 g1 = (e[1] >> 8) | ((e[2] & 0x00ff) << 8);
+	u16 g2 = e[2] >> 8;
+	u8 core, cores = b43_phy_ac_num_cores(dev);
+
+	for (core = 0; core < cores && core < 3; core++) {
+		b43_phy_ac_table_write16(dev, 7, core + 0x100, g0);
+		b43_phy_ac_table_write16(dev, 7, core + 0x103, g1);
+		b43_phy_ac_table_write16(dev, 7, core + 0x106, g2);
+		b43_phy_ac_table_write16(dev, 0xc, 0x63 + 4 * core, bbmult);
+		b43_phy_ac_table_write16(dev, 0xc, 0x73 + 4 * core, bbmult);
+	}
+}
+
+static int b43_phy_ac_op_init(struct b43_wldev *dev)
+{
+	if (dev->phy.rev <= 1) {
+		b43_phy_ac_tables_init(dev);
+		b43_phy_ac_first_init(dev);
+		/* The channel switch that follows init tunes the radio. */
+		b43_phy_ac_setup_regs(dev);
+	} else {
+		b43err(dev->wl, "phy_ac: no table set for PHY rev %u\n",
+		       dev->phy.rev);
+	}
+
+	b43dbg(dev->wl,
+	       "phy_ac: init (core_rev %u, radio24=%d, chip %04x, board %04x, radio_on %d)\n",
+	       dev->dev->core_rev, b43_phy_ac_use_radio24(dev),
+	       dev->dev->chip_id, dev->dev->board_type, dev->phy.ac->radio_on);
+
+	return 0;
+}
+
+/*
+ * Self-healing for a bad init: only about 30 % of fresh inits can transmit; in
+ * the others every TX ends in a PHY TX error. The 15 s work polls the ucode's
+ * txphyerr counter (SHM 0xFE) and restarts the core through mac80211 (fresh
+ * init, fresh coin flip) after >= 3 errors in a window, at most 3 times
+ * (B43_AC_SELFHEAL_MAX); the budget refills after 10 quiet minutes. Polled, not
+ * IRQ-driven: unmasking B43_IRQ_PHY_TXERR hung the machine.
+ */
+#define B43_AC_SELFHEAL_MAX	3
+#define B43_AC_SH_TXPHYERR	0x00FE
+
+static bool b43_phy_ac_check_badinit(struct b43_wldev *dev)
+{
+	static unsigned int restarts;
+	static unsigned long last_restart;
+	static u16 last;
+	u16 now, d;
+
+	if (restarts && time_after(jiffies, last_restart + 600 * HZ))
+		restarts = 0;
+	now = b43_shm_read16(dev, B43_SHM_SHARED, B43_AC_SH_TXPHYERR);
+	d = now - last;
+	last = now;
+	if (d < 3 || restarts >= B43_AC_SELFHEAL_MAX)
+		return false;
+	restarts++;
+	last_restart = jiffies;
+	last = 0;	/* the ucode is reloaded, its counters restart at 0 */
+	b43err(dev->wl, "AC-PHY: bad init (%u PHY TX errors), restart %u/%u\n",
+	       d, restarts, B43_AC_SELFHEAL_MAX);
+	b43_controller_restart(dev, "AC bad init");
+	return true;
+}
+
+static void b43_phy_ac_op_pwork_15sec(struct b43_wldev *dev)
+{
+	b43_phy_ac_check_badinit(dev);
 }
 
 static unsigned int b43_phy_ac_op_get_default_chan(struct b43_wldev *dev)
@@ -79,10 +1078,17 @@ static void b43_phy_ac_op_adjust_txpower(struct b43_wldev *dev)
 const struct b43_phy_operations b43_phyops_ac = {
 	.allocate		= b43_phy_ac_op_allocate,
 	.free			= b43_phy_ac_op_free,
+	.prepare_structs	= b43_phy_ac_op_prepare_structs,
+	.init			= b43_phy_ac_op_init,
+	.phy_write		= b43_phy_ac_op_write,
 	.phy_maskset		= b43_phy_ac_op_maskset,
 	.radio_read		= b43_phy_ac_op_radio_read,
 	.radio_write		= b43_phy_ac_op_radio_write,
+	.software_rfkill	= b43_phy_ac_op_software_rfkill,
+	.switch_analog		= b43_phy_ac_op_switch_analog,
+	.switch_channel		= b43_phy_ac_op_switch_channel,
 	.get_default_chan	= b43_phy_ac_op_get_default_chan,
 	.recalc_txpower		= b43_phy_ac_op_recalc_txpower,
 	.adjust_txpower		= b43_phy_ac_op_adjust_txpower,
+	.pwork_15sec		= b43_phy_ac_op_pwork_15sec,
 };
diff --git a/drivers/net/wireless/broadcom/b43/phy_ac.h b/drivers/net/wireless/broadcom/b43/phy_ac.h
index 02eb6c0..0b6896e 100644
--- a/drivers/net/wireless/broadcom/b43/phy_ac.h
+++ b/drivers/net/wireless/broadcom/b43/phy_ac.h
@@ -32,6 +32,9 @@
 #define  B43_PHY_AC_C3_CLIP_DIS			0x4000
 
 struct b43_phy_ac {
+	/* Band of the last channel switch; valid once chan_set is true. */
+	bool chan_set;
+	bool radio_on;
 };
 
 extern const struct b43_phy_operations b43_phyops_ac;
diff --git a/drivers/net/wireless/broadcom/b43/phy_ac_farrow.h b/drivers/net/wireless/broadcom/b43/phy_ac_farrow.h
new file mode 100644
index 0000000..c707d4d
--- /dev/null
+++ b/drivers/net/wireless/broadcom/b43/phy_ac_farrow.h
@@ -0,0 +1,260 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * the vendor driver's rx_farrow_tbl / tx_farrow_dac1_tbl, 20 MHz section
+ * (dump_farrow.java).
+ *
+ * Copyright (c) 2026 Andreas Wendleder <gonsolo@gmail.com>
+ */
+static const struct b43_phy_ac_farrow {
+	u8 channel;
+	/* PHY 0x19a/0x19b/0x19c/0x199, core 1 at 0x1a1/0x1a2/0x1a3/0x1a0 */
+	u16 rx[4];
+	u16 tx[4];	/* PHY 0x1603/0x1602/0x1607/0x1606 */
+} b43_phy_ac_farrow_20[] = {
+	{ 184, { 0x0000, 0x0048, 0x0f00, 0x00a7 },
+	       { 0xe706, 0x0063, 0xe706, 0x0063 } },
+	{ 186, { 0xaaab, 0x0048, 0x0f00, 0x00a7 },
+	       { 0xb326, 0x0063, 0xb326, 0x0063 } },
+	{ 188, { 0x5555, 0x0049, 0x0f00, 0x00a7 },
+	       { 0x7f7b, 0x0063, 0x7f7b, 0x0063 } },
+	{ 190, { 0x0000, 0x004a, 0x0f00, 0x00a7 },
+	       { 0x4c06, 0x0063, 0x4c06, 0x0063 } },
+	{ 192, { 0xaaab, 0x004a, 0x0f00, 0x00a7 },
+	       { 0x18c6, 0x0063, 0x18c6, 0x0063 } },
+	{ 194, { 0x5555, 0x004b, 0x0f00, 0x00a7 },
+	       { 0xe5bb, 0x0062, 0xe5bb, 0x0062 } },
+	{ 196, { 0x0000, 0x004c, 0x0f00, 0x00a7 },
+	       { 0xb2e4, 0x0062, 0xb2e4, 0x0062 } },
+	{ 198, { 0xaaab, 0x004c, 0x0f00, 0x00a7 },
+	       { 0x8042, 0x0062, 0x8042, 0x0062 } },
+	{ 200, { 0x5555, 0x004d, 0x0f00, 0x00a7 },
+	       { 0x4dd3, 0x0062, 0x4dd3, 0x0062 } },
+	{ 202, { 0x0000, 0x004e, 0x0f00, 0x00a7 },
+	       { 0x1b98, 0x0062, 0x1b98, 0x0062 } },
+	{ 204, { 0xaaab, 0x004e, 0x0f00, 0x00a7 },
+	       { 0xe990, 0x0061, 0xe990, 0x0061 } },
+	{ 206, { 0x5555, 0x004f, 0x0f00, 0x00a7 },
+	       { 0xb7bb, 0x0061, 0xb7bb, 0x0061 } },
+	{ 208, { 0x0000, 0x0050, 0x0f00, 0x00a7 },
+	       { 0x8618, 0x0061, 0x8618, 0x0061 } },
+	{ 210, { 0xaaab, 0x0050, 0x0f00, 0x00a7 },
+	       { 0x54a8, 0x0061, 0x54a8, 0x0061 } },
+	{ 212, { 0x5555, 0x0051, 0x0f00, 0x00a7 },
+	       { 0x236a, 0x0061, 0x236a, 0x0061 } },
+	{ 214, { 0x0000, 0x0052, 0x0f00, 0x00a7 },
+	       { 0xf25e, 0x0060, 0xf25e, 0x0060 } },
+	{ 216, { 0xaaab, 0x0052, 0x0f00, 0x00a7 },
+	       { 0xc183, 0x0060, 0xc183, 0x0060 } },
+	{ 218, { 0x5555, 0x0053, 0x0f00, 0x00a7 },
+	       { 0x90d9, 0x0060, 0x90d9, 0x0060 } },
+	{ 220, { 0x0000, 0x0054, 0x0f00, 0x00a7 },
+	       { 0x6060, 0x0060, 0x6060, 0x0060 } },
+	{ 222, { 0xaaab, 0x0054, 0x0f00, 0x00a7 },
+	       { 0x3018, 0x0060, 0x3018, 0x0060 } },
+	{ 224, { 0x5555, 0x0055, 0x0f00, 0x00a7 },
+	       { 0x0000, 0x0060, 0x0000, 0x0060 } },
+	{ 226, { 0x0000, 0x0056, 0x0f00, 0x00a7 },
+	       { 0xd018, 0x005f, 0xd018, 0x005f } },
+	{ 228, { 0xaaab, 0x0056, 0x0f00, 0x00a7 },
+	       { 0xa060, 0x005f, 0xa060, 0x005f } },
+	{  32, { 0x0000, 0x0058, 0x0f00, 0x00a7 },
+	       { 0x417d, 0x005f, 0x417d, 0x005f } },
+	{  34, { 0xaaab, 0x0058, 0x0f00, 0x00a7 },
+	       { 0x1252, 0x005f, 0x1252, 0x005f } },
+	{  36, { 0x5555, 0x0059, 0x0f00, 0x00a7 },
+	       { 0xe356, 0x005e, 0xe356, 0x005e } },
+	{  38, { 0x0000, 0x005a, 0x0f00, 0x00a7 },
+	       { 0xb488, 0x005e, 0xb488, 0x005e } },
+	{  40, { 0xaaab, 0x005a, 0x0f00, 0x00a7 },
+	       { 0x85e8, 0x005e, 0x85e8, 0x005e } },
+	{  42, { 0x5555, 0x005b, 0x0f00, 0x00a7 },
+	       { 0x5776, 0x005e, 0x5776, 0x005e } },
+	{  44, { 0x0000, 0x005c, 0x0f00, 0x00a7 },
+	       { 0x2932, 0x005e, 0x2932, 0x005e } },
+	{  46, { 0xaaab, 0x005c, 0x0f00, 0x00a7 },
+	       { 0xfb1b, 0x005d, 0xfb1b, 0x005d } },
+	{  48, { 0x5555, 0x005d, 0x0f00, 0x00a7 },
+	       { 0xcd31, 0x005d, 0xcd31, 0x005d } },
+	{  50, { 0x0000, 0x005e, 0x0f00, 0x00a7 },
+	       { 0x9f74, 0x005d, 0x9f74, 0x005d } },
+	{  52, { 0xaaab, 0x005e, 0x0f00, 0x00a7 },
+	       { 0x71e3, 0x005d, 0x71e3, 0x005d } },
+	{  54, { 0x5555, 0x005f, 0x0f00, 0x00a7 },
+	       { 0x447e, 0x005d, 0x447e, 0x005d } },
+	{  56, { 0x0000, 0x0060, 0x0f00, 0x00a7 },
+	       { 0x1746, 0x005d, 0x1746, 0x005d } },
+	{  58, { 0xaaab, 0x0060, 0x0f00, 0x00a7 },
+	       { 0xea39, 0x005c, 0xea39, 0x005c } },
+	{  60, { 0x5555, 0x0061, 0x0f00, 0x00a7 },
+	       { 0xbd58, 0x005c, 0xbd58, 0x005c } },
+	{  62, { 0x0000, 0x0062, 0x0f00, 0x00a7 },
+	       { 0x90a2, 0x005c, 0x90a2, 0x005c } },
+	{  64, { 0xaaab, 0x0062, 0x0f00, 0x00a7 },
+	       { 0x6417, 0x005c, 0x6417, 0x005c } },
+	{  66, { 0x5555, 0x0063, 0x0f00, 0x00a7 },
+	       { 0x37b7, 0x005c, 0x37b7, 0x005c } },
+	{  68, { 0x0000, 0x0064, 0x0f00, 0x00a7 },
+	       { 0x0b81, 0x005c, 0x0b81, 0x005c } },
+	{  70, { 0xaaab, 0x0064, 0x0f00, 0x00a7 },
+	       { 0xdf76, 0x005b, 0xdf76, 0x005b } },
+	{  72, { 0x5555, 0x0065, 0x0f00, 0x00a7 },
+	       { 0xb395, 0x005b, 0xb395, 0x005b } },
+	{  74, { 0x0000, 0x0066, 0x0f00, 0x00a7 },
+	       { 0x87de, 0x005b, 0x87de, 0x005b } },
+	{  76, { 0xaaab, 0x0066, 0x0f00, 0x00a7 },
+	       { 0x5c50, 0x005b, 0x5c50, 0x005b } },
+	{  78, { 0x5555, 0x0067, 0x0f00, 0x00a7 },
+	       { 0x30ec, 0x005b, 0x30ec, 0x005b } },
+	{  80, { 0x0000, 0x0068, 0x0f00, 0x00a7 },
+	       { 0x05b0, 0x005b, 0x05b0, 0x005b } },
+	{  82, { 0xaaab, 0x0068, 0x0f00, 0x00a7 },
+	       { 0xda9e, 0x005a, 0xda9e, 0x005a } },
+	{  84, { 0x5555, 0x0069, 0x0f00, 0x00a7 },
+	       { 0xafb4, 0x005a, 0xafb4, 0x005a } },
+	{  86, { 0x0000, 0x006a, 0x0f00, 0x00a7 },
+	       { 0x84f3, 0x005a, 0x84f3, 0x005a } },
+	{  88, { 0xaaab, 0x006a, 0x0f00, 0x00a7 },
+	       { 0x5a5a, 0x005a, 0x5a5a, 0x005a } },
+	{  90, { 0x5555, 0x006b, 0x0f00, 0x00a7 },
+	       { 0x2fe9, 0x005a, 0x2fe9, 0x005a } },
+	{  92, { 0x0000, 0x006c, 0x0f00, 0x00a7 },
+	       { 0x05a0, 0x005a, 0x05a0, 0x005a } },
+	{  94, { 0xaaab, 0x006c, 0x0f00, 0x00a7 },
+	       { 0xdb7f, 0x0059, 0xdb7f, 0x0059 } },
+	{  96, { 0x5555, 0x006d, 0x0f00, 0x00a7 },
+	       { 0xb185, 0x0059, 0xb185, 0x0059 } },
+	{  98, { 0x0000, 0x006e, 0x0f00, 0x00a7 },
+	       { 0x87b2, 0x0059, 0x87b2, 0x0059 } },
+	{ 100, { 0xaaab, 0x006e, 0x0f00, 0x00a7 },
+	       { 0x5e06, 0x0059, 0x5e06, 0x0059 } },
+	{ 102, { 0x5555, 0x006f, 0x0f00, 0x00a7 },
+	       { 0x3480, 0x0059, 0x3480, 0x0059 } },
+	{ 104, { 0x0000, 0x0070, 0x0f00, 0x00a7 },
+	       { 0x0b21, 0x0059, 0x0b21, 0x0059 } },
+	{ 106, { 0xaaab, 0x0070, 0x0f00, 0x00a7 },
+	       { 0xe1e9, 0x0058, 0xe1e9, 0x0058 } },
+	{ 108, { 0x5555, 0x0071, 0x0f00, 0x00a7 },
+	       { 0xb8d6, 0x0058, 0xb8d6, 0x0058 } },
+	{ 110, { 0x0000, 0x0072, 0x0f00, 0x00a7 },
+	       { 0x8fea, 0x0058, 0x8fea, 0x0058 } },
+	{ 112, { 0xaaab, 0x0072, 0x0f00, 0x00a7 },
+	       { 0x6723, 0x0058, 0x6723, 0x0058 } },
+	{ 114, { 0x5555, 0x0073, 0x0f00, 0x00a7 },
+	       { 0x3e82, 0x0058, 0x3e82, 0x0058 } },
+	{ 116, { 0x0000, 0x0074, 0x0f00, 0x00a7 },
+	       { 0x1606, 0x0058, 0x1606, 0x0058 } },
+	{ 118, { 0xaaab, 0x0074, 0x0f00, 0x00a7 },
+	       { 0xedae, 0x0057, 0xedae, 0x0057 } },
+	{ 120, { 0x5555, 0x0075, 0x0f00, 0x00a7 },
+	       { 0xc57c, 0x0057, 0xc57c, 0x0057 } },
+	{ 122, { 0x0000, 0x0076, 0x0f00, 0x00a7 },
+	       { 0x9d6f, 0x0057, 0x9d6f, 0x0057 } },
+	{ 124, { 0xaaab, 0x0076, 0x0f00, 0x00a7 },
+	       { 0x7586, 0x0057, 0x7586, 0x0057 } },
+	{ 126, { 0x5555, 0x0077, 0x0f00, 0x00a7 },
+	       { 0x4dc1, 0x0057, 0x4dc1, 0x0057 } },
+	{ 128, { 0x0000, 0x0078, 0x0f00, 0x00a7 },
+	       { 0x2621, 0x0057, 0x2621, 0x0057 } },
+	{ 130, { 0xaaab, 0x0078, 0x0f00, 0x00a7 },
+	       { 0xfea4, 0x0056, 0xfea4, 0x0056 } },
+	{ 132, { 0x5555, 0x0079, 0x0f00, 0x00a7 },
+	       { 0xd74b, 0x0056, 0xd74b, 0x0056 } },
+	{ 134, { 0x0000, 0x007a, 0x0f00, 0x00a7 },
+	       { 0xb016, 0x0056, 0xb016, 0x0056 } },
+	{ 136, { 0xaaab, 0x007a, 0x0f00, 0x00a7 },
+	       { 0x8904, 0x0056, 0x8904, 0x0056 } },
+	{ 138, { 0x5555, 0x007b, 0x0f00, 0x00a7 },
+	       { 0x6215, 0x0056, 0x6215, 0x0056 } },
+	{ 140, { 0x0000, 0x007c, 0x0f00, 0x00a7 },
+	       { 0x3b49, 0x0056, 0x3b49, 0x0056 } },
+	{ 142, { 0xaaab, 0x007c, 0x0f00, 0x00a7 },
+	       { 0x14a0, 0x0056, 0x14a0, 0x0056 } },
+	{ 144, { 0x5555, 0x007d, 0x0f00, 0x00a7 },
+	       { 0xee19, 0x0055, 0xee19, 0x0055 } },
+	{ 145, { 0xaaab, 0x007d, 0x0f00, 0x00a7 },
+	       { 0xdae3, 0x0055, 0xdae3, 0x0055 } },
+	{ 146, { 0x0000, 0x007e, 0x0f00, 0x00a7 },
+	       { 0xc7b5, 0x0055, 0xc7b5, 0x0055 } },
+	{ 147, { 0x5555, 0x007e, 0x0f00, 0x00a7 },
+	       { 0xb490, 0x0055, 0xb490, 0x0055 } },
+	{ 148, { 0xaaab, 0x007e, 0x0f00, 0x00a7 },
+	       { 0xa173, 0x0055, 0xa173, 0x0055 } },
+	{ 149, { 0x0000, 0x007f, 0x0f00, 0x00a7 },
+	       { 0x8e5f, 0x0055, 0x8e5f, 0x0055 } },
+	{ 150, { 0x5555, 0x007f, 0x0f00, 0x00a7 },
+	       { 0x7b53, 0x0055, 0x7b53, 0x0055 } },
+	{ 151, { 0xaaab, 0x007f, 0x0f00, 0x00a7 },
+	       { 0x6850, 0x0055, 0x6850, 0x0055 } },
+	{ 152, { 0x0000, 0x0080, 0x0f00, 0x00a7 },
+	       { 0x5555, 0x0055, 0x5555, 0x0055 } },
+	{ 153, { 0x5555, 0x0080, 0x0f00, 0x00a7 },
+	       { 0x4263, 0x0055, 0x4263, 0x0055 } },
+	{ 154, { 0xaaab, 0x0080, 0x0f00, 0x00a7 },
+	       { 0x2f79, 0x0055, 0x2f79, 0x0055 } },
+	{ 155, { 0x0000, 0x0081, 0x0f00, 0x00a7 },
+	       { 0x1c98, 0x0055, 0x1c98, 0x0055 } },
+	{ 156, { 0x5555, 0x0081, 0x0f00, 0x00a7 },
+	       { 0x09be, 0x0055, 0x09be, 0x0055 } },
+	{ 157, { 0xaaab, 0x0081, 0x0f00, 0x00a7 },
+	       { 0xf6ee, 0x0054, 0xf6ee, 0x0054 } },
+	{ 158, { 0x0000, 0x0082, 0x0f00, 0x00a7 },
+	       { 0xe425, 0x0054, 0xe425, 0x0054 } },
+	{ 159, { 0x5555, 0x0082, 0x0f00, 0x00a7 },
+	       { 0xd165, 0x0054, 0xd165, 0x0054 } },
+	{ 160, { 0xaaab, 0x0082, 0x0f00, 0x00a7 },
+	       { 0xbead, 0x0054, 0xbead, 0x0054 } },
+	{ 161, { 0x0000, 0x0083, 0x0f00, 0x00a7 },
+	       { 0xabfd, 0x0054, 0xabfd, 0x0054 } },
+	{ 162, { 0x5555, 0x0083, 0x0f00, 0x00a7 },
+	       { 0x9956, 0x0054, 0x9956, 0x0054 } },
+	{ 163, { 0xaaab, 0x0083, 0x0f00, 0x00a7 },
+	       { 0x86b7, 0x0054, 0x86b7, 0x0054 } },
+	{ 164, { 0x0000, 0x0084, 0x0f00, 0x00a7 },
+	       { 0x7420, 0x0054, 0x7420, 0x0054 } },
+	{ 165, { 0x5555, 0x0084, 0x0f00, 0x00a7 },
+	       { 0x6191, 0x0054, 0x6191, 0x0054 } },
+	{ 166, { 0xaaab, 0x0084, 0x0f00, 0x00a7 },
+	       { 0x4f0a, 0x0054, 0x4f0a, 0x0054 } },
+	{ 168, { 0x5555, 0x0085, 0x0f00, 0x00a7 },
+	       { 0x2a15, 0x0054, 0x2a15, 0x0054 } },
+	{ 170, { 0x0000, 0x0086, 0x0f00, 0x00a7 },
+	       { 0x0540, 0x0054, 0x0540, 0x0054 } },
+	{ 172, { 0xaaab, 0x0086, 0x0f00, 0x00a7 },
+	       { 0xe08c, 0x0053, 0xe08c, 0x0053 } },
+	{ 174, { 0x5555, 0x0087, 0x0f00, 0x00a7 },
+	       { 0xbbf7, 0x0053, 0xbbf7, 0x0053 } },
+	{ 176, { 0x0000, 0x0088, 0x0f00, 0x00a7 },
+	       { 0x9783, 0x0053, 0x9783, 0x0053 } },
+	{ 178, { 0xaaab, 0x0088, 0x0f00, 0x00a7 },
+	       { 0x732e, 0x0053, 0x732e, 0x0053 } },
+	{ 180, { 0x5555, 0x0089, 0x0f00, 0x00a7 },
+	       { 0x4ef8, 0x0053, 0x4ef8, 0x0053 } },
+	{   1, { 0xcccd, 0x0069, 0x1400, 0x00a7 },
+	       { 0x91c3, 0x005a, 0x91c3, 0x005a } },
+	{   2, { 0x8ccd, 0x006a, 0x1400, 0x00a7 },
+	       { 0x61cc, 0x005a, 0x61cc, 0x005a } },
+	{   3, { 0x4ccd, 0x006b, 0x1400, 0x00a7 },
+	       { 0x3208, 0x005a, 0x3208, 0x005a } },
+	{   4, { 0x0ccd, 0x006c, 0x1400, 0x00a7 },
+	       { 0x0276, 0x005a, 0x0276, 0x005a } },
+	{   5, { 0xcccd, 0x006c, 0x1400, 0x00a7 },
+	       { 0xd316, 0x0059, 0xd316, 0x0059 } },
+	{   6, { 0x8ccd, 0x006d, 0x1400, 0x00a7 },
+	       { 0xa3e9, 0x0059, 0xa3e9, 0x0059 } },
+	{   7, { 0x4ccd, 0x006e, 0x1400, 0x00a7 },
+	       { 0x74ec, 0x0059, 0x74ec, 0x0059 } },
+	{   8, { 0x0ccd, 0x006f, 0x1400, 0x00a7 },
+	       { 0x4621, 0x0059, 0x4621, 0x0059 } },
+	{   9, { 0xcccd, 0x006f, 0x1400, 0x00a7 },
+	       { 0x1787, 0x0059, 0x1787, 0x0059 } },
+	{  10, { 0x8ccd, 0x0070, 0x1400, 0x00a7 },
+	       { 0xe91d, 0x0058, 0xe91d, 0x0058 } },
+	{  11, { 0x4ccd, 0x0071, 0x1400, 0x00a7 },
+	       { 0xbae3, 0x0058, 0xbae3, 0x0058 } },
+	{  12, { 0x0ccd, 0x0072, 0x1400, 0x00a7 },
+	       { 0x8cda, 0x0058, 0x8cda, 0x0058 } },
+	{  13, { 0xcccd, 0x0072, 0x1400, 0x00a7 },
+	       { 0x5f00, 0x0058, 0x5f00, 0x0058 } },
+	{  14, { 0x999a, 0x0074, 0x1400, 0x00a7 },
+	       { 0xf1b6, 0x0057, 0xf1b6, 0x0057 } },
+};

  parent reply	other threads:[~2026-11-12 17:57 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12 17:56 [PATCH RFC wireless-next 0/4] wifi: b43: AC-PHY support (series 4/4) Andreas Wendleder
2026-11-12 17:56 ` [PATCH RFC wireless-next 1/4] wifi: b43: radio 2069 and AC-PHY tables Andreas Wendleder
2026-11-12 17:56 ` Andreas Wendleder [this message]
2026-11-12 17:56 ` [PATCH RFC wireless-next 3/4] wifi: b43: 5 GHz, 80 MHz and VHT for the AC PHY Andreas Wendleder
2026-11-12 17:56 ` [PATCH RFC wireless-next 4/4] wifi: b43: enable the AC-PHY support Andreas Wendleder

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=20261112175656.40686-3-gonsolo@gmail.com \
    --to=gonsolo@gmail.com \
    --cc=alessio.ferri@mythread.it \
    --cc=b43-dev@lists.infradead.org \
    --cc=johannes@sipsolutions.net \
    --cc=jpeisach@ubuntu.com \
    --cc=julian.calaby@gmail.com \
    --cc=kvalo@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=m@bues.ch \
    --cc=rosenp@gmail.com \
    /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