Skip to contentSkip to conversation

wifi: rtw88: add RTL8723B/RTL8723BS support

PATCH Superseded Luka Gejak 3 participants 24 messages Sep 23 – Sep 30 Realtek wireless driver (rtw88)

v4 of 8 Superseded Latest is v8 →

lore
Share & tools
lore

Luka Gejak

author
#0 lore raw

v4 cover wifi: rtw88: add RTL8723B/RTL8723BS support

This is the second of two series adding support for the Realtek RTL8723B 802.11n chipset and its RTL8723BS SDIO variant to rtw88. The preparation series has now landed in rtw-next; this series contains only the chip register definitions, the BB/RF/AGC tables, the chip driver, the SDIO bind, the build glue and the MAINTAINERS entry.

The preparation series was posted separately as v11 with seven patches and is not part of this series. The six patches here apply on the rtw-next branch as it stands after those changes were merged, and they can be read on their own: apart from the register definitions in reg.h, the chip code adds nothing to the shared rtw88 core. The series is split by file and dependency so that the register definitions, tables, chip implementation, SDIO bind, build glue and MAINTAINERS changes can be reviewed independently.

The driver supports:

- RTL8723B / RTL8723BS, SDIO only;
- 2.4 GHz 802.11n, HT20 and HT40;
- one spatial stream;
- station mode.

Only station mode has been validated. The common rtw88 core advertises AP and IBSS for all chips, so those modes are advertised for RTL8723BS as well, but neither has been tested. An out-of-tree tester has reported AP mode associating without DHCP completing. The five additional SDIO IDs are included for parity with the existing staging driver; only the 0xb723 device has been exercised.

The chip driver requests:

rtw88/rtw8723b_fw.bin

This is the version 41 firmware extracted from the Realtek rtl8723bs vendor driver. It was submitted separately to linux-firmware with its extraction provenance and carries Ping-Ke Shih's Reviewed-by. There is no known native rtw88 RTL8723B firmware.

The implementation is based on the initial RTL8723B work by Michael Straube:

https://github.com/mistraube/rtw88/tree/rtl8723bs

Michael's Co-developed-by and Signed-off-by tags are present on the three chip-code patches containing code derived from that work.

The known limitations are unchanged. Bluetooth coexistence depends on the BT firmware's status reports. During A2DP streaming those reports identify a connected link but do not expose the active profile, so the WiFi coexistence logic cannot make a correct traffic-aware decision. WiFi throughput can fall sharply while audio remains smooth. This is documented rather than guessed around in the WiFi driver. The intermittent RTL8723BS firmware failure to leave LPS also remains a known limitation; instrumentation shows a firmware stall rather than a polling timeout that can be fixed by extending the budget.

Testing
=======

The exact six-commit branch builds with W=1 and links all rtw88 modules with real modpost. Sparse and smatch were run over the rtw88 directory. Smatch has no finding in the 8723B files; the run reports an existing container-of static assertion from include/net/neighbour.h while checking usb.c. checkpatch --strict is clean apart from the complex-macro report on TRANS_SEQ_END, which is an initializer macro that rtw8703b.c already defines in exactly the same way, and the "does MAINTAINERS need updating" reminder on the file-adding patches, which the last patch answers. git diff --check is clean and all six commits are GPG-signed.

Hardware validation was run at this exact branch, on top of the applied preparation series: scan, authentication, association, WPA2 key negotiation, DHCP, TCP and UDP traffic in both directions, scans under load, link down/up cycles, reconnects and five module reload cycles, with a clean dmesg: no warnings, no errors, and no TX-report, H2C or leave-LPS messages. An extended soak on the same build added twenty reload and reassociate cycles, longer TCP runs and UDP floods in both access categories, a 2000 packet ping run, ten scans while connected and idle periods with power save: nothing was lost, no queue stalled, and dmesg stayed clean. The test machine provides only s2idle, so suspend and resume are not covered.

Changes in v4:
- rebased onto the current rtw-next tip;
- patch 1 and patch 3 commit messages updated as requested;
- patch 4: dropped the comment above the five extra SDIO IDs;
- patch 6: the existing REALTEK WIRELESS DRIVER (rtw88) entry also gains

    an R: line;
  - register hex literals normalized to upper case (0x01e7 -> 0x01E7,
    0x082c -> 0x082C);
  - the Realtek copyright notices in rtw8723b.c, rtw8723b.h,
    rtw8723b_table.c and rtw8723b_table.h are corrected as requested, and
    the rtw8723b.h block is now multi-line like the one in rtw8723b.c;
  - removed comments that the code or the macro name already states,
    including the c2h_ra_report_size explanation, the "See commit
    c7706b1173c7" reference and the Tx Power Tracking section header;
    comment lines in the driver go from 237 to 232;
  - every multi-line comment now starts with an empty line, as coding
    style section 8 asks; eleven comments in the driver are affected,
    each one having had its text moved off the opening line;
  - removed the blank lines left inside six of the seven power-sequence
    tables, so all seven are dense;
  - the RCR comment now says why the value is assigned to hal.rcr rather
    than only written, because rtw_core_start() writes REG_RCR from it
    after power_on;
  - dropped rtw8723b_init_network_type() with MASK_NETTYPE, _NETTYPE() and
    NT_LINK_AP, because the core programs the same field with the same
    value;
  - dropped a duplicate rtw8723b_init_wmac_setting() call from
    rtw8723b_mac_init(), which the init sequence already calls;
  - dropped the file-local SDIO predicate in favour of the shared
    rtw_is_8723bs(), which rx.c, tx.c and sdio.c already use; seven call
    sites;
  - dropped the now-redundant single-statement braces in reassert_rx_path,
    and a duplicate REG_FWHW_TXQ_CTRL BIT(12) write that mac_init already
    does;
  - added a WLAN_ANT_SEL macro for the value the REG_LEDCFG2 write uses,
    BIT(7) | BIT(1), which was a literal before;
  - named the remaining magic values: the efuse grant path in reg.h
    (REG_BT_EFUSE_CTRL_8723B, BIT_BT_PWR_CUT, BIT_BT_OUT_ISO, the vendor's
    own wording), 0xffc00000 as OFDM_SWING_MASK with __ffs() for the
    shift, 0x3 as BIT_DIS_SECOND_CCA, and the two remaining REG_RF_CTRL
    literals as WLAN_RF_CTRL_ENABLE;
  - both usleep_range(1000, 1100) calls are now fsleep(1000);
  - the GNU elvis ?: is now max_t(u8, rtwdev->hal.rf_path_num, 1), which
    is what rtw88 uses elsewhere;
  - reverse x'mas declaration order fixed in eight functions;
  - rtw8723b_hw_spec regrouped to the rtw8822c_hw_spec layout, with
    rx_ldpc and tx_stbc moved out of the early flag block;

- two debug messages were missing their trailing newline.

Changes in v3:
- moved the chip series onto the rtw-next tip containing the applied preparation series, so no shared-core change is part of this series any more: fw, coex, mac80211, main, rx, sdio, tx and sec are untouched here and come from the preparation series, which also drops the unused rtw_coex_8723bs_ensure_pta_path() helper;
- split the previous chip commits into six file-oriented patches, following the series shape from the v2 review: the chip-only register definitions are separated from the chip body, and the body is split further into the tables, the chip driver and the SDIO bind;
- added Michael Straube's Co-developed-by and Signed-off-by to the three chip-code patches. v2 credited him in the commit message and in the copyright headers, but carried no tag;
- fixed the chip RCR to retain RX FCS handling: WLAN_RCR_CFG now sets BIT_APP_FCS, and every other bit of the value is unchanged. v2 instead carried a shared-core exception that stopped advertising RX_INCLUDES_FCS for this chip and special-cased the RX path. The chip driver sets the bit itself now, so the core keeps its unconditional contract;
- added rtw8723b_read_efuse(), which reuses the 8723x efuse reader and fills the chip-specific hardware capability. This chip has no firmware hardware feature report, so without it the usable stream count, antenna count and supported bandwidths stay zero;
- efuse access reuses rtw8723x_efuse_grant() instead of open-coding the switch, and only the chip's BT power-cut/output-isolation bit is written on top of it;
- power tracking now keeps the TXAGC remnant per path and programs the

    IQK matrix for the path being tracked, rather than always for path A;
  - removed the available-page-threshold and reserved-page helpers, which
    programmed thresholds that were already skipped on SDIO in v2;
  - set_channel_rf() programs both RF paths from a single path A read
    instead of read-modify-writing both paths and re-reading them
    afterwards;
  - path B's RF switch control is addressed through
    REG_FPGA0_XA_RF_SW_CTRL, the upper half of the word the shifted mask
    already selects; 0x0872 would be a 32-bit access on a 2-byte boundary;
  - IQK delays are sleepable, the IQK failure path logs with rtw_warn()
    instead of WARN(1, ...), and the RFK debug print uses REG_IQK_RDY
    instead of the literal 0xe90;
  - coexistence: raw addresses are replaced by the named registers they
    were already equal to (REG_BCN_CTRL, REG_BT_TDMA_TIME,
    REG_BT_STAT_CTRL, REG_GPIO_MUXCFG), the duplicated PTA pin, GNT_BT and
    enhanced-interrupt writes inside coex_set_rfe_type() are gone, and the
    antenna-switch registers use the mainline RFE naming;
  - dropped register definitions that no longer have a user
    (REG_BT_COEX_CTRL_8723B, REG_FPGA0_XB_RF_SW_CTRL, BIT_DIS_ATIM) and
    the two rtw8723b.h copies of registers rtw8703b.h already defines;
  - renamed the CCK swing tables from rtw8732b_* to rtw8723b_* and
    recorded why row 20 deliberately differs from the v5.2.17 vendor
    transcription; the values themselves are unchanged from v2;
  - added the five additional SDIO ids for parity with the staging driver,
    together with a comment that only 0xb723 has been verified;

- the remaining comment, naming and typo cleanups in the chip files. The build glue, MAINTAINERS entry, table contents and copyright headers are unchanged from v2.

Luka Gejak (6):
wifi: rtw88: 8723b: add the RTL8723B register definitions
wifi: rtw88: 8723b: add the RTL8723B BB, RF and AGC tables
wifi: rtw88: 8723b: add the RTL8723B chip driver
wifi: rtw88: 8723bs: add the RTL8723BS SDIO bind
wifi: rtw88: 8723bs: enable building the RTL8723BS driver
MAINTAINERS: add entry for the RTL8723B rtw88 driver

 MAINTAINERS                                   |    8 +
 drivers/net/wireless/realtek/rtw88/Kconfig    |   18 +
 drivers/net/wireless/realtek/rtw88/Makefile   |    6 +
 drivers/net/wireless/realtek/rtw88/reg.h      |   35 +
 drivers/net/wireless/realtek/rtw88/rtw8723b.c | 2831 +++++++++++++++++
 drivers/net/wireless/realtek/rtw88/rtw8723b.h |   13 +
 .../wireless/realtek/rtw88/rtw8723b_table.c   |  862 +++++
 .../wireless/realtek/rtw88/rtw8723b_table.h   |   18 +
 .../net/wireless/realtek/rtw88/rtw8723bs.c    |   60 +
 9 files changed, 3851 insertions(+)
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b.c
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b.h
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b_table.c
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b_table.h
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723bs.c
Signature · 1 line
2.55.0
author
#1 lore raw

PATCH v4 1/6 wifi: rtw88: 8723b: add the RTL8723B register definitions

Add the register offsets and bit definitions the RTL8723B driver uses:
the power and system function bits, the BT antenna and efuse control registers, the packet lifetime and MAC ID drop registers, the FPGA0 HSSI and RF switch control registers, and the RFE antenna path registers.

---
 drivers/net/wireless/realtek/rtw88/reg.h | 35 ++++++++++++++++++++++++
 1 file changed, 35 insertions(+)
drivers/net/wireless/realtek/rtw88/reg.h +35 -0
index 0b9ba6f37680..3bbcb1591e6f 100644
@@ -8,7 +8,9 @@
 #define REG_SYS_FUNC_EN		0x0002
 #define BIT_FEN_EN_25_1		BIT(13)
 #define BIT_FEN_ELDR		BIT(12)
+#define BIT_FEN_PPLL		BIT(7)
 #define BIT_FEN_PCIEA		BIT(6)
+#define BIT_FEN_DIO_PCIE	BIT(5)
 #define BIT_FEN_CPUEN		BIT(2)
 #define BIT_FEN_USBA		BIT(2)
 #define BIT_FEN_BB_GLB_RST	BIT(1)
@@ -19,6 +21,7 @@
 #define BIT_PFM_WOWL		BIT(3)
 #define BIT_APFM_OFFMAC		BIT(9)
 #define REG_APS_FSMCO		0x0004
+#define BIT_EN_PDN		BIT(4)
 #define APS_FSMCO_MAC_ENABLE	BIT(8)
 #define APS_FSMCO_MAC_OFF	BIT(9)
 #define APS_FSMCO_HW_POWERDOWN	BIT(15)
@@ -67,6 +70,9 @@
 #define BIT_SHIFT_LDO25_VOLTAGE	4
 #define BIT_LDO25_EN		BIT(7)
 
+#define REG_PWR_DATA		0x0038
+#define BIT_EEPRPAD_RFE_CTRL_EN	BIT(11)
+
 #define REG_ACLK_MON		0x3e
 
 #define REG_GPIO_MUXCFG		0x0040
@@ -105,6 +111,11 @@
 #define BIT_BTGP_SPI_EN		BIT(20)
 #define BIT_LED1DIS		BIT(15)
 #define BIT_SW_DPDT_SEL_DATA	BIT(0)
+#define REG_BT_ANT_SEL_8723B	0x0067
+/* 0x006b is the high byte of the 16 bit register at 0x006a. */
+#define REG_BT_EFUSE_CTRL_8723B	0x006b
+#define BIT_BT_PWR_CUT		BIT(6)
+#define BIT_BT_OUT_ISO		BIT(7)
 #define REG_WL_BT_PWR_CTRL	0x0068
 #define BIT_BT_FUNC_EN		BIT(18)
 #define BIT_BT_DIG_CLK_EN	BIT(8)
@@ -301,6 +312,8 @@
 #define REG_HMEBOX3		0x01DC
 #define REG_LLT_INIT		0x01E0
 #define BIT_LLT_WRITE_ACCESS	BIT(30)
+#define REG_RFK_FW_ACK_8723B	0x01E7
+#define BIT_RFK_FW_ACK_8723B	BIT(0)
 #define REG_HMEBOX0_EX		0x01F0
 #define REG_HMEBOX1_EX		0x01F4
 #define REG_HMEBOX2_EX		0x01F8
@@ -429,6 +442,8 @@
 #define REG_ARFRH5		0x04A8
 #define REG_SW_AMPDU_BURST_MODE_CTRL 0x04BC
 #define BIT_PRE_TX_CMD		BIT(6)
+#define REG_PKT_VO_VI_LIFE_TIME	0x04C0
+#define REG_PKT_BE_BK_LIFE_TIME	0x04C2
 #define REG_QUEUE_CTRL		0x04C6
 #define BIT_PTA_WL_TX_EN	BIT(4)
 #define BIT_PTA_EDCCA_EN	BIT(5)
@@ -437,6 +452,8 @@
 #define REG_PROT_MODE_CTRL	0x04C8
 #define REG_MAX_AGGR_NUM	0x04CA
 #define REG_BAR_MODE_CTRL	0x04CC
+#define REG_MACID_PKT_DROP0	0x04D0
+#define REG_MACID_PKT_SLEEP	0x04D4
 #define REG_PRECNT_CTRL		0x04E5
 #define BIT_BTCCA_CTRL		(BIT(0) | BIT(1))
 #define BIT_EN_PRECNT		BIT(11)
@@ -497,6 +514,8 @@
 #define REG_TIMER0_SRC_SEL	0x05B4
 #define BIT_TSFT_SEL_TIMER0	(BIT(4) | BIT(5) | BIT(6))
 
+#define REG_BWOPMODE		0x0603
+#define BIT_BWOPMODE_20MHZ	BIT(2)
 #define REG_TCR			0x0604
 #define BIT_PWRMGT_HWDATA_EN	BIT(7)
 #define BIT_TCR_UPDATE_TIMIE	BIT(5)
@@ -520,6 +539,7 @@
 #define BIT_UC_MD_EN		BIT(16)
 #define BIT_RXSK_PERPKT		BIT(15)
 #define BIT_HTC_LOC_CTRL	BIT(14)
+#define BIT_AMF			BIT(13)
 #define BIT_RPFM_CAM_ENABLE	BIT(12)
 #define BIT_TA_BCN		BIT(11)
 #define BIT_RCR_ADF		BIT(11)
@@ -546,6 +566,7 @@
 #define REG_ACKTO		0x0640
 #define REG_EIFS		0x0642
 #define REG_NAV_CTRL		0x0650
+#define REG_NAV_UPPER		0x0652
 #define REG_WMAC_TRXPTCL_CTL	0x0668
 #define BIT_RFMOD		(BIT(7) | BIT(8))
 #define BIT_RFMOD_80M		BIT(8)
@@ -581,6 +602,7 @@
 #define REG_BT_COEX_V2		0x0762
 #define BIT_GNT_BT_POLARITY	BIT(12)
 #define BIT_LTE_COEX_EN		BIT(7)
+#define REG_BT_CONTROL_8723B	0x0764
 #define REG_GNT_BT		0x0765
 #define BIT_PTA_SW_CTL		GENMASK(4, 3)
 #define REG_BT_COEX_ENH_INTR_CTRL	0x76E
@@ -606,12 +628,20 @@
 #define BIT_RX_PSEL_RST		(BIT(28) | BIT(29))
 #define REG_TXPSEL		0x080C
 #define REG_RX_GAIN_EN		0x081c
+#define REG_FPGA0_XA_HSSI_PARM2	0x0824
+#define HSSI_3WIRE_ADDR_LEN	0x400
+#define HSSI_3WIRE_DATA_LEN	0x800
 #define REG_CCASEL		0x082C
+#define REG_FPGA0_XB_HSSI_PARM2	0x082C
 #define REG_PDMFTH		0x0830
 #define REG_BWINDICATION	0x0834
 #define REG_CCA2ND		0x0838
 #define REG_L1PKTH		0x0848
 #define REG_CLKTRK		0x0860
+#define REG_FPGA0_XA_RF_INT_OE	0x0860
+#define REG_FPGA0_XB_RF_INT_OE	0x0864
+#define REG_FPGA0_XA_RF_SW_CTRL	0x0870
+#define RFSI_RFENV		0x10
 #define REG_CSI_MASK_SETTING1	0x0874
 #define REG_NBI_SETTING		0x087c
 #define BIT_NBI_ENABLE		BIT(13)
@@ -630,6 +660,10 @@
 #define REG_PSD			0x0910
 #define BIT_PSD_INI		GENMASK(23, 22)
 #define REG_SINGLE_TONE_CONT_TX	0x0914
+#define REG_RFE_CTRL_ANTA_SRC	0x0930
+#define REG_RFE_CTRL_ANT_SW	0x0944
+#define BIT_RFE_CTRL_ANT_SW_SEL	GENMASK(1, 0)
+#define BIT_BT_SEL_BY_WIFI_8723B	BIT(5)
 #define REG_AGC_TABLE		0x0958
 #define REG_RFE_CTRL_E		0x0974
 #define REG_2ND_CCA_CTRL	0x0976
@@ -683,6 +717,7 @@
 #define REG_RX_IQC_AB_A		0x0c10
 #define REG_RX_IQC_CD_A		0x0c14
 #define REG_TXSCALE_A		0x0c1c
+#define REG_B_RXIQI		0x0c1c
 #define BB_SWING_MASK		GENMASK(31, 21)
 #define REG_TX_AGC_A_CCK_11_CCK_1		0xc20
 #define REG_TX_AGC_A_OFDM18_OFDM6		0xc24
Signature · 1 line
2.55.0
#2 lore raw

Re: [PATCH v4 1/6] wifi: rtw88: 8723b: add the RTL8723B register definitions

Quoted 44 lines · Luka Gejak

Add the register offsets and bit definitions the RTL8723B driver uses:
the power and system function bits, the BT antenna and efuse control registers, the packet lifetime and MAC ID drop registers, the FPGA0 HSSI and RF switch control registers, and the RFE antenna path registers.

---
 drivers/net/wireless/realtek/rtw88/reg.h | 35 ++++++++++++++++++++++++
 1 file changed, 35 insertions(+)
drivers/net/wireless/realtek/rtw88/reg.h +6 -0
index 0b9ba6f37680..3bbcb1591e6f 100644
@@ -8,7 +8,9 @@
 #define REG_SYS_FUNC_EN		0x0002
 #define BIT_FEN_EN_25_1		BIT(13)
 #define BIT_FEN_ELDR		BIT(12)
+#define BIT_FEN_PPLL		BIT(7)
 #define BIT_FEN_PCIEA		BIT(6)
+#define BIT_FEN_DIO_PCIE	BIT(5)
 #define BIT_FEN_CPUEN		BIT(2)
 #define BIT_FEN_USBA		BIT(2)
 #define BIT_FEN_BB_GLB_RST	BIT(1)
@@ -19,6 +21,7 @@
 #define BIT_PFM_WOWL		BIT(3)
 #define BIT_APFM_OFFMAC		BIT(9)
 #define REG_APS_FSMCO		0x0004
+#define BIT_EN_PDN		BIT(4)
 #define APS_FSMCO_MAC_ENABLE	BIT(8)
 #define APS_FSMCO_MAC_OFF	BIT(9)
 #define APS_FSMCO_HW_POWERDOWN	BIT(15)
@@ -67,6 +70,9 @@
 #define BIT_SHIFT_LDO25_VOLTAGE	4
 #define BIT_LDO25_EN		BIT(7)
 
+#define REG_PWR_DATA		0x0038
+#define BIT_EEPRPAD_RFE_CTRL_EN	BIT(11)
+
 #define REG_ACLK_MON		0x3e
 
 #define REG_GPIO_MUXCFG		0x0040

drivers/net/wireless/realtek/rtw88/reg.h line 114

Quoted 4 lines
drivers/net/wireless/realtek/rtw88/reg.h +1 -0
@@ -105,6 +111,11 @@
 #define BIT_BTGP_SPI_EN		BIT(20)
 #define BIT_LED1DIS		BIT(15)
 #define BIT_SW_DPDT_SEL_DATA	BIT(0)
+#define REG_BT_ANT_SEL_8723B	0x0067

I think there is no need to add the _8723B suffix to these names.

Quoted 89 lines
drivers/net/wireless/realtek/rtw88/reg.h +23 -0
+/* 0x006b is the high byte of the 16 bit register at 0x006a. */
+#define REG_BT_EFUSE_CTRL_8723B	0x006b
+#define BIT_BT_PWR_CUT		BIT(6)
+#define BIT_BT_OUT_ISO		BIT(7)
 #define REG_WL_BT_PWR_CTRL	0x0068
 #define BIT_BT_FUNC_EN		BIT(18)
 #define BIT_BT_DIG_CLK_EN	BIT(8)
@@ -301,6 +312,8 @@
 #define REG_HMEBOX3		0x01DC
 #define REG_LLT_INIT		0x01E0
 #define BIT_LLT_WRITE_ACCESS	BIT(30)
+#define REG_RFK_FW_ACK_8723B	0x01E7
+#define BIT_RFK_FW_ACK_8723B	BIT(0)
 #define REG_HMEBOX0_EX		0x01F0
 #define REG_HMEBOX1_EX		0x01F4
 #define REG_HMEBOX2_EX		0x01F8
@@ -429,6 +442,8 @@
 #define REG_ARFRH5		0x04A8
 #define REG_SW_AMPDU_BURST_MODE_CTRL 0x04BC
 #define BIT_PRE_TX_CMD		BIT(6)
+#define REG_PKT_VO_VI_LIFE_TIME	0x04C0
+#define REG_PKT_BE_BK_LIFE_TIME	0x04C2
 #define REG_QUEUE_CTRL		0x04C6
 #define BIT_PTA_WL_TX_EN	BIT(4)
 #define BIT_PTA_EDCCA_EN	BIT(5)
@@ -437,6 +452,8 @@
 #define REG_PROT_MODE_CTRL	0x04C8
 #define REG_MAX_AGGR_NUM	0x04CA
 #define REG_BAR_MODE_CTRL	0x04CC
+#define REG_MACID_PKT_DROP0	0x04D0
+#define REG_MACID_PKT_SLEEP	0x04D4
 #define REG_PRECNT_CTRL		0x04E5
 #define BIT_BTCCA_CTRL		(BIT(0) | BIT(1))
 #define BIT_EN_PRECNT		BIT(11)
@@ -497,6 +514,8 @@
 #define REG_TIMER0_SRC_SEL	0x05B4
 #define BIT_TSFT_SEL_TIMER0	(BIT(4) | BIT(5) | BIT(6))
 
+#define REG_BWOPMODE		0x0603
+#define BIT_BWOPMODE_20MHZ	BIT(2)
 #define REG_TCR			0x0604
 #define BIT_PWRMGT_HWDATA_EN	BIT(7)
 #define BIT_TCR_UPDATE_TIMIE	BIT(5)
@@ -520,6 +539,7 @@
 #define BIT_UC_MD_EN		BIT(16)
 #define BIT_RXSK_PERPKT		BIT(15)
 #define BIT_HTC_LOC_CTRL	BIT(14)
+#define BIT_AMF			BIT(13)
 #define BIT_RPFM_CAM_ENABLE	BIT(12)
 #define BIT_TA_BCN		BIT(11)
 #define BIT_RCR_ADF		BIT(11)
@@ -546,6 +566,7 @@
 #define REG_ACKTO		0x0640
 #define REG_EIFS		0x0642
 #define REG_NAV_CTRL		0x0650
+#define REG_NAV_UPPER		0x0652
 #define REG_WMAC_TRXPTCL_CTL	0x0668
 #define BIT_RFMOD		(BIT(7) | BIT(8))
 #define BIT_RFMOD_80M		BIT(8)
@@ -581,6 +602,7 @@
 #define REG_BT_COEX_V2		0x0762
 #define BIT_GNT_BT_POLARITY	BIT(12)
 #define BIT_LTE_COEX_EN		BIT(7)
+#define REG_BT_CONTROL_8723B	0x0764
 #define REG_GNT_BT		0x0765
 #define BIT_PTA_SW_CTL		GENMASK(4, 3)
 #define REG_BT_COEX_ENH_INTR_CTRL	0x76E
@@ -606,12 +628,20 @@
 #define BIT_RX_PSEL_RST		(BIT(28) | BIT(29))
 #define REG_TXPSEL		0x080C
 #define REG_RX_GAIN_EN		0x081c
+#define REG_FPGA0_XA_HSSI_PARM2	0x0824
+#define HSSI_3WIRE_ADDR_LEN	0x400
+#define HSSI_3WIRE_DATA_LEN	0x800
 #define REG_CCASEL		0x082C
+#define REG_FPGA0_XB_HSSI_PARM2	0x082C
 #define REG_PDMFTH		0x0830
 #define REG_BWINDICATION	0x0834
 #define REG_CCA2ND		0x0838
 #define REG_L1PKTH		0x0848
 #define REG_CLKTRK		0x0860
+#define REG_FPGA0_XA_RF_INT_OE	0x0860
+#define REG_FPGA0_XB_RF_INT_OE	0x0864
+#define REG_FPGA0_XA_RF_SW_CTRL	0x0870
+#define RFSI_RFENV		0x10
 #define REG_CSI_MASK_SETTING1	0x0874
 #define REG_NBI_SETTING		0x087c
 #define BIT_NBI_ENABLE		BIT(13)

drivers/net/wireless/realtek/rtw88/reg.h line 666

Quoted 7 lines
drivers/net/wireless/realtek/rtw88/reg.h +4 -0
@@ -630,6 +660,10 @@
 #define REG_PSD			0x0910
 #define BIT_PSD_INI		GENMASK(23, 22)
 #define REG_SINGLE_TONE_CONT_TX	0x0914
+#define REG_RFE_CTRL_ANTA_SRC	0x0930
+#define REG_RFE_CTRL_ANT_SW	0x0944
+#define BIT_RFE_CTRL_ANT_SW_SEL	GENMASK(1, 0)
+#define BIT_BT_SEL_BY_WIFI_8723B	BIT(5)

This bit belongs under REG_BT_ANT_SEL_8723B.

Quoted 11 lines
drivers/net/wireless/realtek/rtw88/reg.h +1 -0
 #define REG_AGC_TABLE		0x0958
 #define REG_RFE_CTRL_E		0x0974
 #define REG_2ND_CCA_CTRL	0x0976
@@ -683,6 +717,7 @@
 #define REG_RX_IQC_AB_A		0x0c10
 #define REG_RX_IQC_CD_A		0x0c14
 #define REG_TXSCALE_A		0x0c1c
+#define REG_B_RXIQI		0x0c1c
 #define BB_SWING_MASK		GENMASK(31, 21)
 #define REG_TX_AGC_A_CCK_11_CCK_1		0xc20
 #define REG_TX_AGC_A_OFDM18_OFDM6		0xc24
author
#3 lore raw

PATCH v4 2/6 wifi: rtw88: 8723b: add the RTL8723B BB, RF and AGC tables

In reply to Luka Gejak

Add the baseband, RF and AGC parameter tables for the RTL8723B, and the transmit power limit and power by rate tables, in the layout the shared rtw88 phy code expects.

The RTL8723B chip support is based on the initial work by Michael Straube <[email protected]>.

---
 .../wireless/realtek/rtw88/rtw8723b_table.c   | 862 ++++++++++++++++++
 .../wireless/realtek/rtw88/rtw8723b_table.h   |  18 +
 2 files changed, 880 insertions(+)
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b_table.c
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b_table.h
drivers/net/wireless/realtek/rtw88/rtw8723b_table.c new file +862 -0
new file mode 100644
index 000000000000..bbb4935a1a01
@@ -0,0 +1,862 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright(c) 2026 Realtek Corporation
+ * Copyright(c) Michael Straube <[email protected]>
+ * Copyright(c) 2024-2026 Luka Gejak <[email protected]>
+ */
+
+#include "main.h"
+#include "phy.h"
+#include "rtw8723b_table.h"
+
+static const u32 rtw8723b_mac[] = {
+	0x02F, 0x00000030,
+	0x035, 0x00000000,
+	0x039, 0x00000008,
+	0x064, 0x00000000,
+	0x067, 0x00000020,
+	0x421, 0x0000000F,
+	0x428, 0x0000000A,
+	0x429, 0x00000010,
+	0x430, 0x00000000,
+	0x431, 0x00000000,
+	0x432, 0x00000000,
+	0x433, 0x00000001,
+	0x434, 0x00000004,
+	0x435, 0x00000005,
+	0x436, 0x00000007,
+	0x437, 0x00000008,
+	0x43C, 0x00000004,
+	0x43D, 0x00000005,
+	0x43E, 0x00000007,
+	0x43F, 0x00000008,
+	0x440, 0x0000005D,
+	0x441, 0x00000001,
+	0x442, 0x00000000,
+	0x444, 0x00000010,
+	0x445, 0x00000000,
+	0x446, 0x00000000,
+	0x447, 0x00000000,
+	0x448, 0x00000000,
+	0x449, 0x000000F0,
+	0x44A, 0x0000000F,
+	0x44B, 0x0000003E,
+	0x44C, 0x00000010,
+	0x44D, 0x00000000,
+	0x44E, 0x00000000,
+	0x44F, 0x00000000,
+	0x450, 0x00000000,
+	0x451, 0x000000F0,
+	0x452, 0x0000000F,
+	0x453, 0x00000000,
+	0x456, 0x0000005E,
+	0x460, 0x00000066,
+	0x461, 0x00000066,
+	0x4C8, 0x000000FF,
+	0x4C9, 0x00000008,
+	0x4CC, 0x000000FF,
+	0x4CD, 0x000000FF,
+	0x4CE, 0x00000001,
+	0x500, 0x00000026,
+	0x501, 0x000000A2,
+	0x502, 0x0000002F,
+	0x503, 0x00000000,
+	0x504, 0x00000028,
+	0x505, 0x000000A3,
+	0x506, 0x0000005E,
+	0x507, 0x00000000,
+	0x508, 0x0000002B,
+	0x509, 0x000000A4,
+	0x50A, 0x0000005E,
+	0x50B, 0x00000000,
+	0x50C, 0x0000004F,
+	0x50D, 0x000000A4,
+	0x50E, 0x00000000,
+	0x50F, 0x00000000,
+	0x512, 0x0000001C,
+	0x514, 0x0000000A,
+	0x516, 0x0000000A,
+	0x525, 0x0000004F,
+	0x550, 0x00000010,
+	0x551, 0x00000010,
+	0x559, 0x00000002,
+	0x55C, 0x00000050,
+	0x55D, 0x000000FF,
+	0x605, 0x00000030,
+	0x608, 0x0000000E,
+	0x609, 0x0000002A,
+	0x620, 0x000000FF,
+	0x621, 0x000000FF,
+	0x622, 0x000000FF,
+	0x623, 0x000000FF,
+	0x624, 0x000000FF,
+	0x625, 0x000000FF,
+	0x626, 0x000000FF,
+	0x627, 0x000000FF,
+	0x638, 0x00000050,
+	0x63C, 0x0000000A,
+	0x63D, 0x0000000A,
+	0x63E, 0x0000000E,
+	0x63F, 0x0000000E,
+	0x640, 0x00000040,
+	0x642, 0x00000040,
+	0x643, 0x00000000,
+	0x652, 0x000000C8,
+	0x66E, 0x00000005,
+	0x700, 0x00000021,
+	0x701, 0x00000043,
+	0x702, 0x00000065,
+	0x703, 0x00000087,
+	0x708, 0x00000021,
+	0x709, 0x00000043,
+	0x70A, 0x00000065,
+	0x70B, 0x00000087,
+	0x765, 0x00000018,
+	0x76E, 0x00000004,
+};
+
+RTW_DECL_TABLE_PHY_COND(rtw8723b_mac, rtw_phy_cfg_mac);
+
+static const u32 rtw8723b_agc[] = {
+	0xC78, 0xFD000001,
+	0xC78, 0xFC010001,
+	0xC78, 0xFB020001,
+	0xC78, 0xFA030001,
+	0xC78, 0xF9040001,
+	0xC78, 0xF8050001,
+	0xC78, 0xF7060001,
+	0xC78, 0xF6070001,
+	0xC78, 0xF5080001,
+	0xC78, 0xF4090001,
+	0xC78, 0xF30A0001,
+	0xC78, 0xF20B0001,
+	0xC78, 0xF10C0001,
+	0xC78, 0xF00D0001,
+	0xC78, 0xEF0E0001,
+	0xC78, 0xEE0F0001,
+	0xC78, 0xED100001,
+	0xC78, 0xEC110001,
+	0xC78, 0xEB120001,
+	0xC78, 0xEA130001,
+	0xC78, 0xE9140001,
+	0xC78, 0xE8150001,
+	0xC78, 0xE7160001,
+	0xC78, 0xE6170001,
+	0xC78, 0xE5180001,
+	0xC78, 0xE4190001,
+	0xC78, 0xE31A0001,
+	0xC78, 0xA51B0001,
+	0xC78, 0xA41C0001,
+	0xC78, 0xA31D0001,
+	0xC78, 0x671E0001,
+	0xC78, 0x661F0001,
+	0xC78, 0x65200001,
+	0xC78, 0x64210001,
+	0xC78, 0x63220001,
+	0xC78, 0x4A230001,
+	0xC78, 0x49240001,
+	0xC78, 0x48250001,
+	0xC78, 0x47260001,
+	0xC78, 0x46270001,
+	0xC78, 0x45280001,
+	0xC78, 0x44290001,
+	0xC78, 0x432A0001,
+	0xC78, 0x422B0001,
+	0xC78, 0x292C0001,
+	0xC78, 0x282D0001,
+	0xC78, 0x272E0001,
+	0xC78, 0x262F0001,
+	0xC78, 0x0A300001,
+	0xC78, 0x09310001,
+	0xC78, 0x08320001,
+	0xC78, 0x07330001,
+	0xC78, 0x06340001,
+	0xC78, 0x05350001,
+	0xC78, 0x04360001,
+	0xC78, 0x03370001,
+	0xC78, 0x02380001,
+	0xC78, 0x01390001,
+	0xC78, 0x013A0001,
+	0xC78, 0x013B0001,
+	0xC78, 0x013C0001,
+	0xC78, 0x013D0001,
+	0xC78, 0x013E0001,
+	0xC78, 0x013F0001,
+	0xC78, 0xFC400001,
+	0xC78, 0xFB410001,
+	0xC78, 0xFA420001,
+	0xC78, 0xF9430001,
+	0xC78, 0xF8440001,
+	0xC78, 0xF7450001,
+	0xC78, 0xF6460001,
+	0xC78, 0xF5470001,
+	0xC78, 0xF4480001,
+	0xC78, 0xF3490001,
+	0xC78, 0xF24A0001,
+	0xC78, 0xF14B0001,
+	0xC78, 0xF04C0001,
+	0xC78, 0xEF4D0001,
+	0xC78, 0xEE4E0001,
+	0xC78, 0xED4F0001,
+	0xC78, 0xEC500001,
+	0xC78, 0xEB510001,
+	0xC78, 0xEA520001,
+	0xC78, 0xE9530001,
+	0xC78, 0xE8540001,
+	0xC78, 0xE7550001,
+	0xC78, 0xE6560001,
+	0xC78, 0xE5570001,
+	0xC78, 0xE4580001,
+	0xC78, 0xE3590001,
+	0xC78, 0xA65A0001,
+	0xC78, 0xA55B0001,
+	0xC78, 0xA45C0001,
+	0xC78, 0xA35D0001,
+	0xC78, 0x675E0001,
+	0xC78, 0x665F0001,
+	0xC78, 0x65600001,
+	0xC78, 0x64610001,
+	0xC78, 0x63620001,
+	0xC78, 0x62630001,
+	0xC78, 0x61640001,
+	0xC78, 0x48650001,
+	0xC78, 0x47660001,
+	0xC78, 0x46670001,
+	0xC78, 0x45680001,
+	0xC78, 0x44690001,
+	0xC78, 0x436A0001,
+	0xC78, 0x426B0001,
+	0xC78, 0x286C0001,
+	0xC78, 0x276D0001,
+	0xC78, 0x266E0001,
+	0xC78, 0x256F0001,
+	0xC78, 0x24700001,
+	0xC78, 0x09710001,
+	0xC78, 0x08720001,
+	0xC78, 0x07730001,
+	0xC78, 0x06740001,
+	0xC78, 0x05750001,
+	0xC78, 0x04760001,
+	0xC78, 0x03770001,
+	0xC78, 0x02780001,
+	0xC78, 0x01790001,
+	0xC78, 0x017A0001,
+	0xC78, 0x017B0001,
+	0xC78, 0x017C0001,
+	0xC78, 0x017D0001,
+	0xC78, 0x017E0001,
+	0xC78, 0x017F0001,
+	0xC50, 0x69553422,
+	0xC50, 0x69553420,
+	0x824, 0x00390204,
+};
+
+RTW_DECL_TABLE_PHY_COND(rtw8723b_agc, rtw_phy_cfg_agc);
+
+static const u32 rtw8723b_bb[] = {
+	0x800, 0x80040000,
+	0x804, 0x00000003,
+	0x808, 0x0000FC00,
+	0x80C, 0x0000000A,
+	0x810, 0x10001331,
+	0x814, 0x020C3D10,
+	0x818, 0x02200385,
+	0x81C, 0x00000000,
+	0x820, 0x01000100,
+	0x824, 0x00190204,
+	0x828, 0x00000000,
+	0x82C, 0x00000000,
+	0x830, 0x00000000,
+	0x834, 0x00000000,
+	0x838, 0x00000000,
+	0x83C, 0x00000000,
+	0x840, 0x00010000,
+	0x844, 0x00000000,
+	0x848, 0x00000000,
+	0x84C, 0x00000000,
+	0x850, 0x00000000,
+	0x854, 0x00000000,
+	0x858, 0x569A11A9,
+	0x85C, 0x01000014,
+	0x860, 0x66F60110,
+	0x864, 0x061F0649,
+	0x868, 0x00000000,
+	0x86C, 0x27272700,
+	0x870, 0x07000760,
+	0x874, 0x25004000,
+	0x878, 0x00000808,
+	0x87C, 0x00000000,
+	0x880, 0xB0000C1C,
+	0x884, 0x00000001,
+	0x888, 0x00000000,
+	0x88C, 0xCCC000C0,
+	0x890, 0x00000800,
+	0x894, 0xFFFFFFFE,
+	0x898, 0x40302010,
+	0x89C, 0x00706050,
+	0x900, 0x00000000,
+	0x904, 0x00000023,
+	0x908, 0x00000000,
+	0x90C, 0x81121111,
+	0x910, 0x00000002,
+	0x914, 0x00000201,
+	0xA00, 0x00D047C8,
+	0xA04, 0x80FF800C,
+	0xA08, 0x8C838300,
+	0xA0C, 0x2E7F120F,
+	0xA10, 0x9500BB78,
+	0xA14, 0x1114D028,
+	0xA18, 0x00881117,
+	0xA1C, 0x89140F00,
+	0xA20, 0x1A1B0000,
+	0xA24, 0x090E1317,
+	0xA28, 0x00000204,
+	0xA2C, 0x00D30000,
+	0xA70, 0x101FBF00,
+	0xA74, 0x00000007,
+	0xA78, 0x00000900,
+	0xA7C, 0x225B0606,
+	0xA80, 0x21806490,
+	0xB2C, 0x00000000,
+	0xC00, 0x48071D40,
+	0xC04, 0x03A05611,
+	0xC08, 0x000000E4,
+	0xC0C, 0x6C6C6C6C,
+	0xC10, 0x08800000,
+	0xC14, 0x40000100,
+	0xC18, 0x08800000,
+	0xC1C, 0x40000100,
+	0xC20, 0x00000000,
+	0xC24, 0x00000000,
+	0xC28, 0x00000000,
+	0xC2C, 0x00000000,
+	0xC30, 0x69E9AC44,
+	0xC34, 0x469652AF,
+	0xC38, 0x49795994,
+	0xC3C, 0x0A97971C,
+	0xC40, 0x1F7C403F,
+	0xC44, 0x000100B7,
+	0xC48, 0xEC020107,
+	0xC4C, 0x007F037F,
+	0xC50, 0x69553420,
+	0xC54, 0x43BC0094,
+	0xC58, 0x00013147,
+	0xC5C, 0x00250492,
+	0xC60, 0x00000000,
+	0xC64, 0x5112848B,
+	0xC68, 0x47C00BFF,
+	0xC6C, 0x00000036,
+	0xC70, 0x2C7F000D,
+	0xC74, 0x020610DB,
+	0xC78, 0x0000001F,
+	0xC7C, 0x00B91612,
+	0xC80, 0x390000E4,
+	0xC84, 0x21F60000,
+	0xC88, 0x40000100,
+	0xC8C, 0x20200000,
+	0xC90, 0x00020E1A,
+	0xC94, 0x00000000,
+	0xC98, 0x00020E1A,
+	0xC9C, 0x00007F7F,
+	0xCA0, 0x00000000,
+	0xCA4, 0x000300A0,
+	0xCA8, 0x00000000,
+	0xCAC, 0x00000000,
+	0xCB0, 0x00000000,
+	0xCB4, 0x00000000,
+	0xCB8, 0x00000000,
+	0xCBC, 0x28000000,
+	0xCC0, 0x00000000,
+	0xCC4, 0x00000000,
+	0xCC8, 0x00000000,
+	0xCCC, 0x00000000,
+	0xCD0, 0x00000000,
+	0xCD4, 0x00000000,
+	0xCD8, 0x64B22427,
+	0xCDC, 0x00766932,
+	0xCE0, 0x00222222,
+	0xCE4, 0x00000000,
+	0xCE8, 0x37644302,
+	0xCEC, 0x2F97D40C,
+	0xD00, 0x00000740,
+	0xD04, 0x40020401,
+	0xD08, 0x0000907F,
+	0xD0C, 0x20010201,
+	0xD10, 0xA0633333,
+	0xD14, 0x3333BC53,
+	0xD18, 0x7A8F5B6F,
+	0xD2C, 0xCC979975,
+	0xD30, 0x00000000,
+	0xD34, 0x80608000,
+	0xD38, 0x00000000,
+	0xD3C, 0x00127353,
+	0xD40, 0x00000000,
+	0xD44, 0x00000000,
+	0xD48, 0x00000000,
+	0xD4C, 0x00000000,
+	0xD50, 0x6437140A,
+	0xD54, 0x00000000,
+	0xD58, 0x00000282,
+	0xD5C, 0x30032064,
+	0xD60, 0x4653DE68,
+	0xD64, 0x04518A3C,
+	0xD68, 0x00002101,
+	0xD6C, 0x2A201C16,
+	0xD70, 0x1812362E,
+	0xD74, 0x322C2220,
+	0xD78, 0x000E3C24,
+	0xE00, 0x2D2D2D2D,
+	0xE04, 0x2D2D2D2D,
+	0xE08, 0x0390272D,
+	0xE10, 0x2D2D2D2D,
+	0xE14, 0x2D2D2D2D,
+	0xE18, 0x2D2D2D2D,
+	0xE1C, 0x2D2D2D2D,
+	0xE28, 0x00000000,
+	0xE30, 0x1000DC1F,
+	0xE34, 0x10008C1F,
+	0xE38, 0x02140102,
+	0xE3C, 0x681604C2,
+	0xE40, 0x01007C00,
+	0xE44, 0x01004800,
+	0xE48, 0xFB000000,
+	0xE4C, 0x000028D1,
+	0xE50, 0x1000DC1F,
+	0xE54, 0x10008C1F,
+	0xE58, 0x02140102,
+	0xE5C, 0x28160D05,
+	0xE60, 0x00000048,
+	0xE68, 0x001B2556,
+	0xE6C, 0x00C00096,
+	0xE70, 0x00C00096,
+	0xE74, 0x01000056,
+	0xE78, 0x01000014,
+	0xE7C, 0x01000056,
+	0xE80, 0x01000014,
+	0xE84, 0x00C00096,
+	0xE88, 0x01000056,
+	0xE8C, 0x00C00096,
+	0xED0, 0x00C00096,
+	0xED4, 0x00C00096,
+	0xED8, 0x00C00096,
+	0xEDC, 0x000000D6,
+	0xEE0, 0x000000D6,
+	0xEEC, 0x01C00016,
+	0xF14, 0x00000003,
+	0xF4C, 0x00000000,
+	0xF00, 0x00000300,
+	0x820, 0x01000100,
+	0x800, 0x83040000,
+};
+
+RTW_DECL_TABLE_PHY_COND(rtw8723b_bb, rtw_phy_cfg_bb);
+
+static const struct rtw_phy_pg_cfg_pair rtw8723b_bb_pg[] = {
+	{ 0, 0, 0, 0x00000e08, 0x0000ff00, 0x00003800, },
+	{ 0, 0, 0, 0x0000086c, 0xffffff00, 0x32343600, },
+	{ 0, 0, 0, 0x00000e00, 0xffffffff, 0x40424444, },
+	{ 0, 0, 0, 0x00000e04, 0xffffffff, 0x28323638, },
+	{ 0, 0, 0, 0x00000e10, 0xffffffff, 0x38404244, },
+	{ 0, 0, 0, 0x00000e14, 0xffffffff, 0x26303436, },
+};
+
+RTW_DECL_TABLE_BB_PG(rtw8723b_bb_pg);
+
+static const u32 rtw8723b_rf_a[] = {
+	0x000, 0x00010000,
+	0x0B0, 0x000DFFE0,
+	0xFFE, 0x00000000,
+	0xFFE, 0x00000000,
+	0xFFE, 0x00000000,
+	0x0B1, 0x00000018,
+	0xFFE, 0x00000000,
+	0xFFE, 0x00000000,
+	0xFFE, 0x00000000,
+	0x0B2, 0x00084C00,
+	0x0B5, 0x0000D2CC,
+	0x0B6, 0x000925AA,
+	0x0B7, 0x00000010,
+	0x0B8, 0x0000907F,
+	0x05C, 0x00000002,
+	0x07C, 0x00000002,
+	0x07E, 0x00000005,
+	0x08B, 0x0006FC00,
+	0x0B0, 0x000FF9F0,
+	0x01C, 0x000739D2,
+	0x01E, 0x00000000,
+	0x0DF, 0x00000780,
+	0x050, 0x00067435,
+	0x80002000, 0x00000000, 0x40000000, 0x00000000,
+	0x051, 0x0006F10E,
+	0x052, 0x000007D3,
+	0x90003000, 0x00000000, 0x40000000, 0x00000000,
+	0x051, 0x0006F10E,
+	0x052, 0x000007D3,
+	0x90004000, 0x00000000, 0x40000000, 0x00000000,
+	0x051, 0x0006F10E,
+	0x052, 0x000007D3,
+	0xA0000000, 0x00000000,
+	0x051, 0x0006B04E,
+	0x052, 0x000007D2,
+	0xB0000000, 0x00000000,
+	0x053, 0x00000000,
+	0x054, 0x00050400,
+	0x055, 0x0004026E,
+	0x0DD, 0x0000004C,
+	0x070, 0x00067435,
+	0x80002000, 0x00000000, 0x40000000, 0x00000000,
+	0x071, 0x0006F10E,
+	0x072, 0x000007D3,
+	0x90003000, 0x00000000, 0x40000000, 0x00000000,
+	0x071, 0x0006F10E,
+	0x072, 0x000007D3,
+	0x90004000, 0x00000000, 0x40000000, 0x00000000,
+	0x071, 0x0006F10E,
+	0x072, 0x000007D3,
+	0xA0000000, 0x00000000,
+	0x071, 0x0006B04E,
+	0x072, 0x000007D2,
+	0xB0000000, 0x00000000,
+	0x073, 0x00000000,
+	0x074, 0x00050400,
+	0x075, 0x0004026E,
+	0x0EF, 0x00000100,
+	0x034, 0x0000ADD7,
+	0x035, 0x00005C00,
+	0x034, 0x00009DD4,
+	0x035, 0x00005000,
+	0x034, 0x00008DD1,
+	0x035, 0x00004400,
+	0x034, 0x00007DCE,
+	0x035, 0x00003800,
+	0x034, 0x00006CD1,
+	0x035, 0x00004400,
+	0x034, 0x00005CCE,
+	0x035, 0x00003800,
+	0x034, 0x000048CE,
+	0x035, 0x00004400,
+	0x034, 0x000034CE,
+	0x035, 0x00003800,
+	0x034, 0x00002451,
+	0x035, 0x00004400,
+	0x034, 0x0000144E,
+	0x035, 0x00003800,
+	0x034, 0x00000051,
+	0x035, 0x00004400,
+	0x0EF, 0x00000000,
+	0x0EF, 0x00000100,
+	0x0ED, 0x00000010,
+	0x044, 0x0000ADD7,
+	0x044, 0x00009DD4,
+	0x044, 0x00008DD1,
+	0x044, 0x00007DCE,
+	0x044, 0x00006CC1,
+	0x044, 0x00005CCE,
+	0x044, 0x000044D1,
+	0x044, 0x000034CE,
+	0x044, 0x00002451,
+	0x044, 0x0000144E,
+	0x044, 0x00000051,
+	0x0EF, 0x00000000,
+	0x0ED, 0x00000000,
+	0x07F, 0x00020080,
+	0x0EF, 0x00002000,
+	0x03B, 0x000389EF,
+	0x03B, 0x000302FE,
+	0x03B, 0x00028CE6,
+	0x03B, 0x000200BC,
+	0x03B, 0x000188A5,
+	0x03B, 0x00010FBC,
+	0x03B, 0x00008F71,
+	0x03B, 0x00000900,
+	0x0EF, 0x00000000,
+	0x0ED, 0x00000001,
+	0x040, 0x000380EF,
+	0x040, 0x000302FE,
+	0x040, 0x00028CE6,
+	0x040, 0x000200BC,
+	0x040, 0x000188A5,
+	0x040, 0x00010FBC,
+	0x040, 0x00008F71,
+	0x040, 0x00000900,
+	0x0ED, 0x00000000,
+	0x082, 0x00080000,
+	0x083, 0x00008000,
+	0x084, 0x00049F80,
+	0x085, 0x00068000,
+	0x0A2, 0x00080000,
+	0x0A3, 0x00008000,
+	0x0A4, 0x00048D80,
+	0x0A5, 0x00068000,
+	0x0ED, 0x00000002,
+	0x0EF, 0x00000002,
+	0x056, 0x00000032,
+	0x076, 0x00000032,
+	0x01F, 0x00001008,
+	0x001, 0x00000780,
+};
+
+RTW_DECL_TABLE_RF_RADIO(rtw8723b_rf_a, A);
+
+/*
+ * Regd: FCC -> 0, ETSI -> 2, MKK -> 1
+ * Band: 2.4G -> 0, 5G -> 1
+ * Bandwidth (bw): 20M -> 0, 40M -> 1, 80M -> 2, 160M -> 3
+ * Rate Section (rs): CCK -> 0, OFDM -> 1, HT -> 2, VHT -> 3
+ */
+static const struct rtw_txpwr_lmt_cfg_pair rtw8723b_txpwr_lmt[] = {
+	{0, 0, 0, 0, 1, 30},
+	{2, 0, 0, 0, 1, 26},
+	{1, 0, 0, 0, 1, 32},
+	{0, 0, 0, 0, 2, 30},
+	{2, 0, 0, 0, 2, 26},
+	{1, 0, 0, 0, 2, 32},
+	{0, 0, 0, 0, 3, 30},
+	{2, 0, 0, 0, 3, 26},
+	{1, 0, 0, 0, 3, 32},
+	{0, 0, 0, 0, 4, 30},
+	{2, 0, 0, 0, 4, 26},
+	{1, 0, 0, 0, 4, 32},
+	{0, 0, 0, 0, 5, 30},
+	{2, 0, 0, 0, 5, 26},
+	{1, 0, 0, 0, 5, 32},
+	{0, 0, 0, 0, 6, 30},
+	{2, 0, 0, 0, 6, 26},
+	{1, 0, 0, 0, 6, 32},
+	{0, 0, 0, 0, 7, 30},
+	{2, 0, 0, 0, 7, 26},
+	{1, 0, 0, 0, 7, 32},
+	{0, 0, 0, 0, 8, 30},
+	{2, 0, 0, 0, 8, 26},
+	{1, 0, 0, 0, 8, 32},
+	{0, 0, 0, 0, 9, 30},
+	{2, 0, 0, 0, 9, 26},
+	{1, 0, 0, 0, 9, 32},
+	{0, 0, 0, 0, 10, 30},
+	{2, 0, 0, 0, 10, 26},
+	{1, 0, 0, 0, 10, 32},
+	{0, 0, 0, 0, 11, 30},
+	{2, 0, 0, 0, 11, 26},
+	{1, 0, 0, 0, 11, 32},
+	{0, 0, 0, 0, 12, 63},
+	{2, 0, 0, 0, 12, 26},
+	{1, 0, 0, 0, 12, 32},
+	{0, 0, 0, 0, 13, 63},
+	{2, 0, 0, 0, 13, 26},
+	{1, 0, 0, 0, 13, 32},
+	{0, 0, 0, 0, 14, 63},
+	{2, 0, 0, 0, 14, 63},
+	{1, 0, 0, 0, 14, 32},
+	{0, 0, 0, 1, 1, 28},
+	{2, 0, 0, 1, 1, 28},
+	{1, 0, 0, 1, 1, 28},
+	{0, 0, 0, 1, 2, 28},
+	{2, 0, 0, 1, 2, 32},
+	{1, 0, 0, 1, 2, 32},
+	{0, 0, 0, 1, 3, 32},
+	{2, 0, 0, 1, 3, 32},
+	{1, 0, 0, 1, 3, 32},
+	{0, 0, 0, 1, 4, 32},
+	{2, 0, 0, 1, 4, 32},
+	{1, 0, 0, 1, 4, 32},
+	{0, 0, 0, 1, 5, 32},
+	{2, 0, 0, 1, 5, 32},
+	{1, 0, 0, 1, 5, 32},
+	{0, 0, 0, 1, 6, 32},
+	{2, 0, 0, 1, 6, 32},
+	{1, 0, 0, 1, 6, 32},
+	{0, 0, 0, 1, 7, 32},
+	{2, 0, 0, 1, 7, 32},
+	{1, 0, 0, 1, 7, 32},
+	{0, 0, 0, 1, 8, 32},
+	{2, 0, 0, 1, 8, 32},
+	{1, 0, 0, 1, 8, 32},
+	{0, 0, 0, 1, 9, 32},
+	{2, 0, 0, 1, 9, 32},
+	{1, 0, 0, 1, 9, 32},
+	{0, 0, 0, 1, 10, 28},
+	{2, 0, 0, 1, 10, 32},
+	{1, 0, 0, 1, 10, 32},
+	{0, 0, 0, 1, 11, 28},
+	{2, 0, 0, 1, 11, 32},
+	{1, 0, 0, 1, 11, 32},
+	{0, 0, 0, 1, 12, 63},
+	{2, 0, 0, 1, 12, 32},
+	{1, 0, 0, 1, 12, 32},
+	{0, 0, 0, 1, 13, 63},
+	{2, 0, 0, 1, 13, 28},
+	{1, 0, 0, 1, 13, 28},
+	{0, 0, 0, 1, 14, 63},
+	{2, 0, 0, 1, 14, 63},
+	{1, 0, 0, 1, 14, 63},
+	{0, 0, 0, 2, 1, 26},
+	{2, 0, 0, 2, 1, 26},
+	{1, 0, 0, 2, 1, 28},
+	{0, 0, 0, 2, 2, 26},
+	{2, 0, 0, 2, 2, 32},
+	{1, 0, 0, 2, 2, 32},
+	{0, 0, 0, 2, 3, 32},
+	{2, 0, 0, 2, 3, 32},
+	{1, 0, 0, 2, 3, 32},
+	{0, 0, 0, 2, 4, 32},
+	{2, 0, 0, 2, 4, 32},
+	{1, 0, 0, 2, 4, 32},
+	{0, 0, 0, 2, 5, 32},
+	{2, 0, 0, 2, 5, 32},
+	{1, 0, 0, 2, 5, 32},
+	{0, 0, 0, 2, 6, 32},
+	{2, 0, 0, 2, 6, 32},
+	{1, 0, 0, 2, 6, 32},
+	{0, 0, 0, 2, 7, 32},
+	{2, 0, 0, 2, 7, 32},
+	{1, 0, 0, 2, 7, 32},
+	{0, 0, 0, 2, 8, 32},
+	{2, 0, 0, 2, 8, 32},
+	{1, 0, 0, 2, 8, 32},
+	{0, 0, 0, 2, 9, 32},
+	{2, 0, 0, 2, 9, 32},
+	{1, 0, 0, 2, 9, 32},
+	{0, 0, 0, 2, 10, 26},
+	{2, 0, 0, 2, 10, 32},
+	{1, 0, 0, 2, 10, 32},
+	{0, 0, 0, 2, 11, 26},
+	{2, 0, 0, 2, 11, 32},
+	{1, 0, 0, 2, 11, 32},
+	{0, 0, 0, 2, 12, 63},
+	{2, 0, 0, 2, 12, 32},
+	{1, 0, 0, 2, 12, 32},
+	{0, 0, 0, 2, 13, 63},
+	{2, 0, 0, 2, 13, 26},
+	{1, 0, 0, 2, 13, 28},
+	{0, 0, 0, 2, 14, 63},
+	{2, 0, 0, 2, 14, 63},
+	{1, 0, 0, 2, 14, 63},
+	{0, 0, 0, 2, 1, 30},
+	{2, 0, 0, 2, 1, 32},
+	{1, 0, 0, 2, 1, 32},
+	{0, 0, 0, 2, 2, 32},
+	{2, 0, 0, 2, 2, 32},
+	{1, 0, 0, 2, 2, 32},
+	{0, 0, 0, 2, 3, 32},
+	{2, 0, 0, 2, 3, 32},
+	{1, 0, 0, 2, 3, 32},
+	{0, 0, 0, 2, 4, 32},
+	{2, 0, 0, 2, 4, 32},
+	{1, 0, 0, 2, 4, 32},
+	{0, 0, 0, 2, 5, 32},
+	{2, 0, 0, 2, 5, 32},
+	{1, 0, 0, 2, 5, 32},
+	{0, 0, 0, 2, 6, 32},
+	{2, 0, 0, 2, 6, 32},
+	{1, 0, 0, 2, 6, 32},
+	{0, 0, 0, 2, 7, 32},
+	{2, 0, 0, 2, 7, 32},
+	{1, 0, 0, 2, 7, 32},
+	{0, 0, 0, 2, 8, 32},
+	{2, 0, 0, 2, 8, 32},
+	{1, 0, 0, 2, 8, 32},
+	{0, 0, 0, 2, 9, 32},
+	{2, 0, 0, 2, 9, 32},
+	{1, 0, 0, 2, 9, 32},
+	{0, 0, 0, 2, 10, 32},
+	{2, 0, 0, 2, 10, 32},
+	{1, 0, 0, 2, 10, 32},
+	{0, 0, 0, 2, 11, 30},
+	{2, 0, 0, 2, 11, 32},
+	{1, 0, 0, 2, 11, 32},
+	{0, 0, 0, 2, 12, 63},
+	{2, 0, 0, 2, 12, 32},
+	{1, 0, 0, 2, 12, 32},
+	{0, 0, 0, 2, 13, 63},
+	{2, 0, 0, 2, 13, 32},
+	{1, 0, 0, 2, 13, 32},
+	{0, 0, 0, 2, 14, 63},
+	{2, 0, 0, 2, 14, 63},
+	{1, 0, 0, 2, 14, 63},
+	{0, 0, 1, 2, 1, 63},
+	{2, 0, 1, 2, 1, 63},
+	{1, 0, 1, 2, 1, 63},
+	{0, 0, 1, 2, 2, 63},
+	{2, 0, 1, 2, 2, 63},
+	{1, 0, 1, 2, 2, 63},
+	{0, 0, 1, 2, 3, 26},
+	{2, 0, 1, 2, 3, 26},
+	{1, 0, 1, 2, 3, 26},
+	{0, 0, 1, 2, 4, 26},
+	{2, 0, 1, 2, 4, 28},
+	{1, 0, 1, 2, 4, 26},
+	{0, 0, 1, 2, 5, 28},
+	{2, 0, 1, 2, 5, 28},
+	{1, 0, 1, 2, 5, 26},
+	{0, 0, 1, 2, 6, 28},
+	{2, 0, 1, 2, 6, 28},
+	{1, 0, 1, 2, 6, 26},
+	{0, 0, 1, 2, 7, 28},
+	{2, 0, 1, 2, 7, 28},
+	{1, 0, 1, 2, 7, 26},
+	{0, 0, 1, 2, 8, 26},
+	{2, 0, 1, 2, 8, 28},
+	{1, 0, 1, 2, 8, 26},
+	{0, 0, 1, 2, 9, 26},
+	{2, 0, 1, 2, 9, 28},
+	{1, 0, 1, 2, 9, 26},
+	{0, 0, 1, 2, 10, 26},
+	{2, 0, 1, 2, 10, 28},
+	{1, 0, 1, 2, 10, 26},
+	{0, 0, 1, 2, 11, 26},
+	{2, 0, 1, 2, 11, 26},
+	{1, 0, 1, 2, 11, 26},
+	{0, 0, 1, 2, 12, 63},
+	{2, 0, 1, 2, 12, 26},
+	{1, 0, 1, 2, 12, 26},
+	{0, 0, 1, 2, 13, 63},
+	{2, 0, 1, 2, 13, 26},
+	{1, 0, 1, 2, 13, 26},
+	{0, 0, 1, 2, 14, 63},
+	{2, 0, 1, 2, 14, 63},
+	{1, 0, 1, 2, 14, 63},
+	{0, 0, 1, 2, 1, 63},
+	{2, 0, 1, 2, 1, 63},
+	{1, 0, 1, 2, 1, 63},
+	{0, 0, 1, 2, 2, 63},
+	{2, 0, 1, 2, 2, 63},
+	{1, 0, 1, 2, 2, 63},
+	{0, 0, 1, 2, 3, 30},
+	{2, 0, 1, 2, 3, 30},
+	{1, 0, 1, 2, 3, 30},
+	{0, 0, 1, 2, 4, 32},
+	{2, 0, 1, 2, 4, 30},
+	{1, 0, 1, 2, 4, 30},
+	{0, 0, 1, 2, 5, 32},
+	{2, 0, 1, 2, 5, 30},
+	{1, 0, 1, 2, 5, 30},
+	{0, 0, 1, 2, 6, 32},
+	{2, 0, 1, 2, 6, 30},
+	{1, 0, 1, 2, 6, 30},
+	{0, 0, 1, 2, 7, 32},
+	{2, 0, 1, 2, 7, 30},
+	{1, 0, 1, 2, 7, 30},
+	{0, 0, 1, 2, 8, 32},
+	{2, 0, 1, 2, 8, 30},
+	{1, 0, 1, 2, 8, 30},
+	{0, 0, 1, 2, 9, 32},
+	{2, 0, 1, 2, 9, 30},
+	{1, 0, 1, 2, 9, 30},
+	{0, 0, 1, 2, 10, 32},
+	{2, 0, 1, 2, 10, 30},
+	{1, 0, 1, 2, 10, 30},
+	{0, 0, 1, 2, 11, 30},
+	{2, 0, 1, 2, 11, 30},
+	{1, 0, 1, 2, 11, 30},
+	{0, 0, 1, 2, 12, 63},
+	{2, 0, 1, 2, 12, 32},
+	{1, 0, 1, 2, 12, 32},
+	{0, 0, 1, 2, 13, 63},
+	{2, 0, 1, 2, 13, 32},
+	{1, 0, 1, 2, 13, 32},
+	{0, 0, 1, 2, 14, 63},
+	{2, 0, 1, 2, 14, 63},
+	{1, 0, 1, 2, 14, 63},
+};
+
+RTW_DECL_TABLE_TXPWR_LMT(rtw8723b_txpwr_lmt);
drivers/net/wireless/realtek/rtw88/rtw8723b_table.h new file +18 -0
new file mode 100644
index 000000000000..1242530425f2
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright(c) 2026 Realtek Corporation
+ * Copyright(c) Michael Straube <[email protected]>
+ * Copyright(c) 2024-2026 Luka Gejak <[email protected]>
+ */
+
+#ifndef __RTW8723B_TABLE_H__
+#define __RTW8723B_TABLE_H__
+
+extern const struct rtw_table rtw8723b_mac_tbl;
+extern const struct rtw_table rtw8723b_agc_tbl;
+extern const struct rtw_table rtw8723b_bb_tbl;
+extern const struct rtw_table rtw8723b_bb_pg_tbl;
+extern const struct rtw_table rtw8723b_rf_a_tbl;
+extern const struct rtw_table rtw8723b_txpwr_lmt_tbl;
+
+#endif
Signature · 1 line
2.55.0
author
#4 lore raw

PATCH v4 3/6 wifi: rtw88: 8723b: add the RTL8723B chip driver

In reply to Luka Gejak

Add the Realtek RTL8723B 802.11n chip driver: the chip operations, the power sequences, the efuse layout, the RF and IQ calibration, and the chip specific coexistence handling.

The RTL8723B chip support is based on the initial work by Michael Straube <[email protected]>.

---
 drivers/net/wireless/realtek/rtw88/rtw8723b.c | 2831 +++++++++++++++++
 drivers/net/wireless/realtek/rtw88/rtw8723b.h |   13 +
 2 files changed, 2844 insertions(+)
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b.c
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b.h
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +2831 -0
new file mode 100644
index 000000000000..a26908bdc989
@@ -0,0 +1,2831 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright(c) 2026 Realtek Corporation
+ * Copyright(c) Michael Straube <[email protected]>
+ * Copyright(c) 2024-2026 Luka Gejak <[email protected]>
+ */
+
+#include <linux/unaligned.h>
+#include "main.h"
+#include "coex.h"
+#include "fw.h"
+#include "mac.h"
+#include "phy.h"
+/*
+ * Shares the receive PHY status layout, the SDIO aggregation burst fields
+ * and a few baseband registers with the RTL8703B; reuse that header.
+ */
+#include "rtw8703b.h"
+#include "rtw8723b.h"
+#include "rtw8723b_table.h"
+#include "sdio.h"
+#include "tx.h"
+
+#define TRANS_SEQ_END			\
+	0xFFFF,				\
+	RTW_PWR_CUT_ALL_MSK,		\
+	RTW_PWR_INTF_ALL_MSK,		\
+	0,				\
+	RTW_PWR_CMD_END, 0, 0
+
+#define TBTT_PROHIBIT_SETUP_TIME		0x04
+#define TBTT_PROHIBIT_HOLD_TIME_STOP_BCN	0x64
+#define WLAN_BCN_DMA_TIME			0x02
+#define WLAN_BAR_VAL				0x0201ffff
+#define WLAN_SLOT_TIME				0x09
+#define WLAN_SYS_FUNC_BB_ENABLE			(BIT_FEN_BB_GLB_RST | \
+						 BIT_FEN_BB_RSTB)
+#define WLAN_RF_CTRL_ENABLE			(BIT_RF_EN | BIT_RF_RSTB | \
+						 BIT_RF_SDM_RSTB)
+/*
+ * 0x03a05611 is the normal RX path (staging and the BB table agree);
+ * 0x03a05600 is only an IQK temporary value and must not be reasserted.
+ */
+#define WLAN_RX_PATH_A_8723B			0x03a05611
+
+#define ADDA_ON_VAL_8723B			0x01c00014
+
+#define WLAN_RX_FILTER0			0xFFFF
+#define WLAN_RX_FILTER1			0x400
+#define WLAN_RX_FILTER2			0xFFFF
+/*
+ * Keep BIT_APP_FCS: rtw88 advertises RX_INCLUDES_FCS for every chip, and
+ * without it mac80211 trims four bytes of real frame data.
+ */
+#define WLAN_RCR_CFG			(BIT_APM | BIT_AM | BIT_AB | \
+					 BIT_CBSSID_DATA | BIT_CBSSID_BCN | \
+					 BIT_AMF | BIT_HTC_LOC_CTRL | \
+					 BIT_APP_PHYSTS | BIT_APP_ICV | \
+					 BIT_APP_MIC | BIT_APP_FCS)
+
+/*
+ * BIT(7) lets the 8051 control antenna selection, BIT(1) is LED2_CM.
+ */
+#define WLAN_ANT_SEL			(BIT(7) | BIT(1))
+
+#define IQK_DELAY_TIME_8723B		20
+
+#define BCNQ_PAGE_NUM_8723B	0x08
+#define BCNQ1_PAGE_NUM_8723B		0x00
+#define WOWLAN_PAGE_NUM_8723B		0x00
+#define TX_TOTAL_PAGE_NUMBER_8723B\
+	(0xFF - BCNQ_PAGE_NUM_8723B - BCNQ1_PAGE_NUM_8723B - \
+	 WOWLAN_PAGE_NUM_8723B)
+
+/* rssi in percent (dbm = % - 100); the values are the vendor driver's. */
+static const u8 wl_rssi_step_8723b[] = {60, 50, 44, 30};
+static const u8 bt_rssi_step_8723b[] = {30, 30, 30, 30};
+static const struct coex_5g_afh_map afh_5g_8723b[] = { {0, 0, 0} };
+
+static const struct coex_rf_para rf_para_tx_8723b[] = {
+	{0, 0, false, 7},  /* for normal */
+	{0, 10, false, 7}, /* for WL-CPT */
+	{1, 0, true, 4},
+	{1, 2, true, 4},
+	{1, 10, true, 4},
+	{1, 15, true, 4}
+};
+
+static const struct coex_rf_para rf_para_rx_8723b[] = {
+	{0, 0, false, 7},  /* for normal */
+	{0, 10, false, 7}, /* for WL-CPT */
+	{1, 0, true, 5},
+	{1, 2, true, 5},
+	{1, 10, true, 5},
+	{1, 15, true, 5}
+};
+
+static_assert(ARRAY_SIZE(rf_para_tx_8723b) == ARRAY_SIZE(rf_para_rx_8723b));
+
+static const u32 rtw8723b_ofdm_swing_table[] = {
+	0x0b40002d, /* 0, -15.0dB */
+	0x0c000030, /* 1, -14.5dB */
+	0x0cc00033, /* 2, -14.0dB */
+	0x0d800036, /* 3, -13.5dB */
+	0x0e400039, /* 4, -13.0dB */
+	0x0f00003c, /* 5, -12.5dB */
+	0x10000040, /* 6, -12.0dB */
+	0x11000044, /* 7, -11.5dB */
+	0x12000048, /* 8, -11.0dB */
+	0x1300004c, /* 9, -10.5dB */
+	0x14400051, /* 10, -10.0dB */
+	0x15800056, /* 11, -9.5dB */
+	0x16c0005b, /* 12, -9.0dB */
+	0x18000060, /* 13, -8.5dB */
+	0x19800066, /* 14, -8.0dB */
+	0x1b00006c, /* 15, -7.5dB */
+	0x1c800072, /* 16, -7.0dB */
+	0x1e400079, /* 17, -6.5dB */
+	0x20000080, /* 18, -6.0dB */
+	0x22000088, /* 19, -5.5dB */
+	0x24000090, /* 20, -5.0dB */
+	0x26000098, /* 21, -4.5dB */
+	0x288000a2, /* 22, -4.0dB */
+	0x2ac000ab, /* 23, -3.5dB */
+	0x2d4000b5, /* 24, -3.0dB */
+	0x300000c0, /* 25, -2.5dB */
+	0x32c000cb, /* 26, -2.0dB */
+	0x35c000d7, /* 27, -1.5dB */
+	0x390000e4, /* 28, -1.0dB */
+	0x3c8000f2, /* 29, -0.5dB */
+	0x40000100, /* 30, +0dB */
+	0x43c0010f, /* 31, +0.5dB */
+	0x47c0011f, /* 32, +1.0dB */
+	0x4c000130, /* 33, +1.5dB */
+	0x50800142, /* 34, +2.0dB */
+	0x55400155, /* 35, +2.5dB */
+	0x5a400169, /* 36, +3.0dB */
+	0x5fc0017f, /* 37, +3.5dB */
+	0x65400195, /* 38, +4.0dB */
+	0x6b8001ae, /* 39, +4.5dB */
+	0x71c001c7, /* 40, +5.0dB */
+	0x788001e2, /* 41, +5.5dB */
+	0x7f8001fe, /* 42, +6.0dB */
+};
+
+static const u32 rtw8723b_cck_pwr_regs[] = {
+	0x0a22, 0x0a23, 0x0a24, 0x0a25, 0x0a26, 0x0a27, 0x0a28, 0x0a29,
+};
+
+/*
+ * Row 20 (-6.0 dB) intentionally does not match the v5.2.17 vendor driver,
+ * which has 0x1c, 0x1a, 0x18, 0x12, 0x0e, 0x08 there. Every other row agrees.
+ * The values below are what rtl8723be, the mainline driver for this same
+ * chip, uses at the same index, and they are also what the vendor's own
+ * cck_swing_table_ch1_ch13_92e and the staging rtl8723bs driver use. They
+ * also track the 0.5 dB step of the surrounding rows: against row 32 as 0 dB,
+ * 0x1b is within 0.06 of the ideal -6.0 dB value while 0x1c is 0.94 away,
+ * the largest error anywhere in the table. Treat the vendor row as the
+ * anomaly and do not "fix" this towards it.
+ */
+static const u8 rtw8723b_cck_swing_table_ch1_ch13[][8] = {
+	{0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01},	/* 0, -16.0dB */
+	{0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01},	/* 1, -15.5dB */
+	{0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01},	/* 2, -15.0dB */
+	{0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01},	/* 3, -14.5dB */
+	{0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01},	/* 4, -14.0dB */
+	{0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01},	/* 5, -13.5dB */
+	{0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01},	/* 6, -13.0dB */
+	{0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01},	/* 7, -12.5dB */
+	{0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01},	/* 8, -12.0dB */
+	{0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01},	/* 9, -11.5dB */
+	{0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01},	/* 10, -11.0dB */
+	{0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01},	/* 11, -10.5dB */
+	{0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01},	/* 12, -10.0dB */
+	{0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01},	/* 13, -9.5dB */
+	{0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01},	/* 14, -9.0dB */
+	{0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02},	/* 15, -8.5dB */
+	{0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01},	/* 16, -8.0dB */
+	{0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02},	/* 17, -7.5dB */
+	{0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02},	/* 18, -7.0dB */
+	{0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02},	/* 19, -6.5dB */
+	{0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02},	/* 20, -6.0dB */
+	{0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02},	/* 21, -5.5dB */
+	{0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02},	/* 22, -5.0dB */
+	{0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02},	/* 23, -4.5dB */
+	{0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02},	/* 24, -4.0dB */
+	{0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03},	/* 25, -3.5dB */
+	{0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03},	/* 26, -3.0dB */
+	{0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03},	/* 27, -2.5dB */
+	{0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03},	/* 28, -2.0dB */
+	{0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03},	/* 29, -1.5dB */
+	{0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03},	/* 30, -1.0dB */
+	{0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04},	/* 31, -0.5dB */
+	{0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04},	/* 32, +0dB */
+};
+
+static const u8 rtw8723b_cck_swing_table_ch14[][8] = {
+	{0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00},	/* 0, -16.0dB */
+	{0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 1, -15.5dB */
+	{0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 2, -15.0dB */
+	{0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 3, -14.5dB */
+	{0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 4, -14.0dB */
+	{0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00},	/* 5, -13.5dB */
+	{0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00},	/* 6, -13.0dB */
+	{0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00},	/* 7, -12.5dB */
+	{0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00},	/* 8, -12.0dB */
+	{0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00},	/* 9, -11.5dB */
+	{0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00},	/* 10, -11.0dB */
+	{0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00},	/* 11, -10.5dB */
+	{0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00},	/* 12, -10.0dB */
+	{0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00},	/* 13, -9.5dB */
+	{0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00},	/* 14, -9.0dB */
+	{0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00},	/* 15, -8.5dB */
+	{0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00},	/* 16, -8.0dB */
+	{0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00},	/* 17, -7.5dB */
+	{0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00},	/* 18, -7.0dB */
+	{0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00},	/* 19, -6.5dB */
+	{0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00},	/* 20, -6.0dB */
+	{0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00},	/* 21, -5.5dB */
+	{0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00},	/* 22, -5.0dB */
+	{0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00},	/* 23, -4.5dB */
+	{0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00},	/* 24, -4.0dB */
+	{0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00},	/* 25, -3.5dB */
+	{0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00},	/* 26, -3.0dB */
+	{0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00},	/* 27, -2.5dB */
+	{0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00},	/* 28, -2.0dB */
+	{0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00},	/* 29, -1.5dB */
+	{0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00},	/* 30, -1.0dB */
+	{0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00},	/* 31, -0.5dB */
+	{0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00},	/* 32, +0dB */
+};
+
+static_assert(ARRAY_SIZE(rtw8723b_cck_swing_table_ch1_ch13) ==
+	      ARRAY_SIZE(rtw8723b_cck_swing_table_ch14));
+
+#define RTW_OFDM_SWING_TABLE_SIZE	ARRAY_SIZE(rtw8723b_ofdm_swing_table)
+#define RTW_CCK_SWING_TABLE_SIZE	ARRAY_SIZE(rtw8723b_cck_swing_table_ch14)
+
+/*
+ * Swing index field of REG_OFDM_0_XA_TX_IQ_IMBALANCE; the table entries
+ * below hold the full register value, hence the shift by __ffs().
+ */
+#define OFDM_SWING_MASK			GENMASK(31, 22)
+
+static const struct rtw_pwr_seq_cmd trans_pre_enable_8723b[] = {
+	/* unlock ISO/CLK/power control register */
+	{REG_RSV_CTRL,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xff, 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8723b[] = {
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3) | BIT(7), 0},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_POLLING, BIT(1), BIT(1)},
+	{0x004A,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), 0},
+	{0x0023,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4), 0},
+	{0x0301,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_cardemu_to_act_8723b[] = {
+	{0x0020,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0067,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4), 0},
+	{0x0001,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_DELAY, 1, RTW_PWR_DELAY_MS},
+	{0x0000,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(5), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3) | BIT(2)), 0},
+	{0x0075,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0006,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, BIT(1), BIT(1)},
+	{0x0075,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0006,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(7), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4) | BIT(3), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, BIT(0), 0},
+	{0x0010,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(6), BIT(6)},
+	{0x0049,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0063,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0062,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0058,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x005A,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0068,
+	 RTW_PWR_CUT_TEST_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3), BIT(3)},
+	{0x0069,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(6), BIT(6)},
+	 {TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_act_to_lps_8723b[] = {
+	{0x0301,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0xFF},
+	{0x0522,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0xFF},
+	{0x05F8,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x05F9,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x05FA,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x05FB,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x0002,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0002,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_DELAY, 0, RTW_PWR_DELAY_US},
+	{0x0002,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0100,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0x03},
+	{0x0101,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0093,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0x00},
+	{0x0553,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(5), BIT(5)},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_act_to_reset_mcu_8723b[] = {
+	{REG_SYS_FUNC_EN + 1,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT_FEN_CPUEN, 0},
+	/* reset MCU ready */
+	{REG_MCUFW_CTRL,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xff, 0},
+	/* reset MCU IO wrapper */
+	{REG_RSV_CTRL + 1,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{REG_RSV_CTRL + 1,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 1},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_act_to_cardemu_8723b[] = {
+	{0x001F,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0},
+	{0x0049,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0006,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, BIT(1), 0},
+	{0x0010,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(6), 0},
+	{0x0000,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(5), BIT(5)},
+	{0x0020,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_cardemu_to_carddis_8723b[] = {
+	{0x0007,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0x20},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(2), BIT(2)},
+	{0x004A,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 1},
+	{0x0023,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4), BIT(4)},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_POLLING, BIT(1), 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd * const card_enable_flow_8723b[] = {
+	trans_pre_enable_8723b,
+	trans_carddis_to_cardemu_8723b,
+	trans_cardemu_to_act_8723b,
+	NULL
+};
+
+static const struct rtw_pwr_seq_cmd * const card_disable_flow_8723b[] = {
+	trans_act_to_lps_8723b,
+	trans_act_to_reset_mcu_8723b,
+	trans_act_to_cardemu_8723b,
+	trans_cardemu_to_carddis_8723b,
+	NULL
+};
+
+static const struct rtw_page_table page_table_8723b[] = {
+	{12, 2, 2, 0, 1}, /* SDIO */
+	{12, 2, 2, 0, 1},
+	{12, 2, 2, 0, 1},
+	{12, 2, 2, 0, 1},
+	{12, 2, 2, 0, 1},
+};
+
+static const struct rtw_rqpn rqpn_table_8723b[] = {
+	/* SDIO maps VO, MGMT and HI to the high queue. */
+	{RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* PCIE */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* USB bulkout 2 */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_HIGH,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* USB bulkout 3 */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* USB bulkout 4 */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+};
+
+static const u8 rtw8723b_pwrtrk_2gb_n[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 6, 6,
+	7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2gb_p[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+	9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2ga_n[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 6, 6,
+	7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2ga_p[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+	9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_b_n[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_b_p[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 7, 7,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_a_n[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_a_p[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 7, 7,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const struct rtw_pwr_track_tbl rtw8723b_rtw_pwr_track_tbl = {
+	.pwrtrk_2gb_n = rtw8723b_pwrtrk_2gb_n,
+	.pwrtrk_2gb_p = rtw8723b_pwrtrk_2gb_p,
+	.pwrtrk_2ga_n = rtw8723b_pwrtrk_2ga_n,
+	.pwrtrk_2ga_p = rtw8723b_pwrtrk_2ga_p,
+	.pwrtrk_2g_cckb_n = rtw8723b_pwrtrk_2g_cck_b_n,
+	.pwrtrk_2g_cckb_p = rtw8723b_pwrtrk_2g_cck_b_p,
+	.pwrtrk_2g_ccka_n = rtw8723b_pwrtrk_2g_cck_a_n,
+	.pwrtrk_2g_ccka_p = rtw8723b_pwrtrk_2g_cck_a_p,
+	/* rtw8723x_pwrtrack_set_xtal() is not used on this chip. */
+	.pwrtrk_xtal_n = NULL,
+	.pwrtrk_xtal_p = NULL,
+};
+
+static const struct rtw_rfe_def rtw8723b_rfe_defs[] = {
+	[0] = { .phy_pg_tbl	= &rtw8723b_bb_pg_tbl,
+		.txpwr_lmt_tbl	= &rtw8723b_txpwr_lmt_tbl,
+		.pwr_track_tbl	= &rtw8723b_rtw_pwr_track_tbl, },
+};
+
+/* Shared-Antenna Coex Table */
+static const struct coex_table_para table_sant_8723b[] = {
+	{0xffffffff, 0xffffffff}, /* case-0 */
+	{0x55555555, 0x55555555},
+	{0x66555555, 0x66555555},
+	{0xaaaaaaaa, 0xaaaaaaaa},
+	{0x5a5a5a5a, 0x5a5a5a5a},
+	{0xfafafafa, 0xfafafafa}, /* case-5 */
+	{0x6a5a5555, 0xaaaaaaaa},
+	{0x6a5a56aa, 0x6a5a56aa},
+	{0x6a5a5a5a, 0x6a5a5a5a},
+	{0x66555555, 0x5a5a5a5a},
+	{0x66555555, 0x6a5a5a5a}, /* case-10 */
+	{0x66555555, 0x6a5a5aaa},
+	{0x66555555, 0x5a5a5aaa},
+	{0x66555555, 0x6aaa5aaa},
+	{0x66555555, 0xaaaa5aaa},
+	{0x66555555, 0xaaaaaaaa}, /* case-15 */
+	{0xffff55ff, 0xfafafafa},
+	{0xffff55ff, 0x6afa5afa},
+	{0xaaffffaa, 0xfafafafa},
+	{0xaa5555aa, 0x5a5a5a5a},
+	{0xaa5555aa, 0x6a5a5a5a}, /* case-20 */
+	{0xaa5555aa, 0xaaaaaaaa},
+	{0xffffffff, 0x5a5a5a5a},
+	{0xffffffff, 0x5a5a5a5a},
+	{0xffffffff, 0x55555555},
+	{0xffffffff, 0x5a5a5aaa}, /* case-25 */
+	{0x55555555, 0x5a5a5a5a},
+	{0x55555555, 0xaaaaaaaa},
+	{0x55555555, 0x6a5a6a5a},
+	{0x66556655, 0x66556655},
+	{0x66556aaa, 0x6a5a6aaa}, /* case-30 */
+	{0xffffffff, 0x5aaa5aaa},
+	{0x56555555, 0x5a5a5aaa},
+};
+
+/* Non-Shared-Antenna Coex Table */
+static const struct coex_table_para table_nsant_8723b[] = {
+	{0xffffffff, 0xffffffff}, /* case-100 */
+	{0x55555555, 0x55555555},
+	{0x66555555, 0x66555555},
+	{0xaaaaaaaa, 0xaaaaaaaa},
+	{0x5a5a5a5a, 0x5a5a5a5a},
+	{0xfafafafa, 0xfafafafa}, /* case-105 */
+	{0x5afa5afa, 0x5afa5afa},
+	{0x55555555, 0xfafafafa},
+	{0x66555555, 0xfafafafa},
+	{0x66555555, 0x5a5a5a5a},
+	{0x66555555, 0x6a5a5a5a}, /* case-110 */
+	{0x66555555, 0xaaaaaaaa},
+	{0xffff55ff, 0xfafafafa},
+	{0xffff55ff, 0x5afa5afa},
+	{0xffff55ff, 0xaaaaaaaa},
+	{0xffff55ff, 0xffff55ff}, /* case-115 */
+	{0xaaffffaa, 0x5afa5afa},
+	{0xaaffffaa, 0xaaaaaaaa},
+	{0xffffffff, 0xfafafafa},
+	{0xffffffff, 0x5afa5afa},
+	{0xffffffff, 0xaaaaaaaa}, /* case-120 */
+	{0x55ff55ff, 0x5afa5afa},
+	{0x55ff55ff, 0xaaaaaaaa},
+	{0x55ff55ff, 0x55ff55ff}
+};
+
+/* Shared-Antenna TDMA */
+static const struct coex_tdma_para tdma_sant_8723b[] = {
+	{ {0x00, 0x00, 0x00, 0x00, 0x00} }, /* case-0 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-1 */
+	{ {0x61, 0x3a, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-5 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x3a, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-10 */
+	{ {0x61, 0x08, 0x03, 0x11, 0x14} },
+	{ {0x61, 0x08, 0x03, 0x10, 0x14} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x55} },
+	{ {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-15 */
+	{ {0x51, 0x45, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x3a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x30, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x20, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-20 */
+	{ {0x51, 0x4a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x0c, 0x03, 0x10, 0x54} },
+	{ {0x55, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x65, 0x10, 0x03, 0x11, 0x10} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x51} }, /* case-25 */
+	{ {0x51, 0x08, 0x03, 0x10, 0x50} },
+	{ {0x61, 0x08, 0x03, 0x11, 0x11} }
+};
+
+/* Non-Shared-Antenna TDMA */
+static const struct coex_tdma_para tdma_nsant_8723b[] = {
+	{ {0x00, 0x00, 0x00, 0x00, 0x01} }, /* case-100 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-101 */
+	{ {0x61, 0x3a, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-105 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x3a, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-110 */
+	{ {0x61, 0x08, 0x03, 0x11, 0x14} },
+	{ {0x61, 0x08, 0x03, 0x10, 0x14} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x55} },
+	{ {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-115 */
+	{ {0x51, 0x45, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x3a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x30, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x20, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-120 */
+	{ {0x51, 0x08, 0x03, 0x10, 0x50} }
+};
+
+static void rtw8723b_efuse_grant(struct rtw_dev *rtwdev, bool on)
+{
+	rtw_write8_set(rtwdev, REG_BT_EFUSE_CTRL_8723B,
+		       on ? BIT_BT_PWR_CUT : BIT_BT_OUT_ISO);
+
+	rtw8723x_efuse_grant(rtwdev, on);
+}
+
+static u8 rtw8723b_default_ofdm_index(struct rtw_dev *rtwdev)
+{
+	u32 val32;
+	u32 swing;
+	u8 i;
+
+	swing = rtw_read32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+				OFDM_SWING_MASK);
+
+	for (i = 0; i < RTW_OFDM_SWING_TABLE_SIZE; i++) {
+		val32 = rtw8723b_ofdm_swing_table[i];
+
+		if (val32 >= 0x100000)
+			val32 >>= __ffs(OFDM_SWING_MASK);
+
+		if (val32 == swing)
+			break;
+	}
+
+	if (i >= RTW_OFDM_SWING_TABLE_SIZE)
+		i = 30; /* 30, +0dB */
+
+	return i;
+}
+
+static u8 rtw8723b_default_cck_index(struct rtw_dev *rtwdev)
+{
+	u8 i;
+	u8 swing;
+
+	swing = rtw_read8(rtwdev, rtw8723b_cck_pwr_regs[0]);
+
+	for (i = 0; i < RTW_CCK_SWING_TABLE_SIZE; i++) {
+		if (rtw8723b_cck_swing_table_ch1_ch13[i][0] == swing)
+			break;
+	}
+
+	if (i >= RTW_CCK_SWING_TABLE_SIZE)
+		i = 20; /* 20, -6.0dB */
+
+	return i;
+}
+
+static void rtw8723b_pwrtrack_init(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u8 path;
+
+	dm_info->default_ofdm_index = rtw8723b_default_ofdm_index(rtwdev);
+	dm_info->default_cck_index = rtw8723b_default_cck_index(rtwdev);
+
+	/* Clear the OFDM remnant per path: tracking keeps one per path. */
+	for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
+		ewma_thermal_init(&dm_info->avg_thermal[path]);
+		dm_info->delta_power_index[path] = 0;
+		dm_info->txagc_remnant_ofdm[path] = 0;
+	}
+	dm_info->pwr_trk_triggered = false;
+	dm_info->pwr_trk_init_trigger = true;
+	dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k;
+	dm_info->txagc_remnant_cck = 0;
+}
+
+static void rtw8723b_sdio_restore_pad_ctrl(struct rtw_dev *rtwdev,
+					   bool keep_pta_owner)
+{
+	u32 before;
+	u32 after;
+
+	if (!rtw_is_8723bs(rtwdev))
+		return;
+
+	before = rtw_read32(rtwdev, REG_PAD_CTRL1);
+	after = before & ~(BIT_LNAON_WLBT_SEL | BIT_SW_DPDT_SEL_DATA);
+	if (keep_pta_owner)
+		after |= BIT_PAPE_WLBT_SEL;
+	else
+		after &= ~BIT_PAPE_WLBT_SEL;
+	if (after == before)
+		return;
+
+	rtw_write32(rtwdev, REG_PAD_CTRL1, after);
+}
+
+static void rtw8723b_post_enable_flow(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Enable falling edge triggered interrupts and GPIO9 interrupt mode.
+	 * The power-on sequence sets both, but it runs before the firmware is
+	 * downloaded, so reassert them here as the vendor driver does.
+	 */
+	rtw_write8_set(rtwdev, 0x0049, BIT(1));
+	rtw_write8_set(rtwdev, 0x0063, BIT(1));
+
+	rtw_write16_set(rtwdev, REG_APS_FSMCO, BIT_EN_PDN);
+
+	/*
+	 * Only OR in the missing enables: writing CR to zero would reset the
+	 * hardware free-page counters and leave TX DMA no pages to allocate.
+	 */
+	rtw_write16_set(rtwdev, REG_CR, MAC_TRX_ENABLE | BIT_MAC_SEC_EN |
+				       BIT_32K_CAL_TMR_EN);
+
+	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) {
+		rtw_write16_set(rtwdev, REG_PWR_DATA,
+				BIT_EEPRPAD_RFE_CTRL_EN);
+
+		/*
+		 * rtw_mac_power_on() sets PAD mux bits this chip must not have;
+		 * restore the SDIO PAD mux before RF and coex setup.
+		 */
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, false);
+	}
+
+	rtw_write8(rtwdev, REG_EARLY_MODE_CONTROL, 0);
+
+	/* Keep every MACID eligible for firmware-scheduled TX at power-on. */
+	rtw_write32(rtwdev, REG_MACID_PKT_DROP0, 0);
+	rtw_write32(rtwdev, REG_MACID_PKT_SLEEP, 0);
+}
+
+static void rtw8723b_phy_bb_config(struct rtw_dev *rtwdev)
+{
+	u8 xtal_cap;
+
+	/* Enable BB and RF */
+	rtw_write16_set(rtwdev, REG_SYS_FUNC_EN,
+			BIT_FEN_EN_25_1 | BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB);
+
+	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB)
+		rtw_write32(rtwdev, REG_BB_SEL_BTG, 0x0);
+	else
+		rtw_write32(rtwdev, REG_BB_SEL_BTG, 0x280);
+
+	/* Full write: preserving spuriously set bits can wedge the RF bus. */
+	rtw_write8(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+	fsleep(1000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK, 0x0780);
+	rtw_write8(rtwdev, REG_SYS_FUNC_EN,
+		   BIT_FEN_PPLL | BIT_FEN_PCIEA | BIT_FEN_DIO_PCIE |
+		   BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB);
+	rtw_write8(rtwdev, REG_AFE_CTRL1 + 1, 0x80);
+
+	xtal_cap = rtwdev->efuse.crystal_cap & 0x3f;
+	rtw_write32_mask(rtwdev,  REG_AFE_CTRL3, BIT_MASK_XTAL,
+			 xtal_cap | (xtal_cap << 6));
+}
+
+static void rtw8723b_phy_load_bb_tables(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	const struct rtw_rfe_def *rfe_def = rtw_get_rfe_def(rtwdev);
+
+	rtw_load_table(rtwdev, chip->bb_tbl);
+	rtw_load_table(rtwdev, chip->agc_tbl);
+	if (rfe_def && rfe_def->agc_btg_tbl)
+		rtw_load_table(rtwdev, rfe_def->agc_btg_tbl);
+}
+
+static void rtw8723b_phy_rf6052_config(struct rtw_dev *rtwdev)
+{
+	struct rtw_hal *hal = &rtwdev->hal;
+	u32 intf_s, intf_oe, hssi_2;
+	u32 val32, mask;
+	u8 path;
+
+	for (path = RF_PATH_A; path < hal->rf_path_num; path++) {
+		switch (path) {
+		case RF_PATH_A:
+			intf_s = REG_FPGA0_XA_RF_SW_CTRL;
+			intf_oe = REG_FPGA0_XA_RF_INT_OE;
+			hssi_2 = REG_FPGA0_XA_HSSI_PARM2;
+			mask = RFSI_RFENV;
+			break;
+		case RF_PATH_B:
+			/*
+			 * The path B switch control is the upper half of the
+			 * word at REG_FPGA0_XA_RF_SW_CTRL, which the shifted
+			 * mask already selects. Addressing 0x0872 directly
+			 * would be a 32-bit access on a 2-byte boundary and
+			 * would reach into the register that follows.
+			 */
+			intf_s = REG_FPGA0_XA_RF_SW_CTRL;
+			intf_oe = REG_FPGA0_XB_RF_INT_OE;
+			hssi_2 = REG_FPGA0_XB_HSSI_PARM2;
+			mask = RFSI_RFENV << 16;
+			break;
+		default:
+			rtw_err(rtwdev, "invalid rf path %c\n", path + 'A');
+			return;
+		}
+
+		val32 = rtw_read32_mask(rtwdev, intf_s, mask);
+
+		rtw_write32_mask(rtwdev, intf_oe, RFSI_RFENV << 16, 0x1);
+		udelay(1);
+
+		rtw_write32_mask(rtwdev, intf_oe, RFSI_RFENV, 0x1);
+		udelay(1);
+
+		rtw_write32_mask(rtwdev, hssi_2, HSSI_3WIRE_ADDR_LEN, 0x0);
+		udelay(1);
+
+		rtw_write32_mask(rtwdev, hssi_2, HSSI_3WIRE_DATA_LEN, 0x0);
+		udelay(1);
+
+		/* Path B has no table of its own; radio_a serves both paths. */
+		rtw_load_table(rtwdev, rtwdev->chip->rf_tbl[RF_PATH_A]);
+
+		rtw_write32_mask(rtwdev, intf_s, mask, val32);
+	}
+}
+
+static void rtw8723b_phy_lck(struct rtw_dev *rtwdev)
+{
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, 0x8c01);
+	fsleep(200 * 1000);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdffe0);
+}
+
+static void rtw8723b_phy_rf_config(struct rtw_dev *rtwdev)
+{
+	rtw8723b_phy_rf6052_config(rtwdev);
+
+	/* LCK must run as part of the RF configuration. */
+	rtw8723b_phy_lck(rtwdev);
+}
+
+static void rtw8723b_init_tx_buffer_boundary(struct rtw_dev *rtwdev)
+{
+	u8 val8 = TX_TOTAL_PAGE_NUMBER_8723B + 1;
+
+	rtw_write8(rtwdev, REG_BCNQ_BDNY, val8);
+	rtw_write8(rtwdev, REG_MGQ_BDNY, val8);
+	rtw_write8(rtwdev, REG_WMAC_LBK_BF_HD, val8);
+	rtw_write8(rtwdev, REG_TRXFF_BNDY, val8);
+	rtw_write8(rtwdev, REG_DWBCN0_CTRL + 1, val8);
+}
+
+static void rtw8723b_init_page_boundary(struct rtw_dev *rtwdev)
+{
+	/* Matches the value __priority_queue_cfg_legacy() programs here. */
+	rtw_write16(rtwdev, REG_TRXFF_BNDY + 2, 0x4000 - REPORT_BUF - 1);
+}
+
+static void rtw8723b_init_transfer_page_size(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_PBP, 0x11);
+}
+
+static void rtw8723b_init_driver_info_size(struct rtw_dev *rtwdev)
+{
+	/* NOTE: also is done in rtw_drv_info_cfg */
+	rtw_write8(rtwdev, REG_RX_DRVINFO_SZ, PHY_STATUS_SIZE);
+}
+
+static void rtw8723b_init_wmac_setting(struct rtw_dev *rtwdev)
+{
+	/*
+	 * The vendor's 8723x filter value plus BIT_APP_FCS, which rtw88
+	 * needs because it advertises RX_INCLUDES_FCS. It is assigned to
+	 * hal.rcr and not merely written because rtw_core_start()
+	 * rewrites REG_RCR from hal.rcr after power_on, which would undo
+	 * a register-only write; fw.c toggles BIT_CBSSID_BCN in it.
+	 */
+	rtwdev->hal.rcr = WLAN_RCR_CFG;
+	rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
+
+	rtw_write32(rtwdev, REG_MAR, 0xffffffff);
+	rtw_write32(rtwdev, REG_MAR + 4, 0xffffffff);
+
+	rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2);
+	rtw_write16(rtwdev, REG_RXFLTMAP1, WLAN_RX_FILTER1);
+	rtw_write16(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0);
+}
+
+static void rtw8723b_init_adaptive_ctrl(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Firmware validates RRSR at init: narrowing it to the mandatory
+	 * rates makes it drop all management TX, so keep the full set.
+	 */
+	rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, 0xffff1);
+	rtwdev->dm_info.rrsr_val_init = 0xffff1;
+	rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x3030);
+}
+
+static void rtw8723b_init_edca(struct rtw_dev *rtwdev)
+{
+	rtw_write16(rtwdev, REG_SPEC_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_MAC_SPEC_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_SIFS + 2, 0x100a);
+
+	/*
+	 * RESP_SIFS must leave the AP enough slack, or it times out before the
+	 * ACK reaches the air and the unicast handshake never completes.
+	 */
+	rtw_write16(rtwdev, REG_RESP_SIFS_CCK, 0x0808);
+	rtw_write16(rtwdev, REG_RESP_SIFS_OFDM, 0x0a0a);
+
+	/* TXOP */
+	rtw_write32(rtwdev, REG_EDCA_BE_PARAM, 0x005EA42B);
+	rtw_write32(rtwdev, REG_EDCA_BK_PARAM, 0x0000A44F);
+	rtw_write32(rtwdev, REG_EDCA_VI_PARAM, 0x005EA324);
+	rtw_write32(rtwdev, REG_EDCA_VO_PARAM, 0x002FA226);
+}
+
+static void rtw8723b_init_retry_function(struct rtw_dev *rtwdev)
+{
+	rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
+	rtw_write8(rtwdev, REG_ACKTO, 0x40);
+}
+
+static void rtw8723b_init_operation_mode(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_BWOPMODE, BIT_BWOPMODE_20MHZ);
+}
+
+static void rtw8723b_init_beacon_parameters(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Both ports get DIS_TSF_UDT and EN_BCN_FUNCTION only; DIS_BCNQ_SUB
+	 * belongs to AP/IBSS and must stay clear for station mode.
+	 */
+	rtw_write16(rtwdev, REG_BCN_CTRL,
+		    (BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION) |
+		    ((BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION) << 8));
+	rtw_write8(rtwdev, REG_TBTT_PROHIBIT, TBTT_PROHIBIT_SETUP_TIME);
+	rtw_write8(rtwdev, REG_TBTT_PROHIBIT + 1,
+		   TBTT_PROHIBIT_HOLD_TIME_STOP_BCN & 0xff);
+	rtw_write8(rtwdev, REG_TBTT_PROHIBIT + 2,
+		   (rtw_read8(rtwdev, REG_TBTT_PROHIBIT + 2) & 0xf0) |
+		   (TBTT_PROHIBIT_HOLD_TIME_STOP_BCN >> 8));
+
+	rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME);
+	/* Largest beacon AIFS: the chip does not contend before beaconing. */
+	rtw_write16(rtwdev, REG_BCNTCFG, 0x660F);
+}
+
+static void rtw8723b_init_burst_pkt_len(struct rtw_dev *rtwdev)
+{
+	rtw_write8_set(rtwdev, REG_SINGLE_AMPDU_CTRL, BIT_EN_SINGLE_APMDU);
+	rtw_write8(rtwdev, REG_RX_PKT_LIMIT, 0x18);
+	rtw_write8(rtwdev, REG_MAX_AGGR_NUM, 0x1F);
+	rtw_write8(rtwdev, REG_PIFS, 0x00);
+	rtw_write8_clr(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
+	rtw_write8(rtwdev, REG_AMPDU_MAX_TIME, 0x70);
+}
+
+static void rtw8723b_init_antenna_selection(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_LEDCFG2, WLAN_ANT_SEL);
+}
+
+#define RF_AC	0x00
+
+static void rtw8723b_lck(struct rtw_dev *rtwdev)
+{
+	u32 rf_mode = 0, lc_cal;
+	int ret;
+	u8 val_ctx;
+	u8 rf_val;
+
+	val_ctx = rtw_read8(rtwdev, REG_CTX);
+
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0)
+		rtw_write8(rtwdev, REG_CTX, val_ctx & ~BIT_MASK_CTX_TYPE);
+	else
+		rtw_write8(rtwdev, REG_TXPAUSE, 0xff);
+
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) {
+		/* 1. Read original RF mode */
+		rf_mode = rtw_read_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS);
+		/* 2. Set RF mode = standby mode */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS, (rf_mode & 0x8ffff) | 0x10000);
+	}
+
+	/* 3. Read RF reg18 */
+	lc_cal = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS);
+
+	/* 4. Set LC calibration begin	bit15 */
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS, lc_cal | BIT_LCK);
+
+	ret = read_poll_timeout(rtw_read_rf, rf_val, rf_val != 0x1,
+				10000, 1000000, false,
+			 rtwdev, RF_PATH_A, RF_CFGCH, BIT_LCK);
+	if (ret)
+		rtw_warn(rtwdev, "failed to poll LCK status bit\n");
+
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdffe0);
+
+	/* Restore original situation */
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) {
+		rtw_write8(rtwdev, REG_CTX, val_ctx);
+
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS, rf_mode);
+	} else {
+		rtw_write8(rtwdev, REG_TXPAUSE, 0x00);
+	}
+}
+
+static void rtw8723b_inform_rfk_status(struct rtw_dev *rtwdev, bool start)
+{
+	int ret;
+	u8 val8;
+
+	rtw_fw_inform_rfk_status(rtwdev, start);
+
+	if (!start)
+		return;
+
+	ret = read_poll_timeout(rtw_read8, val8,
+				val8 & BIT_RFK_FW_ACK_8723B,
+				50000, 400000, false,
+				rtwdev, REG_RFK_FW_ACK_8723B);
+	if (ret)
+		rtw_warn(rtwdev, "failed to poll firmware RFK start ack\n");
+}
+
+static int rtw8723b_mac_init(struct rtw_dev *rtwdev)
+{
+	rtw_write32(rtwdev, REG_INT_MIG, 0);
+	rtw_write32(rtwdev, REG_MCUTST_1, 0x0);
+
+	rtw_write8(rtwdev, REG_MISC_CTRL, BIT_DIS_SECOND_CCA);
+	rtw_write8(rtwdev, REG_2ND_CCA_CTRL, 0x0);
+
+	return 0;
+}
+
+static void rtw8723b_phy_set_param(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	u32 val32;
+
+	rtw8723b_post_enable_flow(rtwdev);
+
+	rtw_load_table(rtwdev, chip->mac_tbl);
+	rtw8723b_phy_bb_config(rtwdev);
+	rtw8723b_phy_load_bb_tables(rtwdev);
+	rtw8723b_phy_rf_config(rtwdev);
+
+	/* enable CCK and OFDM block */
+	rtw_write32_set(rtwdev, REG_FPGA0_RFMOD, BIT_CCKEN | BIT_OFDMEN);
+
+	rtw8723b_init_tx_buffer_boundary(rtwdev);
+	rtw8723b_init_page_boundary(rtwdev);
+	rtw8723b_init_transfer_page_size(rtwdev);
+	rtw8723b_init_driver_info_size(rtwdev);
+
+	/* The MAC table sets REG_RCR and REG_MAR, so apply ours after it. */
+	rtw8723b_init_wmac_setting(rtwdev);
+
+	rtw8723b_init_adaptive_ctrl(rtwdev);
+	rtw8723b_init_edca(rtwdev);
+	rtw8723b_init_retry_function(rtwdev);
+
+	/* sdio.c sets the DMA mode, but not the burst parameters. */
+	rtw_write8(rtwdev, REG_RXDMA_MODE,
+		   BIT_DMA_MODE |
+		   FIELD_PREP_CONST(BIT_MASK_AGG_BURST_NUM, AGG_BURST_NUM) |
+		   FIELD_PREP_CONST(BIT_MASK_AGG_BURST_SIZE, AGG_BURST_SIZE));
+
+	rtw8723b_init_operation_mode(rtwdev);
+	rtw8723b_init_beacon_parameters(rtwdev);
+	rtw8723b_init_burst_pkt_len(rtwdev);
+
+	/*
+	 * 256 ms per-AC lifetime: the ROM default of ~1 s lets the chip retry
+	 * data frames past the EAPOL retry window and reorders the connect.
+	 */
+	rtw_write16(rtwdev, REG_PKT_VO_VI_LIFE_TIME, 0x0400);
+	rtw_write16(rtwdev, REG_PKT_BE_BK_LIFE_TIME, 0x0400);
+
+	rtw_write8(rtwdev, REG_SLOT, WLAN_SLOT_TIME);
+
+	/* disable BAR */
+	rtw_write32(rtwdev, REG_BAR_MODE_CTRL, WLAN_BAR_VAL);
+
+	/* Enable hardware sequence numbering for all queues. */
+	rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0xff);
+
+	/* Enable Rx DMA timer masking: clear 0x0[2:0] and 0x2[15:0] only. */
+	val32 = rtw_read32(rtwdev, REG_SDIO_TX_CTRL);
+	val32 &= 0x0000fff8;
+	rtw_write32(rtwdev, REG_SDIO_TX_CTRL, val32);
+
+	rtw_write16(rtwdev, REG_ATIMWND, 0x2);
+
+	rtw8723b_init_antenna_selection(rtwdev);
+
+	rtw_write8_set(rtwdev, REG_CR, BIT_MACTXEN | BIT_MACRXEN);
+
+	rtw_write8(rtwdev, REG_NAV_UPPER, 0xeb); /* ((30000 + 128 - 1) / 128) */
+
+	/* ack for xmit mgmt frames */
+	rtw_write32_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(12));
+
+	rtw_phy_init(rtwdev);
+
+	rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x50);
+	rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x20);
+
+	rtw8723b_pwrtrack_init(rtwdev);
+}
+
+static u32 rtw8723b_iqk_ant_switch_path(struct rtw_dev *rtwdev)
+{
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO)
+		return rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB ? 0x280 : 0x0;
+
+	/* Scan and connect use the PTA mux, so calibrate the path they use. */
+	return (rtwdev->efuse.bt_setting & BIT(6)) ? 0x80 : 0x200;
+}
+
+static void rtw8723b_reassert_rx_path(struct rtw_dev *rtwdev)
+{
+	u32 rf_wlint_before;
+	u32 rx_path_before;
+	u32 fpga0_before;
+	u8 sys_func_before;
+	u8 rf_ctrl_before;
+
+	if (!rtw_is_8723bs(rtwdev))
+		return;
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	rf_ctrl_before = rtw_read8(rtwdev, REG_RF_CTRL);
+	fpga0_before = rtw_read32(rtwdev, REG_FPGA0_RFMOD);
+	rx_path_before = rtw_read32(rtwdev, REG_BB_RX_PATH_11N);
+	rf_wlint_before = rtw_read_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK);
+
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
+	    WLAN_SYS_FUNC_BB_ENABLE)
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+
+	if ((rf_ctrl_before & WLAN_RF_CTRL_ENABLE) != WLAN_RF_CTRL_ENABLE) {
+		rtw_write8_set(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+		usleep_range(10, 11);
+	}
+
+	if ((fpga0_before & (BIT_CCKEN | BIT_OFDMEN)) !=
+	    (BIT_CCKEN | BIT_OFDMEN)) {
+		rtw_write32_set(rtwdev, REG_FPGA0_RFMOD,
+				BIT_CCKEN | BIT_OFDMEN);
+	}
+
+	if (rx_path_before != WLAN_RX_PATH_A_8723B) {
+		rtw_write32(rtwdev, REG_BB_RX_PATH_11N,
+			    WLAN_RX_PATH_A_8723B);
+	}
+
+	if (rf_wlint_before != 0x0780) {
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
+			     0x0780);
+	}
+}
+
+static void rtw8723b_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw)
+{
+	u32 rf_cfgch;
+
+	rf_cfgch = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK);
+
+	rf_cfgch &= ~RFCFGCH_CHANNEL_MASK;
+	rf_cfgch |= channel & RFCFGCH_CHANNEL_MASK;
+
+	rf_cfgch &= ~RFCFGCH_BW_MASK;
+	switch (bw) {
+	case RTW_CHANNEL_WIDTH_20:
+		rf_cfgch |= RFCFGCH_BW_20M;
+		break;
+	case RTW_CHANNEL_WIDTH_40:
+		rf_cfgch |= RFCFGCH_BW_40M;
+		break;
+	default:
+		break;
+	}
+
+	/* The vendor driver programs both paths from the path A value. */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, rf_cfgch);
+	if (rtwdev->hal.rf_path_num > 1)
+		rtw_write_rf(rtwdev, RF_PATH_B, RF_CFGCH, RFREG_MASK, rf_cfgch);
+}
+
+static void rtw8723b_set_channel_bb(struct rtw_dev *rtwdev, u8 bw,
+				    u8 primary_ch_idx)
+{
+	switch (bw) {
+	case RTW_CHANNEL_WIDTH_20:
+		rtw_write32_mask(rtwdev, REG_FPGA0_RFMOD, BIT_MASK_RFMOD, 0x0);
+		rtw_write32_mask(rtwdev, REG_FPGA1_RFMOD, BIT_MASK_RFMOD, 0x0);
+		rtw_write32_mask(rtwdev, REG_OFDM0_TX_PSD_NOISE,
+				 GENMASK(31, 30), 0x0);
+		break;
+	case RTW_CHANNEL_WIDTH_40:
+		rtw_write32_mask(rtwdev, REG_FPGA0_RFMOD, BIT_MASK_RFMOD, 0x1);
+		rtw_write32_mask(rtwdev, REG_FPGA1_RFMOD, BIT_MASK_RFMOD, 0x1);
+		rtw_write32_mask(rtwdev, REG_CCK0_SYS, BIT_CCK_SIDE_BAND,
+				 primary_ch_idx == RTW_SC_20_UPPER ? 1 : 0);
+		rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, 0xc00,
+				 primary_ch_idx == RTW_SC_20_UPPER ? 2 : 1);
+		rtw_write32_mask(rtwdev, REG_BB_PWR_SAV5_11N, GENMASK(27, 26),
+				 primary_ch_idx == RTW_SC_20_UPPER ? 1 : 2);
+		break;
+	default:
+		break;
+	}
+}
+
+static void rtw8723b_set_channel(struct rtw_dev *rtwdev, u8 channel,
+				 u8 bw, u8 primary_chan_idx)
+{
+	rtw8723b_set_channel_rf(rtwdev, channel, bw);
+	rtw_set_channel_mac(rtwdev, channel, bw, primary_chan_idx);
+	rtw8723b_set_channel_bb(rtwdev, bw, primary_chan_idx);
+	rtw8723b_reassert_rx_path(rtwdev);
+
+	if (rtw_is_8723bs(rtwdev)) {
+		bool keep_pta_owner;
+
+		keep_pta_owner = test_bit(RTW_FLAG_SCANNING, rtwdev->flags) ||
+				 (rtw_read32(rtwdev, REG_PAD_CTRL1) &
+				  BIT_PAPE_WLBT_SEL);
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, keep_pta_owner);
+
+		rtw_write8(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+		fsleep(1000);
+
+		/*
+		 * RF_WLINT bits 0-1 gate the data path into the BB and a prior
+		 * IQK or coex run can leave them blocking TX, so re-arm them.
+		 */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
+			     0x0780);
+	}
+}
+
+static s8 rtw8723b_cck_rx_power(u8 lna_idx, u8 vga_idx)
+{
+	s8 rx_power = 0;
+
+	switch (lna_idx) {
+	case 6:
+		rx_power = -40 - (2 * vga_idx);
+		break;
+	case 4:
+		rx_power = -20 - (2 * vga_idx);
+		break;
+	case 1:
+		rx_power = 0 - (2 * vga_idx);
+		break;
+	case 0:
+		rx_power = 10 - (2 * vga_idx);
+		break;
+	default:
+		break;
+	}
+
+	return rx_power;
+}
+
+static void rtw8723b_query_phy_status_cck(struct rtw_dev *rtwdev, u8 *phy_raw,
+					  struct rtw_rx_pkt_stat *pkt_stat)
+{
+	struct phy_status_8703b *phy_status = (struct phy_status_8703b *)phy_raw;
+	u8 lna_idx = (phy_status->cck_agc_rpt_ofdm_cfosho_a & 0xE0) >> 5;
+	u8 vga_idx = (phy_status->cck_agc_rpt_ofdm_cfosho_a & 0x1F);
+	s8 rx_power = rtw8723b_cck_rx_power(lna_idx, vga_idx);
+	s8 min_rx_power = -120;
+
+	pkt_stat->bw = RTW_CHANNEL_WIDTH_20;
+
+	pkt_stat->rx_power[RF_PATH_A] = rx_power;
+	pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1);
+	pkt_stat->signal_power = max(pkt_stat->rx_power[RF_PATH_A],
+				     min_rx_power);
+	rtwdev->dm_info.rssi[RF_PATH_A] = pkt_stat->rssi;
+}
+
+static void rtw8723b_query_phy_status_ofdm(struct rtw_dev *rtwdev, u8 *phy_raw,
+					   struct rtw_rx_pkt_stat *pkt_stat)
+{
+	struct phy_status_8703b *phy_status = (struct phy_status_8703b *)phy_raw;
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	s8 val_s8;
+
+	/* pkt_stat->bw comes from the RX descriptor, not the PHY status. */
+
+	val_s8 = phy_status->path_agc[RF_PATH_A].gain & 0x3F;
+	pkt_stat->rx_power[RF_PATH_A] = (val_s8 * 2) - 110;
+
+	pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1);
+	pkt_stat->rx_snr[RF_PATH_A] = (s8)(phy_status->path_rxsnr[RF_PATH_A] / 2);
+
+	/* signal power reported by HW */
+	val_s8 = phy_status->cck_sig_qual_ofdm_pwdb_all >> 1;
+	pkt_stat->signal_power = (val_s8 & 0x7f) - 110;
+
+	pkt_stat->rx_evm[RF_PATH_A] = phy_status->stream_rxevm[RF_PATH_A];
+	pkt_stat->cfo_tail[RF_PATH_A] = phy_status->path_cfotail[RF_PATH_A];
+
+	dm_info->curr_rx_rate = pkt_stat->rate;
+	dm_info->rssi[RF_PATH_A] = pkt_stat->rssi;
+	dm_info->rx_snr[RF_PATH_A] = pkt_stat->rx_snr[RF_PATH_A] >> 1;
+	dm_info->cfo_tail[RF_PATH_A] = (pkt_stat->cfo_tail[RF_PATH_A] * 5) >> 1;
+
+	val_s8 = (s8)pkt_stat->rx_evm[RF_PATH_A];
+	val_s8 = clamp_t(s8, -val_s8 >> 1, 0, 64);
+	val_s8 &= 0x3F; /* 64->0: second path of 1SS rate is 64 */
+	dm_info->rx_evm_dbm[RF_PATH_A] = val_s8;
+}
+
+static void rtw8723b_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
+				      struct rtw_rx_pkt_stat *pkt_stat)
+{
+	/*
+	 * The 8723B PHY status does not report the channel, so we must
+	 * mark it invalid to allow mac80211/rtw88 to parse it from the IE
+	 * during scanning.
+	 */
+	pkt_stat->channel_invalid = true;
+
+	if (pkt_stat->rate <= DESC_RATE11M)
+		rtw8723b_query_phy_status_cck(rtwdev, phy_status, pkt_stat);
+	else
+		rtw8723b_query_phy_status_ofdm(rtwdev, phy_status, pkt_stat);
+}
+
+static void rtw8723b_set_iqk_matrix_by_result(struct rtw_dev *rtwdev,
+					      u32 ofdm_swing, u8 path)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	s32 ele_A, ele_D, ele_C, ele_A_ext;
+	s32 iqk_result_x;
+	s32 iqk_result_y;
+	s32 value32;
+
+	switch (path) {
+	default:
+	case RF_PATH_A:
+		iqk_result_x = dm_info->iqk.result.s1_x;
+		iqk_result_y = dm_info->iqk.result.s1_y;
+		break;
+	case RF_PATH_B:
+		iqk_result_x = dm_info->iqk.result.s0_x;
+		iqk_result_y = dm_info->iqk.result.s0_y;
+		break;
+	}
+
+	/* new element D */
+	ele_D = OFDM_SWING_D(ofdm_swing);
+
+	/* new element A */
+	iqk_result_x = iqkxy_to_s32(iqk_result_x);
+	ele_A = iqk_mult(iqk_result_x, ele_D, &ele_A_ext);
+
+	/* new element C */
+	iqk_result_y = iqkxy_to_s32(iqk_result_y);
+	ele_C = iqk_mult(iqk_result_y, ele_D, NULL);
+
+	switch (path) {
+	case RF_PATH_A:
+	default:
+		/* write new elements A, C, D, element B is always 0 */
+		value32 = BIT_SET_TXIQ_ELM_ACD(ele_A, ele_C, ele_D);
+		rtw_write32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, value32);
+		value32 = BIT_SET_TXIQ_ELM_C1(ele_C);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS,
+				 value32);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(24),
+				 ele_A_ext);
+		break;
+
+	case RF_PATH_B:
+		/* write new elements A, C, D, element B is always 0 */
+		value32 = BIT_SET_TXIQ_ELM_ACD(ele_A, ele_C, ele_D);
+		rtw_write32(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE, value32);
+		value32 = BIT_SET_TXIQ_ELM_C1(ele_C);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXB_LSB2_11N, MASKH4BITS,
+				 value32);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(28),
+				 ele_A_ext);
+		break;
+	}
+}
+
+static void rtw8723b_set_iqk_matrix(struct rtw_dev *rtwdev, s8 ofdm_index,
+				    u8 path)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u32 ofdm_swing;
+
+	ofdm_index = clamp_t(s8, ofdm_index, 0, RTW_OFDM_SWING_TABLE_SIZE - 1);
+
+	ofdm_swing = rtw8723b_ofdm_swing_table[ofdm_index];
+
+	if (dm_info->iqk.done) {
+		rtw8723b_set_iqk_matrix_by_result(rtwdev, ofdm_swing, path);
+		return;
+	}
+
+	switch (path) {
+	case RF_PATH_A:
+	default:
+		rtw_write32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, ofdm_swing);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS,
+				 0x00);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(24),
+				 0x00);
+		break;
+
+	case RF_PATH_B:
+		rtw_write32(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE, ofdm_swing);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXB_LSB2_11N, MASKH4BITS,
+				 0x00);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(28),
+				 0x00);
+		break;
+	}
+}
+
+static u8 rtw8723b_iqk_check_tx_failed(struct rtw_dev *rtwdev)
+{
+	s32 tx_x, tx_y;
+	u32 tx_fail;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xeac = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RES_RY));
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xe94 = 0x%x, 0xe9c = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RES_TX),
+		rtw_read32(rtwdev, REG_IQK_RES_TY));
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] 0xe90(before IQK) = 0x%x, 0xe98(after IQK) = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RDY),
+		rtw_read32(rtwdev, 0xe98));
+
+	tx_fail = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_IQK_TX_FAIL);
+	tx_x = rtw_read32_mask(rtwdev, REG_IQK_RES_TX, BIT_MASK_RES_TX);
+	tx_y = rtw_read32_mask(rtwdev, REG_IQK_RES_TY, BIT_MASK_RES_TY);
+
+	if (!tx_fail && tx_x != IQK_TX_X_ERR && tx_y != IQK_TX_Y_ERR)
+		return IQK_TX_OK;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] A TX IQK failed\n");
+
+	return 0;
+}
+
+static u8 rtw8723b_iqk_check_rx_failed(struct rtw_dev *rtwdev)
+{
+	s32 rx_x, rx_y;
+	u32 rx_fail;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xea4 = 0x%x, 0xeac = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RES_RX),
+		rtw_read32(rtwdev, REG_IQK_RES_RY));
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] 0xea0(before IQK) = 0x%x, 0xea8(after IQK) = 0x%x\n",
+		rtw_read32(rtwdev, 0xea0),
+		rtw_read32(rtwdev, 0xea8));
+
+	rx_fail = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_IQK_RX_FAIL);
+	rx_x = rtw_read32_mask(rtwdev, REG_IQK_RES_RX, BIT_MASK_RES_RX);
+	rx_y = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_MASK_RES_RY);
+	rx_y = abs(iqkxy_to_s32(rx_y));
+
+	if (!rx_fail && rx_x != IQK_RX_X_ERR && rx_y != IQK_RX_Y_ERR &&
+	    rx_x < IQK_RX_X_UPPER && rx_x > IQK_RX_X_LOWER &&
+	     rx_y < IQK_RX_Y_LMT)
+		return IQK_RX_OK;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] A RX IQK failed\n");
+
+	return 0;
+}
+
+static u8 rtw8723b_iqk_tx_path_a(struct rtw_dev *rtwdev)
+{
+	u32 path_sel;
+	bool sdio_iqk = rtw_is_8723bs(rtwdev);
+	u8 status;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A TX IQK!\n");
+
+	/* Save RF path */
+	path_sel = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* enable path A PA in TX IQK mode */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x20000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0003f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xc7f87);
+
+	/* Tx IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00);
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N,
+		    sdio_iqk ? 0x821303ea : 0x821403ea);
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N, 0x28110000);
+	rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_B, 0x28110000);
+
+	/* LO calibration setting */
+	rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x00462911);
+
+	/* enter IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x808000);
+
+	/* ant switch */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG,
+		    rtw8723b_iqk_ant_switch_path(rtwdev));
+
+	/* GNT_BT = 0 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00000800);
+
+	/* One shot, path A LOK & IQK */
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf9000000);
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf8000000);
+
+	msleep(IQK_DELAY_TIME_8723B);
+
+	/* restore ant path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, path_sel);
+
+	/* GNT_BT = 1 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00001800);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */
+	status = rtw8723b_iqk_check_tx_failed(rtwdev);
+
+	return status;
+}
+
+static u8 rtw8723b_iqk_rx_path_a(struct rtw_dev *rtwdev)
+{
+	u32 reg_e94, reg_e9c, val32, path_sel;
+	bool sdio_iqk = rtw_is_8723bs(rtwdev);
+	u8 status;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK step1!\n");
+
+	/* Save RF path */
+	path_sel = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x30000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7fb7);
+
+	/* IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00);
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N,
+		    sdio_iqk ? 0x82130ff0 : 0x82160ff0);
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N, 0x28110000);
+	rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_B, 0x28110000);
+
+	/* LO calibration setting */
+	rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x0046a911);
+
+	/* enter IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x808000);
+
+	/* ant switch */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG,
+		    rtw8723b_iqk_ant_switch_path(rtwdev));
+
+	/* GNT_BT = 0 (disable BT) */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00000800);
+
+	/* One shot, path A LOK & IQK */
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf9000000);
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf8000000);
+
+	msleep(IQK_DELAY_TIME_8723B);
+
+	/* restore ant path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, path_sel);
+
+	/* GNT_BT = 1 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00001800);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */
+	status = rtw8723b_iqk_check_tx_failed(rtwdev);
+
+	/* if Tx not OK, ignore Rx */
+	if (!status)
+		return status;
+
+	reg_e94 = rtw_read32(rtwdev, REG_IQK_RES_TX);
+	reg_e9c = rtw_read32(rtwdev, REG_IQK_RES_TY);
+	val32 = 0x80007c00 | (reg_e94 & 0x3ff0000) |
+	((reg_e9c & 0x3ff0000) >> 16);
+	rtw_write32(rtwdev, REG_TXIQK_11N, val32);
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK step2!\n");
+
+	/* modify RX IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x30000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7d77);
+
+	/* PA, PAD setting */
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xdf, RFREG_MASK, 0xf80);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x55, RFREG_MASK, 0x4021f);
+
+	/* IQK setting */
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N,
+		    sdio_iqk ? 0x2813001f : 0x2816001f);
+	rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_B, 0x28110000);
+
+	/* LO calibration setting */
+	rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x0046a8d1);
+
+	/* enter IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x808000);
+
+	/* ant switch */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG,
+		    rtw8723b_iqk_ant_switch_path(rtwdev));
+
+	/* GNT_BT = 0 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00000800);
+
+	/* One shot, path A LOK & IQK */
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf9000000);
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf8000000);
+
+	msleep(IQK_DELAY_TIME_8723B);
+
+	/* restore ant path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, path_sel);
+
+	/* GNT_BT = 1 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00001800);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */
+
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xdf, RFREG_MASK, 0x780);
+
+	status |= rtw8723b_iqk_check_rx_failed(rtwdev);
+
+	return status;
+}
+
+static void
+rtw8723b_iqk_fill_a_matrix(struct rtw_dev *rtwdev, const s32 result[])
+{
+	s32 tx1_a, tx1_a_ext;
+	s32 tx1_c, tx1_c_ext;
+	s32 oldval_1;
+	s32 x, y;
+
+	if (result[IQK_S1_TX_X] == 0)
+		return;
+
+	oldval_1 = rtw_read32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+				   BIT_MASK_TXIQ_ELM_D);
+
+	x = iqkxy_to_s32(result[IQK_S1_TX_X]);
+	tx1_a = iqk_mult(x, oldval_1, &tx1_a_ext);
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_A, tx1_a);
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD,
+			 BIT_MASK_OFDM0_EXT_A, tx1_a_ext);
+
+	y = iqkxy_to_s32(result[IQK_S1_TX_Y]);
+	tx1_c = iqk_mult(y, oldval_1, &tx1_c_ext);
+	rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS,
+			 BIT_SET_TXIQ_ELM_C1(tx1_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_C, BIT_SET_TXIQ_ELM_C2(tx1_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD,
+			 BIT_MASK_OFDM0_EXT_C, tx1_c_ext);
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] X = 0x%x, TX1_A = 0x%x, oldval_1 0x%x\n",
+		x, tx1_a, oldval_1);
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] Y = 0x%x, TX1_C = 0x%x\n", y, tx1_c);
+
+	if (result[IQK_S1_RX_X] == 0)
+		return;
+
+	rtw_write32_mask(rtwdev, REG_A_RXIQI, BIT_MASK_RXIQ_S1_X,
+			 result[IQK_S1_RX_X]);
+	rtw_write32_mask(rtwdev, REG_A_RXIQI, BIT_MASK_RXIQ_S1_Y1,
+			 BIT_SET_RXIQ_S1_Y1(result[IQK_S1_RX_Y]));
+	rtw_write32_mask(rtwdev, REG_RXIQK_MATRIX_LSB_11N, BIT_MASK_RXIQ_S1_Y2,
+			 BIT_SET_RXIQ_S1_Y2(result[IQK_S1_RX_Y]));
+}
+
+static void
+rtw8723b_iqk_fill_b_matrix(struct rtw_dev *rtwdev, const s32 result[])
+{
+	s32 tx0_a, tx0_a_ext;
+	s32 tx0_c, tx0_c_ext;
+	s32 oldval_0;
+	s32 x, y;
+
+	if (result[IQK_S0_TX_X] == 0)
+		return;
+
+	oldval_0 = rtw_read32_mask(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE,
+				   BIT_MASK_TXIQ_ELM_D);
+
+	x = iqkxy_to_s32(result[IQK_S0_TX_X]);
+	tx0_a = iqk_mult(x, oldval_0, &tx0_a_ext);
+
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_A, tx0_a);
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(27),
+			 tx0_a_ext);
+
+	y = iqkxy_to_s32(result[IQK_S0_TX_Y]);
+	tx0_c = iqk_mult(y, oldval_0, &tx0_c_ext);
+
+	rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXB_LSB2_11N, MASKH4BITS,
+			 BIT_SET_TXIQ_ELM_C1(tx0_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_C, BIT_SET_TXIQ_ELM_C2(tx0_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(25),
+			 tx0_c_ext);
+
+	if (result[IQK_S0_RX_X] == 0)
+		return;
+
+	rtw_write32_mask(rtwdev, REG_B_RXIQI, BIT_MASK_RXIQ_X_S0,
+			 result[IQK_S0_RX_X]);
+	rtw_write32_mask(rtwdev, REG_B_RXIQI, BIT_MASK_RXIQ_S1_Y1,
+			 BIT_SET_RXIQ_S1_Y1(result[IQK_S0_RX_Y]));
+}
+
+static void
+rtw8723b_iqk_config_mac(struct rtw_dev *rtwdev,
+			const struct rtw8723x_iqk_backup_regs *backup)
+{
+	int i;
+
+	rtw_write8(rtwdev, rtw8723x_common.iqk_mac8_regs[0], 0x3f);
+
+	for (i = 1; i < RTW8723X_IQK_MAC8_REG_NUM; i++)
+		rtw_write8(rtwdev, rtw8723x_common.iqk_mac8_regs[i],
+			   backup->mac8[i] & (~BIT(3)));
+
+	/* This MAC backup register needs a byte-wide write. */
+	rtw_write8(rtwdev, rtw8723x_common.iqk_mac32_regs[0],
+		   backup->mac32[0] & (~BIT(5)));
+}
+
+static void
+rtw8723b_iqk_one_round(struct rtw_dev *rtwdev, s32 result[][IQK_NR], u8 t,
+		       const struct rtw8723x_iqk_backup_regs *backup)
+{
+	u32 i;
+	u8 a_ok;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] IQ Calibration for 1T1R_S0/S1 for %d times\n", t);
+
+	rtw8723x_iqk_path_adda_on(rtwdev, ADDA_ON_VAL_8723B);
+	rtw8723b_iqk_config_mac(rtwdev, backup);
+
+	rtw_write32_mask(rtwdev, REG_CCK_ANT_SEL_11N, 0x0f000000, 0xf);
+	rtw_write32(rtwdev, REG_BB_RX_PATH_11N, 0x03a05600);
+	rtw_write32(rtwdev, REG_TRMUX_11N, 0x000800e4);
+	rtw_write32(rtwdev, REG_BB_PWR_SAV1_11N, 0x22204000);
+
+	/*
+	 * RX IQ calibration setting for 8723B D cut large current issue
+	 * when leaving IPS
+	 */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x30000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7fb7);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xed, 0x20, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x43, RFREG_MASK, 0x60fbd);
+
+	for (i = 0; i < PATH_IQK_RETRY; i++) {
+		a_ok = rtw8723b_iqk_tx_path_a(rtwdev);
+		if (a_ok == IQK_TX_OK) {
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"[IQK] path A TX IQK success!\n");
+
+			rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N,
+					 MASKH3BYTES, 0x000000);
+
+			result[t][IQK_S1_TX_X] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_TX,
+						BIT_MASK_RES_TX);
+			result[t][IQK_S1_TX_Y] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_TY,
+						BIT_MASK_RES_TY);
+			break;
+		}
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A TX IQK fail!\n");
+		result[t][IQK_S1_TX_X] = 0x100;
+		result[t][IQK_S1_TX_Y] = 0x0;
+	}
+
+	for (i = 0; i < PATH_IQK_RETRY; i++) {
+		a_ok = rtw8723b_iqk_rx_path_a(rtwdev);
+		if (a_ok == (IQK_TX_OK | IQK_RX_OK)) {
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"[IQK] path A RX IQK success!\n");
+			result[t][IQK_S1_RX_X] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_RX,
+						BIT_MASK_RES_RX);
+			result[t][IQK_S1_RX_Y] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_RY,
+						BIT_MASK_RES_RY);
+			break;
+		}
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK fail!\n");
+		result[t][IQK_S1_RX_X] = 0x100;
+		result[t][IQK_S1_RX_Y] = 0x0;
+	}
+
+	if (a_ok == 0x0)
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A IQK fail!\n");
+
+	/* rtl8723b is 1T1R, so path B is not calibrated. */
+
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+}
+
+static void rtw8723b_phy_calibration(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw8723x_iqk_backup_regs backup;
+	s32 result[IQK_ROUND_SIZE][IQK_NR];
+	u32 bt_control;
+	bool good;
+	u8 final_candidate = IQK_ROUND_INVALID;
+	u8 i, j;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] Start!\n");
+	memset(result, 0, sizeof(result));
+
+	rtw8723b_lck(rtwdev);
+	rtw8723b_inform_rfk_status(rtwdev, true);
+
+	/* The LTE path GNT backup that 8723d does is not needed on SDIO. */
+	rtw8723x_iqk_backup_path_ctrl(rtwdev, &backup);
+	rtw8723x_iqk_backup_regs(rtwdev, &backup);
+
+	/* save default GNT_BT */
+	bt_control = rtw_read32(rtwdev, REG_BT_CONTROL_8723B);
+
+	for (i = IQK_ROUND_0; i <= IQK_ROUND_2; i++) {
+		if (!rtw_is_8723bs(rtwdev))
+			rtw8723x_iqk_config_path_ctrl(rtwdev);
+
+		rtw8723b_iqk_one_round(rtwdev, result, i, &backup);
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"[IQK] back to BB mode, load original value!\n");
+
+		if (i > IQK_ROUND_0) {
+			rtw8723x_iqk_restore_regs(rtwdev, &backup);
+
+			/* Restore RX initial gain */
+			rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x50);
+			rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, backup.igia);
+
+			/* load 0xe30 IQC default value */
+			rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x01008c00);
+			rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x01008c00);
+		}
+
+		if (!rtw_is_8723bs(rtwdev))
+			rtw8723x_iqk_restore_path_ctrl(rtwdev, &backup);
+
+		for (j = IQK_ROUND_0; j < i; j++) {
+			good = rtw8723x_iqk_similarity_cmp(rtwdev, result, j, i);
+			if (good) {
+				final_candidate = j;
+				rtw_dbg(rtwdev, RTW_DBG_RFK,
+					"[IQK] cmp %d:%d final_candidate is %x\n",
+					j, i, final_candidate);
+				goto iqk_done;
+			}
+		}
+	}
+
+	if (final_candidate == IQK_ROUND_INVALID) {
+		s32 reg_tmp = 0;
+
+		for (i = 0; i < IQK_NR; i++)
+			reg_tmp += result[IQK_ROUND_HYBRID][i];
+
+		if (reg_tmp != 0) {
+			final_candidate = IQK_ROUND_HYBRID;
+		} else {
+			rtw_warn(rtwdev, "IQK failed\n");
+			goto out;
+		}
+	}
+
+iqk_done:
+	if (result[final_candidate][IQK_S1_TX_X])
+		rtw8723b_iqk_fill_a_matrix(rtwdev, result[final_candidate]);
+	if (result[final_candidate][IQK_S0_TX_X])
+		rtw8723b_iqk_fill_b_matrix(rtwdev, result[final_candidate]);
+
+	dm_info->iqk.result.s1_x = result[final_candidate][IQK_S1_TX_X];
+	dm_info->iqk.result.s1_y = result[final_candidate][IQK_S1_TX_Y];
+	dm_info->iqk.result.s0_x = result[final_candidate][IQK_S0_TX_X];
+	dm_info->iqk.result.s0_y = result[final_candidate][IQK_S0_TX_Y];
+	dm_info->iqk.done = true;
+
+out:
+	/* restore RF path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, backup.bb_sel_btg);
+
+	/* restore GNT_BT */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, bt_control);
+
+	/* Restore RX mode table parameter */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x18000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xe6177);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xed, 0x20, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x43, RFREG_MASK, 0x300bd);
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] final_candidate is %x\n",
+		final_candidate);
+
+	for (i = IQK_ROUND_0; i < IQK_ROUND_SIZE; i++)
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"[IQK] Result %u: rege94_s1=%x rege9c_s1=%x regea4_s1=%x regeac_s1=%x rege94_s0=%x rege9c_s0=%x regea4_s0=%x regeac_s0=%x %s\n",
+			i,
+			result[i][0], result[i][1], result[i][2], result[i][3],
+			result[i][4], result[i][5], result[i][6], result[i][7],
+			final_candidate == i ? "(final candidate)" : "");
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK]0xc80 = 0x%x 0xc94 = 0x%x 0xc14 = 0x%x 0xca0 = 0x%x\n",
+	rtw_read32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE),
+		rtw_read32(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N),
+		rtw_read32(rtwdev, REG_A_RXIQI),
+		rtw_read32(rtwdev, REG_RXIQK_MATRIX_LSB_11N));
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK]0xcd0 = 0x%x 0xcd4 = 0x%x 0xcd8 = 0x%x\n",
+	rtw_read32(rtwdev, REG_TXIQ_AB_S0),
+		rtw_read32(rtwdev, REG_TXIQ_CD_S0),
+		rtw_read32(rtwdev, REG_RXIQ_AB_S0));
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] finished\n");
+
+	rtw8723b_inform_rfk_status(rtwdev, false);
+}
+
+static void rtw8723b_pwrtrack_set_ofdm_pwr(struct rtw_dev *rtwdev, u8 path,
+					   s8 swing_idx, s8 txagc_idx)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	dm_info->txagc_remnant_ofdm[path] = txagc_idx;
+
+	rtw8723b_set_iqk_matrix(rtwdev, swing_idx, path);
+}
+
+static void rtw8723b_pwrtrack_set_cck_pwr(struct rtw_dev *rtwdev, s8 swing_idx,
+					  s8 txagc_idx)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	dm_info->txagc_remnant_cck = txagc_idx;
+
+	swing_idx = clamp_t(s8, swing_idx, 0, RTW_CCK_SWING_TABLE_SIZE - 1);
+
+	BUILD_BUG_ON(ARRAY_SIZE(rtw8723b_cck_pwr_regs) !=
+		     ARRAY_SIZE(rtw8723b_cck_swing_table_ch1_ch13[0]));
+
+	/* Only ch1-13 is wired up; channel 14 is Japan-only and unreachable. */
+	for (int i = 0; i < ARRAY_SIZE(rtw8723b_cck_pwr_regs); i++)
+		rtw_write8(rtwdev, rtw8723b_cck_pwr_regs[i],
+			   rtw8723b_cck_swing_table_ch1_ch13[swing_idx][i]);
+}
+
+static void rtw8723b_pwrtrack_set(struct rtw_dev *rtwdev, u8 path)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_hal *hal = &rtwdev->hal;
+	u8 limit_ofdm;
+	/* 8703b and 8723d seem to use RTW_CCK_SWING_TABLE_SIZE */
+	u8 limit_cck = 28; /* -2dB */
+	s8 final_ofdm_swing_index;
+	s8 final_cck_swing_index;
+
+	limit_ofdm = rtw8723x_pwrtrack_get_limit_ofdm(rtwdev);
+
+	final_ofdm_swing_index = dm_info->default_ofdm_index +
+				 dm_info->delta_power_index[path];
+	final_cck_swing_index = dm_info->default_cck_index +
+				dm_info->delta_power_index[path];
+
+	if (final_ofdm_swing_index > limit_ofdm)
+		rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, path, limit_ofdm,
+					       final_ofdm_swing_index - limit_ofdm);
+	else if (final_ofdm_swing_index < 0)
+		rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, path, 0,
+					       final_ofdm_swing_index);
+	else
+		rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, path, final_ofdm_swing_index, 0);
+
+	if (final_cck_swing_index > limit_cck)
+		rtw8723b_pwrtrack_set_cck_pwr(rtwdev, limit_cck,
+					      final_cck_swing_index - limit_cck);
+	else if (final_cck_swing_index < 0)
+		rtw8723b_pwrtrack_set_cck_pwr(rtwdev, 0,
+					      final_cck_swing_index);
+	else
+		rtw8723b_pwrtrack_set_cck_pwr(rtwdev, final_cck_swing_index, 0);
+
+	rtw_phy_set_tx_power_level(rtwdev, hal->current_channel);
+}
+
+static void rtw8723b_phy_pwrtrack(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_swing_table swing_table;
+	u8 thermal_value, delta, path;
+	bool do_iqk = false;
+
+	rtw_phy_config_swing_table(rtwdev, &swing_table);
+
+	if (rtwdev->efuse.thermal_meter[0] == 0xff)
+		return;
+
+	thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00);
+
+	/* Average the thermal meter readings. */
+	rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A);
+
+	do_iqk = rtw_phy_pwrtrack_need_iqk(rtwdev);
+
+	if (do_iqk)
+		rtw8723x_lck(rtwdev);
+
+	if (dm_info->pwr_trk_init_trigger)
+		dm_info->pwr_trk_init_trigger = false;
+	else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value,
+						   RF_PATH_A))
+		goto iqk;
+
+	delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A);
+
+	/* NOTE: also done in rtw_phy_pwrtrack_get_delta */
+	delta = min_t(u8, delta, RTW_PWR_TRK_TBL_SZ - 1);
+
+	for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
+		s8 delta_cur, delta_last;
+
+		delta_last = dm_info->delta_power_index[path];
+		delta_cur = rtw_phy_pwrtrack_get_pwridx(rtwdev, &swing_table,
+							path, RF_PATH_A, delta);
+		if (delta_last == delta_cur)
+			continue;
+
+		dm_info->delta_power_index[path] = delta_cur;
+		rtw8723b_pwrtrack_set(rtwdev, path);
+	}
+
+iqk:
+	if (do_iqk)
+		rtw8723b_phy_calibration(rtwdev);
+}
+
+static void rtw8723b_pwr_track(struct rtw_dev *rtwdev)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	if (efuse->power_track_type != 0) {
+		rtw_warn(rtwdev, "unsupported power track type\n");
+		return;
+	}
+
+	if (!dm_info->pwr_trk_triggered) {
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER,
+			     GENMASK(17, 16), 0x03);
+		dm_info->pwr_trk_triggered = true;
+		return;
+	}
+
+	rtw8723b_phy_pwrtrack(rtwdev);
+	dm_info->pwr_trk_triggered = false;
+}
+
+/*
+ * The vendor sequence for this chip differs from rtw8723x_coex_cfg_init():
+ * REG_BT_TDMA_TIME is written whole, PTA pins and REG_QUEUE_CTRL untouched.
+ */
+static void rtw8723b_coex_cfg_init(struct rtw_dev *rtwdev)
+{
+	/* enable TBTT interrupt */
+	rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION);
+
+	/* BT report packet sample rate, 0x790[5:0] = 0x5 */
+	rtw_write8(rtwdev, REG_BT_TDMA_TIME, 0x5);
+
+	/* enable BT counter statistics */
+	rtw_write8(rtwdev, REG_BT_STAT_CTRL, 0x1);
+
+	/* enable PTA (3-wire function from BT side) */
+	rtw_write8_mask(rtwdev, REG_GPIO_MUXCFG, BIT_BT_PTA_EN, 0x1);
+}
+
+static void rtw8723b_coex_set_gnt_fix(struct rtw_dev *rtwdev)
+{
+	/* intentionally empty: rtw8723d's coex_set_gnt_fix is empty too */
+}
+
+static void rtw8723b_coex_set_gnt_debug(struct rtw_dev *rtwdev)
+{
+	/* GNT_BT debug routing is not implemented; not needed for operation. */
+}
+
+static bool rtw8723b_coex_ant_is_aux(struct rtw_dev *rtwdev)
+{
+	return !!(rtwdev->efuse.bt_setting & BIT(6));
+}
+
+static void rtw8723b_coex_write8_verify(struct rtw_dev *rtwdev, u32 addr,
+					u8 value)
+{
+	u8 readback;
+
+	rtw_write8(rtwdev, addr, value);
+	readback = rtw_read8(rtwdev, addr);
+	if (readback == value)
+		return;
+
+	usleep_range(10, 11);
+	rtw_write8(rtwdev, addr, value);
+}
+
+static void rtw8723b_coex_set_ant_ctrl_by_wifi(struct rtw_dev *rtwdev)
+{
+	/* 0x4c[23] = 1, 0x4c[24] = 0: antenna control by 0x64. */
+	rtw_write32_clr(rtwdev, REG_LED_CFG, BIT(24));
+	rtw_write32_set(rtwdev, REG_LED_CFG, BIT(23));
+}
+
+static void rtw8723b_coex_set_ant_ctrl_by_bt(struct rtw_dev *rtwdev)
+{
+	/* 0x4c[24:23] = 0: antenna control by BT_RFE_CTRL. */
+	rtw_write32_clr(rtwdev, REG_LED_CFG, BIT(23) | BIT(24));
+}
+
+static void rtw8723b_coex_cfg_ant_buffer(struct rtw_dev *rtwdev)
+{
+	u8 sys_func_before;
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
+	    WLAN_SYS_FUNC_BB_ENABLE) {
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+		usleep_range(10, 11);
+	}
+
+	rtw_write8_set(rtwdev, REG_PWR_DATA + 1,
+		       BIT_EEPRPAD_RFE_CTRL_EN >> 8);
+	rtw8723b_coex_write8_verify(rtwdev, REG_RFE_CTRL_E, 0xff);
+	rtw_write8_mask(rtwdev, REG_RFE_CTRL_ANT_SW, BIT_RFE_CTRL_ANT_SW_SEL, 0x3);
+	rtw_write8(rtwdev, REG_RFE_CTRL_ANTA_SRC, 0x77);
+}
+
+static u32 rtw8723b_coex_write_bb_sel_btg(struct rtw_dev *rtwdev, u32 value)
+{
+	u32 readback;
+	u8 sys_func_before;
+
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO) {
+		rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+		return rtw_read32(rtwdev, REG_BB_SEL_BTG);
+	}
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
+	    WLAN_SYS_FUNC_BB_ENABLE) {
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+		usleep_range(10, 11);
+	}
+
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+	readback = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+	if (readback == value)
+		return readback;
+
+	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, WLAN_SYS_FUNC_BB_ENABLE);
+	usleep_range(10, 11);
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+
+	return rtw_read32(rtwdev, REG_BB_SEL_BTG);
+}
+
+static u32 rtw8723b_coex_ant_path_value(struct rtw_dev *rtwdev, u8 pos_type)
+{
+	bool aux = rtw8723b_coex_ant_is_aux(rtwdev);
+
+	switch (pos_type) {
+	case COEX_SWITCH_TO_BT:
+		return aux ? 0x0 : 0x280;
+	case COEX_SWITCH_TO_WLG:
+	case COEX_SWITCH_TO_WLA:
+		return aux ? 0x280 : 0x0;
+	case COEX_SWITCH_TO_WLG_BT:
+	case COEX_SWITCH_TO_NOCARE:
+	default:
+		return aux ? 0x80 : 0x200;
+	}
+}
+
+static void rtw8723b_coex_cfg_ant_switch(struct rtw_dev *rtwdev,
+					 u8 ctrl_type, u8 pos_type)
+{
+	u32 ant_path;
+
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO)
+		return;
+
+	if (ctrl_type == COEX_SWITCH_CTRL_BY_BT) {
+		rtw_write8(rtwdev, REG_GNT_BT, 0x18);
+		rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0x4);
+		rtw_write8_clr(rtwdev, REG_BT_ANT_SEL_8723B,
+			       BIT_BT_SEL_BY_WIFI_8723B);
+		rtw8723b_coex_set_ant_ctrl_by_bt(rtwdev);
+		ant_path = rtw8723b_coex_ant_path_value(rtwdev,
+							COEX_SWITCH_TO_BT);
+		rtw8723b_coex_write_bb_sel_btg(rtwdev, ant_path);
+
+		rtw_dbg(rtwdev, RTW_DBG_COEX,
+			"[BTCoex], 8723bs ant switch by BT BB_SEL_BTG=0x%08x 0x4c=0x%08x 0x67=0x%02x 0x765=0x%02x 0x76e=0x%02x\n",
+			rtw_read32(rtwdev, REG_BB_SEL_BTG),
+			rtw_read32(rtwdev, REG_LED_CFG),
+			rtw_read8(rtwdev, REG_BT_ANT_SEL_8723B),
+			rtw_read8(rtwdev, REG_GNT_BT),
+			rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL));
+		return;
+	}
+
+	rtw8723b_coex_set_ant_ctrl_by_wifi(rtwdev);
+	rtw_write8(rtwdev, REG_BT_ANT_SEL_8723B, 0x20);
+
+	if (ctrl_type == COEX_SWITCH_CTRL_BY_BBSW &&
+	    pos_type == COEX_SWITCH_TO_BT) {
+		rtw_write8(rtwdev, REG_GNT_BT, 0x18);
+		rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0x4);
+	} else {
+		if (rtw_read8(rtwdev, REG_GNT_BT) != 0)
+			rtw_write8(rtwdev, REG_GNT_BT, 0x0);
+
+		if (rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL) != 0xc)
+			rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0xc);
+	}
+
+	if (ctrl_type == COEX_SWITCH_CTRL_BY_BBSW)
+		ant_path = rtw8723b_coex_ant_path_value(rtwdev, pos_type);
+	else
+		ant_path = rtw8723b_coex_ant_path_value(rtwdev,
+							COEX_SWITCH_TO_NOCARE);
+
+	rtw8723b_coex_write_bb_sel_btg(rtwdev, ant_path);
+	rtw8723b_sdio_restore_pad_ctrl(rtwdev,
+				       ctrl_type == COEX_SWITCH_CTRL_BY_PTA);
+
+	rtw_dbg(rtwdev, RTW_DBG_COEX,
+		"[BTCoex], 8723bs ant switch ctrl=%u pos=%u BB_SEL_BTG=0x%08x 0x4c=0x%08x 0x67=0x%02x 0x765=0x%02x 0x76e=0x%02x\n",
+		ctrl_type, pos_type, rtw_read32(rtwdev, REG_BB_SEL_BTG),
+		rtw_read32(rtwdev, REG_LED_CFG),
+		rtw_read8(rtwdev, REG_BT_ANT_SEL_8723B),
+		rtw_read8(rtwdev, REG_GNT_BT),
+		rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL));
+}
+
+static void rtw8723b_coex_set_rfe_type(struct rtw_dev *rtwdev)
+{
+	struct rtw_coex *coex = &rtwdev->coex;
+	struct rtw_coex_rfe *coex_rfe = &coex->rfe;
+	enum rtw_hci_type hci_type = rtw_hci_type(rtwdev);
+	u32 reg;
+	bool aux = rtw8723b_coex_ant_is_aux(rtwdev);
+
+	coex_rfe->rfe_module_type = rtwdev->efuse.rfe_option;
+	coex_rfe->ant_switch_polarity = aux ? 1 : 0;
+	coex_rfe->ant_switch_exist = hci_type == RTW_HCI_TYPE_SDIO;
+	coex_rfe->ant_switch_with_bt = false;
+	coex_rfe->ant_switch_diversity = false;
+	coex_rfe->wlg_at_btg = true;
+
+	rtw_write8(rtwdev, REG_BT_ANT_SEL_8723B, 0x20);
+
+	/* set GRAN_BT = 1 */
+	rtw_write8(rtwdev, REG_GNT_BT, 0x18);
+
+	/* set WLAN_ACT = 0 */
+	rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0x4);
+
+	switch (hci_type) {
+	case RTW_HCI_TYPE_USB:
+		rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x0);
+		rtw_write8(rtwdev, 0xfe08, 0x1);
+		break;
+	case RTW_HCI_TYPE_PCIE:
+		reg = 0x384;
+		/* 0xc3[6]: 0 = S1 (main, path A), 1 = S0 (aux, path B). */
+		if (aux) {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x0);
+			rtw_write8(rtwdev, reg, 0x1);
+		} else {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x280);
+			rtw_write8(rtwdev, reg, 0x0);
+		}
+		break;
+	case RTW_HCI_TYPE_SDIO:
+		/*
+		 * Internal switch: WiFi drives S0/S1, firmware gets the
+		 * inverse hint.
+		 */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK, 0x0780);
+
+		if (aux) {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x0);
+			rtw_write8(rtwdev, REG_SDIO_H2C, 0x1);
+		} else {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x280);
+			rtw_write8(rtwdev, REG_SDIO_H2C, 0x0);
+		}
+
+		rtw8723b_coex_set_ant_ctrl_by_wifi(rtwdev);
+		rtw_write8_mask(rtwdev, REG_PAD_CTRL1,
+				BIT_SW_DPDT_SEL_DATA, 0x0);
+		rtw8723b_coex_cfg_ant_buffer(rtwdev);
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, false);
+
+		/* COEX_ANT_SEL_RSV is sent later from rtw_power_on(). */
+		break;
+	default:
+		break;
+	}
+}
+
+static void rtw8723b_coex_set_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr)
+{
+	/* Deferred: 8723d adjusts WL Tx power (0xb2/0x90) with BT on. */
+}
+
+static void rtw8723b_coex_set_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain)
+{
+	/* Deferred: 8723d lowers the WL Rx AGC while BT is active. */
+}
+
+static void rtw8723b_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
+{
+	/* Nothing to do: rtw88 only reads the efuse, it never writes it. */
+}
+
+static void rtw8723b_fill_txdesc_checksum(struct rtw_dev *rtwdev,
+					  struct rtw_tx_pkt_info *pkt_info,
+					  u8 *txdesc)
+{
+	struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)txdesc;
+	const u8 *data = txdesc;
+	u16 checksum = 0;
+	int words = 32 / 2;
+
+	/* Unlike the shared 8723x helper, the checksum is not inverted here. */
+	le32p_replace_bits(&tx_desc->w7, 0, RTW_TX_DESC_W7_TXDESC_CHECKSUM);
+
+	while (words--) {
+		checksum ^= get_unaligned_le16(data);
+		data += sizeof(__le16);
+	}
+
+	le32p_replace_bits(&tx_desc->w7, checksum,
+			   RTW_TX_DESC_W7_TXDESC_CHECKSUM);
+}
+
+static int rtw8723b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	int ret;
+
+	ret = rtw8723x_read_efuse(rtwdev, log_map);
+	if (ret)
+		return ret;
+
+	/*
+	 * No firmware hardware feature report exists, so fill the hardware
+	 * capability here; a zero stream count kills the HT MCS set.
+	 */
+	efuse->hw_cap.nss = max_t(u8, rtwdev->hal.rf_path_num, 1);
+	efuse->hw_cap.ant_num = efuse->hw_cap.nss;
+	efuse->hw_cap.bw = BIT(RTW_CHANNEL_WIDTH_20) |
+			   BIT(RTW_CHANNEL_WIDTH_40);
+
+	return 0;
+}
+
+static const struct rtw_chip_ops rtw8723b_ops = {
+	.power_on		= rtw_power_on,
+	.power_off		= rtw_power_off,
+
+	.mac_init		= rtw8723b_mac_init,
+	.mac_postinit		= rtw8723x_mac_postinit,
+
+	.dump_fw_crash		= NULL,
+	/*
+	 * 8723d sets REG_HCI_OPT_CTRL BIT_USB_SUS_DIS in its shutdown
+	 * function; that is USB-only.
+	 */
+	.shutdown		= NULL,
+	.read_efuse		= rtw8723b_read_efuse,
+	.phy_set_param		= rtw8723b_phy_set_param,
+
+	.set_channel		= rtw8723b_set_channel,
+
+	.query_phy_status	= rtw8723b_query_phy_status,
+	.read_rf		= rtw_phy_read_rf_sipi,
+	.write_rf		= rtw_phy_write_rf_reg_sipi,
+	.set_tx_power_index	= rtw8723x_set_tx_power_index,
+	.rsvd_page_dump		= NULL,
+	.set_antenna		= NULL,
+	.cfg_ldo25		= rtw8723b_cfg_ldo25,
+	.efuse_grant		= rtw8723b_efuse_grant,
+	.set_ampdu_factor	= NULL,
+	.false_alarm_statistics	= rtw8723x_false_alarm_statistics,
+	.phy_calibration	= rtw8723b_phy_calibration,
+	.dpk_track		= NULL,
+	/* REG_CSRATIO does not exist on this chip generation. */
+	.cck_pd_set		= NULL,
+	.pwr_track		= rtw8723b_pwr_track,
+	.config_bfee		= NULL,
+	.set_gid_table		= NULL,
+	.cfg_csi_rate		= NULL,
+	.adaptivity_init	= NULL,
+	.adaptivity		= NULL,
+	.cfo_init		= NULL,
+	.cfo_track		= NULL,
+	.config_tx_path		= NULL,
+	.config_txrx_mode	= NULL,
+	.led_set		= NULL,
+	.fill_txdesc_checksum	= rtw8723b_fill_txdesc_checksum,
+
+	.coex_set_init		= rtw8723b_coex_cfg_init,
+	.coex_set_ant_switch	= rtw8723b_coex_cfg_ant_switch,
+	.coex_set_gnt_fix	= rtw8723b_coex_set_gnt_fix,
+	.coex_set_gnt_debug	= rtw8723b_coex_set_gnt_debug,
+	.coex_set_rfe_type	= rtw8723b_coex_set_rfe_type,
+	.coex_set_wl_tx_power	= rtw8723b_coex_set_wl_tx_power,
+	.coex_set_wl_rx_gain	= rtw8723b_coex_set_wl_rx_gain,
+};
+
+const struct rtw_chip_info rtw8723b_hw_spec = {
+	.ops = &rtw8723b_ops,
+	.id = RTW_CHIP_TYPE_8723B,
+	.fw_name = "rtw88/rtw8723b_fw.bin",
+	.wlan_cpu = RTW_WCPU_8051,
+	.tx_pkt_desc_sz = 40,
+	.tx_buf_desc_sz = 16,
+	.rx_pkt_desc_sz = 24,
+	.rx_buf_desc_sz = 8,
+	.phy_efuse_size = 512,
+	.log_efuse_size = 512,
+	.ptct_efuse_size = 15,
+	.txff_size = 32768,
+	.rxff_size = 16384,
+	.rsvd_drv_pg_num = 8,
+	.txgi_factor = 1,
+	.is_pwr_by_rate_dec = true,
+	.max_power_index = 0x3f,
+	.csi_buf_pg_num = 0,
+	.band = RTW_BAND_2G,
+	.page_size = TX_PAGE_SIZE,
+	.dig_min = 0x20,
+	.usb_tx_agg_desc_num = 1,
+	/*
+	 * The firmware reports id 0xfd instead of C2H_HW_FEATURE_REPORT, so
+	 * the hardware feature report is not supported on this chip.
+	 */
+	.hw_feature_report = false,
+	.c2h_ra_report_size = 4,
+	.old_datarate_fb_limit = true,
+	.path_div_supported = false,
+	.ht_supported = true,
+	.vht_supported = false,
+	.lps_deep_mode_supported = 0,
+	.sys_func_en = 0xfd,
+	.pwr_on_seq = card_enable_flow_8723b,
+	.pwr_off_seq = card_disable_flow_8723b,
+	.page_table = page_table_8723b,
+	.rqpn_table = rqpn_table_8723b,
+	/* same shared table as the sibling rtw8703b and rtw8723d */
+	.prioq_addrs = &rtw8723x_common.prioq_addrs,
+	/* used only in pci.c, not needed for SDIO devices */
+	.intf_table = NULL,
+	.dig = rtw8723x_common.dig,
+	.dig_cck = rtw8723x_common.dig_cck,
+	.rf_sipi_addr = {0x840, 0x844},
+	.rf_sipi_read_addr = rtw8723x_common.rf_sipi_addr,
+	.fix_rf_phy_num = 2,
+	/* This chip has no LTE coex registers. */
+	.ltecoex_addr = NULL,
+	.mac_tbl = &rtw8723b_mac_tbl,
+	.agc_tbl = &rtw8723b_agc_tbl,
+	.bb_tbl = &rtw8723b_bb_tbl,
+	.rf_tbl = {&rtw8723b_rf_a_tbl},
+	.rfe_defs = rtw8723b_rfe_defs,
+	.rfe_defs_size = ARRAY_SIZE(rtw8723b_rfe_defs),
+	.iqk_threshold = 8,
+	.rx_ldpc = false,
+	.tx_stbc = false,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
+	.coex_para_ver = 20180201,	/* glcoex_ver_date_8723b_1ant */
+	.bt_desired_ver = 0x6d,
+	.scbd_support = false,
+	.new_scbd10_def = true,
+	.ble_hid_profile_support = false,
+	.wl_mimo_ps_support = false,
+	.pstdma_type = COEX_PSTDMA_FORCE_LPSOFF,
+	.bt_rssi_type = COEX_BTRSSI_RATIO,
+	.ant_isolation = 15,
+	.rssi_tolerance = 2,
+	.wl_rssi_step = wl_rssi_step_8723b,
+	.bt_rssi_step = bt_rssi_step_8723b,
+	.table_sant_num = ARRAY_SIZE(table_sant_8723b),
+	.table_sant = table_sant_8723b,
+	.table_nsant_num = ARRAY_SIZE(table_nsant_8723b),
+	.table_nsant = table_nsant_8723b,
+	.tdma_sant_num = ARRAY_SIZE(tdma_sant_8723b),
+	.tdma_sant = tdma_sant_8723b,
+	.tdma_nsant_num = ARRAY_SIZE(tdma_nsant_8723b),
+	.tdma_nsant = tdma_nsant_8723b,
+	.wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8723b),
+	.wl_rf_para_tx = rf_para_tx_8723b,
+	.wl_rf_para_rx = rf_para_rx_8723b,
+	.bt_afh_span_bw20 = 0x20,
+	.bt_afh_span_bw40 = 0x30,
+	.afh_5g_num =  ARRAY_SIZE(afh_5g_8723b),
+	.afh_5g = afh_5g_8723b,
+	/* BTG_SEL is driven by the cardemu_to_act power sequence instead. */
+	.btg_reg = NULL,
+	.coex_info_hw_regs_num = 0,
+	.coex_info_hw_regs = NULL,
+};
+EXPORT_SYMBOL(rtw8723b_hw_spec);
+
+MODULE_FIRMWARE("rtw88/rtw8723b_fw.bin");
+
+MODULE_AUTHOR("Luka Gejak <[email protected]>");
+MODULE_AUTHOR("Michael Straube <[email protected]>");
+MODULE_DESCRIPTION("Realtek 802.11n wireless 8723b driver");
+MODULE_LICENSE("Dual BSD/GPL");
drivers/net/wireless/realtek/rtw88/rtw8723b.h new file +13 -0
new file mode 100644
index 000000000000..181782ec3f1b
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright(c) 2026 Realtek Corporation
+ */
+
+#ifndef __RTW8723B_H__
+#define __RTW8723B_H__
+
+#include "rtw8723x.h"
+
+extern const struct rtw_chip_info rtw8723b_hw_spec;
+
+#endif
Signature · 1 line
2.55.0
#5 lore raw

Re: [PATCH v4 3/6] wifi: rtw88: 8723b: add the RTL8723B chip driver

Quoted 845 lines · Luka Gejak

Add the Realtek RTL8723B 802.11n chip driver: the chip operations, the power sequences, the efuse layout, the RF and IQ calibration, and the chip specific coexistence handling.

The RTL8723B chip support is based on the initial work by Michael Straube <[email protected]>.

---
 drivers/net/wireless/realtek/rtw88/rtw8723b.c | 2831 +++++++++++++++++
 drivers/net/wireless/realtek/rtw88/rtw8723b.h |   13 +
 2 files changed, 2844 insertions(+)
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b.c
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b.h
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +821 -0
new file mode 100644
index 000000000000..a26908bdc989
@@ -0,0 +1,2831 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright(c) 2026 Realtek Corporation
+ * Copyright(c) Michael Straube <[email protected]>
+ * Copyright(c) 2024-2026 Luka Gejak <[email protected]>
+ */
+
+#include <linux/unaligned.h>
+#include "main.h"
+#include "coex.h"
+#include "fw.h"
+#include "mac.h"
+#include "phy.h"
+/*
+ * Shares the receive PHY status layout, the SDIO aggregation burst fields
+ * and a few baseband registers with the RTL8703B; reuse that header.
+ */
+#include "rtw8703b.h"
+#include "rtw8723b.h"
+#include "rtw8723b_table.h"
+#include "sdio.h"
+#include "tx.h"
+
+#define TRANS_SEQ_END			\
+	0xFFFF,				\
+	RTW_PWR_CUT_ALL_MSK,		\
+	RTW_PWR_INTF_ALL_MSK,		\
+	0,				\
+	RTW_PWR_CMD_END, 0, 0
+
+#define TBTT_PROHIBIT_SETUP_TIME		0x04
+#define TBTT_PROHIBIT_HOLD_TIME_STOP_BCN	0x64
+#define WLAN_BCN_DMA_TIME			0x02
+#define WLAN_BAR_VAL				0x0201ffff
+#define WLAN_SLOT_TIME				0x09
+#define WLAN_SYS_FUNC_BB_ENABLE			(BIT_FEN_BB_GLB_RST | \
+						 BIT_FEN_BB_RSTB)
+#define WLAN_RF_CTRL_ENABLE			(BIT_RF_EN | BIT_RF_RSTB | \
+						 BIT_RF_SDM_RSTB)
+/*
+ * 0x03a05611 is the normal RX path (staging and the BB table agree);
+ * 0x03a05600 is only an IQK temporary value and must not be reasserted.
+ */
+#define WLAN_RX_PATH_A_8723B			0x03a05611
+
+#define ADDA_ON_VAL_8723B			0x01c00014
+
+#define WLAN_RX_FILTER0			0xFFFF
+#define WLAN_RX_FILTER1			0x400
+#define WLAN_RX_FILTER2			0xFFFF
+/*
+ * Keep BIT_APP_FCS: rtw88 advertises RX_INCLUDES_FCS for every chip, and
+ * without it mac80211 trims four bytes of real frame data.
+ */
+#define WLAN_RCR_CFG			(BIT_APM | BIT_AM | BIT_AB | \
+					 BIT_CBSSID_DATA | BIT_CBSSID_BCN | \
+					 BIT_AMF | BIT_HTC_LOC_CTRL | \
+					 BIT_APP_PHYSTS | BIT_APP_ICV | \
+					 BIT_APP_MIC | BIT_APP_FCS)
+
+/*
+ * BIT(7) lets the 8051 control antenna selection, BIT(1) is LED2_CM.
+ */
+#define WLAN_ANT_SEL			(BIT(7) | BIT(1))
+
+#define IQK_DELAY_TIME_8723B		20
+
+#define BCNQ_PAGE_NUM_8723B	0x08
+#define BCNQ1_PAGE_NUM_8723B		0x00
+#define WOWLAN_PAGE_NUM_8723B		0x00
+#define TX_TOTAL_PAGE_NUMBER_8723B\
+	(0xFF - BCNQ_PAGE_NUM_8723B - BCNQ1_PAGE_NUM_8723B - \
+	 WOWLAN_PAGE_NUM_8723B)
+
+/* rssi in percent (dbm = % - 100); the values are the vendor driver's. */
+static const u8 wl_rssi_step_8723b[] = {60, 50, 44, 30};
+static const u8 bt_rssi_step_8723b[] = {30, 30, 30, 30};
+static const struct coex_5g_afh_map afh_5g_8723b[] = { {0, 0, 0} };
+
+static const struct coex_rf_para rf_para_tx_8723b[] = {
+	{0, 0, false, 7},  /* for normal */
+	{0, 10, false, 7}, /* for WL-CPT */
+	{1, 0, true, 4},
+	{1, 2, true, 4},
+	{1, 10, true, 4},
+	{1, 15, true, 4}
+};
+
+static const struct coex_rf_para rf_para_rx_8723b[] = {
+	{0, 0, false, 7},  /* for normal */
+	{0, 10, false, 7}, /* for WL-CPT */
+	{1, 0, true, 5},
+	{1, 2, true, 5},
+	{1, 10, true, 5},
+	{1, 15, true, 5}
+};
+
+static_assert(ARRAY_SIZE(rf_para_tx_8723b) == ARRAY_SIZE(rf_para_rx_8723b));
+
+static const u32 rtw8723b_ofdm_swing_table[] = {
+	0x0b40002d, /* 0, -15.0dB */
+	0x0c000030, /* 1, -14.5dB */
+	0x0cc00033, /* 2, -14.0dB */
+	0x0d800036, /* 3, -13.5dB */
+	0x0e400039, /* 4, -13.0dB */
+	0x0f00003c, /* 5, -12.5dB */
+	0x10000040, /* 6, -12.0dB */
+	0x11000044, /* 7, -11.5dB */
+	0x12000048, /* 8, -11.0dB */
+	0x1300004c, /* 9, -10.5dB */
+	0x14400051, /* 10, -10.0dB */
+	0x15800056, /* 11, -9.5dB */
+	0x16c0005b, /* 12, -9.0dB */
+	0x18000060, /* 13, -8.5dB */
+	0x19800066, /* 14, -8.0dB */
+	0x1b00006c, /* 15, -7.5dB */
+	0x1c800072, /* 16, -7.0dB */
+	0x1e400079, /* 17, -6.5dB */
+	0x20000080, /* 18, -6.0dB */
+	0x22000088, /* 19, -5.5dB */
+	0x24000090, /* 20, -5.0dB */
+	0x26000098, /* 21, -4.5dB */
+	0x288000a2, /* 22, -4.0dB */
+	0x2ac000ab, /* 23, -3.5dB */
+	0x2d4000b5, /* 24, -3.0dB */
+	0x300000c0, /* 25, -2.5dB */
+	0x32c000cb, /* 26, -2.0dB */
+	0x35c000d7, /* 27, -1.5dB */
+	0x390000e4, /* 28, -1.0dB */
+	0x3c8000f2, /* 29, -0.5dB */
+	0x40000100, /* 30, +0dB */
+	0x43c0010f, /* 31, +0.5dB */
+	0x47c0011f, /* 32, +1.0dB */
+	0x4c000130, /* 33, +1.5dB */
+	0x50800142, /* 34, +2.0dB */
+	0x55400155, /* 35, +2.5dB */
+	0x5a400169, /* 36, +3.0dB */
+	0x5fc0017f, /* 37, +3.5dB */
+	0x65400195, /* 38, +4.0dB */
+	0x6b8001ae, /* 39, +4.5dB */
+	0x71c001c7, /* 40, +5.0dB */
+	0x788001e2, /* 41, +5.5dB */
+	0x7f8001fe, /* 42, +6.0dB */
+};
+
+static const u32 rtw8723b_cck_pwr_regs[] = {
+	0x0a22, 0x0a23, 0x0a24, 0x0a25, 0x0a26, 0x0a27, 0x0a28, 0x0a29,
+};
+
+/*
+ * Row 20 (-6.0 dB) intentionally does not match the v5.2.17 vendor driver,
+ * which has 0x1c, 0x1a, 0x18, 0x12, 0x0e, 0x08 there. Every other row agrees.
+ * The values below are what rtl8723be, the mainline driver for this same
+ * chip, uses at the same index, and they are also what the vendor's own
+ * cck_swing_table_ch1_ch13_92e and the staging rtl8723bs driver use. They
+ * also track the 0.5 dB step of the surrounding rows: against row 32 as 0 dB,
+ * 0x1b is within 0.06 of the ideal -6.0 dB value while 0x1c is 0.94 away,
+ * the largest error anywhere in the table. Treat the vendor row as the
+ * anomaly and do not "fix" this towards it.
+ */
+static const u8 rtw8723b_cck_swing_table_ch1_ch13[][8] = {
+	{0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01},	/* 0, -16.0dB */
+	{0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01},	/* 1, -15.5dB */
+	{0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01},	/* 2, -15.0dB */
+	{0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01},	/* 3, -14.5dB */
+	{0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01},	/* 4, -14.0dB */
+	{0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01},	/* 5, -13.5dB */
+	{0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01},	/* 6, -13.0dB */
+	{0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01},	/* 7, -12.5dB */
+	{0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01},	/* 8, -12.0dB */
+	{0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01},	/* 9, -11.5dB */
+	{0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01},	/* 10, -11.0dB */
+	{0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01},	/* 11, -10.5dB */
+	{0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01},	/* 12, -10.0dB */
+	{0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01},	/* 13, -9.5dB */
+	{0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01},	/* 14, -9.0dB */
+	{0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02},	/* 15, -8.5dB */
+	{0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01},	/* 16, -8.0dB */
+	{0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02},	/* 17, -7.5dB */
+	{0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02},	/* 18, -7.0dB */
+	{0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02},	/* 19, -6.5dB */
+	{0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02},	/* 20, -6.0dB */
+	{0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02},	/* 21, -5.5dB */
+	{0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02},	/* 22, -5.0dB */
+	{0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02},	/* 23, -4.5dB */
+	{0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02},	/* 24, -4.0dB */
+	{0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03},	/* 25, -3.5dB */
+	{0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03},	/* 26, -3.0dB */
+	{0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03},	/* 27, -2.5dB */
+	{0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03},	/* 28, -2.0dB */
+	{0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03},	/* 29, -1.5dB */
+	{0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03},	/* 30, -1.0dB */
+	{0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04},	/* 31, -0.5dB */
+	{0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04},	/* 32, +0dB */
+};
+
+static const u8 rtw8723b_cck_swing_table_ch14[][8] = {
+	{0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00},	/* 0, -16.0dB */
+	{0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 1, -15.5dB */
+	{0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 2, -15.0dB */
+	{0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 3, -14.5dB */
+	{0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 4, -14.0dB */
+	{0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00},	/* 5, -13.5dB */
+	{0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00},	/* 6, -13.0dB */
+	{0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00},	/* 7, -12.5dB */
+	{0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00},	/* 8, -12.0dB */
+	{0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00},	/* 9, -11.5dB */
+	{0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00},	/* 10, -11.0dB */
+	{0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00},	/* 11, -10.5dB */
+	{0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00},	/* 12, -10.0dB */
+	{0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00},	/* 13, -9.5dB */
+	{0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00},	/* 14, -9.0dB */
+	{0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00},	/* 15, -8.5dB */
+	{0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00},	/* 16, -8.0dB */
+	{0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00},	/* 17, -7.5dB */
+	{0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00},	/* 18, -7.0dB */
+	{0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00},	/* 19, -6.5dB */
+	{0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00},	/* 20, -6.0dB */
+	{0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00},	/* 21, -5.5dB */
+	{0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00},	/* 22, -5.0dB */
+	{0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00},	/* 23, -4.5dB */
+	{0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00},	/* 24, -4.0dB */
+	{0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00},	/* 25, -3.5dB */
+	{0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00},	/* 26, -3.0dB */
+	{0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00},	/* 27, -2.5dB */
+	{0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00},	/* 28, -2.0dB */
+	{0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00},	/* 29, -1.5dB */
+	{0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00},	/* 30, -1.0dB */
+	{0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00},	/* 31, -0.5dB */
+	{0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00},	/* 32, +0dB */
+};
+
+static_assert(ARRAY_SIZE(rtw8723b_cck_swing_table_ch1_ch13) ==
+	      ARRAY_SIZE(rtw8723b_cck_swing_table_ch14));
+
+#define RTW_OFDM_SWING_TABLE_SIZE	ARRAY_SIZE(rtw8723b_ofdm_swing_table)
+#define RTW_CCK_SWING_TABLE_SIZE	ARRAY_SIZE(rtw8723b_cck_swing_table_ch14)
+
+/*
+ * Swing index field of REG_OFDM_0_XA_TX_IQ_IMBALANCE; the table entries
+ * below hold the full register value, hence the shift by __ffs().
+ */
+#define OFDM_SWING_MASK			GENMASK(31, 22)
+
+static const struct rtw_pwr_seq_cmd trans_pre_enable_8723b[] = {
+	/* unlock ISO/CLK/power control register */
+	{REG_RSV_CTRL,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xff, 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8723b[] = {
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3) | BIT(7), 0},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_POLLING, BIT(1), BIT(1)},
+	{0x004A,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), 0},
+	{0x0023,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4), 0},
+	{0x0301,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_cardemu_to_act_8723b[] = {
+	{0x0020,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0067,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4), 0},
+	{0x0001,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_DELAY, 1, RTW_PWR_DELAY_MS},
+	{0x0000,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(5), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3) | BIT(2)), 0},
+	{0x0075,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0006,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, BIT(1), BIT(1)},
+	{0x0075,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0006,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(7), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4) | BIT(3), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, BIT(0), 0},
+	{0x0010,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(6), BIT(6)},
+	{0x0049,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0063,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0062,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0058,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x005A,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0068,
+	 RTW_PWR_CUT_TEST_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3), BIT(3)},
+	{0x0069,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(6), BIT(6)},
+	 {TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_act_to_lps_8723b[] = {
+	{0x0301,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0xFF},
+	{0x0522,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0xFF},
+	{0x05F8,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x05F9,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x05FA,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x05FB,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x0002,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0002,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_DELAY, 0, RTW_PWR_DELAY_US},
+	{0x0002,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0100,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0x03},
+	{0x0101,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0093,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0x00},
+	{0x0553,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(5), BIT(5)},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_act_to_reset_mcu_8723b[] = {
+	{REG_SYS_FUNC_EN + 1,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT_FEN_CPUEN, 0},
+	/* reset MCU ready */
+	{REG_MCUFW_CTRL,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xff, 0},
+	/* reset MCU IO wrapper */
+	{REG_RSV_CTRL + 1,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{REG_RSV_CTRL + 1,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 1},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_act_to_cardemu_8723b[] = {
+	{0x001F,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0},
+	{0x0049,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0006,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, BIT(1), 0},
+	{0x0010,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(6), 0},
+	{0x0000,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(5), BIT(5)},
+	{0x0020,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_cardemu_to_carddis_8723b[] = {
+	{0x0007,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0x20},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(2), BIT(2)},
+	{0x004A,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 1},
+	{0x0023,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4), BIT(4)},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_POLLING, BIT(1), 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd * const card_enable_flow_8723b[] = {
+	trans_pre_enable_8723b,
+	trans_carddis_to_cardemu_8723b,
+	trans_cardemu_to_act_8723b,
+	NULL
+};
+
+static const struct rtw_pwr_seq_cmd * const card_disable_flow_8723b[] = {
+	trans_act_to_lps_8723b,
+	trans_act_to_reset_mcu_8723b,
+	trans_act_to_cardemu_8723b,
+	trans_cardemu_to_carddis_8723b,
+	NULL
+};
+
+static const struct rtw_page_table page_table_8723b[] = {
+	{12, 2, 2, 0, 1}, /* SDIO */
+	{12, 2, 2, 0, 1},
+	{12, 2, 2, 0, 1},
+	{12, 2, 2, 0, 1},
+	{12, 2, 2, 0, 1},
+};
+
+static const struct rtw_rqpn rqpn_table_8723b[] = {
+	/* SDIO maps VO, MGMT and HI to the high queue. */
+	{RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* PCIE */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* USB bulkout 2 */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_HIGH,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* USB bulkout 3 */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* USB bulkout 4 */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+};
+
+static const u8 rtw8723b_pwrtrk_2gb_n[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 6, 6,
+	7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2gb_p[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+	9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2ga_n[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 6, 6,
+	7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2ga_p[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+	9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_b_n[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_b_p[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 7, 7,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_a_n[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_a_p[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 7, 7,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const struct rtw_pwr_track_tbl rtw8723b_rtw_pwr_track_tbl = {
+	.pwrtrk_2gb_n = rtw8723b_pwrtrk_2gb_n,
+	.pwrtrk_2gb_p = rtw8723b_pwrtrk_2gb_p,
+	.pwrtrk_2ga_n = rtw8723b_pwrtrk_2ga_n,
+	.pwrtrk_2ga_p = rtw8723b_pwrtrk_2ga_p,
+	.pwrtrk_2g_cckb_n = rtw8723b_pwrtrk_2g_cck_b_n,
+	.pwrtrk_2g_cckb_p = rtw8723b_pwrtrk_2g_cck_b_p,
+	.pwrtrk_2g_ccka_n = rtw8723b_pwrtrk_2g_cck_a_n,
+	.pwrtrk_2g_ccka_p = rtw8723b_pwrtrk_2g_cck_a_p,
+	/* rtw8723x_pwrtrack_set_xtal() is not used on this chip. */
+	.pwrtrk_xtal_n = NULL,
+	.pwrtrk_xtal_p = NULL,
+};
+
+static const struct rtw_rfe_def rtw8723b_rfe_defs[] = {
+	[0] = { .phy_pg_tbl	= &rtw8723b_bb_pg_tbl,
+		.txpwr_lmt_tbl	= &rtw8723b_txpwr_lmt_tbl,
+		.pwr_track_tbl	= &rtw8723b_rtw_pwr_track_tbl, },
+};
+
+/* Shared-Antenna Coex Table */
+static const struct coex_table_para table_sant_8723b[] = {
+	{0xffffffff, 0xffffffff}, /* case-0 */
+	{0x55555555, 0x55555555},
+	{0x66555555, 0x66555555},
+	{0xaaaaaaaa, 0xaaaaaaaa},
+	{0x5a5a5a5a, 0x5a5a5a5a},
+	{0xfafafafa, 0xfafafafa}, /* case-5 */
+	{0x6a5a5555, 0xaaaaaaaa},
+	{0x6a5a56aa, 0x6a5a56aa},
+	{0x6a5a5a5a, 0x6a5a5a5a},
+	{0x66555555, 0x5a5a5a5a},
+	{0x66555555, 0x6a5a5a5a}, /* case-10 */
+	{0x66555555, 0x6a5a5aaa},
+	{0x66555555, 0x5a5a5aaa},
+	{0x66555555, 0x6aaa5aaa},
+	{0x66555555, 0xaaaa5aaa},
+	{0x66555555, 0xaaaaaaaa}, /* case-15 */
+	{0xffff55ff, 0xfafafafa},
+	{0xffff55ff, 0x6afa5afa},
+	{0xaaffffaa, 0xfafafafa},
+	{0xaa5555aa, 0x5a5a5a5a},
+	{0xaa5555aa, 0x6a5a5a5a}, /* case-20 */
+	{0xaa5555aa, 0xaaaaaaaa},
+	{0xffffffff, 0x5a5a5a5a},
+	{0xffffffff, 0x5a5a5a5a},
+	{0xffffffff, 0x55555555},
+	{0xffffffff, 0x5a5a5aaa}, /* case-25 */
+	{0x55555555, 0x5a5a5a5a},
+	{0x55555555, 0xaaaaaaaa},
+	{0x55555555, 0x6a5a6a5a},
+	{0x66556655, 0x66556655},
+	{0x66556aaa, 0x6a5a6aaa}, /* case-30 */
+	{0xffffffff, 0x5aaa5aaa},
+	{0x56555555, 0x5a5a5aaa},
+};
+
+/* Non-Shared-Antenna Coex Table */
+static const struct coex_table_para table_nsant_8723b[] = {
+	{0xffffffff, 0xffffffff}, /* case-100 */
+	{0x55555555, 0x55555555},
+	{0x66555555, 0x66555555},
+	{0xaaaaaaaa, 0xaaaaaaaa},
+	{0x5a5a5a5a, 0x5a5a5a5a},
+	{0xfafafafa, 0xfafafafa}, /* case-105 */
+	{0x5afa5afa, 0x5afa5afa},
+	{0x55555555, 0xfafafafa},
+	{0x66555555, 0xfafafafa},
+	{0x66555555, 0x5a5a5a5a},
+	{0x66555555, 0x6a5a5a5a}, /* case-110 */
+	{0x66555555, 0xaaaaaaaa},
+	{0xffff55ff, 0xfafafafa},
+	{0xffff55ff, 0x5afa5afa},
+	{0xffff55ff, 0xaaaaaaaa},
+	{0xffff55ff, 0xffff55ff}, /* case-115 */
+	{0xaaffffaa, 0x5afa5afa},
+	{0xaaffffaa, 0xaaaaaaaa},
+	{0xffffffff, 0xfafafafa},
+	{0xffffffff, 0x5afa5afa},
+	{0xffffffff, 0xaaaaaaaa}, /* case-120 */
+	{0x55ff55ff, 0x5afa5afa},
+	{0x55ff55ff, 0xaaaaaaaa},
+	{0x55ff55ff, 0x55ff55ff}
+};
+
+/* Shared-Antenna TDMA */
+static const struct coex_tdma_para tdma_sant_8723b[] = {
+	{ {0x00, 0x00, 0x00, 0x00, 0x00} }, /* case-0 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-1 */
+	{ {0x61, 0x3a, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-5 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x3a, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-10 */
+	{ {0x61, 0x08, 0x03, 0x11, 0x14} },
+	{ {0x61, 0x08, 0x03, 0x10, 0x14} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x55} },
+	{ {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-15 */
+	{ {0x51, 0x45, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x3a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x30, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x20, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-20 */
+	{ {0x51, 0x4a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x0c, 0x03, 0x10, 0x54} },
+	{ {0x55, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x65, 0x10, 0x03, 0x11, 0x10} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x51} }, /* case-25 */
+	{ {0x51, 0x08, 0x03, 0x10, 0x50} },
+	{ {0x61, 0x08, 0x03, 0x11, 0x11} }
+};
+
+/* Non-Shared-Antenna TDMA */
+static const struct coex_tdma_para tdma_nsant_8723b[] = {
+	{ {0x00, 0x00, 0x00, 0x00, 0x01} }, /* case-100 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-101 */
+	{ {0x61, 0x3a, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-105 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x3a, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-110 */
+	{ {0x61, 0x08, 0x03, 0x11, 0x14} },
+	{ {0x61, 0x08, 0x03, 0x10, 0x14} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x55} },
+	{ {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-115 */
+	{ {0x51, 0x45, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x3a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x30, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x20, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-120 */
+	{ {0x51, 0x08, 0x03, 0x10, 0x50} }
+};
+
+static void rtw8723b_efuse_grant(struct rtw_dev *rtwdev, bool on)
+{
+	rtw_write8_set(rtwdev, REG_BT_EFUSE_CTRL_8723B,
+		       on ? BIT_BT_PWR_CUT : BIT_BT_OUT_ISO);
+
+	rtw8723x_efuse_grant(rtwdev, on);
+}
+
+static u8 rtw8723b_default_ofdm_index(struct rtw_dev *rtwdev)
+{
+	u32 val32;
+	u32 swing;

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 831

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	u8 i;
+
+	swing = rtw_read32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+				OFDM_SWING_MASK);
+
+	for (i = 0; i < RTW_OFDM_SWING_TABLE_SIZE; i++) {
+		val32 = rtw8723b_ofdm_swing_table[i];
+
+		if (val32 >= 0x100000)
+			val32 >>= __ffs(OFDM_SWING_MASK);

Isn't this the same as u32_get_bits(val32, OFDM_SWING_MASK) ?

Also, every value in the table is bigger than 0x100000, so no need for if.

Quoted 69 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +69 -0
+
+		if (val32 == swing)
+			break;
+	}
+
+	if (i >= RTW_OFDM_SWING_TABLE_SIZE)
+		i = 30; /* 30, +0dB */
+
+	return i;
+}
+
+static u8 rtw8723b_default_cck_index(struct rtw_dev *rtwdev)
+{
+	u8 i;
+	u8 swing;
+
+	swing = rtw_read8(rtwdev, rtw8723b_cck_pwr_regs[0]);
+
+	for (i = 0; i < RTW_CCK_SWING_TABLE_SIZE; i++) {
+		if (rtw8723b_cck_swing_table_ch1_ch13[i][0] == swing)
+			break;
+	}
+
+	if (i >= RTW_CCK_SWING_TABLE_SIZE)
+		i = 20; /* 20, -6.0dB */
+
+	return i;
+}
+
+static void rtw8723b_pwrtrack_init(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u8 path;
+
+	dm_info->default_ofdm_index = rtw8723b_default_ofdm_index(rtwdev);
+	dm_info->default_cck_index = rtw8723b_default_cck_index(rtwdev);
+
+	/* Clear the OFDM remnant per path: tracking keeps one per path. */
+	for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
+		ewma_thermal_init(&dm_info->avg_thermal[path]);
+		dm_info->delta_power_index[path] = 0;
+		dm_info->txagc_remnant_ofdm[path] = 0;
+	}
+	dm_info->pwr_trk_triggered = false;
+	dm_info->pwr_trk_init_trigger = true;
+	dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k;
+	dm_info->txagc_remnant_cck = 0;
+}
+
+static void rtw8723b_sdio_restore_pad_ctrl(struct rtw_dev *rtwdev,
+					   bool keep_pta_owner)
+{
+	u32 before;
+	u32 after;
+
+	if (!rtw_is_8723bs(rtwdev))
+		return;
+
+	before = rtw_read32(rtwdev, REG_PAD_CTRL1);
+	after = before & ~(BIT_LNAON_WLBT_SEL | BIT_SW_DPDT_SEL_DATA);
+	if (keep_pta_owner)
+		after |= BIT_PAPE_WLBT_SEL;
+	else
+		after &= ~BIT_PAPE_WLBT_SEL;
+	if (after == before)
+		return;
+
+	rtw_write32(rtwdev, REG_PAD_CTRL1, after);
+}

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 910

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+static void rtw8723b_post_enable_flow(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Enable falling edge triggered interrupts and GPIO9 interrupt mode.
+	 * The power-on sequence sets both, but it runs before the firmware is
+	 * downloaded, so reassert them here as the vendor driver does.
+	 */
+	rtw_write8_set(rtwdev, 0x0049, BIT(1));
+	rtw_write8_set(rtwdev, 0x0063, BIT(1));

This seems like bit 25 of REG_GPIO_PIN_CTRL_2.

Quoted 113 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +113 -0
+
+	rtw_write16_set(rtwdev, REG_APS_FSMCO, BIT_EN_PDN);
+
+	/*
+	 * Only OR in the missing enables: writing CR to zero would reset the
+	 * hardware free-page counters and leave TX DMA no pages to allocate.
+	 */
+	rtw_write16_set(rtwdev, REG_CR, MAC_TRX_ENABLE | BIT_MAC_SEC_EN |
+				       BIT_32K_CAL_TMR_EN);
+
+	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) {
+		rtw_write16_set(rtwdev, REG_PWR_DATA,
+				BIT_EEPRPAD_RFE_CTRL_EN);
+
+		/*
+		 * rtw_mac_power_on() sets PAD mux bits this chip must not have;
+		 * restore the SDIO PAD mux before RF and coex setup.
+		 */
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, false);
+	}
+
+	rtw_write8(rtwdev, REG_EARLY_MODE_CONTROL, 0);
+
+	/* Keep every MACID eligible for firmware-scheduled TX at power-on. */
+	rtw_write32(rtwdev, REG_MACID_PKT_DROP0, 0);
+	rtw_write32(rtwdev, REG_MACID_PKT_SLEEP, 0);
+}
+
+static void rtw8723b_phy_bb_config(struct rtw_dev *rtwdev)
+{
+	u8 xtal_cap;
+
+	/* Enable BB and RF */
+	rtw_write16_set(rtwdev, REG_SYS_FUNC_EN,
+			BIT_FEN_EN_25_1 | BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB);
+
+	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB)
+		rtw_write32(rtwdev, REG_BB_SEL_BTG, 0x0);
+	else
+		rtw_write32(rtwdev, REG_BB_SEL_BTG, 0x280);
+
+	/* Full write: preserving spuriously set bits can wedge the RF bus. */
+	rtw_write8(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+	fsleep(1000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK, 0x0780);
+	rtw_write8(rtwdev, REG_SYS_FUNC_EN,
+		   BIT_FEN_PPLL | BIT_FEN_PCIEA | BIT_FEN_DIO_PCIE |
+		   BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB);
+	rtw_write8(rtwdev, REG_AFE_CTRL1 + 1, 0x80);
+
+	xtal_cap = rtwdev->efuse.crystal_cap & 0x3f;
+	rtw_write32_mask(rtwdev,  REG_AFE_CTRL3, BIT_MASK_XTAL,
+			 xtal_cap | (xtal_cap << 6));
+}
+
+static void rtw8723b_phy_load_bb_tables(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	const struct rtw_rfe_def *rfe_def = rtw_get_rfe_def(rtwdev);
+
+	rtw_load_table(rtwdev, chip->bb_tbl);
+	rtw_load_table(rtwdev, chip->agc_tbl);
+	if (rfe_def && rfe_def->agc_btg_tbl)
+		rtw_load_table(rtwdev, rfe_def->agc_btg_tbl);
+}
+
+static void rtw8723b_phy_rf6052_config(struct rtw_dev *rtwdev)
+{
+	struct rtw_hal *hal = &rtwdev->hal;
+	u32 intf_s, intf_oe, hssi_2;
+	u32 val32, mask;
+	u8 path;
+
+	for (path = RF_PATH_A; path < hal->rf_path_num; path++) {
+		switch (path) {
+		case RF_PATH_A:
+			intf_s = REG_FPGA0_XA_RF_SW_CTRL;
+			intf_oe = REG_FPGA0_XA_RF_INT_OE;
+			hssi_2 = REG_FPGA0_XA_HSSI_PARM2;
+			mask = RFSI_RFENV;
+			break;
+		case RF_PATH_B:
+			/*
+			 * The path B switch control is the upper half of the
+			 * word at REG_FPGA0_XA_RF_SW_CTRL, which the shifted
+			 * mask already selects. Addressing 0x0872 directly
+			 * would be a 32-bit access on a 2-byte boundary and
+			 * would reach into the register that follows.
+			 */
+			intf_s = REG_FPGA0_XA_RF_SW_CTRL;
+			intf_oe = REG_FPGA0_XB_RF_INT_OE;
+			hssi_2 = REG_FPGA0_XB_HSSI_PARM2;
+			mask = RFSI_RFENV << 16;
+			break;
+		default:
+			rtw_err(rtwdev, "invalid rf path %c\n", path + 'A');
+			return;
+		}
+
+		val32 = rtw_read32_mask(rtwdev, intf_s, mask);
+
+		rtw_write32_mask(rtwdev, intf_oe, RFSI_RFENV << 16, 0x1);
+		udelay(1);
+
+		rtw_write32_mask(rtwdev, intf_oe, RFSI_RFENV, 0x1);
+		udelay(1);
+
+		rtw_write32_mask(rtwdev, hssi_2, HSSI_3WIRE_ADDR_LEN, 0x0);
+		udelay(1);
+
+		rtw_write32_mask(rtwdev, hssi_2, HSSI_3WIRE_DATA_LEN, 0x0);
+		udelay(1);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1033

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+		/* Path B has no table of its own; radio_a serves both paths. */
+		rtw_load_table(rtwdev, rtwdev->chip->rf_tbl[RF_PATH_A]);
+
+		rtw_write32_mask(rtwdev, intf_s, mask, val32);
+	}
+}
+
+static void rtw8723b_phy_lck(struct rtw_dev *rtwdev)
+{
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);

We have a name for 0xb0: RF_SYN_PFD. Not sure if it's the right name here.

Quoted 12 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +12 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, 0x8c01);
+	fsleep(200 * 1000);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdffe0);
+}
+
+static void rtw8723b_phy_rf_config(struct rtw_dev *rtwdev)
+{
+	rtw8723b_phy_rf6052_config(rtwdev);
+
+	/* LCK must run as part of the RF configuration. */
+	rtw8723b_phy_lck(rtwdev);
+}

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1055

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+static void rtw8723b_init_tx_buffer_boundary(struct rtw_dev *rtwdev)
+{
+	u8 val8 = TX_TOTAL_PAGE_NUMBER_8723B + 1;
+
+	rtw_write8(rtwdev, REG_BCNQ_BDNY, val8);
+	rtw_write8(rtwdev, REG_MGQ_BDNY, val8);
+	rtw_write8(rtwdev, REG_WMAC_LBK_BF_HD, val8);
+	rtw_write8(rtwdev, REG_TRXFF_BNDY, val8);
+	rtw_write8(rtwdev, REG_DWBCN0_CTRL + 1, val8);

__priority_queue_cfg_legacy() already takes care of these. Is it necessary to set them again here?

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1066

Quoted 11 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +11 -0
+}
+
+static void rtw8723b_init_page_boundary(struct rtw_dev *rtwdev)
+{
+	/* Matches the value __priority_queue_cfg_legacy() programs here. */
+	rtw_write16(rtwdev, REG_TRXFF_BNDY + 2, 0x4000 - REPORT_BUF - 1);
+}
+
+static void rtw8723b_init_transfer_page_size(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_PBP, 0x11);

We have some macros for this in reg.h right under REG_PBP. You can use them with u8_encode_bits().

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1071

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+}
+
+static void rtw8723b_init_driver_info_size(struct rtw_dev *rtwdev)
+{
+	/* NOTE: also is done in rtw_drv_info_cfg */

If it's done there do you have to do it again here?

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1084

Quoted 13 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +13 -0
+	rtw_write8(rtwdev, REG_RX_DRVINFO_SZ, PHY_STATUS_SIZE);
+}
+
+static void rtw8723b_init_wmac_setting(struct rtw_dev *rtwdev)
+{
+	/*
+	 * The vendor's 8723x filter value plus BIT_APP_FCS, which rtw88
+	 * needs because it advertises RX_INCLUDES_FCS. It is assigned to
+	 * hal.rcr and not merely written because rtw_core_start()
+	 * rewrites REG_RCR from hal.rcr after power_on, which would undo
+	 * a register-only write; fw.c toggles BIT_CBSSID_BCN in it.
+	 */
+	rtwdev->hal.rcr = WLAN_RCR_CFG;

Is it necessary to change the default value assigned in rtw_core_init()?

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1091

Quoted 7 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +7 -0
+	rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
+
+	rtw_write32(rtwdev, REG_MAR, 0xffffffff);
+	rtw_write32(rtwdev, REG_MAR + 4, 0xffffffff);
+
+	rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2);
+	rtw_write16(rtwdev, REG_RXFLTMAP1, WLAN_RX_FILTER1);

Please also update rtwdev->hal.rxfltmap1 when you change REG_RXFLTMAP1:

https://lore.kernel.org/linux-wireless/[email protected]/

Quoted 34 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +34 -0
+	rtw_write16(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0);
+}
+
+static void rtw8723b_init_adaptive_ctrl(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Firmware validates RRSR at init: narrowing it to the mandatory
+	 * rates makes it drop all management TX, so keep the full set.
+	 */
+	rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, 0xffff1);
+	rtwdev->dm_info.rrsr_val_init = 0xffff1;
+	rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x3030);
+}
+
+static void rtw8723b_init_edca(struct rtw_dev *rtwdev)
+{
+	rtw_write16(rtwdev, REG_SPEC_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_MAC_SPEC_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_SIFS + 2, 0x100a);
+
+	/*
+	 * RESP_SIFS must leave the AP enough slack, or it times out before the
+	 * ACK reaches the air and the unicast handshake never completes.
+	 */
+	rtw_write16(rtwdev, REG_RESP_SIFS_CCK, 0x0808);
+	rtw_write16(rtwdev, REG_RESP_SIFS_OFDM, 0x0a0a);
+
+	/* TXOP */
+	rtw_write32(rtwdev, REG_EDCA_BE_PARAM, 0x005EA42B);
+	rtw_write32(rtwdev, REG_EDCA_BK_PARAM, 0x0000A44F);
+	rtw_write32(rtwdev, REG_EDCA_VI_PARAM, 0x005EA324);
+	rtw_write32(rtwdev, REG_EDCA_VO_PARAM, 0x002FA226);
+}

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1135

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+static void rtw8723b_init_retry_function(struct rtw_dev *rtwdev)
+{
+	rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
+	rtw_write8(rtwdev, REG_ACKTO, 0x40);
+}
+
+static void rtw8723b_init_operation_mode(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_BWOPMODE, BIT_BWOPMODE_20MHZ);

I don't see this in the vendor driver v5.2.17.1.

Quoted 29 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +29 -0
+}
+
+static void rtw8723b_init_beacon_parameters(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Both ports get DIS_TSF_UDT and EN_BCN_FUNCTION only; DIS_BCNQ_SUB
+	 * belongs to AP/IBSS and must stay clear for station mode.
+	 */
+	rtw_write16(rtwdev, REG_BCN_CTRL,
+		    (BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION) |
+		    ((BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION) << 8));
+	rtw_write8(rtwdev, REG_TBTT_PROHIBIT, TBTT_PROHIBIT_SETUP_TIME);
+	rtw_write8(rtwdev, REG_TBTT_PROHIBIT + 1,
+		   TBTT_PROHIBIT_HOLD_TIME_STOP_BCN & 0xff);
+	rtw_write8(rtwdev, REG_TBTT_PROHIBIT + 2,
+		   (rtw_read8(rtwdev, REG_TBTT_PROHIBIT + 2) & 0xf0) |
+		   (TBTT_PROHIBIT_HOLD_TIME_STOP_BCN >> 8));
+
+	rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME);
+	/* Largest beacon AIFS: the chip does not contend before beaconing. */
+	rtw_write16(rtwdev, REG_BCNTCFG, 0x660F);
+}
+
+static void rtw8723b_init_burst_pkt_len(struct rtw_dev *rtwdev)
+{
+	rtw_write8_set(rtwdev, REG_SINGLE_AMPDU_CTRL, BIT_EN_SINGLE_APMDU);
+	rtw_write8(rtwdev, REG_RX_PKT_LIMIT, 0x18);
+	rtw_write8(rtwdev, REG_MAX_AGGR_NUM, 0x1F);
+	rtw_write8(rtwdev, REG_PIFS, 0x00);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1174

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_write8_clr(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
+	rtw_write8(rtwdev, REG_AMPDU_MAX_TIME, 0x70);
+}
+
+static void rtw8723b_init_antenna_selection(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_LEDCFG2, WLAN_ANT_SEL);
+}
+
+#define RF_AC	0x00

You can use the existing RF_MODE name for this.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1185

Quoted 11 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +11 -0
+
+static void rtw8723b_lck(struct rtw_dev *rtwdev)
+{
+	u32 rf_mode = 0, lc_cal;
+	int ret;
+	u8 val_ctx;
+	u8 rf_val;
+
+	val_ctx = rtw_read8(rtwdev, REG_CTX);
+
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0)

No need to compare.

Quoted 11 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +11 -0
+		rtw_write8(rtwdev, REG_CTX, val_ctx & ~BIT_MASK_CTX_TYPE);
+	else
+		rtw_write8(rtwdev, REG_TXPAUSE, 0xff);
+
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) {
+		/* 1. Read original RF mode */
+		rf_mode = rtw_read_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS);
+		/* 2. Set RF mode = standby mode */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS, (rf_mode & 0x8ffff) | 0x10000);
+	}
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1206

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	/* 3. Read RF reg18 */
+	lc_cal = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS);
+
+	/* 4. Set LC calibration begin	bit15 */
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS, lc_cal | BIT_LCK);
+
+	ret = read_poll_timeout(rtw_read_rf, rf_val, rf_val != 0x1,
+				10000, 1000000, false,
+			 rtwdev, RF_PATH_A, RF_CFGCH, BIT_LCK);

checkpatch should catch alignment issues like this. Please always run it.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1219

Quoted 13 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +13 -0
+	if (ret)
+		rtw_warn(rtwdev, "failed to poll LCK status bit\n");
+
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdffe0);
+
+	/* Restore original situation */
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) {
+		rtw_write8(rtwdev, REG_CTX, val_ctx);
+
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS, rf_mode);
+	} else {
+		rtw_write8(rtwdev, REG_TXPAUSE, 0x00);
+	}

I think the vendor driver is using MASK12BITS (0xfff) by mistake in this function. It doesn't make any sense, it should be RFREG_MASK (0xfffff) instead.

Quoted 104 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +104 -0
+}
+
+static void rtw8723b_inform_rfk_status(struct rtw_dev *rtwdev, bool start)
+{
+	int ret;
+	u8 val8;
+
+	rtw_fw_inform_rfk_status(rtwdev, start);
+
+	if (!start)
+		return;
+
+	ret = read_poll_timeout(rtw_read8, val8,
+				val8 & BIT_RFK_FW_ACK_8723B,
+				50000, 400000, false,
+				rtwdev, REG_RFK_FW_ACK_8723B);
+	if (ret)
+		rtw_warn(rtwdev, "failed to poll firmware RFK start ack\n");
+}
+
+static int rtw8723b_mac_init(struct rtw_dev *rtwdev)
+{
+	rtw_write32(rtwdev, REG_INT_MIG, 0);
+	rtw_write32(rtwdev, REG_MCUTST_1, 0x0);
+
+	rtw_write8(rtwdev, REG_MISC_CTRL, BIT_DIS_SECOND_CCA);
+	rtw_write8(rtwdev, REG_2ND_CCA_CTRL, 0x0);
+
+	return 0;
+}
+
+static void rtw8723b_phy_set_param(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	u32 val32;
+
+	rtw8723b_post_enable_flow(rtwdev);
+
+	rtw_load_table(rtwdev, chip->mac_tbl);
+	rtw8723b_phy_bb_config(rtwdev);
+	rtw8723b_phy_load_bb_tables(rtwdev);
+	rtw8723b_phy_rf_config(rtwdev);
+
+	/* enable CCK and OFDM block */
+	rtw_write32_set(rtwdev, REG_FPGA0_RFMOD, BIT_CCKEN | BIT_OFDMEN);
+
+	rtw8723b_init_tx_buffer_boundary(rtwdev);
+	rtw8723b_init_page_boundary(rtwdev);
+	rtw8723b_init_transfer_page_size(rtwdev);
+	rtw8723b_init_driver_info_size(rtwdev);
+
+	/* The MAC table sets REG_RCR and REG_MAR, so apply ours after it. */
+	rtw8723b_init_wmac_setting(rtwdev);
+
+	rtw8723b_init_adaptive_ctrl(rtwdev);
+	rtw8723b_init_edca(rtwdev);
+	rtw8723b_init_retry_function(rtwdev);
+
+	/* sdio.c sets the DMA mode, but not the burst parameters. */
+	rtw_write8(rtwdev, REG_RXDMA_MODE,
+		   BIT_DMA_MODE |
+		   FIELD_PREP_CONST(BIT_MASK_AGG_BURST_NUM, AGG_BURST_NUM) |
+		   FIELD_PREP_CONST(BIT_MASK_AGG_BURST_SIZE, AGG_BURST_SIZE));
+
+	rtw8723b_init_operation_mode(rtwdev);
+	rtw8723b_init_beacon_parameters(rtwdev);
+	rtw8723b_init_burst_pkt_len(rtwdev);
+
+	/*
+	 * 256 ms per-AC lifetime: the ROM default of ~1 s lets the chip retry
+	 * data frames past the EAPOL retry window and reorders the connect.
+	 */
+	rtw_write16(rtwdev, REG_PKT_VO_VI_LIFE_TIME, 0x0400);
+	rtw_write16(rtwdev, REG_PKT_BE_BK_LIFE_TIME, 0x0400);
+
+	rtw_write8(rtwdev, REG_SLOT, WLAN_SLOT_TIME);
+
+	/* disable BAR */
+	rtw_write32(rtwdev, REG_BAR_MODE_CTRL, WLAN_BAR_VAL);
+
+	/* Enable hardware sequence numbering for all queues. */
+	rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0xff);
+
+	/* Enable Rx DMA timer masking: clear 0x0[2:0] and 0x2[15:0] only. */
+	val32 = rtw_read32(rtwdev, REG_SDIO_TX_CTRL);
+	val32 &= 0x0000fff8;
+	rtw_write32(rtwdev, REG_SDIO_TX_CTRL, val32);
+
+	rtw_write16(rtwdev, REG_ATIMWND, 0x2);
+
+	rtw8723b_init_antenna_selection(rtwdev);
+
+	rtw_write8_set(rtwdev, REG_CR, BIT_MACTXEN | BIT_MACRXEN);
+
+	rtw_write8(rtwdev, REG_NAV_UPPER, 0xeb); /* ((30000 + 128 - 1) / 128) */
+
+	/* ack for xmit mgmt frames */
+	rtw_write32_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(12));
+
+	rtw_phy_init(rtwdev);
+
+	rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x50);
+	rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x20);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1333

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw8723b_pwrtrack_init(rtwdev);
+}
+
+static u32 rtw8723b_iqk_ant_switch_path(struct rtw_dev *rtwdev)
+{
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO)
+		return rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB ? 0x280 : 0x0;
+
+	/* Scan and connect use the PTA mux, so calibrate the path they use. */
+	return (rtwdev->efuse.bt_setting & BIT(6)) ? 0x80 : 0x200;

This function would be clearer if you don't use the ternary operator at all.

Not sure this logic is correct. The check in the vendor driver is like this:

bool shared_ant = bt_setting & BIT(0);

if (USB) {
	if (shared_ant)
		ant_path = RF_PATH_B;
	else
		ant_path = RF_PATH_A;
} else {
	if (bt_setting & BIT(6))
		ant_path = RF_PATH_B;
	else
		ant_path = RF_PATH_A;
}

if (!shared_ant || ant_path == RF_PATH_A)

	return 0;
else
	return 0x280;

I didn't see 0x80 and 0x200 in the IQK code.

Quoted 44 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +44 -0
+}
+
+static void rtw8723b_reassert_rx_path(struct rtw_dev *rtwdev)
+{
+	u32 rf_wlint_before;
+	u32 rx_path_before;
+	u32 fpga0_before;
+	u8 sys_func_before;
+	u8 rf_ctrl_before;
+
+	if (!rtw_is_8723bs(rtwdev))
+		return;
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	rf_ctrl_before = rtw_read8(rtwdev, REG_RF_CTRL);
+	fpga0_before = rtw_read32(rtwdev, REG_FPGA0_RFMOD);
+	rx_path_before = rtw_read32(rtwdev, REG_BB_RX_PATH_11N);
+	rf_wlint_before = rtw_read_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK);
+
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
+	    WLAN_SYS_FUNC_BB_ENABLE)
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+
+	if ((rf_ctrl_before & WLAN_RF_CTRL_ENABLE) != WLAN_RF_CTRL_ENABLE) {
+		rtw_write8_set(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+		usleep_range(10, 11);
+	}
+
+	if ((fpga0_before & (BIT_CCKEN | BIT_OFDMEN)) !=
+	    (BIT_CCKEN | BIT_OFDMEN)) {
+		rtw_write32_set(rtwdev, REG_FPGA0_RFMOD,
+				BIT_CCKEN | BIT_OFDMEN);
+	}
+
+	if (rx_path_before != WLAN_RX_PATH_A_8723B) {
+		rtw_write32(rtwdev, REG_BB_RX_PATH_11N,
+			    WLAN_RX_PATH_A_8723B);
+	}
+
+	if (rf_wlint_before != 0x0780) {
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
+			     0x0780);
+	}

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1387

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+}
+
+static void rtw8723b_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw)
+{
+	u32 rf_cfgch;
+
+	rf_cfgch = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK);
+
+	rf_cfgch &= ~RFCFGCH_CHANNEL_MASK;
+	rf_cfgch |= channel & RFCFGCH_CHANNEL_MASK;

This is what u32(p)_replace_bits is for.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1399

Quoted 12 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +12 -0
+
+	rf_cfgch &= ~RFCFGCH_BW_MASK;
+	switch (bw) {
+	case RTW_CHANNEL_WIDTH_20:
+		rf_cfgch |= RFCFGCH_BW_20M;
+		break;
+	case RTW_CHANNEL_WIDTH_40:
+		rf_cfgch |= RFCFGCH_BW_40M;
+		break;
+	default:
+		break;
+	}

bw is only going to be 20 or 40, so a simple if would be shorter.

Quoted 28 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +28 -0
+
+	/* The vendor driver programs both paths from the path A value. */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, rf_cfgch);
+	if (rtwdev->hal.rf_path_num > 1)
+		rtw_write_rf(rtwdev, RF_PATH_B, RF_CFGCH, RFREG_MASK, rf_cfgch);
+}
+
+static void rtw8723b_set_channel_bb(struct rtw_dev *rtwdev, u8 bw,
+				    u8 primary_ch_idx)
+{
+	switch (bw) {
+	case RTW_CHANNEL_WIDTH_20:
+		rtw_write32_mask(rtwdev, REG_FPGA0_RFMOD, BIT_MASK_RFMOD, 0x0);
+		rtw_write32_mask(rtwdev, REG_FPGA1_RFMOD, BIT_MASK_RFMOD, 0x0);
+		rtw_write32_mask(rtwdev, REG_OFDM0_TX_PSD_NOISE,
+				 GENMASK(31, 30), 0x0);
+		break;
+	case RTW_CHANNEL_WIDTH_40:
+		rtw_write32_mask(rtwdev, REG_FPGA0_RFMOD, BIT_MASK_RFMOD, 0x1);
+		rtw_write32_mask(rtwdev, REG_FPGA1_RFMOD, BIT_MASK_RFMOD, 0x1);
+		rtw_write32_mask(rtwdev, REG_CCK0_SYS, BIT_CCK_SIDE_BAND,
+				 primary_ch_idx == RTW_SC_20_UPPER ? 1 : 0);
+		rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, 0xc00,
+				 primary_ch_idx == RTW_SC_20_UPPER ? 2 : 1);
+		rtw_write32_mask(rtwdev, REG_BB_PWR_SAV5_11N, GENMASK(27, 26),
+				 primary_ch_idx == RTW_SC_20_UPPER ? 1 : 2);
+		break;
+	default:

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1437

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+		break;
+	}
+}
+
+static void rtw8723b_set_channel(struct rtw_dev *rtwdev, u8 channel,
+				 u8 bw, u8 primary_chan_idx)
+{
+	rtw8723b_set_channel_rf(rtwdev, channel, bw);
+	rtw_set_channel_mac(rtwdev, channel, bw, primary_chan_idx);
+	rtw8723b_set_channel_bb(rtwdev, bw, primary_chan_idx);

I don't think the vendor driver v5.2.17.1 does the stuff below:

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw8723b_reassert_rx_path(rtwdev);
+
+	if (rtw_is_8723bs(rtwdev)) {
+		bool keep_pta_owner;
+
+		keep_pta_owner = test_bit(RTW_FLAG_SCANNING, rtwdev->flags) ||
+				 (rtw_read32(rtwdev, REG_PAD_CTRL1) &
+				  BIT_PAPE_WLBT_SEL);
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, keep_pta_owner);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1457

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+		rtw_write8(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+		fsleep(1000);
+
+		/*
+		 * RF_WLINT bits 0-1 gate the data path into the BB and a prior
+		 * IQK or coex run can leave them blocking TX, so re-arm them.
+		 */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
+			     0x0780);
+	}

If this driver doesn't work without it, you probably have a bug somewhere.

Quoted 249 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +249 -0
+}
+
+static s8 rtw8723b_cck_rx_power(u8 lna_idx, u8 vga_idx)
+{
+	s8 rx_power = 0;
+
+	switch (lna_idx) {
+	case 6:
+		rx_power = -40 - (2 * vga_idx);
+		break;
+	case 4:
+		rx_power = -20 - (2 * vga_idx);
+		break;
+	case 1:
+		rx_power = 0 - (2 * vga_idx);
+		break;
+	case 0:
+		rx_power = 10 - (2 * vga_idx);
+		break;
+	default:
+		break;
+	}
+
+	return rx_power;
+}
+
+static void rtw8723b_query_phy_status_cck(struct rtw_dev *rtwdev, u8 *phy_raw,
+					  struct rtw_rx_pkt_stat *pkt_stat)
+{
+	struct phy_status_8703b *phy_status = (struct phy_status_8703b *)phy_raw;
+	u8 lna_idx = (phy_status->cck_agc_rpt_ofdm_cfosho_a & 0xE0) >> 5;
+	u8 vga_idx = (phy_status->cck_agc_rpt_ofdm_cfosho_a & 0x1F);
+	s8 rx_power = rtw8723b_cck_rx_power(lna_idx, vga_idx);
+	s8 min_rx_power = -120;
+
+	pkt_stat->bw = RTW_CHANNEL_WIDTH_20;
+
+	pkt_stat->rx_power[RF_PATH_A] = rx_power;
+	pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1);
+	pkt_stat->signal_power = max(pkt_stat->rx_power[RF_PATH_A],
+				     min_rx_power);
+	rtwdev->dm_info.rssi[RF_PATH_A] = pkt_stat->rssi;
+}
+
+static void rtw8723b_query_phy_status_ofdm(struct rtw_dev *rtwdev, u8 *phy_raw,
+					   struct rtw_rx_pkt_stat *pkt_stat)
+{
+	struct phy_status_8703b *phy_status = (struct phy_status_8703b *)phy_raw;
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	s8 val_s8;
+
+	/* pkt_stat->bw comes from the RX descriptor, not the PHY status. */
+
+	val_s8 = phy_status->path_agc[RF_PATH_A].gain & 0x3F;
+	pkt_stat->rx_power[RF_PATH_A] = (val_s8 * 2) - 110;
+
+	pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1);
+	pkt_stat->rx_snr[RF_PATH_A] = (s8)(phy_status->path_rxsnr[RF_PATH_A] / 2);
+
+	/* signal power reported by HW */
+	val_s8 = phy_status->cck_sig_qual_ofdm_pwdb_all >> 1;
+	pkt_stat->signal_power = (val_s8 & 0x7f) - 110;
+
+	pkt_stat->rx_evm[RF_PATH_A] = phy_status->stream_rxevm[RF_PATH_A];
+	pkt_stat->cfo_tail[RF_PATH_A] = phy_status->path_cfotail[RF_PATH_A];
+
+	dm_info->curr_rx_rate = pkt_stat->rate;
+	dm_info->rssi[RF_PATH_A] = pkt_stat->rssi;
+	dm_info->rx_snr[RF_PATH_A] = pkt_stat->rx_snr[RF_PATH_A] >> 1;
+	dm_info->cfo_tail[RF_PATH_A] = (pkt_stat->cfo_tail[RF_PATH_A] * 5) >> 1;
+
+	val_s8 = (s8)pkt_stat->rx_evm[RF_PATH_A];
+	val_s8 = clamp_t(s8, -val_s8 >> 1, 0, 64);
+	val_s8 &= 0x3F; /* 64->0: second path of 1SS rate is 64 */
+	dm_info->rx_evm_dbm[RF_PATH_A] = val_s8;
+}
+
+static void rtw8723b_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
+				      struct rtw_rx_pkt_stat *pkt_stat)
+{
+	/*
+	 * The 8723B PHY status does not report the channel, so we must
+	 * mark it invalid to allow mac80211/rtw88 to parse it from the IE
+	 * during scanning.
+	 */
+	pkt_stat->channel_invalid = true;
+
+	if (pkt_stat->rate <= DESC_RATE11M)
+		rtw8723b_query_phy_status_cck(rtwdev, phy_status, pkt_stat);
+	else
+		rtw8723b_query_phy_status_ofdm(rtwdev, phy_status, pkt_stat);
+}
+
+static void rtw8723b_set_iqk_matrix_by_result(struct rtw_dev *rtwdev,
+					      u32 ofdm_swing, u8 path)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	s32 ele_A, ele_D, ele_C, ele_A_ext;
+	s32 iqk_result_x;
+	s32 iqk_result_y;
+	s32 value32;
+
+	switch (path) {
+	default:
+	case RF_PATH_A:
+		iqk_result_x = dm_info->iqk.result.s1_x;
+		iqk_result_y = dm_info->iqk.result.s1_y;
+		break;
+	case RF_PATH_B:
+		iqk_result_x = dm_info->iqk.result.s0_x;
+		iqk_result_y = dm_info->iqk.result.s0_y;
+		break;
+	}
+
+	/* new element D */
+	ele_D = OFDM_SWING_D(ofdm_swing);
+
+	/* new element A */
+	iqk_result_x = iqkxy_to_s32(iqk_result_x);
+	ele_A = iqk_mult(iqk_result_x, ele_D, &ele_A_ext);
+
+	/* new element C */
+	iqk_result_y = iqkxy_to_s32(iqk_result_y);
+	ele_C = iqk_mult(iqk_result_y, ele_D, NULL);
+
+	switch (path) {
+	case RF_PATH_A:
+	default:
+		/* write new elements A, C, D, element B is always 0 */
+		value32 = BIT_SET_TXIQ_ELM_ACD(ele_A, ele_C, ele_D);
+		rtw_write32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, value32);
+		value32 = BIT_SET_TXIQ_ELM_C1(ele_C);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS,
+				 value32);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(24),
+				 ele_A_ext);
+		break;
+
+	case RF_PATH_B:
+		/* write new elements A, C, D, element B is always 0 */
+		value32 = BIT_SET_TXIQ_ELM_ACD(ele_A, ele_C, ele_D);
+		rtw_write32(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE, value32);
+		value32 = BIT_SET_TXIQ_ELM_C1(ele_C);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXB_LSB2_11N, MASKH4BITS,
+				 value32);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(28),
+				 ele_A_ext);
+		break;
+	}
+}
+
+static void rtw8723b_set_iqk_matrix(struct rtw_dev *rtwdev, s8 ofdm_index,
+				    u8 path)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u32 ofdm_swing;
+
+	ofdm_index = clamp_t(s8, ofdm_index, 0, RTW_OFDM_SWING_TABLE_SIZE - 1);
+
+	ofdm_swing = rtw8723b_ofdm_swing_table[ofdm_index];
+
+	if (dm_info->iqk.done) {
+		rtw8723b_set_iqk_matrix_by_result(rtwdev, ofdm_swing, path);
+		return;
+	}
+
+	switch (path) {
+	case RF_PATH_A:
+	default:
+		rtw_write32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, ofdm_swing);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS,
+				 0x00);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(24),
+				 0x00);
+		break;
+
+	case RF_PATH_B:
+		rtw_write32(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE, ofdm_swing);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXB_LSB2_11N, MASKH4BITS,
+				 0x00);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(28),
+				 0x00);
+		break;
+	}
+}
+
+static u8 rtw8723b_iqk_check_tx_failed(struct rtw_dev *rtwdev)
+{
+	s32 tx_x, tx_y;
+	u32 tx_fail;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xeac = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RES_RY));
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xe94 = 0x%x, 0xe9c = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RES_TX),
+		rtw_read32(rtwdev, REG_IQK_RES_TY));
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] 0xe90(before IQK) = 0x%x, 0xe98(after IQK) = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RDY),
+		rtw_read32(rtwdev, 0xe98));
+
+	tx_fail = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_IQK_TX_FAIL);
+	tx_x = rtw_read32_mask(rtwdev, REG_IQK_RES_TX, BIT_MASK_RES_TX);
+	tx_y = rtw_read32_mask(rtwdev, REG_IQK_RES_TY, BIT_MASK_RES_TY);
+
+	if (!tx_fail && tx_x != IQK_TX_X_ERR && tx_y != IQK_TX_Y_ERR)
+		return IQK_TX_OK;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] A TX IQK failed\n");
+
+	return 0;
+}
+
+static u8 rtw8723b_iqk_check_rx_failed(struct rtw_dev *rtwdev)
+{
+	s32 rx_x, rx_y;
+	u32 rx_fail;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xea4 = 0x%x, 0xeac = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RES_RX),
+		rtw_read32(rtwdev, REG_IQK_RES_RY));
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] 0xea0(before IQK) = 0x%x, 0xea8(after IQK) = 0x%x\n",
+		rtw_read32(rtwdev, 0xea0),
+		rtw_read32(rtwdev, 0xea8));
+
+	rx_fail = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_IQK_RX_FAIL);
+	rx_x = rtw_read32_mask(rtwdev, REG_IQK_RES_RX, BIT_MASK_RES_RX);
+	rx_y = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_MASK_RES_RY);
+	rx_y = abs(iqkxy_to_s32(rx_y));
+
+	if (!rx_fail && rx_x != IQK_RX_X_ERR && rx_y != IQK_RX_Y_ERR &&
+	    rx_x < IQK_RX_X_UPPER && rx_x > IQK_RX_X_LOWER &&
+	     rx_y < IQK_RX_Y_LMT)
+		return IQK_RX_OK;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] A RX IQK failed\n");
+
+	return 0;
+}
+
+static u8 rtw8723b_iqk_tx_path_a(struct rtw_dev *rtwdev)
+{
+	u32 path_sel;
+	bool sdio_iqk = rtw_is_8723bs(rtwdev);
+	u8 status;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A TX IQK!\n");
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1716

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	/* Save RF path */
+	path_sel = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* enable path A PA in TX IQK mode */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x20000);

Where does 0x18000 come from? The vendor driver v5.2.17.1 uses 0x20000 here.

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0003f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xc7f87);
+
+	/* Tx IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1731

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N,
+		    sdio_iqk ? 0x821303ea : 0x821403ea);

Here, too, it uses the second value.

Quoted 45 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +45 -0
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N, 0x28110000);
+	rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_B, 0x28110000);
+
+	/* LO calibration setting */
+	rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x00462911);
+
+	/* enter IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x808000);
+
+	/* ant switch */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG,
+		    rtw8723b_iqk_ant_switch_path(rtwdev));
+
+	/* GNT_BT = 0 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00000800);
+
+	/* One shot, path A LOK & IQK */
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf9000000);
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf8000000);
+
+	msleep(IQK_DELAY_TIME_8723B);
+
+	/* restore ant path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, path_sel);
+
+	/* GNT_BT = 1 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00001800);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */
+	status = rtw8723b_iqk_check_tx_failed(rtwdev);
+
+	return status;
+}
+
+static u8 rtw8723b_iqk_rx_path_a(struct rtw_dev *rtwdev)
+{
+	u32 reg_e94, reg_e9c, val32, path_sel;
+	bool sdio_iqk = rtw_is_8723bs(rtwdev);
+	u8 status;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK step1!\n");

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1786

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+	/* Save RF path */
+	path_sel = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x30000);

Vendor driver uses the second value.

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7fb7);
+
+	/* IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1801

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N,
+		    sdio_iqk ? 0x82130ff0 : 0x82160ff0);

Here as well.

Quoted 23 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +23 -0
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N, 0x28110000);
+	rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_B, 0x28110000);
+
+	/* LO calibration setting */
+	rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x0046a911);
+
+	/* enter IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x808000);
+
+	/* ant switch */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG,
+		    rtw8723b_iqk_ant_switch_path(rtwdev));
+
+	/* GNT_BT = 0 (disable BT) */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00000800);
+
+	/* One shot, path A LOK & IQK */
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf9000000);
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf8000000);
+
+	msleep(IQK_DELAY_TIME_8723B);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1834

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	/* restore ant path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, path_sel);
+
+	/* GNT_BT = 1 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00001800);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */

No need for a comment that just repeats what the name of the function already says.

Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+	status = rtw8723b_iqk_check_tx_failed(rtwdev);
+
+	/* if Tx not OK, ignore Rx */
+	if (!status)
+		return status;
+
+	reg_e94 = rtw_read32(rtwdev, REG_IQK_RES_TX);
+	reg_e9c = rtw_read32(rtwdev, REG_IQK_RES_TY);
+	val32 = 0x80007c00 | (reg_e94 & 0x3ff0000) |

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1853

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	((reg_e9c & 0x3ff0000) >> 16);
+	rtw_write32(rtwdev, REG_TXIQK_11N, val32);
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK step2!\n");
+
+	/* modify RX IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x30000);

Here as well.

Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7d77);
+
+	/* PA, PAD setting */
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xdf, RFREG_MASK, 0xf80);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x55, RFREG_MASK, 0x4021f);
+
+	/* IQK setting */
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1872

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N,
+		    sdio_iqk ? 0x2813001f : 0x2816001f);

Here as well.

Quoted 168 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +168 -0
+	rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_B, 0x28110000);
+
+	/* LO calibration setting */
+	rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x0046a8d1);
+
+	/* enter IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x808000);
+
+	/* ant switch */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG,
+		    rtw8723b_iqk_ant_switch_path(rtwdev));
+
+	/* GNT_BT = 0 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00000800);
+
+	/* One shot, path A LOK & IQK */
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf9000000);
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf8000000);
+
+	msleep(IQK_DELAY_TIME_8723B);
+
+	/* restore ant path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, path_sel);
+
+	/* GNT_BT = 1 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00001800);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */
+
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xdf, RFREG_MASK, 0x780);
+
+	status |= rtw8723b_iqk_check_rx_failed(rtwdev);
+
+	return status;
+}
+
+static void
+rtw8723b_iqk_fill_a_matrix(struct rtw_dev *rtwdev, const s32 result[])
+{
+	s32 tx1_a, tx1_a_ext;
+	s32 tx1_c, tx1_c_ext;
+	s32 oldval_1;
+	s32 x, y;
+
+	if (result[IQK_S1_TX_X] == 0)
+		return;
+
+	oldval_1 = rtw_read32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+				   BIT_MASK_TXIQ_ELM_D);
+
+	x = iqkxy_to_s32(result[IQK_S1_TX_X]);
+	tx1_a = iqk_mult(x, oldval_1, &tx1_a_ext);
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_A, tx1_a);
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD,
+			 BIT_MASK_OFDM0_EXT_A, tx1_a_ext);
+
+	y = iqkxy_to_s32(result[IQK_S1_TX_Y]);
+	tx1_c = iqk_mult(y, oldval_1, &tx1_c_ext);
+	rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS,
+			 BIT_SET_TXIQ_ELM_C1(tx1_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_C, BIT_SET_TXIQ_ELM_C2(tx1_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD,
+			 BIT_MASK_OFDM0_EXT_C, tx1_c_ext);
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] X = 0x%x, TX1_A = 0x%x, oldval_1 0x%x\n",
+		x, tx1_a, oldval_1);
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] Y = 0x%x, TX1_C = 0x%x\n", y, tx1_c);
+
+	if (result[IQK_S1_RX_X] == 0)
+		return;
+
+	rtw_write32_mask(rtwdev, REG_A_RXIQI, BIT_MASK_RXIQ_S1_X,
+			 result[IQK_S1_RX_X]);
+	rtw_write32_mask(rtwdev, REG_A_RXIQI, BIT_MASK_RXIQ_S1_Y1,
+			 BIT_SET_RXIQ_S1_Y1(result[IQK_S1_RX_Y]));
+	rtw_write32_mask(rtwdev, REG_RXIQK_MATRIX_LSB_11N, BIT_MASK_RXIQ_S1_Y2,
+			 BIT_SET_RXIQ_S1_Y2(result[IQK_S1_RX_Y]));
+}
+
+static void
+rtw8723b_iqk_fill_b_matrix(struct rtw_dev *rtwdev, const s32 result[])
+{
+	s32 tx0_a, tx0_a_ext;
+	s32 tx0_c, tx0_c_ext;
+	s32 oldval_0;
+	s32 x, y;
+
+	if (result[IQK_S0_TX_X] == 0)
+		return;
+
+	oldval_0 = rtw_read32_mask(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE,
+				   BIT_MASK_TXIQ_ELM_D);
+
+	x = iqkxy_to_s32(result[IQK_S0_TX_X]);
+	tx0_a = iqk_mult(x, oldval_0, &tx0_a_ext);
+
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_A, tx0_a);
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(27),
+			 tx0_a_ext);
+
+	y = iqkxy_to_s32(result[IQK_S0_TX_Y]);
+	tx0_c = iqk_mult(y, oldval_0, &tx0_c_ext);
+
+	rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXB_LSB2_11N, MASKH4BITS,
+			 BIT_SET_TXIQ_ELM_C1(tx0_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_C, BIT_SET_TXIQ_ELM_C2(tx0_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(25),
+			 tx0_c_ext);
+
+	if (result[IQK_S0_RX_X] == 0)
+		return;
+
+	rtw_write32_mask(rtwdev, REG_B_RXIQI, BIT_MASK_RXIQ_X_S0,
+			 result[IQK_S0_RX_X]);
+	rtw_write32_mask(rtwdev, REG_B_RXIQI, BIT_MASK_RXIQ_S1_Y1,
+			 BIT_SET_RXIQ_S1_Y1(result[IQK_S0_RX_Y]));
+}
+
+static void
+rtw8723b_iqk_config_mac(struct rtw_dev *rtwdev,
+			const struct rtw8723x_iqk_backup_regs *backup)
+{
+	int i;
+
+	rtw_write8(rtwdev, rtw8723x_common.iqk_mac8_regs[0], 0x3f);
+
+	for (i = 1; i < RTW8723X_IQK_MAC8_REG_NUM; i++)
+		rtw_write8(rtwdev, rtw8723x_common.iqk_mac8_regs[i],
+			   backup->mac8[i] & (~BIT(3)));
+
+	/* This MAC backup register needs a byte-wide write. */
+	rtw_write8(rtwdev, rtw8723x_common.iqk_mac32_regs[0],
+		   backup->mac32[0] & (~BIT(5)));
+}
+
+static void
+rtw8723b_iqk_one_round(struct rtw_dev *rtwdev, s32 result[][IQK_NR], u8 t,
+		       const struct rtw8723x_iqk_backup_regs *backup)
+{
+	u32 i;
+	u8 a_ok;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] IQ Calibration for 1T1R_S0/S1 for %d times\n", t);
+
+	rtw8723x_iqk_path_adda_on(rtwdev, ADDA_ON_VAL_8723B);
+	rtw8723b_iqk_config_mac(rtwdev, backup);
+
+	rtw_write32_mask(rtwdev, REG_CCK_ANT_SEL_11N, 0x0f000000, 0xf);
+	rtw_write32(rtwdev, REG_BB_RX_PATH_11N, 0x03a05600);
+	rtw_write32(rtwdev, REG_TRMUX_11N, 0x000800e4);
+	rtw_write32(rtwdev, REG_BB_PWR_SAV1_11N, 0x22204000);
+
+	/*
+	 * RX IQ calibration setting for 8723B D cut large current issue
+	 * when leaving IPS
+	 */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2050

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x30000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7fb7);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xed, 0x20, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x43, RFREG_MASK, 0x60fbd);
+
+	for (i = 0; i < PATH_IQK_RETRY; i++) {
+		a_ok = rtw8723b_iqk_tx_path_a(rtwdev);
+		if (a_ok == IQK_TX_OK) {

If you invert the condition, this block with long lines can be indented less.

Quoted 33 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +33 -0
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"[IQK] path A TX IQK success!\n");
+
+			rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N,
+					 MASKH3BYTES, 0x000000);
+
+			result[t][IQK_S1_TX_X] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_TX,
+						BIT_MASK_RES_TX);
+			result[t][IQK_S1_TX_Y] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_TY,
+						BIT_MASK_RES_TY);
+			break;
+		}
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A TX IQK fail!\n");
+		result[t][IQK_S1_TX_X] = 0x100;
+		result[t][IQK_S1_TX_Y] = 0x0;
+	}
+
+	for (i = 0; i < PATH_IQK_RETRY; i++) {
+		a_ok = rtw8723b_iqk_rx_path_a(rtwdev);
+		if (a_ok == (IQK_TX_OK | IQK_RX_OK)) {
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"[IQK] path A RX IQK success!\n");
+			result[t][IQK_S1_RX_X] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_RX,
+						BIT_MASK_RES_RX);
+			result[t][IQK_S1_RX_Y] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_RY,
+						BIT_MASK_RES_RY);
+			break;
+		}

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2093

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK fail!\n");
+		result[t][IQK_S1_RX_X] = 0x100;
+		result[t][IQK_S1_RX_Y] = 0x0;
+	}
+
+	if (a_ok == 0x0)
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A IQK fail!\n");
+
+	/* rtl8723b is 1T1R, so path B is not calibrated. */

It looks like the vendor driver is calibrating path B as well (phy_path_b_iqk_8723b() and phy_path_b_rx_iqk_8723b()) when the device has two antennas, and only path A when the device has one antenna.

Quoted 20 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +20 -0
+
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+}
+
+static void rtw8723b_phy_calibration(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw8723x_iqk_backup_regs backup;
+	s32 result[IQK_ROUND_SIZE][IQK_NR];
+	u32 bt_control;
+	bool good;
+	u8 final_candidate = IQK_ROUND_INVALID;
+	u8 i, j;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] Start!\n");
+	memset(result, 0, sizeof(result));
+
+	rtw8723b_lck(rtwdev);
+	rtw8723b_inform_rfk_status(rtwdev, true);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2123

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	/* The LTE path GNT backup that 8723d does is not needed on SDIO. */
+	rtw8723x_iqk_backup_path_ctrl(rtwdev, &backup);
+	rtw8723x_iqk_backup_regs(rtwdev, &backup);
+
+	/* save default GNT_BT */
+	bt_control = rtw_read32(rtwdev, REG_BT_CONTROL_8723B);
+
+	for (i = IQK_ROUND_0; i <= IQK_ROUND_2; i++) {
+		if (!rtw_is_8723bs(rtwdev))
+			rtw8723x_iqk_config_path_ctrl(rtwdev);

I didn't see this in the RTL8723BE or RTL8723BU drivers. I guess it was copied from another chip?

Quoted 74 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +74 -0
+
+		rtw8723b_iqk_one_round(rtwdev, result, i, &backup);
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"[IQK] back to BB mode, load original value!\n");
+
+		if (i > IQK_ROUND_0) {
+			rtw8723x_iqk_restore_regs(rtwdev, &backup);
+
+			/* Restore RX initial gain */
+			rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x50);
+			rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, backup.igia);
+
+			/* load 0xe30 IQC default value */
+			rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x01008c00);
+			rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x01008c00);
+		}
+
+		if (!rtw_is_8723bs(rtwdev))
+			rtw8723x_iqk_restore_path_ctrl(rtwdev, &backup);
+
+		for (j = IQK_ROUND_0; j < i; j++) {
+			good = rtw8723x_iqk_similarity_cmp(rtwdev, result, j, i);
+			if (good) {
+				final_candidate = j;
+				rtw_dbg(rtwdev, RTW_DBG_RFK,
+					"[IQK] cmp %d:%d final_candidate is %x\n",
+					j, i, final_candidate);
+				goto iqk_done;
+			}
+		}
+	}
+
+	if (final_candidate == IQK_ROUND_INVALID) {
+		s32 reg_tmp = 0;
+
+		for (i = 0; i < IQK_NR; i++)
+			reg_tmp += result[IQK_ROUND_HYBRID][i];
+
+		if (reg_tmp != 0) {
+			final_candidate = IQK_ROUND_HYBRID;
+		} else {
+			rtw_warn(rtwdev, "IQK failed\n");
+			goto out;
+		}
+	}
+
+iqk_done:
+	if (result[final_candidate][IQK_S1_TX_X])
+		rtw8723b_iqk_fill_a_matrix(rtwdev, result[final_candidate]);
+	if (result[final_candidate][IQK_S0_TX_X])
+		rtw8723b_iqk_fill_b_matrix(rtwdev, result[final_candidate]);
+
+	dm_info->iqk.result.s1_x = result[final_candidate][IQK_S1_TX_X];
+	dm_info->iqk.result.s1_y = result[final_candidate][IQK_S1_TX_Y];
+	dm_info->iqk.result.s0_x = result[final_candidate][IQK_S0_TX_X];
+	dm_info->iqk.result.s0_y = result[final_candidate][IQK_S0_TX_Y];
+	dm_info->iqk.done = true;
+
+out:
+	/* restore RF path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, backup.bb_sel_btg);
+
+	/* restore GNT_BT */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, bt_control);
+
+	/* Restore RX mode table parameter */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x18000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xe6177);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xed, 0x20, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x43, RFREG_MASK, 0x300bd);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2207

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] final_candidate is %x\n",
+		final_candidate);
+
+	for (i = IQK_ROUND_0; i < IQK_ROUND_SIZE; i++)
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"[IQK] Result %u: rege94_s1=%x rege9c_s1=%x regea4_s1=%x regeac_s1=%x rege94_s0=%x rege9c_s0=%x regea4_s0=%x regeac_s0=%x %s\n",
+			i,
+			result[i][0], result[i][1], result[i][2], result[i][3],
+			result[i][4], result[i][5], result[i][6], result[i][7],
+			final_candidate == i ? "(final candidate)" : "");

You can make this two calls to rtw_dbg to avoid such a long line.

Quoted 31 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +31 -0
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK]0xc80 = 0x%x 0xc94 = 0x%x 0xc14 = 0x%x 0xca0 = 0x%x\n",
+	rtw_read32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE),
+		rtw_read32(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N),
+		rtw_read32(rtwdev, REG_A_RXIQI),
+		rtw_read32(rtwdev, REG_RXIQK_MATRIX_LSB_11N));
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK]0xcd0 = 0x%x 0xcd4 = 0x%x 0xcd8 = 0x%x\n",
+	rtw_read32(rtwdev, REG_TXIQ_AB_S0),
+		rtw_read32(rtwdev, REG_TXIQ_CD_S0),
+		rtw_read32(rtwdev, REG_RXIQ_AB_S0));
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] finished\n");
+
+	rtw8723b_inform_rfk_status(rtwdev, false);
+}
+
+static void rtw8723b_pwrtrack_set_ofdm_pwr(struct rtw_dev *rtwdev, u8 path,
+					   s8 swing_idx, s8 txagc_idx)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	dm_info->txagc_remnant_ofdm[path] = txagc_idx;
+
+	rtw8723b_set_iqk_matrix(rtwdev, swing_idx, path);
+}
+
+static void rtw8723b_pwrtrack_set_cck_pwr(struct rtw_dev *rtwdev, s8 swing_idx,
+					  s8 txagc_idx)
+{

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2248

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	dm_info->txagc_remnant_cck = txagc_idx;
+
+	swing_idx = clamp_t(s8, swing_idx, 0, RTW_CCK_SWING_TABLE_SIZE - 1);
+
+	BUILD_BUG_ON(ARRAY_SIZE(rtw8723b_cck_pwr_regs) !=
+		     ARRAY_SIZE(rtw8723b_cck_swing_table_ch1_ch13[0]));
+
+	/* Only ch1-13 is wired up; channel 14 is Japan-only and unreachable. */

If I change my country code to JP and trigger a scan, rtw88 visits channel 14.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2250

Quoted 2 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +2 -0
+	for (int i = 0; i < ARRAY_SIZE(rtw8723b_cck_pwr_regs); i++)
+		rtw_write8(rtwdev, rtw8723b_cck_pwr_regs[i],

These register addresses are consecutive values, there is no need to put them in an array.

Quoted 62 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +62 -0
+			   rtw8723b_cck_swing_table_ch1_ch13[swing_idx][i]);
+}
+
+static void rtw8723b_pwrtrack_set(struct rtw_dev *rtwdev, u8 path)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_hal *hal = &rtwdev->hal;
+	u8 limit_ofdm;
+	/* 8703b and 8723d seem to use RTW_CCK_SWING_TABLE_SIZE */
+	u8 limit_cck = 28; /* -2dB */
+	s8 final_ofdm_swing_index;
+	s8 final_cck_swing_index;
+
+	limit_ofdm = rtw8723x_pwrtrack_get_limit_ofdm(rtwdev);
+
+	final_ofdm_swing_index = dm_info->default_ofdm_index +
+				 dm_info->delta_power_index[path];
+	final_cck_swing_index = dm_info->default_cck_index +
+				dm_info->delta_power_index[path];
+
+	if (final_ofdm_swing_index > limit_ofdm)
+		rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, path, limit_ofdm,
+					       final_ofdm_swing_index - limit_ofdm);
+	else if (final_ofdm_swing_index < 0)
+		rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, path, 0,
+					       final_ofdm_swing_index);
+	else
+		rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, path, final_ofdm_swing_index, 0);
+
+	if (final_cck_swing_index > limit_cck)
+		rtw8723b_pwrtrack_set_cck_pwr(rtwdev, limit_cck,
+					      final_cck_swing_index - limit_cck);
+	else if (final_cck_swing_index < 0)
+		rtw8723b_pwrtrack_set_cck_pwr(rtwdev, 0,
+					      final_cck_swing_index);
+	else
+		rtw8723b_pwrtrack_set_cck_pwr(rtwdev, final_cck_swing_index, 0);
+
+	rtw_phy_set_tx_power_level(rtwdev, hal->current_channel);
+}
+
+static void rtw8723b_phy_pwrtrack(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_swing_table swing_table;
+	u8 thermal_value, delta, path;
+	bool do_iqk = false;
+
+	rtw_phy_config_swing_table(rtwdev, &swing_table);
+
+	if (rtwdev->efuse.thermal_meter[0] == 0xff)
+		return;
+
+	thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00);
+
+	/* Average the thermal meter readings. */
+	rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A);
+
+	do_iqk = rtw_phy_pwrtrack_need_iqk(rtwdev);
+
+	if (do_iqk)
+		rtw8723x_lck(rtwdev);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2322

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+	if (dm_info->pwr_trk_init_trigger)
+		dm_info->pwr_trk_init_trigger = false;
+	else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value,
+						   RF_PATH_A))
+		goto iqk;
+
+	delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A);
+
+	/* NOTE: also done in rtw_phy_pwrtrack_get_delta */

Is it necessary to do it again?

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+	delta = min_t(u8, delta, RTW_PWR_TRK_TBL_SZ - 1);
+
+	for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
+		s8 delta_cur, delta_last;
+
+		delta_last = dm_info->delta_power_index[path];
+		delta_cur = rtw_phy_pwrtrack_get_pwridx(rtwdev, &swing_table,
+							path, RF_PATH_A, delta);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2340

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+		if (delta_last == delta_cur)
+			continue;
+
+		dm_info->delta_power_index[path] = delta_cur;
+		rtw8723b_pwrtrack_set(rtwdev, path);
+	}
+
+iqk:
+	if (do_iqk)
+		rtw8723b_phy_calibration(rtwdev);

The vendor driver doesn't redo IQK here.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2349

Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+}
+
+static void rtw8723b_pwr_track(struct rtw_dev *rtwdev)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	if (efuse->power_track_type != 0) {
+		rtw_warn(rtwdev, "unsupported power track type\n");

This function runs every two seconds, forever. Please put this warning elsewhere, like rtw8723b_read_efuse(), to avoid filling the kernel log. (Or create rtw_warn_once()).

Quoted 76 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +76 -0
+		return;
+	}
+
+	if (!dm_info->pwr_trk_triggered) {
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER,
+			     GENMASK(17, 16), 0x03);
+		dm_info->pwr_trk_triggered = true;
+		return;
+	}
+
+	rtw8723b_phy_pwrtrack(rtwdev);
+	dm_info->pwr_trk_triggered = false;
+}
+
+/*
+ * The vendor sequence for this chip differs from rtw8723x_coex_cfg_init():
+ * REG_BT_TDMA_TIME is written whole, PTA pins and REG_QUEUE_CTRL untouched.
+ */
+static void rtw8723b_coex_cfg_init(struct rtw_dev *rtwdev)
+{
+	/* enable TBTT interrupt */
+	rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION);
+
+	/* BT report packet sample rate, 0x790[5:0] = 0x5 */
+	rtw_write8(rtwdev, REG_BT_TDMA_TIME, 0x5);
+
+	/* enable BT counter statistics */
+	rtw_write8(rtwdev, REG_BT_STAT_CTRL, 0x1);
+
+	/* enable PTA (3-wire function from BT side) */
+	rtw_write8_mask(rtwdev, REG_GPIO_MUXCFG, BIT_BT_PTA_EN, 0x1);
+}
+
+static void rtw8723b_coex_set_gnt_fix(struct rtw_dev *rtwdev)
+{
+	/* intentionally empty: rtw8723d's coex_set_gnt_fix is empty too */
+}
+
+static void rtw8723b_coex_set_gnt_debug(struct rtw_dev *rtwdev)
+{
+	/* GNT_BT debug routing is not implemented; not needed for operation. */
+}
+
+static bool rtw8723b_coex_ant_is_aux(struct rtw_dev *rtwdev)
+{
+	return !!(rtwdev->efuse.bt_setting & BIT(6));
+}
+
+static void rtw8723b_coex_write8_verify(struct rtw_dev *rtwdev, u32 addr,
+					u8 value)
+{
+	u8 readback;
+
+	rtw_write8(rtwdev, addr, value);
+	readback = rtw_read8(rtwdev, addr);
+	if (readback == value)
+		return;
+
+	usleep_range(10, 11);
+	rtw_write8(rtwdev, addr, value);
+}
+
+static void rtw8723b_coex_set_ant_ctrl_by_wifi(struct rtw_dev *rtwdev)
+{
+	/* 0x4c[23] = 1, 0x4c[24] = 0: antenna control by 0x64. */
+	rtw_write32_clr(rtwdev, REG_LED_CFG, BIT(24));
+	rtw_write32_set(rtwdev, REG_LED_CFG, BIT(23));
+}
+
+static void rtw8723b_coex_set_ant_ctrl_by_bt(struct rtw_dev *rtwdev)
+{
+	/* 0x4c[24:23] = 0: antenna control by BT_RFE_CTRL. */
+	rtw_write32_clr(rtwdev, REG_LED_CFG, BIT(23) | BIT(24));
+}
+
+static void rtw8723b_coex_cfg_ant_buffer(struct rtw_dev *rtwdev)

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2435

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+{
+	u8 sys_func_before;
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
+	    WLAN_SYS_FUNC_BB_ENABLE) {
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+		usleep_range(10, 11);
+	}

Not sure why you need to touch REG_SYS_FUNC_EN here. The vendor driver doesn't do it.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2439

Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+
+	rtw_write8_set(rtwdev, REG_PWR_DATA + 1,
+		       BIT_EEPRPAD_RFE_CTRL_EN >> 8);
+	rtw8723b_coex_write8_verify(rtwdev, REG_RFE_CTRL_E, 0xff);

The vendor driver uses a normal rtw_write8() here.

Quoted 22 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +22 -0
+	rtw_write8_mask(rtwdev, REG_RFE_CTRL_ANT_SW, BIT_RFE_CTRL_ANT_SW_SEL, 0x3);
+	rtw_write8(rtwdev, REG_RFE_CTRL_ANTA_SRC, 0x77);
+}
+
+static u32 rtw8723b_coex_write_bb_sel_btg(struct rtw_dev *rtwdev, u32 value)
+{
+	u32 readback;
+	u8 sys_func_before;
+
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO) {
+		rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+		return rtw_read32(rtwdev, REG_BB_SEL_BTG);
+	}
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
+	    WLAN_SYS_FUNC_BB_ENABLE) {
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+		usleep_range(10, 11);
+	}
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2471

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+	readback = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+	if (readback == value)
+		return readback;
+
+	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, WLAN_SYS_FUNC_BB_ENABLE);
+	usleep_range(10, 11);
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+
+	return rtw_read32(rtwdev, REG_BB_SEL_BTG);

All this looks strange too. What happens if you eliminate this function and write REG_BB_SEL_BTG with a simple rtw_write32()?

Quoted 227 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +227 -0
+}
+
+static u32 rtw8723b_coex_ant_path_value(struct rtw_dev *rtwdev, u8 pos_type)
+{
+	bool aux = rtw8723b_coex_ant_is_aux(rtwdev);
+
+	switch (pos_type) {
+	case COEX_SWITCH_TO_BT:
+		return aux ? 0x0 : 0x280;
+	case COEX_SWITCH_TO_WLG:
+	case COEX_SWITCH_TO_WLA:
+		return aux ? 0x280 : 0x0;
+	case COEX_SWITCH_TO_WLG_BT:
+	case COEX_SWITCH_TO_NOCARE:
+	default:
+		return aux ? 0x80 : 0x200;
+	}
+}
+
+static void rtw8723b_coex_cfg_ant_switch(struct rtw_dev *rtwdev,
+					 u8 ctrl_type, u8 pos_type)
+{
+	u32 ant_path;
+
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO)
+		return;
+
+	if (ctrl_type == COEX_SWITCH_CTRL_BY_BT) {
+		rtw_write8(rtwdev, REG_GNT_BT, 0x18);
+		rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0x4);
+		rtw_write8_clr(rtwdev, REG_BT_ANT_SEL_8723B,
+			       BIT_BT_SEL_BY_WIFI_8723B);
+		rtw8723b_coex_set_ant_ctrl_by_bt(rtwdev);
+		ant_path = rtw8723b_coex_ant_path_value(rtwdev,
+							COEX_SWITCH_TO_BT);
+		rtw8723b_coex_write_bb_sel_btg(rtwdev, ant_path);
+
+		rtw_dbg(rtwdev, RTW_DBG_COEX,
+			"[BTCoex], 8723bs ant switch by BT BB_SEL_BTG=0x%08x 0x4c=0x%08x 0x67=0x%02x 0x765=0x%02x 0x76e=0x%02x\n",
+			rtw_read32(rtwdev, REG_BB_SEL_BTG),
+			rtw_read32(rtwdev, REG_LED_CFG),
+			rtw_read8(rtwdev, REG_BT_ANT_SEL_8723B),
+			rtw_read8(rtwdev, REG_GNT_BT),
+			rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL));
+		return;
+	}
+
+	rtw8723b_coex_set_ant_ctrl_by_wifi(rtwdev);
+	rtw_write8(rtwdev, REG_BT_ANT_SEL_8723B, 0x20);
+
+	if (ctrl_type == COEX_SWITCH_CTRL_BY_BBSW &&
+	    pos_type == COEX_SWITCH_TO_BT) {
+		rtw_write8(rtwdev, REG_GNT_BT, 0x18);
+		rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0x4);
+	} else {
+		if (rtw_read8(rtwdev, REG_GNT_BT) != 0)
+			rtw_write8(rtwdev, REG_GNT_BT, 0x0);
+
+		if (rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL) != 0xc)
+			rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0xc);
+	}
+
+	if (ctrl_type == COEX_SWITCH_CTRL_BY_BBSW)
+		ant_path = rtw8723b_coex_ant_path_value(rtwdev, pos_type);
+	else
+		ant_path = rtw8723b_coex_ant_path_value(rtwdev,
+							COEX_SWITCH_TO_NOCARE);
+
+	rtw8723b_coex_write_bb_sel_btg(rtwdev, ant_path);
+	rtw8723b_sdio_restore_pad_ctrl(rtwdev,
+				       ctrl_type == COEX_SWITCH_CTRL_BY_PTA);
+
+	rtw_dbg(rtwdev, RTW_DBG_COEX,
+		"[BTCoex], 8723bs ant switch ctrl=%u pos=%u BB_SEL_BTG=0x%08x 0x4c=0x%08x 0x67=0x%02x 0x765=0x%02x 0x76e=0x%02x\n",
+		ctrl_type, pos_type, rtw_read32(rtwdev, REG_BB_SEL_BTG),
+		rtw_read32(rtwdev, REG_LED_CFG),
+		rtw_read8(rtwdev, REG_BT_ANT_SEL_8723B),
+		rtw_read8(rtwdev, REG_GNT_BT),
+		rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL));
+}
+
+static void rtw8723b_coex_set_rfe_type(struct rtw_dev *rtwdev)
+{
+	struct rtw_coex *coex = &rtwdev->coex;
+	struct rtw_coex_rfe *coex_rfe = &coex->rfe;
+	enum rtw_hci_type hci_type = rtw_hci_type(rtwdev);
+	u32 reg;
+	bool aux = rtw8723b_coex_ant_is_aux(rtwdev);
+
+	coex_rfe->rfe_module_type = rtwdev->efuse.rfe_option;
+	coex_rfe->ant_switch_polarity = aux ? 1 : 0;
+	coex_rfe->ant_switch_exist = hci_type == RTW_HCI_TYPE_SDIO;
+	coex_rfe->ant_switch_with_bt = false;
+	coex_rfe->ant_switch_diversity = false;
+	coex_rfe->wlg_at_btg = true;
+
+	rtw_write8(rtwdev, REG_BT_ANT_SEL_8723B, 0x20);
+
+	/* set GRAN_BT = 1 */
+	rtw_write8(rtwdev, REG_GNT_BT, 0x18);
+
+	/* set WLAN_ACT = 0 */
+	rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0x4);
+
+	switch (hci_type) {
+	case RTW_HCI_TYPE_USB:
+		rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x0);
+		rtw_write8(rtwdev, 0xfe08, 0x1);
+		break;
+	case RTW_HCI_TYPE_PCIE:
+		reg = 0x384;
+		/* 0xc3[6]: 0 = S1 (main, path A), 1 = S0 (aux, path B). */
+		if (aux) {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x0);
+			rtw_write8(rtwdev, reg, 0x1);
+		} else {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x280);
+			rtw_write8(rtwdev, reg, 0x0);
+		}
+		break;
+	case RTW_HCI_TYPE_SDIO:
+		/*
+		 * Internal switch: WiFi drives S0/S1, firmware gets the
+		 * inverse hint.
+		 */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK, 0x0780);
+
+		if (aux) {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x0);
+			rtw_write8(rtwdev, REG_SDIO_H2C, 0x1);
+		} else {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x280);
+			rtw_write8(rtwdev, REG_SDIO_H2C, 0x0);
+		}
+
+		rtw8723b_coex_set_ant_ctrl_by_wifi(rtwdev);
+		rtw_write8_mask(rtwdev, REG_PAD_CTRL1,
+				BIT_SW_DPDT_SEL_DATA, 0x0);
+		rtw8723b_coex_cfg_ant_buffer(rtwdev);
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, false);
+
+		/* COEX_ANT_SEL_RSV is sent later from rtw_power_on(). */
+		break;
+	default:
+		break;
+	}
+}
+
+static void rtw8723b_coex_set_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr)
+{
+	/* Deferred: 8723d adjusts WL Tx power (0xb2/0x90) with BT on. */
+}
+
+static void rtw8723b_coex_set_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain)
+{
+	/* Deferred: 8723d lowers the WL Rx AGC while BT is active. */
+}
+
+static void rtw8723b_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
+{
+	/* Nothing to do: rtw88 only reads the efuse, it never writes it. */
+}
+
+static void rtw8723b_fill_txdesc_checksum(struct rtw_dev *rtwdev,
+					  struct rtw_tx_pkt_info *pkt_info,
+					  u8 *txdesc)
+{
+	struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)txdesc;
+	const u8 *data = txdesc;
+	u16 checksum = 0;
+	int words = 32 / 2;
+
+	/* Unlike the shared 8723x helper, the checksum is not inverted here. */
+	le32p_replace_bits(&tx_desc->w7, 0, RTW_TX_DESC_W7_TXDESC_CHECKSUM);
+
+	while (words--) {
+		checksum ^= get_unaligned_le16(data);
+		data += sizeof(__le16);
+	}
+
+	le32p_replace_bits(&tx_desc->w7, checksum,
+			   RTW_TX_DESC_W7_TXDESC_CHECKSUM);
+}
+
+static int rtw8723b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	int ret;
+
+	ret = rtw8723x_read_efuse(rtwdev, log_map);
+	if (ret)
+		return ret;
+
+	/*
+	 * No firmware hardware feature report exists, so fill the hardware
+	 * capability here; a zero stream count kills the HT MCS set.
+	 */
+	efuse->hw_cap.nss = max_t(u8, rtwdev->hal.rf_path_num, 1);
+	efuse->hw_cap.ant_num = efuse->hw_cap.nss;
+	efuse->hw_cap.bw = BIT(RTW_CHANNEL_WIDTH_20) |
+			   BIT(RTW_CHANNEL_WIDTH_40);
+
+	return 0;
+}
+
+static const struct rtw_chip_ops rtw8723b_ops = {
+	.power_on		= rtw_power_on,
+	.power_off		= rtw_power_off,
+
+	.mac_init		= rtw8723b_mac_init,
+	.mac_postinit		= rtw8723x_mac_postinit,
+
+	.dump_fw_crash		= NULL,
+	/*
+	 * 8723d sets REG_HCI_OPT_CTRL BIT_USB_SUS_DIS in its shutdown
+	 * function; that is USB-only.
+	 */
+	.shutdown		= NULL,
+	.read_efuse		= rtw8723b_read_efuse,
+	.phy_set_param		= rtw8723b_phy_set_param,
+
+	.set_channel		= rtw8723b_set_channel,
+
+	.query_phy_status	= rtw8723b_query_phy_status,
+	.read_rf		= rtw_phy_read_rf_sipi,
+	.write_rf		= rtw_phy_write_rf_reg_sipi,
+	.set_tx_power_index	= rtw8723x_set_tx_power_index,

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2708

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	.rsvd_page_dump		= NULL,
+	.set_antenna		= NULL,
+	.cfg_ldo25		= rtw8723b_cfg_ldo25,
+	.efuse_grant		= rtw8723b_efuse_grant,
+	.set_ampdu_factor	= NULL,
+	.false_alarm_statistics	= rtw8723x_false_alarm_statistics,
+	.phy_calibration	= rtw8723b_phy_calibration,
+	.dpk_track		= NULL,
+	/* REG_CSRATIO does not exist on this chip generation. */
+	.cck_pd_set		= NULL,

You can use rtw88xxa_phy_cck_pd_set() from rtw88xxa.c for this chip too.

Quoted 35 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +35 -0
+	.pwr_track		= rtw8723b_pwr_track,
+	.config_bfee		= NULL,
+	.set_gid_table		= NULL,
+	.cfg_csi_rate		= NULL,
+	.adaptivity_init	= NULL,
+	.adaptivity		= NULL,
+	.cfo_init		= NULL,
+	.cfo_track		= NULL,
+	.config_tx_path		= NULL,
+	.config_txrx_mode	= NULL,
+	.led_set		= NULL,
+	.fill_txdesc_checksum	= rtw8723b_fill_txdesc_checksum,
+
+	.coex_set_init		= rtw8723b_coex_cfg_init,
+	.coex_set_ant_switch	= rtw8723b_coex_cfg_ant_switch,
+	.coex_set_gnt_fix	= rtw8723b_coex_set_gnt_fix,
+	.coex_set_gnt_debug	= rtw8723b_coex_set_gnt_debug,
+	.coex_set_rfe_type	= rtw8723b_coex_set_rfe_type,
+	.coex_set_wl_tx_power	= rtw8723b_coex_set_wl_tx_power,
+	.coex_set_wl_rx_gain	= rtw8723b_coex_set_wl_rx_gain,
+};
+
+const struct rtw_chip_info rtw8723b_hw_spec = {
+	.ops = &rtw8723b_ops,
+	.id = RTW_CHIP_TYPE_8723B,
+	.fw_name = "rtw88/rtw8723b_fw.bin",
+	.wlan_cpu = RTW_WCPU_8051,
+	.tx_pkt_desc_sz = 40,
+	.tx_buf_desc_sz = 16,
+	.rx_pkt_desc_sz = 24,
+	.rx_buf_desc_sz = 8,
+	.phy_efuse_size = 512,
+	.log_efuse_size = 512,
+	.ptct_efuse_size = 15,
+	.txff_size = 32768,

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2753

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	.rxff_size = 16384,
+	.rsvd_drv_pg_num = 8,
+	.txgi_factor = 1,
+	.is_pwr_by_rate_dec = true,
+	.max_power_index = 0x3f,
+	.csi_buf_pg_num = 0,
+	.band = RTW_BAND_2G,
+	.page_size = TX_PAGE_SIZE,
+	.dig_min = 0x20,
+	.usb_tx_agg_desc_num = 1,

The RTL8723BU vendor driver v5.2.17 sets this to 6.

Quoted 12 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +12 -0
+	/*
+	 * The firmware reports id 0xfd instead of C2H_HW_FEATURE_REPORT, so
+	 * the hardware feature report is not supported on this chip.
+	 */
+	.hw_feature_report = false,
+	.c2h_ra_report_size = 4,
+	.old_datarate_fb_limit = true,
+	.path_div_supported = false,
+	.ht_supported = true,
+	.vht_supported = false,
+	.lps_deep_mode_supported = 0,
+	.sys_func_en = 0xfd,

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2775

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	.pwr_on_seq = card_enable_flow_8723b,
+	.pwr_off_seq = card_disable_flow_8723b,
+	.page_table = page_table_8723b,
+	.rqpn_table = rqpn_table_8723b,
+	/* same shared table as the sibling rtw8703b and rtw8723d */
+	.prioq_addrs = &rtw8723x_common.prioq_addrs,
+	/* used only in pci.c, not needed for SDIO devices */
+	.intf_table = NULL,
+	.dig = rtw8723x_common.dig,
+	.dig_cck = rtw8723x_common.dig_cck,

This chip doesn't have dig_cck.

Quoted 75 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +56 -0
+	.rf_sipi_addr = {0x840, 0x844},
+	.rf_sipi_read_addr = rtw8723x_common.rf_sipi_addr,
+	.fix_rf_phy_num = 2,
+	/* This chip has no LTE coex registers. */
+	.ltecoex_addr = NULL,
+	.mac_tbl = &rtw8723b_mac_tbl,
+	.agc_tbl = &rtw8723b_agc_tbl,
+	.bb_tbl = &rtw8723b_bb_tbl,
+	.rf_tbl = {&rtw8723b_rf_a_tbl},
+	.rfe_defs = rtw8723b_rfe_defs,
+	.rfe_defs_size = ARRAY_SIZE(rtw8723b_rfe_defs),
+	.iqk_threshold = 8,
+	.rx_ldpc = false,
+	.tx_stbc = false,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
+	.coex_para_ver = 20180201,	/* glcoex_ver_date_8723b_1ant */
+	.bt_desired_ver = 0x6d,
+	.scbd_support = false,
+	.new_scbd10_def = true,
+	.ble_hid_profile_support = false,
+	.wl_mimo_ps_support = false,
+	.pstdma_type = COEX_PSTDMA_FORCE_LPSOFF,
+	.bt_rssi_type = COEX_BTRSSI_RATIO,
+	.ant_isolation = 15,
+	.rssi_tolerance = 2,
+	.wl_rssi_step = wl_rssi_step_8723b,
+	.bt_rssi_step = bt_rssi_step_8723b,
+	.table_sant_num = ARRAY_SIZE(table_sant_8723b),
+	.table_sant = table_sant_8723b,
+	.table_nsant_num = ARRAY_SIZE(table_nsant_8723b),
+	.table_nsant = table_nsant_8723b,
+	.tdma_sant_num = ARRAY_SIZE(tdma_sant_8723b),
+	.tdma_sant = tdma_sant_8723b,
+	.tdma_nsant_num = ARRAY_SIZE(tdma_nsant_8723b),
+	.tdma_nsant = tdma_nsant_8723b,
+	.wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8723b),
+	.wl_rf_para_tx = rf_para_tx_8723b,
+	.wl_rf_para_rx = rf_para_rx_8723b,
+	.bt_afh_span_bw20 = 0x20,
+	.bt_afh_span_bw40 = 0x30,
+	.afh_5g_num =  ARRAY_SIZE(afh_5g_8723b),
+	.afh_5g = afh_5g_8723b,
+	/* BTG_SEL is driven by the cardemu_to_act power sequence instead. */
+	.btg_reg = NULL,
+	.coex_info_hw_regs_num = 0,
+	.coex_info_hw_regs = NULL,
+};
+EXPORT_SYMBOL(rtw8723b_hw_spec);
+
+MODULE_FIRMWARE("rtw88/rtw8723b_fw.bin");
+
+MODULE_AUTHOR("Luka Gejak <[email protected]>");
+MODULE_AUTHOR("Michael Straube <[email protected]>");
+MODULE_DESCRIPTION("Realtek 802.11n wireless 8723b driver");
+MODULE_LICENSE("Dual BSD/GPL");
drivers/net/wireless/realtek/rtw88/rtw8723b.h new file +13 -0
new file mode 100644
index 000000000000..181782ec3f1b
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright(c) 2026 Realtek Corporation
+ */
+
+#ifndef __RTW8723B_H__
+#define __RTW8723B_H__
+
+#include "rtw8723x.h"
+
+extern const struct rtw_chip_info rtw8723b_hw_spec;
+
+#endif
#6 lore raw

Re: [PATCH v4 3/6] wifi: rtw88: 8723b: add the RTL8723B chip driver

Quoted 953 lines · Bitterblue Smith
Quoted 845 lines · Luka Gejak

Add the Realtek RTL8723B 802.11n chip driver: the chip operations, the power sequences, the efuse layout, the RF and IQ calibration, and the chip specific coexistence handling.

The RTL8723B chip support is based on the initial work by Michael Straube <[email protected]>.

---
 drivers/net/wireless/realtek/rtw88/rtw8723b.c | 2831 +++++++++++++++++
 drivers/net/wireless/realtek/rtw88/rtw8723b.h |   13 +
 2 files changed, 2844 insertions(+)
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b.c
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723b.h
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +821 -0
new file mode 100644
index 000000000000..a26908bdc989
@@ -0,0 +1,2831 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright(c) 2026 Realtek Corporation
+ * Copyright(c) Michael Straube <[email protected]>
+ * Copyright(c) 2024-2026 Luka Gejak <[email protected]>
+ */
+
+#include <linux/unaligned.h>
+#include "main.h"
+#include "coex.h"
+#include "fw.h"
+#include "mac.h"
+#include "phy.h"
+/*
+ * Shares the receive PHY status layout, the SDIO aggregation burst fields
+ * and a few baseband registers with the RTL8703B; reuse that header.
+ */
+#include "rtw8703b.h"
+#include "rtw8723b.h"
+#include "rtw8723b_table.h"
+#include "sdio.h"
+#include "tx.h"
+
+#define TRANS_SEQ_END			\
+	0xFFFF,				\
+	RTW_PWR_CUT_ALL_MSK,		\
+	RTW_PWR_INTF_ALL_MSK,		\
+	0,				\
+	RTW_PWR_CMD_END, 0, 0
+
+#define TBTT_PROHIBIT_SETUP_TIME		0x04
+#define TBTT_PROHIBIT_HOLD_TIME_STOP_BCN	0x64
+#define WLAN_BCN_DMA_TIME			0x02
+#define WLAN_BAR_VAL				0x0201ffff
+#define WLAN_SLOT_TIME				0x09
+#define WLAN_SYS_FUNC_BB_ENABLE			(BIT_FEN_BB_GLB_RST | \
+						 BIT_FEN_BB_RSTB)
+#define WLAN_RF_CTRL_ENABLE			(BIT_RF_EN | BIT_RF_RSTB | \
+						 BIT_RF_SDM_RSTB)
+/*
+ * 0x03a05611 is the normal RX path (staging and the BB table agree);
+ * 0x03a05600 is only an IQK temporary value and must not be reasserted.
+ */
+#define WLAN_RX_PATH_A_8723B			0x03a05611
+
+#define ADDA_ON_VAL_8723B			0x01c00014
+
+#define WLAN_RX_FILTER0			0xFFFF
+#define WLAN_RX_FILTER1			0x400
+#define WLAN_RX_FILTER2			0xFFFF
+/*
+ * Keep BIT_APP_FCS: rtw88 advertises RX_INCLUDES_FCS for every chip, and
+ * without it mac80211 trims four bytes of real frame data.
+ */
+#define WLAN_RCR_CFG			(BIT_APM | BIT_AM | BIT_AB | \
+					 BIT_CBSSID_DATA | BIT_CBSSID_BCN | \
+					 BIT_AMF | BIT_HTC_LOC_CTRL | \
+					 BIT_APP_PHYSTS | BIT_APP_ICV | \
+					 BIT_APP_MIC | BIT_APP_FCS)
+
+/*
+ * BIT(7) lets the 8051 control antenna selection, BIT(1) is LED2_CM.
+ */
+#define WLAN_ANT_SEL			(BIT(7) | BIT(1))
+
+#define IQK_DELAY_TIME_8723B		20
+
+#define BCNQ_PAGE_NUM_8723B	0x08
+#define BCNQ1_PAGE_NUM_8723B		0x00
+#define WOWLAN_PAGE_NUM_8723B		0x00
+#define TX_TOTAL_PAGE_NUMBER_8723B\
+	(0xFF - BCNQ_PAGE_NUM_8723B - BCNQ1_PAGE_NUM_8723B - \
+	 WOWLAN_PAGE_NUM_8723B)
+
+/* rssi in percent (dbm = % - 100); the values are the vendor driver's. */
+static const u8 wl_rssi_step_8723b[] = {60, 50, 44, 30};
+static const u8 bt_rssi_step_8723b[] = {30, 30, 30, 30};
+static const struct coex_5g_afh_map afh_5g_8723b[] = { {0, 0, 0} };
+
+static const struct coex_rf_para rf_para_tx_8723b[] = {
+	{0, 0, false, 7},  /* for normal */
+	{0, 10, false, 7}, /* for WL-CPT */
+	{1, 0, true, 4},
+	{1, 2, true, 4},
+	{1, 10, true, 4},
+	{1, 15, true, 4}
+};
+
+static const struct coex_rf_para rf_para_rx_8723b[] = {
+	{0, 0, false, 7},  /* for normal */
+	{0, 10, false, 7}, /* for WL-CPT */
+	{1, 0, true, 5},
+	{1, 2, true, 5},
+	{1, 10, true, 5},
+	{1, 15, true, 5}
+};
+
+static_assert(ARRAY_SIZE(rf_para_tx_8723b) == ARRAY_SIZE(rf_para_rx_8723b));
+
+static const u32 rtw8723b_ofdm_swing_table[] = {
+	0x0b40002d, /* 0, -15.0dB */
+	0x0c000030, /* 1, -14.5dB */
+	0x0cc00033, /* 2, -14.0dB */
+	0x0d800036, /* 3, -13.5dB */
+	0x0e400039, /* 4, -13.0dB */
+	0x0f00003c, /* 5, -12.5dB */
+	0x10000040, /* 6, -12.0dB */
+	0x11000044, /* 7, -11.5dB */
+	0x12000048, /* 8, -11.0dB */
+	0x1300004c, /* 9, -10.5dB */
+	0x14400051, /* 10, -10.0dB */
+	0x15800056, /* 11, -9.5dB */
+	0x16c0005b, /* 12, -9.0dB */
+	0x18000060, /* 13, -8.5dB */
+	0x19800066, /* 14, -8.0dB */
+	0x1b00006c, /* 15, -7.5dB */
+	0x1c800072, /* 16, -7.0dB */
+	0x1e400079, /* 17, -6.5dB */
+	0x20000080, /* 18, -6.0dB */
+	0x22000088, /* 19, -5.5dB */
+	0x24000090, /* 20, -5.0dB */
+	0x26000098, /* 21, -4.5dB */
+	0x288000a2, /* 22, -4.0dB */
+	0x2ac000ab, /* 23, -3.5dB */
+	0x2d4000b5, /* 24, -3.0dB */
+	0x300000c0, /* 25, -2.5dB */
+	0x32c000cb, /* 26, -2.0dB */
+	0x35c000d7, /* 27, -1.5dB */
+	0x390000e4, /* 28, -1.0dB */
+	0x3c8000f2, /* 29, -0.5dB */
+	0x40000100, /* 30, +0dB */
+	0x43c0010f, /* 31, +0.5dB */
+	0x47c0011f, /* 32, +1.0dB */
+	0x4c000130, /* 33, +1.5dB */
+	0x50800142, /* 34, +2.0dB */
+	0x55400155, /* 35, +2.5dB */
+	0x5a400169, /* 36, +3.0dB */
+	0x5fc0017f, /* 37, +3.5dB */
+	0x65400195, /* 38, +4.0dB */
+	0x6b8001ae, /* 39, +4.5dB */
+	0x71c001c7, /* 40, +5.0dB */
+	0x788001e2, /* 41, +5.5dB */
+	0x7f8001fe, /* 42, +6.0dB */
+};
+
+static const u32 rtw8723b_cck_pwr_regs[] = {
+	0x0a22, 0x0a23, 0x0a24, 0x0a25, 0x0a26, 0x0a27, 0x0a28, 0x0a29,
+};
+
+/*
+ * Row 20 (-6.0 dB) intentionally does not match the v5.2.17 vendor driver,
+ * which has 0x1c, 0x1a, 0x18, 0x12, 0x0e, 0x08 there. Every other row agrees.
+ * The values below are what rtl8723be, the mainline driver for this same
+ * chip, uses at the same index, and they are also what the vendor's own
+ * cck_swing_table_ch1_ch13_92e and the staging rtl8723bs driver use. They
+ * also track the 0.5 dB step of the surrounding rows: against row 32 as 0 dB,
+ * 0x1b is within 0.06 of the ideal -6.0 dB value while 0x1c is 0.94 away,
+ * the largest error anywhere in the table. Treat the vendor row as the
+ * anomaly and do not "fix" this towards it.
+ */
+static const u8 rtw8723b_cck_swing_table_ch1_ch13[][8] = {
+	{0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01},	/* 0, -16.0dB */
+	{0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01},	/* 1, -15.5dB */
+	{0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01},	/* 2, -15.0dB */
+	{0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01},	/* 3, -14.5dB */
+	{0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01},	/* 4, -14.0dB */
+	{0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01},	/* 5, -13.5dB */
+	{0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01},	/* 6, -13.0dB */
+	{0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01},	/* 7, -12.5dB */
+	{0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01},	/* 8, -12.0dB */
+	{0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01},	/* 9, -11.5dB */
+	{0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01},	/* 10, -11.0dB */
+	{0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01},	/* 11, -10.5dB */
+	{0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01},	/* 12, -10.0dB */
+	{0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01},	/* 13, -9.5dB */
+	{0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01},	/* 14, -9.0dB */
+	{0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02},	/* 15, -8.5dB */
+	{0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01},	/* 16, -8.0dB */
+	{0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02},	/* 17, -7.5dB */
+	{0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02},	/* 18, -7.0dB */
+	{0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02},	/* 19, -6.5dB */
+	{0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02},	/* 20, -6.0dB */
+	{0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02},	/* 21, -5.5dB */
+	{0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02},	/* 22, -5.0dB */
+	{0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02},	/* 23, -4.5dB */
+	{0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02},	/* 24, -4.0dB */
+	{0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03},	/* 25, -3.5dB */
+	{0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03},	/* 26, -3.0dB */
+	{0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03},	/* 27, -2.5dB */
+	{0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03},	/* 28, -2.0dB */
+	{0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03},	/* 29, -1.5dB */
+	{0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03},	/* 30, -1.0dB */
+	{0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04},	/* 31, -0.5dB */
+	{0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04},	/* 32, +0dB */
+};
+
+static const u8 rtw8723b_cck_swing_table_ch14[][8] = {
+	{0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00},	/* 0, -16.0dB */
+	{0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 1, -15.5dB */
+	{0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 2, -15.0dB */
+	{0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 3, -14.5dB */
+	{0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00},	/* 4, -14.0dB */
+	{0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00},	/* 5, -13.5dB */
+	{0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00},	/* 6, -13.0dB */
+	{0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00},	/* 7, -12.5dB */
+	{0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00},	/* 8, -12.0dB */
+	{0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00},	/* 9, -11.5dB */
+	{0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00},	/* 10, -11.0dB */
+	{0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00},	/* 11, -10.5dB */
+	{0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00},	/* 12, -10.0dB */
+	{0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00},	/* 13, -9.5dB */
+	{0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00},	/* 14, -9.0dB */
+	{0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00},	/* 15, -8.5dB */
+	{0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00},	/* 16, -8.0dB */
+	{0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00},	/* 17, -7.5dB */
+	{0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00},	/* 18, -7.0dB */
+	{0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00},	/* 19, -6.5dB */
+	{0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00},	/* 20, -6.0dB */
+	{0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00},	/* 21, -5.5dB */
+	{0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00},	/* 22, -5.0dB */
+	{0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00},	/* 23, -4.5dB */
+	{0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00},	/* 24, -4.0dB */
+	{0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00},	/* 25, -3.5dB */
+	{0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00},	/* 26, -3.0dB */
+	{0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00},	/* 27, -2.5dB */
+	{0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00},	/* 28, -2.0dB */
+	{0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00},	/* 29, -1.5dB */
+	{0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00},	/* 30, -1.0dB */
+	{0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00},	/* 31, -0.5dB */
+	{0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00},	/* 32, +0dB */
+};
+
+static_assert(ARRAY_SIZE(rtw8723b_cck_swing_table_ch1_ch13) ==
+	      ARRAY_SIZE(rtw8723b_cck_swing_table_ch14));
+
+#define RTW_OFDM_SWING_TABLE_SIZE	ARRAY_SIZE(rtw8723b_ofdm_swing_table)
+#define RTW_CCK_SWING_TABLE_SIZE	ARRAY_SIZE(rtw8723b_cck_swing_table_ch14)
+
+/*
+ * Swing index field of REG_OFDM_0_XA_TX_IQ_IMBALANCE; the table entries
+ * below hold the full register value, hence the shift by __ffs().
+ */
+#define OFDM_SWING_MASK			GENMASK(31, 22)
+
+static const struct rtw_pwr_seq_cmd trans_pre_enable_8723b[] = {
+	/* unlock ISO/CLK/power control register */
+	{REG_RSV_CTRL,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xff, 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8723b[] = {
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3) | BIT(7), 0},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_POLLING, BIT(1), BIT(1)},
+	{0x004A,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), 0},
+	{0x0023,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4), 0},
+	{0x0301,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_cardemu_to_act_8723b[] = {
+	{0x0020,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0067,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4), 0},
+	{0x0001,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_DELAY, 1, RTW_PWR_DELAY_MS},
+	{0x0000,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(5), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3) | BIT(2)), 0},
+	{0x0075,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0006,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, BIT(1), BIT(1)},
+	{0x0075,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0006,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(7), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4) | BIT(3), 0},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, BIT(0), 0},
+	{0x0010,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(6), BIT(6)},
+	{0x0049,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0063,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0062,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0058,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x005A,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0068,
+	 RTW_PWR_CUT_TEST_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3), BIT(3)},
+	{0x0069,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(6), BIT(6)},
+	 {TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_act_to_lps_8723b[] = {
+	{0x0301,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0xFF},
+	{0x0522,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0xFF},
+	{0x05F8,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x05F9,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x05FA,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x05FB,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, 0xFF, 0},
+	{0x0002,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{0x0002,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_DELAY, 0, RTW_PWR_DELAY_US},
+	{0x0002,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0100,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0x03},
+	{0x0101,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0093,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0x00},
+	{0x0553,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(5), BIT(5)},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_act_to_reset_mcu_8723b[] = {
+	{REG_SYS_FUNC_EN + 1,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT_FEN_CPUEN, 0},
+	/* reset MCU ready */
+	{REG_MCUFW_CTRL,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xff, 0},
+	/* reset MCU IO wrapper */
+	{REG_RSV_CTRL + 1,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{REG_RSV_CTRL + 1,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 1},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_act_to_cardemu_8723b[] = {
+	{0x001F,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0},
+	{0x0049,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), 0},
+	{0x0006,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_POLLING, BIT(1), 0},
+	{0x0010,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_ALL_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(6), 0},
+	{0x0000,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(5), BIT(5)},
+	{0x0020,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd trans_cardemu_to_carddis_8723b[] = {
+	{0x0007,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, 0xFF, 0x20},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3)},
+	{0x0005,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_PCI_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(2), BIT(2)},
+	{0x004A,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_USB_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(0), 1},
+	{0x0023,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_MAC,
+	 RTW_PWR_CMD_WRITE, BIT(4), BIT(4)},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+	{0x0086,
+	 RTW_PWR_CUT_ALL_MSK,
+	 RTW_PWR_INTF_SDIO_MSK,
+	 RTW_PWR_ADDR_SDIO,
+	 RTW_PWR_CMD_POLLING, BIT(1), 0},
+	{TRANS_SEQ_END},
+};
+
+static const struct rtw_pwr_seq_cmd * const card_enable_flow_8723b[] = {
+	trans_pre_enable_8723b,
+	trans_carddis_to_cardemu_8723b,
+	trans_cardemu_to_act_8723b,
+	NULL
+};
+
+static const struct rtw_pwr_seq_cmd * const card_disable_flow_8723b[] = {
+	trans_act_to_lps_8723b,
+	trans_act_to_reset_mcu_8723b,
+	trans_act_to_cardemu_8723b,
+	trans_cardemu_to_carddis_8723b,
+	NULL
+};
+
+static const struct rtw_page_table page_table_8723b[] = {
+	{12, 2, 2, 0, 1}, /* SDIO */
+	{12, 2, 2, 0, 1},
+	{12, 2, 2, 0, 1},
+	{12, 2, 2, 0, 1},
+	{12, 2, 2, 0, 1},
+};
+
+static const struct rtw_rqpn rqpn_table_8723b[] = {
+	/* SDIO maps VO, MGMT and HI to the high queue. */
+	{RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* PCIE */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* USB bulkout 2 */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_HIGH,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* USB bulkout 3 */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+	/* USB bulkout 4 */
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+};
+
+static const u8 rtw8723b_pwrtrk_2gb_n[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 6, 6,
+	7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2gb_p[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+	9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2ga_n[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 6, 6,
+	7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2ga_p[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+	9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_b_n[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_b_p[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 7, 7,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_a_n[] = {
+	0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const u8 rtw8723b_pwrtrk_2g_cck_a_p[] = {
+	0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 7, 7,
+	8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15
+};
+
+static const struct rtw_pwr_track_tbl rtw8723b_rtw_pwr_track_tbl = {
+	.pwrtrk_2gb_n = rtw8723b_pwrtrk_2gb_n,
+	.pwrtrk_2gb_p = rtw8723b_pwrtrk_2gb_p,
+	.pwrtrk_2ga_n = rtw8723b_pwrtrk_2ga_n,
+	.pwrtrk_2ga_p = rtw8723b_pwrtrk_2ga_p,
+	.pwrtrk_2g_cckb_n = rtw8723b_pwrtrk_2g_cck_b_n,
+	.pwrtrk_2g_cckb_p = rtw8723b_pwrtrk_2g_cck_b_p,
+	.pwrtrk_2g_ccka_n = rtw8723b_pwrtrk_2g_cck_a_n,
+	.pwrtrk_2g_ccka_p = rtw8723b_pwrtrk_2g_cck_a_p,
+	/* rtw8723x_pwrtrack_set_xtal() is not used on this chip. */
+	.pwrtrk_xtal_n = NULL,
+	.pwrtrk_xtal_p = NULL,
+};
+
+static const struct rtw_rfe_def rtw8723b_rfe_defs[] = {
+	[0] = { .phy_pg_tbl	= &rtw8723b_bb_pg_tbl,
+		.txpwr_lmt_tbl	= &rtw8723b_txpwr_lmt_tbl,
+		.pwr_track_tbl	= &rtw8723b_rtw_pwr_track_tbl, },
+};
+
+/* Shared-Antenna Coex Table */
+static const struct coex_table_para table_sant_8723b[] = {
+	{0xffffffff, 0xffffffff}, /* case-0 */
+	{0x55555555, 0x55555555},
+	{0x66555555, 0x66555555},
+	{0xaaaaaaaa, 0xaaaaaaaa},
+	{0x5a5a5a5a, 0x5a5a5a5a},
+	{0xfafafafa, 0xfafafafa}, /* case-5 */
+	{0x6a5a5555, 0xaaaaaaaa},
+	{0x6a5a56aa, 0x6a5a56aa},
+	{0x6a5a5a5a, 0x6a5a5a5a},
+	{0x66555555, 0x5a5a5a5a},
+	{0x66555555, 0x6a5a5a5a}, /* case-10 */
+	{0x66555555, 0x6a5a5aaa},
+	{0x66555555, 0x5a5a5aaa},
+	{0x66555555, 0x6aaa5aaa},
+	{0x66555555, 0xaaaa5aaa},
+	{0x66555555, 0xaaaaaaaa}, /* case-15 */
+	{0xffff55ff, 0xfafafafa},
+	{0xffff55ff, 0x6afa5afa},
+	{0xaaffffaa, 0xfafafafa},
+	{0xaa5555aa, 0x5a5a5a5a},
+	{0xaa5555aa, 0x6a5a5a5a}, /* case-20 */
+	{0xaa5555aa, 0xaaaaaaaa},
+	{0xffffffff, 0x5a5a5a5a},
+	{0xffffffff, 0x5a5a5a5a},
+	{0xffffffff, 0x55555555},
+	{0xffffffff, 0x5a5a5aaa}, /* case-25 */
+	{0x55555555, 0x5a5a5a5a},
+	{0x55555555, 0xaaaaaaaa},
+	{0x55555555, 0x6a5a6a5a},
+	{0x66556655, 0x66556655},
+	{0x66556aaa, 0x6a5a6aaa}, /* case-30 */
+	{0xffffffff, 0x5aaa5aaa},
+	{0x56555555, 0x5a5a5aaa},
+};
+
+/* Non-Shared-Antenna Coex Table */
+static const struct coex_table_para table_nsant_8723b[] = {
+	{0xffffffff, 0xffffffff}, /* case-100 */
+	{0x55555555, 0x55555555},
+	{0x66555555, 0x66555555},
+	{0xaaaaaaaa, 0xaaaaaaaa},
+	{0x5a5a5a5a, 0x5a5a5a5a},
+	{0xfafafafa, 0xfafafafa}, /* case-105 */
+	{0x5afa5afa, 0x5afa5afa},
+	{0x55555555, 0xfafafafa},
+	{0x66555555, 0xfafafafa},
+	{0x66555555, 0x5a5a5a5a},
+	{0x66555555, 0x6a5a5a5a}, /* case-110 */
+	{0x66555555, 0xaaaaaaaa},
+	{0xffff55ff, 0xfafafafa},
+	{0xffff55ff, 0x5afa5afa},
+	{0xffff55ff, 0xaaaaaaaa},
+	{0xffff55ff, 0xffff55ff}, /* case-115 */
+	{0xaaffffaa, 0x5afa5afa},
+	{0xaaffffaa, 0xaaaaaaaa},
+	{0xffffffff, 0xfafafafa},
+	{0xffffffff, 0x5afa5afa},
+	{0xffffffff, 0xaaaaaaaa}, /* case-120 */
+	{0x55ff55ff, 0x5afa5afa},
+	{0x55ff55ff, 0xaaaaaaaa},
+	{0x55ff55ff, 0x55ff55ff}
+};
+
+/* Shared-Antenna TDMA */
+static const struct coex_tdma_para tdma_sant_8723b[] = {
+	{ {0x00, 0x00, 0x00, 0x00, 0x00} }, /* case-0 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-1 */
+	{ {0x61, 0x3a, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-5 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x3a, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-10 */
+	{ {0x61, 0x08, 0x03, 0x11, 0x14} },
+	{ {0x61, 0x08, 0x03, 0x10, 0x14} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x55} },
+	{ {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-15 */
+	{ {0x51, 0x45, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x3a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x30, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x20, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-20 */
+	{ {0x51, 0x4a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x0c, 0x03, 0x10, 0x54} },
+	{ {0x55, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x65, 0x10, 0x03, 0x11, 0x10} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x51} }, /* case-25 */
+	{ {0x51, 0x08, 0x03, 0x10, 0x50} },
+	{ {0x61, 0x08, 0x03, 0x11, 0x11} }
+};
+
+/* Non-Shared-Antenna TDMA */
+static const struct coex_tdma_para tdma_nsant_8723b[] = {
+	{ {0x00, 0x00, 0x00, 0x00, 0x01} }, /* case-100 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-101 */
+	{ {0x61, 0x3a, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-105 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x3a, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-110 */
+	{ {0x61, 0x08, 0x03, 0x11, 0x14} },
+	{ {0x61, 0x08, 0x03, 0x10, 0x14} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x55} },
+	{ {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-115 */
+	{ {0x51, 0x45, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x3a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x30, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x20, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-120 */
+	{ {0x51, 0x08, 0x03, 0x10, 0x50} }
+};
+
+static void rtw8723b_efuse_grant(struct rtw_dev *rtwdev, bool on)
+{
+	rtw_write8_set(rtwdev, REG_BT_EFUSE_CTRL_8723B,
+		       on ? BIT_BT_PWR_CUT : BIT_BT_OUT_ISO);
+
+	rtw8723x_efuse_grant(rtwdev, on);
+}
+
+static u8 rtw8723b_default_ofdm_index(struct rtw_dev *rtwdev)
+{
+	u32 val32;
+	u32 swing;

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 831

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	u8 i;
+
+	swing = rtw_read32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+				OFDM_SWING_MASK);
+
+	for (i = 0; i < RTW_OFDM_SWING_TABLE_SIZE; i++) {
+		val32 = rtw8723b_ofdm_swing_table[i];
+
+		if (val32 >= 0x100000)
+			val32 >>= __ffs(OFDM_SWING_MASK);

Isn't this the same as u32_get_bits(val32, OFDM_SWING_MASK) ?

Also, every value in the table is bigger than 0x100000, so no need for if.

Quoted 69 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +69 -0
+
+		if (val32 == swing)
+			break;
+	}
+
+	if (i >= RTW_OFDM_SWING_TABLE_SIZE)
+		i = 30; /* 30, +0dB */
+
+	return i;
+}
+
+static u8 rtw8723b_default_cck_index(struct rtw_dev *rtwdev)
+{
+	u8 i;
+	u8 swing;
+
+	swing = rtw_read8(rtwdev, rtw8723b_cck_pwr_regs[0]);
+
+	for (i = 0; i < RTW_CCK_SWING_TABLE_SIZE; i++) {
+		if (rtw8723b_cck_swing_table_ch1_ch13[i][0] == swing)
+			break;
+	}
+
+	if (i >= RTW_CCK_SWING_TABLE_SIZE)
+		i = 20; /* 20, -6.0dB */
+
+	return i;
+}
+
+static void rtw8723b_pwrtrack_init(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u8 path;
+
+	dm_info->default_ofdm_index = rtw8723b_default_ofdm_index(rtwdev);
+	dm_info->default_cck_index = rtw8723b_default_cck_index(rtwdev);
+
+	/* Clear the OFDM remnant per path: tracking keeps one per path. */
+	for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
+		ewma_thermal_init(&dm_info->avg_thermal[path]);
+		dm_info->delta_power_index[path] = 0;
+		dm_info->txagc_remnant_ofdm[path] = 0;
+	}
+	dm_info->pwr_trk_triggered = false;
+	dm_info->pwr_trk_init_trigger = true;
+	dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k;
+	dm_info->txagc_remnant_cck = 0;
+}
+
+static void rtw8723b_sdio_restore_pad_ctrl(struct rtw_dev *rtwdev,
+					   bool keep_pta_owner)
+{
+	u32 before;
+	u32 after;
+
+	if (!rtw_is_8723bs(rtwdev))
+		return;
+
+	before = rtw_read32(rtwdev, REG_PAD_CTRL1);
+	after = before & ~(BIT_LNAON_WLBT_SEL | BIT_SW_DPDT_SEL_DATA);
+	if (keep_pta_owner)
+		after |= BIT_PAPE_WLBT_SEL;
+	else
+		after &= ~BIT_PAPE_WLBT_SEL;
+	if (after == before)
+		return;
+
+	rtw_write32(rtwdev, REG_PAD_CTRL1, after);
+}

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 910

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+static void rtw8723b_post_enable_flow(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Enable falling edge triggered interrupts and GPIO9 interrupt mode.
+	 * The power-on sequence sets both, but it runs before the firmware is
+	 * downloaded, so reassert them here as the vendor driver does.
+	 */
+	rtw_write8_set(rtwdev, 0x0049, BIT(1));
+	rtw_write8_set(rtwdev, 0x0063, BIT(1));

This seems like bit 25 of REG_GPIO_PIN_CTRL_2.

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+	rtw_write16_set(rtwdev, REG_APS_FSMCO, BIT_EN_PDN);
+
+	/*
+	 * Only OR in the missing enables: writing CR to zero would reset the
+	 * hardware free-page counters and leave TX DMA no pages to allocate.
+	 */
+	rtw_write16_set(rtwdev, REG_CR, MAC_TRX_ENABLE | BIT_MAC_SEC_EN |
+				       BIT_32K_CAL_TMR_EN);
+
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 928

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) {
+		rtw_write16_set(rtwdev, REG_PWR_DATA,
+				BIT_EEPRPAD_RFE_CTRL_EN);
+
+		/*
+		 * rtw_mac_power_on() sets PAD mux bits this chip must not have;
+		 * restore the SDIO PAD mux before RF and coex setup.
+		 */

By the way, rtw_mac_power_on() doesn't touch REG_PAD_CTRL1 for this chip.

Quoted 2,081 lines
Quoted 95 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +95 -0
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, false);
+	}
+
+	rtw_write8(rtwdev, REG_EARLY_MODE_CONTROL, 0);
+
+	/* Keep every MACID eligible for firmware-scheduled TX at power-on. */
+	rtw_write32(rtwdev, REG_MACID_PKT_DROP0, 0);
+	rtw_write32(rtwdev, REG_MACID_PKT_SLEEP, 0);
+}
+
+static void rtw8723b_phy_bb_config(struct rtw_dev *rtwdev)
+{
+	u8 xtal_cap;
+
+	/* Enable BB and RF */
+	rtw_write16_set(rtwdev, REG_SYS_FUNC_EN,
+			BIT_FEN_EN_25_1 | BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB);
+
+	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB)
+		rtw_write32(rtwdev, REG_BB_SEL_BTG, 0x0);
+	else
+		rtw_write32(rtwdev, REG_BB_SEL_BTG, 0x280);
+
+	/* Full write: preserving spuriously set bits can wedge the RF bus. */
+	rtw_write8(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+	fsleep(1000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK, 0x0780);
+	rtw_write8(rtwdev, REG_SYS_FUNC_EN,
+		   BIT_FEN_PPLL | BIT_FEN_PCIEA | BIT_FEN_DIO_PCIE |
+		   BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB);
+	rtw_write8(rtwdev, REG_AFE_CTRL1 + 1, 0x80);
+
+	xtal_cap = rtwdev->efuse.crystal_cap & 0x3f;
+	rtw_write32_mask(rtwdev,  REG_AFE_CTRL3, BIT_MASK_XTAL,
+			 xtal_cap | (xtal_cap << 6));
+}
+
+static void rtw8723b_phy_load_bb_tables(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	const struct rtw_rfe_def *rfe_def = rtw_get_rfe_def(rtwdev);
+
+	rtw_load_table(rtwdev, chip->bb_tbl);
+	rtw_load_table(rtwdev, chip->agc_tbl);
+	if (rfe_def && rfe_def->agc_btg_tbl)
+		rtw_load_table(rtwdev, rfe_def->agc_btg_tbl);
+}
+
+static void rtw8723b_phy_rf6052_config(struct rtw_dev *rtwdev)
+{
+	struct rtw_hal *hal = &rtwdev->hal;
+	u32 intf_s, intf_oe, hssi_2;
+	u32 val32, mask;
+	u8 path;
+
+	for (path = RF_PATH_A; path < hal->rf_path_num; path++) {
+		switch (path) {
+		case RF_PATH_A:
+			intf_s = REG_FPGA0_XA_RF_SW_CTRL;
+			intf_oe = REG_FPGA0_XA_RF_INT_OE;
+			hssi_2 = REG_FPGA0_XA_HSSI_PARM2;
+			mask = RFSI_RFENV;
+			break;
+		case RF_PATH_B:
+			/*
+			 * The path B switch control is the upper half of the
+			 * word at REG_FPGA0_XA_RF_SW_CTRL, which the shifted
+			 * mask already selects. Addressing 0x0872 directly
+			 * would be a 32-bit access on a 2-byte boundary and
+			 * would reach into the register that follows.
+			 */
+			intf_s = REG_FPGA0_XA_RF_SW_CTRL;
+			intf_oe = REG_FPGA0_XB_RF_INT_OE;
+			hssi_2 = REG_FPGA0_XB_HSSI_PARM2;
+			mask = RFSI_RFENV << 16;
+			break;
+		default:
+			rtw_err(rtwdev, "invalid rf path %c\n", path + 'A');
+			return;
+		}
+
+		val32 = rtw_read32_mask(rtwdev, intf_s, mask);
+
+		rtw_write32_mask(rtwdev, intf_oe, RFSI_RFENV << 16, 0x1);
+		udelay(1);
+
+		rtw_write32_mask(rtwdev, intf_oe, RFSI_RFENV, 0x1);
+		udelay(1);
+
+		rtw_write32_mask(rtwdev, hssi_2, HSSI_3WIRE_ADDR_LEN, 0x0);
+		udelay(1);
+
+		rtw_write32_mask(rtwdev, hssi_2, HSSI_3WIRE_DATA_LEN, 0x0);
+		udelay(1);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1033

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+		/* Path B has no table of its own; radio_a serves both paths. */
+		rtw_load_table(rtwdev, rtwdev->chip->rf_tbl[RF_PATH_A]);
+
+		rtw_write32_mask(rtwdev, intf_s, mask, val32);
+	}
+}
+
+static void rtw8723b_phy_lck(struct rtw_dev *rtwdev)
+{
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);

We have a name for 0xb0: RF_SYN_PFD. Not sure if it's the right name here.

Quoted 12 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +12 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, 0x8c01);
+	fsleep(200 * 1000);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdffe0);
+}
+
+static void rtw8723b_phy_rf_config(struct rtw_dev *rtwdev)
+{
+	rtw8723b_phy_rf6052_config(rtwdev);
+
+	/* LCK must run as part of the RF configuration. */
+	rtw8723b_phy_lck(rtwdev);
+}

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1055

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+static void rtw8723b_init_tx_buffer_boundary(struct rtw_dev *rtwdev)
+{
+	u8 val8 = TX_TOTAL_PAGE_NUMBER_8723B + 1;
+
+	rtw_write8(rtwdev, REG_BCNQ_BDNY, val8);
+	rtw_write8(rtwdev, REG_MGQ_BDNY, val8);
+	rtw_write8(rtwdev, REG_WMAC_LBK_BF_HD, val8);
+	rtw_write8(rtwdev, REG_TRXFF_BNDY, val8);
+	rtw_write8(rtwdev, REG_DWBCN0_CTRL + 1, val8);

__priority_queue_cfg_legacy() already takes care of these. Is it necessary to set them again here?

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1066

Quoted 11 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +11 -0
+}
+
+static void rtw8723b_init_page_boundary(struct rtw_dev *rtwdev)
+{
+	/* Matches the value __priority_queue_cfg_legacy() programs here. */
+	rtw_write16(rtwdev, REG_TRXFF_BNDY + 2, 0x4000 - REPORT_BUF - 1);
+}
+
+static void rtw8723b_init_transfer_page_size(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_PBP, 0x11);

We have some macros for this in reg.h right under REG_PBP. You can use them with u8_encode_bits().

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1071

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+}
+
+static void rtw8723b_init_driver_info_size(struct rtw_dev *rtwdev)
+{
+	/* NOTE: also is done in rtw_drv_info_cfg */

If it's done there do you have to do it again here?

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1084

Quoted 13 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +13 -0
+	rtw_write8(rtwdev, REG_RX_DRVINFO_SZ, PHY_STATUS_SIZE);
+}
+
+static void rtw8723b_init_wmac_setting(struct rtw_dev *rtwdev)
+{
+	/*
+	 * The vendor's 8723x filter value plus BIT_APP_FCS, which rtw88
+	 * needs because it advertises RX_INCLUDES_FCS. It is assigned to
+	 * hal.rcr and not merely written because rtw_core_start()
+	 * rewrites REG_RCR from hal.rcr after power_on, which would undo
+	 * a register-only write; fw.c toggles BIT_CBSSID_BCN in it.
+	 */
+	rtwdev->hal.rcr = WLAN_RCR_CFG;

Is it necessary to change the default value assigned in rtw_core_init()?

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1091

Quoted 7 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +7 -0
+	rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
+
+	rtw_write32(rtwdev, REG_MAR, 0xffffffff);
+	rtw_write32(rtwdev, REG_MAR + 4, 0xffffffff);
+
+	rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2);
+	rtw_write16(rtwdev, REG_RXFLTMAP1, WLAN_RX_FILTER1);

Please also update rtwdev->hal.rxfltmap1 when you change REG_RXFLTMAP1:

https://lore.kernel.org/linux-wireless/[email protected]/

Quoted 34 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +34 -0
+	rtw_write16(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0);
+}
+
+static void rtw8723b_init_adaptive_ctrl(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Firmware validates RRSR at init: narrowing it to the mandatory
+	 * rates makes it drop all management TX, so keep the full set.
+	 */
+	rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, 0xffff1);
+	rtwdev->dm_info.rrsr_val_init = 0xffff1;
+	rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x3030);
+}
+
+static void rtw8723b_init_edca(struct rtw_dev *rtwdev)
+{
+	rtw_write16(rtwdev, REG_SPEC_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_MAC_SPEC_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_SIFS + 2, 0x100a);
+
+	/*
+	 * RESP_SIFS must leave the AP enough slack, or it times out before the
+	 * ACK reaches the air and the unicast handshake never completes.
+	 */
+	rtw_write16(rtwdev, REG_RESP_SIFS_CCK, 0x0808);
+	rtw_write16(rtwdev, REG_RESP_SIFS_OFDM, 0x0a0a);
+
+	/* TXOP */
+	rtw_write32(rtwdev, REG_EDCA_BE_PARAM, 0x005EA42B);
+	rtw_write32(rtwdev, REG_EDCA_BK_PARAM, 0x0000A44F);
+	rtw_write32(rtwdev, REG_EDCA_VI_PARAM, 0x005EA324);
+	rtw_write32(rtwdev, REG_EDCA_VO_PARAM, 0x002FA226);
+}

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1135

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+static void rtw8723b_init_retry_function(struct rtw_dev *rtwdev)
+{
+	rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
+	rtw_write8(rtwdev, REG_ACKTO, 0x40);
+}
+
+static void rtw8723b_init_operation_mode(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_BWOPMODE, BIT_BWOPMODE_20MHZ);

I don't see this in the vendor driver v5.2.17.1.

Quoted 29 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +29 -0
+}
+
+static void rtw8723b_init_beacon_parameters(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Both ports get DIS_TSF_UDT and EN_BCN_FUNCTION only; DIS_BCNQ_SUB
+	 * belongs to AP/IBSS and must stay clear for station mode.
+	 */
+	rtw_write16(rtwdev, REG_BCN_CTRL,
+		    (BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION) |
+		    ((BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION) << 8));
+	rtw_write8(rtwdev, REG_TBTT_PROHIBIT, TBTT_PROHIBIT_SETUP_TIME);
+	rtw_write8(rtwdev, REG_TBTT_PROHIBIT + 1,
+		   TBTT_PROHIBIT_HOLD_TIME_STOP_BCN & 0xff);
+	rtw_write8(rtwdev, REG_TBTT_PROHIBIT + 2,
+		   (rtw_read8(rtwdev, REG_TBTT_PROHIBIT + 2) & 0xf0) |
+		   (TBTT_PROHIBIT_HOLD_TIME_STOP_BCN >> 8));
+
+	rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME);
+	/* Largest beacon AIFS: the chip does not contend before beaconing. */
+	rtw_write16(rtwdev, REG_BCNTCFG, 0x660F);
+}
+
+static void rtw8723b_init_burst_pkt_len(struct rtw_dev *rtwdev)
+{
+	rtw_write8_set(rtwdev, REG_SINGLE_AMPDU_CTRL, BIT_EN_SINGLE_APMDU);
+	rtw_write8(rtwdev, REG_RX_PKT_LIMIT, 0x18);
+	rtw_write8(rtwdev, REG_MAX_AGGR_NUM, 0x1F);
+	rtw_write8(rtwdev, REG_PIFS, 0x00);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1174

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_write8_clr(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
+	rtw_write8(rtwdev, REG_AMPDU_MAX_TIME, 0x70);
+}
+
+static void rtw8723b_init_antenna_selection(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_LEDCFG2, WLAN_ANT_SEL);
+}
+
+#define RF_AC	0x00

You can use the existing RF_MODE name for this.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1185

Quoted 11 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +11 -0
+
+static void rtw8723b_lck(struct rtw_dev *rtwdev)
+{
+	u32 rf_mode = 0, lc_cal;
+	int ret;
+	u8 val_ctx;
+	u8 rf_val;
+
+	val_ctx = rtw_read8(rtwdev, REG_CTX);
+
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0)

No need to compare.

Quoted 11 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +11 -0
+		rtw_write8(rtwdev, REG_CTX, val_ctx & ~BIT_MASK_CTX_TYPE);
+	else
+		rtw_write8(rtwdev, REG_TXPAUSE, 0xff);
+
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) {
+		/* 1. Read original RF mode */
+		rf_mode = rtw_read_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS);
+		/* 2. Set RF mode = standby mode */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS, (rf_mode & 0x8ffff) | 0x10000);
+	}
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1206

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	/* 3. Read RF reg18 */
+	lc_cal = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS);
+
+	/* 4. Set LC calibration begin	bit15 */
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS, lc_cal | BIT_LCK);
+
+	ret = read_poll_timeout(rtw_read_rf, rf_val, rf_val != 0x1,
+				10000, 1000000, false,
+			 rtwdev, RF_PATH_A, RF_CFGCH, BIT_LCK);

checkpatch should catch alignment issues like this. Please always run it.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1219

Quoted 13 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +13 -0
+	if (ret)
+		rtw_warn(rtwdev, "failed to poll LCK status bit\n");
+
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdffe0);
+
+	/* Restore original situation */
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) {
+		rtw_write8(rtwdev, REG_CTX, val_ctx);
+
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS, rf_mode);
+	} else {
+		rtw_write8(rtwdev, REG_TXPAUSE, 0x00);
+	}

I think the vendor driver is using MASK12BITS (0xfff) by mistake in this function. It doesn't make any sense, it should be RFREG_MASK (0xfffff) instead.

Quoted 104 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +104 -0
+}
+
+static void rtw8723b_inform_rfk_status(struct rtw_dev *rtwdev, bool start)
+{
+	int ret;
+	u8 val8;
+
+	rtw_fw_inform_rfk_status(rtwdev, start);
+
+	if (!start)
+		return;
+
+	ret = read_poll_timeout(rtw_read8, val8,
+				val8 & BIT_RFK_FW_ACK_8723B,
+				50000, 400000, false,
+				rtwdev, REG_RFK_FW_ACK_8723B);
+	if (ret)
+		rtw_warn(rtwdev, "failed to poll firmware RFK start ack\n");
+}
+
+static int rtw8723b_mac_init(struct rtw_dev *rtwdev)
+{
+	rtw_write32(rtwdev, REG_INT_MIG, 0);
+	rtw_write32(rtwdev, REG_MCUTST_1, 0x0);
+
+	rtw_write8(rtwdev, REG_MISC_CTRL, BIT_DIS_SECOND_CCA);
+	rtw_write8(rtwdev, REG_2ND_CCA_CTRL, 0x0);
+
+	return 0;
+}
+
+static void rtw8723b_phy_set_param(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	u32 val32;
+
+	rtw8723b_post_enable_flow(rtwdev);
+
+	rtw_load_table(rtwdev, chip->mac_tbl);
+	rtw8723b_phy_bb_config(rtwdev);
+	rtw8723b_phy_load_bb_tables(rtwdev);
+	rtw8723b_phy_rf_config(rtwdev);
+
+	/* enable CCK and OFDM block */
+	rtw_write32_set(rtwdev, REG_FPGA0_RFMOD, BIT_CCKEN | BIT_OFDMEN);
+
+	rtw8723b_init_tx_buffer_boundary(rtwdev);
+	rtw8723b_init_page_boundary(rtwdev);
+	rtw8723b_init_transfer_page_size(rtwdev);
+	rtw8723b_init_driver_info_size(rtwdev);
+
+	/* The MAC table sets REG_RCR and REG_MAR, so apply ours after it. */
+	rtw8723b_init_wmac_setting(rtwdev);
+
+	rtw8723b_init_adaptive_ctrl(rtwdev);
+	rtw8723b_init_edca(rtwdev);
+	rtw8723b_init_retry_function(rtwdev);
+
+	/* sdio.c sets the DMA mode, but not the burst parameters. */
+	rtw_write8(rtwdev, REG_RXDMA_MODE,
+		   BIT_DMA_MODE |
+		   FIELD_PREP_CONST(BIT_MASK_AGG_BURST_NUM, AGG_BURST_NUM) |
+		   FIELD_PREP_CONST(BIT_MASK_AGG_BURST_SIZE, AGG_BURST_SIZE));
+
+	rtw8723b_init_operation_mode(rtwdev);
+	rtw8723b_init_beacon_parameters(rtwdev);
+	rtw8723b_init_burst_pkt_len(rtwdev);
+
+	/*
+	 * 256 ms per-AC lifetime: the ROM default of ~1 s lets the chip retry
+	 * data frames past the EAPOL retry window and reorders the connect.
+	 */
+	rtw_write16(rtwdev, REG_PKT_VO_VI_LIFE_TIME, 0x0400);
+	rtw_write16(rtwdev, REG_PKT_BE_BK_LIFE_TIME, 0x0400);
+
+	rtw_write8(rtwdev, REG_SLOT, WLAN_SLOT_TIME);
+
+	/* disable BAR */
+	rtw_write32(rtwdev, REG_BAR_MODE_CTRL, WLAN_BAR_VAL);
+
+	/* Enable hardware sequence numbering for all queues. */
+	rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0xff);
+
+	/* Enable Rx DMA timer masking: clear 0x0[2:0] and 0x2[15:0] only. */
+	val32 = rtw_read32(rtwdev, REG_SDIO_TX_CTRL);
+	val32 &= 0x0000fff8;
+	rtw_write32(rtwdev, REG_SDIO_TX_CTRL, val32);
+
+	rtw_write16(rtwdev, REG_ATIMWND, 0x2);
+
+	rtw8723b_init_antenna_selection(rtwdev);
+
+	rtw_write8_set(rtwdev, REG_CR, BIT_MACTXEN | BIT_MACRXEN);
+
+	rtw_write8(rtwdev, REG_NAV_UPPER, 0xeb); /* ((30000 + 128 - 1) / 128) */
+
+	/* ack for xmit mgmt frames */
+	rtw_write32_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(12));
+
+	rtw_phy_init(rtwdev);
+
+	rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x50);
+	rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x20);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1333

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw8723b_pwrtrack_init(rtwdev);
+}
+
+static u32 rtw8723b_iqk_ant_switch_path(struct rtw_dev *rtwdev)
+{
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO)
+		return rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB ? 0x280 : 0x0;
+
+	/* Scan and connect use the PTA mux, so calibrate the path they use. */
+	return (rtwdev->efuse.bt_setting & BIT(6)) ? 0x80 : 0x200;

This function would be clearer if you don't use the ternary operator at all.

Not sure this logic is correct. The check in the vendor driver is like this:

bool shared_ant = bt_setting & BIT(0);

if (USB) {
	if (shared_ant)
		ant_path = RF_PATH_B;
	else
		ant_path = RF_PATH_A;
} else {
	if (bt_setting & BIT(6))
		ant_path = RF_PATH_B;
	else
		ant_path = RF_PATH_A;
}

if (!shared_ant || ant_path == RF_PATH_A)

	return 0;
else
	return 0x280;

I didn't see 0x80 and 0x200 in the IQK code.

Quoted 44 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +44 -0
+}
+
+static void rtw8723b_reassert_rx_path(struct rtw_dev *rtwdev)
+{
+	u32 rf_wlint_before;
+	u32 rx_path_before;
+	u32 fpga0_before;
+	u8 sys_func_before;
+	u8 rf_ctrl_before;
+
+	if (!rtw_is_8723bs(rtwdev))
+		return;
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	rf_ctrl_before = rtw_read8(rtwdev, REG_RF_CTRL);
+	fpga0_before = rtw_read32(rtwdev, REG_FPGA0_RFMOD);
+	rx_path_before = rtw_read32(rtwdev, REG_BB_RX_PATH_11N);
+	rf_wlint_before = rtw_read_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK);
+
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
+	    WLAN_SYS_FUNC_BB_ENABLE)
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+
+	if ((rf_ctrl_before & WLAN_RF_CTRL_ENABLE) != WLAN_RF_CTRL_ENABLE) {
+		rtw_write8_set(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+		usleep_range(10, 11);
+	}
+
+	if ((fpga0_before & (BIT_CCKEN | BIT_OFDMEN)) !=
+	    (BIT_CCKEN | BIT_OFDMEN)) {
+		rtw_write32_set(rtwdev, REG_FPGA0_RFMOD,
+				BIT_CCKEN | BIT_OFDMEN);
+	}
+
+	if (rx_path_before != WLAN_RX_PATH_A_8723B) {
+		rtw_write32(rtwdev, REG_BB_RX_PATH_11N,
+			    WLAN_RX_PATH_A_8723B);
+	}
+
+	if (rf_wlint_before != 0x0780) {
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
+			     0x0780);
+	}

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1387

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+}
+
+static void rtw8723b_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw)
+{
+	u32 rf_cfgch;
+
+	rf_cfgch = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK);
+
+	rf_cfgch &= ~RFCFGCH_CHANNEL_MASK;
+	rf_cfgch |= channel & RFCFGCH_CHANNEL_MASK;

This is what u32(p)_replace_bits is for.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1399

Quoted 12 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +12 -0
+
+	rf_cfgch &= ~RFCFGCH_BW_MASK;
+	switch (bw) {
+	case RTW_CHANNEL_WIDTH_20:
+		rf_cfgch |= RFCFGCH_BW_20M;
+		break;
+	case RTW_CHANNEL_WIDTH_40:
+		rf_cfgch |= RFCFGCH_BW_40M;
+		break;
+	default:
+		break;
+	}

bw is only going to be 20 or 40, so a simple if would be shorter.

Quoted 28 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +28 -0
+
+	/* The vendor driver programs both paths from the path A value. */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, rf_cfgch);
+	if (rtwdev->hal.rf_path_num > 1)
+		rtw_write_rf(rtwdev, RF_PATH_B, RF_CFGCH, RFREG_MASK, rf_cfgch);
+}
+
+static void rtw8723b_set_channel_bb(struct rtw_dev *rtwdev, u8 bw,
+				    u8 primary_ch_idx)
+{
+	switch (bw) {
+	case RTW_CHANNEL_WIDTH_20:
+		rtw_write32_mask(rtwdev, REG_FPGA0_RFMOD, BIT_MASK_RFMOD, 0x0);
+		rtw_write32_mask(rtwdev, REG_FPGA1_RFMOD, BIT_MASK_RFMOD, 0x0);
+		rtw_write32_mask(rtwdev, REG_OFDM0_TX_PSD_NOISE,
+				 GENMASK(31, 30), 0x0);
+		break;
+	case RTW_CHANNEL_WIDTH_40:
+		rtw_write32_mask(rtwdev, REG_FPGA0_RFMOD, BIT_MASK_RFMOD, 0x1);
+		rtw_write32_mask(rtwdev, REG_FPGA1_RFMOD, BIT_MASK_RFMOD, 0x1);
+		rtw_write32_mask(rtwdev, REG_CCK0_SYS, BIT_CCK_SIDE_BAND,
+				 primary_ch_idx == RTW_SC_20_UPPER ? 1 : 0);
+		rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, 0xc00,
+				 primary_ch_idx == RTW_SC_20_UPPER ? 2 : 1);
+		rtw_write32_mask(rtwdev, REG_BB_PWR_SAV5_11N, GENMASK(27, 26),
+				 primary_ch_idx == RTW_SC_20_UPPER ? 1 : 2);
+		break;
+	default:

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1437

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+		break;
+	}
+}
+
+static void rtw8723b_set_channel(struct rtw_dev *rtwdev, u8 channel,
+				 u8 bw, u8 primary_chan_idx)
+{
+	rtw8723b_set_channel_rf(rtwdev, channel, bw);
+	rtw_set_channel_mac(rtwdev, channel, bw, primary_chan_idx);
+	rtw8723b_set_channel_bb(rtwdev, bw, primary_chan_idx);

I don't think the vendor driver v5.2.17.1 does the stuff below:

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw8723b_reassert_rx_path(rtwdev);
+
+	if (rtw_is_8723bs(rtwdev)) {
+		bool keep_pta_owner;
+
+		keep_pta_owner = test_bit(RTW_FLAG_SCANNING, rtwdev->flags) ||
+				 (rtw_read32(rtwdev, REG_PAD_CTRL1) &
+				  BIT_PAPE_WLBT_SEL);
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, keep_pta_owner);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1457

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+		rtw_write8(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+		fsleep(1000);
+
+		/*
+		 * RF_WLINT bits 0-1 gate the data path into the BB and a prior
+		 * IQK or coex run can leave them blocking TX, so re-arm them.
+		 */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
+			     0x0780);
+	}

If this driver doesn't work without it, you probably have a bug somewhere.

Quoted 249 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +249 -0
+}
+
+static s8 rtw8723b_cck_rx_power(u8 lna_idx, u8 vga_idx)
+{
+	s8 rx_power = 0;
+
+	switch (lna_idx) {
+	case 6:
+		rx_power = -40 - (2 * vga_idx);
+		break;
+	case 4:
+		rx_power = -20 - (2 * vga_idx);
+		break;
+	case 1:
+		rx_power = 0 - (2 * vga_idx);
+		break;
+	case 0:
+		rx_power = 10 - (2 * vga_idx);
+		break;
+	default:
+		break;
+	}
+
+	return rx_power;
+}
+
+static void rtw8723b_query_phy_status_cck(struct rtw_dev *rtwdev, u8 *phy_raw,
+					  struct rtw_rx_pkt_stat *pkt_stat)
+{
+	struct phy_status_8703b *phy_status = (struct phy_status_8703b *)phy_raw;
+	u8 lna_idx = (phy_status->cck_agc_rpt_ofdm_cfosho_a & 0xE0) >> 5;
+	u8 vga_idx = (phy_status->cck_agc_rpt_ofdm_cfosho_a & 0x1F);
+	s8 rx_power = rtw8723b_cck_rx_power(lna_idx, vga_idx);
+	s8 min_rx_power = -120;
+
+	pkt_stat->bw = RTW_CHANNEL_WIDTH_20;
+
+	pkt_stat->rx_power[RF_PATH_A] = rx_power;
+	pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1);
+	pkt_stat->signal_power = max(pkt_stat->rx_power[RF_PATH_A],
+				     min_rx_power);
+	rtwdev->dm_info.rssi[RF_PATH_A] = pkt_stat->rssi;
+}
+
+static void rtw8723b_query_phy_status_ofdm(struct rtw_dev *rtwdev, u8 *phy_raw,
+					   struct rtw_rx_pkt_stat *pkt_stat)
+{
+	struct phy_status_8703b *phy_status = (struct phy_status_8703b *)phy_raw;
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	s8 val_s8;
+
+	/* pkt_stat->bw comes from the RX descriptor, not the PHY status. */
+
+	val_s8 = phy_status->path_agc[RF_PATH_A].gain & 0x3F;
+	pkt_stat->rx_power[RF_PATH_A] = (val_s8 * 2) - 110;
+
+	pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1);
+	pkt_stat->rx_snr[RF_PATH_A] = (s8)(phy_status->path_rxsnr[RF_PATH_A] / 2);
+
+	/* signal power reported by HW */
+	val_s8 = phy_status->cck_sig_qual_ofdm_pwdb_all >> 1;
+	pkt_stat->signal_power = (val_s8 & 0x7f) - 110;
+
+	pkt_stat->rx_evm[RF_PATH_A] = phy_status->stream_rxevm[RF_PATH_A];
+	pkt_stat->cfo_tail[RF_PATH_A] = phy_status->path_cfotail[RF_PATH_A];
+
+	dm_info->curr_rx_rate = pkt_stat->rate;
+	dm_info->rssi[RF_PATH_A] = pkt_stat->rssi;
+	dm_info->rx_snr[RF_PATH_A] = pkt_stat->rx_snr[RF_PATH_A] >> 1;
+	dm_info->cfo_tail[RF_PATH_A] = (pkt_stat->cfo_tail[RF_PATH_A] * 5) >> 1;
+
+	val_s8 = (s8)pkt_stat->rx_evm[RF_PATH_A];
+	val_s8 = clamp_t(s8, -val_s8 >> 1, 0, 64);
+	val_s8 &= 0x3F; /* 64->0: second path of 1SS rate is 64 */
+	dm_info->rx_evm_dbm[RF_PATH_A] = val_s8;
+}
+
+static void rtw8723b_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
+				      struct rtw_rx_pkt_stat *pkt_stat)
+{
+	/*
+	 * The 8723B PHY status does not report the channel, so we must
+	 * mark it invalid to allow mac80211/rtw88 to parse it from the IE
+	 * during scanning.
+	 */
+	pkt_stat->channel_invalid = true;
+
+	if (pkt_stat->rate <= DESC_RATE11M)
+		rtw8723b_query_phy_status_cck(rtwdev, phy_status, pkt_stat);
+	else
+		rtw8723b_query_phy_status_ofdm(rtwdev, phy_status, pkt_stat);
+}
+
+static void rtw8723b_set_iqk_matrix_by_result(struct rtw_dev *rtwdev,
+					      u32 ofdm_swing, u8 path)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	s32 ele_A, ele_D, ele_C, ele_A_ext;
+	s32 iqk_result_x;
+	s32 iqk_result_y;
+	s32 value32;
+
+	switch (path) {
+	default:
+	case RF_PATH_A:
+		iqk_result_x = dm_info->iqk.result.s1_x;
+		iqk_result_y = dm_info->iqk.result.s1_y;
+		break;
+	case RF_PATH_B:
+		iqk_result_x = dm_info->iqk.result.s0_x;
+		iqk_result_y = dm_info->iqk.result.s0_y;
+		break;
+	}
+
+	/* new element D */
+	ele_D = OFDM_SWING_D(ofdm_swing);
+
+	/* new element A */
+	iqk_result_x = iqkxy_to_s32(iqk_result_x);
+	ele_A = iqk_mult(iqk_result_x, ele_D, &ele_A_ext);
+
+	/* new element C */
+	iqk_result_y = iqkxy_to_s32(iqk_result_y);
+	ele_C = iqk_mult(iqk_result_y, ele_D, NULL);
+
+	switch (path) {
+	case RF_PATH_A:
+	default:
+		/* write new elements A, C, D, element B is always 0 */
+		value32 = BIT_SET_TXIQ_ELM_ACD(ele_A, ele_C, ele_D);
+		rtw_write32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, value32);
+		value32 = BIT_SET_TXIQ_ELM_C1(ele_C);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS,
+				 value32);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(24),
+				 ele_A_ext);
+		break;
+
+	case RF_PATH_B:
+		/* write new elements A, C, D, element B is always 0 */
+		value32 = BIT_SET_TXIQ_ELM_ACD(ele_A, ele_C, ele_D);
+		rtw_write32(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE, value32);
+		value32 = BIT_SET_TXIQ_ELM_C1(ele_C);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXB_LSB2_11N, MASKH4BITS,
+				 value32);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(28),
+				 ele_A_ext);
+		break;
+	}
+}
+
+static void rtw8723b_set_iqk_matrix(struct rtw_dev *rtwdev, s8 ofdm_index,
+				    u8 path)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u32 ofdm_swing;
+
+	ofdm_index = clamp_t(s8, ofdm_index, 0, RTW_OFDM_SWING_TABLE_SIZE - 1);
+
+	ofdm_swing = rtw8723b_ofdm_swing_table[ofdm_index];
+
+	if (dm_info->iqk.done) {
+		rtw8723b_set_iqk_matrix_by_result(rtwdev, ofdm_swing, path);
+		return;
+	}
+
+	switch (path) {
+	case RF_PATH_A:
+	default:
+		rtw_write32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, ofdm_swing);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS,
+				 0x00);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(24),
+				 0x00);
+		break;
+
+	case RF_PATH_B:
+		rtw_write32(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE, ofdm_swing);
+		rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXB_LSB2_11N, MASKH4BITS,
+				 0x00);
+		rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(28),
+				 0x00);
+		break;
+	}
+}
+
+static u8 rtw8723b_iqk_check_tx_failed(struct rtw_dev *rtwdev)
+{
+	s32 tx_x, tx_y;
+	u32 tx_fail;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xeac = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RES_RY));
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xe94 = 0x%x, 0xe9c = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RES_TX),
+		rtw_read32(rtwdev, REG_IQK_RES_TY));
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] 0xe90(before IQK) = 0x%x, 0xe98(after IQK) = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RDY),
+		rtw_read32(rtwdev, 0xe98));
+
+	tx_fail = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_IQK_TX_FAIL);
+	tx_x = rtw_read32_mask(rtwdev, REG_IQK_RES_TX, BIT_MASK_RES_TX);
+	tx_y = rtw_read32_mask(rtwdev, REG_IQK_RES_TY, BIT_MASK_RES_TY);
+
+	if (!tx_fail && tx_x != IQK_TX_X_ERR && tx_y != IQK_TX_Y_ERR)
+		return IQK_TX_OK;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] A TX IQK failed\n");
+
+	return 0;
+}
+
+static u8 rtw8723b_iqk_check_rx_failed(struct rtw_dev *rtwdev)
+{
+	s32 rx_x, rx_y;
+	u32 rx_fail;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xea4 = 0x%x, 0xeac = 0x%x\n",
+		rtw_read32(rtwdev, REG_IQK_RES_RX),
+		rtw_read32(rtwdev, REG_IQK_RES_RY));
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] 0xea0(before IQK) = 0x%x, 0xea8(after IQK) = 0x%x\n",
+		rtw_read32(rtwdev, 0xea0),
+		rtw_read32(rtwdev, 0xea8));
+
+	rx_fail = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_IQK_RX_FAIL);
+	rx_x = rtw_read32_mask(rtwdev, REG_IQK_RES_RX, BIT_MASK_RES_RX);
+	rx_y = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_MASK_RES_RY);
+	rx_y = abs(iqkxy_to_s32(rx_y));
+
+	if (!rx_fail && rx_x != IQK_RX_X_ERR && rx_y != IQK_RX_Y_ERR &&
+	    rx_x < IQK_RX_X_UPPER && rx_x > IQK_RX_X_LOWER &&
+	     rx_y < IQK_RX_Y_LMT)
+		return IQK_RX_OK;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] A RX IQK failed\n");
+
+	return 0;
+}
+
+static u8 rtw8723b_iqk_tx_path_a(struct rtw_dev *rtwdev)
+{
+	u32 path_sel;
+	bool sdio_iqk = rtw_is_8723bs(rtwdev);
+	u8 status;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A TX IQK!\n");
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1716

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	/* Save RF path */
+	path_sel = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* enable path A PA in TX IQK mode */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x20000);

Where does 0x18000 come from? The vendor driver v5.2.17.1 uses 0x20000 here.

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0003f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xc7f87);
+
+	/* Tx IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1731

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N,
+		    sdio_iqk ? 0x821303ea : 0x821403ea);

Here, too, it uses the second value.

Quoted 45 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +45 -0
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N, 0x28110000);
+	rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_B, 0x28110000);
+
+	/* LO calibration setting */
+	rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x00462911);
+
+	/* enter IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x808000);
+
+	/* ant switch */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG,
+		    rtw8723b_iqk_ant_switch_path(rtwdev));
+
+	/* GNT_BT = 0 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00000800);
+
+	/* One shot, path A LOK & IQK */
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf9000000);
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf8000000);
+
+	msleep(IQK_DELAY_TIME_8723B);
+
+	/* restore ant path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, path_sel);
+
+	/* GNT_BT = 1 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00001800);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */
+	status = rtw8723b_iqk_check_tx_failed(rtwdev);
+
+	return status;
+}
+
+static u8 rtw8723b_iqk_rx_path_a(struct rtw_dev *rtwdev)
+{
+	u32 reg_e94, reg_e9c, val32, path_sel;
+	bool sdio_iqk = rtw_is_8723bs(rtwdev);
+	u8 status;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK step1!\n");

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1786

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+	/* Save RF path */
+	path_sel = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x30000);

Vendor driver uses the second value.

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7fb7);
+
+	/* IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1801

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N,
+		    sdio_iqk ? 0x82130ff0 : 0x82160ff0);

Here as well.

Quoted 23 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +23 -0
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N, 0x28110000);
+	rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_B, 0x28110000);
+
+	/* LO calibration setting */
+	rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x0046a911);
+
+	/* enter IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x808000);
+
+	/* ant switch */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG,
+		    rtw8723b_iqk_ant_switch_path(rtwdev));
+
+	/* GNT_BT = 0 (disable BT) */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00000800);
+
+	/* One shot, path A LOK & IQK */
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf9000000);
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf8000000);
+
+	msleep(IQK_DELAY_TIME_8723B);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1834

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	/* restore ant path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, path_sel);
+
+	/* GNT_BT = 1 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00001800);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */

No need for a comment that just repeats what the name of the function already says.

Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+	status = rtw8723b_iqk_check_tx_failed(rtwdev);
+
+	/* if Tx not OK, ignore Rx */
+	if (!status)
+		return status;
+
+	reg_e94 = rtw_read32(rtwdev, REG_IQK_RES_TX);
+	reg_e9c = rtw_read32(rtwdev, REG_IQK_RES_TY);
+	val32 = 0x80007c00 | (reg_e94 & 0x3ff0000) |

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1853

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	((reg_e9c & 0x3ff0000) >> 16);
+	rtw_write32(rtwdev, REG_TXIQK_11N, val32);
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK step2!\n");
+
+	/* modify RX IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x30000);

Here as well.

Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7d77);
+
+	/* PA, PAD setting */
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xdf, RFREG_MASK, 0xf80);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x55, RFREG_MASK, 0x4021f);
+
+	/* IQK setting */
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1872

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N,
+		    sdio_iqk ? 0x2813001f : 0x2816001f);

Here as well.

Quoted 168 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +168 -0
+	rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_B, 0x28110000);
+
+	/* LO calibration setting */
+	rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x0046a8d1);
+
+	/* enter IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x808000);
+
+	/* ant switch */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG,
+		    rtw8723b_iqk_ant_switch_path(rtwdev));
+
+	/* GNT_BT = 0 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00000800);
+
+	/* One shot, path A LOK & IQK */
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf9000000);
+	rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf8000000);
+
+	msleep(IQK_DELAY_TIME_8723B);
+
+	/* restore ant path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, path_sel);
+
+	/* GNT_BT = 1 */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, 0x00001800);
+
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */
+
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xdf, RFREG_MASK, 0x780);
+
+	status |= rtw8723b_iqk_check_rx_failed(rtwdev);
+
+	return status;
+}
+
+static void
+rtw8723b_iqk_fill_a_matrix(struct rtw_dev *rtwdev, const s32 result[])
+{
+	s32 tx1_a, tx1_a_ext;
+	s32 tx1_c, tx1_c_ext;
+	s32 oldval_1;
+	s32 x, y;
+
+	if (result[IQK_S1_TX_X] == 0)
+		return;
+
+	oldval_1 = rtw_read32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+				   BIT_MASK_TXIQ_ELM_D);
+
+	x = iqkxy_to_s32(result[IQK_S1_TX_X]);
+	tx1_a = iqk_mult(x, oldval_1, &tx1_a_ext);
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_A, tx1_a);
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD,
+			 BIT_MASK_OFDM0_EXT_A, tx1_a_ext);
+
+	y = iqkxy_to_s32(result[IQK_S1_TX_Y]);
+	tx1_c = iqk_mult(y, oldval_1, &tx1_c_ext);
+	rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS,
+			 BIT_SET_TXIQ_ELM_C1(tx1_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_C, BIT_SET_TXIQ_ELM_C2(tx1_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD,
+			 BIT_MASK_OFDM0_EXT_C, tx1_c_ext);
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] X = 0x%x, TX1_A = 0x%x, oldval_1 0x%x\n",
+		x, tx1_a, oldval_1);
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] Y = 0x%x, TX1_C = 0x%x\n", y, tx1_c);
+
+	if (result[IQK_S1_RX_X] == 0)
+		return;
+
+	rtw_write32_mask(rtwdev, REG_A_RXIQI, BIT_MASK_RXIQ_S1_X,
+			 result[IQK_S1_RX_X]);
+	rtw_write32_mask(rtwdev, REG_A_RXIQI, BIT_MASK_RXIQ_S1_Y1,
+			 BIT_SET_RXIQ_S1_Y1(result[IQK_S1_RX_Y]));
+	rtw_write32_mask(rtwdev, REG_RXIQK_MATRIX_LSB_11N, BIT_MASK_RXIQ_S1_Y2,
+			 BIT_SET_RXIQ_S1_Y2(result[IQK_S1_RX_Y]));
+}
+
+static void
+rtw8723b_iqk_fill_b_matrix(struct rtw_dev *rtwdev, const s32 result[])
+{
+	s32 tx0_a, tx0_a_ext;
+	s32 tx0_c, tx0_c_ext;
+	s32 oldval_0;
+	s32 x, y;
+
+	if (result[IQK_S0_TX_X] == 0)
+		return;
+
+	oldval_0 = rtw_read32_mask(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE,
+				   BIT_MASK_TXIQ_ELM_D);
+
+	x = iqkxy_to_s32(result[IQK_S0_TX_X]);
+	tx0_a = iqk_mult(x, oldval_0, &tx0_a_ext);
+
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_A, tx0_a);
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(27),
+			 tx0_a_ext);
+
+	y = iqkxy_to_s32(result[IQK_S0_TX_Y]);
+	tx0_c = iqk_mult(y, oldval_0, &tx0_c_ext);
+
+	rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXB_LSB2_11N, MASKH4BITS,
+			 BIT_SET_TXIQ_ELM_C1(tx0_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_XB_TX_IQ_IMBALANCE,
+			 BIT_MASK_TXIQ_ELM_C, BIT_SET_TXIQ_ELM_C2(tx0_c));
+	rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT(25),
+			 tx0_c_ext);
+
+	if (result[IQK_S0_RX_X] == 0)
+		return;
+
+	rtw_write32_mask(rtwdev, REG_B_RXIQI, BIT_MASK_RXIQ_X_S0,
+			 result[IQK_S0_RX_X]);
+	rtw_write32_mask(rtwdev, REG_B_RXIQI, BIT_MASK_RXIQ_S1_Y1,
+			 BIT_SET_RXIQ_S1_Y1(result[IQK_S0_RX_Y]));
+}
+
+static void
+rtw8723b_iqk_config_mac(struct rtw_dev *rtwdev,
+			const struct rtw8723x_iqk_backup_regs *backup)
+{
+	int i;
+
+	rtw_write8(rtwdev, rtw8723x_common.iqk_mac8_regs[0], 0x3f);
+
+	for (i = 1; i < RTW8723X_IQK_MAC8_REG_NUM; i++)
+		rtw_write8(rtwdev, rtw8723x_common.iqk_mac8_regs[i],
+			   backup->mac8[i] & (~BIT(3)));
+
+	/* This MAC backup register needs a byte-wide write. */
+	rtw_write8(rtwdev, rtw8723x_common.iqk_mac32_regs[0],
+		   backup->mac32[0] & (~BIT(5)));
+}
+
+static void
+rtw8723b_iqk_one_round(struct rtw_dev *rtwdev, s32 result[][IQK_NR], u8 t,
+		       const struct rtw8723x_iqk_backup_regs *backup)
+{
+	u32 i;
+	u8 a_ok;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK] IQ Calibration for 1T1R_S0/S1 for %d times\n", t);
+
+	rtw8723x_iqk_path_adda_on(rtwdev, ADDA_ON_VAL_8723B);
+	rtw8723b_iqk_config_mac(rtwdev, backup);
+
+	rtw_write32_mask(rtwdev, REG_CCK_ANT_SEL_11N, 0x0f000000, 0xf);
+	rtw_write32(rtwdev, REG_BB_RX_PATH_11N, 0x03a05600);
+	rtw_write32(rtwdev, REG_TRMUX_11N, 0x000800e4);
+	rtw_write32(rtwdev, REG_BB_PWR_SAV1_11N, 0x22204000);
+
+	/*
+	 * RX IQ calibration setting for 8723B D cut large current issue
+	 * when leaving IPS
+	 */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2050

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x30000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7fb7);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xed, 0x20, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x43, RFREG_MASK, 0x60fbd);
+
+	for (i = 0; i < PATH_IQK_RETRY; i++) {
+		a_ok = rtw8723b_iqk_tx_path_a(rtwdev);
+		if (a_ok == IQK_TX_OK) {

If you invert the condition, this block with long lines can be indented less.

Quoted 33 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +33 -0
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"[IQK] path A TX IQK success!\n");
+
+			rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N,
+					 MASKH3BYTES, 0x000000);
+
+			result[t][IQK_S1_TX_X] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_TX,
+						BIT_MASK_RES_TX);
+			result[t][IQK_S1_TX_Y] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_TY,
+						BIT_MASK_RES_TY);
+			break;
+		}
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A TX IQK fail!\n");
+		result[t][IQK_S1_TX_X] = 0x100;
+		result[t][IQK_S1_TX_Y] = 0x0;
+	}
+
+	for (i = 0; i < PATH_IQK_RETRY; i++) {
+		a_ok = rtw8723b_iqk_rx_path_a(rtwdev);
+		if (a_ok == (IQK_TX_OK | IQK_RX_OK)) {
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"[IQK] path A RX IQK success!\n");
+			result[t][IQK_S1_RX_X] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_RX,
+						BIT_MASK_RES_RX);
+			result[t][IQK_S1_RX_Y] =
+				rtw_read32_mask(rtwdev, REG_IQK_RES_RY,
+						BIT_MASK_RES_RY);
+			break;
+		}

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2093

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK fail!\n");
+		result[t][IQK_S1_RX_X] = 0x100;
+		result[t][IQK_S1_RX_Y] = 0x0;
+	}
+
+	if (a_ok == 0x0)
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A IQK fail!\n");
+
+	/* rtl8723b is 1T1R, so path B is not calibrated. */

It looks like the vendor driver is calibrating path B as well (phy_path_b_iqk_8723b() and phy_path_b_rx_iqk_8723b()) when the device has two antennas, and only path A when the device has one antenna.

Quoted 20 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +20 -0
+
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+}
+
+static void rtw8723b_phy_calibration(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw8723x_iqk_backup_regs backup;
+	s32 result[IQK_ROUND_SIZE][IQK_NR];
+	u32 bt_control;
+	bool good;
+	u8 final_candidate = IQK_ROUND_INVALID;
+	u8 i, j;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] Start!\n");
+	memset(result, 0, sizeof(result));
+
+	rtw8723b_lck(rtwdev);
+	rtw8723b_inform_rfk_status(rtwdev, true);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2123

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	/* The LTE path GNT backup that 8723d does is not needed on SDIO. */
+	rtw8723x_iqk_backup_path_ctrl(rtwdev, &backup);
+	rtw8723x_iqk_backup_regs(rtwdev, &backup);
+
+	/* save default GNT_BT */
+	bt_control = rtw_read32(rtwdev, REG_BT_CONTROL_8723B);
+
+	for (i = IQK_ROUND_0; i <= IQK_ROUND_2; i++) {
+		if (!rtw_is_8723bs(rtwdev))
+			rtw8723x_iqk_config_path_ctrl(rtwdev);

I didn't see this in the RTL8723BE or RTL8723BU drivers. I guess it was copied from another chip?

Quoted 74 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +74 -0
+
+		rtw8723b_iqk_one_round(rtwdev, result, i, &backup);
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"[IQK] back to BB mode, load original value!\n");
+
+		if (i > IQK_ROUND_0) {
+			rtw8723x_iqk_restore_regs(rtwdev, &backup);
+
+			/* Restore RX initial gain */
+			rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x50);
+			rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, backup.igia);
+
+			/* load 0xe30 IQC default value */
+			rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x01008c00);
+			rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x01008c00);
+		}
+
+		if (!rtw_is_8723bs(rtwdev))
+			rtw8723x_iqk_restore_path_ctrl(rtwdev, &backup);
+
+		for (j = IQK_ROUND_0; j < i; j++) {
+			good = rtw8723x_iqk_similarity_cmp(rtwdev, result, j, i);
+			if (good) {
+				final_candidate = j;
+				rtw_dbg(rtwdev, RTW_DBG_RFK,
+					"[IQK] cmp %d:%d final_candidate is %x\n",
+					j, i, final_candidate);
+				goto iqk_done;
+			}
+		}
+	}
+
+	if (final_candidate == IQK_ROUND_INVALID) {
+		s32 reg_tmp = 0;
+
+		for (i = 0; i < IQK_NR; i++)
+			reg_tmp += result[IQK_ROUND_HYBRID][i];
+
+		if (reg_tmp != 0) {
+			final_candidate = IQK_ROUND_HYBRID;
+		} else {
+			rtw_warn(rtwdev, "IQK failed\n");
+			goto out;
+		}
+	}
+
+iqk_done:
+	if (result[final_candidate][IQK_S1_TX_X])
+		rtw8723b_iqk_fill_a_matrix(rtwdev, result[final_candidate]);
+	if (result[final_candidate][IQK_S0_TX_X])
+		rtw8723b_iqk_fill_b_matrix(rtwdev, result[final_candidate]);
+
+	dm_info->iqk.result.s1_x = result[final_candidate][IQK_S1_TX_X];
+	dm_info->iqk.result.s1_y = result[final_candidate][IQK_S1_TX_Y];
+	dm_info->iqk.result.s0_x = result[final_candidate][IQK_S0_TX_X];
+	dm_info->iqk.result.s0_y = result[final_candidate][IQK_S0_TX_Y];
+	dm_info->iqk.done = true;
+
+out:
+	/* restore RF path */
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, backup.bb_sel_btg);
+
+	/* restore GNT_BT */
+	rtw_write32(rtwdev, REG_BT_CONTROL_8723B, bt_control);
+
+	/* Restore RX mode table parameter */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x18000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xe6177);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xed, 0x20, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x43, RFREG_MASK, 0x300bd);
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2207

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] final_candidate is %x\n",
+		final_candidate);
+
+	for (i = IQK_ROUND_0; i < IQK_ROUND_SIZE; i++)
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"[IQK] Result %u: rege94_s1=%x rege9c_s1=%x regea4_s1=%x regeac_s1=%x rege94_s0=%x rege9c_s0=%x regea4_s0=%x regeac_s0=%x %s\n",
+			i,
+			result[i][0], result[i][1], result[i][2], result[i][3],
+			result[i][4], result[i][5], result[i][6], result[i][7],
+			final_candidate == i ? "(final candidate)" : "");

You can make this two calls to rtw_dbg to avoid such a long line.

Quoted 31 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +31 -0
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK]0xc80 = 0x%x 0xc94 = 0x%x 0xc14 = 0x%x 0xca0 = 0x%x\n",
+	rtw_read32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE),
+		rtw_read32(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N),
+		rtw_read32(rtwdev, REG_A_RXIQI),
+		rtw_read32(rtwdev, REG_RXIQK_MATRIX_LSB_11N));
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"[IQK]0xcd0 = 0x%x 0xcd4 = 0x%x 0xcd8 = 0x%x\n",
+	rtw_read32(rtwdev, REG_TXIQ_AB_S0),
+		rtw_read32(rtwdev, REG_TXIQ_CD_S0),
+		rtw_read32(rtwdev, REG_RXIQ_AB_S0));
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] finished\n");
+
+	rtw8723b_inform_rfk_status(rtwdev, false);
+}
+
+static void rtw8723b_pwrtrack_set_ofdm_pwr(struct rtw_dev *rtwdev, u8 path,
+					   s8 swing_idx, s8 txagc_idx)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	dm_info->txagc_remnant_ofdm[path] = txagc_idx;
+
+	rtw8723b_set_iqk_matrix(rtwdev, swing_idx, path);
+}
+
+static void rtw8723b_pwrtrack_set_cck_pwr(struct rtw_dev *rtwdev, s8 swing_idx,
+					  s8 txagc_idx)
+{

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2248

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	dm_info->txagc_remnant_cck = txagc_idx;
+
+	swing_idx = clamp_t(s8, swing_idx, 0, RTW_CCK_SWING_TABLE_SIZE - 1);
+
+	BUILD_BUG_ON(ARRAY_SIZE(rtw8723b_cck_pwr_regs) !=
+		     ARRAY_SIZE(rtw8723b_cck_swing_table_ch1_ch13[0]));
+
+	/* Only ch1-13 is wired up; channel 14 is Japan-only and unreachable. */

If I change my country code to JP and trigger a scan, rtw88 visits channel 14.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2250

Quoted 2 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +2 -0
+	for (int i = 0; i < ARRAY_SIZE(rtw8723b_cck_pwr_regs); i++)
+		rtw_write8(rtwdev, rtw8723b_cck_pwr_regs[i],

These register addresses are consecutive values, there is no need to put them in an array.

Quoted 62 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +62 -0
+			   rtw8723b_cck_swing_table_ch1_ch13[swing_idx][i]);
+}
+
+static void rtw8723b_pwrtrack_set(struct rtw_dev *rtwdev, u8 path)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_hal *hal = &rtwdev->hal;
+	u8 limit_ofdm;
+	/* 8703b and 8723d seem to use RTW_CCK_SWING_TABLE_SIZE */
+	u8 limit_cck = 28; /* -2dB */
+	s8 final_ofdm_swing_index;
+	s8 final_cck_swing_index;
+
+	limit_ofdm = rtw8723x_pwrtrack_get_limit_ofdm(rtwdev);
+
+	final_ofdm_swing_index = dm_info->default_ofdm_index +
+				 dm_info->delta_power_index[path];
+	final_cck_swing_index = dm_info->default_cck_index +
+				dm_info->delta_power_index[path];
+
+	if (final_ofdm_swing_index > limit_ofdm)
+		rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, path, limit_ofdm,
+					       final_ofdm_swing_index - limit_ofdm);
+	else if (final_ofdm_swing_index < 0)
+		rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, path, 0,
+					       final_ofdm_swing_index);
+	else
+		rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, path, final_ofdm_swing_index, 0);
+
+	if (final_cck_swing_index > limit_cck)
+		rtw8723b_pwrtrack_set_cck_pwr(rtwdev, limit_cck,
+					      final_cck_swing_index - limit_cck);
+	else if (final_cck_swing_index < 0)
+		rtw8723b_pwrtrack_set_cck_pwr(rtwdev, 0,
+					      final_cck_swing_index);
+	else
+		rtw8723b_pwrtrack_set_cck_pwr(rtwdev, final_cck_swing_index, 0);
+
+	rtw_phy_set_tx_power_level(rtwdev, hal->current_channel);
+}
+
+static void rtw8723b_phy_pwrtrack(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_swing_table swing_table;
+	u8 thermal_value, delta, path;
+	bool do_iqk = false;
+
+	rtw_phy_config_swing_table(rtwdev, &swing_table);
+
+	if (rtwdev->efuse.thermal_meter[0] == 0xff)
+		return;
+
+	thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00);
+
+	/* Average the thermal meter readings. */
+	rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A);
+
+	do_iqk = rtw_phy_pwrtrack_need_iqk(rtwdev);
+
+	if (do_iqk)
+		rtw8723x_lck(rtwdev);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2322

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+
+	if (dm_info->pwr_trk_init_trigger)
+		dm_info->pwr_trk_init_trigger = false;
+	else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value,
+						   RF_PATH_A))
+		goto iqk;
+
+	delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A);
+
+	/* NOTE: also done in rtw_phy_pwrtrack_get_delta */

Is it necessary to do it again?

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+	delta = min_t(u8, delta, RTW_PWR_TRK_TBL_SZ - 1);
+
+	for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
+		s8 delta_cur, delta_last;
+
+		delta_last = dm_info->delta_power_index[path];
+		delta_cur = rtw_phy_pwrtrack_get_pwridx(rtwdev, &swing_table,
+							path, RF_PATH_A, delta);

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2340

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+		if (delta_last == delta_cur)
+			continue;
+
+		dm_info->delta_power_index[path] = delta_cur;
+		rtw8723b_pwrtrack_set(rtwdev, path);
+	}
+
+iqk:
+	if (do_iqk)
+		rtw8723b_phy_calibration(rtwdev);

The vendor driver doesn't redo IQK here.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2349

Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+}
+
+static void rtw8723b_pwr_track(struct rtw_dev *rtwdev)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	if (efuse->power_track_type != 0) {
+		rtw_warn(rtwdev, "unsupported power track type\n");

This function runs every two seconds, forever. Please put this warning elsewhere, like rtw8723b_read_efuse(), to avoid filling the kernel log. (Or create rtw_warn_once()).

Quoted 76 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +76 -0
+		return;
+	}
+
+	if (!dm_info->pwr_trk_triggered) {
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER,
+			     GENMASK(17, 16), 0x03);
+		dm_info->pwr_trk_triggered = true;
+		return;
+	}
+
+	rtw8723b_phy_pwrtrack(rtwdev);
+	dm_info->pwr_trk_triggered = false;
+}
+
+/*
+ * The vendor sequence for this chip differs from rtw8723x_coex_cfg_init():
+ * REG_BT_TDMA_TIME is written whole, PTA pins and REG_QUEUE_CTRL untouched.
+ */
+static void rtw8723b_coex_cfg_init(struct rtw_dev *rtwdev)
+{
+	/* enable TBTT interrupt */
+	rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION);
+
+	/* BT report packet sample rate, 0x790[5:0] = 0x5 */
+	rtw_write8(rtwdev, REG_BT_TDMA_TIME, 0x5);
+
+	/* enable BT counter statistics */
+	rtw_write8(rtwdev, REG_BT_STAT_CTRL, 0x1);
+
+	/* enable PTA (3-wire function from BT side) */
+	rtw_write8_mask(rtwdev, REG_GPIO_MUXCFG, BIT_BT_PTA_EN, 0x1);
+}
+
+static void rtw8723b_coex_set_gnt_fix(struct rtw_dev *rtwdev)
+{
+	/* intentionally empty: rtw8723d's coex_set_gnt_fix is empty too */
+}
+
+static void rtw8723b_coex_set_gnt_debug(struct rtw_dev *rtwdev)
+{
+	/* GNT_BT debug routing is not implemented; not needed for operation. */
+}
+
+static bool rtw8723b_coex_ant_is_aux(struct rtw_dev *rtwdev)
+{
+	return !!(rtwdev->efuse.bt_setting & BIT(6));
+}
+
+static void rtw8723b_coex_write8_verify(struct rtw_dev *rtwdev, u32 addr,
+					u8 value)
+{
+	u8 readback;
+
+	rtw_write8(rtwdev, addr, value);
+	readback = rtw_read8(rtwdev, addr);
+	if (readback == value)
+		return;
+
+	usleep_range(10, 11);
+	rtw_write8(rtwdev, addr, value);
+}
+
+static void rtw8723b_coex_set_ant_ctrl_by_wifi(struct rtw_dev *rtwdev)
+{
+	/* 0x4c[23] = 1, 0x4c[24] = 0: antenna control by 0x64. */
+	rtw_write32_clr(rtwdev, REG_LED_CFG, BIT(24));
+	rtw_write32_set(rtwdev, REG_LED_CFG, BIT(23));
+}
+
+static void rtw8723b_coex_set_ant_ctrl_by_bt(struct rtw_dev *rtwdev)
+{
+	/* 0x4c[24:23] = 0: antenna control by BT_RFE_CTRL. */
+	rtw_write32_clr(rtwdev, REG_LED_CFG, BIT(23) | BIT(24));
+}
+
+static void rtw8723b_coex_cfg_ant_buffer(struct rtw_dev *rtwdev)

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2435

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+{
+	u8 sys_func_before;
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
+	    WLAN_SYS_FUNC_BB_ENABLE) {
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+		usleep_range(10, 11);
+	}

Not sure why you need to touch REG_SYS_FUNC_EN here. The vendor driver doesn't do it.

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2439

Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+
+	rtw_write8_set(rtwdev, REG_PWR_DATA + 1,
+		       BIT_EEPRPAD_RFE_CTRL_EN >> 8);
+	rtw8723b_coex_write8_verify(rtwdev, REG_RFE_CTRL_E, 0xff);

The vendor driver uses a normal rtw_write8() here.

Quoted 22 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +22 -0
+	rtw_write8_mask(rtwdev, REG_RFE_CTRL_ANT_SW, BIT_RFE_CTRL_ANT_SW_SEL, 0x3);
+	rtw_write8(rtwdev, REG_RFE_CTRL_ANTA_SRC, 0x77);
+}
+
+static u32 rtw8723b_coex_write_bb_sel_btg(struct rtw_dev *rtwdev, u32 value)
+{
+	u32 readback;
+	u8 sys_func_before;
+
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO) {
+		rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+		return rtw_read32(rtwdev, REG_BB_SEL_BTG);
+	}
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
+	    WLAN_SYS_FUNC_BB_ENABLE) {
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+		usleep_range(10, 11);
+	}
+

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2471

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+	readback = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+	if (readback == value)
+		return readback;
+
+	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, WLAN_SYS_FUNC_BB_ENABLE);
+	usleep_range(10, 11);
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+
+	return rtw_read32(rtwdev, REG_BB_SEL_BTG);

All this looks strange too. What happens if you eliminate this function and write REG_BB_SEL_BTG with a simple rtw_write32()?

Quoted 227 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +227 -0
+}
+
+static u32 rtw8723b_coex_ant_path_value(struct rtw_dev *rtwdev, u8 pos_type)
+{
+	bool aux = rtw8723b_coex_ant_is_aux(rtwdev);
+
+	switch (pos_type) {
+	case COEX_SWITCH_TO_BT:
+		return aux ? 0x0 : 0x280;
+	case COEX_SWITCH_TO_WLG:
+	case COEX_SWITCH_TO_WLA:
+		return aux ? 0x280 : 0x0;
+	case COEX_SWITCH_TO_WLG_BT:
+	case COEX_SWITCH_TO_NOCARE:
+	default:
+		return aux ? 0x80 : 0x200;
+	}
+}
+
+static void rtw8723b_coex_cfg_ant_switch(struct rtw_dev *rtwdev,
+					 u8 ctrl_type, u8 pos_type)
+{
+	u32 ant_path;
+
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO)
+		return;
+
+	if (ctrl_type == COEX_SWITCH_CTRL_BY_BT) {
+		rtw_write8(rtwdev, REG_GNT_BT, 0x18);
+		rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0x4);
+		rtw_write8_clr(rtwdev, REG_BT_ANT_SEL_8723B,
+			       BIT_BT_SEL_BY_WIFI_8723B);
+		rtw8723b_coex_set_ant_ctrl_by_bt(rtwdev);
+		ant_path = rtw8723b_coex_ant_path_value(rtwdev,
+							COEX_SWITCH_TO_BT);
+		rtw8723b_coex_write_bb_sel_btg(rtwdev, ant_path);
+
+		rtw_dbg(rtwdev, RTW_DBG_COEX,
+			"[BTCoex], 8723bs ant switch by BT BB_SEL_BTG=0x%08x 0x4c=0x%08x 0x67=0x%02x 0x765=0x%02x 0x76e=0x%02x\n",
+			rtw_read32(rtwdev, REG_BB_SEL_BTG),
+			rtw_read32(rtwdev, REG_LED_CFG),
+			rtw_read8(rtwdev, REG_BT_ANT_SEL_8723B),
+			rtw_read8(rtwdev, REG_GNT_BT),
+			rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL));
+		return;
+	}
+
+	rtw8723b_coex_set_ant_ctrl_by_wifi(rtwdev);
+	rtw_write8(rtwdev, REG_BT_ANT_SEL_8723B, 0x20);
+
+	if (ctrl_type == COEX_SWITCH_CTRL_BY_BBSW &&
+	    pos_type == COEX_SWITCH_TO_BT) {
+		rtw_write8(rtwdev, REG_GNT_BT, 0x18);
+		rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0x4);
+	} else {
+		if (rtw_read8(rtwdev, REG_GNT_BT) != 0)
+			rtw_write8(rtwdev, REG_GNT_BT, 0x0);
+
+		if (rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL) != 0xc)
+			rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0xc);
+	}
+
+	if (ctrl_type == COEX_SWITCH_CTRL_BY_BBSW)
+		ant_path = rtw8723b_coex_ant_path_value(rtwdev, pos_type);
+	else
+		ant_path = rtw8723b_coex_ant_path_value(rtwdev,
+							COEX_SWITCH_TO_NOCARE);
+
+	rtw8723b_coex_write_bb_sel_btg(rtwdev, ant_path);
+	rtw8723b_sdio_restore_pad_ctrl(rtwdev,
+				       ctrl_type == COEX_SWITCH_CTRL_BY_PTA);
+
+	rtw_dbg(rtwdev, RTW_DBG_COEX,
+		"[BTCoex], 8723bs ant switch ctrl=%u pos=%u BB_SEL_BTG=0x%08x 0x4c=0x%08x 0x67=0x%02x 0x765=0x%02x 0x76e=0x%02x\n",
+		ctrl_type, pos_type, rtw_read32(rtwdev, REG_BB_SEL_BTG),
+		rtw_read32(rtwdev, REG_LED_CFG),
+		rtw_read8(rtwdev, REG_BT_ANT_SEL_8723B),
+		rtw_read8(rtwdev, REG_GNT_BT),
+		rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL));
+}
+
+static void rtw8723b_coex_set_rfe_type(struct rtw_dev *rtwdev)
+{
+	struct rtw_coex *coex = &rtwdev->coex;
+	struct rtw_coex_rfe *coex_rfe = &coex->rfe;
+	enum rtw_hci_type hci_type = rtw_hci_type(rtwdev);
+	u32 reg;
+	bool aux = rtw8723b_coex_ant_is_aux(rtwdev);
+
+	coex_rfe->rfe_module_type = rtwdev->efuse.rfe_option;
+	coex_rfe->ant_switch_polarity = aux ? 1 : 0;
+	coex_rfe->ant_switch_exist = hci_type == RTW_HCI_TYPE_SDIO;
+	coex_rfe->ant_switch_with_bt = false;
+	coex_rfe->ant_switch_diversity = false;
+	coex_rfe->wlg_at_btg = true;
+
+	rtw_write8(rtwdev, REG_BT_ANT_SEL_8723B, 0x20);
+
+	/* set GRAN_BT = 1 */
+	rtw_write8(rtwdev, REG_GNT_BT, 0x18);
+
+	/* set WLAN_ACT = 0 */
+	rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, 0x4);
+
+	switch (hci_type) {
+	case RTW_HCI_TYPE_USB:
+		rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x0);
+		rtw_write8(rtwdev, 0xfe08, 0x1);
+		break;
+	case RTW_HCI_TYPE_PCIE:
+		reg = 0x384;
+		/* 0xc3[6]: 0 = S1 (main, path A), 1 = S0 (aux, path B). */
+		if (aux) {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x0);
+			rtw_write8(rtwdev, reg, 0x1);
+		} else {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x280);
+			rtw_write8(rtwdev, reg, 0x0);
+		}
+		break;
+	case RTW_HCI_TYPE_SDIO:
+		/*
+		 * Internal switch: WiFi drives S0/S1, firmware gets the
+		 * inverse hint.
+		 */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK, 0x0780);
+
+		if (aux) {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x0);
+			rtw_write8(rtwdev, REG_SDIO_H2C, 0x1);
+		} else {
+			rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x280);
+			rtw_write8(rtwdev, REG_SDIO_H2C, 0x0);
+		}
+
+		rtw8723b_coex_set_ant_ctrl_by_wifi(rtwdev);
+		rtw_write8_mask(rtwdev, REG_PAD_CTRL1,
+				BIT_SW_DPDT_SEL_DATA, 0x0);
+		rtw8723b_coex_cfg_ant_buffer(rtwdev);
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, false);
+
+		/* COEX_ANT_SEL_RSV is sent later from rtw_power_on(). */
+		break;
+	default:
+		break;
+	}
+}
+
+static void rtw8723b_coex_set_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr)
+{
+	/* Deferred: 8723d adjusts WL Tx power (0xb2/0x90) with BT on. */
+}
+
+static void rtw8723b_coex_set_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain)
+{
+	/* Deferred: 8723d lowers the WL Rx AGC while BT is active. */
+}
+
+static void rtw8723b_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
+{
+	/* Nothing to do: rtw88 only reads the efuse, it never writes it. */
+}
+
+static void rtw8723b_fill_txdesc_checksum(struct rtw_dev *rtwdev,
+					  struct rtw_tx_pkt_info *pkt_info,
+					  u8 *txdesc)
+{
+	struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)txdesc;
+	const u8 *data = txdesc;
+	u16 checksum = 0;
+	int words = 32 / 2;
+
+	/* Unlike the shared 8723x helper, the checksum is not inverted here. */
+	le32p_replace_bits(&tx_desc->w7, 0, RTW_TX_DESC_W7_TXDESC_CHECKSUM);
+
+	while (words--) {
+		checksum ^= get_unaligned_le16(data);
+		data += sizeof(__le16);
+	}
+
+	le32p_replace_bits(&tx_desc->w7, checksum,
+			   RTW_TX_DESC_W7_TXDESC_CHECKSUM);
+}
+
+static int rtw8723b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	int ret;
+
+	ret = rtw8723x_read_efuse(rtwdev, log_map);
+	if (ret)
+		return ret;
+
+	/*
+	 * No firmware hardware feature report exists, so fill the hardware
+	 * capability here; a zero stream count kills the HT MCS set.
+	 */
+	efuse->hw_cap.nss = max_t(u8, rtwdev->hal.rf_path_num, 1);
+	efuse->hw_cap.ant_num = efuse->hw_cap.nss;
+	efuse->hw_cap.bw = BIT(RTW_CHANNEL_WIDTH_20) |
+			   BIT(RTW_CHANNEL_WIDTH_40);
+
+	return 0;
+}
+
+static const struct rtw_chip_ops rtw8723b_ops = {
+	.power_on		= rtw_power_on,
+	.power_off		= rtw_power_off,
+
+	.mac_init		= rtw8723b_mac_init,
+	.mac_postinit		= rtw8723x_mac_postinit,
+
+	.dump_fw_crash		= NULL,
+	/*
+	 * 8723d sets REG_HCI_OPT_CTRL BIT_USB_SUS_DIS in its shutdown
+	 * function; that is USB-only.
+	 */
+	.shutdown		= NULL,
+	.read_efuse		= rtw8723b_read_efuse,
+	.phy_set_param		= rtw8723b_phy_set_param,
+
+	.set_channel		= rtw8723b_set_channel,
+
+	.query_phy_status	= rtw8723b_query_phy_status,
+	.read_rf		= rtw_phy_read_rf_sipi,
+	.write_rf		= rtw_phy_write_rf_reg_sipi,
+	.set_tx_power_index	= rtw8723x_set_tx_power_index,

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2708

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	.rsvd_page_dump		= NULL,
+	.set_antenna		= NULL,
+	.cfg_ldo25		= rtw8723b_cfg_ldo25,
+	.efuse_grant		= rtw8723b_efuse_grant,
+	.set_ampdu_factor	= NULL,
+	.false_alarm_statistics	= rtw8723x_false_alarm_statistics,
+	.phy_calibration	= rtw8723b_phy_calibration,
+	.dpk_track		= NULL,
+	/* REG_CSRATIO does not exist on this chip generation. */
+	.cck_pd_set		= NULL,

You can use rtw88xxa_phy_cck_pd_set() from rtw88xxa.c for this chip too.

Quoted 35 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +35 -0
+	.pwr_track		= rtw8723b_pwr_track,
+	.config_bfee		= NULL,
+	.set_gid_table		= NULL,
+	.cfg_csi_rate		= NULL,
+	.adaptivity_init	= NULL,
+	.adaptivity		= NULL,
+	.cfo_init		= NULL,
+	.cfo_track		= NULL,
+	.config_tx_path		= NULL,
+	.config_txrx_mode	= NULL,
+	.led_set		= NULL,
+	.fill_txdesc_checksum	= rtw8723b_fill_txdesc_checksum,
+
+	.coex_set_init		= rtw8723b_coex_cfg_init,
+	.coex_set_ant_switch	= rtw8723b_coex_cfg_ant_switch,
+	.coex_set_gnt_fix	= rtw8723b_coex_set_gnt_fix,
+	.coex_set_gnt_debug	= rtw8723b_coex_set_gnt_debug,
+	.coex_set_rfe_type	= rtw8723b_coex_set_rfe_type,
+	.coex_set_wl_tx_power	= rtw8723b_coex_set_wl_tx_power,
+	.coex_set_wl_rx_gain	= rtw8723b_coex_set_wl_rx_gain,
+};
+
+const struct rtw_chip_info rtw8723b_hw_spec = {
+	.ops = &rtw8723b_ops,
+	.id = RTW_CHIP_TYPE_8723B,
+	.fw_name = "rtw88/rtw8723b_fw.bin",
+	.wlan_cpu = RTW_WCPU_8051,
+	.tx_pkt_desc_sz = 40,
+	.tx_buf_desc_sz = 16,
+	.rx_pkt_desc_sz = 24,
+	.rx_buf_desc_sz = 8,
+	.phy_efuse_size = 512,
+	.log_efuse_size = 512,
+	.ptct_efuse_size = 15,
+	.txff_size = 32768,

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2753

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	.rxff_size = 16384,
+	.rsvd_drv_pg_num = 8,
+	.txgi_factor = 1,
+	.is_pwr_by_rate_dec = true,
+	.max_power_index = 0x3f,
+	.csi_buf_pg_num = 0,
+	.band = RTW_BAND_2G,
+	.page_size = TX_PAGE_SIZE,
+	.dig_min = 0x20,
+	.usb_tx_agg_desc_num = 1,

The RTL8723BU vendor driver v5.2.17 sets this to 6.

Quoted 12 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +12 -0
+	/*
+	 * The firmware reports id 0xfd instead of C2H_HW_FEATURE_REPORT, so
+	 * the hardware feature report is not supported on this chip.
+	 */
+	.hw_feature_report = false,
+	.c2h_ra_report_size = 4,
+	.old_datarate_fb_limit = true,
+	.path_div_supported = false,
+	.ht_supported = true,
+	.vht_supported = false,
+	.lps_deep_mode_supported = 0,
+	.sys_func_en = 0xfd,

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2775

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	.pwr_on_seq = card_enable_flow_8723b,
+	.pwr_off_seq = card_disable_flow_8723b,
+	.page_table = page_table_8723b,
+	.rqpn_table = rqpn_table_8723b,
+	/* same shared table as the sibling rtw8703b and rtw8723d */
+	.prioq_addrs = &rtw8723x_common.prioq_addrs,
+	/* used only in pci.c, not needed for SDIO devices */
+	.intf_table = NULL,
+	.dig = rtw8723x_common.dig,
+	.dig_cck = rtw8723x_common.dig_cck,

This chip doesn't have dig_cck.

Quoted 75 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +56 -0
+	.rf_sipi_addr = {0x840, 0x844},
+	.rf_sipi_read_addr = rtw8723x_common.rf_sipi_addr,
+	.fix_rf_phy_num = 2,
+	/* This chip has no LTE coex registers. */
+	.ltecoex_addr = NULL,
+	.mac_tbl = &rtw8723b_mac_tbl,
+	.agc_tbl = &rtw8723b_agc_tbl,
+	.bb_tbl = &rtw8723b_bb_tbl,
+	.rf_tbl = {&rtw8723b_rf_a_tbl},
+	.rfe_defs = rtw8723b_rfe_defs,
+	.rfe_defs_size = ARRAY_SIZE(rtw8723b_rfe_defs),
+	.iqk_threshold = 8,
+	.rx_ldpc = false,
+	.tx_stbc = false,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
+	.coex_para_ver = 20180201,	/* glcoex_ver_date_8723b_1ant */
+	.bt_desired_ver = 0x6d,
+	.scbd_support = false,
+	.new_scbd10_def = true,
+	.ble_hid_profile_support = false,
+	.wl_mimo_ps_support = false,
+	.pstdma_type = COEX_PSTDMA_FORCE_LPSOFF,
+	.bt_rssi_type = COEX_BTRSSI_RATIO,
+	.ant_isolation = 15,
+	.rssi_tolerance = 2,
+	.wl_rssi_step = wl_rssi_step_8723b,
+	.bt_rssi_step = bt_rssi_step_8723b,
+	.table_sant_num = ARRAY_SIZE(table_sant_8723b),
+	.table_sant = table_sant_8723b,
+	.table_nsant_num = ARRAY_SIZE(table_nsant_8723b),
+	.table_nsant = table_nsant_8723b,
+	.tdma_sant_num = ARRAY_SIZE(tdma_sant_8723b),
+	.tdma_sant = tdma_sant_8723b,
+	.tdma_nsant_num = ARRAY_SIZE(tdma_nsant_8723b),
+	.tdma_nsant = tdma_nsant_8723b,
+	.wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8723b),
+	.wl_rf_para_tx = rf_para_tx_8723b,
+	.wl_rf_para_rx = rf_para_rx_8723b,
+	.bt_afh_span_bw20 = 0x20,
+	.bt_afh_span_bw40 = 0x30,
+	.afh_5g_num =  ARRAY_SIZE(afh_5g_8723b),
+	.afh_5g = afh_5g_8723b,
+	/* BTG_SEL is driven by the cardemu_to_act power sequence instead. */
+	.btg_reg = NULL,
+	.coex_info_hw_regs_num = 0,
+	.coex_info_hw_regs = NULL,
+};
+EXPORT_SYMBOL(rtw8723b_hw_spec);
+
+MODULE_FIRMWARE("rtw88/rtw8723b_fw.bin");
+
+MODULE_AUTHOR("Luka Gejak <[email protected]>");
+MODULE_AUTHOR("Michael Straube <[email protected]>");
+MODULE_DESCRIPTION("Realtek 802.11n wireless 8723b driver");
+MODULE_LICENSE("Dual BSD/GPL");
drivers/net/wireless/realtek/rtw88/rtw8723b.h new file +13 -0
new file mode 100644
index 000000000000..181782ec3f1b
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright(c) 2026 Realtek Corporation
+ */
+
+#ifndef __RTW8723B_H__
+#define __RTW8723B_H__
+
+#include "rtw8723x.h"
+
+extern const struct rtw_chip_info rtw8723b_hw_spec;
+
+#endif
author
#7 lore raw

Re: [PATCH v4 3/6] wifi: rtw88: 8723b: add the RTL8723B chip driver

Quoted 7 lines · Bitterblue Smith
Quoted 3 lines · Bitterblue Smith

On 24/09/2026 00:35, Luka Gejak wrote:

Quoted 11 lines
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 928

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) {
+		rtw_write16_set(rtwdev, REG_PWR_DATA,
+				BIT_EEPRPAD_RFE_CTRL_EN);
+
+		/*
+		 * rtw_mac_power_on() sets PAD mux bits this chip must not have;
+		 * restore the SDIO PAD mux before RF and coex setup.
+		 */

By the way, rtw_mac_power_on() doesn't touch REG_PAD_CTRL1 for this chip.

It does, through rtw_mac_pre_system_cfg(), which rtw_mac_power_on() calls at mac.c:382. At mac.c:111 that function reads REG_PAD_CTRL1, ORs in BIT_PAPE_WLBT_SEL and BIT_LNAON_WLBT_SEL and writes it back, for every HCI type including SDIO. So the bits are set during power on and the SDIO PAD mux has to have them cleared, which is what rtw8723b_sdio_restore_pad_ctrl() does. The comment in rtw8723b_post_enable_flow() names that function and the two bits now.

Best regards,
Luka Gejak

#8 lore raw

Re: [PATCH v4 3/6] wifi: rtw88: 8723b: add the RTL8723B chip driver

Quoted 22 lines · Luka Gejak
Quoted 7 lines · Bitterblue Smith
Quoted 3 lines · Bitterblue Smith

On 24/09/2026 00:35, Luka Gejak wrote:

Quoted 11 lines
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 928

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) {
+		rtw_write16_set(rtwdev, REG_PWR_DATA,
+				BIT_EEPRPAD_RFE_CTRL_EN);
+
+		/*
+		 * rtw_mac_power_on() sets PAD mux bits this chip must not have;
+		 * restore the SDIO PAD mux before RF and coex setup.
+		 */

By the way, rtw_mac_power_on() doesn't touch REG_PAD_CTRL1 for this chip.

Quoted 7 lines

It does, through rtw_mac_pre_system_cfg(), which rtw_mac_power_on() calls at mac.c:382. At mac.c:111 that function reads REG_PAD_CTRL1, ORs in BIT_PAPE_WLBT_SEL and BIT_LNAON_WLBT_SEL and writes it back, for every HCI type including SDIO. So the bits are set during power on and the SDIO PAD mux has to have them cleared, which is what rtw8723b_sdio_restore_pad_ctrl() does. The comment in rtw8723b_post_enable_flow() names that function and the two bits now.

That code is not reachable with this chip.

Quoted 3 lines

Best regards,
Luka Gejak

author
#9 lore raw

Re: [PATCH v4 3/6] wifi: rtw88: 8723b: add the RTL8723B chip driver

Quoted 37 lines · Bitterblue Smith
Quoted 26 lines · Luka Gejak
Quoted 3 lines · Bitterblue Smith

On 27/09/2026 18:21, Bitterblue Smith wrote:

On 24/09/2026 00:35, Luka Gejak wrote:

+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) {
+ rtw_write16_set(rtwdev, REG_PWR_DATA,
+ BIT_EEPRPAD_RFE_CTRL_EN);
+
+ /*
+ * rtw_mac_power_on() sets PAD mux bits this chip must not have;
+ * restore the SDIO PAD mux before RF and coex setup.
+ */

Quoted 4 lines

By the way, rtw_mac_power_on() doesn't touch REG_PAD_CTRL1
for this chip.

Quoted 8 lines

It does, through rtw_mac_pre_system_cfg(), which rtw_mac_power_on() calls at mac.c:382. At mac.c:111 that function reads REG_PAD_CTRL1, ORs in BIT_PAPE_WLBT_SEL and BIT_LNAON_WLBT_SEL and writes it back, for every HCI type including SDIO. So the bits are set during power on and the SDIO PAD mux has to have them cleared, which is what rtw8723b_sdio_restore_pad_ctrl() does. The comment in rtw8723b_post_enable_flow() names that function and the two bits now.

Quoted 2 lines

That code is not reachable with this chip.

My bad, I missed the wcpu check.

I removed the call and the comment.

Best regards,
Luka Gejak

author
#10 lore raw

Re: [PATCH v4 3/6] wifi: rtw88: 8723b: add the RTL8723B chip driver

In reply to Bitterblue Smith
Quoted 4 lines · Bitterblue Smith

On 24/09/2026 00:35, Luka Gejak wrote:

Quoted 16 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+static u8 rtw8723b_default_ofdm_index(struct rtw_dev *rtwdev)
+{
+	u32 val32;
+	u32 swing;

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 831

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	u8 i;
+
+	swing = rtw_read32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+				OFDM_SWING_MASK);
+
+	for (i = 0; i < RTW_OFDM_SWING_TABLE_SIZE; i++) {
+		val32 = rtw8723b_ofdm_swing_table[i];
+
+		if (val32 >= 0x100000)
+			val32 >>= __ffs(OFDM_SWING_MASK);

Isn't this the same as u32_get_bits(val32, OFDM_SWING_MASK) ?

Yes. It is u32_get_bits() now, and the if goes away with it.

Quoted 4 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 831

Quoted 2 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +2 -0
+		if (val32 >= 0x100000)
+			val32 >>= __ffs(OFDM_SWING_MASK);

Also, every value in the table is bigger than 0x100000, so no need for if.

There is no val32 left to test.

Quoted 11 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 910

Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+static void rtw8723b_post_enable_flow(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Enable falling edge triggered interrupts and GPIO9 interrupt mode.
+	 * The power-on sequence sets both, but it runs before the firmware is
+	 * downloaded, so reassert them here as the vendor driver does.
+	 */
+	rtw_write8_set(rtwdev, 0x0049, BIT(1));
+	rtw_write8_set(rtwdev, 0x0063, BIT(1));

This seems like bit 25 of REG_GPIO_PIN_CTRL_2.

It is. That write is rtw_write32_set(REG_GPIO_PIN_CTRL_2, BIT(25)) now. The write above it is bit 9 of REG_GPIO_INTM.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1033

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+static void rtw8723b_phy_lck(struct rtw_dev *rtwdev)
+{
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);

We have a name for 0xb0: RF_SYN_PFD. Not sure if it's the right name here.

I have decided to use it.

Quoted 4 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+static void rtw8723b_init_tx_buffer_boundary(struct rtw_dev *rtwdev)
+{
+	u8 val8 = TX_TOTAL_PAGE_NUMBER_8723B + 1;
+
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1055

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	rtw_write8(rtwdev, REG_BCNQ_BDNY, val8);
+	rtw_write8(rtwdev, REG_MGQ_BDNY, val8);
+	rtw_write8(rtwdev, REG_WMAC_LBK_BF_HD, val8);
+	rtw_write8(rtwdev, REG_TRXFF_BNDY, val8);
+	rtw_write8(rtwdev, REG_DWBCN0_CTRL + 1, val8);

__priority_queue_cfg_legacy() already takes care of these. Is it necessary to set them again here?

No. init_tx_buffer_boundary() and init_page_boundary() are both gone.

Quoted 6 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1066

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+static void rtw8723b_init_transfer_page_size(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_PBP, 0x11);

We have some macros for this in reg.h right under REG_PBP. You can use them with u8_encode_bits().

Done, u8_encode_bits(PBP_128, PBP_RX_MASK) and the same for TX.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1071

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+static void rtw8723b_init_driver_info_size(struct rtw_dev *rtwdev)
+{
+	/* NOTE: also is done in rtw_drv_info_cfg */

If it's done there do you have to do it again here?

No. init_driver_info_size() is gone.

Quoted 4 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+static void rtw8723b_init_wmac_setting(struct rtw_dev *rtwdev)
+{
+	/*
+	 * The vendor's 8723x filter value plus BIT_APP_FCS, which rtw88
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1084

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	 * needs because it advertises RX_INCLUDES_FCS. It is assigned to
+	 * hal.rcr and not merely written because rtw_core_start()
+	 * rewrites REG_RCR from hal.rcr after power_on, which would undo
+	 * a register-only write; fw.c toggles BIT_CBSSID_BCN in it.
+	 */
+	rtwdev->hal.rcr = WLAN_RCR_CFG;

Is it necessary to change the default value assigned in rtw_core_init()?

No, that is where it belngs. hal.rcr is assigned in rtw_core_init() per chip now. It is a patch of its own at the beggining of the series.

Quoted 10 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1091

Quoted 7 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +7 -0
+	rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
+
+	rtw_write32(rtwdev, REG_MAR, 0xffffffff);
+	rtw_write32(rtwdev, REG_MAR + 4, 0xffffffff);
+
+	rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2);
+	rtw_write16(rtwdev, REG_RXFLTMAP1, WLAN_RX_FILTER1);

Please also update rtwdev->hal.rxfltmap1 when you change REG_RXFLTMAP1:
https://lore.kernel.org/linux-wireless/[email protected]/

You are right, and the reuse turns out to be the fix. The chip uses rtw8723x_mac_init() now, which writes REG_RXFLTMAP0/1/2 and sets hal.rxfltmap1. rtw8723b_mac_init() and rtw8723b_init_wmac_setting() are both gone.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1135

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+static void rtw8723b_init_operation_mode(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_BWOPMODE, BIT_BWOPMODE_20MHZ);

I don't see this in the vendor driver v5.2.17.1.

After rechecking it myself, me neither. init_operation_mode() and the REG_BWOPMODE defines are gone.

Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1174

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+static void rtw8723b_init_antenna_selection(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_LEDCFG2, WLAN_ANT_SEL);
+}
+
+#define RF_AC	0x00

You can use the existing RF_MODE name for this.

Done, the local RF_AC is gone.

Quoted 4 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+static void rtw8723b_lck(struct rtw_dev *rtwdev)
+{
+	u32 rf_mode = 0, lc_cal;
+	int ret;
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1185

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	u8 val_ctx;
+	u8 rf_val;
+
+	val_ctx = rtw_read8(rtwdev, REG_CTX);
+
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0)

No need to compare.

Dropped.

Quoted 4 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+	/* 3. Read RF reg18 */
+	lc_cal = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS);
+
+	/* 4. Set LC calibration begin	bit15 */
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1206

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS, lc_cal | BIT_LCK);
+
+	ret = read_poll_timeout(rtw_read_rf, rf_val, rf_val != 0x1,
+				10000, 1000000, false,
+			 rtwdev, RF_PATH_A, RF_CFGCH, BIT_LCK);

checkpatch should catch alignment issues like this. Please always run it.

Fixed, and I am running it. checkpatch --strict is clean except the MAINTAINERS reminder on the patches that add files and the comllex-macro report on TRANS_SEQ_END, which rtw8703b.c already has.

Quoted 9 lines
Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+	if (ret)
+		rtw_warn(rtwdev, "failed to poll LCK status bit\n");
+
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdffe0);
+
+	/* Restore original situation */
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) {
+		rtw_write8(rtwdev, REG_CTX, val_ctx);
+
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1219

Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS, rf_mode);
+	} else {
+		rtw_write8(rtwdev, REG_TXPAUSE, 0x00);
+	}

I think the vendor driver is using MASK12BITS (0xfff) by mistake in this function. It doesn't make any sense, it should be RFREG_MASK (0xfffff) instead.

Agreed, and that function is gone. The chip had its own lck carrying the shared one plus the RF_MODE standby read and write and the two RF_SYN_PFD writes. The RF_MODE pair only runs in the continuous TX branch, which this driver never enters, and rtw8723b_phy_lck() already does the RF_SYN_PFD pair at RF configuration. So rtw8723b calls rtw8723x_lck() now, the way 8703b and 8723d do, and that one reads RF_CHNLBW with RFREG_MASK. Test results are the same with it dropped.

Quoted 10 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1333

Quoted 7 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +7 -0
+static u32 rtw8723b_iqk_ant_switch_path(struct rtw_dev *rtwdev)
+{
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO)
+		return rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB ? 0x280 : 0x0;
+
+	/* Scan and connect use the PTA mux, so calibrate the path they use. */
+	return (rtwdev->efuse.bt_setting & BIT(6)) ? 0x80 : 0x200;

This function would be clearer if you don't use the ternary operator at all.

Rewritten without it.

Quoted 20 lines

Not sure this logic is correct. The check in the vendor driver is like this:

bool shared_ant = bt_setting & BIT(0);

if (USB) {
	if (shared_ant)
		ant_path = RF_PATH_B;
	else
		ant_path = RF_PATH_A;
} else {
	if (bt_setting & BIT(6))
		ant_path = RF_PATH_B;
	else
		ant_path = RF_PATH_A;
}
if (!shared_ant || ant_path == RF_PATH_A)
	return 0;
else
	return 0x280;
Quoted 1 line

I didn't see 0x80 and 0x200 in the IQK code.

It follows the vendor now, shared_ant from bit 0 and ant_path from bit 6, returning 0 or 0x280. The 0x80 and 0x200 are gone.

Quoted 10 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1387

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+static void rtw8723b_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw)
+{
+	u32 rf_cfgch;
+
+	rf_cfgch = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK);
+
+	rf_cfgch &= ~RFCFGCH_CHANNEL_MASK;
+	rf_cfgch |= channel & RFCFGCH_CHANNEL_MASK;

This is what u32(p)_replace_bits is for.

Done, now using u32p_replace_bits().

Quoted 6 lines
Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+
+	rf_cfgch &= ~RFCFGCH_BW_MASK;
+	switch (bw) {
+	case RTW_CHANNEL_WIDTH_20:
+		rf_cfgch |= RFCFGCH_BW_20M;
+		break;
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1399

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	case RTW_CHANNEL_WIDTH_40:
+		rf_cfgch |= RFCFGCH_BW_40M;
+		break;
+	default:
+		break;
+	}

bw is only going to be 20 or 40, so a simple if would be shorter.

This is the one I left alone, the bandwidth assignment is still a single ternary.

Quoted 24 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1437

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+static void rtw8723b_set_channel(struct rtw_dev *rtwdev, u8 channel,
+				 u8 bw, u8 primary_chan_idx)
+{
+	rtw8723b_set_channel_rf(rtwdev, channel, bw);
+	rtw_set_channel_mac(rtwdev, channel, bw, primary_chan_idx);
+	rtw8723b_set_channel_bb(rtwdev, bw, primary_chan_idx);

I don't think the vendor driver v5.2.17.1 does the stuff below:

Quoted 15 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +15 -0
+	rtw8723b_reassert_rx_path(rtwdev);
+
+	if (rtw_is_8723bs(rtwdev)) {
+		bool keep_pta_owner;
+
+		keep_pta_owner = test_bit(RTW_FLAG_SCANNING, rtwdev->flags) ||
+				 (rtw_read32(rtwdev, REG_PAD_CTRL1) &
+				  BIT_PAPE_WLBT_SEL);
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, keep_pta_owner);
+
+		rtw_write8(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+		fsleep(1000);
+
+		/*
+		 * RF_WLINT bits 0-1 gate the data path into the BB and a prior
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1457

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+		 * IQK or coex run can leave them blocking TX, so re-arm them.
+		 */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
+			     0x0780);
+	}

If this driver doesn't work without it, you probably have a bug somewhere.

The block is still there. I did not find a bug, and I have not found a reason to delete it either. rtw8723b_reassert_rx_path() has a comment saying what it puts back, and where the PAD bits come from is in my reply to your other mail.

Quoted 6 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1716

Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+	/* enable path A PA in TX IQK mode */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x20000);

Where does 0x18000 come from? The vendor driver v5.2.17.1 uses 0x20000 here.

That value was mine and it is wrong. The vendor's value is used now.

Quoted 4 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1731

Quoted 2 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +2 -0
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N,
+		    sdio_iqk ? 0x821303ea : 0x821403ea);

Here, too, it uses the second value.

Same, the vendor's value.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1786

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x30000);

Vendor driver uses the second value.

0x30000 now.

Quoted 6 lines
Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	/* IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00);
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x18008c1c);
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1801

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N,
+		    sdio_iqk ? 0x82130ff0 : 0x82160ff0);

Here as well.

Also the vendor's value, in the RX path RF_MODE_TABLE_ADDR write.

Quoted 9 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1853

Quoted 7 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +7 -0
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK step2!\n");
+
+	/* modify RX IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x30000);

Here as well.

And in the REG_RXIQK_PI_A_11N write.

Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1764

Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */

No need for a comment that just repeats what the name of the function already says.

Quoted 11 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1872

Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N,
+		    sdio_iqk ? 0x2813001f : 0x2816001f);

Here as well.

All three are gone.

Quoted 6 lines
Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x30000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7fb7);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xed, 0x20, 0x1);
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2050

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x43, RFREG_MASK, 0x60fbd);
+
+	for (i = 0; i < PATH_IQK_RETRY; i++) {
+		a_ok = rtw8723b_iqk_tx_path_a(rtwdev);
+		if (a_ok == IQK_TX_OK) {

If you invert the condition, this block with long lines can be indented less.

Inverted.

Quoted 9 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2093

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+
+	if (a_ok == 0x0)
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A IQK fail!\n");
+
+	/* rtl8723b is 1T1R, so path B is not calibrated. */

It looks like the vendor driver is calibrating path B as well (phy_path_b_iqk_8723b() and phy_path_b_rx_iqk_8723b()) when the device has two antennas, and only path A when the device has one antenna.

Path B is still not calibrated, so path A only. The comment now says what you said, that the vendor calibrates it when the board is not 1-antenna and that this driver does not support that case. I would rather not add IQK code I cannot test on a 1x1 card.

Quoted 10 lines
Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	memset(result, 0, sizeof(result));
+
+	rtw8723b_lck(rtwdev);
+	rtw8723b_inform_rfk_status(rtwdev, true);
+
+	/* The LTE path GNT backup that 8723d does is not needed on SDIO. */
+	rtw8723x_iqk_backup_path_ctrl(rtwdev, &backup);
+	rtw8723x_iqk_backup_regs(rtwdev, &backup);
+
+	/* save default GNT_BT */
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2123

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	bt_control = rtw_read32(rtwdev, REG_BT_CONTROL_8723B);
+
+	for (i = IQK_ROUND_0; i <= IQK_ROUND_2; i++) {
+		if (!rtw_is_8723bs(rtwdev))
+			rtw8723x_iqk_config_path_ctrl(rtwdev);

I didn't see this in the RTL8723BE or RTL8723BU drivers. I guess it was copied from another chip?

It was. The iqk_config_path_ctrl() and restore calls are gone.

Quoted 4 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] final_candidate is %x\n",
+		final_candidate);
+
+	for (i = IQK_ROUND_0; i < IQK_ROUND_SIZE; i++)
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2207

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"[IQK] Result %u: rege94_s1=%x rege9c_s1=%x regea4_s1=%x regeac_s1=%x rege94_s0=%x rege9c_s0=%x regea4_s0=%x regeac_s0=%x %s\n",
+			i,
+			result[i][0], result[i][1], result[i][2], result[i][3],
+			result[i][4], result[i][5], result[i][6], result[i][7],
+			final_candidate == i ? "(final candidate)" : "");

You can make this two calls to rtw_dbg to avoid such a long line.

Two calls now.

Quoted 8 lines
Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+static void rtw8723b_pwrtrack_set_cck_pwr(struct rtw_dev *rtwdev, s8 swing_idx,
+					  s8 txagc_idx)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	dm_info->txagc_remnant_cck = txagc_idx;
+
+	swing_idx = clamp_t(s8, swing_idx, 0, RTW_CCK_SWING_TABLE_SIZE - 1);
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2248

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+
+	BUILD_BUG_ON(ARRAY_SIZE(rtw8723b_cck_pwr_regs) !=
+		     ARRAY_SIZE(rtw8723b_cck_swing_table_ch1_ch13[0]));
+
+	/* Only ch1-13 is wired up; channel 14 is Japan-only and unreachable. */

If I change my country code to JP and trigger a scan, rtw88 visits channel 14.

Right. Channel 14 selects rtw8723b_cck_swing_table_ch14 now.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2250

Quoted 2 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +2 -0
+	for (int i = 0; i < ARRAY_SIZE(rtw8723b_cck_pwr_regs); i++)
+		rtw_write8(rtwdev, rtw8723b_cck_pwr_regs[i],

These register addresses are consecutive values, there is no need to put them in an array.

The array is gone, it loops over the eight consecutive registers from 0x0a22.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2322

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+	delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A);
+
+	/* NOTE: also done in rtw_phy_pwrtrack_get_delta */

Is it necessary to do it again?

No. The clamp is only in rtw_phy_pwrtrack_get_delta() now.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2340

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+iqk:
+	if (do_iqk)
+		rtw8723b_phy_calibration(rtwdev);

The vendor driver doesn't redo IQK here.

8723d and 8703b in rtw88 both call phy_calibration() from phy_pwrtrack() when rtw_phy_pwrtrack_need_iqk() returns true, so I kept it and match the other chips.

Quoted 11 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2349

Quoted 7 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +7 -0
+static void rtw8723b_pwr_track(struct rtw_dev *rtwdev)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	if (efuse->power_track_type != 0) {
+		rtw_warn(rtwdev, "unsupported power track type\n");

This function runs every two seconds, forever. Please put this warning elsewhere, like rtw8723b_read_efuse(), to avoid filling the kernel log. (Or create rtw_warn_once()).

Moved to rtw8723b_read_efuse().

Quoted 6 lines
Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+static void rtw8723b_coex_cfg_ant_buffer(struct rtw_dev *rtwdev)
+{
+	u8 sys_func_before;
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2435

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	    WLAN_SYS_FUNC_BB_ENABLE) {
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+		usleep_range(10, 11);
+	}

Not sure why you need to touch REG_SYS_FUNC_EN here. The vendor driver doesn't do it.

That is gone from rtw8723b_coex_cfg_ant_buffer().

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2439

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+	rtw_write8_set(rtwdev, REG_PWR_DATA + 1,
+		       BIT_EEPRPAD_RFE_CTRL_EN >> 8);
+	rtw8723b_coex_write8_verify(rtwdev, REG_RFE_CTRL_E, 0xff);

The vendor driver uses a normal rtw_write8() here.

coex_write8_verify() is gone and the call is a plain rtw_write8() now.

Quoted 10 lines
Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+		usleep_range(10, 11);
+	}
+
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+	readback = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+	if (readback == value)
+		return readback;
+
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2471

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, WLAN_SYS_FUNC_BB_ENABLE);
+	usleep_range(10, 11);
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+
+	return rtw_read32(rtwdev, REG_BB_SEL_BTG);

All this looks strange too. What happens if you eliminate this function and write REG_BB_SEL_BTG with a simple rtw_write32()?

Nothing, it works. The function is gone and every call site does a plain rtw_write32() now.

Quoted 9 lines
Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+	.query_phy_status	= rtw8723b_query_phy_status,
+	.read_rf		= rtw_phy_read_rf_sipi,
+	.write_rf		= rtw_phy_write_rf_reg_sipi,
+	.set_tx_power_index	= rtw8723x_set_tx_power_index,
+	.rsvd_page_dump		= NULL,
+	.set_antenna		= NULL,
+	.cfg_ldo25		= rtw8723b_cfg_ldo25,
+	.efuse_grant		= rtw8723b_efuse_grant,
+	.set_ampdu_factor	= NULL,
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2708

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	.false_alarm_statistics	= rtw8723x_false_alarm_statistics,
+	.phy_calibration	= rtw8723b_phy_calibration,
+	.dpk_track		= NULL,
+	/* REG_CSRATIO does not exist on this chip generation. */
+	.cck_pd_set		= NULL,

You can use rtw88xxa_phy_cck_pd_set() from rtw88xxa.c for this chip too.

Done, and it has a consequence you should know about. The chip driver calls into rtw88xxa.c now, so RTW88_8723B selects RTW88_88XXA, which makes rtw88_8723b.ko depend on rtw88_88xxa.ko and pulls that PCI chip module into an SDIO only build. 8821A and 8812A select it the same way, it builds and loads clean, and it was on the card for a suite and a soak. Say so if you would rather not have the dependency and I will put the function in the chip driver instead. The same file carries the adaptive control and EDCA init now as well, because the chip was writing the same values, so the dependency does more than that one call.

Quoted 10 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2753

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+	.txgi_factor = 1,
+	.is_pwr_by_rate_dec = true,
+	.max_power_index = 0x3f,
+	.csi_buf_pg_num = 0,
+	.band = RTW_BAND_2G,
+	.page_size = TX_PAGE_SIZE,
+	.dig_min = 0x20,
+	.usb_tx_agg_desc_num = 1,

The RTL8723BU vendor driver v5.2.17 sets this to 6.

usb_tx_agg_desc_num is 6 now.

Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2775

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	/* same shared table as the sibling rtw8703b and rtw8723d */
+	.prioq_addrs = &rtw8723x_common.prioq_addrs,
+	/* used only in pci.c, not needed for SDIO devices */
+	.intf_table = NULL,
+	.dig = rtw8723x_common.dig,
+	.dig_cck = rtw8723x_common.dig_cck,

This chip doesn't have dig_cck.

You are right, and I had to reread the vendor tree to figure out why. ODM_IC_PHY_STATUE_NEW_TYPE is 8197F | 8822B | 8723D | 8821C | 8710B, no 8723B in it, so that write never runs here. dig_cck is NULL.

Best regards,
Luka Gejak

#11 lore raw

Re: [PATCH v4 3/6] wifi: rtw88: 8723b: add the RTL8723B chip driver

Quoted 189 lines · Luka Gejak
Quoted 4 lines · Bitterblue Smith

On 24/09/2026 00:35, Luka Gejak wrote:

Quoted 16 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+static u8 rtw8723b_default_ofdm_index(struct rtw_dev *rtwdev)
+{
+	u32 val32;
+	u32 swing;

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 831

Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	u8 i;
+
+	swing = rtw_read32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE,
+				OFDM_SWING_MASK);
+
+	for (i = 0; i < RTW_OFDM_SWING_TABLE_SIZE; i++) {
+		val32 = rtw8723b_ofdm_swing_table[i];
+
+		if (val32 >= 0x100000)
+			val32 >>= __ffs(OFDM_SWING_MASK);

Isn't this the same as u32_get_bits(val32, OFDM_SWING_MASK) ?

Yes. It is u32_get_bits() now, and the if goes away with it.

Quoted 4 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 831

Quoted 2 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +2 -0
+		if (val32 >= 0x100000)
+			val32 >>= __ffs(OFDM_SWING_MASK);

Also, every value in the table is bigger than 0x100000, so no need for if.

There is no val32 left to test.

Quoted 11 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 910

Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+static void rtw8723b_post_enable_flow(struct rtw_dev *rtwdev)
+{
+	/*
+	 * Enable falling edge triggered interrupts and GPIO9 interrupt mode.
+	 * The power-on sequence sets both, but it runs before the firmware is
+	 * downloaded, so reassert them here as the vendor driver does.
+	 */
+	rtw_write8_set(rtwdev, 0x0049, BIT(1));
+	rtw_write8_set(rtwdev, 0x0063, BIT(1));

This seems like bit 25 of REG_GPIO_PIN_CTRL_2.

It is. That write is rtw_write32_set(REG_GPIO_PIN_CTRL_2, BIT(25)) now. The write above it is bit 9 of REG_GPIO_INTM.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1033

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+static void rtw8723b_phy_lck(struct rtw_dev *rtwdev)
+{
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);

We have a name for 0xb0: RF_SYN_PFD. Not sure if it's the right name here.

I have decided to use it.

Quoted 4 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+static void rtw8723b_init_tx_buffer_boundary(struct rtw_dev *rtwdev)
+{
+	u8 val8 = TX_TOTAL_PAGE_NUMBER_8723B + 1;
+
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1055

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	rtw_write8(rtwdev, REG_BCNQ_BDNY, val8);
+	rtw_write8(rtwdev, REG_MGQ_BDNY, val8);
+	rtw_write8(rtwdev, REG_WMAC_LBK_BF_HD, val8);
+	rtw_write8(rtwdev, REG_TRXFF_BNDY, val8);
+	rtw_write8(rtwdev, REG_DWBCN0_CTRL + 1, val8);

__priority_queue_cfg_legacy() already takes care of these. Is it necessary to set them again here?

No. init_tx_buffer_boundary() and init_page_boundary() are both gone.

Quoted 6 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1066

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+static void rtw8723b_init_transfer_page_size(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_PBP, 0x11);

We have some macros for this in reg.h right under REG_PBP. You can use them with u8_encode_bits().

Done, u8_encode_bits(PBP_128, PBP_RX_MASK) and the same for TX.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1071

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+static void rtw8723b_init_driver_info_size(struct rtw_dev *rtwdev)
+{
+	/* NOTE: also is done in rtw_drv_info_cfg */

If it's done there do you have to do it again here?

No. init_driver_info_size() is gone.

Quoted 4 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+static void rtw8723b_init_wmac_setting(struct rtw_dev *rtwdev)
+{
+	/*
+	 * The vendor's 8723x filter value plus BIT_APP_FCS, which rtw88
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1084

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	 * needs because it advertises RX_INCLUDES_FCS. It is assigned to
+	 * hal.rcr and not merely written because rtw_core_start()
+	 * rewrites REG_RCR from hal.rcr after power_on, which would undo
+	 * a register-only write; fw.c toggles BIT_CBSSID_BCN in it.
+	 */
+	rtwdev->hal.rcr = WLAN_RCR_CFG;

Is it necessary to change the default value assigned in rtw_core_init()?

No, that is where it belngs. hal.rcr is assigned in rtw_core_init() per chip now. It is a patch of its own at the beggining of the series.

Quoted 10 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1091

Quoted 7 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +7 -0
+	rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
+
+	rtw_write32(rtwdev, REG_MAR, 0xffffffff);
+	rtw_write32(rtwdev, REG_MAR + 4, 0xffffffff);
+
+	rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2);
+	rtw_write16(rtwdev, REG_RXFLTMAP1, WLAN_RX_FILTER1);

Please also update rtwdev->hal.rxfltmap1 when you change REG_RXFLTMAP1:
https://lore.kernel.org/linux-wireless/[email protected]/

You are right, and the reuse turns out to be the fix. The chip uses rtw8723x_mac_init() now, which writes REG_RXFLTMAP0/1/2 and sets hal.rxfltmap1. rtw8723b_mac_init() and rtw8723b_init_wmac_setting() are both gone.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1135

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+static void rtw8723b_init_operation_mode(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_BWOPMODE, BIT_BWOPMODE_20MHZ);

I don't see this in the vendor driver v5.2.17.1.

After rechecking it myself, me neither. init_operation_mode() and the REG_BWOPMODE defines are gone.

Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1174

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+static void rtw8723b_init_antenna_selection(struct rtw_dev *rtwdev)
+{
+	rtw_write8(rtwdev, REG_LEDCFG2, WLAN_ANT_SEL);
+}
+
+#define RF_AC	0x00

You can use the existing RF_MODE name for this.

Done, the local RF_AC is gone.

Quoted 4 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+static void rtw8723b_lck(struct rtw_dev *rtwdev)
+{
+	u32 rf_mode = 0, lc_cal;
+	int ret;
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1185

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	u8 val_ctx;
+	u8 rf_val;
+
+	val_ctx = rtw_read8(rtwdev, REG_CTX);
+
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0)

No need to compare.

Dropped.

Quoted 4 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+	/* 3. Read RF reg18 */
+	lc_cal = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS);
+
+	/* 4. Set LC calibration begin	bit15 */
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1206

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS, lc_cal | BIT_LCK);
+
+	ret = read_poll_timeout(rtw_read_rf, rf_val, rf_val != 0x1,
+				10000, 1000000, false,
+			 rtwdev, RF_PATH_A, RF_CFGCH, BIT_LCK);

checkpatch should catch alignment issues like this. Please always run it.

Fixed, and I am running it. checkpatch --strict is clean except the MAINTAINERS reminder on the patches that add files and the comllex-macro report on TRANS_SEQ_END, which rtw8703b.c already has.

Quoted 9 lines
Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+	if (ret)
+		rtw_warn(rtwdev, "failed to poll LCK status bit\n");
+
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdffe0);
+
+	/* Restore original situation */
+	if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) {
+		rtw_write8(rtwdev, REG_CTX, val_ctx);
+
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1219

Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_AC, MASK12BITS, rf_mode);
+	} else {
+		rtw_write8(rtwdev, REG_TXPAUSE, 0x00);
+	}

I think the vendor driver is using MASK12BITS (0xfff) by mistake in this function. It doesn't make any sense, it should be RFREG_MASK (0xfffff) instead.

Quoted 7 lines

Agreed, and that function is gone. The chip had its own lck carrying the shared one plus the RF_MODE standby read and write and the two RF_SYN_PFD writes. The RF_MODE pair only runs in the continuous TX branch, which this driver never enters, and rtw8723b_phy_lck() already does the RF_SYN_PFD pair at RF configuration. So rtw8723b calls rtw8723x_lck() now, the way 8703b and 8723d do, and that one reads RF_CHNLBW with RFREG_MASK. Test results are the same with it dropped.

Why drop this function? You don't know what RF_SYN_PFD is for. Maybe it's required every time it does LC calibration?

Quoted 103 lines
Quoted 11 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1333

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +7 -0
+static u32 rtw8723b_iqk_ant_switch_path(struct rtw_dev *rtwdev)
+{
+	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO)
+		return rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB ? 0x280 : 0x0;
+
+	/* Scan and connect use the PTA mux, so calibrate the path they use. */
+	return (rtwdev->efuse.bt_setting & BIT(6)) ? 0x80 : 0x200;

This function would be clearer if you don't use the ternary operator at all.

Rewritten without it.

Quoted 20 lines

Not sure this logic is correct. The check in the vendor driver is like this:

bool shared_ant = bt_setting & BIT(0);

if (USB) {
	if (shared_ant)
		ant_path = RF_PATH_B;
	else
		ant_path = RF_PATH_A;
} else {
	if (bt_setting & BIT(6))
		ant_path = RF_PATH_B;
	else
		ant_path = RF_PATH_A;
}
if (!shared_ant || ant_path == RF_PATH_A)
	return 0;
else
	return 0x280;
Quoted 1 line

I didn't see 0x80 and 0x200 in the IQK code.

It follows the vendor now, shared_ant from bit 0 and ant_path from bit 6, returning 0 or 0x280. The 0x80 and 0x200 are gone.

Quoted 10 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1387

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+static void rtw8723b_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw)
+{
+	u32 rf_cfgch;
+
+	rf_cfgch = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK);
+
+	rf_cfgch &= ~RFCFGCH_CHANNEL_MASK;
+	rf_cfgch |= channel & RFCFGCH_CHANNEL_MASK;

This is what u32(p)_replace_bits is for.

Done, now using u32p_replace_bits().

Quoted 6 lines
Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+
+	rf_cfgch &= ~RFCFGCH_BW_MASK;
+	switch (bw) {
+	case RTW_CHANNEL_WIDTH_20:
+		rf_cfgch |= RFCFGCH_BW_20M;
+		break;
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1399

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	case RTW_CHANNEL_WIDTH_40:
+		rf_cfgch |= RFCFGCH_BW_40M;
+		break;
+	default:
+		break;
+	}

bw is only going to be 20 or 40, so a simple if would be shorter.

This is the one I left alone, the bandwidth assignment is still a single ternary.

Quoted 24 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1437

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+static void rtw8723b_set_channel(struct rtw_dev *rtwdev, u8 channel,
+				 u8 bw, u8 primary_chan_idx)
+{
+	rtw8723b_set_channel_rf(rtwdev, channel, bw);
+	rtw_set_channel_mac(rtwdev, channel, bw, primary_chan_idx);
+	rtw8723b_set_channel_bb(rtwdev, bw, primary_chan_idx);

I don't think the vendor driver v5.2.17.1 does the stuff below:

Quoted 15 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +15 -0
+	rtw8723b_reassert_rx_path(rtwdev);
+
+	if (rtw_is_8723bs(rtwdev)) {
+		bool keep_pta_owner;
+
+		keep_pta_owner = test_bit(RTW_FLAG_SCANNING, rtwdev->flags) ||
+				 (rtw_read32(rtwdev, REG_PAD_CTRL1) &
+				  BIT_PAPE_WLBT_SEL);
+		rtw8723b_sdio_restore_pad_ctrl(rtwdev, keep_pta_owner);
+
+		rtw_write8(rtwdev, REG_RF_CTRL, WLAN_RF_CTRL_ENABLE);
+		fsleep(1000);
+
+		/*
+		 * RF_WLINT bits 0-1 gate the data path into the BB and a prior
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1457

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+		 * IQK or coex run can leave them blocking TX, so re-arm them.
+		 */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
+			     0x0780);
+	}

If this driver doesn't work without it, you probably have a bug somewhere.

Quoted 4 lines

The block is still there. I did not find a bug, and I have not found a reason to delete it either. rtw8723b_reassert_rx_path() has a comment saying what it puts back, and where the PAD bits come from is in my reply to your other mail.

So the driver doesn't work if you delete this part?

Quoted 186 lines
Quoted 7 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1716

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+	/* enable path A PA in TX IQK mode */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x20000);

Where does 0x18000 come from? The vendor driver v5.2.17.1 uses 0x20000 here.

That value was mine and it is wrong. The vendor's value is used now.

Quoted 4 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1731

Quoted 2 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +2 -0
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N,
+		    sdio_iqk ? 0x821303ea : 0x821403ea);

Here, too, it uses the second value.

Same, the vendor's value.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1786

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x30000);

Vendor driver uses the second value.

0x30000 now.

Quoted 6 lines
Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	/* IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00);
+	rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800);
+
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x18008c1c);
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1801

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N,
+		    sdio_iqk ? 0x82130ff0 : 0x82160ff0);

Here as well.

Also the vendor's value, in the RX path RF_MODE_TABLE_ADDR write.

Quoted 9 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1853

Quoted 7 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +7 -0
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A RX IQK step2!\n");
+
+	/* modify RX IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+		     sdio_iqk ? 0x18000 : 0x30000);

Here as well.

And in the REG_RXIQK_PI_A_11N write.

Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1764

Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+	/* leave IQK mode */
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+
+	/* Check failed */

No need for a comment that just repeats what the name of the function already says.

Quoted 11 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 1872

Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+	/* path-A IQK setting */
+	rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x18008c1c);
+	rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c);
+	rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+	rtw_write32(rtwdev, REG_TXIQK_PI_A_11N, 0x82110000);
+	rtw_write32(rtwdev, REG_RXIQK_PI_A_11N,
+		    sdio_iqk ? 0x2813001f : 0x2816001f);

Here as well.

All three are gone.

Quoted 6 lines
Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, 0x80000, 0x1);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x30000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x0001f);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xf7fb7);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0xed, 0x20, 0x1);
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2050

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x43, RFREG_MASK, 0x60fbd);
+
+	for (i = 0; i < PATH_IQK_RETRY; i++) {
+		a_ok = rtw8723b_iqk_tx_path_a(rtwdev);
+		if (a_ok == IQK_TX_OK) {

If you invert the condition, this block with long lines can be indented less.

Inverted.

Quoted 9 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2093

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+
+	if (a_ok == 0x0)
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path A IQK fail!\n");
+
+	/* rtl8723b is 1T1R, so path B is not calibrated. */

It looks like the vendor driver is calibrating path B as well (phy_path_b_iqk_8723b() and phy_path_b_rx_iqk_8723b()) when the device has two antennas, and only path A when the device has one antenna.

Path B is still not calibrated, so path A only. The comment now says what you said, that the vendor calibrates it when the board is not 1-antenna and that this driver does not support that case. I would rather not add IQK code I cannot test on a 1x1 card.

Quoted 10 lines
Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+	memset(result, 0, sizeof(result));
+
+	rtw8723b_lck(rtwdev);
+	rtw8723b_inform_rfk_status(rtwdev, true);
+
+	/* The LTE path GNT backup that 8723d does is not needed on SDIO. */
+	rtw8723x_iqk_backup_path_ctrl(rtwdev, &backup);
+	rtw8723x_iqk_backup_regs(rtwdev, &backup);
+
+	/* save default GNT_BT */
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2123

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	bt_control = rtw_read32(rtwdev, REG_BT_CONTROL_8723B);
+
+	for (i = IQK_ROUND_0; i <= IQK_ROUND_2; i++) {
+		if (!rtw_is_8723bs(rtwdev))
+			rtw8723x_iqk_config_path_ctrl(rtwdev);

I didn't see this in the RTL8723BE or RTL8723BU drivers. I guess it was copied from another chip?

It was. The iqk_config_path_ctrl() and restore calls are gone.

Quoted 4 lines
Quoted 4 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +4 -0
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] final_candidate is %x\n",
+		final_candidate);
+
+	for (i = IQK_ROUND_0; i < IQK_ROUND_SIZE; i++)
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2207

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"[IQK] Result %u: rege94_s1=%x rege9c_s1=%x regea4_s1=%x regeac_s1=%x rege94_s0=%x rege9c_s0=%x regea4_s0=%x regeac_s0=%x %s\n",
+			i,
+			result[i][0], result[i][1], result[i][2], result[i][3],
+			result[i][4], result[i][5], result[i][6], result[i][7],
+			final_candidate == i ? "(final candidate)" : "");

You can make this two calls to rtw_dbg to avoid such a long line.

Two calls now.

Quoted 8 lines
Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+static void rtw8723b_pwrtrack_set_cck_pwr(struct rtw_dev *rtwdev, s8 swing_idx,
+					  s8 txagc_idx)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	dm_info->txagc_remnant_cck = txagc_idx;
+
+	swing_idx = clamp_t(s8, swing_idx, 0, RTW_CCK_SWING_TABLE_SIZE - 1);
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2248

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+
+	BUILD_BUG_ON(ARRAY_SIZE(rtw8723b_cck_pwr_regs) !=
+		     ARRAY_SIZE(rtw8723b_cck_swing_table_ch1_ch13[0]));
+
+	/* Only ch1-13 is wired up; channel 14 is Japan-only and unreachable. */

If I change my country code to JP and trigger a scan, rtw88 visits channel 14.

Right. Channel 14 selects rtw8723b_cck_swing_table_ch14 now.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2250

Quoted 2 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +2 -0
+	for (int i = 0; i < ARRAY_SIZE(rtw8723b_cck_pwr_regs); i++)
+		rtw_write8(rtwdev, rtw8723b_cck_pwr_regs[i],

These register addresses are consecutive values, there is no need to put them in an array.

The array is gone, it loops over the eight consecutive registers from 0x0a22.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2322

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+	delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A);
+
+	/* NOTE: also done in rtw_phy_pwrtrack_get_delta */

Is it necessary to do it again?

No. The clamp is only in rtw_phy_pwrtrack_get_delta() now.

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2340

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+iqk:
+	if (do_iqk)
+		rtw8723b_phy_calibration(rtwdev);

The vendor driver doesn't redo IQK here.

Quoted 3 lines

8723d and 8703b in rtw88 both call phy_calibration() from phy_pwrtrack() when rtw_phy_pwrtrack_need_iqk() returns true, so I kept it and match the other chips.

Different chips can have different needs...

Quoted 80 lines
Quoted 12 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2349

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +7 -0
+static void rtw8723b_pwr_track(struct rtw_dev *rtwdev)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	if (efuse->power_track_type != 0) {
+		rtw_warn(rtwdev, "unsupported power track type\n");

This function runs every two seconds, forever. Please put this warning elsewhere, like rtw8723b_read_efuse(), to avoid filling the kernel log. (Or create rtw_warn_once()).

Moved to rtw8723b_read_efuse().

Quoted 6 lines
Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+static void rtw8723b_coex_cfg_ant_buffer(struct rtw_dev *rtwdev)
+{
+	u8 sys_func_before;
+
+	sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	if ((sys_func_before & WLAN_SYS_FUNC_BB_ENABLE) !=
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2435

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	    WLAN_SYS_FUNC_BB_ENABLE) {
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+		usleep_range(10, 11);
+	}

Not sure why you need to touch REG_SYS_FUNC_EN here. The vendor driver doesn't do it.

That is gone from rtw8723b_coex_cfg_ant_buffer().

Quoted 5 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2439

Quoted 3 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +3 -0
+	rtw_write8_set(rtwdev, REG_PWR_DATA + 1,
+		       BIT_EEPRPAD_RFE_CTRL_EN >> 8);
+	rtw8723b_coex_write8_verify(rtwdev, REG_RFE_CTRL_E, 0xff);

The vendor driver uses a normal rtw_write8() here.

coex_write8_verify() is gone and the call is a plain rtw_write8() now.

Quoted 10 lines
Quoted 10 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +10 -0
+		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
+			       WLAN_SYS_FUNC_BB_ENABLE);
+		usleep_range(10, 11);
+	}
+
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+	readback = rtw_read32(rtwdev, REG_BB_SEL_BTG);
+	if (readback == value)
+		return readback;
+
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2471

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, WLAN_SYS_FUNC_BB_ENABLE);
+	usleep_range(10, 11);
+	rtw_write32(rtwdev, REG_BB_SEL_BTG, value);
+
+	return rtw_read32(rtwdev, REG_BB_SEL_BTG);

All this looks strange too. What happens if you eliminate this function and write REG_BB_SEL_BTG with a simple rtw_write32()?

Nothing, it works. The function is gone and every call site does a plain rtw_write32() now.

Quoted 9 lines
Quoted 9 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +9 -0
+	.query_phy_status	= rtw8723b_query_phy_status,
+	.read_rf		= rtw_phy_read_rf_sipi,
+	.write_rf		= rtw_phy_write_rf_reg_sipi,
+	.set_tx_power_index	= rtw8723x_set_tx_power_index,
+	.rsvd_page_dump		= NULL,
+	.set_antenna		= NULL,
+	.cfg_ldo25		= rtw8723b_cfg_ldo25,
+	.efuse_grant		= rtw8723b_efuse_grant,
+	.set_ampdu_factor	= NULL,
Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2708

Quoted 5 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +5 -0
+	.false_alarm_statistics	= rtw8723x_false_alarm_statistics,
+	.phy_calibration	= rtw8723b_phy_calibration,
+	.dpk_track		= NULL,
+	/* REG_CSRATIO does not exist on this chip generation. */
+	.cck_pd_set		= NULL,

You can use rtw88xxa_phy_cck_pd_set() from rtw88xxa.c for this chip too.

Quoted 10 lines

Done, and it has a consequence you should know about. The chip driver calls into rtw88xxa.c now, so RTW88_8723B selects RTW88_88XXA, which makes rtw88_8723b.ko depend on rtw88_88xxa.ko and pulls that PCI chip module into an SDIO only build. 8821A and 8812A select it the same way, it builds and loads clean, and it was on the card for a suite and a soak. Say so if you would rather not have the dependency and I will put the function in the chip driver instead. The same file carries the adaptive control and EDCA init now as well, because the chip was writing the same values, so the dependency does more than that one call.

A better idea: move rtw88xxa_phy_cck_pd_set() to phy.c and don't depend on rtw88_88xxa. That can be a separate patch, of course.

Quoted 28 lines
Quoted 10 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2753

Quoted 8 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +8 -0
+	.txgi_factor = 1,
+	.is_pwr_by_rate_dec = true,
+	.max_power_index = 0x3f,
+	.csi_buf_pg_num = 0,
+	.band = RTW_BAND_2G,
+	.page_size = TX_PAGE_SIZE,
+	.dig_min = 0x20,
+	.usb_tx_agg_desc_num = 1,

The RTL8723BU vendor driver v5.2.17 sets this to 6.

usb_tx_agg_desc_num is 6 now.

Quoted 8 lines

drivers/net/wireless/realtek/rtw88/rtw8723b.c line 2775

Quoted 6 lines
drivers/net/wireless/realtek/rtw88/rtw8723b.c new file +6 -0
+	/* same shared table as the sibling rtw8703b and rtw8723d */
+	.prioq_addrs = &rtw8723x_common.prioq_addrs,
+	/* used only in pci.c, not needed for SDIO devices */
+	.intf_table = NULL,
+	.dig = rtw8723x_common.dig,
+	.dig_cck = rtw8723x_common.dig_cck,

This chip doesn't have dig_cck.

You are right, and I had to reread the vendor tree to figure out why. ODM_IC_PHY_STATUE_NEW_TYPE is 8197F | 8822B | 8723D | 8821C | 8710B, no 8723B in it, so that write never runs here. dig_cck is NULL.

Best regards,
Luka Gejak

author
#12 lore raw

Re: [PATCH v4 3/6] wifi: rtw88: 8723b: add the RTL8723B chip driver

Quoted 13 lines · Bitterblue Smith
Quoted 9 lines · Luka Gejak
	static void rtw8723b_lck(struct rtw_dev *rtwdev)
	{
		...
		rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdfbe0);
		rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, MASK12BITS, lc_cal | BIT_LCK);
		...
		rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdffe0);
	}
Quoted 2 lines

Why drop this function? You don't know what RF_SYN_PFD is for. Maybe it's required every time it does LC calibration?

That is rtw8723b_lck(). You are right about the pair, and I had it backwards. The vendor writes it inside the calibration itself, in _phy_lc_calibrate_8723b(), which is what halrf_lck_trigger() calls for this chip, at init and from the power tracking: 0xDFBE0 on RF reg 0xB0 before the LCK trigger and 0xDFFE0 after it. rtw8723x_lck() does not touch 0xB0, so every calibration after the first one ran with the LDO off.

The pair is back, and it is the whole function now:

	static void rtw8723b_lck(struct rtw_dev *rtwdev)
	{
		rtw_write_rf(rtwdev, RF_PATH_A, RF_SYN_PFD, RFREG_MASK, 0xdfbe0);

		rtw8723x_lck(rtwdev);

		rtw_write_rf(rtwdev, RF_PATH_A, RF_SYN_PFD, RFREG_MASK, 0xdffe0);
	}

The RF_MODE standby pair stays gone, it only runs in the continuous TX branch of the vendor. On the card RF_SYN_PFD reads 0xdfbe0 at the LCK now and 0xdffe0 before, and the LCK completes in both cases.

Quoted 9 lines
Quoted 7 lines
	rtw8723b_reassert_rx_path(rtwdev);

	if (rtw_is_8723bs(rtwdev)) {
		...
		rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
			           0x0780);
	}

So the driver doesn't work if you delete this part?

It does, and I removed it, both the helper and the block. Its own record is against it too: four of the five registers it checks already held the value it was about to write, and the RF_WLINT write it depends on did not take effect at the time. The stall it was added for was the firmware dropping unicast management frames, so it can safely go out.

Quoted 5 lines
Quoted 3 lines
	iqk:
	if (do_iqk)
		rtw8723b_phy_calibration(rtwdev);

Different chips can have different needs...

You are right, and the vendor says it outright. Its power tracking excludes this chip from the IQK rerun, and the LCK just above it is not excluded, so the chip now redoes the LCK on a drift and leaves the IQK alone. I forced iqk_threshold to 1 to exercise it: the LCK ran, no IQK, link stayed up.

Quoted 5 lines
Quoted 2 lines
	/* REG_CSRATIO does not exist on this chip generation. */
	.cck_pd_set		= NULL,

A better idea: move rtw88xxa_phy_cck_pd_set() to phy.c and don't depend on rtw88_88xxa. That can be a separate patch, of course.

Done, and it took the other two calls with it. It is rtw_phy_cck_pd_set() in phy.c now, and the adaptive control and EDCA init are rtw_mac_init_* in mac.c, so RTW88_8723B no longer selects RTW88_88XXA. rtw88_8723b.ko depends on rtw88_core and rtw88_8723x only, the same two modules as rtw8723d and rtw8703b, and the patch is the first of the series.

Best regards,
Luka Gejak

author
#13 lore raw

PATCH v4 4/6 wifi: rtw88: 8723bs: add the RTL8723BS SDIO bind

In reply to Luka Gejak

Add the SDIO module for the RTL8723BS, the SDIO variant of the RTL8723B. It binds the Realtek SDIO device ids to the shared rtw88 SDIO probe and points it at the RTL8723B chip information.

The device id table carries the same six ids the staging rtl8723bs driver binds to this chip, so the staging driver can be dropped without losing hardware support. I only have the 0xb723 variant to test with; the other five are included for parity with staging, which has carried them for years, rather than being left behind.

The RTL8723B chip support is based on the initial work by Michael Straube <[email protected]>.

---
 .../net/wireless/realtek/rtw88/rtw8723bs.c    | 60 +++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723bs.c
drivers/net/wireless/realtek/rtw88/rtw8723bs.c new file +60 -0
new file mode 100644
index 000000000000..be2c13417a48
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright(c) 2007-2017  Realtek Corporation
+ * Copyright(c) Michael Straube <[email protected]>
+ * Copyright(c) 2024-2026 Luka Gejak <[email protected]>
+ */
+
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/module.h>
+#include "main.h"
+#include "rtw8723b.h"
+#include "sdio.h"
+
+static const struct sdio_device_id rtw_8723bs_id_table[] = {
+	{
+		SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK,
+			    SDIO_DEVICE_ID_REALTEK_RTW8723BS),
+		.driver_data = (kernel_ulong_t)&rtw8723b_hw_spec,
+	},
+	{
+		SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, 0x0523),
+		.driver_data = (kernel_ulong_t)&rtw8723b_hw_spec,
+	},
+	{
+		SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, 0x0525),
+		.driver_data = (kernel_ulong_t)&rtw8723b_hw_spec,
+	},
+	{
+		SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, 0x0623),
+		.driver_data = (kernel_ulong_t)&rtw8723b_hw_spec,
+	},
+	{
+		SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, 0x0626),
+		.driver_data = (kernel_ulong_t)&rtw8723b_hw_spec,
+	},
+	{
+		SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, 0x0627),
+		.driver_data = (kernel_ulong_t)&rtw8723b_hw_spec,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(sdio, rtw_8723bs_id_table);
+
+static struct sdio_driver rtw_8723bs_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = rtw_8723bs_id_table,
+	.probe = rtw_sdio_probe,
+	.remove = rtw_sdio_remove,
+	.shutdown = rtw_sdio_shutdown,
+	.drv = {
+		.pm = &rtw_sdio_pm_ops,
+	}
+};
+module_sdio_driver(rtw_8723bs_driver);
+
+MODULE_AUTHOR("Michael Straube <[email protected]>");
+MODULE_AUTHOR("Luka Gejak <[email protected]>");
+MODULE_DESCRIPTION("Realtek 802.11n wireless 8723bs driver");
+MODULE_LICENSE("Dual BSD/GPL");
Signature · 1 line
2.55.0
author
#14 lore raw

PATCH v4 5/6 wifi: rtw88: 8723bs: enable building the RTL8723BS driver

In reply to Luka Gejak

Now that the chip support, firmware interface, coexistence, power management and the SDIO transfer and association paths are all in place, add the Kconfig symbols and Makefile rules that build the RTL8723B chip module and the RTL8723BS SDIO front end.

---
 drivers/net/wireless/realtek/rtw88/Kconfig  | 18 ++++++++++++++++++
 drivers/net/wireless/realtek/rtw88/Makefile |  6 ++++++
 2 files changed, 24 insertions(+)
drivers/net/wireless/realtek/rtw88/Kconfig +18 -0
index 3736f290bd42..e1f40d6785e6 100644
@@ -36,6 +36,10 @@ config RTW88_8703B
 	tristate
 	select RTW88_8723X
 
+config RTW88_8723B
+	tristate
+	select RTW88_8723X
+
 config RTW88_8723D
 	tristate
 	select RTW88_8723X
@@ -159,6 +163,20 @@ config RTW88_8723CS
 
 	  If you choose to build a module, it'll be called rtw88_8723cs.
 
+config RTW88_8723BS
+	tristate "Realtek 8723BS SDIO wireless network adapter"
+	depends on MMC
+	select RTW88_CORE
+	select RTW88_SDIO
+	select RTW88_8723B
+	help
+	  Select this option to enable support for 8723BS chipset
+
+	  This module adds support for the 8723BS 802.11n SDIO
+	  wireless network adapter.
+
+	  If you choose to build a module, it'll be called rtw88_8723bs.
+
 config RTW88_8723DU
 	tristate "Realtek 8723DU USB wireless network adapter"
 	depends on USB
drivers/net/wireless/realtek/rtw88/Makefile +6 -0
index 0b3da05a2938..6889b3796449 100644
@@ -55,6 +55,12 @@ rtw88_8703b-objs		:= rtw8703b.o rtw8703b_tables.o
 obj-$(CONFIG_RTW88_8723CS)	+= rtw88_8723cs.o
 rtw88_8723cs-objs		:= rtw8723cs.o
 
+obj-$(CONFIG_RTW88_8723B)	+= rtw88_8723b.o
+rtw88_8723b-objs		:= rtw8723b.o rtw8723b_table.o
+
+obj-$(CONFIG_RTW88_8723BS)	+= rtw88_8723bs.o
+rtw88_8723bs-objs		:= rtw8723bs.o
+
 obj-$(CONFIG_RTW88_8723D)	+= rtw88_8723d.o
 rtw88_8723d-objs		:= rtw8723d.o rtw8723d_table.o
 
Signature · 1 line
2.55.0
author
#15 lore raw

PATCH v4 6/6 MAINTAINERS: add entry for the RTL8723B rtw88 driver

In reply to Luka Gejak

Add Luka Gejak as the maintainer for the RTL8723B/RTL8723BS and as designated reviewer for support in rtw88 subsystem.

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
index 0b42e898f4d8..7851cfcc9368 100644
@@ -23007,6 +23007,13 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/net/dsa/realtek.yaml
 F:	drivers/net/dsa/realtek/*
 
+REALTEK RTL8723B WIRELESS DRIVER (rtw88)
+M:	Luka Gejak <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	drivers/net/wireless/realtek/rtw88/rtw8723b*.c
+F:	drivers/net/wireless/realtek/rtw88/rtw8723b*.h
+
 REALTEK SPI-NAND
 M:	Chris Packham <[email protected]>
 S:	Maintained
@@ -23027,6 +23034,7 @@ F:	drivers/net/wireless/realtek/rtlwifi/
 
 REALTEK WIRELESS DRIVER (rtw88)
 M:	Ping-Ke Shih <[email protected]>
+R:	Luka Gejak <[email protected]>
 L:	[email protected]
 S:	Maintained
 T:	git https://github.com/pkshih/rtw.git
Signature · 1 line
2.55.0
maintainer
#16 lore raw

RE: [PATCH v4 6/6] MAINTAINERS: add entry for the RTL8723B rtw88 driver

Quoted 13 lines · Luka Gejak

Add Luka Gejak as the maintainer for the RTL8723B/RTL8723BS and as designated reviewer for support in rtw88 subsystem.

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +0 -0
index 0b42e898f4d8..7851cfcc9368 100644

MAINTAINERS S: Maintained · line 23016

Quoted 10 lines
MAINTAINERS +7 -0
@@ -23007,6 +23007,13 @@ S:     Maintained
 F:     Documentation/devicetree/bindings/net/dsa/realtek.yaml
 F:     drivers/net/dsa/realtek/*
 
+REALTEK RTL8723B WIRELESS DRIVER (rtw88)
+M:     Luka Gejak <[email protected]>
+L:     [email protected]
+S:     Maintained
+F:     drivers/net/wireless/realtek/rtw88/rtw8723b*.c
+F:     drivers/net/wireless/realtek/rtw88/rtw8723b*.h
+

I think no need this entry.

Just with below 'R', the ./scripts/get_maintainer.pl with rtw88 patch can output your name, no?

Quoted 12 lines
MAINTAINERS +1 -1
 REALTEK SPI-NAND
 M:     Chris Packham <[email protected]>
 S:     Maintained
@@ -23027,6 +23034,7 @@ F:      drivers/net/wireless/realtek/rtlwifi/
 
 REALTEK WIRELESS DRIVER (rtw88)
 M:     Ping-Ke Shih <[email protected]>
+R:     Luka Gejak <[email protected]>
 L:     [email protected]
 S:     Maintained
 T:     git https://github.com/pkshih/rtw.git
--
Quoted 1 line

2.55.0

By the way, your patch should tag rtw-next (i.e. [PATCH v5 rtw-next]). Otherwise, NIPA test your patches on wireless-next tree, reporting errors [1].

I don't know if someone is reading your patches. I'd skip v4. Please wait a while and send v5.

[1] https://patchwork.kernel.org/user/todo/linux-wireless/?series=1172592

author
#17 lore raw

Re: [PATCH v4 6/6] MAINTAINERS: add entry for the RTL8723B rtw88 driver

Quoted 1 line · Ping-Ke Shih

Luka Gejak <[email protected]> wrote:

[...]

Quoted 22 lines

MAINTAINERS S: Maintained · line 23016

Quoted 7 lines
MAINTAINERS +7 -0
+REALTEK RTL8723B WIRELESS DRIVER (rtw88)
+M:     Luka Gejak <[email protected]>
+L:     [email protected]
+S:     Maintained
+F:     drivers/net/wireless/realtek/rtw88/rtw8723b*.c
+F:     drivers/net/wireless/realtek/rtw88/rtw8723b*.h
+

I think no need this entry.

Just with below 'R', the ./scripts/get_maintainer.pl with rtw88 patch can output your name, no?

Quoted 8 lines

REALTEK WIRELESS DRIVER (rtw88)

 M:     Ping-Ke Shih <[email protected]>
+R:     Luka Gejak <[email protected]>
 L:     [email protected]
 S:     Maintained
 T:     git https://github.com/pkshih/rtw.git

--
2.55.0

Quoted 8 lines

By the way, your patch should tag rtw-next (i.e. [PATCH v5 rtw-next]). Otherwise, NIPA test your patches on wireless-next tree, reporting errors [1].

I don't know if someone is reading your patches. I'd skip v4. Please wait a while and send v5.

[1] https://patchwork.kernel.org/user/todo/linux-wireless/?series=1172592

Thank you. I will hold and send v5 tagged [PATCH v5 rtw-next], and I am reading the list in the meantime.

On the entry: it is a maintainer entry for one driver, which is not the same thing as the reviewer line. The file's legend:

M: *Mail* patches to: FullName <address@domain>
R: Designated *Reviewer*: FullName <address@domain>
These reviewers should be CCed on patches.

The names it reports them under are "maintainer:" and "reviewer:":
get_maintainer gives the M: role the first and pushes R: entries as the second. With R: alone the entry says my name should be CCed on RTL8723B patches, which is correct, and I have kept that line, but it does not say that anyone is expected to look after the chip.

Documentation/maintainer/feature-and-driver-maintainers.rst covers exactly this case, a maintainer for one driver inside a larger subsystem:

- "The term maintainer spans a very wide range of levels of engagement ... to people responsible for a small feature or a driver";
- "Drivers and alike most often do not have their own mailing lists and git trees but instead send and review patches on the list of a larger subsystem" - which is why the entry deliberately has no T:
line and patches keep going through rtw-next;
- "Maintainers must review *all* patches touching exclusively their drivers, no matter how trivial" and "an Acked-by or Reviewed-by tag ... from a single maintainer is enough to satisfy this requirement" - an Acked-by on the list for 8723b patches is the role I am asking for, not a tree of my own;
- "Most natural and common choice of a maintainer is the author of the code", and the file "is not a list of credits ... it is a list of those who will actively help with the code".

I know that least one person(with my help) is working on an RTL8723BE (PCIe) build and I expect an RTL8723BU (USB) to be added eventually. rtw8723b*.c and rtw8723b*.h already cover all three variants, so the family has one named owner as it grows, and those patches do not all have to reach you first.

It is not a claim on rtw88. The patterns cover rtw8723b*.c and rtw8723b*.h only. Nothing else in the directory changes: get_maintainer on reg.h returns you as maintainer and me as reviewer whether the section is present or not, and the R: line you suggested is what puts me on those patches.

The nesting is not new: the ath family keeps separate entries for ath5k, ath9k, ath10k and carl9170 inside ATHEROS ATH GENERIC UTILITIES, whose
F: is drivers/net/wireless/ath/* and whose M: is Jeff Johnson, and ath5k and carl9170 carry M: with no T: line, as this one would.

This is about the one chip I have the hardware for, not a proposal to split the other rtw88 chips out of your entry.

checkpatch --strict on the MAINTAINERS patch is 0 errors, 0 warnings.

If you would still rather keep it to the R: line, say so and the section goes, it is one patch, and it should not hold up the series, but please take these points into account.

Best regards,
Luka Gejak

maintainer
#18 lore raw

RE: [PATCH v4 6/6] MAINTAINERS: add entry for the RTL8723B rtw88 driver

Quoted 83 lines · Luka Gejak
Quoted 1 line · Ping-Ke Shih

Luka Gejak <[email protected]> wrote:

[...]

Quoted 22 lines

MAINTAINERS S: Maintained · line 23016

Quoted 7 lines
MAINTAINERS +7 -0
+REALTEK RTL8723B WIRELESS DRIVER (rtw88)
+M:     Luka Gejak <[email protected]>
+L:     [email protected]
+S:     Maintained
+F:     drivers/net/wireless/realtek/rtw88/rtw8723b*.c
+F:     drivers/net/wireless/realtek/rtw88/rtw8723b*.h
+

I think no need this entry.

Just with below 'R', the ./scripts/get_maintainer.pl with rtw88 patch can output your name, no?

Quoted 8 lines

REALTEK WIRELESS DRIVER (rtw88)

 M:     Ping-Ke Shih <[email protected]>
+R:     Luka Gejak <[email protected]>
 L:     [email protected]
 S:     Maintained
 T:     git https://github.com/pkshih/rtw.git

--
2.55.0

Quoted 8 lines

By the way, your patch should tag rtw-next (i.e. [PATCH v5 rtw-next]). Otherwise, NIPA test your patches on wireless-next tree, reporting errors [1].

I don't know if someone is reading your patches. I'd skip v4. Please wait a while and send v5.

[1] https://patchwork.kernel.org/user/todo/linux-wireless/?series=1172592

Thank you. I will hold and send v5 tagged [PATCH v5 rtw-next], and I am reading the list in the meantime.

On the entry: it is a maintainer entry for one driver, which is not the same thing as the reviewer line. The file's legend:

M: *Mail* patches to: FullName <address@domain>
R: Designated *Reviewer*: FullName <address@domain>
These reviewers should be CCed on patches.

The names it reports them under are "maintainer:" and "reviewer:":
get_maintainer gives the M: role the first and pushes R: entries as the second. With R: alone the entry says my name should be CCed on RTL8723B patches, which is correct, and I have kept that line, but it does not say that anyone is expected to look after the chip.

Documentation/maintainer/feature-and-driver-maintainers.rst covers exactly this case, a maintainer for one driver inside a larger subsystem:

- "The term maintainer spans a very wide range of levels of engagement ... to people responsible for a small feature or a driver";
- "Drivers and alike most often do not have their own mailing lists and git trees but instead send and review patches on the list of a larger subsystem" - which is why the entry deliberately has no T:
line and patches keep going through rtw-next;
- "Maintainers must review *all* patches touching exclusively their drivers, no matter how trivial" and "an Acked-by or Reviewed-by tag ... from a single maintainer is enough to satisfy this requirement" - an Acked-by on the list for 8723b patches is the role I am asking for, not a tree of my own;
- "Most natural and common choice of a maintainer is the author of the code", and the file "is not a list of credits ... it is a list of those who will actively help with the code".

I know that least one person(with my help) is working on an RTL8723BE (PCIe) build and I expect an RTL8723BU (USB) to be added eventually. rtw8723b*.c and rtw8723b*.h already cover all three variants, so the family has one named owner as it grows, and those patches do not all have to reach you first.

It is not a claim on rtw88. The patterns cover rtw8723b*.c and rtw8723b*.h only. Nothing else in the directory changes: get_maintainer on reg.h returns you as maintainer and me as reviewer whether the section is present or not, and the R: line you suggested is what puts me on those patches.

Quoted 4 lines

The nesting is not new: the ath family keeps separate entries for ath5k, ath9k, ath10k and carl9170 inside ATHEROS ATH GENERIC UTILITIES, whose
F: is drivers/net/wireless/ath/* and whose M: is Jeff Johnson, and ath5k and carl9170 carry M: with no T: line, as this one would.

Is this a good example to your case? Jeff is the maintainer of all ath drivers.

Quoted 9 lines

This is about the one chip I have the hardware for, not a proposal to split the other rtw88 chips out of your entry.

checkpatch --strict on the MAINTAINERS patch is 0 errors, 0 warnings.

If you would still rather keep it to the R: line, say so and the section goes, it is one patch, and it should not hold up the series, but please take these points into account.

As I mentioned earlier, I suggested to subscribe mailing list and review rtw88 patches (I read your limitation, but I think it is easier to find an alternative way, no?).

As you replied [1], I suppose you have subscribed the list, right?

I still think 'R' is enough. With your subscription of mailing list, I even think we can drop this patch, no need to update MAINTAINERS.

Bitterblue Smith you Cc'd is a main contributor of WiFi drivers (not limit to Realtek). I don't see any difficult to him to review and contribute patches. (If I miss something, please correct me, Bitterblue). Anyway, I want to see people really contribute community and then add their names, not reverse way. I don't like to clean MAINTAINERS sometime.

[1] https://lore.kernel.org/linux-wireless/[email protected]/#t

Ping-Ke

author
#19 lore raw

Re: [PATCH v4 6/6] MAINTAINERS: add entry for the RTL8723B rtw88 driver

Quoted 87 lines · Ping-Ke Shih
Quoted 84 lines · Luka Gejak

On Thu Sep 24, 2026 at 3:24 AM CEST, Ping-Ke Shih wrote:
Luka Gejak <[email protected]> wrote:

[...]

+REALTEK RTL8723B WIRELESS DRIVER (rtw88) +M: Luka Gejak <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/net/wireless/realtek/rtw88/rtw8723b*.c +F: drivers/net/wireless/realtek/rtw88/rtw8723b*.h +

I think no need this entry.

Just with below 'R', the ./scripts/get_maintainer.pl with rtw88 patch can output your name, no?

REALTEK WIRELESS DRIVER (rtw88)
M: Ping-Ke Shih <[email protected]>
+R: Luka Gejak <[email protected]>
L: [email protected]
S: Maintained
T: git https://github.com/pkshih/rtw.git
--
2.55.0

By the way, your patch should tag rtw-next (i.e. [PATCH v5 rtw-next]). Otherwise, NIPA test your patches on wireless-next tree, reporting errors [1].

I don't know if someone is reading your patches. I'd skip v4. Please wait a while and send v5.

[1] https://patchwork.kernel.org/user/todo/linux-wireless/?series=1172592

Thank you. I will hold and send v5 tagged [PATCH v5 rtw-next], and I am reading the list in the meantime.

On the entry: it is a maintainer entry for one driver, which is not the same thing as the reviewer line. The file's legend:

M: *Mail* patches to: FullName <address@domain>
R: Designated *Reviewer*: FullName <address@domain> These reviewers should be CCed on patches.

The names it reports them under are "maintainer:" and "reviewer:":
get_maintainer gives the M: role the first and pushes R: entries as the second. With R: alone the entry says my name should be CCed on RTL8723B patches, which is correct, and I have kept that line, but it does not say that anyone is expected to look after the chip.

Documentation/maintainer/feature-and-driver-maintainers.rst covers exactly this case, a maintainer for one driver inside a larger subsystem:

- "The term maintainer spans a very wide range of levels of engagement ... to people responsible for a small feature or a driver";
- "Drivers and alike most often do not have their own mailing lists and git trees but instead send and review patches on the list of a larger subsystem" - which is why the entry deliberately has no T:
line and patches keep going through rtw-next;
- "Maintainers must review *all* patches touching exclusively their drivers, no matter how trivial" and "an Acked-by or Reviewed-by tag ... from a single maintainer is enough to satisfy this requirement" - an Acked-by on the list for 8723b patches is the role I am asking for, not a tree of my own;
- "Most natural and common choice of a maintainer is the author of the code", and the file "is not a list of credits ... it is a list of those who will actively help with the code".

I know that least one person(with my help) is working on an RTL8723BE (PCIe) build and I expect an RTL8723BU (USB) to be added eventually. rtw8723b*.c and rtw8723b*.h already cover all three variants, so the family has one named owner as it grows, and those patches do not all have to reach you first.

It is not a claim on rtw88. The patterns cover rtw8723b*.c and rtw8723b*.h only. Nothing else in the directory changes: get_maintainer on reg.h returns you as maintainer and me as reviewer whether the section is present or not, and the R: line you suggested is what puts me on those patches.

Quoted 6 lines
Quoted 5 lines

The nesting is not new: the ath family keeps separate entries for ath5k, ath9k, ath10k and carl9170 inside ATHEROS ATH GENERIC UTILITIES, whose
F: is drivers/net/wireless/ath/* and whose M: is Jeff Johnson, and ath5k and carl9170 carry M: with no T: line, as this one would.

Is this a good example to your case? Jeff is the maintainer of all ath drivers.

I meant him as an example of nested maintainer, I didn't focus on the size of code he is maintaining. But I will drop M as you requested.

Quoted 11 lines
Quoted 11 lines

This is about the one chip I have the hardware for, not a proposal to
split the other rtw88 chips out of your entry.

checkpatch --strict on the MAINTAINERS patch is 0 errors, 0 warnings.

If you would still rather keep it to the R: line, say so and the section goes, it is one patch, and it should not hold up the series, but please take these points into account.

Quoted 5 lines

As I mentioned earlier, I suggested to subscribe mailing list and review rtw88 patches (I read your limitation, but I think it is easier to find an alternative way, no?).

As you replied [1], I suppose you have subscribed the list, right?

Yes, I have setup lei(just need a little bit of configuring).

Quoted 2 lines

I still think 'R' is enough. With your subscription of mailing list,

I understand and respect your decision.

Quoted 2 lines

I even think we can drop this patch, no need to update MAINTAINERS.

I little confused, as this conflicts past emails and this above.

Quoted 11 lines

Bitterblue Smith you Cc'd is a main contributor of WiFi drivers (not limit to Realtek). I don't see any difficult to him to review and contribute patches. (If I miss something, please correct me, Bitterblue). Anyway, I want to see people really contribute community and then add their names, not reverse way. I don't like to clean MAINTAINERS sometime.

[1] https://lore.kernel.org/linux-wireless/[email protected]/#t

Ping-Ke

Best regards,
Luka Gejak

author
#20 lore raw

Re: [PATCH v4 6/6] MAINTAINERS: add entry for the RTL8723B rtw88 driver

Quoted 120 lines · Luka Gejak
Quoted 87 lines · Ping-Ke Shih

Luka Gejak <[email protected]> wrote:

On Thu Sep 24, 2026 at 3:24 AM CEST, Ping-Ke Shih wrote:
Luka Gejak <[email protected]> wrote:

[...]

+REALTEK RTL8723B WIRELESS DRIVER (rtw88) +M: Luka Gejak <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/net/wireless/realtek/rtw88/rtw8723b*.c +F: drivers/net/wireless/realtek/rtw88/rtw8723b*.h +

I think no need this entry.

Just with below 'R', the ./scripts/get_maintainer.pl with rtw88 patch can output your name, no?

REALTEK WIRELESS DRIVER (rtw88)
M: Ping-Ke Shih <[email protected]>
+R: Luka Gejak <[email protected]>
L: [email protected]
S: Maintained
T: git https://github.com/pkshih/rtw.git
--
2.55.0

By the way, your patch should tag rtw-next (i.e. [PATCH v5 rtw-next]). Otherwise, NIPA test your patches on wireless-next tree, reporting errors [1].

I don't know if someone is reading your patches. I'd skip v4. Please wait a while and send v5.

[1] https://patchwork.kernel.org/user/todo/linux-wireless/?series=1172592

Thank you. I will hold and send v5 tagged [PATCH v5 rtw-next], and I am reading the list in the meantime.

On the entry: it is a maintainer entry for one driver, which is not the same thing as the reviewer line. The file's legend:

M: *Mail* patches to: FullName <address@domain>
R: Designated *Reviewer*: FullName <address@domain> These reviewers should be CCed on patches.

The names it reports them under are "maintainer:" and "reviewer:":
get_maintainer gives the M: role the first and pushes R: entries as the second. With R: alone the entry says my name should be CCed on RTL8723B patches, which is correct, and I have kept that line, but it does not say that anyone is expected to look after the chip.

Documentation/maintainer/feature-and-driver-maintainers.rst covers exactly this case, a maintainer for one driver inside a larger subsystem:

- "The term maintainer spans a very wide range of levels of engagement ... to people responsible for a small feature or a driver";
- "Drivers and alike most often do not have their own mailing lists and git trees but instead send and review patches on the list of a larger subsystem" - which is why the entry deliberately has no T:
line and patches keep going through rtw-next;
- "Maintainers must review *all* patches touching exclusively their drivers, no matter how trivial" and "an Acked-by or Reviewed-by tag ... from a single maintainer is enough to satisfy this requirement" - an Acked-by on the list for 8723b patches is the role I am asking for, not a tree of my own;
- "Most natural and common choice of a maintainer is the author of the code", and the file "is not a list of credits ... it is a list of those who will actively help with the code".

I know that least one person(with my help) is working on an RTL8723BE (PCIe) build and I expect an RTL8723BU (USB) to be added eventually. rtw8723b*.c and rtw8723b*.h already cover all three variants, so the family has one named owner as it grows, and those patches do not all have to reach you first.

It is not a claim on rtw88. The patterns cover rtw8723b*.c and rtw8723b*.h only. Nothing else in the directory changes: get_maintainer on reg.h returns you as maintainer and me as reviewer whether the section is present or not, and the R: line you suggested is what puts me on those patches.

Quoted 7 lines

The nesting is not new: the ath family keeps separate entries for ath5k, ath9k, ath10k and carl9170 inside ATHEROS ATH GENERIC UTILITIES, whose
F: is drivers/net/wireless/ath/* and whose M: is Jeff Johnson, and ath5k and carl9170 carry M: with no T: line, as this one would.

Is this a good example to your case? Jeff is the maintainer of all ath drivers.

I meant him as an example of nested maintainer, I didn't focus on the size of code he is maintaining. But I will drop M as you requested.

Quoted 10 lines

This is about the one chip I have the hardware for, not a proposal to
split the other rtw88 chips out of your entry.

checkpatch --strict on the MAINTAINERS patch is 0 errors, 0 warnings.

If you would still rather keep it to the R: line, say so and the section goes, it is one patch, and it should not hold up the series, but please take these points into account.

Quoted 6 lines

As I mentioned earlier, I suggested to subscribe mailing list and review rtw88 patches (I read your limitation, but I think it is easier to find an alternative way, no?).

As you replied [1], I suppose you have subscribed the list, right?

Yes, I have setup lei(just need a little bit of configuring).

Quoted 1 line
Quoted 8 lines
Quoted 2 lines

I still think 'R' is enough. With your subscription of mailing list,

I understand and respect your decision.

Quoted 3 lines

I even think we can drop this patch, no need to update MAINTAINERS.

I little confused, as this conflicts past emails and this above.

I *am little...

So how would you like to proceed?

Quoted 16 lines
Quoted 9 lines

Bitterblue Smith you Cc'd is a main contributor of WiFi drivers (not
limit to Realtek). I don't see any difficult to him to review and contribute patches. (If I miss something, please correct me, Bitterblue). Anyway, I want to see people really contribute community and then add their names, not reverse way. I don't like to clean MAINTAINERS sometime.

Quoted 4 lines

[1] https://lore.kernel.org/linux-wireless/[email protected]/#t

Ping-Ke

Best regards,
Luka Gejak

maintainer
#21 lore raw

RE: [PATCH v4 6/6] MAINTAINERS: add entry for the RTL8723B rtw88 driver

Quoted 7 lines
Quoted 3 lines
Quoted 2 lines

I even think we can drop this patch, no need to update MAINTAINERS.

I little confused, as this conflicts past emails and this above.

I *am little...

So how would you like to proceed?

You are the first people who asks me to add MAINTAINERS, and I have not thought about that before.

After thinking this weekend, I think the basic requirements are:

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

Anyway, I want to see people really contribute community and then add their names, not reverse way. I don't like to clean MAINTAINERS sometime.

So, if you ask my personal opinion now, I'd say let's drop this patch.

As you subscribed mailing list, give suggestions and reviewed-by to the patches after reviewing.

Ping-Ke

author
#22 lore raw

Re: [PATCH v4 6/6] MAINTAINERS: add entry for the RTL8723B rtw88 driver

Quoted 20 lines · Ping-Ke Shih
Quoted 10 lines
Quoted 4 lines

I even think we can drop this patch, no need to update MAINTAINERS.

I little confused, as this conflicts past emails and this above.

I *am little...

So how would you like to proceed?

You are the first people who asks me to add MAINTAINERS, and I have not thought about that before.

After thinking this weekend, I think the basic requirements are:

Quoted 5 lines
Quoted 4 lines

Anyway, I want to see people really contribute community and then add their names, not reverse way. I don't like to clean MAINTAINERS sometime.

Quoted 7 lines

So, if you ask my personal opinion now, I'd say let's drop this patch.

As you subscribed mailing list, give suggestions and reviewed-by to the patches after reviewing.

Ping-Ke

Understandable, will do in v5.

Best regards,
Luka Gejak

#23 lore raw

Re: [PATCH v4 6/6] MAINTAINERS: add entry for the RTL8723B rtw88 driver

In reply to Ping-Ke Shih
Quoted 48 lines · Ping-Ke Shih
Quoted 13 lines · Luka Gejak

Add Luka Gejak as the maintainer for the RTL8723B/RTL8723BS and as designated reviewer for support in rtw88 subsystem.

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +0 -0
index 0b42e898f4d8..7851cfcc9368 100644

MAINTAINERS S: Maintained · line 23016

Quoted 10 lines
MAINTAINERS +7 -0
@@ -23007,6 +23007,13 @@ S:     Maintained
 F:     Documentation/devicetree/bindings/net/dsa/realtek.yaml
 F:     drivers/net/dsa/realtek/*
 
+REALTEK RTL8723B WIRELESS DRIVER (rtw88)
+M:     Luka Gejak <[email protected]>
+L:     [email protected]
+S:     Maintained
+F:     drivers/net/wireless/realtek/rtw88/rtw8723b*.c
+F:     drivers/net/wireless/realtek/rtw88/rtw8723b*.h
+

I think no need this entry.

Just with below 'R', the ./scripts/get_maintainer.pl with rtw88 patch can output your name, no?

Quoted 12 lines
MAINTAINERS +1 -1
 REALTEK SPI-NAND
 M:     Chris Packham <[email protected]>
 S:     Maintained
@@ -23027,6 +23034,7 @@ F:      drivers/net/wireless/realtek/rtlwifi/
 
 REALTEK WIRELESS DRIVER (rtw88)
 M:     Ping-Ke Shih <[email protected]>
+R:     Luka Gejak <[email protected]>
 L:     [email protected]
 S:     Maintained
 T:     git https://github.com/pkshih/rtw.git
--
Quoted 1 line

2.55.0

By the way, your patch should tag rtw-next (i.e. [PATCH v5 rtw-next]). Otherwise, NIPA test your patches on wireless-next tree, reporting errors [1].

Quoted 2 lines

I don't know if someone is reading your patches. I'd skip v4. Please wait a while and send v5.

I'm reading it now. I should have some comments today or tomorrow.

Quoted 2 lines

[1] https://patchwork.kernel.org/user/todo/linux-wireless/?series=1172592