* [PATCH ath-current v2 8/8] wifi: ath12k: resolve PENDING ML peer ID from MLO_PEER_MAP HTT event
From: Baochen Qiang @ 2026-07-20 6:43 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260720-ath12k-fw-allocated-ml-peer-id-v2-0-630632758a80@oss.qualcomm.com>
Add ath12k_dp_peer_fixup_peer_id() and call it from the
HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP handler. For devices where the
firmware allocates the MLD peer ID, this is the point at which
all data structures that were left with ATH12K_MLO_PEER_ID_PENDING
or ATH12K_MLO_PEER_ID_INVALID get their real ID:
- dp_peer->peer_id is updated and the dp_peer is published into
dp_hw->dp_peers[];
- every existing dp_link_peer in dp_peer->link_peers[] gets its
ml_id set to the same value;
- ahsta->ml_peer_id is updated to the same value so peer_assoc,
sta_state and cleanup paths see a consistent ID.
Devices with host_alloc_ml_id == true also receive the same HTT
event, but the firmware-reported ID always matches the
host-allocated one and everything has already been populated by
ath12k_dp_peer_create(); Skips the helper entirely on those devices.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221039
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/core.c | 2 ++
drivers/net/wireless/ath/ath12k/core.h | 1 +
drivers/net/wireless/ath/ath12k/dp_htt.c | 19 +++++++++++
drivers/net/wireless/ath/ath12k/dp_peer.c | 52 +++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/dp_peer.h | 2 ++
drivers/net/wireless/ath/ath12k/mac.c | 24 ++++++++++++++
6 files changed, 100 insertions(+)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 0e7c732f8222..cf9c5b068fa6 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1547,6 +1547,8 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
}
wiphy_unlock(ah->hw->wiphy);
+
+ complete(&ah->peer_ml_id_done);
}
wake_up(&ab->wmi_ab.tx_credits_wq);
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 8769b41f5db5..30726e580833 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -795,6 +795,7 @@ struct ath12k_hw {
bool regd_updated;
bool use_6ghz_regd;
bool host_alloc_ml_id;
+ struct completion peer_ml_id_done;
u8 num_radio;
diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c
index 150b190f9c7f..68968f96b4f1 100644
--- a/drivers/net/wireless/ath/ath12k/dp_htt.c
+++ b/drivers/net/wireless/ath/ath12k/dp_htt.c
@@ -6,6 +6,7 @@
#include "core.h"
#include "peer.h"
+#include "dp_peer.h"
#include "htc.h"
#include "dp_htt.h"
#include "debugfs_htt_stats.h"
@@ -582,6 +583,7 @@ static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab,
struct htt_t2h_mlo_peer_map_event *ev = &resp->mlo_peer_map_ev;
u16 raw_peer_id, peer_id, addr_h16;
u8 peer_addr[ETH_ALEN];
+ int ret;
if (skb->len < sizeof(*ev)) {
ath12k_warn(ab, "unexpected htt mlo peer map event len %u\n",
@@ -600,6 +602,23 @@ static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab,
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt mlo peer map peer %pM id %u\n",
peer_addr, peer_id);
+
+ /*
+ * Fix up the dp_peer entry created with ATH12K_MLO_PEER_ID_PENDING
+ * earlier; on chips with host_alloc_ml_id == false this is the only
+ * point at which the host learns the firmware-assigned ID. Chips
+ * that allocate the ID on the host also receive this event but the
+ * firmware-reported ID matches the host-allocated one, so there is
+ * nothing to fix up.
+ */
+ if (!ab->hw_params->host_alloc_ml_id) {
+ ret = ath12k_dp_peer_fixup_peer_id(ab, peer_addr,
+ peer_id);
+ if (ret)
+ ath12k_warn(ab,
+ "failed to fix up peer id %u for dp peer %pM: %d\n",
+ peer_id, peer_addr, ret);
+ }
}
void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c
index 2a2eae194007..09142dcb74f9 100644
--- a/drivers/net/wireless/ath/ath12k/dp_peer.c
+++ b/drivers/net/wireless/ath/ath12k/dp_peer.c
@@ -695,3 +695,55 @@ void ath12k_dp_link_peer_reset_rx_stats(struct ath12k_dp *dp, const u8 *addr)
if (rx_stats)
memset(rx_stats, 0, sizeof(*rx_stats));
}
+
+int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab,
+ const u8 *peer_addr, u16 peer_id)
+{
+ struct ath12k_dp_link_peer *link_peer;
+ struct ath12k_dp_peer *dp_peer = NULL;
+ struct ath12k_hw_group *ag = ab->ag;
+ struct ath12k_dp_hw *dp_hw = NULL;
+ struct ath12k_hw *ah;
+ int i;
+
+ if (peer_id >= (ATH12K_PEER_ML_ID_VALID | ATH12K_MAX_MLO_PEERS))
+ return -EINVAL;
+
+ for (i = 0; i < ag->num_hw; i++) {
+ ah = ag->ah[i];
+ if (!ah)
+ continue;
+
+ spin_lock_bh(&ah->dp_hw.peer_lock);
+ dp_peer = ath12k_dp_peer_find_by_addr(&ah->dp_hw,
+ (u8 *)peer_addr);
+ if (dp_peer) {
+ dp_hw = &ah->dp_hw;
+ break;
+ }
+ spin_unlock_bh(&ah->dp_hw.peer_lock);
+ }
+
+ if (!dp_peer)
+ return -ENOENT;
+
+ /* dp_hw->peer_lock is held */
+
+ dp_peer->peer_id = peer_id;
+ rcu_assign_pointer(dp_hw->dp_peers[peer_id], dp_peer);
+
+ for (i = 0; i < ATH12K_NUM_MAX_LINKS; i++) {
+ link_peer = rcu_dereference_protected(dp_peer->link_peers[i],
+ lockdep_is_held(&dp_hw->peer_lock));
+ if (link_peer)
+ link_peer->ml_id = peer_id;
+ }
+
+ ath12k_sta_to_ahsta(dp_peer->sta)->ml_peer_id = peer_id;
+
+ spin_unlock_bh(&dp_hw->peer_lock);
+
+ complete(&ah->peer_ml_id_done);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h
index f5067e66f1e1..9842671b5475 100644
--- a/drivers/net/wireless/ath/ath12k/dp_peer.h
+++ b/drivers/net/wireless/ath/ath12k/dp_peer.h
@@ -180,4 +180,6 @@ struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_p
struct ath12k_dp_link_peer *
ath12k_dp_link_peer_find_by_peerid(struct ath12k_pdev_dp *dp_pdev, u16 peer_id);
void ath12k_dp_link_peer_free(struct ath12k_dp_link_peer *peer);
+int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab, const u8 *peer_addr,
+ u16 peer_id);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 533109f9d5bd..e4c240f168e3 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -3855,9 +3855,11 @@ static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar,
static int ath12k_mac_peer_assoc(struct ath12k *ar,
struct ath12k_wmi_peer_assoc_arg *peer_arg)
{
+ struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
int ret;
reinit_completion(&ar->peer_assoc_done);
+ reinit_completion(&ah->peer_ml_id_done);
ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
if (ret) {
@@ -3872,6 +3874,27 @@ static int ath12k_mac_peer_assoc(struct ath12k *ar,
return -ETIMEDOUT;
}
+ /*
+ * For devices where the firmware allocates the MLD peer ID, the host
+ * learns the real ID only from the MLO_RX_PEER_MAP HTT event, which is
+ * handled in a softirq (BH workqueue) context that cannot take the
+ * wiphy lock. Block here, while still holding the wiphy lock, until
+ * that event has fixed up the ID. This serialises the fixup against
+ * all other wiphy-locked ml_peer_id accesses.
+ *
+ * The firmware sends the event only once, in response to the assoc-link
+ * peer assoc, so block only for that link.
+ */
+ if (!ah->host_alloc_ml_id &&
+ peer_arg->is_assoc &&
+ peer_arg->ml.enabled &&
+ peer_arg->ml.assoc_link &&
+ !wait_for_completion_timeout(&ah->peer_ml_id_done, 1 * HZ)) {
+ ath12k_warn(ar->ab, "failed to get MLO peer map event for %pM vdev %i\n",
+ peer_arg->peer_mac, peer_arg->vdev_id);
+ return -ETIMEDOUT;
+ }
+
return 0;
}
@@ -15332,6 +15355,7 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag,
ah->num_radio = num_pdev_map;
mutex_init(&ah->hw_mutex);
+ init_completion(&ah->peer_ml_id_done);
spin_lock_init(&ah->dp_hw.peer_lock);
INIT_LIST_HEAD(&ah->dp_hw.dp_peers_list);
--
2.25.1
^ permalink raw reply related
* [PATCH ath-current v2 7/8] wifi: ath12k: defer dp_peer registration when firmware allocates MLD peer ID
From: Baochen Qiang @ 2026-07-20 6:43 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260720-ath12k-fw-allocated-ml-peer-id-v2-0-630632758a80@oss.qualcomm.com>
For chips with host_alloc_ml_id=true (QCN9274 etc.), the host allocates
the MLD peer ID up front; ath12k_dp_peer_create() publishes the dp_peer
into dp_hw->dp_peers[] using that ID immediately. WCN7850/QCC2072 does
not work that way: the firmware picks the ID and only tells the host
afterwards via HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP, so the publication has
to be delayed until the event arrives.
Introduce ATH12K_MLO_PEER_ID_PENDING (0xFFFE) as a sentinel for "is_mlo,
but ID not yet known". On the firmware-allocates path:
- ath12k_mac_op_sta_state(NOTEXIST->NONE) skips ath12k_peer_ml_alloc()
and stores PENDING in ahsta->ml_peer_id and dp_params.peer_id;
- ath12k_dp_peer_create() skips dp_peer registration until a real ID is
known;
- ath12k_peer_create() leaves peer->ml_id at INVALID so consumer sites
do not treat PENDING as a real ID;
- ath12k_peer_ml_free() and ath12k_mac_dp_peer_cleanup() skip the
dp_peers[] write and the free_ml_peer_id_map clear when
host_alloc_ml_id is false or the ID is still PENDING.
The HTT handler change that resolves the PENDING ID is added in a
follow-up patch.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/core.h | 1 +
drivers/net/wireless/ath/ath12k/dp_peer.c | 23 +++++++++++++++--------
drivers/net/wireless/ath/ath12k/mac.c | 22 ++++++++++++++++------
drivers/net/wireless/ath/ath12k/peer.c | 20 +++++++++++++++++---
4 files changed, 49 insertions(+), 17 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 1f56474efbea..8769b41f5db5 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -72,6 +72,7 @@
#define ATH12K_MAX_MLO_PEERS 256
#define ATH12K_MLO_PEER_ID_INVALID 0xFFFF
+#define ATH12K_MLO_PEER_ID_PENDING 0xFFFE
#define ATH12K_INVALID_RSSI_FULL -1
#define ATH12K_INVALID_RSSI_EMPTY -128
diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c
index 47d009a0d61f..2a2eae194007 100644
--- a/drivers/net/wireless/ath/ath12k/dp_peer.c
+++ b/drivers/net/wireless/ath/ath12k/dp_peer.c
@@ -472,7 +472,9 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr,
dp_peer->is_mlo = params->is_mlo;
/*
- * For MLO client, the host assigns the ML peer ID, so set peer_id in dp_peer
+ * For MLO client, the ML peer ID, either known or PENDING, needs to be
+ * initialized here since the following logic depends on it.
+ *
* For non-MLO client, host gets link peer ID from firmware and will be
* assigned at the time of link peer creation
*/
@@ -488,13 +490,17 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr,
list_add(&dp_peer->list, &dp_hw->dp_peers_list);
/*
- * For MLO client, the peer_id for ath12k_dp_peer is allocated by host
- * and that peer_id is known at this point, and hence this ath12k_dp_peer
- * can be added to the RCU table using the peer_id.
- * For non-MLO client, this addition to RCU table shall be done at the
- * time of assignment of ath12k_dp_link_peer to ath12k_dp_peer.
+ * For an MLO client whose ML peer ID is allocated by the host, the
+ * peer_id is known here and the dp_peer can be added to the RCU
+ * table using it. For an MLO client on chips where the firmware
+ * allocates the ID, peer_id is ATH12K_MLO_PEER_ID_PENDING and the
+ * RCU table publish is deferred to the
+ * HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP handler. For a non-MLO client
+ * the publish happens later, at the time of assignment of
+ * ath12k_dp_link_peer to ath12k_dp_peer.
*/
- if (dp_peer->is_mlo)
+ if (dp_peer->is_mlo &&
+ dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], dp_peer);
spin_unlock_bh(&dp_hw->peer_lock);
@@ -515,7 +521,8 @@ void ath12k_dp_peer_delete(struct ath12k_dp_hw *dp_hw, u8 *addr,
return;
}
- if (dp_peer->is_mlo)
+ if (dp_peer->is_mlo &&
+ dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);
list_del(&dp_peer->list);
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 485be2998180..533109f9d5bd 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -1285,7 +1285,9 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah)
spin_lock_bh(&dp_hw->peer_lock);
list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) {
if (dp_peer->is_mlo) {
- rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);
+ if (dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
+ rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id],
+ NULL);
ath12k_peer_ml_free(ah, ath12k_sta_to_ahsta(dp_peer->sta));
}
@@ -7746,11 +7748,19 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
/* ML sta */
if (sta->mlo && !ahsta->links_map &&
(hweight16(sta->valid_links) == 1)) {
- ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah);
- if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) {
- ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM",
- sta->addr);
- goto exit;
+ if (ah->host_alloc_ml_id) {
+ ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah);
+ if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) {
+ ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM",
+ sta->addr);
+ goto exit;
+ }
+ } else {
+ /*
+ * firmware allocates the ML peer ID and notifies
+ * the host via HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP
+ */
+ ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_PENDING;
}
dp_params.is_mlo = true;
diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c
index ae93731b4177..25e4b79f11d6 100644
--- a/drivers/net/wireless/ath/ath12k/peer.c
+++ b/drivers/net/wireless/ath/ath12k/peer.c
@@ -230,7 +230,16 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif,
/* Fill ML info into created peer */
if (sta->mlo) {
ml_peer_id = ahsta->ml_peer_id;
- peer->ml_id = ml_peer_id;
+ /*
+ * For chips where firmware allocates the ML peer ID,
+ * ml_peer_id is ATH12K_MLO_PEER_ID_PENDING here. The
+ * MLO_RX_PEER_MAP HTT event handler fixes up
+ * peer->ml_id once the ID is known.
+ */
+ if (ml_peer_id == ATH12K_MLO_PEER_ID_PENDING)
+ peer->ml_id = ATH12K_MLO_PEER_ID_INVALID;
+ else
+ peer->ml_id = ml_peer_id;
ether_addr_copy(peer->ml_addr, sta->addr);
/* the assoc link is considered primary for now */
@@ -285,8 +294,13 @@ void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta)
{
lockdep_assert_wiphy(ah->hw->wiphy);
- if (ahsta->ml_peer_id <
- (ATH12K_MAX_MLO_PEERS | ATH12K_PEER_ML_ID_VALID))
+ /*
+ * Only devices that allocate the ID on the host own a slot in
+ * free_ml_peer_id_map.
+ */
+ if (ah->host_alloc_ml_id &&
+ (ahsta->ml_peer_id <
+ (ATH12K_MAX_MLO_PEERS | ATH12K_PEER_ML_ID_VALID)))
clear_bit(ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID,
ah->free_ml_peer_id_map);
ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
--
2.25.1
^ permalink raw reply related
* [PATCH ath-current v2 6/8] wifi: ath12k: do not advertise MLD peer ID for firmware-allocate devices
From: Baochen Qiang @ 2026-07-20 6:43 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260720-ath12k-fw-allocated-ml-peer-id-v2-0-630632758a80@oss.qualcomm.com>
ath12k_peer_assoc_h_mlo() unconditionally sets ml->peer_id_valid and copies
ahsta->ml_peer_id (with the ATH12K_PEER_ML_ID_VALID bookkeeping bit masked
off) into the WMI_PEER_ASSOC_CMDID ML params, which causes
ath12k_wmi_send_peer_assoc_cmd() to set ATH12K_WMI_FLAG_MLO_PEER_ID_VALID.
This needs to be gated on chips where the firmware allocates the MLD peer
ID:
- WCN7850/QCC2072 firmware always picks the ID itself and does not honor
a host-supplied one, so the value would be silently ignored anyway;
- QCC2072 firmware additionally crashes during MLO disconnect when
ATH12K_WMI_FLAG_MLO_PEER_ID_VALID was set in the preceding peer assoc,
so the bit must not be sent at all.
Branch on ah->host_alloc_ml_id:
- When true (QCN9274 etc.), behavior is unchanged: peer_id_valid is set
and the raw ahsta->ml_peer_id (without the VALID bit) is sent down.
- When false (WCN7850, QCC2072), peer_id_valid stays unset and
ml_peer_id is sent as 0. The firmware ignores both fields and reports
the ID it allocated through HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP.
The early-return on ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID only
applies on the host-alloc path, since on the firmware-alloc path the value
is ATH12K_MLO_PEER_ID_PENDING here, not INVALID.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/mac.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 1ae21618afb6..485be2998180 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -3529,11 +3529,16 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
struct ath12k_sta *ahsta = arsta->ahsta;
struct ath12k_link_sta *arsta_p;
struct ath12k_link_vif *arvif;
+ struct ath12k_hw *ah = arsta->arvif->ar->ah;
unsigned long links;
u8 link_id;
int i;
- if (!sta->mlo || ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID)
+ if (!sta->mlo)
+ return;
+
+ if (ah->host_alloc_ml_id &&
+ ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID)
return;
ml->enabled = true;
@@ -3541,16 +3546,25 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
/* For now considering the primary umac based on assoc link */
ml->primary_umac = arsta->is_assoc_link;
- ml->peer_id_valid = true;
+ /*
+ * Only chips that allocate the MLD peer ID on the host send a valid
+ * ml_peer_id in WMI_PEER_ASSOC_CMDID. For chips where the firmware
+ * picks the ID, leave peer_id_valid false to avoid unexpected issues.
+ */
+ ml->peer_id_valid = ah->host_alloc_ml_id;
ml->logical_link_idx_valid = true;
ether_addr_copy(ml->mld_addr, sta->addr);
ml->logical_link_idx = arsta->link_idx;
/*
* WMI_MLO_PEER_ASSOC_PARAMS expects the raw ML peer ID without
- * the host-side ATH12K_PEER_ML_ID_VALID bookkeeping bit.
+ * the host-side ATH12K_PEER_ML_ID_VALID bookkeeping bit. For chips
+ * where the firmware allocates the ID, the field is unused (the
+ * firmware always allocates regardless of the value here); send 0
+ * to make that intent explicit.
*/
- ml->ml_peer_id = ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID;
+ ml->ml_peer_id = ah->host_alloc_ml_id ?
+ (ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID) : 0;
ml->ieee_link_id = arsta->link_id;
ml->num_partner_links = 0;
ml->eml_cap = sta->eml_cap;
--
2.25.1
^ permalink raw reply related
* [PATCH ath-current v2 5/8] wifi: ath12k: introduce host_alloc_ml_id hardware parameter
From: Baochen Qiang @ 2026-07-20 6:43 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260720-ath12k-fw-allocated-ml-peer-id-v2-0-630632758a80@oss.qualcomm.com>
Different ath12k devices diverge on who allocates MLD peer id:
WCN7850/QCC2072 have the firmware allocate it and notify the host via
HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP event; While others let the host allocate
it and pass it down through WMI_PEER_ASSOC_CMDID with
ATH12K_WMI_FLAG_MLO_PEER_ID_VALID set.
Currently ath12k host allocates this ID and sends it to firmware by
default for all devices. This breaks WCN7850/QCC2072, because the host
maintained ID may be different from the firmware-allocated one.
Consequently data path may fail to find the dp peer and drop some received
packets. From user point of view, this results in bugs reported in [1] or
the 4-way handshake timeout issue.
Add host_alloc_ml_id flag to struct ath12k_hw_params (and a copy on struct
ath12k_hw for hot-path access) so subsequent patches can branch on it. Set
true for QCN9274/IPQ5332/IPQ5424, false for WCN7850/QCC2072. The flag will
be consumed by subsequent patches.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Link: https://bugzilla.kernel.org/show_bug.cgi?id=221039 # 1
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/core.h | 1 +
drivers/net/wireless/ath/ath12k/hw.h | 2 ++
drivers/net/wireless/ath/ath12k/mac.c | 18 +++++++++++++++++-
drivers/net/wireless/ath/ath12k/wifi7/hw.c | 12 ++++++++++++
4 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index fc5127b5c1a3..1f56474efbea 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -793,6 +793,7 @@ struct ath12k_hw {
enum ath12k_hw_state state;
bool regd_updated;
bool use_6ghz_regd;
+ bool host_alloc_ml_id;
u8 num_radio;
diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h
index d135b2936378..8091501cf742 100644
--- a/drivers/net/wireless/ath/ath12k/hw.h
+++ b/drivers/net/wireless/ath/ath12k/hw.h
@@ -232,6 +232,8 @@ struct ath12k_hw_params {
u32 max_client_dbs;
u32 max_client_dbs_sbs;
} client;
+
+ bool host_alloc_ml_id;
};
struct ath12k_hw_ops {
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 7d0d7d5fbf53..1ae21618afb6 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -15382,8 +15382,9 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
int mac_id, device_id, total_radio, num_hw;
struct ath12k_base *ab;
struct ath12k_hw *ah;
- int ret, i, j;
+ bool conf = false;
u8 radio_per_hw;
+ int ret, i, j;
total_radio = 0;
for (i = 0; i < ag->num_devices; i++) {
@@ -15423,6 +15424,20 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
}
ab = ag->ab[device_id];
+
+ /*
+ * the assumption is all devices within an ah
+ * share the same host_alloc_ml_id configuration
+ */
+ if (j == 0) {
+ conf = ab->hw_params->host_alloc_ml_id;
+ } else if (conf != ab->hw_params->host_alloc_ml_id) {
+ ath12k_warn(ab, "inconsistent ML ID config within ah, device 0 uses %s allocated ID, while device %u doesn't\n",
+ conf ? "host" : "firmware", device_id);
+ ret = -EINVAL;
+ goto err;
+ }
+
pdev_map[j].ab = ab;
pdev_map[j].pdev_idx = mac_id;
mac_id++;
@@ -15447,6 +15462,7 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
}
ah->dev = ab->dev;
+ ah->host_alloc_ml_id = conf;
ag->ah[i] = ah;
ag->num_hw++;
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
index e5bf9d218104..0c277f51d99c 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
@@ -439,6 +439,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
+
+ .host_alloc_ml_id = true,
},
{
.name = "wcn7850 hw2.0",
@@ -530,6 +532,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
+
+ .host_alloc_ml_id = false,
},
{
.name = "qcn9274 hw2.0",
@@ -617,6 +621,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
+
+ .host_alloc_ml_id = true,
},
{
.name = "ipq5332 hw1.0",
@@ -697,6 +703,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
+
+ .host_alloc_ml_id = true,
},
{
.name = "qcc2072 hw1.0",
@@ -789,6 +797,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
+
+ .host_alloc_ml_id = false,
},
{
.name = "ipq5424 hw1.0",
@@ -873,6 +883,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
+
+ .host_alloc_ml_id = true,
},
};
--
2.25.1
^ permalink raw reply related
* [PATCH ath-current v2 4/8] wifi: ath12k: add support for HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP
From: Baochen Qiang @ 2026-07-20 6:43 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260720-ath12k-fw-allocated-ml-peer-id-v2-0-630632758a80@oss.qualcomm.com>
Firmware on chips that allocate the MLD peer ID itself (WCN7850 and
QCC2072) reports the assignment back to the host through
HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP. The message carries the chosen
MLD peer id, the MLD MAC address etc.
Add the message type, the on-the-wire struct, the field masks and a
handler that parses them out. The host-side state update (publishing the
dp peer into ath12k_dp_hw::dp_peers[], propagating the ID to
ath12k_dp_link_peer::ml_id and ath12k_sta::ml_peer_id) is added in a
follow-up patch;
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/dp_htt.c | 30 ++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/dp_htt.h | 12 ++++++++++++
2 files changed, 42 insertions(+)
diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c
index 52e10059c6d5..150b190f9c7f 100644
--- a/drivers/net/wireless/ath/ath12k/dp_htt.c
+++ b/drivers/net/wireless/ath/ath12k/dp_htt.c
@@ -575,6 +575,33 @@ static void ath12k_htt_mlo_offset_event_handler(struct ath12k_base *ab,
rcu_read_unlock();
}
+static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab,
+ struct sk_buff *skb)
+{
+ struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data;
+ struct htt_t2h_mlo_peer_map_event *ev = &resp->mlo_peer_map_ev;
+ u16 raw_peer_id, peer_id, addr_h16;
+ u8 peer_addr[ETH_ALEN];
+
+ if (skb->len < sizeof(*ev)) {
+ ath12k_warn(ab, "unexpected htt mlo peer map event len %u\n",
+ skb->len);
+ return;
+ }
+
+ raw_peer_id = le32_get_bits(ev->info0,
+ HTT_T2H_MLO_PEER_MAP_INFO0_MLO_PEER_ID);
+ peer_id = raw_peer_id | ATH12K_PEER_ML_ID_VALID;
+
+ addr_h16 = le32_get_bits(ev->info1,
+ HTT_T2H_MLO_PEER_MAP_INFO1_MAC_ADDR_H16);
+ ath12k_dp_get_mac_addr(le32_to_cpu(ev->mac_addr_l32), addr_h16,
+ peer_addr);
+
+ ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt mlo peer map peer %pM id %u\n",
+ peer_addr, peer_id);
+}
+
void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
struct sk_buff *skb)
{
@@ -659,6 +686,9 @@ void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
case HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND:
ath12k_htt_mlo_offset_event_handler(ab, skb);
break;
+ case HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP:
+ ath12k_dp_htt_mlo_peer_map_handler(ab, skb);
+ break;
default:
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "dp_htt event %d not handled\n",
type);
diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.h b/drivers/net/wireless/ath/ath12k/dp_htt.h
index 987689f11cda..2db7fb27c036 100644
--- a/drivers/net/wireless/ath/ath12k/dp_htt.h
+++ b/drivers/net/wireless/ath/ath12k/dp_htt.h
@@ -930,6 +930,7 @@ enum htt_t2h_msg_type {
HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c,
HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND = 0x24,
HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND = 0x28,
+ HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP = 0x29,
HTT_T2H_MSG_TYPE_PEER_MAP3 = 0x2b,
HTT_T2H_MSG_TYPE_VDEV_TXRX_STATS_PERIODIC_IND = 0x2c,
};
@@ -974,11 +975,22 @@ struct htt_t2h_peer_unmap_event {
__le32 info1;
} __packed;
+#define HTT_T2H_MLO_PEER_MAP_INFO0_MLO_PEER_ID GENMASK(23, 8)
+#define HTT_T2H_MLO_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0)
+
+struct htt_t2h_mlo_peer_map_event {
+ __le32 info0;
+ __le32 mac_addr_l32;
+ __le32 info1;
+ __le32 reserved[5];
+} __packed;
+
struct htt_resp_msg {
union {
struct htt_t2h_version_conf_msg version_msg;
struct htt_t2h_peer_map_event peer_map_ev;
struct htt_t2h_peer_unmap_event peer_unmap_ev;
+ struct htt_t2h_mlo_peer_map_event mlo_peer_map_ev;
};
} __packed;
--
2.25.1
^ permalink raw reply related
* [PATCH ath-current v2 3/8] wifi: ath12k: keep ATH12K_PEER_ML_ID_VALID set in ath12k_sta::ml_peer_id
From: Baochen Qiang @ 2026-07-20 6:43 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260720-ath12k-fw-allocated-ml-peer-id-v2-0-630632758a80@oss.qualcomm.com>
Several pieces of host bookkeeping for MLD peer IDs encode the
same fact in different ways:
- ath12k_sta::ml_peer_id stores the raw ID in [0, ATH12K_MAX_MLO_PEERS);
- ath12k_dp_peer::peer_id, ath12k_dp_link_peer::ml_id and the index used
on ath12k_dp_hw::dp_peers[] always carry the ATH12K_PEER_ML_ID_VALID
bit (BIT(13)) when the ID is real;
- WMI_MLO_PEER_ASSOC_PARAMS::ml_peer_id sent down to firmware is
raw, without the bookkeeping bit.
The mismatch leaks into call sites that have to remember to OR
the bit in (ath12k_peer_create(), ath12k_mac_op_sta_state()) or
remember not to (ath12k_peer_assoc_h_mlo()).
Make ath12k_sta::ml_peer_id carry the VALID bit when valid, the same
way ath12k_dp_peer::peer_id and ath12k_dp_link_peer::ml_id do:
- ath12k_peer_ml_alloc() OR-s the bit in once on the way out;
the internal bitmap stays raw [0, ATH12K_MAX_MLO_PEERS);
- ath12k_peer_create() and ath12k_mac_op_sta_state() drop the
explicit OR;
- ath12k_peer_assoc_h_mlo() masks the bit off when populating
the WMI ml_peer_id;
While there, introduce ath12k_peer_ml_free() to mirror
ath12k_peer_ml_alloc(), which helps avoid code duplication.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/mac.c | 27 +++++++++++++--------------
drivers/net/wireless/ath/ath12k/peer.c | 17 ++++++++++++++---
drivers/net/wireless/ath/ath12k/peer.h | 1 +
3 files changed, 28 insertions(+), 17 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 3e3b06e15f80..7d0d7d5fbf53 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -1278,16 +1278,15 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah)
struct ath12k_dp_peer *dp_peer, *tmp;
struct ath12k_dp_hw *dp_hw = &ah->dp_hw;
+ lockdep_assert_wiphy(ah->hw->wiphy);
+
INIT_LIST_HEAD(&peers);
spin_lock_bh(&dp_hw->peer_lock);
list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) {
if (dp_peer->is_mlo) {
- struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(dp_peer->sta);
-
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);
- clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map);
- ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
+ ath12k_peer_ml_free(ah, ath12k_sta_to_ahsta(dp_peer->sta));
}
list_move(&dp_peer->list, &peers);
@@ -3547,7 +3546,11 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
ether_addr_copy(ml->mld_addr, sta->addr);
ml->logical_link_idx = arsta->link_idx;
- ml->ml_peer_id = ahsta->ml_peer_id;
+ /*
+ * WMI_MLO_PEER_ASSOC_PARAMS expects the raw ML peer ID without
+ * the host-side ATH12K_PEER_ML_ID_VALID bookkeeping bit.
+ */
+ ml->ml_peer_id = ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID;
ml->ieee_link_id = arsta->link_id;
ml->num_partner_links = 0;
ml->eml_cap = sta->eml_cap;
@@ -7264,10 +7267,8 @@ static void ath12k_mac_ml_station_remove(struct ath12k_vif *ahvif,
ath12k_mac_free_unassign_link_sta(ah, ahsta, link_id);
}
- if (sta->mlo) {
- clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map);
- ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
- }
+ if (sta->mlo)
+ ath12k_peer_ml_free(ah, ahsta);
}
static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw,
@@ -7739,7 +7740,7 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
}
dp_params.is_mlo = true;
- dp_params.peer_id = ahsta->ml_peer_id | ATH12K_PEER_ML_ID_VALID;
+ dp_params.peer_id = ahsta->ml_peer_id;
}
dp_params.sta = sta;
@@ -7876,10 +7877,8 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
peer_delete:
ath12k_dp_peer_delete(&ah->dp_hw, sta->addr, sta);
ml_peer_id_clear:
- if (sta->mlo) {
- clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map);
- ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
- }
+ if (sta->mlo)
+ ath12k_peer_ml_free(ah, ahsta);
exit:
/* update the state if everything went well */
if (!ret)
diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c
index c222bdaa333c..ae93731b4177 100644
--- a/drivers/net/wireless/ath/ath12k/peer.c
+++ b/drivers/net/wireless/ath/ath12k/peer.c
@@ -230,7 +230,7 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif,
/* Fill ML info into created peer */
if (sta->mlo) {
ml_peer_id = ahsta->ml_peer_id;
- peer->ml_id = ml_peer_id | ATH12K_PEER_ML_ID_VALID;
+ peer->ml_id = ml_peer_id;
ether_addr_copy(peer->ml_addr, sta->addr);
/* the assoc link is considered primary for now */
@@ -276,9 +276,20 @@ u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah)
}
if (ml_peer_id == ATH12K_MAX_MLO_PEERS)
- ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
+ return ATH12K_MLO_PEER_ID_INVALID;
- return ml_peer_id;
+ return ml_peer_id | ATH12K_PEER_ML_ID_VALID;
+}
+
+void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta)
+{
+ lockdep_assert_wiphy(ah->hw->wiphy);
+
+ if (ahsta->ml_peer_id <
+ (ATH12K_MAX_MLO_PEERS | ATH12K_PEER_ML_ID_VALID))
+ clear_bit(ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID,
+ ah->free_ml_peer_id_map);
+ ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
}
int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta)
diff --git a/drivers/net/wireless/ath/ath12k/peer.h b/drivers/net/wireless/ath/ath12k/peer.h
index 49d89796bc46..0f7f25b8e89c 100644
--- a/drivers/net/wireless/ath/ath12k/peer.h
+++ b/drivers/net/wireless/ath/ath12k/peer.h
@@ -26,4 +26,5 @@ int ath12k_link_sta_rhash_add(struct ath12k_base *ab, struct ath12k_link_sta *ar
struct ath12k_link_sta *ath12k_link_sta_find_by_addr(struct ath12k_base *ab,
const u8 *addr);
u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah);
+void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta);
#endif /* _PEER_H_ */
--
2.25.1
^ permalink raw reply related
* [PATCH ath-current v2 2/8] wifi: ath12k: factor out peer assoc send-and-wait into a helper
From: Baochen Qiang @ 2026-07-20 6:43 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260720-ath12k-fw-allocated-ml-peer-id-v2-0-630632758a80@oss.qualcomm.com>
ath12k_bss_assoc(), ath12k_mac_station_assoc() and
ath12k_sta_rc_update_wk() all open-code the same sequence: reinit the
peer_assoc_done completion, send the peer assoc WMI command, then wait
for the firmware confirmation event. The reinit_completion() was buried
in ath12k_peer_assoc_prepare(), far from the wait_for_completion_timeout()
that consumes it, making the reinit/send/wait sequence hard to follow,
and the three open-coded copies are easy to get out of sync.
Move the sequence into a new helper ath12k_mac_peer_assoc() and call it
from all three sites. The reinit, send and wait now live together so the
completion's lifecycle is easy to read.
While at it, ath12k_sta_rc_update_wk() previously warned but still
waited the full timeout when the peer assoc command failed to send. Now
a send failure returns immediately and skips the pointless 1 second
wait, matching the other two callers.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/mac.c | 59 +++++++++++++++++------------------
1 file changed, 29 insertions(+), 30 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index b7eba8ea981b..3e3b06e15f80 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -3594,8 +3594,6 @@ static void ath12k_peer_assoc_prepare(struct ath12k *ar,
memset(arg, 0, sizeof(*arg));
- reinit_completion(&ar->peer_assoc_done);
-
arg->peer_new_assoc = !reassoc;
ath12k_peer_assoc_h_basic(ar, arvif, arsta, arg);
ath12k_peer_assoc_h_crypto(ar, arvif, arsta, arg);
@@ -3835,6 +3833,29 @@ static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar,
return bw;
}
+static int ath12k_mac_peer_assoc(struct ath12k *ar,
+ struct ath12k_wmi_peer_assoc_arg *peer_arg)
+{
+ int ret;
+
+ reinit_completion(&ar->peer_assoc_done);
+
+ ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n",
+ peer_arg->peer_mac, peer_arg->vdev_id, ret);
+ return ret;
+ }
+
+ if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
+ ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
+ peer_arg->peer_mac, peer_arg->vdev_id);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
static void ath12k_bss_assoc(struct ath12k *ar,
struct ath12k_link_vif *arvif,
struct ieee80211_bss_conf *bss_conf)
@@ -3915,18 +3936,10 @@ static void ath12k_bss_assoc(struct ath12k *ar,
}
peer_arg->is_assoc = true;
- ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
- if (ret) {
- ath12k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n",
- bss_conf->bssid, arvif->vdev_id, ret);
- return;
- }
- if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
- ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
- bss_conf->bssid, arvif->vdev_id);
+ ret = ath12k_mac_peer_assoc(ar, peer_arg);
+ if (ret)
return;
- }
ret = ath12k_setup_peer_smps(ar, arvif, bss_conf->bssid,
&link_sta->ht_cap, &link_sta->he_6ghz_capa);
@@ -6480,18 +6493,10 @@ static int ath12k_mac_station_assoc(struct ath12k *ar,
}
peer_arg->is_assoc = true;
- ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
- if (ret) {
- ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n",
- arsta->addr, arvif->vdev_id, ret);
- return ret;
- }
- if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
- ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
- arsta->addr, arvif->vdev_id);
- return -ETIMEDOUT;
- }
+ ret = ath12k_mac_peer_assoc(ar, peer_arg);
+ if (ret)
+ return ret;
num_vht_rates = ath12k_mac_bitrate_mask_num_vht_rates(ar, band, mask);
num_he_rates = ath12k_mac_bitrate_mask_num_he_rates(ar, band, mask);
@@ -6840,14 +6845,8 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
peer_arg, true);
peer_arg->is_assoc = false;
- err = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
- if (err)
- ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n",
- arsta->addr, arvif->vdev_id, err);
- if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ))
- ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
- arsta->addr, arvif->vdev_id);
+ ath12k_mac_peer_assoc(ar, peer_arg);
}
}
}
--
2.25.1
^ permalink raw reply related
* [PATCH ath-current v2 1/8] wifi: ath12k: fix out-of-bounds clear_bit in ath12k_mac_dp_peer_cleanup()
From: Baochen Qiang @ 2026-07-20 6:43 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260720-ath12k-fw-allocated-ml-peer-id-v2-0-630632758a80@oss.qualcomm.com>
ath12k_mac_dp_peer_cleanup() clears the ML peer ID slot on the
free_ml_peer_id_map bitmap by indexing it with dp_peer->peer_id. That is
wrong: dp_peer->peer_id for an MLO peer always carries the
ATH12K_PEER_ML_ID_VALID bit (BIT(13)), so clear_bit() is invoked with
index >= 0x2000, which is far outside the bitmap of ATH12K_MAX_MLO_PEERS
(256) bits and corrupts memory adjacent to ah->free_ml_peer_id_map. The
intended bitmap entry also never gets cleared, so subsequent
ath12k_peer_ml_alloc() calls eventually run out of IDs.
The ID without the VALID bit is what ath12k_peer_ml_alloc() returned and
is stored in ahsta->ml_peer_id. Use that instead.
While there, also reset ahsta->ml_peer_id to ATH12K_MLO_PEER_ID_INVALID so
the bitmap and ahsta->ml_peer_id stay in sync;
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Fixes: ee16dcf573d5 ("wifi: ath12k: Define ath12k_dp_peer structure & APIs for create & delete")
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/mac.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 490c134c1f29..b7eba8ea981b 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -1283,8 +1283,11 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah)
spin_lock_bh(&dp_hw->peer_lock);
list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) {
if (dp_peer->is_mlo) {
+ struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(dp_peer->sta);
+
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);
- clear_bit(dp_peer->peer_id, ah->free_ml_peer_id_map);
+ clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map);
+ ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
}
list_move(&dp_peer->list, &peers);
--
2.25.1
^ permalink raw reply related
* [PATCH ath-current v2 0/8] wifi: ath12k: support firmware-allocated MLD peer ID
From: Baochen Qiang @ 2026-07-20 6:43 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
ath12k currently assumes the host allocates the MLD peer ID and passes
it down to firmware via WMI_PEER_ASSOC_CMDID. This works on QCN9274
but breaks WCN7850/QCC2072, whose firmware always picks the ID itself
and reports it back through HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP. As a
result dp_hw->dp_peers[] is never populated for MLO peers and the data
path lookup fails. On QCC2072 the firmware additionally crashes on MLO
disconnect when ATH12K_WMI_FLAG_MLO_PEER_ID_VALID was set in the peer
assoc command.
Add a host_alloc_ml_id hw_param to branch behavior, defer the
dp_peers[] publish to the HTT event for firmware-allocated chips, and
propagate the firmware-assigned ID through the existing host
bookkeeping when it arrives.
Patch summary:
1: fix for an out-of-bounds clear_bit() in
ath12k_mac_dp_peer_cleanup().
2: group peer assoc send-and-wait into a helper
3: refactor, keep ATH12K_PEER_ML_ID_VALID set in ahsta->ml_peer_id
so later patches do not have to OR or mask it at every call site;
4: parse the HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP message;
5: introduce hw_param host_alloc_ml_id, set true on QCN9274 family
and false on WCN7850/QCC2072;
6: on host_alloc_ml_id == false, leave peer_id_valid unset and send
ml_peer_id == 0 in WMI_PEER_ASSOC_CMDID;
7: on host_alloc_ml_id == false, mark ahsta->ml_peer_id and
dp_peer->peer_id as ATH12K_MLO_PEER_ID_PENDING and skip the
dp_hw->dp_peers[] publish until the firmware reports the ID;
8: in the MLO_RX_PEER_MAP handler, propagate the firmware-assigned
ID into dp_peer->peer_id, every dp_link_peer in
dp_peer->link_peers[], and ahsta->ml_peer_id, all under
dp_hw->peer_lock.
---
Changes in v2:
- patch 5/8: initialize ret before goto err handling in ath12k_mac_allocate()
- Link to v1: https://lore.kernel.org/r/20260713-ath12k-fw-allocated-ml-peer-id-v1-0-d0a2a1a519eb@oss.qualcomm.com
---
Baochen Qiang (8):
wifi: ath12k: fix out-of-bounds clear_bit in ath12k_mac_dp_peer_cleanup()
wifi: ath12k: factor out peer assoc send-and-wait into a helper
wifi: ath12k: keep ATH12K_PEER_ML_ID_VALID set in ath12k_sta::ml_peer_id
wifi: ath12k: add support for HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP
wifi: ath12k: introduce host_alloc_ml_id hardware parameter
wifi: ath12k: do not advertise MLD peer ID for firmware-allocate devices
wifi: ath12k: defer dp_peer registration when firmware allocates MLD peer ID
wifi: ath12k: resolve PENDING ML peer ID from MLO_PEER_MAP HTT event
drivers/net/wireless/ath/ath12k/core.c | 2 +
drivers/net/wireless/ath/ath12k/core.h | 3 +
drivers/net/wireless/ath/ath12k/dp_htt.c | 49 +++++++++
drivers/net/wireless/ath/ath12k/dp_htt.h | 12 +++
drivers/net/wireless/ath/ath12k/dp_peer.c | 75 +++++++++++--
drivers/net/wireless/ath/ath12k/dp_peer.h | 2 +
drivers/net/wireless/ath/ath12k/hw.h | 2 +
drivers/net/wireless/ath/ath12k/mac.c | 165 ++++++++++++++++++++---------
drivers/net/wireless/ath/ath12k/peer.c | 31 +++++-
drivers/net/wireless/ath/ath12k/peer.h | 1 +
drivers/net/wireless/ath/ath12k/wifi7/hw.c | 12 +++
11 files changed, 293 insertions(+), 61 deletions(-)
---
base-commit: 951dc0a744e4dc8490935316d3b76e23990bde3c
change-id: 20260527-ath12k-fw-allocated-ml-peer-id-2b456891157f
Best regards,
--
Baochen Qiang <baochen.qiang@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v3] wifi: ath11k: fix txpower in ap mode for 6 Ghz
From: Baochen Qiang @ 2026-07-20 6:31 UTC (permalink / raw)
To: Sebastian Gottschall, ath11k; +Cc: linux-wireless
In-Reply-To: <20260514202750.3040404-1-s.gottschall@dd-wrt.com>
On 5/15/2026 4:27 AM, Sebastian Gottschall wrote:
> there is an issue which has been discovered a while ago while testing
> which killed txpower to 0 dBm once scanning is triggered in AP mode
> on QCN9074 based chipsets if 6 GHz is in use.
> ath11k_wmi_send_vdev_set_tpc_power must be set in AP mode in the same
> way is it is for STA as it is implemented in ath12k and in the
> qsdk version for ath11k.
> this patch must be considered to be backported (i discovered that issue
> already 2 years ago)
>
> fixes: 74ef2d05ede63fd6416aa635aa8972dff901325f
> (wifi: ath11k: use WMI_VDEV_SET_TPC_POWER_CMDID when EXT_TPC_REG_SUPPORT for 6 GHz)
checkpatch errors on this. Three problems: (1) it must be Fixes: (capital F), not fixes:;
(2) the SHA should be truncated to 12 chars with the title in parentheses on the same
line, not split across two lines with the full 40-char SHA; (3) the tag belongs in the
trailer block just above Signed-off-by, not buried in the body separated by a blank line.
Correct form:
Fixes: 74ef2d05ede6 ("wifi: ath11k: use WMI_VDEV_SET_TPC_POWER_CMDID when
EXT_TPC_REG_SUPPORT for 6 GHz")
>
> changes:
> v2: code formating fixes, take care about LPI setting in AP mode
> v3: fix typo in email annotation
The changes: / v2: / v3: block sits above the --- line, so git am would commit it into git
history. It should go below the --- scissors (after Signed-off-by, before the diff), so it
stays in the mail and is stripped from the final commit.
>
> Signed-off-by: Sebastian Gottschall <s.gottschall@dd-wrt.com>
> ---
> drivers/net/wireless/ath/ath11k/mac.c | 21 ++++++++++++++-------
> 1 file changed, 14 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
> index a48b6bf1f29a..0984c3981ee4 100644
> --- a/drivers/net/wireless/ath/ath11k/mac.c
> +++ b/drivers/net/wireless/ath/ath11k/mac.c
> @@ -3497,13 +3497,13 @@ static int ath11k_mac_config_obss_pd(struct ath11k *ar,
> return 0;
> }
>
> -static bool ath11k_mac_supports_station_tpc(struct ath11k *ar,
> - struct ath11k_vif *arvif,
> - const struct cfg80211_chan_def *chandef)
> +static bool ath11k_mac_supports_tpc(struct ath11k *ar, struct ath11k_vif *arvif,
> + const struct cfg80211_chan_def *chandef)
> {
> return ath11k_wmi_supports_6ghz_cc_ext(ar) &&
> test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map) &&
> - arvif->vdev_type == WMI_VDEV_TYPE_STA &&
> + (arvif->vdev_type == WMI_VDEV_TYPE_STA ||
> + arvif->vdev_type == WMI_VDEV_TYPE_AP) &&
The continuation arvif->vdev_type == WMI_VDEV_TYPE_AP should align to the char after the
open paren.
> arvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE &&
> chandef->chan &&
> chandef->chan->band == NL80211_BAND_6GHZ;
> @@ -7647,8 +7647,8 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif,
> /* TODO: For now we only set TPC power here. However when
> * channel changes, say CSA, it should be updated again.
> */
> - if (ath11k_mac_supports_station_tpc(ar, arvif, chandef)) {
> - ath11k_mac_fill_reg_tpc_info(ar, arvif->vif, &arvif->chanctx);
> + if (ath11k_mac_supports_tpc(ar, arvif, chandef)) {
> + ath11k_mac_fill_reg_tpc_info(ar, arvif->vif, ctx);
> ath11k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id,
> &arvif->reg_tpc_info);
> }
> @@ -8155,6 +8155,7 @@ void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
> psd_power, tx_power;
> s8 eirp_power = 0;
> u16 start_freq, center_freq;
> + u8 reg_6ghz_power_mode;
>
> chan = ctx->def.chan;
> start_freq = ath11k_mac_get_6ghz_start_frequency(&ctx->def);
> @@ -8311,8 +8312,14 @@ void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
> reg_tpc_info->num_pwr_levels = num_pwr_levels;
> reg_tpc_info->is_psd_power = is_psd_power;
> reg_tpc_info->eirp_power = eirp_power;
> + if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
> + reg_6ghz_power_mode = vif->bss_conf.power_type;
The function already has struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; use it.
> + else
> + /* For now, LPI is the only supported AP power mode */
> + reg_6ghz_power_mode = IEEE80211_REG_LPI_AP;
> +
> reg_tpc_info->ap_power_type =
> - ath11k_reg_ap_pwr_convert(vif->bss_conf.power_type);
> + ath11k_reg_ap_pwr_convert(reg_6ghz_power_mode);
> }
>
> static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar,
^ permalink raw reply
* RE: [RFC PATCH v1 2/9] wifi: rtw88: 8723bs: handle SDIO management and data TX
From: Ping-Ke Shih @ 2026-07-20 6:30 UTC (permalink / raw)
To: luka.gejak@linux.dev, linux-wireless@vger.kernel.org
Cc: straube.linux@gmail.com
In-Reply-To: <1ac8fad97e3fcdea41f20963f771fbf5efbe6f6a.1784047561.git.luka.gejak@linux.dev>
luka.gejak@linux.dev <luka.gejak@linux.dev> wrote:
> From: Luka Gejak <luka.gejak@linux.dev>
>
> The RTL8723BS SDIO firmware expects the vendor TX-descriptor contract for
> management frames: SPE_RPT set so the 8051 schedules the frame on air, a
> fixed data retry limit, and management frames carried on the shared BCMC
> station context. Program the descriptor accordingly and select the
> management rate (CCK on 2.4 GHz, following the BSS basic-rate set), and add
> rtw_tx_report_handle_8723b() to consume the vendor CCX TX reports.
Doesn't the existing logic work to you? What is the problem?
>
> Mark non-management packets as complete first/last segments and transmit
> EAPOL at the lowest basic rate so the four-way handshake follows the
> vendor data descriptor contract.
Can you explain why this is necessary? As I know, first/last segments are
related to TX in bus. Why does it can affect four-way?
>
> Also apply the firmware-reported TX rate through the descriptor: on this
> chip the hardware otherwise keys the CCK floor on air regardless of the
> rate mask, so force the rate the firmware last reported via C2H while
> leaving rate fallback enabled for recovery after a power-save wake.
Not sure what the problem you encountered. For management frames, the rtw88
TX fixed rate depends on operating band and lowest supported rate.
And, the TX report struct only contains 'sn' not TX rate.
>
> Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
> ---
> drivers/net/wireless/realtek/rtw88/tx.c | 225 +++++++++++++++++++++++-
> drivers/net/wireless/realtek/rtw88/tx.h | 5 +
> 2 files changed, 223 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c
> index 9d747a060b98..5b4d4713be6f 100644
> --- a/drivers/net/wireless/realtek/rtw88/tx.c
> +++ b/drivers/net/wireless/realtek/rtw88/tx.c
> @@ -37,14 +37,20 @@ void rtw_tx_fill_tx_desc(struct rtw_dev *rtwdev,
> {
> struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)skb->data;
> bool more_data = false;
> + bool first_seg = false;
>
> if (pkt_info->qsel == TX_DESC_QSEL_HIGH)
> more_data = true;
>
> + if (rtw_is_8723bs_sdio(rtwdev) &&
> + pkt_info->qsel != TX_DESC_QSEL_MGMT)
> + first_seg = true;
> +
> tx_desc->w0 = le32_encode_bits(pkt_info->tx_pkt_size, RTW_TX_DESC_W0_TXPKTSIZE) |
> le32_encode_bits(pkt_info->offset, RTW_TX_DESC_W0_OFFSET) |
> le32_encode_bits(pkt_info->bmc, RTW_TX_DESC_W0_BMC) |
> le32_encode_bits(pkt_info->ls, RTW_TX_DESC_W0_LS) |
> + le32_encode_bits(first_seg, RTW_TX_DESC_W0_FS) |
> le32_encode_bits(pkt_info->dis_qselseq, RTW_TX_DESC_W0_DISQSELSEQ);
>
> tx_desc->w1 = le32_encode_bits(pkt_info->mac_id, RTW_TX_DESC_W1_MACID) |
> @@ -68,9 +74,15 @@ void rtw_tx_fill_tx_desc(struct rtw_dev *rtwdev,
>
> tx_desc->w4 = le32_encode_bits(pkt_info->rate, RTW_TX_DESC_W4_DATARATE);
>
> - if (rtwdev->chip->old_datarate_fb_limit)
> + if (rtwdev->chip->old_datarate_fb_limit &&
> + !pkt_info->disable_data_rate_fb_limit)
> tx_desc->w4 |= le32_encode_bits(0x1f, RTW_TX_DESC_W4_DATARATE_FB_LIMIT);
>
> + if (pkt_info->retry_limit_en)
> + tx_desc->w4 |= le32_encode_bits(true, RTW_TX_DESC_W4_RETRY_LIMIT_EN) |
> + le32_encode_bits(pkt_info->data_retry_limit,
> + RTW_TX_DESC_W4_DATA_RETRY_LIMIT);
> +
> tx_desc->w5 = le32_encode_bits(pkt_info->short_gi, RTW_TX_DESC_W5_DATA_SHORT) |
> le32_encode_bits(pkt_info->bw, RTW_TX_DESC_W5_DATA_BW) |
> le32_encode_bits(pkt_info->ldpc, RTW_TX_DESC_W5_DATA_LDPC) |
> @@ -262,6 +274,36 @@ void rtw_tx_report_handle(struct rtw_dev *rtwdev, struct sk_buff *skb, int src)
> spin_unlock_irqrestore(&tx_report->q_lock, flags);
> }
>
> +/* 8723BS SDIO v41 firmware reports management TX through the vendor CCX C2H
> + * (0x12 for auth/assoc/data, 0x32 for scan probe). Payload byte 0 is the
> + * report type/status (bit 6 lifetime-over, bit 7 retry-over); byte 6 is the
> + * W6 SW_DEFINE (sequence number) the vendor driver reads back.
> + */
> +void rtw_tx_report_handle_8723b(struct rtw_dev *rtwdev, u8 report_type,
> + u8 *payload, u8 len)
> +{
> + struct rtw_tx_report *tx_report = &rtwdev->tx_report;
> + struct sk_buff *cur, *tmp;
> + unsigned long flags;
> + bool failed = len > 0 && (payload[0] & (BIT(6) | BIT(7)));
> + u8 sn = len >= 7 ? payload[6] : 0xff;
Use le8_get_bits() with predefined mask to get the value.
> + u8 *n;
> +
> + if (len < 7)
> + return;
> +
Only the decode format is different from existing, the following can be reuse.
> + spin_lock_irqsave(&tx_report->q_lock, flags);
> + skb_queue_walk_safe(&tx_report->queue, cur, tmp) {
> + n = (u8 *)IEEE80211_SKB_CB(cur)->status.status_driver_data;
> + if (*n == sn) {
> + __skb_unlink(cur, &tx_report->queue);
> + rtw_tx_report_tx_status(rtwdev, cur, !failed);
> + break;
> + }
> + }
> + spin_unlock_irqrestore(&tx_report->q_lock, flags);
> +}
> +
> static u8 rtw_get_mgmt_rate(struct rtw_dev *rtwdev, struct sk_buff *skb,
> u8 lowest_rate, bool ignore_rate)
> {
> @@ -275,15 +317,118 @@ static u8 rtw_get_mgmt_rate(struct rtw_dev *rtwdev, struct sk_buff *skb,
> return __ffs(vif->bss_conf.basic_rates) + lowest_rate;
> }
>
> +static bool rtw_tx_8723bs_sdio_2g(struct rtw_dev *rtwdev)
rtw_tx_8723bs_sdio_2ghz
But I think we don't need this. The consumers seem not need special cases.
I mean just use existing logics.
> +{
> + return rtw_is_8723bs_sdio(rtwdev) &&
> + rtwdev->hal.current_band_type == RTW_BAND_2G;
> +}
> +
> +static bool rtw_tx_8723bs_rates_have_cck(const u8 *ie)
> +{
> + int i;
> +
> + if (!ie)
> + return false;
> +
> + for (i = 2; i < ie[1] + 2; i++) {
> + switch (ie[i] & 0x7f) {
> + case 2: /* 1 Mbps */
> + case 4: /* 2 Mbps */
> + case 11: /* 5.5 Mbps */
> + case 22: /* 11 Mbps */
> + return true;
> + default:
> + break;
> + }
> + }
> +
> + return false;
> +}
> +
> +static bool rtw_tx_8723bs_bss_has_cck(struct rtw_dev *rtwdev,
> + struct ieee80211_vif *vif,
> + const u8 *bssid,
> + bool *known)
> +{
> + struct cfg80211_bss *bss;
> + const u8 *rates;
> + const u8 *ext_rates;
> + bool has_cck = false;
> +
> + *known = false;
> +
> + if (!vif || !bssid || !is_valid_ether_addr(bssid))
> + return false;
> +
> + bss = cfg80211_get_bss(rtwdev->hw->wiphy, NULL, bssid, NULL, 0,
> + IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY);
> + if (!bss)
> + return false;
> +
> + rcu_read_lock();
> + rates = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
> + ext_rates = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
> + if (rates || ext_rates) {
> + *known = true;
> + has_cck = rtw_tx_8723bs_rates_have_cck(rates) ||
> + rtw_tx_8723bs_rates_have_cck(ext_rates);
> + }
> + rcu_read_unlock();
> +
> + cfg80211_put_bss(rtwdev->hw->wiphy, bss);
> +
> + return has_cck;
> +}
> +
> +/* 8723BS SDIO follows the vendor driver's tx_rate rule: the initial scan
> + * default is 1 Mbps CCK, and join-time update_wireless_mode() keeps 1 Mbps
> + * whenever the selected BSS rate set includes CCK; only pure-G BSSes use 6
> + * Mbps OFDM.
> + */
> +static void rtw_tx_8723bs_sdio_rate(struct rtw_dev *rtwdev,
> + struct rtw_tx_pkt_info *pkt_info,
> + struct sk_buff *skb)
> +{
> + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
> + struct ieee80211_vif *vif = tx_info->control.vif;
> + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
> + const u8 *bssid = NULL;
> + bool known = false;
> + bool has_cck = true;
> +
> + if (ieee80211_is_data(hdr->frame_control) ||
> + ieee80211_is_mgmt(hdr->frame_control))
> + bssid = hdr->addr1;
> +
> + has_cck = rtw_tx_8723bs_bss_has_cck(rtwdev, vif, bssid, &known);
> + if (!known && vif && vif->bss_conf.basic_rates)
> + has_cck = vif->bss_conf.basic_rates & 0xf;
> + else if (!known)
> + has_cck = true;
> +
> + if (has_cck) {
> + pkt_info->rate_id = RTW_RATEID_B_20M;
> + pkt_info->rate = DESC_RATE1M;
> + } else {
> + pkt_info->rate_id = RTW_RATEID_G;
> + pkt_info->rate = DESC_RATE6M;
> + }
> +}
> +
> static void rtw_tx_pkt_info_update_rate(struct rtw_dev *rtwdev,
> struct rtw_tx_pkt_info *pkt_info,
> struct sk_buff *skb,
> bool ignore_rate)
> {
> if (rtwdev->hal.current_band_type == RTW_BAND_2G) {
> - pkt_info->rate_id = RTW_RATEID_B_20M;
> - pkt_info->rate = rtw_get_mgmt_rate(rtwdev, skb, DESC_RATE1M,
> - ignore_rate);
> + if (rtw_tx_8723bs_sdio_2g(rtwdev)) {
> + rtw_tx_8723bs_sdio_rate(rtwdev, pkt_info, skb);
I don't think we need this special case.
> + } else {
> + pkt_info->rate_id = RTW_RATEID_B_20M;
> + pkt_info->rate = rtw_get_mgmt_rate(rtwdev, skb,
> + DESC_RATE1M,
> + ignore_rate);
> + }
> } else {
> pkt_info->rate_id = RTW_RATEID_G;
> pkt_info->rate = rtw_get_mgmt_rate(rtwdev, skb, DESC_RATE6M,
> @@ -292,6 +437,12 @@ static void rtw_tx_pkt_info_update_rate(struct rtw_dev *rtwdev,
>
> pkt_info->use_rate = true;
> pkt_info->dis_rate_fallback = true;
> +
> + /* 8723BS SDIO 2.4 GHz: the vendor path leaves dis_rate_fallback=0 for
> + * MGNT_FRAMETAG and EAPOL/ARP data frames.
> + */
> + if (rtw_tx_8723bs_sdio_2g(rtwdev))
> + pkt_info->dis_rate_fallback = false;
This is not necessary either.
> }
>
> static void rtw_tx_pkt_info_update_sec(struct rtw_dev *rtwdev,
> @@ -326,7 +477,30 @@ static void rtw_tx_mgmt_pkt_info_update(struct rtw_dev *rtwdev,
> struct ieee80211_sta *sta,
> struct sk_buff *skb)
> {
> + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
> +
> rtw_tx_pkt_info_update_rate(rtwdev, pkt_info, skb, false);
> +
> + if (rtw_is_8723bs_sdio(rtwdev)) {
> + /* The vendor v41 firmware requires SPE_RPT=1 in the TX
> + * descriptor to schedule a management frame for air
> + * transmission (report=true), with SW_DEFINE/sn=0. Keep the
> + * retry limit and rate-fallback control aligned with the
> + * vendor rtl8723b_fill_default_txdesc() contract.
> + */
As rtw_tx_pkt_info_update_rate() has configured fixed rate. I suppose we
don't need so many additional settings. Especially, why does it need
report?
> + pkt_info->seq = (le16_to_cpu(hdr->seq_ctrl) &
> + IEEE80211_SCTL_SEQ) >> 4;
> + pkt_info->en_hwseq = true;
> + pkt_info->hw_ssn_sel = 0;
> + pkt_info->dis_rate_fallback = false;
> + pkt_info->retry_limit_en = true;
> + pkt_info->data_retry_limit = 6;
> + pkt_info->disable_data_rate_fb_limit = true;
> + pkt_info->report = true;
> + pkt_info->sn = 0;
> + return;
> + }
> +
> pkt_info->dis_qselseq = true;
> pkt_info->en_hwseq = true;
> pkt_info->hw_ssn_sel = 0;
> @@ -396,6 +570,30 @@ static void rtw_tx_data_pkt_info_update(struct rtw_dev *rtwdev,
> pkt_info->stbc = stbc;
> pkt_info->ldpc = ldpc;
>
> + /* 8723BS SDIO keys the CCK floor on air regardless of the rate mask
> + * unless the firmware-reported rate is forced through the descriptor.
> + * Apply the rate the firmware last reported via C2H as the initial TX
> + * rate. Leave rate fallback enabled (dis_rate_fallback stays 0): the
> + * applied rate is only the initial one, so a transiently-stale value -
> + * e.g. right after a power-save wake - can still step down and recover.
> + */
> + if (sta && rtw_is_8723bs_sdio(rtwdev)) {
> + si = (struct rtw_sta_info *)sta->drv_priv;
> + if (si->ra_report.desc_rate >= DESC_RATEMCS0 &&
> + si->ra_report.desc_rate < DESC_RATE_MAX) {
> + pkt_info->rate = si->ra_report.desc_rate;
> + pkt_info->use_rate = true;
> + }
> + }
> +
> + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
> + rtw_tx_pkt_info_update_rate(rtwdev, pkt_info, skb, true);
> +
> + if (rtw_is_8723bs_sdio(rtwdev) && info->control.vif &&
> + info->control.vif->bss_conf.use_short_preamble)
> + pkt_info->short_gi = true;
> + }
> +
> fix_rate = dm_info->fix_rate;
> if (fix_rate < DESC_RATE_MAX) {
> pkt_info->rate = fix_rate;
> @@ -416,6 +614,7 @@ void rtw_tx_pkt_info_update(struct rtw_dev *rtwdev,
> struct rtw_sta_info *si;
> struct rtw_vif *rtwvif;
> __le16 fc = hdr->frame_control;
> + bool is_mgmt = ieee80211_is_mgmt(fc);
> bool bmc;
>
> if (sta) {
> @@ -426,7 +625,14 @@ void rtw_tx_pkt_info_update(struct rtw_dev *rtwdev,
> pkt_info->mac_id = rtwvif->mac_id;
> }
>
> - if (ieee80211_is_mgmt(fc) || ieee80211_is_any_nullfunc(fc))
> + /* The vendor 8723BS SDIO path sends management frames through the
> + * shared BCMC station context (macid 1 in station mode). Data frames
> + * keep the normal peer/vif macid.
> + */
> + if (rtw_is_8723bs_sdio(rtwdev) && is_mgmt)
> + pkt_info->mac_id = 1;
I think it should still use si/vif->macid, or you should allocate another mac_id
for a station (but I don't think it is actually needed).
> +
> + if (is_mgmt || ieee80211_is_any_nullfunc(fc))
> rtw_tx_mgmt_pkt_info_update(rtwdev, pkt_info, sta, skb);
> else if (ieee80211_is_data(fc))
> rtw_tx_data_pkt_info_update(rtwdev, pkt_info, sta, skb);
> @@ -434,7 +640,12 @@ void rtw_tx_pkt_info_update(struct rtw_dev *rtwdev,
> bmc = is_broadcast_ether_addr(hdr->addr1) ||
> is_multicast_ether_addr(hdr->addr1);
>
> - if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
> + /* 8723BS SDIO management frames carry the vendor SPE_RPT/sn=0 contract
> + * set above; skip rtw_tx_report_enable() so the sn stays 0. Data
> + * frames still use the normal CCX TX-report path.
> + */
> + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS &&
> + !(rtw_is_8723bs_sdio(rtwdev) && is_mgmt))
> rtw_tx_report_enable(rtwdev, pkt_info);
>
> pkt_info->bmc = bmc;
> @@ -442,7 +653,7 @@ void rtw_tx_pkt_info_update(struct rtw_dev *rtwdev,
> pkt_info->tx_pkt_size = skb->len;
> pkt_info->offset = chip->tx_pkt_desc_sz;
> pkt_info->qsel = skb->priority;
> - pkt_info->ls = true;
> + pkt_info->ls = !(rtw_is_8723bs_sdio(rtwdev) && is_mgmt);
>
> /* maybe merge with tx status ? */
> rtw_tx_stats(rtwdev, vif, skb);
> diff --git a/drivers/net/wireless/realtek/rtw88/tx.h b/drivers/net/wireless/realtek/rtw88/tx.h
> index d34cdeca16f1..d7e1fa83bbae 100644
> --- a/drivers/net/wireless/realtek/rtw88/tx.h
> +++ b/drivers/net/wireless/realtek/rtw88/tx.h
> @@ -26,6 +26,7 @@ struct rtw_tx_desc {
> #define RTW_TX_DESC_W0_OFFSET GENMASK(23, 16)
> #define RTW_TX_DESC_W0_BMC BIT(24)
> #define RTW_TX_DESC_W0_LS BIT(26)
> +#define RTW_TX_DESC_W0_FS BIT(27)
> #define RTW_TX_DESC_W0_DISQSELSEQ BIT(31)
> #define RTW_TX_DESC_W1_MACID GENMASK(7, 0)
> #define RTW_TX_DESC_W1_QSEL GENMASK(12, 8)
> @@ -45,6 +46,8 @@ struct rtw_tx_desc {
> #define RTW_TX_DESC_W3_MAX_AGG_NUM GENMASK(21, 17)
> #define RTW_TX_DESC_W4_DATARATE GENMASK(6, 0)
> #define RTW_TX_DESC_W4_DATARATE_FB_LIMIT GENMASK(12, 8)
> +#define RTW_TX_DESC_W4_RETRY_LIMIT_EN BIT(17)
> +#define RTW_TX_DESC_W4_DATA_RETRY_LIMIT GENMASK(23, 18)
> #define RTW_TX_DESC_W4_RTSRATE GENMASK(28, 24)
> #define RTW_TX_DESC_W5_DATA_SHORT BIT(4)
> #define RTW_TX_DESC_W5_DATA_BW GENMASK(6, 5)
> @@ -99,6 +102,8 @@ void rtw_tx_fill_tx_desc(struct rtw_dev *rtwdev,
> struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb);
> void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn);
> void rtw_tx_report_handle(struct rtw_dev *rtwdev, struct sk_buff *skb, int src);
> +void rtw_tx_report_handle_8723b(struct rtw_dev *rtwdev, u8 report_type,
> + u8 *payload, u8 len);
> void rtw_tx_rsvd_page_pkt_info_update(struct rtw_dev *rtwdev,
> struct rtw_tx_pkt_info *pkt_info,
> struct sk_buff *skb,
> --
> 2.55.0
I will continue to review this RFC later.
Since this patch contains three stuffs at least, can you split the stuffs
into smaller patches?
I can't always have a long free time to review whole things.
^ permalink raw reply
* Re: [PATCH v12] PCI: Add device-specific reset for Qualcomm devices
From: Jose Ignacio Tornos Martinez @ 2026-07-20 6:24 UTC (permalink / raw)
To: bhelgaas, alex, mani
Cc: jtornosm, ath11k, ath12k, jjohnson, linux-kernel, linux-pci,
linux-wireless, mhi
In-Reply-To: <20260630065815.199693-1-jtornosm@redhat.com>
Hi Bjorn,
Gentle ping on this patch. It has Mani's Reviewed-by and has
been tested over 100+ VM reset cycles for both WLAN and modem
devices.
This patch adds device-specific reset for Qualcomm PCIe devices
(WCN6855, WCN7850 WLAN and SDX62/SDX65 modems) that lack working
reset methods for VFIO passthrough. The reset uses BAR-space
hardware registers, replicating the sequences from ath11k/ath12k
and MHI drivers. Without this, these devices cannot be reused
after an unclean VM termination.
The approach follows Alex's guidance through the series:
device-specific reset via pci_dev_reset_methods[] (suggested
in v5), and BAR-space hardware reset registers instead of
D3hot power cycling (refined with Mani from v10 onwards).
The quirk_no_bus_reset part was already merged for v7.2, and
this device-specific reset completes the solution.
Is there anything else needed for this to be considered?
Thanks,
Jose Ignacio
^ permalink raw reply
* Re: [PATCH v2] wifi: mt76: mt7996: remove beacon_int_min_gcd from ADHOC interface combinations
From: Jose Ignacio Tornos Martinez @ 2026-07-20 6:04 UTC (permalink / raw)
To: nbd, lorenzo; +Cc: linux-wireless, ryder.lee, shayne.chen
In-Reply-To: <20260702104337.679536-1-jtornosm@redhat.com>
Hi Felix, Lorenzo,
Just a gentle ping on this patch when you have a chance to take a look.
It fixes a device registration failure (-EINVAL) for mt7996/mt7992 devices
introduced by commit 5ef0e8e2653b. Without this fix the device is completely
unusable, as cfg80211 validates all advertised interface combinations at
registration time and rejects the entire driver.
Please let me know if there is anything I should improve or change.
Thank you for your time
Best regards,
Jose Ignacio
^ permalink raw reply
* Re: [PATCH v3] wifi: ath12k: fix survey indexing across bands
From: Baochen Qiang @ 2026-07-20 5:58 UTC (permalink / raw)
To: Matthew Leach, Jeff Johnson, Vasanthakumar Thiagarajan,
Baochen Qiang, Ramya Gnanasekar, Karthikeyan Periyasamy
Cc: Kalle Valo, Pradeep Kumar Chitrapu, P Praneesh, Sriram R,
linux-wireless, ath12k, linux-kernel, kernel,
Rameshkumar Sundaram, Nicolas Escande, Rameshkumar Sundaram,
Jeff Johnson
In-Reply-To: <20260703-ath12-survey-band-fix-v3-1-2fb050c2505a@collabora.com>
On 7/3/2026 11:56 PM, Matthew Leach wrote:
> When running 'iw dev wlan0 survey dump' the values for the channel busy
> time have the same sequence across bands. This is caused by indexing
> into the ath12k survey array using a band-local index rather than the
> global index passed by mac80211. This results in surveys for 5 GHz and 6
> GHz channels returning values from 2.4 GHz slots, making the survey
> unusable on those bands. Further, there are redundant survey slots for
> multi-radio/single-phy instances.
>
> Fix by moving the survey data into ath12k_hw so multiple radios under a
> single wiphy share one table, and index into it using the global
> mac80211 index. A new spinlock in ath12k_hw serialises access to the
> survey array, which is now shared across all radios under a single hw.
>
> Band busy-times Before this fix:
>
> 2.4 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
> 5 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
> 6 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
>
> After this fix, times are independent:
>
> 2.4 GHz: 23, 5, 5, 12, 2, 12, 26, 5, 3, 1, 27
> 5 GHz: 30, 40, 29, 27, 118, 118, 112, 120, 11, 11, 11
> 6 GHz: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
>
> Tested-on: wcn7850 hw2.0 PCI WLAN.IOE_HMT.1.1-00018-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1
>
> Fixes: 4f242b1d6996 ("wifi: ath12k: support get_survey mac op for single wiphy")
> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH] wifi: ath12k: restore country code during resume
From: Baochen Qiang @ 2026-07-20 5:00 UTC (permalink / raw)
To: Stian Knudsen, Jeff Johnson, ath12k; +Cc: linux-wireless
In-Reply-To: <20260717130254.36732-1-stian@pokerfj.es>
On 7/17/2026 9:02 PM, Stian Knudsen wrote:
> The country code configured before suspend is lost after resume:
> the device is powered down in suspend_late and powered back up in
> resume_early, so firmware reboots with its default regulatory
> settings and the previously set country code is no longer applied.
>
> On WCN7850 the firmware comes back in the world regulatory domain
> (country 00) and takes ~7 seconds to rediscover the country code
> from AP beacons, part of a ~16 second total delay before wifi
> reconnects after resume.
>
> Restore it by resending WMI_SET_CURRENT_COUNTRY_CMDID during resume
> if a country code was set before suspend, i.e. when ar->alpha2 is
> valid. This follows the same approach as ath11k commit 7f0343b7b871
> ("wifi: ath11k: restore country code during resume").
>
> Note that only single_pdev_only devices support suspend/resume (see
> ath12k_core_continue_suspend_resume()), so handling the first and
> only pdev is sufficient.
>
> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
>
Fixes: 8d5f4da8d70b ("wifi: ath12k: support suspend/resume") ?
> Suggested-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
> Link: https://lore.kernel.org/r/-_iDJ_M5RrqACWB0qmtleg@pokerfj.es
> Signed-off-by: Stian Knudsen <stian@pokerfj.es>
> ---
> drivers/net/wireless/ath/ath12k/core.c | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
> --- a/drivers/net/wireless/ath/ath12k/core.c
> +++ b/drivers/net/wireless/ath/ath12k/core.c
> @@ -197,6 +197,7 @@ EXPORT_SYMBOL(ath12k_core_resume_early);
>
> int ath12k_core_resume(struct ath12k_base *ab)
> {
> + struct ath12k *ar;
> long time_left;
> int ret;
>
> @@ -211,6 +212,26 @@ int ath12k_core_resume(struct ath12k_base *ab)
> return -ETIMEDOUT;
> }
>
> + /* So far only single_pdev_only devices can reach here,
> + * so it is valid to handle the first, and the only, pdev.
> + */
> + ar = ab->pdevs[0].ar;
> + if (ab->hw_params->current_cc_support &&
> + ar->alpha2[0] != 0 && ar->alpha2[1] != 0) {
> + struct wmi_set_current_country_arg arg = {};
> +
> + memcpy(&arg.alpha2, ar->alpha2, 2);
> +
> + reinit_completion(&ar->regd_update_completed);
> +
> + ret = ath12k_wmi_send_set_current_country_cmd(ar, &arg);
> + if (ret) {
> + ath12k_warn(ab, "failed to set country code during resume: %d\n",
> + ret);
> + return ret;
> + }
> + }
> +
> return 0;
> }
> EXPORT_SYMBOL(ath12k_core_resume);
> --
> 2.50.0
^ permalink raw reply
* Re: [PATCH wireless-next v2] wifi: cfg80211: fix regulatory.db async firmware request blocking __usermodehelper_disable()
From: Kavita Kavita @ 2026-07-20 4:56 UTC (permalink / raw)
To: johannes; +Cc: linux-wireless
In-Reply-To: <20260629063159.3288957-1-kavita.kavita@oss.qualcomm.com>
Gentle Reminder!!
On 6/29/2026 12:01 PM, Kavita Kavita wrote:
> cfg80211 schedules an asynchronous request_firmware() work item for
> regulatory.db via request_firmware_work_func(). When the direct
> firmware load fails, _request_firmware() falls back to the sysfs
> fallback path via firmware_fallback_sysfs(), which blocks indefinitely
> in wait_for_completion_killable_timeout() waiting for userspace to
> supply the firmware through the sysfs interface.
>
> While this work item is pending, any caller of
> __usermodehelper_disable() will deadlock attempting to acquire the
> usermodehelper rwsem for writing, since the sysfs firmware fallback
> path holds the rwsem for reading and is blocked waiting for userspace
> response that can never arrive while usermode helpers are being
> disabled.
>
> Observed call traces where system suspend blocked due to regulatory.db
> async firmware request:
>
> kworker/6:3 (pid 194) holding usermodehelper rwsem read lock, blocked
> waiting for userspace firmware response:
> wait_for_completion_killable_timeout+0x48
> firmware_fallback_sysfs+0x270
> _request_firmware+0x790
> request_firmware_work_func+0x44
> process_one_work[jt]+0x59c
> worker_thread+0x260
> kthread+0x150
> ret_from_fork+0x10
>
> Caller blocked in __usermodehelper_disable() during system suspend:
> rwsem_down_write_slowpath+0x768
> down_write+0x98
> __usermodehelper_disable+0x3c
> freeze_processes+0x18
> pm_suspend+0x320
> state_store+0x104
> kernfs_fop_write_iter[jt]+0x168
> vfs_write+0x270
> ksys_write+0x78
>
> Any service or kernel subsystem invoking __usermodehelper_disable() is
> susceptible to this hang as long as the regulatory.db sysfs fallback
> request remains outstanding.
>
> Fix this by replacing the unconditional uevent-based load with a
> two-step approach. First, attempt a synchronous load via
> request_firmware_direct() at init time. This is fast and
> non-blocking, if the file is present in standard paths it is loaded
> immediately with no delay. If not found, the load is deferred to
> wiphy_regulatory_register() and triggered via
> firmware_request_nowait_nowarn() only when the first non-self-managed
> wiphy registers.
>
> For self-managed drivers (REGULATORY_WIPHY_SELF_MANAGED), the hang is
> avoided because wiphy_regulatory_register() handles them separately
> and the deferred load path is never reached, so the file load is not
> attempted at all. For this case, regulatory information is obtained
> from driver/firmware during wiphy registration. For non-self-managed
> drivers, the file is required and is expected to be present. The
> deferred load via firmware_request_nowait_nowarn() is triggered only
> when the first such wiphy registers. This ensures the database is
> loaded only when it is actually needed, avoiding the sysfs fallback
> path on systems where the file is absent at init time.
>
> Also refactor regdb_fw_cb() into two functions: regdb_load() which
> validates and stores the firmware image, and regdb_fw_cb_restore()
> which is the async callback that calls restore_regulatory_settings()
> to replay all pending regulatory requests (country hints from core,
> user, driver and country IE) that arrived while the database was not
> yet available.
>
> NOTE:
> This issue was observed on Android platforms where regulatory.db is
> absent.
> Steps to reproduce on Android platforms:
> 1. Power off the device completely.
> 2. Connect the charger; the device enters off-mode charging.
> 3. While in off-mode charging, short press the power key.
>
> Fixes: 007f6c5e6eb4 ("cfg80211: support loading regulatory database as firmware file")
> Signed-off-by: Kavita Kavita <kavita.kavita@oss.qualcomm.com>
> ---
> Changes in v2:
> - Added Fixes Tag in commit.
> ---
> net/wireless/reg.c | 132 ++++++++++++++++++++++++++-------------------
> 1 file changed, 78 insertions(+), 54 deletions(-)
>
> diff --git a/net/wireless/reg.c b/net/wireless/reg.c
> index 1e8214d6b6d8..d7b864d32ba1 100644
> --- a/net/wireless/reg.c
> +++ b/net/wireless/reg.c
> @@ -988,78 +988,98 @@ static int query_regdb(const char *alpha2)
> return -ENODATA;
> }
>
> -static void regdb_fw_cb(const struct firmware *fw, void *context)
> +MODULE_FIRMWARE("regulatory.db");
> +
> +/* Validate and store firmware image as regdb. Used by all load paths. */
> +static int regdb_load(const struct firmware *fw)
> {
> - int set_error = 0;
> - bool restore = true;
> void *db;
>
> - if (!fw) {
> - pr_info("failed to load regulatory.db\n");
> - set_error = -ENODATA;
> - } else if (!valid_regdb(fw->data, fw->size)) {
> + ASSERT_RTNL();
> +
> + if (!valid_regdb(fw->data, fw->size)) {
> pr_info("loaded regulatory.db is malformed or signature is missing/invalid\n");
> - set_error = -EINVAL;
> + return -EINVAL;
> }
>
> + db = kmemdup(fw->data, fw->size, GFP_KERNEL);
> + if (!db)
> + return -ENOMEM;
> +
> + regdb = db;
> + return 0;
> +}
> +
> +static void regdb_fw_cb_restore(const struct firmware *fw, void *context)
> +{
> + int err;
> +
> rtnl_lock();
> - if (regdb && !IS_ERR(regdb)) {
> - /* negative case - a bug
> - * positive case - can happen due to race in case of multiple cb's in
> - * queue, due to usage of asynchronous callback
> - *
> - * Either case, just restore and free new db.
> - */
> - } else if (set_error) {
> - regdb = ERR_PTR(set_error);
> - } else if (fw) {
> - db = kmemdup(fw->data, fw->size, GFP_KERNEL);
> - if (db) {
> - regdb = db;
> - restore = context && query_regdb(context);
> - } else {
> - restore = true;
> - }
> - }
>
> - if (restore)
> + /* Skip if a concurrent wiphy registration already loaded the db. */
> + if (regdb && !IS_ERR(regdb))
> + goto out_unlock;
> +
> + /*
> + * Replay all pending regulatory hints that arrived while the
> + * database was not yet available, regardless of load outcome.
> + */
> + if (!fw) {
> + pr_info("failed to load regulatory.db\n");
> + regdb = ERR_PTR(-ENODATA);
> restore_regulatory_settings(true, false);
> + goto out_unlock;
> + }
>
> - rtnl_unlock();
> + err = regdb_load(fw);
> + if (err)
> + regdb = ERR_PTR(err);
>
> - kfree(context);
> + restore_regulatory_settings(true, false);
>
> +out_unlock:
> + rtnl_unlock();
> release_firmware(fw);
> }
>
> -MODULE_FIRMWARE("regulatory.db");
> -
> static int query_regdb_file(const char *alpha2)
> {
> + const struct firmware *fw;
> int err;
>
> ASSERT_RTNL();
>
> - if (regdb)
> + if (regdb && !IS_ERR(regdb))
> return query_regdb(alpha2);
>
> - alpha2 = kmemdup(alpha2, 2, GFP_KERNEL);
> - if (!alpha2)
> - return -ENOMEM;
> + /*
> + * Load failed or async udev load in progress. If -EINPROGRESS,
> + * hints are preserved and replayed once the udev load completes.
> + */
> + if (IS_ERR(regdb) && PTR_ERR(regdb) != -EINPROGRESS)
> + return PTR_ERR(regdb);
>
> - err = request_firmware_nowait(THIS_MODULE, true, "regulatory.db",
> - ®_fdev->dev, GFP_KERNEL,
> - (void *)alpha2, regdb_fw_cb);
> + /*
> + * Preserve the hint if the file is not found on direct paths;
> + * an async udev load will be triggered on wiphy registration
> + * and will replay all pending hints on completion.
> + */
> + err = request_firmware_direct(&fw, "regulatory.db", ®_fdev->dev);
> if (err)
> - kfree(alpha2);
> + return 0;
> + err = regdb_load(fw);
> + release_firmware(fw);
> + if (err) {
> + regdb = ERR_PTR(err);
> + return err;
> + }
>
> - return err;
> + return query_regdb(alpha2);
> }
>
> int reg_reload_regdb(void)
> {
> const struct firmware *fw;
> - void *db;
> int err;
> const struct ieee80211_regdomain *current_regdomain;
> struct regulatory_request *request;
> @@ -1068,21 +1088,14 @@ int reg_reload_regdb(void)
> if (err)
> return err;
>
> - if (!valid_regdb(fw->data, fw->size)) {
> - err = -ENODATA;
> - goto out;
> - }
> -
> - db = kmemdup(fw->data, fw->size, GFP_KERNEL);
> - if (!db) {
> - err = -ENOMEM;
> - goto out;
> - }
> -
> rtnl_lock();
> if (!IS_ERR_OR_NULL(regdb))
> kfree(regdb);
> - regdb = db;
> + err = regdb_load(fw);
> + if (err) {
> + regdb = ERR_PTR(err);
> + goto out_unlock;
> + }
>
> /* reset regulatory domain */
> current_regdomain = get_cfg80211_regdom();
> @@ -1103,7 +1116,6 @@ int reg_reload_regdb(void)
>
> out_unlock:
> rtnl_unlock();
> - out:
> release_firmware(fw);
> return err;
> }
> @@ -4085,6 +4097,8 @@ void wiphy_regulatory_register(struct wiphy *wiphy)
> {
> struct regulatory_request *lr = get_last_request();
>
> + ASSERT_RTNL();
> +
> /* self-managed devices ignore beacon hints and country IE */
> if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
> wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
> @@ -4097,6 +4111,16 @@ void wiphy_regulatory_register(struct wiphy *wiphy)
> */
> if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
> reg_call_notifier(wiphy, lr);
> + } else if (!regdb) {
> + /*
> + * regulatory.db not yet loaded; trigger an async udev
> + * request to load it when the first wiphy registers.
> + */
> + if (!firmware_request_nowait_nowarn(THIS_MODULE,
> + "regulatory.db",
> + ®_fdev->dev, GFP_KERNEL,
> + NULL, regdb_fw_cb_restore))
> + regdb = ERR_PTR(-EINPROGRESS);
> }
>
> if (!reg_dev_ignore_cell_hint(wiphy))
>
> base-commit: 972c4dd19cb92e03d75b66c426cfade07582a1ba
^ permalink raw reply
* Re: [PATCH ath-next] wifi: ath12k: Avoid buffer overread in ath12k_wmi_op_rx()
From: Rameshkumar Sundaram @ 2026-07-20 3:43 UTC (permalink / raw)
To: Jeff Johnson, Jeff Johnson; +Cc: linux-wireless, ath12k, linux-kernel
In-Reply-To: <20260716-ath12k_wmi_op_rx-overread-v1-1-327a4b1c2372@oss.qualcomm.com>
On 7/17/2026 1:31 AM, Jeff Johnson wrote:
> Currently, in ath12k_wmi_op_rx(), the firmware buffer is read without
> first verifying that the buffer has enough data to hold a header. This
> could result in a buffer overread.
>
> Update the logic to verify the buffer contains at least enough data to
> hold a wmi_cmd_hdr before reading from the buffer.
>
> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
>
> Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
> Assisted-by: Claude:claude-sonnet-4-6
> Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
^ permalink raw reply
* RE: [RFC PATCH v1 1/9] wifi: rtw88: add RTL8723B chip support
From: Ping-Ke Shih @ 2026-07-20 3:00 UTC (permalink / raw)
To: luka.gejak@linux.dev, linux-wireless@vger.kernel.org
Cc: straube.linux@gmail.com
In-Reply-To: <db819d2ac8f282b1fd081bd21f316274a69dc1ce.1784047561.git.luka.gejak@linux.dev>
Please find my comments, since this patch is quite large.
luka.gejak@linux.dev <luka.gejak@linux.dev> wrote:
> From: Luka Gejak <luka.gejak@linux.dev>
>
> Add support for the Realtek RTL8723B 802.11n chip and its RTL8723BS SDIO
> variant: the chip driver, the BB/RF/AGC parameter tables, the SDIO bind,
> and the core plumbing they depend on - the RTW_CHIP_TYPE_8723B chip type,
> the register and security-config definitions, an rtw_is_8723bs_sdio()
> helper, and the per-vif/per-station state used by the later patches.
>
> Subsequent patches build the firmware interface, TX handling, WiFi/BT
> coexistence, power management and the SDIO association sequence on top of
> this foundation.
>
> The RTL8723B chip support is based on the initial work by
> Michael Straube <straube.linux@gmail.com>.
Can you share the Link?
Patch subject can explicitly point out 8723b, like "wifi: rtw88: 8723b: ..."
>
> Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
> ---
> drivers/net/wireless/realtek/rtw88/Kconfig | 18 +
> drivers/net/wireless/realtek/rtw88/Makefile | 6 +
Before other preparations in tree, RTL8723BS will not work. So I'd
move these two updates to the last patch (before the MAINTAINERS patch)
of this patchset.
> drivers/net/wireless/realtek/rtw88/main.h | 35 +
> drivers/net/wireless/realtek/rtw88/reg.h | 6 +
> drivers/net/wireless/realtek/rtw88/rtw8723b.c | 3382 +++++++++++++++++
> drivers/net/wireless/realtek/rtw88/rtw8723b.h | 16 +
> .../wireless/realtek/rtw88/rtw8723b_table.c | 858 +++++
> .../wireless/realtek/rtw88/rtw8723b_table.h | 15 +
> .../net/wireless/realtek/rtw88/rtw8723bs.c | 36 +
> drivers/net/wireless/realtek/rtw88/sec.h | 1 +
> 10 files changed, 4373 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
>
> diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig
> b/drivers/net/wireless/realtek/rtw88/Kconfig
> index 3736f290bd42..e1f40d6785e6 100644
> --- a/drivers/net/wireless/realtek/rtw88/Kconfig
> +++ b/drivers/net/wireless/realtek/rtw88/Kconfig
> @@ -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
> diff --git a/drivers/net/wireless/realtek/rtw88/Makefile
> b/drivers/net/wireless/realtek/rtw88/Makefile
> index 0b3da05a2938..6889b3796449 100644
> --- a/drivers/net/wireless/realtek/rtw88/Makefile
> +++ b/drivers/net/wireless/realtek/rtw88/Makefile
> @@ -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
>
> diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
> index c6e981ba7986..4a43ec13625e 100644
> --- a/drivers/net/wireless/realtek/rtw88/main.h
> +++ b/drivers/net/wireless/realtek/rtw88/main.h
> @@ -194,6 +194,7 @@ enum rtw_chip_type {
> RTW_CHIP_TYPE_8723D,
> RTW_CHIP_TYPE_8821C,
> RTW_CHIP_TYPE_8703B,
> + RTW_CHIP_TYPE_8723B,
> RTW_CHIP_TYPE_8821A,
> RTW_CHIP_TYPE_8812A,
> RTW_CHIP_TYPE_8814A,
> @@ -369,6 +370,7 @@ enum rtw_flags {
> RTW_FLAG_FW_RUNNING,
> RTW_FLAG_SCANNING,
> RTW_FLAG_POWERON,
> + RTW_FLAG_SOFT_IPS,
Add this flag until the patch actually uses it. That will be easier to review.
> RTW_FLAG_LEISURE_PS,
> RTW_FLAG_LEISURE_PS_DEEP,
> RTW_FLAG_DIG_DISABLE,
> @@ -616,6 +618,7 @@ struct rtw_tx_pkt_info {
> u8 bw;
> u8 sec_type;
> u8 sn;
> + u8 data_retry_limit;
ditto. This patch doesn't use it yet.
> bool ampdu_en;
> u8 ampdu_factor;
> u8 ampdu_density;
> @@ -623,6 +626,8 @@ struct rtw_tx_pkt_info {
> bool stbc;
> bool ldpc;
> bool dis_rate_fallback;
> + bool retry_limit_en;
> + bool disable_data_rate_fb_limit;
ditto. This patch doesn't use them yet.
> bool bmc;
> bool use_rate;
> bool ls;
> @@ -781,6 +786,7 @@ struct rtw_sta_info {
> bool vht_enable;
> u8 init_ra_lv;
> u64 ra_mask;
> + u64 ra_mask_last; /* 8723BS SDIO: last mask sent, to gate no_update */
ditto. This patch doesn't use it yet.
>
> DECLARE_BITMAP(tid_ba, IEEE80211_NUM_TIDS);
>
> @@ -828,6 +834,10 @@ struct rtw_vif {
> u8 bssid[ETH_ALEN];
> u8 port;
> u8 bcn_ctrl;
> + /* 8723BS SDIO join-state tracking (see mac80211.c mgd_prepare_tx path) */
> + bool fw_media_connected;
> + bool pre_auth_h2c_sent;
> + bool pre_auth_join_done;
ditto. This patch doesn't use them yet.
> struct list_head rsvd_page_list;
> struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS];
> const struct rtw_vif_port *conf;
> @@ -2057,6 +2067,18 @@ struct rtw_hw_scan_info {
> u8 op_bw;
> };
>
> +/* 8723BS SDIO: synchronises the pre-auth wait on a beacon/probe-resp from the
> + * target BSSID, so the join sequence mirrors the vendor start_clnt_join().
> + */
> +struct rtw_auth_sync {
> + wait_queue_head_t wait;
> + spinlock_t lock;
> + u8 bssid[ETH_ALEN];
> + bool active;
> + bool seen;
> + u32 seen_count;
> +};
Move this to the patch you implement this function.
But I wonder why we need to implement this in driver?
> +
> struct rtw_dev {
> struct ieee80211_hw *hw;
> struct device *dev;
> @@ -2130,9 +2152,12 @@ struct rtw_dev {
> struct rtw_wow_param wow;
>
> bool need_rfk;
> + bool initial_rfk_done; /* 8723BS SDIO: run IQK once, not per IPS-leave */
ditto. This patch doesn't use it yet.
More, you doesn't enter IPS. How does it leave IPS causing it power-on again?
> struct completion fw_scan_density;
> bool ap_active;
>
> + struct rtw_auth_sync auth_sync;
> +
> bool led_registered;
> char led_name[32];
> struct led_classdev led_cdev;
> @@ -2194,6 +2219,12 @@ static inline bool rtw_chip_has_tx_stbc(struct rtw_dev *rtwdev)
> return rtwdev->chip->tx_stbc;
> }
>
> +static inline bool rtw_is_8723bs_sdio(struct rtw_dev *rtwdev)
Just rtw_is_8723bs(). The tail 's' means it is 'sdio' already, no?
or rtw_is_8723b_sdio()? if you prefere.
> +{
> + return rtwdev->chip->id == RTW_CHIP_TYPE_8723B &&
> + rtwdev->hci.type == RTW_HCI_TYPE_SDIO;
> +}
> +
> static inline u8 rtw_acquire_macid(struct rtw_dev *rtwdev)
> {
> unsigned long mac_id;
> @@ -2284,4 +2315,8 @@ bool rtw_core_check_sta_active(struct rtw_dev *rtwdev);
> void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable);
> void rtw_set_ampdu_factor(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
> struct ieee80211_bss_conf *bss_conf);
> +void rtw8723bs_auth_sync_rx(struct rtw_dev *rtwdev,
> + const struct ieee80211_hdr *hdr, u32 len,
> + const struct rtw_rx_pkt_stat *pkt_stat,
> + const struct ieee80211_rx_status *rx_status);
Move to the patch you add the function.
> #endif
> diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
> index 08e9494977e0..690767bf5204 100644
> --- a/drivers/net/wireless/realtek/rtw88/reg.h
> +++ b/drivers/net/wireless/realtek/rtw88/reg.h
> @@ -437,6 +437,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)
> @@ -479,6 +481,7 @@
> #define BIT_DIS_TSF_UDT BIT(4)
> #define BIT_EN_BCN_FUNCTION BIT(3)
> #define BIT_EN_TXBCN_RPT BIT(2)
> +#define BIT_DIS_ATIM BIT(0)
> #define REG_BCN_CTRL_CLINT0 0x0551
> #define REG_DRVERLYINT 0x0558
> #define REG_BCNDMATIM 0x0559
> @@ -495,6 +498,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)
> @@ -518,6 +523,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)
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723b.c
> b/drivers/net/wireless/realtek/rtw88/rtw8723b.c
> new file mode 100644
> index 000000000000..26a2008f747a
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8723b.c
> @@ -0,0 +1,3382 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
Copyright(c) Realtek Corporation ?
> +/* Copyright(c) Michael Straube <straube.linux@gmail.com> */
> +/* Copyright(c) 2024-2026 Luka Gejak <luka.gejak@linux.dev> */
> +
> +#include "main.h"
> +#include "coex.h"
> +#include "fw.h"
> +#include "phy.h"
> +#include "mac.h"
> +#include "sdio.h"
> +#include "rtw8723b.h"
> +#include "tx.h"
> +#include "rtw8723b_table.h"
> +#include <linux/unaligned.h>
> +/* for struct phy_status_8703b */
> +#include "rtw8703b.h"
Keep them in alphabetic order. And kernel header first.
> +
> +#define TRANS_SEQ_END \
> + 0xFFFF, \
> + RTW_PWR_CUT_ALL_MSK, \
> + RTW_PWR_INTF_ALL_MSK, \
> + 0, \
> + RTW_PWR_CMD_END, 0, 0
> +
> +#define BIT_FEN_PPLL BIT(7)
> +#define BIT_FEN_DIO_PCIE BIT(5)
> +
> +#define TBTT_PROHIBIT_SETUP_TIME 0x04
> +#define TBTT_PROHIBIT_HOLD_TIME_STOP_BCN 0x64
> +#define WLAN_BCN_DMA_TIME 0x02
> +#define WLAN_ANT_SEL 0x82
> +#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)
> +/* Staging and the BB table use 0x03a05611 as the normal RX path. 0x03a05600
> + * is only an IQK temporary value and must not be reasserted during scan.
> + */
> +#define WLAN_RX_PATH_A_8723B 0x03a05611
> +
> +#define ADDA_ON_VAL_8723B 0x01c00014
> +
> +#define MASK_NETTYPE 0x30000
> +#define _NETTYPE(x) (((x) & 0x3) << 16)
> +#define NT_LINK_AP 0x2
> +
> +#define WLAN_RX_FILTER0 0xFFFF
> +#define WLAN_RX_FILTER1 0x400
> +#define WLAN_RX_FILTER2 0xFFFF
> +#define WLAN_RCR_CFG (0x700060CE | BIT_AMF)
> +#define REG_RFK_FW_ACK_8723B 0x01e7
> +#define BIT_RFK_FW_ACK_8723B BIT(0)
> +
> +#define REG_FPGA0_XA_RF_SW_CTRL 0x0870
> +#define REG_FPGA0_XA_RF_INT_OE 0x0860
> +#define REG_FPGA0_XA_HSSI_PARM2 0x0824
> +
> +#define REG_FPGA0_XB_RF_SW_CTRL 0x0872
> +#define REG_FPGA0_XB_RF_INT_OE 0x0864
> +#define REG_FPGA0_XB_HSSI_PARM2 0x082c
> +
> +#define RFSI_RFENV 0x10
> +#define HSSI_3WIRE_ADDR_LEN 0x400
> +#define HSSI_3WIRE_DATA_LEN 0x800
> +
> +#define BIT_EN_PDN BIT(4)
> +
> +#define REG_CAM_CMD 0x0670
> +#define CAM_CMD_POLLING BIT(31)
> +
> +#define REG_PKT_VO_VI_LIFE_TIME 0x04C0
> +#define REG_PKT_BE_BK_LIFE_TIME 0x04C2
> +
> +#define REG_BT_CONTROL_8723B 0x0764
> +#define REG_PWR_DATA 0x0038
> +#define REG_BT_COEX_CTRL_8723B 0x0039
> +#define REG_ANTSEL_SW_8723B 0x0064
> +#define REG_BT_ANT_SEL_8723B 0x0067
> +#define REG_BT_GNT_BT_8723B 0x0765
> +#define REG_BT_WLAN_ACT_8723B 0x076e
> +#define REG_BB_ANT_CFG_8723B 0x0930
> +#define REG_BB_ANT_CFG1_8723B 0x0944
> +#define REG_BB_ANT_BUF_8723B 0x0974
> +
> +#define BIT_BT_SEL_BY_WIFI_8723B BIT(5)
> +
> +#define RF_RCK_OS 0x30
> +#define RF_TXPA_G1 0x31
> +#define RF_TXPA_G2 0x32
> +#define IQK_DELAY_TIME_8723B 20
> +
> +#define REG_B_RXIQI 0x0c1c
> +
> +#define REG_NAV_UPPER 0x0652
> +/* REG_EARLY_MODE_CONTROL for 8723B is now in reg.h */
> +
> +/* local page-layout constants (no rtw88 equivalents exist); used below */
> +#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)
> +
> +/* local TXPKTBUF boundary regs (no rtw88 equivalents exist); used below */
> +#define REG_TXPKTBUF_BCNQ_BDNY_8723B 0x0424
> +#define REG_TXPKTBUF_MGQ_BDNY_8723B 0x0425
> +#define REG_TXPKTBUF_WMAC_LBK_BF_HD_8723B 0x045D
> +#define REG_TRXFF_BNDY 0x0114
> +#define REG_TDECTRL 0x0208
Move these registers (from BIT_FEN_PPLL) to reg.h.
> +
> +/* rssi in percentage % (dbm = % - 100) */
> +/* These are used to select simple signal quality levels, might need
> + * tweaking. Same for rf_para tables below.
> + */
We don't use networking specific comment style anymore.
Current style is
/*
* first line
* ...
* last line
*/
Follow this style across whole patchset.
> +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));
> +
> +/* taken from vendor file hal/phydm/halrf/halrf_powertracking_ce.c
> + * ofdm_swing_table_new
> + */
No need to mention the place you are copying from.
> +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 */
> +};
> +
> +/* adapted from vendor file hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: set_cck_filter_coefficient
> + */
> +static const u32 rtw8723b_cck_pwr_regs[] = {
> + 0x0a22, 0x0a23, 0x0a24, 0x0a25, 0x0a26, 0x0a27, 0x0a28, 0x0a29,
> +};
> +
> +/* taken from vendor file hal/phydm/halrf/halrf_powertracking_ce.c
> + * cck_swing_table_ch1_ch13_new
> + */
> +static const u8 rtw8732b_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 */
> +};
> +
> +/* taken from vendor file hal/phydm/halrf/halrf_powertracking_ce.c
> + * cck_swing_table_ch14_new
> + */
> +static const u8 rtw8732b_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(rtw8732b_cck_swing_table_ch1_ch13) ==
> + ARRAY_SIZE(rtw8732b_cck_swing_table_ch14));
> +
> +#define RTW_OFDM_SWING_TABLE_SIZE ARRAY_SIZE(rtw8723b_ofdm_swing_table)
> +#define RTW_CCK_SWING_TABLE_SIZE ARRAY_SIZE(rtw8732b_cck_swing_table_ch14)
> +
> +/* see vendor functions _InitPowerOn_8723BS and CardEnable
> + */
No need.
> +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},
> +
No this empty line.
> + {TRANS_SEQ_END},
> +};
> +
> +/* transitions adapted from vendor file include/Hal8723BPwrSeq.h
> + */
Remove this kind of reference across whole patch.
> +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},
> +
no need empty line.
> + {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},
> +};
> +
> +/* adapted from vendor function CardDisableRTL8723BSdio
> + */
> +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},
> +};
> +
> +/* adapted from vendor file hal/rtl8723b/Hal8723BPwrSeq.c
> + */
> +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
> +};
> +
> +/* see vendor function CardDisableRTL8723BSdio
> + */
> +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[] = {
> + /* Matches the vendor queue split with rsvd_drv_pg_num = 8. */
> + {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: 3-out-pipe staging maps VO/MG/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},
> +};
> +
> +/* taken from vendor file hal/phydm/rtl8723b/halhwimg8723b_rf.c
> + * txpowertrack_sdio.TXT section
> + * NOTE: tables for pcie and usb slightly differ in the vendor driver
> + */
> +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
> +};
> +
> +/* ----------------------------------------------------------------------- */
not preference a separator.
> +
> +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,
> + /* used in rtw8723x_pwrtrack_set_xtal which is not done in 8723b vendor driver*/
> + .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} }
> +};
> +
> +/* vendor: hal/rtl8723b/rtl8723b_hal_init.c
> + * function: Hal_EfusePowerSwitch
> + */
> +static void rtw8723b_efuse_grant(struct rtw_dev *rtwdev, bool on)
> +{
> + /*
> + * The BT power-cut / output-isolation writes to 0x6A[14]/[15] are
> + * part of the vendor's WiFi efuse power switch (Hal_EfusePowerSwitch,
> + * both PwrState branches), not just the separate BT efuse path, so
> + * this cannot use the common __rtw8723x_efuse_grant which omits them.
> + */
> + if (on) {
> + /* enable BT power cut 0x6A[14] = 1 */
> + rtw_write8_set(rtwdev, 0x6b, BIT(6));
> +
> + rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON);
> +
> + rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_ELDR);
> + rtw_write16_set(rtwdev, REG_SYS_CLKR, BIT_LOADER_CLK_EN | BIT_ANA8M);
> + } else {
> + /* enable BT output isolation 0x6A[15] = 1 */
> + rtw_write8_set(rtwdev, 0x6b, BIT(7));
> +
> + rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF);
> + }
> +}
> +
> +/* adapted from vendor: halrf_powertracking_ce.c
> + * function: get_swing_index
> + */
> +static u8 rtw8723b_default_ofdm_index(struct rtw_dev *rtwdev)
> +{
> + u8 i;
> + u32 val32;
> + u32 swing;
> +
> + swing = rtw_read32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, 0xffc00000);
> +
> + for (i = 0; i < RTW_OFDM_SWING_TABLE_SIZE; i++) {
> + val32 = rtw8723b_ofdm_swing_table[i];
> +
> + if (val32 >= 0x100000)
> + val32 >>= 22;
> +
> + if (val32 == swing)
> + break;
> + }
> +
> + if (i >= RTW_OFDM_SWING_TABLE_SIZE)
> + i = 30;
> +
> + return i;
> +}
> +
> +/* adapted from vendor: halrf_powertracking_ce.c
> + * function: get_cck_swing_index
> + */
> +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 (rtw8732b_cck_swing_table_ch1_ch13[i][0] == swing)
> + break;
> + }
> +
> + if (i >= RTW_CCK_SWING_TABLE_SIZE)
> + i = 20;
> +
> + return i;
> +}
> +
> +/* vendor: halrf_powertracking_ce.c
> + * function: odm_txpowertracking_thermal_meter_init
> + */
> +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);
> +
> + /* thermal/power-track init is identical to rtw8723d dm_init */
> + 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->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;
> + dm_info->txagc_remnant_ofdm[RF_PATH_A] = 0;
> +}
> +
> +static bool rtw8723b_sdio_needs_rx_path_fix(struct rtw_dev *rtwdev);
No need.
> +static void rtw8723b_sdio_restore_pad_ctrl(struct rtw_dev *rtwdev,
> + bool keep_pta_owner);
Just implement it here?
I only do this to avoid churning existing code. What is the purpose you did?
> +
> +/* adapted from: _InitPowerOn_8723BS (steps after calling cardEnable)
> + */
> +static void rtw8723b_post_enable_flow(struct rtw_dev *rtwdev)
> +{
> + u32 value32;
> +
> + /* These two are also done in card_enable_flow. */
> + rtw_write8_set(rtwdev, 0x0049, BIT(1));
> + rtw_write8_set(rtwdev, 0x0063, BIT(1));
> +
> + rtw_write16_set(rtwdev, REG_APS_FSMCO, BIT_EN_PDN);
> +
> + /* The vendor runs its CR zero-then-enable cycle at power-on, before
> + * the RQPN page split is latched. Here we are after the latch:
> + * writing CR to 0 drops the TX DMA enables and resets the hardware
> + * free-page counters to zero, leaving the TX DMA with no allocatable
> + * pages so every TX FIFO write is silently discarded. Only OR in
> + * the missing enables; the zero-then-enable cycle already ran in
> + * the queue-mapping init.
> + */
> + rtw_write16_set(rtwdev, REG_CR, MAC_TRX_ENABLE | BIT_MAC_SEC_EN |
> + BIT_32K_CAL_TMR_EN);
coding style -- BIT_32K_CAL_TMR_EN should align open parenthesis.
Please run checkkpatch.pl before sending patches.
> +
> + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) {
> + rtw_write16_set(rtwdev, REG_PWR_DATA, BIT(11));
> +
> + /* rtw_mac_power_on() sets generic PAD mux bits that the
> + * 8723BS vendor path leaves clear. Restore the SDIO PAD
> + * mux to staging's hal_init_done value before RF/coex setup.
> + */
> + rtw8723b_sdio_restore_pad_ctrl(rtwdev, false);
> +
> + /*
> + * Vendor rtl8723bs v5.2.17 driver sets BIT(12) of
> + * REG_FWHW_TXQ_CTRL under CONFIG_XMIT_ACK to enable
> + * management-frame TX ACK reporting via the C2H path.
> + * The v41 firmware expects this bit to be set for
> + * CCX TX report delivery; without it the firmware may
> + * suppress management TX report generation entirely.
> + */
If you just follow vendor driver does, I think you don't need so many comments.
Or, you can have a separate patch to add this tricky chunk?
There are many comments pointing how vendor driver does. For me, this kind of
things are not necessary unless you fix or change the vendor driver behavior.
I guess these are the notes for yourself while you are rewriting the driver.
Remove them.
> + value32 = rtw_read32(rtwdev, REG_FWHW_TXQ_CTRL);
> + value32 |= BIT(12);
> + rtw_write32(rtwdev, REG_FWHW_TXQ_CTRL, value32);
> + }
> +
> + rtw_write8(rtwdev, REG_EARLY_MODE_CONTROL, 0);
> +
> + /* Vendor headers map 0x04d0/0x04d4 to MACID packet drop/sleep
> + * masks on 8723B. Keep every MACID eligible for firmware-scheduled
> + * TX at power-on; mac80211/firmware state handles the real peer
> + * lifetime.
> + */
> + 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);
> +
> + /* Staging writes REG_RF_CTRL as a full 8-bit write (0x07), NOT
> + * as a read-modify-write. If other bits are spuriously set the
> + * OR-based write preserves them, which can leave the RF bus in
> + * an unexpected state on this stepping.
> + */
> + rtw_write8(rtwdev, REG_RF_CTRL,
> + BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB);
> + usleep_range(1000, 1100);
> + 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); /* 0xe3 */
> + 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);
> +}
> +
> +/* vendor: hal/rtl8723b/rtl8723b_rf6052.c
> + * function: PHY_RF6052_Config8723B
> + */
> +static void rtw8723b_phy_rf6052_config(struct rtw_dev *rtwdev)
> +{
> + struct rtw_hal *hal = &rtwdev->hal;
> +
no empty line.
> + u8 path;
> + u32 val32, mask;
> + u32 intf_s, intf_oe, hssi_2;
In reverse X'mas tree order.
> +
> + 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:
> + intf_s = REG_FPGA0_XB_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);
> +
> + /* NOTE: path A only, there is no table for path B
> + * vendor driver also uses radio_a table for both paths
> + */
> + rtw_load_table(rtwdev, rtwdev->chip->rf_tbl[RF_PATH_A]);
> +
> + rtw_write32_mask(rtwdev, intf_s, mask, val32);
> + }
> +
> + /* 3 Configuration of Tx Power Tracking */
> + /* NOTE: reads only pwr track tables into memory in the vendor driver,
> + * we define them directly in rtw8723b_rtw_pwr_track_tbl
> + */
> +}
> +
> +/* vendor: hal/rtl8723b/rtl8723b_phycfg.c
> + * function: PHY_RFConfig8723B
> + */
> +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);
> + mdelay(200); /* rtl8xxxu uses msleep(200) */
By the comment, what is the difference? I guess checkpatch.pl would
complain it. Maybe, just use fsleep(200 * 1000)?
> + rtw_write_rf(rtwdev, RF_PATH_A, 0xb0, RFREG_MASK, 0xdffe0);
> +}
> +
> +/* vendor: hal/rtl8723b/rtl8723b_phycfg.c
> + * function: PHY_RFConfig8723B
> + */
> +static void rtw8723b_phy_rf_config(struct rtw_dev *rtwdev)
> +{
> + rtw8723b_phy_rf6052_config(rtwdev);
> +
> + /* vendor does LCK during RF config too (phy_LCK_8723B in
> + * rtl8723b_phycfg.c), so this is required
> + */
> + rtw8723b_phy_lck(rtwdev);
> +}
> +
> +static void rtw8723b_init_available_page_threshold(struct rtw_dev *rtwdev)
> +{
> + const struct rtw_chip_info *chip = rtwdev->chip;
> + struct rtw_fifo_conf *fifo = &rtwdev->fifo;
> + const struct rtw_page_table *pg_tbl = NULL;
> + u16 hq_threshold, nq_threshold, lq_threshold;
> + u16 pubq_num;
an empty line after declarations.
> + /* Only initialize these page thresholds for SDIO devices.
> + * PCIe and USB handle TX FIFO/thresholds differently (DMA/host
> + * scheduling) and writing these registers on those buses can be
> + * unnecessary or counter-productive.
> + */
There are so many comments... Please seriously consider if they are needed
and you fully understand what they are saying.
> + if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_SDIO)
> + return;
> +
> + pg_tbl = &chip->page_table[0]; /* SDIO */
> +
> + /* fifo must be initialized before this is called */
> + if (fifo->acq_pg_num == 0)
> + return;
> +
> + /* ensure we don't underflow if tables are misconfigured */
> + if (fifo->acq_pg_num <= (pg_tbl->hq_num + pg_tbl->lq_num +
> + pg_tbl->nq_num + pg_tbl->exq_num +
> + pg_tbl->gapq_num))
> + return;
> +
> + pubq_num = fifo->acq_pg_num - pg_tbl->hq_num - pg_tbl->lq_num -
> + pg_tbl->nq_num - pg_tbl->exq_num - pg_tbl->gapq_num;
> +
> + hq_threshold = (pubq_num + pg_tbl->hq_num + 1) >> 1;
> + hq_threshold |= (hq_threshold << 8);
Just merge to single statement? as well as following two statements.
+ hq_threshold = (pubq_num + pg_tbl->hq_num + 1) >> 1 |
+ (hq_threshold << 8);
> +
> + nq_threshold = (pubq_num + pg_tbl->nq_num + 1) >> 1;
> + nq_threshold |= (nq_threshold << 8);
> +
> + lq_threshold = (pubq_num + pg_tbl->lq_num + 1) >> 1;
> + lq_threshold |= (lq_threshold << 8);
> +
> + rtw_write16(rtwdev, 0x218, hq_threshold);
> + rtw_write16(rtwdev, 0x21a, nq_threshold);
> + rtw_write16(rtwdev, 0x21c, lq_threshold);
> +}
> +
> +static void rtw8723b_init_queue_reserved_page(struct rtw_dev *rtwdev)
> +{
> + /* The reserved-page/RQPN setup is handled by
> + * mac.c:__priority_queue_cfg_legacy.
> + *
> + * The available-page thresholds (0x218/0x21a/0x21c) must NOT be
> + * programmed on 8723BS SDIO: doing so against the small SDIO page
> + * pools was one of the TX-dead root causes (the AVAL thresholds were
> + * set larger than the pool, so the MAC never released pages). Skip
> + * them here; only non-SDIO paths run the threshold init. There is no
> + * trace of this init in rtl8xxxu either.
> + */
> + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO &&
> + rtwdev->chip->id == RTW_CHIP_TYPE_8723B)
> + return;
> +
> + rtw8723b_init_available_page_threshold(rtwdev);
> +}
> +
> +static void rtw8723b_init_tx_buffer_boundary(struct rtw_dev *rtwdev)
> +{
> + u8 val8 = TX_TOTAL_PAGE_NUMBER_8723B + 1; /* 0xf7 */
> +
> + rtw_write8(rtwdev, REG_TXPKTBUF_BCNQ_BDNY_8723B, val8);
> + rtw_write8(rtwdev, REG_TXPKTBUF_MGQ_BDNY_8723B, val8);
> + rtw_write8(rtwdev, REG_TXPKTBUF_WMAC_LBK_BF_HD_8723B, val8);
> + rtw_write8(rtwdev, REG_TRXFF_BNDY, val8);
> + rtw_write8(rtwdev, REG_TDECTRL + 1, val8);
> +}
> +
> +static void rtw8723b_init_llt_table(struct rtw_dev *rtwdev)
> +{
> + /* vendor functionality handled in
> + *
> + * mac.c:__priority_queue_cfg_legacy
> + *
> + */
> +}
> +
> +static void rtw8723b_init_page_boundary(struct rtw_dev *rtwdev)
> +{
> + /* NOTE: this is also done in __priority_queue_cfg_legacy,
> + * maybe we can remove it
> + */
> + 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_network_type(struct rtw_dev *rtwdev)
> +{
> + u32 val32;
> +
> + val32 = rtw_read32(rtwdev, REG_CR);
> + val32 = (val32 & ~MASK_NETTYPE) | _NETTYPE(NT_LINK_AP);
> + rtw_write32(rtwdev, REG_CR, val32);
> +}
> +
> +static void rtw8723b_init_wmac_setting(struct rtw_dev *rtwdev)
> +{
> + /* Override the default rcr filter for 8723B */
> + 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)
> +{
> + /* REG_RRSR low 20 bits programs the BSS basic rate set the chip
> + * uses for ACK/CTS rate selection, response duration calculation,
> + * and CTS-to-self protection. The legacy rtl8723bs staging driver
> + * init leaves this at the upstream 0xffff1 value;
> + * staging_regs:hal_init_done confirms RRSR=0x000ffff1 at the end
> + * of rtl8723bs_hal_init(). The per-join narrowing to the IEEE
> + * mandatory rates (0x15F) happens later via HW_VAR_BASIC_RATE in
> + * start_clnt_join(), not at MAC init.
> + *
> + * With the staging v35 firmware loaded (rtl8723bs_nic.bin), the
> + * 8051 firmware reads REG_RRSR during init to validate the PHY
> + * rate capabilities. Forcing 0x15F (which clears MCS0-7 and
> + * higher OFDM bits) causes a mismatch with the firmware's
> + * internal rate tables and can lead to the firmware silently
> + * discarding all management TX from SDIO.
> + */
> + 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 controls how soon the chip transmits an ACK after
> + * receiving a unicast frame. The chip default for OFDM (0x0e =
> + * 14 us) leaves only ~2 us of slack inside the 16 us 802.11
> + * SIFS window once on-chip processing is included, which is
> + * enough margin for the AP's RX timer to expire before our ACK
> + * arrives on air. The legacy rtl8723bs staging driver works
> + * around this by writing 0x0a0a0808 (CCK = 0x08, OFDM = 0x0a)
> + * via HW_VAR_RESP_SIFS in update_wireless_mode() right before
> + * each client join, which is the only path that reliably gets
> + * Open Auth / Assoc replies from a real AP on this chip. Bake
> + * the same values in at MAC init so every 8723b variant ACKs
> + * AP responses fast enough to keep the unicast handshake alive.
> + */
> + 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);
> +}
> +
> +/* vendor: hal/rtl8723b/sdio/sdio_halinit.c
> + * function: _InitOperationMode
> + */
> +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)
> +{
> + /* Match staging's _InitBeaconParameters() for 8723b: program
> + * port-0 and port-1 BCN_CTRL with DIS_TSF_UDT | EN_BCN_FUNCTION
> + * only.
> + *
> + * eeffbe2 added DIS_BCNQ_SUB here on the (incorrect) assumption
> + * that _InitBeaconParameters() set it; staging only sets
> + * DIS_BCNQ_SUB from _BeaconFunctionEnable(), which is exclusive
> + * to AP / IBSS modes via rtl8723b_SetBeaconRelatedRegisters().
> + * The 8723BS SDIO STA join path keeps this 0x18 value until auth;
> + * the vendor start_clnt_join() path only calls Set_MSR() directly
> + * and the known-good v41 trace shows the same BCN_CTRL value at
> + * probe/auth TX time.
> + */
> + 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);
> + /* Suggested by designer timchen. Change beacon AIFS to the largest number */
> + /* beacause test chip does not contension before sending beacon. by tynli. 2009.11.03 */
> + rtw_write16(rtwdev, REG_BCNTCFG, 0x660F);
> +
> + /* Note: staging programs REG_RD_CTRL+1 = 0x6F only inside
> + * _BeaconFunctionEnable() (AP / IBSS path). STA mode never writes
> + * this register, so leave it at the chip ROM default (0x4F).
> + * eeffbe2 incorrectly wrote 0x6F here for all modes, which was a
> + * non-staging change for the STA-only build we exercise.
> + */
> +}
> +
> +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)
> +{
> + /* Let 8051 take control antenna setting. Vendor v5.2.17 writes
> + * REG_LEDCFG2 = 0x82 (BIT(7) | BIT(1)). BIT(7) enables the 8051
> + * to control antenna selection; BIT(1) is LED2_CM which the vendor
> + * driver also sets at init.
> + */
> + rtw_write8(rtwdev, REG_LEDCFG2, BIT(7) | BIT(1));
> +}
> +
> +#define RF_AC 0x00
> +
> +/* vendor function: _phy_lc_calibrate_8723b
> + * (the vendor function is always called with is2T == false)
> + */
> +static void rtw8723b_lck(struct rtw_dev *rtwdev)
> +{
> + u8 val_ctx;
> + u32 rf_mode = 0, lc_cal;
> + u8 rf_val;
> + int ret;
In reverse X'mas.
> +
> + 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); /* LDO ON */
> + 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); /* LDO OFF */
> +
> + /* 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)
> +{
> + u8 val8;
> + int ret;
> +
> + rtw_fw_inform_rfk_status(rtwdev, start);
empty line
> + 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)
> +{
> + /*
> + * Left disabled: the vendor does not do this blind BIT_TCR_CFG write.
> + * It configures REG_TCR via a read-modify-write elsewhere in hal init
> + * (rtl8723b_hal_init.c ~2795), and TX works without setting it here.
> + */
> +
> + rtw8723b_init_wmac_setting(rtwdev);
> +
> + /*
> + * These match the common rtw8723x_mac_init (used by rtw8703b);
> + * REG_INT_MIG=0 is also the vendor's default (rtl8723b_dm.c toggles it
> + * between 0 and 0xff000fa0 for interrupt moderation, 0 = off).
> + */
> + rtw_write32(rtwdev, REG_INT_MIG, 0);
> + rtw_write32(rtwdev, REG_MCUTST_1, 0x0);
> +
> + rtw_write8(rtwdev, REG_MISC_CTRL, 0x3); /* CCA */
> + rtw_write8(rtwdev, REG_2ND_CCA_CTRL, 0x0);
> +
> + return 0;
> +}
> +
> +/* based on
> + * vendor: hal/rtl8723b/sdio/sdio_halinit.c
> + * function: rtl8723bs_hal_init
> + *
> + */
> +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_queue_reserved_page(rtwdev);
> + rtw8723b_init_tx_buffer_boundary(rtwdev);
> + rtw8723b_init_llt_table(rtwdev);
> +
> + rtw8723b_init_page_boundary(rtwdev);
> + rtw8723b_init_transfer_page_size(rtwdev);
> + rtw8723b_init_driver_info_size(rtwdev);
> + rtw8723b_init_network_type(rtwdev);
> +
> + rtw8723b_init_wmac_setting(rtwdev);
> +
> + rtw8723b_init_adaptive_ctrl(rtwdev);
> + rtw8723b_init_edca(rtwdev);
> + rtw8723b_init_retry_function(rtwdev);
> +
> + /* Set up RX aggregation. sdio.c also sets 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);
> +
> + /* Match staging's rtl8723bs_hal_init(): program per-AC packet
> + * lifetime to 256 ms (0x0400, in 256 us units). The chip ROM
> + * default is 0x1000 (~1.05 s) and the upstream rtw88 driver
> + * never writes these for 8723b. Leaving the long default lets
> + * the chip queue/retry data frames (including unicast EAPOL on
> + * the BE/BK queue) for ~1 s before giving up, which is far
> + * outside the WPA-Supplicant 1-s EAPOL retry window and produces
> + * out-of-order TX behaviour during the connect window. Use the
> + * staging-parity 256 ms value so VO/VI/BE/BK TX matches what
> + * the legacy rtl8723bs driver does on the same hardware.
> + */
> + 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);
> +
> + /* set 0x0 to 0xFF by tynli. Default enable HW SEQ NUM. */
> + rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0xff);
> +
> + /*
> + * Configure SDIO TxRx Control to enable Rx DMA timer masking.
> + * 2010.02.24.
> + * Only clear necessary bits 0x0[2:0] and 0x2[15:0] and keep 0x0[15:3]
> + * 2015.03.19.
> + */
> + 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);
> +
> + /* NOTE: the following is also done in rtw8723b_post_enable_flow */
> + /* Enable MACTXEN/MACRXEN block */
> + 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);
> +
> + /*
> + * 8723d does these two alongside its cck_pd_set, but chip_ops.cck_pd_set
> + * is NULL on 8723b (REG_CSRATIO does not exist on this generation - see
> + * the chip_ops comment), so they stay disabled. Kept for reference in
> + * case cck_pd is ever wired up; the registers/constants would then need
> + * checking against rtl8723b.
> + */
> +
> + /* 8723B LCK runs after the RF table load in staging's
> + * PHY_RFConfig8723B, before later per-band calibrations.
> + * NOTE: Enabling LCK here corrupts the RF path on SDIO
> + * and prevents RX from working; keep commented out.
> + * LCK still runs inside IQK on the PTA antenna path.
> + */
> +
> + rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x50);
> + rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x20);
> +
> + rtw8723b_pwrtrack_init(rtwdev);
> +}
> +
> +static bool rtw8723b_sdio_needs_rx_path_fix(struct rtw_dev *rtwdev)
> +{
> + return rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO;
> +}
> +
> +static void rtw8723b_sdio_restore_pad_ctrl(struct rtw_dev *rtwdev,
> + bool keep_pta_owner)
> +{
> + u32 before;
> + u32 after;
> +
> + if (!rtw8723b_sdio_needs_rx_path_fix(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 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;
> +
> + /* 8723BS SDIO scan/connect now run through the PTA mux, just like the
> + * staging BT-disabled scan path leaves them. Run IQK through the same
> + * mux so the calibration is applied to the path used for auth/assoc TX.
> + */
> + return (rtwdev->efuse.bt_setting & BIT(6)) ? 0x80 : 0x200;
> +}
> +
> +static void rtw8723b_reassert_rx_path(struct rtw_dev *rtwdev)
> +{
> + u8 sys_func_before;
> + u8 rf_ctrl_before;
> + u32 fpga0_before;
> + u32 rx_path_before;
> + u32 rf_wlint_before;
> + bool changed = false;
In reverse X'mas order
> +
> + if (!rtw8723b_sdio_needs_rx_path_fix(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);
> + changed = true;
> + }
> +
> + 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);
> + changed = true;
> + }
> +
> + if ((fpga0_before & (BIT_CCKEN | BIT_OFDMEN)) !=
> + (BIT_CCKEN | BIT_OFDMEN)) {
> + rtw_write32_set(rtwdev, REG_FPGA0_RFMOD,
> + BIT_CCKEN | BIT_OFDMEN);
> + changed = true;
> + }
> +
> + if (rx_path_before != WLAN_RX_PATH_A_8723B) {
> + rtw_write32(rtwdev, REG_BB_RX_PATH_11N,
> + WLAN_RX_PATH_A_8723B);
> + changed = true;
> + }
> +
> + if (rf_wlint_before != 0x0780) {
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
> + 0x0780);
> + changed = true;
> + }
> +
> + if (!changed)
> + return;
> +}
> +
> +/* based on vendor functions
> + * hal/rtl8723b/rtl8723b_phycfg.c: phy_SwChnl8723B
> + * hal/rtl8723b/rtl8723b_phycfg.c: phy_PostSetBwMode8723B
> + * hal/rtl8723b/rtl8723b_rf6052.c: PHY_RF6052SetBandwidth8723B
> + */
> +static void rtw8723b_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw)
> +{
> + u32 rf_cfgch_a;
> + u32 rf_cfgch_b = 0;
> +
> + rf_cfgch_a = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK);
> + if (rtwdev->hal.rf_path_num > 1)
> + rf_cfgch_b = rtw_read_rf(rtwdev, RF_PATH_B, RF_CFGCH, RFREG_MASK);
> +
> + rf_cfgch_a &= ~RFCFGCH_CHANNEL_MASK;
> + if (rtwdev->hal.rf_path_num > 1)
> + rf_cfgch_b &= ~RFCFGCH_CHANNEL_MASK;
> +
> + rf_cfgch_a |= (channel & RFCFGCH_CHANNEL_MASK);
> + if (rtwdev->hal.rf_path_num > 1)
> + rf_cfgch_b |= (channel & RFCFGCH_CHANNEL_MASK);
> +
> + rf_cfgch_a &= ~RFCFGCH_BW_MASK;
> +
> + switch (bw) {
> + case RTW_CHANNEL_WIDTH_20:
> + rf_cfgch_a |= RFCFGCH_BW_20M;
> + break;
> + case RTW_CHANNEL_WIDTH_40:
> + rf_cfgch_a |= RFCFGCH_BW_40M;
> + break;
> + default:
> + break;
> + }
> +
> + if (rtwdev->hal.rf_path_num > 1) {
> + /* the vendor driver writes A value also to B */
> + rf_cfgch_b = rf_cfgch_a;
> + }
> +
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, rf_cfgch_a);
> + if (rtwdev->hal.rf_path_num > 1)
> + rtw_write_rf(rtwdev, RF_PATH_B, RF_CFGCH, RFREG_MASK, rf_cfgch_b);
> +
> + rf_cfgch_a = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK);
> + if (rtwdev->hal.rf_path_num > 1)
> + rf_cfgch_b = rtw_read_rf(rtwdev, RF_PATH_B, RF_CFGCH, RFREG_MASK);
> +
> + /* NOTE: not called in vendor driver */
> +}
> +
> +/* based on vendor functions
> + * hal/rtl8723b/rtl8723b_phycfg.c: phy_SwChnl8723B
> + * hal/rtl8723b/rtl8723b_phycfg.c: phy_PostSetBwMode8723B
> + * hal/rtl8723b/rtl8723b_rf6052.c: PHY_RF6052SetBandwidth8723B
> + */
> +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 (rtw8723b_sdio_needs_rx_path_fix(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);
> +
> + /* RF_WLINT (0x01) is the RF wireless-interface register.
> + * Bits 0-1 gate the TX/RX data path into the BB; if they
> + * are set to 0x03 (from a prior IQK or coex run), the chip
> + * may not be able to transmit. Staging always leaves this
> + * register at 0x0780 after power-on.
> + *
> + * Re-write REG_RF_CTRL (0x07) to re-arm the RF block
> + * after the BB/RF channel path may have touched it, then
> + * write the known-good RF_WLINT value. Clear REG_RF_CTRL
> + * to 0 is explicitly avoided here — that fully disables
> + * the RF frontend (RF_EN=0, RF in reset), and on this
> + * 8723B SDIO stepping the RF does not recover from a
> + * cold disable at every channel switch.
> + */
> + rtw_write8(rtwdev, REG_RF_CTRL,
> + WLAN_RF_CTRL_ENABLE | BIT_RF_RSTB |
> + BIT_RF_SDM_RSTB);
> + usleep_range(1000, 1100);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK,
> + 0x0780);
> + }
> +}
> +
> +/* adapted from vendor file hal/phydm/rtl8723b/phydm_rtl8723b.c
> + * function: odm_CCKRSSI_8723B
> + */
> +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;
> +}
> +
> +/* vendor: hal/phydm/phydm_phystatus.c
> + * function: phydm_rx_phy_status92c_series_parsing (is_cck_rate arm)
> + */
> +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;
> +}
> +
> +/* vendor: hal/phydm/phydm_phystatus.c
> + * function: phydm_rx_phy_status92c_series_parsing (!is_cck_rate arm)
> + */
> +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 is left at its default (RTW_CHANNEL_WIDTH_20), which is
> + * correct while the driver only operates at 20 MHz. Parsing the RX
> + * bandwidth out of phy_status is only needed once HT40 is enabled.
> + */
> +
> + 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;
> +}
> +
> +/* vendor: hal/phydm/phydm_phystatus.c
> + * function: phydm_rx_phy_status92c_series_parsing
> + */
> +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);
> +}
> +
> +/* vendor: hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: set_iqk_matrix_8723b
> + */
> +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;
> + }
> +}
> +
> +/* vendor: hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: set_iqk_matrix_8723b
> + */
> +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, 0xe90),
> + 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; /* BIT(0) */
> +
> + 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; /* BIT(1) */
> +
> + rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] A RX IQK failed\n");
> +
> + return 0;
> +}
> +
> +/* vendor: hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: phy_path_a_iqk_8723b
> + */
> +static u8 rtw8723b_iqk_tx_path_a(struct rtw_dev *rtwdev)
> +{
> + bool sdio_iqk = rtw8723b_sdio_needs_rx_path_fix(rtwdev);
> + u8 status;
> + u32 path_sel;
> +
> + 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_RCK_OS, RFREG_MASK,
> + sdio_iqk ? 0x18000 : 0x20000);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXPA_G1, RFREG_MASK, 0x0003f);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXPA_G2, 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);
> +
> + mdelay(IQK_DELAY_TIME_8723B); /* NOTE: rtl8xxxu uses mdelay(1) */
> +
> + /* 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;
> +}
> +
> +/* vendor: hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: phy_path_a_rx_iqk_8723b
> + */
> +static u8 rtw8723b_iqk_rx_path_a(struct rtw_dev *rtwdev)
> +{
> + bool sdio_iqk = rtw8723b_sdio_needs_rx_path_fix(rtwdev);
> + u32 reg_e94, reg_e9c, val32, path_sel;
> + 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_RCK_OS, RFREG_MASK,
> + sdio_iqk ? 0x18000 : 0x30000);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXPA_G1, RFREG_MASK, 0x0001f);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXPA_G2, 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);
> +
> + mdelay(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!");
> +
> + /* 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_RCK_OS, RFREG_MASK,
> + sdio_iqk ? 0x18000 : 0x30000);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXPA_G1, RFREG_MASK, 0x0001f);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXPA_G2, 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);
> +
> + mdelay(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 */
> +
> + /* NOTE: in vendor driver this is called after reading reg_eac
> + * and reg_ea4, we read them in rtw8723b_iqk_check_rx_failed
> + */
> + rtw_write_rf(rtwdev, RF_PATH_A, 0xdf, RFREG_MASK, 0x780);
> +
> + status |= rtw8723b_iqk_check_rx_failed(rtwdev);
> +
> + return status;
> +}
> +
> +/* vendor: hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: _phy_path_a_fill_iqk_matrix8723b
> + */
> +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]));
> +}
> +
> +/* vendor: hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: _phy_path_b_fill_iqk_matrix8723b
> + */
> +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]));
> +}
> +
> +/* vendor hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: _phy_mac_setting_calibration8723b
> + */
> +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)));
> +
> + /* Match the vendor byte-wide write for this MAC backup register. */
> + rtw_write8(rtwdev, rtw8723x_common.iqk_mac32_regs[0],
> + backup->mac32[0] & (~BIT(5)));
> +}
> +
> +/* vendor file hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: _phy_iq_calibrate_8723b / phy_iq_calibrate_8723b
> + */
> +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;
> + /* u8 b_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_RCK_OS, RFREG_MASK, 0x30000);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXPA_G1, RFREG_MASK, 0x0001f);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXPA_G2, 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");
> +
> + /* NOTE: the vendor driver does path B only for 2T, but rtl8723b is 1T1R */
> +
> + rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, MASKH3BYTES, 0x000000);
> +}
> +
> +/* vendor hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: phy_iq_calibrate_8723b / _phy_iq_calibrate_8723b
> + */
> +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];
> + u8 final_candidate = IQK_ROUND_INVALID;
> + u32 bt_control;
> + bool good;
> + 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);
> +
> + /* backup_path_ctrl + backup_regs match rtw8723d's IQK. 8723d also
> + * backs up the LTE path GNT here, but the vendor rtl8723bs does not,
> + * so that one stays disabled for 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 (!rtw8723b_sdio_needs_rx_path_fix(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);
> +
> + /* path B; only for 2T */
> +
> + /* load 0xe30 IQC default value */
> + rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x01008c00);
> + rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x01008c00);
> + }
> +
> + if (!rtw8723b_sdio_needs_rx_path_fix(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 {
> + WARN(1, "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);
> +
> + /* vendor ADDA save/restore not needed: neither this driver nor
> + * rtw8723d does it, and IQK completes correctly without it
> + */
> +
> + /* 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_RCK_OS, RFREG_MASK, 0x18000);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXPA_G1, RFREG_MASK, 0x0001f);
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXPA_G2, RFREG_MASK, 0xe6177);
> + rtw_write_rf(rtwdev, RF_PATH_A, 0xed, 0x20, 0x1);
> + rtw_write_rf(rtwdev, RF_PATH_A, 0x43, RFREG_MASK, 0x300bd);
> +
> + /* not needed: this vendor branch only runs in MP (manufacturing
> + * test) mode, which this driver never enters
> + */
> +
> + 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, s8 swing_idx,
> + s8 txagc_idx)
> +{
> + struct rtw_dm_info *dm_info = &rtwdev->dm_info;
> +
> + dm_info->txagc_remnant_ofdm[RF_PATH_A] = txagc_idx;
> +
> + rtw8723b_set_iqk_matrix(rtwdev, swing_idx, RF_PATH_A);
> +}
> +
> +/* adapted from vendor file hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: set_cck_filter_coefficient
> + */
> +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(rtw8732b_cck_swing_table_ch1_ch13[0]));
> +
> + /*
> + * Always uses the ch1-13 CCK swing table (see the commented vendor
> + * code above). Channel 14 needs its own table but is Japan-only 2.4G
> + * and disallowed by the regulatory domains we run in, so it is not
> + * wired up.
> + */
> + for (int i = 0; i < ARRAY_SIZE(rtw8723b_cck_pwr_regs); i++)
> + rtw_write8(rtwdev, rtw8723b_cck_pwr_regs[i],
> + rtw8732b_cck_swing_table_ch1_ch13[swing_idx][i]);
> +}
> +
> +/* vendor driver hal/phydm/halrf/rtl8723b/halrf_8723b_ce.c
> + * function: odm_tx_pwr_track_set_pwr_8723b
> + */
> +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, limit_ofdm,
> + final_ofdm_swing_index - limit_ofdm);
> + else if (final_ofdm_swing_index < 0)
> + rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, 0,
> + final_ofdm_swing_index);
> + else
> + rtw8723b_pwrtrack_set_ofdm_pwr(rtwdev, 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);
> +}
> +
> +/* vendor driver hal/phydm/halrf/halphyrf_ce.c
> + * function: odm_txpowertracking_callback_thermal_meter
> + */
> +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);
> +
> + /*4 4. Calculate average thermal meter*/
> + rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A);
> +
> + do_iqk = rtw_phy_pwrtrack_need_iqk(rtwdev);
> +
> + /* matches rtw8723d power tracking: LCK via the common helper on the
> + * IQK trigger, then the IQK itself below
> + */
> + 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);
> + }
> +
> + /* not done in the 8723bs vendor driver */
> +
> +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");
> + 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;
> +}
> +
> +/* vendor file hal/btc/halbtc8723b1ant.c
> + * function: halbtc8723b1ant_init_hw_config
> + */
> +static void rtw8723b_coex_cfg_init(struct rtw_dev *rtwdev)
> +{
> + /* enable TBTT nterrupt */
> + rtw_write8_mask(rtwdev, 0x550, 0x8, 0x1);
> +
> + /* 0x790[5:0]= 0x5 */
> + rtw_write8(rtwdev, 0x790, 0x5);
> +
> + /* enable counter statistics */
> + rtw_write8(rtwdev, 0x778, 0x1);
> + rtw_write8_mask(rtwdev, 0x40, 0x20, 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)
> +{
> + /*
> + * Not implemented: rtw8723d routes GNT_BT to debug GPIOs here
> + * (REG_LEDCFG2/REG_PAD_CTRL1/REG_GPIO_*). That is coex debug
> + * instrumentation only and is not needed for normal 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_BT_COEX_CTRL_8723B, BIT(3));
> + rtw8723b_coex_write8_verify(rtwdev, REG_BB_ANT_BUF_8723B, 0xff);
> + rtw_write8_mask(rtwdev, REG_BB_ANT_CFG1_8723B, 0x3, 0x3);
> + rtw_write8(rtwdev, REG_BB_ANT_CFG_8723B, 0x77);
> +}
> +
> +static u32 rtw8723b_coex_write_bb_sel_btg(struct rtw_dev *rtwdev, u32 value)
> +{
> + u8 sys_func_before;
> + u32 readback;
> +
> + 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_BT_GNT_BT_8723B, 0x18);
> + rtw_write8(rtwdev, REG_BT_WLAN_ACT_8723B, 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_BT_GNT_BT_8723B),
> + rtw_read8(rtwdev, REG_BT_WLAN_ACT_8723B));
> + 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_BT_GNT_BT_8723B, 0x18);
> + rtw_write8(rtwdev, REG_BT_WLAN_ACT_8723B, 0x4);
> + } else {
> + if (rtw_read8(rtwdev, REG_BT_GNT_BT_8723B) != 0)
> + rtw_write8(rtwdev, REG_BT_GNT_BT_8723B, 0x0);
> +
> + if (rtw_read8(rtwdev, REG_BT_WLAN_ACT_8723B) != 0xc)
> + rtw_write8(rtwdev, REG_BT_WLAN_ACT_8723B, 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_BT_GNT_BT_8723B),
> + rtw_read8(rtwdev, REG_BT_WLAN_ACT_8723B));
> +}
> +
> +/* vendor file: hal/btc/halbtc8723b1ant.c
> + * function: ex_halbtc8723b1ant_power_on_setting
> + */
> +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);
> + bool aux = rtw8723b_coex_ant_is_aux(rtwdev);
> + u32 reg;
> +
> + 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_BT_GNT_BT_8723B, 0x18);
> +
> + /* set WLAN_ACT = 0 */
> + rtw_write8(rtwdev, REG_BT_WLAN_ACT_8723B, 0x4);
> +
> + switch (hci_type) {
> + case RTW_HCI_TYPE_USB:
> + rtw8723b_coex_write_bb_sel_btg(rtwdev, 0x0);
> + rtw_write8(rtwdev, 0xfe08, 0x1); /* antenna inverse */
> + break;
> + case RTW_HCI_TYPE_PCIE:
> + reg = 0x384;
> + /* efuse 0xc3[6] == 0, S1(Main), RF_PATH_A
> + * efuse 0xc3[6] == 1, S0(Aux), RF_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:
> + /* Staging power-on and SetAntPath(init) sequence for the
> + * internal switch: keep WiFi TRx enabled, let WiFi control
> + * S0/S1 through 0x64, and program the firmware antenna-inverse
> + * hint with H2C 0x65 type 0.
> + */
> + rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK, 0x0780);
> +
> + rtw_write8(rtwdev, REG_BT_ANT_SEL_8723B, 0x20);
> + rtw_write8(rtwdev, REG_BT_GNT_BT_8723B, 0x18);
> + rtw_write8(rtwdev, REG_BT_WLAN_ACT_8723B, 0x4);
> +
> + 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_ANTSEL_SW_8723B,
> + BIT_SW_DPDT_SEL_DATA, 0x0);
> + rtw8723b_coex_cfg_ant_buffer(rtwdev);
> + rtw8723b_sdio_restore_pad_ctrl(rtwdev, false);
> +
> + /* H2C 0x65 (COEX_ANT_SEL_RSV) is sent from the post-init
> + * block in rtw_power_on() after 0x6E (GNT_BT), matching
> + * vendor v5.2.17 order: 0x6D,0x6D,0x60,0x6E,0x65,0x61.
> + * The register writes above stay here to correctly
> + * initialise RFE/antenna hardware before IQK.
> + */
> + break;
> + default:
> + break;
> + }
> +}
> +
> +static void rtw8723b_coex_set_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr)
> +{
> + /*
> + * Not implemented: rtw8723d adjusts WL Tx power (0xb2/0x90) when BT is
> + * active. Coexistence quality only, not needed for basic operation;
> + * deferred like coex_set_wl_rx_gain.
> + */
> +}
> +
> +static void rtw8723b_coex_set_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain)
> +{
> + /*
> + * Not implemented: rtw8723d lowers the WL Rx AGC via gain tables when
> + * BT is active. This only affects WiFi/BT coexistence quality under
> + * concurrent BT traffic, not basic operation; deferred.
> + */
> +}
> +
> +static void rtw8723b_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
> +{
> + /*
> + * Intentionally empty on 8723bs. The 2.5V efuse LDO (EFUSE_TEST+3) is
> + * only enabled by the vendor's Hal_EfusePowerSwitch when *writing* the
> + * efuse (guarded by bWrite == TRUE). rtw88 only ever reads the efuse,
> + * and the vendor skips the LDO write on the read path too, so there is
> + * nothing to do here.
> + */
> +}
> +
> +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 8723B does not invert the XOR
> + * checksum over the first 16 half-words of the TX descriptor.
> + */
> + 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 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 to BIT_USB_SUS_DIS in
> + * its shutdown fubction, not needed for SDIO devices.
> + */
> + .shutdown = NULL,
> + .read_efuse = rtw8723x_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,
> + /*
> + * 8723d uses REG_CSRATIO to set dm_info.cck_pd_default for its
> + * cck_pd_set. Per the vendor comments that register does not exist in
> + * this chip generation (only 0xa0a "ODM_CCK_PD_THRESH", write-only),
> + * so there is nothing to read back. The sibling rtw8703b (same
> + * generation) also leaves this NULL, confirming it applies to 8723b.
> + */
> + .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, /* vendor: EFUSE_OOB_PROTECT_BYTES */
> +
> + .txff_size = 32768, /* vendor: TX_DMA_SIZE_8723B 0x8000 */
> + .rxff_size = 16384, /* vendor: RX_DMA_SIZE_8723B 0x4000 */
> + .rsvd_drv_pg_num = 8,
> +
> + .txgi_factor = 1,
> + .is_pwr_by_rate_dec = true,
> + .rx_ldpc = false,
> + .tx_stbc = false,
> +
> + .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,
> +
> + /* vendor function hal_read_mac_hidden_rpt is not called in the
> + * rtl8723bs driver, also when true main.c:rtw_dump_hw_feature fails,
> + * because firmware reports id=0xfd instead of C2H_HW_FEATURE_REPORT,
> + * so seems not supported
> + */
> + .hw_feature_report = false,
> +
> + .c2h_ra_report_size = 4, /* rtw88/rtw8723b_fw.bin v41 emits the
> + * legacy 8051 4-byte rate report
> + * (rate_sgi, mac_id, byte2, status).
> + * Setting this to 7 — like the upstream
> + * default — caused every C2H_RA_REPORT
> + * to be dropped with
> + * "short ra report c2h length 4
> + * expected 7" on every connect attempt
> + * so the firmware-driven rate adaptation
> + * feedback path
> + * never updated si->ra_report. The
> + * legacy 8051 8723b/8703b/8723d firmware
> + * uses the same 4-byte format as the
> + * older 8821a/8812a chips. byte4..bw fall
> + * back to the per-station defaults in
> + * rtw_fw_ra_report_iter(), which matches
> + * what those chips already do safely.
> + */
> + .old_datarate_fb_limit = true, /* likely true; see main-line commit c7706b1 */
> +
> + .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,
> +
> + /* there are no traces of lte coex registers in the vendor driver */
> + .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,
> + .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 = 0x6f, /* but for 2 ant it's 0x52 */
> + .scbd_support = true,
> + .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,
> + /* REG_BTG_SEL doesn't seem to have a counterpart in the
> + * vendor driver. Mathematically it's REG_PAD_CTRL1 + 3.
> + *
> + * It is used in the cardemu_to_act power sequence by though
> + * (by address, 0x0067), comment: "0x67[0] = 0 to disable
> + * BT_GPS_SEL pins" That seems to fit.
> + */
> + .btg_reg = NULL,
> +
> + /* These registers are used to read (and print) from if
> + * CONFIG_RTW88_DEBUGFS is enabled.
> + */
> + .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 <luka.gejak@linux.dev>");
> +MODULE_AUTHOR("Michael Straube <straube.linux@gmail.com>");
> +MODULE_DESCRIPTION("Realtek 802.11n wireless 8723b driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723b.h
> b/drivers/net/wireless/realtek/rtw88/rtw8723b.h
> new file mode 100644
> index 000000000000..2880d41e4b01
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8723b.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
> +/* Copyright(c) 2018-2019 Realtek Corporation
Copyright(c) Realtek Corporation
> + */
> +
> +#ifndef __RTW8723B_H__
> +#define __RTW8723B_H__
> +
> +#include "rtw8723x.h"
> +
> +extern const struct rtw_chip_info rtw8723b_hw_spec;
> +
> +/* shared with rtw8703b.c; could move to rtw8723x.h */
> +#define REG_TXIQK_MATRIXB_LSB2_11N 0x0c9c
> +#define REG_BB_PWR_SAV5_11N 0x0818
> +
> +#endif
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723b_table.c
> b/drivers/net/wireless/realtek/rtw88/rtw8723b_table.c
> new file mode 100644
> index 000000000000..e6bdbcc023e0
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8723b_table.c
> @@ -0,0 +1,858 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
Copyright(c) Realtek Corporation
> +/* Copyright(c) Michael Straube <straube.linux@gmail.com> */
> +/* Copyright(c) 2024-2026 Luka Gejak <luka.gejak@linux.dev> */
> +
> +#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);
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723b_table.h
> b/drivers/net/wireless/realtek/rtw88/rtw8723b_table.h
> new file mode 100644
> index 000000000000..7f6f823d98ae
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8723b_table.h
> @@ -0,0 +1,15 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
Copyright(c) Realtek Corporation
> +/* Copyright(c) Michael Straube <straube.linux@gmail.com> */
> +/* Copyright(c) 2024-2026 Luka Gejak <luka.gejak@linux.dev> */
> +
> +#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
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723bs.c
> b/drivers/net/wireless/realtek/rtw88/rtw8723bs.c
> new file mode 100644
> index 000000000000..da0b9802f633
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8723bs.c
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
Copyright(c) Realtek Corporation
> +/* Copyright(c) Michael Straube <straube.linux@gmail.com> */
> +/* Copyright(c) 2024-2026 Luka Gejak <luka.gejak@linux.dev> */
> +
> +#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,
> + },
> + {}
> +};
> +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 <straube.linux@gmail.com>");
> +MODULE_AUTHOR("Luka Gejak <luka.gejak@linux.dev>");
> +MODULE_DESCRIPTION("Realtek 802.11n wireless 8723bs driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/net/wireless/realtek/rtw88/sec.h b/drivers/net/wireless/realtek/rtw88/sec.h
> index efcf45433999..73f2af66effe 100644
> --- a/drivers/net/wireless/realtek/rtw88/sec.h
> +++ b/drivers/net/wireless/realtek/rtw88/sec.h
> @@ -22,6 +22,7 @@
> #define RTW_SEC_RX_DEC_EN BIT(3)
> #define RTW_SEC_TX_BC_USE_DK BIT(6)
> #define RTW_SEC_RX_BC_USE_DK BIT(7)
> +#define RTW_SEC_CHK_KEYID BIT(8)
>
> #define RTW_SEC_ENGINE_EN BIT(9)
>
> --
> 2.55.0
^ permalink raw reply
* Re: [PATCH v3 0/2] wifi: ath11k/ath12k: release QMI handles on late init failures
From: Baochen Qiang @ 2026-07-20 2:20 UTC (permalink / raw)
To: Guangshuo Li, Jeff Johnson, Pradeep Kumar Chitrapu,
Vasanthakumar Thiagarajan, Sathishkumar Muruganandam,
Anilkumar Kolli, Ganesh Sesetti, linux-wireless, ath11k,
linux-kernel, ath12k
In-Reply-To: <20260718074026.3085688-1-lgs201920130244@gmail.com>
On 7/18/2026 3:40 PM, Guangshuo Li wrote:
> This series fixes QMI handle leaks in the late initialization failure
> paths of ath11k and ath12k.
>
> Both drivers initialize the QMI handle before allocating the QMI event
> workqueue and registering the service lookup. If either of these later
> steps fails, the initialized QMI handle is not released.
>
> Patch 1 fixes the leak in ath11k.
>
> Patch 2 fixes the leak in ath12k and sets ab->qmi.ab only after QMI
> service initialization completes successfully.
>
> v3:
> - Split the ath11k and ath12k changes into separate patches, as
> requested by Jeff Johnson.
>
> v2:
> - Set ath12k ab->qmi.ab only after QMI service initialization succeeds,
> as suggested by Baochen Qiang.
> - Fix the same late initialization QMI handle leak in ath11k, as
> suggested by Vasanthakumar Thiagarajan.
> - Drop the Reviewed-by tag due to the code changes.
>
> Guangshuo Li (2):
> wifi: ath11k: release QMI handle on late init failures
> wifi: ath12k: release QMI handle on late init failures
>
> drivers/net/wireless/ath/ath11k/qmi.c | 10 ++++++++--
> drivers/net/wireless/ath/ath12k/qmi.c | 13 ++++++++++---
> 2 files changed, 18 insertions(+), 5 deletions(-)
>
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
^ permalink raw reply
* RE: [PATCH] wifi: rtw88: disable ASPM and deep PS on ASUS TUF Gaming A15 FA506II
From: Ping-Ke Shih @ 2026-07-20 1:55 UTC (permalink / raw)
To: Mihail Dimoski; +Cc: linux-wireless@vger.kernel.org
In-Reply-To: <20260718124045.23493-1-mihaildimoski@gmail.com>
Mihail Dimoski <mihaildimoski@gmail.com> wrote:
> The RTL8822CE on the ASUS TUF Gaming A15 FA506II wedges during normal
> use. The driver watchdog toggles PCIe ASPM while leaving power save;
> the DBI read of the ASPM link-config register fails with -EIO, the PCIe
> link becomes unstable, and the device drops off the bus, taking Wi-Fi
> down until a cold power cycle:
>
> rtw88_8822ce 0000:03:00.0: failed to read ASPM, ret=-5
> rtw88_8822ce 0000:03:00.0: firmware failed to leave lps state
> rtw88_8822ce 0000:03:00.0: mac power on failed
>
> This is the same platform ASPM inter-operability problem already
> handled for other machines through rtw_pci_quirks[]. Disabling PCI
> ASPM and deep power save on this model stops the failure. Add a DMI
> quirk so the workaround is applied automatically.
>
> Signed-off-by: Mihail Dimoski <mihaildimoski@gmail.com>
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
^ permalink raw reply
* RE: [RFC PATCH v1 0/9] wifi: rtw88: add RTL8723B/RTL8723BS support
From: Ping-Ke Shih @ 2026-07-20 1:53 UTC (permalink / raw)
To: luka.gejak@linux.dev, linux-wireless@vger.kernel.org
Cc: straube.linux@gmail.com
In-Reply-To: <cover.1784047561.git.luka.gejak@linux.dev>
luka.gejak@linux.dev <luka.gejak@linux.dev> wrote:
> From: Luka Gejak <luka.gejak@linux.dev>
>
> This RFC adds support for the Realtek RTL8723B 802.11n chipset and its
> RTL8723BS SDIO variant to rtw88. The series is SDIO-only; untested PCIe
> and USB variants are intentionally left for future work.
RTL8723BE and RTL8723BU are supported by rtlwifi and rtl8xxxu respectively.
Though it isn't so good to maintain three interfaces in tree drivers, I'd
keep them as was, unless people can take time to verify them, especially
BT-coexistence.
>
> The implementation is based on the initial RTL8723B work by Michael
> Straube <straube.linux@gmail.com>.
Can you share the link or GitHub?
>
> Hardware validation covered repeated scan, authentication, association,
> WPA2 key negotiation, DHCP, bidirectional traffic, reconnects, link
> cycles, module reloads, scans under traffic and a sustained stability
> run.
Can you additionally test BT-coexistence? Download or navigate web page
while a Bluetooth is playing music to see if it plays music smoothly?
> The final series also builds cleanly with W=1 at every intermediate
> commit. RTL8703B, RTL8723CS/DS and the other rtw88 bus modules continue to
> build.
I did additional sparse/smatch tests which both are fine. Once you send
new version, run them again.
^ permalink raw reply
* RE: [PATCH] rtw88: add firmware v41.0.0 for RTL8723B
From: Ping-Ke Shih @ 2026-07-20 1:31 UTC (permalink / raw)
To: luka.gejak@linux.dev, linux-firmware@kernel.org
Cc: linux-wireless@vger.kernel.org
In-Reply-To: <20260719180033.66867-1-luka.gejak@linux.dev>
luka.gejak@linux.dev <luka.gejak@linux.dev> wrote:
> From: Luka Gejak <luka.gejak@linux.dev>
>
> Add the RTL8723B NIC firmware image used by the rtw88 driver. Support for
> the RTL8723BS (SDIO) device that uses it is being added to the mainline
> rtw88 driver.
>
> The image was extracted from the Realtek "rtl8723bs" vendor driver,
> version v5.2.17.1_26955.20180307_COEX20180201-6f52, from the C array
> array_mp_8723b_fw_nic[] in hal/rtl8723b/hal8723b_fw.c, using the
> convert_firmware.c tool. A public copy of that vendor driver is at
> https://github.com/MocLG/rtl8723bs-5.2.17
>
> The image reports version 41.0.0 and is covered by the existing Realtek
> redistribution licence (LICENCE.rtlwifi_firmware.txt).
>
> Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
I checked firmware binary with official release. They are identical.
Except to a nit below,
Reviewed-by: Ping-Ke Shih <pkshih@realtek.com>
> ---
> WHENCE | 1 +
> rtw88/rtw8723b_fw.bin | Bin 0 -> 32272 bytes
> 2 files changed, 1 insertion(+)
> create mode 100644 rtw88/rtw8723b_fw.bin
>
> diff --git a/WHENCE b/WHENCE
> index 6cfe142d4e91..b4b7e6acda1c 100644
> --- a/WHENCE
> +++ b/WHENCE
> @@ -3352,6 +3352,7 @@ File: rtw88/rtw8822c_fw.bin
> File: rtw88/rtw8822c_wow_fw.bin
> File: rtw88/README
Why not putting rtw8723b_fw.bin here in alphabetic order?
> File: rtw88/rtw8723d_fw.bin
> +File: rtw88/rtw8723b_fw.bin
> File: rtw88/rtw8821c_fw.bin
> File: rtw88/rtw8703b_fw.bin
> File: rtw88/rtw8703b_wow_fw.bin
^ permalink raw reply
* Re: [RFC PATCH net-next v0.1 1/1] net: add GeoNetworking protocol
From: Andrew Lunn @ 2026-07-19 23:33 UTC (permalink / raw)
To: Simon Dietz
Cc: netdev, andrew+netdev, davem, edumazet, johannes, kuniyu,
linux-wireless, dietz23838
In-Reply-To: <20260718210046.2357882-2-simon.dietz@plantwatch.de>
> +struct gn_coord {
> + __s32 lat;
> + __s32 lon;
> +};
> +
> +struct gn_position {
> + struct __kernel_timespec tst;
> + struct gn_coord coord;
> + __u8 flags;
> +};
> +static int gn_set_link_af(struct net_device *dev, const struct nlattr *attr,
> + struct netlink_ext_ack *extack)
> +{
> + struct nlattr *tb[IFLA_GN_MAX + 1];
> + struct gn_position pos;
> + struct gn_iface *gnif;
> + int rc;
> +
> + rc = nla_parse_nested(tb, IFLA_GN_MAX, attr, ifla_gn_policy, extack);
> + if (rc < 0)
> + return rc;
> +
> + if (!tb[IFLA_GN_POSITION])
> + return 0;
> +
> + if (!capable(CAP_NET_ADMIN))
> + return -EPERM;
> +
> + if (nla_len(tb[IFLA_GN_POSITION]) < sizeof(struct gn_position))
> + return -EINVAL;
> +
> + memcpy(&pos, nla_data(tb[IFLA_GN_POSITION]), sizeof(pos));
Passing a binary structure as a netlink attribute is not going to fly.
Netlink messages are meant to be built up from a number of attributes
using its fundamental types. That gives you extendability. New
attributes can be added later without breaking backwards
compatibility. And i think you need this. As far as i can see, you are
only passing coordinates, but no indication of direction and
speed. NMEA sentences do support this, and if the GPS does not, you
can calculate it. So at some point new attributes are going to be
needed.
> + rc = gn_validate_pos(&pos);
> + if (rc < 0)
> + return rc;
> +
> + gnif = gn_find_interface_by_dev(dev);
> + if (!gnif)
> + return -EADDRNOTAVAIL;
> +
> + memcpy(&gnif->pos, &pos, sizeof(pos));
This also does not feel correct. Why is location a property of an
interface? Can one machines interfaces be in different locations?
I suppose you might have one interface pointing forwards, another
pointing backwards, both shaped to be mostly unidirectional. And a
third omni directional interface on the roof? The interfaces can then
be a couple of meters apart. But is that sufficient to matter?
Andrew
^ permalink raw reply
* [RFC PATCH 3/3] wifi: ath11k: use private page-frag caches for RXDMA rings
From: Mark Ruvald Pedersen @ 2026-07-19 21:58 UTC (permalink / raw)
To: linux-wireless; +Cc: Jeff Johnson, ath11k
In-Reply-To: <20260719215817.2468580-1-wabsie@gmail.com>
ath11k allocates RXDMA skb heads with dev_alloc_skb(), which draws from
shared per-CPU page-frag caches. RX buffers from different rings and
unrelated networking allocations can therefore share a high-order
backing page despite having different ownership lifetimes.
On IPQ8074, RX ownership tracing found bounded ring and IDR occupancy
while page-frag backing grew. Two captures found current or historical
cross-origin fragments on 86-87% of the final tracker-visible physical
pages.
Give each RXDMA ring its own page_frag_cache and use it for regular and
monitor-status buffers. New fragments from distinct rings can no longer
share backing pages with one another or with unrelated users. The
monitor-destination path that intentionally uses the data refill ring
also uses that ring's cache.
Commit d455e805de70 ("wifi: ath11k: rearrange IRQ enable/disable in reset
path") moved HIF IRQ shutdown from the common crash-reconfiguration path
into the reset worker. The non-reset QMI recovery path can consequently
reach DP teardown while NAPI can still refill an RXDMA ring.
Quiesce HIF IRQ/NAPI processing on that non-reset path before teardown.
The reset worker already does so before power cycling, therefore keep the
existing reset path unchanged. This makes the cache change self-contained
without relying on repeated IRQ lifecycle transitions.
The refill paths serialize each cache with the corresponding SRNG lock.
Drain the cache only after ring-owned skbs have been DMA-unmapped and
freed and the IDR has been destroyed. Keep the dev_alloc_skb() allocation
flags and headroom behavior; build_skb() propagates head-frag and
pfmemalloc metadata from the backing page.
This leaves buffer and ring sizes and all DMA map/unmap operations
unchanged. It isolates cross-owner lifetime mixing; it does not prevent
lifetime variation among buffers belonging to the same ring.
With the private caches, page-frag backing remained bounded in natural
load, resident, and high-packet-rate pressure runs on IPQ8074. The
non-reset recovery path has not been runtime-tested in isolation.
Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Fixes: d455e805de70 ("wifi: ath11k: rearrange IRQ enable/disable in reset path")
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1
Signed-off-by: Mark Ruvald Pedersen <wabsie@gmail.com>
---
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 8dacc878c006..e71a22ce332d 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -2330,6 +2330,9 @@ static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab)
{
int ret;
+ if (!ab->is_reset)
+ ath11k_hif_irq_disable(ab);
+
mutex_lock(&ab->core_lock);
ath11k_thermal_unregister(ab);
ath11k_dp_pdev_free(ab);
diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h
index 84f66839f0c6..e69dc23b2051 100644
--- a/drivers/net/wireless/ath/ath11k/dp.h
+++ b/drivers/net/wireless/ath/ath11k/dp.h
@@ -7,6 +7,8 @@
#ifndef ATH11K_DP_H
#define ATH11K_DP_H
+#include <linux/page_frag_cache.h>
+
#include "hal_rx.h"
#define MAX_RXDMA_PER_PDEV 2
@@ -72,6 +74,7 @@ struct dp_srng {
struct dp_rxdma_ring {
struct dp_srng refill_buf_ring;
+ struct page_frag_cache frag_cache;
struct idr bufs_idr;
/* Protects bufs_idr */
spinlock_t idr_lock;
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index e4ea0c216740..6bbfa0ebddd0 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -5,9 +5,11 @@
*/
#include <linux/fips.h>
+#include <linux/gfp.h>
#include <linux/ieee80211.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
+#include <net/sock.h>
#include "core.h"
#include "debug.h"
#include "debugfs_htt_stats.h"
@@ -340,6 +342,36 @@ static int ath11k_dp_purge_mon_ring(struct ath11k_base *ab)
return -ETIMEDOUT;
}
+static struct sk_buff *
+ath11k_dp_rx_alloc_skb(struct dp_rxdma_ring *rx_ring, unsigned int len)
+{
+ struct page_frag_cache *cache = &rx_ring->frag_cache;
+ gfp_t gfp_mask = GFP_ATOMIC;
+ struct sk_buff *skb;
+ void *data;
+
+ len += NET_SKB_PAD;
+ len = SKB_HEAD_ALIGN(len);
+
+ if (sk_memalloc_socks())
+ gfp_mask |= __GFP_MEMALLOC;
+
+ /* Refill paths serialize the per-ring cache with the SRNG lock. */
+ data = page_frag_alloc(cache, len, gfp_mask);
+ if (!data)
+ return NULL;
+
+ skb = build_skb(data, len);
+ if (!skb) {
+ page_frag_free(data);
+ return NULL;
+ }
+
+ skb_reserve(skb, NET_SKB_PAD);
+
+ return skb;
+}
+
/* Returns number of Rx buffers replenished */
int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id,
struct dp_rxdma_ring *rx_ring,
@@ -371,8 +403,8 @@ int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id,
num_remain = req_entries;
while (num_remain > 0) {
- skb = dev_alloc_skb(DP_RX_BUFFER_SIZE +
- DP_RX_BUFFER_ALIGN_SIZE);
+ skb = ath11k_dp_rx_alloc_skb(rx_ring, DP_RX_BUFFER_SIZE +
+ DP_RX_BUFFER_ALIGN_SIZE);
if (!skb)
break;
@@ -453,6 +485,8 @@ static int ath11k_dp_rxdma_buf_ring_free(struct ath11k *ar,
idr_destroy(&rx_ring->bufs_idr);
spin_unlock_bh(&rx_ring->idr_lock);
+ page_frag_cache_drain(&rx_ring->frag_cache);
+
return 0;
}
@@ -2867,8 +2901,8 @@ static struct sk_buff *ath11k_dp_rx_alloc_mon_status_buf(struct ath11k_base *ab,
struct sk_buff *skb;
dma_addr_t paddr;
- skb = dev_alloc_skb(DP_RX_BUFFER_SIZE +
- DP_RX_BUFFER_ALIGN_SIZE);
+ skb = ath11k_dp_rx_alloc_skb(rx_ring, DP_RX_BUFFER_SIZE +
+ DP_RX_BUFFER_ALIGN_SIZE);
if (!skb)
goto fail_alloc_skb;
^ permalink raw reply related
* [RFC PATCH 2/3] wifi: ath11k: make external IRQ control idempotent
From: Mark Ruvald Pedersen @ 2026-07-19 21:58 UTC (permalink / raw)
To: linux-wireless; +Cc: Jeff Johnson, ath11k
In-Reply-To: <20260719215817.2468580-1-wabsie@gmail.com>
ath11k tracks each external IRQ group's NAPI lifecycle with napi_enabled,
but calls the physical IRQ enable and disable helpers outside the state
guards. Repeating a lifecycle disable therefore increments Linux's IRQ
disable depth on AHB and multi-MSI PCI even when NAPI is already
disabled. One later enable leaves the IRQ masked.
Move each physical group transition inside the matching NAPI state
transition. Preserve the ordering: mask IRQs before synchronizing and
disabling NAPI, then enable NAPI before unmasking IRQs. The outer
synchronize_irq() calls remain unchanged.
This makes sequential lifecycle transitions idempotent. It does not
change the temporary disable/enable pairing in interrupt handlers and
NAPI poll completion.
Fixes: d943fdad7589 ("ath11k: Fix napi related hang")
Fixes: bbfdc5a751a6 ("ath11k: Refactor PCI code to support WCN6750")
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1
Signed-off-by: Mark Ruvald Pedersen <wabsie@gmail.com>
---
diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
index 1e1dea485..1f1562ff3 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -235,9 +235,9 @@ static void __ath11k_ahb_ext_irq_disable(struct ath11k_base *ab)
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
- ath11k_ahb_ext_grp_disable(irq_grp);
-
if (irq_grp->napi_enabled) {
+ ath11k_ahb_ext_grp_disable(irq_grp);
+
napi_synchronize(&irq_grp->napi);
napi_disable(&irq_grp->napi);
irq_grp->napi_enabled = false;
@@ -380,8 +380,8 @@ static void ath11k_ahb_ext_irq_enable(struct ath11k_base *ab)
if (!irq_grp->napi_enabled) {
napi_enable(&irq_grp->napi);
irq_grp->napi_enabled = true;
+ ath11k_ahb_ext_grp_enable(irq_grp);
}
- ath11k_ahb_ext_grp_enable(irq_grp);
}
}
diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c
index 2259adc3b..bf367f60b 100644
--- a/drivers/net/wireless/ath/ath11k/pcic.c
+++ b/drivers/net/wireless/ath/ath11k/pcic.c
@@ -455,9 +455,9 @@ static void __ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
- ath11k_pcic_ext_grp_disable(irq_grp);
-
if (irq_grp->napi_enabled) {
+ ath11k_pcic_ext_grp_disable(irq_grp);
+
napi_synchronize(&irq_grp->napi);
napi_disable(&irq_grp->napi);
irq_grp->napi_enabled = false;
@@ -490,8 +490,8 @@ void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab)
if (!irq_grp->napi_enabled) {
napi_enable(&irq_grp->napi);
irq_grp->napi_enabled = true;
+ ath11k_pcic_ext_grp_enable(irq_grp);
}
- ath11k_pcic_ext_grp_enable(irq_grp);
}
set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox