* [PATCH rtw-next 0/5] wifi: rtw88: pci: Support an older design, like RTL8821AE/RTL8812AE
@ 2026-10-23 22:57 Bitterblue Smith
2026-10-23 22:59 ` [PATCH rtw-next 1/5] wifi: rtw88: Pass RX buffer to rtw_rx_query_rx_desc() Bitterblue Smith
` (4 more replies)
0 siblings, 5 replies; 8+ messages in thread
From: Bitterblue Smith @ 2026-10-23 22:57 UTC (permalink / raw)
To: linux-wireless@vger.kernel.org; +Cc: Ping-Ke Shih
This series extends rtw88_pci to support an older design, like
RTL8821AE, RTL8812AE, RTL8723BE, etc.
After this, another ~20 small patches can enable support for RTL8821AE
and RTL8812AE. Then rtl8821ae can be deleted from rtlwifi.
Why delete rtl8821ae?
* RTL8821AE loses the connection on 2.4 GHz several times a day when
using Bluetooth headphones. Most of the time it's not noticeable
because it reconnects immediately, but sometimes (once so far) it
stays disconnected because it can no longer find any networks. No
clue what causes this.
* The mouse cursor can stutter when running iperf3.
* RTL8812AE has a strange problem with TX: iperf3 TX test shows
drops to 0 Mbps every few seconds, which can last several seconds.
This happens often.
* Most of the code for these chips is already in rtw88.
Theoretically rtlwifi supports WOWLAN, but it doesn't work for me with
RTL8821AE or RTL8812AE. rtw88 doesn't support WOWLAN with these chips.
rtw88 is not without problems, but in my experience it's more reliable
with these two chips. In the 6-8 months I used this code, I observed
these problems with rtw88:
* The top speed is a bit lower: ~220 Mbps instead of ~300 Mbps. In the
case of TX, the difference is due to the EDCA parameters. rtw88 uses
the ones from mac80211 (rtw_conf_tx()), whereas rtlwifi uses its own
"turbo" values when there are not too many non-BE packets
(rtl8821ae_dm_check_edca_turbo()). Not sure why the RX speed is
lower.
* RTL8821AE RX speed gets limited to 70-100 Mbps sometimes. If I watch
a Youtube video with Bluetooth headphones for 10 minutes and then
switch to another wifi adapter while the video is playing, leaving
the RTL8821AE's wifi side idle, usually within 20 minutes it will
start to raise IMR_RDU when it reaches the end of the RX ring and
goes back to the beginning.
It uses RX descriptor number 511, then number 0, then it doesn't
want to use number 1 for "a while". It raises IMR_RDU once or twice,
and only then it moves on to number 1, 2...
Usually when it raises IMR_RDU there are 510 or 511 RX descriptors
available (out of 512), so it has no excuse.
Once it has raised IMR_RDU the RX speed doesn't exceed 70-100 Mbps
anymore.
Powering off and on again, which happens a lot when the wifi
interface is idle, doesn't help.
The only thing that seems to help is reloading the driver, but even
that may not always work.
Switching to another wifi adapter while playing some video is the
most reliable way to trigger the problem, but it can also happen
randomly, it just takes many hours.
Bitterblue Smith (5):
wifi: rtw88: Pass RX buffer to rtw_rx_query_rx_desc()
wifi: rtw88: Pass TX descriptor to rtw_tx_fill_tx_desc()
wifi: rtw88: pci: Introduce struct rtw_pci_gen
wifi: rtw88: pci: Add functions to support older chips
wifi: rtw88: pci: Handle more RX interrupts for RTL8821AE
drivers/net/wireless/realtek/rtw88/Makefile | 2 +-
drivers/net/wireless/realtek/rtw88/fw.c | 4 +-
drivers/net/wireless/realtek/rtw88/main.h | 3 +-
drivers/net/wireless/realtek/rtw88/pci.c | 167 +++---
drivers/net/wireless/realtek/rtw88/pci.h | 44 +-
drivers/net/wireless/realtek/rtw88/pci_old.c | 529 ++++++++++++++++++
drivers/net/wireless/realtek/rtw88/pci_old.h | 39 ++
drivers/net/wireless/realtek/rtw88/reg.h | 3 +
drivers/net/wireless/realtek/rtw88/rtw8723b.c | 2 +-
.../net/wireless/realtek/rtw88/rtw8723de.c | 7 +-
drivers/net/wireless/realtek/rtw88/rtw8723x.c | 2 +-
drivers/net/wireless/realtek/rtw88/rtw8723x.h | 4 +-
drivers/net/wireless/realtek/rtw88/rtw8812a.c | 2 +-
drivers/net/wireless/realtek/rtw88/rtw8814a.c | 2 +-
.../net/wireless/realtek/rtw88/rtw8814ae.c | 7 +-
drivers/net/wireless/realtek/rtw88/rtw8821a.c | 2 +-
drivers/net/wireless/realtek/rtw88/rtw8821c.c | 2 +-
.../net/wireless/realtek/rtw88/rtw8821ce.c | 9 +-
drivers/net/wireless/realtek/rtw88/rtw8822b.c | 2 +-
.../net/wireless/realtek/rtw88/rtw8822be.c | 7 +-
drivers/net/wireless/realtek/rtw88/rtw8822c.c | 2 +-
.../net/wireless/realtek/rtw88/rtw8822ce.c | 9 +-
drivers/net/wireless/realtek/rtw88/rx.c | 6 +-
drivers/net/wireless/realtek/rtw88/rx.h | 6 +-
drivers/net/wireless/realtek/rtw88/sdio.c | 8 +-
drivers/net/wireless/realtek/rtw88/tx.c | 4 +-
drivers/net/wireless/realtek/rtw88/tx.h | 15 +-
drivers/net/wireless/realtek/rtw88/usb.c | 21 +-
28 files changed, 802 insertions(+), 108 deletions(-)
create mode 100644 drivers/net/wireless/realtek/rtw88/pci_old.c
create mode 100644 drivers/net/wireless/realtek/rtw88/pci_old.h
--
2.55.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH rtw-next 1/5] wifi: rtw88: Pass RX buffer to rtw_rx_query_rx_desc()
2026-10-23 22:57 [PATCH rtw-next 0/5] wifi: rtw88: pci: Support an older design, like RTL8821AE/RTL8812AE Bitterblue Smith
@ 2026-10-23 22:59 ` Bitterblue Smith
2026-10-23 23:00 ` [PATCH rtw-next 2/5] wifi: rtw88: Pass TX descriptor to rtw_tx_fill_tx_desc() Bitterblue Smith
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Bitterblue Smith @ 2026-10-23 22:59 UTC (permalink / raw)
To: linux-wireless@vger.kernel.org; +Cc: Ping-Ke Shih
rtw_rx_query_rx_desc() assumes that the RX descriptor and the 802.11
frame are in the same buffer, but that's not true for the RTL8821AE.
It uses an older design which keeps the RX descriptor in the RX ring.
In order to support the RTL8821AE (PCIe) pass the RX buffer as a
separate pointer.
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
drivers/net/wireless/realtek/rtw88/pci.c | 4 +++-
drivers/net/wireless/realtek/rtw88/rx.c | 6 +++---
drivers/net/wireless/realtek/rtw88/rx.h | 2 +-
drivers/net/wireless/realtek/rtw88/sdio.c | 4 +++-
drivers/net/wireless/realtek/rtw88/usb.c | 4 +++-
5 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index 66d2e5f51444..c2d6d3a939cd 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -1056,6 +1056,7 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci,
dma_addr_t dma;
u32 new_len;
u8 *rx_desc;
+ u8 *rx_buf;
int ret;
count = rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci);
@@ -1068,7 +1069,8 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci,
dma_sync_single_for_cpu(rtwdev->dev, dma, RTK_PCI_RX_BUF_SIZE,
DMA_FROM_DEVICE);
rx_desc = skb->data;
- ret = rtw_rx_query_rx_desc(rtwdev, rx_desc,
+ rx_buf = rx_desc + pkt_desc_sz;
+ ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, rx_buf,
&pkt_stat, &rx_status);
if (ret)
goto next_rp;
diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c
index fde21c840ac0..0555e2e5c1ac 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.c
+++ b/drivers/net/wireless/realtek/rtw88/rx.c
@@ -265,10 +265,9 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev,
}
int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
- struct rtw_rx_pkt_stat *pkt_stat,
+ void *rx_buf, struct rtw_rx_pkt_stat *pkt_stat,
struct ieee80211_rx_status *rx_status)
{
- u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz;
struct rtw_rx_desc *rx_desc = rx_desc8;
struct ieee80211_hdr *hdr;
u32 enc_type, swdec;
@@ -321,7 +320,8 @@ int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
if (unlikely(pkt_stat->pkt_len <= FCS_LEN))
return -EINVAL;
- phy_status = rx_desc8 + desc_sz + pkt_stat->shift;
+ phy_status = rx_buf + pkt_stat->shift;
+
hdr = phy_status + pkt_stat->drv_info_sz;
pkt_stat->hdr = hdr;
diff --git a/drivers/net/wireless/realtek/rtw88/rx.h b/drivers/net/wireless/realtek/rtw88/rx.h
index 74359f641c76..ebef85a3f848 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.h
+++ b/drivers/net/wireless/realtek/rtw88/rx.h
@@ -46,7 +46,7 @@ struct rtw_rx_desc {
void rtw_rx_stats(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
struct sk_buff *skb);
int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
- struct rtw_rx_pkt_stat *pkt_stat,
+ void *rx_buf, struct rtw_rx_pkt_stat *pkt_stat,
struct ieee80211_rx_status *rx_status);
void rtw_update_rx_freq_from_ie(struct rtw_dev *rtwdev, struct sk_buff *skb,
struct ieee80211_rx_status *rx_status,
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index dc2fd0f8f9ff..2f0a6739df41 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -1360,6 +1360,7 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
u32 pkt_offset, curr_pkt_len;
size_t bufsz;
u8 *rx_desc;
+ u8 *rx_buf;
int ret;
bufsz = sdio_align_size(rtwsdio->sdio_func, rx_len);
@@ -1376,7 +1377,8 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
while (true) {
rx_desc = skb->data;
- ret = rtw_rx_query_rx_desc(rtwdev, rx_desc,
+ rx_buf = rx_desc + pkt_desc_sz;
+ ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, rx_buf,
&pkt_stat, &rx_status);
if (ret) {
dev_kfree_skb_any(skb);
diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
index 715a2ce342d9..a1b58061eb46 100644
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
@@ -682,6 +682,7 @@ static void rtw_usb_rx_handler(struct work_struct *work)
u32 pkt_offset, next_pkt, skb_len;
int limit, ret;
u8 *rx_desc;
+ u8 *rx_buf;
for (limit = 0; limit < 200; limit++) {
rx_skb = skb_dequeue(&rtwusb->rx_queue);
@@ -697,7 +698,8 @@ static void rtw_usb_rx_handler(struct work_struct *work)
rx_desc = rx_skb->data;
do {
- ret = rtw_rx_query_rx_desc(rtwdev, rx_desc,
+ rx_buf = rx_desc + pkt_desc_sz;
+ ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, rx_buf,
&pkt_stat, &rx_status);
if (ret)
break;
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH rtw-next 2/5] wifi: rtw88: Pass TX descriptor to rtw_tx_fill_tx_desc()
2026-10-23 22:57 [PATCH rtw-next 0/5] wifi: rtw88: pci: Support an older design, like RTL8821AE/RTL8812AE Bitterblue Smith
2026-10-23 22:59 ` [PATCH rtw-next 1/5] wifi: rtw88: Pass RX buffer to rtw_rx_query_rx_desc() Bitterblue Smith
@ 2026-10-23 23:00 ` Bitterblue Smith
2026-10-23 23:01 ` [PATCH rtw-next 3/5] wifi: rtw88: pci: Introduce struct rtw_pci_gen Bitterblue Smith
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Bitterblue Smith @ 2026-10-23 23:00 UTC (permalink / raw)
To: linux-wireless@vger.kernel.org; +Cc: Ping-Ke Shih
The current code assumes the TX descriptor is in the same buffer as
the 802.11 frame, but that's not true for the RTL8821AE. It uses an
older design which keeps the TX descriptor in the TX ring.
In order to support the RTL8821AE (PCIe) give rtw_tx_fill_tx_desc() a
pointer to struct rtw_tx_desc instead of struct sk_buff.
Also pass the TX descriptor to rtw_tx_fill_txdesc_checksum() as
pointer to struct rtw_tx_desc instead of u8.
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
drivers/net/wireless/realtek/rtw88/fw.c | 4 ++--
drivers/net/wireless/realtek/rtw88/main.h | 3 ++-
drivers/net/wireless/realtek/rtw88/pci.c | 4 ++--
drivers/net/wireless/realtek/rtw88/rtw8723b.c | 2 +-
drivers/net/wireless/realtek/rtw88/rtw8723x.c | 2 +-
drivers/net/wireless/realtek/rtw88/rtw8723x.h | 4 ++--
drivers/net/wireless/realtek/rtw88/rtw8812a.c | 2 +-
drivers/net/wireless/realtek/rtw88/rtw8814a.c | 2 +-
drivers/net/wireless/realtek/rtw88/rtw8821a.c | 2 +-
drivers/net/wireless/realtek/rtw88/rtw8821c.c | 2 +-
drivers/net/wireless/realtek/rtw88/rtw8822b.c | 2 +-
drivers/net/wireless/realtek/rtw88/rtw8822c.c | 2 +-
drivers/net/wireless/realtek/rtw88/sdio.c | 4 ++--
drivers/net/wireless/realtek/rtw88/tx.c | 4 ++--
drivers/net/wireless/realtek/rtw88/tx.h | 10 +++++-----
drivers/net/wireless/realtek/rtw88/usb.c | 17 +++++++++--------
16 files changed, 34 insertions(+), 32 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
index 945fedcd375b..3a9e1325a519 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.c
+++ b/drivers/net/wireless/realtek/rtw88/fw.c
@@ -1302,12 +1302,12 @@ static void rtw_fill_rsvd_page_desc(struct rtw_dev *rtwdev, struct sk_buff *skb,
{
struct rtw_tx_pkt_info pkt_info = {0};
const struct rtw_chip_info *chip = rtwdev->chip;
- u8 *pkt_desc;
+ struct rtw_tx_desc *pkt_desc;
rtw_tx_rsvd_page_pkt_info_update(rtwdev, &pkt_info, skb, type);
pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
- rtw_tx_fill_tx_desc(rtwdev, &pkt_info, skb);
+ rtw_tx_fill_tx_desc(rtwdev, &pkt_info, pkt_desc);
}
static inline u8 rtw_len_to_page(unsigned int len, u16 page_size)
diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
index d59f6e323adf..6287994f9b31 100644
--- a/drivers/net/wireless/realtek/rtw88/main.h
+++ b/drivers/net/wireless/realtek/rtw88/main.h
@@ -51,6 +51,7 @@ extern const struct ieee80211_ops rtw_ops;
struct rtw_dev;
struct rtw_debugfs;
+struct rtw_tx_desc;
enum rtw_hci_type {
RTW_HCI_TYPE_PCIE,
@@ -913,7 +914,7 @@ struct rtw_chip_ops {
/* for USB/SDIO only */
void (*fill_txdesc_checksum)(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc);
+ struct rtw_tx_desc *txdesc);
/* for coex */
void (*coex_set_init)(struct rtw_dev *rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index c2d6d3a939cd..d8c5c11bf728 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -811,7 +811,7 @@ static int rtw_pci_tx_write_data(struct rtw_dev *rtwdev,
u32 tx_buf_desc_sz = chip->tx_buf_desc_sz;
u32 size;
u32 psb_len;
- u8 *pkt_desc;
+ struct rtw_tx_desc *pkt_desc;
struct rtw_pci_tx_buffer_desc *buf_desc;
ring = &rtwpci->tx_rings[queue];
@@ -826,7 +826,7 @@ static int rtw_pci_tx_write_data(struct rtw_dev *rtwdev,
pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
memset(pkt_desc, 0, tx_pkt_desc_sz);
pkt_info->qsel = rtw_pci_get_tx_qsel(skb, queue);
- rtw_tx_fill_tx_desc(rtwdev, pkt_info, skb);
+ rtw_tx_fill_tx_desc(rtwdev, pkt_info, pkt_desc);
dma = dma_map_single(&rtwpci->pdev->dev, skb->data, skb->len,
DMA_TO_DEVICE);
if (dma_mapping_error(&rtwpci->pdev->dev, dma))
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723b.c b/drivers/net/wireless/realtek/rtw88/rtw8723b.c
index 8bb9c03d769c..af78277fcb17 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8723b.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8723b.c
@@ -2343,7 +2343,7 @@ static void rtw8723b_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
static void rtw8723b_fill_txdesc_checksum(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc)
+ struct rtw_tx_desc *txdesc)
{
/* Unlike the shared 8723x helper, the checksum is not inverted here. */
fill_txdesc_checksum_common(txdesc, 16);
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723x.c b/drivers/net/wireless/realtek/rtw88/rtw8723x.c
index 97e6f6a62c0d..9a039cd5c299 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8723x.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8723x.c
@@ -681,7 +681,7 @@ void __rtw8723x_pwrtrack_set_xtal(struct rtw_dev *rtwdev, u8 therm_path,
static
void __rtw8723x_fill_txdesc_checksum(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc)
+ struct rtw_tx_desc *txdesc)
{
size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */
__le16 chksum = 0;
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723x.h b/drivers/net/wireless/realtek/rtw88/rtw8723x.h
index e07b6ca99b3d..5e674784dfb1 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8723x.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw8723x.h
@@ -154,7 +154,7 @@ struct rtw8723x_common {
void (*coex_cfg_init)(struct rtw_dev *rtwdev);
void (*fill_txdesc_checksum)(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc);
+ struct rtw_tx_desc *txdesc);
void (*debug_txpwr_limit)(struct rtw_dev *rtwdev,
struct rtw_txpwr_idx *table,
int tx_path_count);
@@ -510,7 +510,7 @@ static inline void rtw8723x_coex_cfg_init(struct rtw_dev *rtwdev)
static inline
void rtw8723x_fill_txdesc_checksum(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc)
+ struct rtw_tx_desc *txdesc)
{
rtw8723x_common.fill_txdesc_checksum(rtwdev, pkt_info, txdesc);
}
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index 781b66ccde67..61afaa02b857 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -886,7 +886,7 @@ static void rtw8812a_led_set(struct led_classdev *led,
static void rtw8812a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc)
+ struct rtw_tx_desc *txdesc)
{
fill_txdesc_checksum_common(txdesc, 16);
}
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
index fa02aa299b5e..fac69e8fb222 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
@@ -2041,7 +2041,7 @@ static void rtw8814a_led_set(struct led_classdev *led,
static void rtw8814a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc)
+ struct rtw_tx_desc *txdesc)
{
size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
index 6096d3b8e41b..f0987196caee 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -733,7 +733,7 @@ static void rtw8821a_led_set(struct led_classdev *led,
static void rtw8821a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc)
+ struct rtw_tx_desc *txdesc)
{
fill_txdesc_checksum_common(txdesc, 16);
}
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c
index ee689fa3d87f..da67a6845fd5 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c
@@ -1230,7 +1230,7 @@ static void rtw8821c_led_set(struct led_classdev *led,
static void rtw8821c_fill_txdesc_checksum(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc)
+ struct rtw_tx_desc *txdesc)
{
fill_txdesc_checksum_common(txdesc, 16);
}
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
index a56c8befa077..a33ed11b2a63 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
@@ -1593,7 +1593,7 @@ static void rtw8822b_led_set(struct led_classdev *led,
static void rtw8822b_fill_txdesc_checksum(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc)
+ struct rtw_tx_desc *txdesc)
{
size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
index 32d9709f2c24..d2d98ae98e33 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
@@ -4562,7 +4562,7 @@ static void rtw8822c_led_set(struct led_classdev *led,
static void rtw8822c_fill_txdesc_checksum(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc)
+ struct rtw_tx_desc *txdesc)
{
const struct rtw_chip_info *chip = rtwdev->chip;
size_t words;
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index 2f0a6739df41..d9a73d30b7f5 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -1180,8 +1180,8 @@ static void rtw_sdio_tx_skb_prepare(struct rtw_dev *rtwdev,
{
const struct rtw_chip_info *chip = rtwdev->chip;
unsigned long data_addr, aligned_addr;
+ struct rtw_tx_desc *pkt_desc;
size_t offset;
- u8 *pkt_desc;
pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
@@ -1207,7 +1207,7 @@ static void rtw_sdio_tx_skb_prepare(struct rtw_dev *rtwdev,
pkt_info->qsel = rtw_sdio_get_tx_qsel(rtwdev, skb, queue);
- rtw_tx_fill_tx_desc(rtwdev, pkt_info, skb);
+ rtw_tx_fill_tx_desc(rtwdev, pkt_info, pkt_desc);
rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, pkt_desc);
}
diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c
index 701ed7c4f3f7..49ce16352334 100644
--- a/drivers/net/wireless/realtek/rtw88/tx.c
+++ b/drivers/net/wireless/realtek/rtw88/tx.c
@@ -33,9 +33,9 @@ void rtw_tx_stats(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
}
void rtw_tx_fill_tx_desc(struct rtw_dev *rtwdev,
- struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb)
+ struct rtw_tx_pkt_info *pkt_info,
+ struct rtw_tx_desc *tx_desc)
{
- struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)skb->data;
bool more_data = false;
if (pkt_info->qsel == TX_DESC_QSEL_HIGH)
diff --git a/drivers/net/wireless/realtek/rtw88/tx.h b/drivers/net/wireless/realtek/rtw88/tx.h
index 168f48e87853..af031c02b4b7 100644
--- a/drivers/net/wireless/realtek/rtw88/tx.h
+++ b/drivers/net/wireless/realtek/rtw88/tx.h
@@ -97,7 +97,8 @@ void rtw_tx_pkt_info_update(struct rtw_dev *rtwdev,
struct ieee80211_sta *sta,
struct sk_buff *skb);
void rtw_tx_fill_tx_desc(struct rtw_dev *rtwdev,
- struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb);
+ struct rtw_tx_pkt_info *pkt_info,
+ struct rtw_tx_desc *tx_desc);
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_rsvd_page_pkt_info_update(struct rtw_dev *rtwdev,
@@ -117,11 +118,10 @@ enum rtw_tx_queue_type rtw_tx_ac_to_hwq(enum ieee80211_ac_numbers ac);
enum rtw_tx_queue_type rtw_tx_queue_mapping(struct sk_buff *skb);
static inline
-void fill_txdesc_checksum_common(u8 *txdesc, size_t words)
+void fill_txdesc_checksum_common(struct rtw_tx_desc *tx_desc, size_t words)
{
__le16 chksum = 0;
- __le16 *data = (__le16 *)(txdesc);
- struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)txdesc;
+ __le16 *data = (__le16 *)(tx_desc);
le32p_replace_bits(&tx_desc->w7, 0, RTW_TX_DESC_W7_TXDESC_CHECKSUM);
@@ -134,7 +134,7 @@ void fill_txdesc_checksum_common(u8 *txdesc, size_t words)
static inline void rtw_tx_fill_txdesc_checksum(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- u8 *txdesc)
+ struct rtw_tx_desc *txdesc)
{
const struct rtw_chip_info *chip = rtwdev->chip;
diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
index a1b58061eb46..599e80650c65 100644
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
@@ -38,7 +38,7 @@ static void rtw_usb_fill_tx_checksum(struct rtw_usb *rtwusb,
le32p_replace_bits(&tx_desc->w7, agg_num, RTW_TX_DESC_W7_DMA_TXAGG_NUM);
pkt_info.pkt_offset = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_PKT_OFFSET);
- rtw_tx_fill_txdesc_checksum(rtwdev, &pkt_info, skb->data);
+ rtw_tx_fill_txdesc_checksum(rtwdev, &pkt_info, tx_desc);
}
static void rtw_usb_reg_sec(struct rtw_dev *rtwdev, u32 addr, __le32 *data)
@@ -511,6 +511,7 @@ static int rtw_usb_write_data(struct rtw_dev *rtwdev,
u8 *buf)
{
const struct rtw_chip_info *chip = rtwdev->chip;
+ struct rtw_tx_desc *pkt_desc;
struct sk_buff *skb;
unsigned int size;
u8 qsel;
@@ -525,10 +526,10 @@ static int rtw_usb_write_data(struct rtw_dev *rtwdev,
skb_reserve(skb, chip->tx_pkt_desc_sz);
skb_put_data(skb, buf, size);
- skb_push(skb, chip->tx_pkt_desc_sz);
- memset(skb->data, 0, chip->tx_pkt_desc_sz);
- rtw_tx_fill_tx_desc(rtwdev, pkt_info, skb);
- rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, skb->data);
+ pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
+ memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
+ rtw_tx_fill_tx_desc(rtwdev, pkt_info, pkt_desc);
+ rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, pkt_desc);
ret = rtw_usb_write_port(rtwdev, qsel, skb,
rtw_usb_write_port_complete, skb);
@@ -644,15 +645,15 @@ static int rtw_usb_tx_write(struct rtw_dev *rtwdev,
struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
const struct rtw_chip_info *chip = rtwdev->chip;
struct rtw_usb_tx_data *tx_data;
- u8 *pkt_desc;
+ struct rtw_tx_desc *pkt_desc;
int ep;
pkt_info->qsel = rtw_usb_tx_queue_mapping_to_qsel(rtwusb, skb);
pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
ep = qsel_to_ep(rtwusb, pkt_info->qsel);
- rtw_tx_fill_tx_desc(rtwdev, pkt_info, skb);
- rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, skb->data);
+ rtw_tx_fill_tx_desc(rtwdev, pkt_info, pkt_desc);
+ rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, pkt_desc);
tx_data = rtw_usb_get_tx_data(skb);
tx_data->sn = pkt_info->sn;
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH rtw-next 3/5] wifi: rtw88: pci: Introduce struct rtw_pci_gen
2026-10-23 22:57 [PATCH rtw-next 0/5] wifi: rtw88: pci: Support an older design, like RTL8821AE/RTL8812AE Bitterblue Smith
2026-10-23 22:59 ` [PATCH rtw-next 1/5] wifi: rtw88: Pass RX buffer to rtw_rx_query_rx_desc() Bitterblue Smith
2026-10-23 23:00 ` [PATCH rtw-next 2/5] wifi: rtw88: Pass TX descriptor to rtw_tx_fill_tx_desc() Bitterblue Smith
@ 2026-10-23 23:01 ` Bitterblue Smith
2026-10-24 8:41 ` Luka Gejak
2026-10-23 23:02 ` [PATCH rtw-next 4/5] wifi: rtw88: pci: Add functions to support older chips Bitterblue Smith
2026-10-23 23:05 ` [PATCH rtw-next 5/5] wifi: rtw88: pci: Handle more RX interrupts for RTL8821AE Bitterblue Smith
4 siblings, 1 reply; 8+ messages in thread
From: Bitterblue Smith @ 2026-10-23 23:01 UTC (permalink / raw)
To: linux-wireless@vger.kernel.org; +Cc: Ping-Ke Shih
Move a few functions into a new struct rtw_pci_gen in order to add
support for RTL8821AE and RTL8812AE. That is an older generation of
chips which handle the PCI communications differently.
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
drivers/net/wireless/realtek/rtw88/pci.c | 109 ++++++++++++------
drivers/net/wireless/realtek/rtw88/pci.h | 35 ++++++
.../net/wireless/realtek/rtw88/rtw8723de.c | 7 +-
.../net/wireless/realtek/rtw88/rtw8814ae.c | 7 +-
.../net/wireless/realtek/rtw88/rtw8821ce.c | 9 +-
.../net/wireless/realtek/rtw88/rtw8822be.c | 7 +-
.../net/wireless/realtek/rtw88/rtw8822ce.c | 9 +-
7 files changed, 143 insertions(+), 40 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index d8c5c11bf728..c51ad15cfba7 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -254,6 +254,8 @@ static int rtw_pci_init_rx_ring(struct rtw_dev *rtwdev,
struct rtw_pci_rx_ring *rx_ring,
u8 desc_size, u32 len)
{
+ struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
struct pci_dev *pdev = to_pci_dev(rtwdev->dev);
struct sk_buff *skb = NULL;
dma_addr_t dma;
@@ -269,6 +271,11 @@ static int rtw_pci_init_rx_ring(struct rtw_dev *rtwdev,
return -ENOMEM;
}
rx_ring->r.head = head;
+ rx_ring->r.dma = dma;
+ rx_ring->r.len = len;
+ rx_ring->r.desc_size = desc_size;
+ rx_ring->r.wp = 0;
+ rx_ring->r.rp = 0;
for (i = 0; i < len; i++) {
skb = dev_alloc_skb(buf_sz);
@@ -280,7 +287,7 @@ static int rtw_pci_init_rx_ring(struct rtw_dev *rtwdev,
memset(skb->data, 0, buf_sz);
rx_ring->buf[i] = skb;
- ret = rtw_pci_reset_rx_desc(rtwdev, skb, rx_ring, i, desc_size);
+ ret = pci_gen->reset_rx_desc(rtwdev, skb, rx_ring, i, desc_size);
if (ret) {
allocated = i;
dev_kfree_skb_any(skb);
@@ -288,12 +295,6 @@ static int rtw_pci_init_rx_ring(struct rtw_dev *rtwdev,
}
}
- rx_ring->r.dma = dma;
- rx_ring->r.len = len;
- rx_ring->r.desc_size = desc_size;
- rx_ring->r.wp = 0;
- rx_ring->r.rp = 0;
-
return 0;
err_out:
@@ -316,6 +317,7 @@ static int rtw_pci_init_rx_ring(struct rtw_dev *rtwdev,
static int rtw_pci_init_trx_ring(struct rtw_dev *rtwdev)
{
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
struct rtw_pci_tx_ring *tx_ring;
struct rtw_pci_rx_ring *rx_ring;
const struct rtw_chip_info *chip = rtwdev->chip;
@@ -329,7 +331,7 @@ static int rtw_pci_init_trx_ring(struct rtw_dev *rtwdev)
for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++) {
tx_ring = &rtwpci->tx_rings[i];
len = max_num_of_tx_queue(i);
- ret = rtw_pci_init_tx_ring(rtwdev, tx_ring, tx_desc_size, len);
+ ret = pci_gen->init_tx_ring(rtwdev, tx_ring, tx_desc_size, len);
if (ret)
goto out;
}
@@ -525,7 +527,7 @@ static void rtw_pci_dma_reset(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci)
rtwpci->rx_tag = 0;
}
-static int rtw_pci_setup(struct rtw_dev *rtwdev)
+static int rtw_pci_reset(struct rtw_dev *rtwdev)
{
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
@@ -535,6 +537,14 @@ static int rtw_pci_setup(struct rtw_dev *rtwdev)
return 0;
}
+static int rtw_pci_setup(struct rtw_dev *rtwdev)
+{
+ struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
+
+ return pci_gen->reset(rtwdev);
+}
+
static void rtw_pci_dma_release(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci)
{
struct rtw_pci_tx_ring *tx_ring;
@@ -710,7 +720,7 @@ static u32 __pci_get_hw_tx_ring_rp(struct rtw_dev *rtwdev, u8 pci_q)
return FIELD_GET(TRX_BD_IDX_MASK, bd_idx);
}
-static void __pci_flush_queue(struct rtw_dev *rtwdev, u8 pci_q, bool drop)
+static void rtw_pci_flush_queue(struct rtw_dev *rtwdev, u8 pci_q, bool drop)
{
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
struct rtw_pci_tx_ring *ring = &rtwpci->tx_rings[pci_q];
@@ -738,6 +748,9 @@ static void __pci_flush_queue(struct rtw_dev *rtwdev, u8 pci_q, bool drop)
static void __rtw_pci_flush_queues(struct rtw_dev *rtwdev, u32 pci_queues,
bool drop)
{
+ struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
+
u8 q;
for (q = 0; q < RTK_MAX_TX_QUEUE_NUM; q++) {
@@ -747,7 +760,7 @@ static void __rtw_pci_flush_queues(struct rtw_dev *rtwdev, u32 pci_queues,
continue;
if (pci_queues & BIT(q))
- __pci_flush_queue(rtwdev, q, drop);
+ pci_gen->flush_queue(rtwdev, q, drop);
}
}
@@ -790,11 +803,12 @@ static void rtw_pci_tx_kick_off_queue(struct rtw_dev *rtwdev,
static void rtw_pci_tx_kick_off(struct rtw_dev *rtwdev)
{
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
enum rtw_tx_queue_type queue;
for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++)
if (test_and_clear_bit(queue, rtwpci->tx_queued))
- rtw_pci_tx_kick_off_queue(rtwdev, queue);
+ pci_gen->tx_kick_off_queue(rtwdev, queue);
}
static int rtw_pci_tx_write_data(struct rtw_dev *rtwdev,
@@ -867,28 +881,32 @@ static int rtw_pci_tx_write_data(struct rtw_dev *rtwdev,
return 0;
}
+static void rtw_pci_kick_beacon_queue(struct rtw_dev *rtwdev)
+{
+ rtw_write8_set(rtwdev, RTK_PCI_TXBD_BCN_WORK, BIT_PCI_BCNQ_FLAG);
+}
+
static int rtw_pci_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf,
u32 size)
{
- struct sk_buff *skb;
+ struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
struct rtw_tx_pkt_info pkt_info = {0};
- u8 reg_bcn_work;
+ struct sk_buff *skb;
int ret;
skb = rtw_tx_write_data_rsvd_page_get(rtwdev, &pkt_info, buf, size);
if (!skb)
return -ENOMEM;
- ret = rtw_pci_tx_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_BCN);
+ ret = pci_gen->tx_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_BCN);
if (ret) {
rtw_err(rtwdev, "failed to write rsvd page data\n");
return ret;
}
/* reserved pages go through beacon queue */
- reg_bcn_work = rtw_read8(rtwdev, RTK_PCI_TXBD_BCN_WORK);
- reg_bcn_work |= BIT_PCI_BCNQ_FLAG;
- rtw_write8(rtwdev, RTK_PCI_TXBD_BCN_WORK, reg_bcn_work);
+ pci_gen->kick_beacon_queue(rtwdev);
return 0;
}
@@ -920,10 +938,11 @@ static int rtw_pci_tx_write(struct rtw_dev *rtwdev,
{
enum rtw_tx_queue_type queue = rtw_tx_queue_mapping(skb);
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
struct rtw_pci_tx_ring *ring;
int ret;
- ret = rtw_pci_tx_write_data(rtwdev, pkt_info, skb, queue);
+ ret = pci_gen->tx_write_data(rtwdev, pkt_info, skb, queue);
if (ret)
return ret;
@@ -1174,6 +1193,7 @@ static irqreturn_t rtw_pci_interrupt_threadfn(int irq, void *dev)
{
struct rtw_dev *rtwdev = dev;
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
u32 irq_status[4];
bool rx = false;
@@ -1181,19 +1201,19 @@ static irqreturn_t rtw_pci_interrupt_threadfn(int irq, void *dev)
rtw_pci_irq_recognized(rtwdev, rtwpci, irq_status);
if (irq_status[0] & IMR_MGNTDOK)
- rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_MGMT);
+ pci_gen->tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_MGMT);
if (irq_status[0] & IMR_HIGHDOK)
- rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_HI0);
+ pci_gen->tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_HI0);
if (irq_status[0] & IMR_BEDOK)
- rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_BE);
+ pci_gen->tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_BE);
if (irq_status[0] & IMR_BKDOK)
- rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_BK);
+ pci_gen->tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_BK);
if (irq_status[0] & IMR_VODOK)
- rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_VO);
+ pci_gen->tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_VO);
if (irq_status[0] & IMR_VIDOK)
- rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_VI);
+ pci_gen->tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_VI);
if (irq_status[3] & IMR_H2CDOK)
- rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_H2C);
+ pci_gen->tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_H2C);
if (irq_status[0] & IMR_ROK) {
rtw_pci_rx_isr(rtwdev);
rx = true;
@@ -1385,6 +1405,7 @@ static void rtw_pci_aspm_set(struct rtw_dev *rtwdev, bool enable)
static void rtw_pci_link_ps(struct rtw_dev *rtwdev, bool enter)
{
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
/* Like CLKREQ, ASPM is also implemented by two HW modules, and can
* only be enabled when host supports it.
@@ -1401,13 +1422,14 @@ static void rtw_pci_link_ps(struct rtw_dev *rtwdev, bool enter)
if ((enter && atomic_dec_if_positive(&rtwpci->link_usage) == 0) ||
(!enter && atomic_inc_return(&rtwpci->link_usage) == 1))
- rtw_pci_aspm_set(rtwdev, enter);
+ pci_gen->aspm_set(rtwdev, enter);
}
static void rtw_pci_link_cfg(struct rtw_dev *rtwdev)
{
- const struct rtw_chip_info *chip = rtwdev->chip;
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
+ const struct rtw_chip_info *chip = rtwdev->chip;
struct pci_dev *pdev = rtwpci->pdev;
u16 link_ctrl;
int ret;
@@ -1441,7 +1463,7 @@ static void rtw_pci_link_cfg(struct rtw_dev *rtwdev)
}
if (link_ctrl & PCI_EXP_LNKCTL_CLKREQ_EN)
- rtw_pci_clkreq_set(rtwdev, true);
+ pci_gen->clkreq_set(rtwdev, true);
rtwpci->link_ctrl = link_ctrl;
}
@@ -1664,6 +1686,7 @@ static int rtw_pci_napi_poll(struct napi_struct *napi, int budget)
struct rtw_pci *rtwpci = container_of(napi, struct rtw_pci, napi);
struct rtw_dev *rtwdev = container_of((void *)rtwpci, struct rtw_dev,
priv);
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
int work_done = 0;
if (rtwpci->rx_no_aspm)
@@ -1672,8 +1695,9 @@ static int rtw_pci_napi_poll(struct napi_struct *napi, int budget)
while (work_done < budget) {
u32 work_done_once;
- work_done_once = rtw_pci_rx_napi(rtwdev, rtwpci, RTW_RX_QUEUE_MPDU,
- budget - work_done);
+ work_done_once = pci_gen->rx_napi(rtwdev, rtwpci,
+ RTW_RX_QUEUE_MPDU,
+ budget - work_done);
if (work_done_once == 0)
break;
work_done += work_done_once;
@@ -1689,7 +1713,7 @@ static int rtw_pci_napi_poll(struct napi_struct *napi, int budget)
* not be processed immediately. Check whether dma ring is
* empty and perform napi_schedule accordingly.
*/
- if (rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci))
+ if (pci_gen->get_hw_rx_ring_nr(rtwdev, rtwpci))
napi_schedule(napi);
}
if (rtwpci->rx_no_aspm)
@@ -1826,6 +1850,7 @@ static const struct dmi_system_id rtw_pci_quirks[] = {
int rtw_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
+ struct rtw_pci_info *pci_info = (struct rtw_pci_info *)id->driver_data;
struct pci_dev *bridge = pci_upstream_bridge(pdev);
struct ieee80211_hw *hw;
struct rtw_dev *rtwdev;
@@ -1843,13 +1868,15 @@ int rtw_pci_probe(struct pci_dev *pdev,
rtwdev = hw->priv;
rtwdev->hw = hw;
rtwdev->dev = &pdev->dev;
- rtwdev->chip = (struct rtw_chip_info *)id->driver_data;
+ rtwdev->chip = pci_info->chip_info;
rtwdev->hci.ops = &rtw_pci_ops;
rtwdev->hci.type = RTW_HCI_TYPE_PCIE;
rtwpci = (struct rtw_pci *)rtwdev->priv;
atomic_set(&rtwpci->link_usage, 1);
+ rtwpci->gen = pci_info->pci_gen;
+
dmi_check_system(rtw_pci_quirks);
ret = rtw_core_init(rtwdev);
@@ -1966,6 +1993,22 @@ void rtw_pci_shutdown(struct pci_dev *pdev)
}
EXPORT_SYMBOL(rtw_pci_shutdown);
+const struct rtw_pci_gen rtw_pci_gen_new = {
+ .init_tx_ring = rtw_pci_init_tx_ring,
+ .reset_rx_desc = rtw_pci_reset_rx_desc,
+ .reset = rtw_pci_reset,
+ .flush_queue = rtw_pci_flush_queue,
+ .tx_kick_off_queue = rtw_pci_tx_kick_off_queue,
+ .tx_write_data = rtw_pci_tx_write_data,
+ .kick_beacon_queue = rtw_pci_kick_beacon_queue,
+ .tx_isr = rtw_pci_tx_isr,
+ .get_hw_rx_ring_nr = rtw_pci_get_hw_rx_ring_nr,
+ .rx_napi = rtw_pci_rx_napi,
+ .clkreq_set = rtw_pci_clkreq_set,
+ .aspm_set = rtw_pci_aspm_set,
+};
+EXPORT_SYMBOL(rtw_pci_gen_new);
+
MODULE_AUTHOR("Realtek Corporation");
MODULE_DESCRIPTION("Realtek PCI 802.11ac wireless driver");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/pci.h b/drivers/net/wireless/realtek/rtw88/pci.h
index 8ffdea11378f..5ab78ba3255d 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.h
+++ b/drivers/net/wireless/realtek/rtw88/pci.h
@@ -203,8 +203,37 @@ struct rtw_pci_rx_ring {
#define RX_TAG_MAX 8192
+struct rtw_pci;
+
+struct rtw_pci_gen {
+ int (*init_tx_ring)(struct rtw_dev *rtwdev,
+ struct rtw_pci_tx_ring *tx_ring,
+ u8 desc_size, u32 len);
+ int (*reset_rx_desc)(struct rtw_dev *rtwdev, struct sk_buff *skb,
+ struct rtw_pci_rx_ring *rx_ring, u32 idx,
+ u32 desc_sz);
+ int (*reset)(struct rtw_dev *rtwdev);
+ void (*flush_queue)(struct rtw_dev *rtwdev, u8 pci_q, bool drop);
+ void (*tx_kick_off_queue)(struct rtw_dev *rtwdev,
+ enum rtw_tx_queue_type queue);
+ int (*tx_write_data)(struct rtw_dev *rtwdev,
+ struct rtw_tx_pkt_info *pkt_info,
+ struct sk_buff *skb,
+ enum rtw_tx_queue_type queue);
+ void (*kick_beacon_queue)(struct rtw_dev *rtwdev);
+ void (*tx_isr)(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci,
+ u8 hw_queue);
+ int (*get_hw_rx_ring_nr)(struct rtw_dev *rtwdev,
+ struct rtw_pci *rtwpci);
+ u32 (*rx_napi)(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci,
+ u8 hw_queue, u32 limit);
+ void (*clkreq_set)(struct rtw_dev *rtwdev, bool enable);
+ void (*aspm_set)(struct rtw_dev *rtwdev, bool enable);
+};
+
struct rtw_pci {
struct pci_dev *pdev;
+ const struct rtw_pci_gen *gen;
/* Used for PCI interrupt. */
spinlock_t hwirq_lock;
@@ -230,8 +259,14 @@ struct rtw_pci {
void __iomem *mmap;
};
+struct rtw_pci_info {
+ const struct rtw_chip_info *chip_info;
+ const struct rtw_pci_gen *pci_gen;
+};
+
extern const struct dev_pm_ops rtw_pm_ops;
extern const struct pci_error_handlers rtw_pci_err_handler;
+extern const struct rtw_pci_gen rtw_pci_gen_new;
int rtw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id);
void rtw_pci_remove(struct pci_dev *pdev);
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723de.c b/drivers/net/wireless/realtek/rtw88/rtw8723de.c
index c6d0c88e5d81..894b6639ac98 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8723de.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8723de.c
@@ -7,10 +7,15 @@
#include "pci.h"
#include "rtw8723d.h"
+static const struct rtw_pci_info rtw_8723de_pci_info = {
+ .chip_info = &rtw8723d_hw_spec,
+ .pci_gen = &rtw_pci_gen_new,
+};
+
static const struct pci_device_id rtw_8723de_id_table[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xD723),
- .driver_data = (kernel_ulong_t)&rtw8723d_hw_spec
+ .driver_data = (kernel_ulong_t)&rtw_8723de_pci_info
},
{}
};
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814ae.c b/drivers/net/wireless/realtek/rtw88/rtw8814ae.c
index c7436f1c8d40..bfcf4c073a39 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8814ae.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814ae.c
@@ -7,10 +7,15 @@
#include "pci.h"
#include "rtw8814a.h"
+static const struct rtw_pci_info rtw_8814ae_pci_info = {
+ .chip_info = &rtw8814a_hw_spec,
+ .pci_gen = &rtw_pci_gen_new,
+};
+
static const struct pci_device_id rtw_8814ae_id_table[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8813),
- .driver_data = (kernel_ulong_t)&rtw8814a_hw_spec
+ .driver_data = (kernel_ulong_t)&rtw_8814ae_pci_info
},
{}
};
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821ce.c b/drivers/net/wireless/realtek/rtw88/rtw8821ce.c
index 52a19cb17daa..b696a83eeb33 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821ce.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821ce.c
@@ -7,14 +7,19 @@
#include "pci.h"
#include "rtw8821c.h"
+static const struct rtw_pci_info rtw_8821ce_pci_info = {
+ .chip_info = &rtw8821c_hw_spec,
+ .pci_gen = &rtw_pci_gen_new,
+};
+
static const struct pci_device_id rtw_8821ce_id_table[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xB821),
- .driver_data = (kernel_ulong_t)&rtw8821c_hw_spec
+ .driver_data = (kernel_ulong_t)&rtw_8821ce_pci_info
},
{
PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xC821),
- .driver_data = (kernel_ulong_t)&rtw8821c_hw_spec
+ .driver_data = (kernel_ulong_t)&rtw_8821ce_pci_info
},
{}
};
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822be.c b/drivers/net/wireless/realtek/rtw88/rtw8822be.c
index dda597d73219..b6ff59820bc5 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822be.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822be.c
@@ -7,10 +7,15 @@
#include "pci.h"
#include "rtw8822b.h"
+static const struct rtw_pci_info rtw_8822be_pci_info = {
+ .chip_info = &rtw8822b_hw_spec,
+ .pci_gen = &rtw_pci_gen_new,
+};
+
static const struct pci_device_id rtw_8822be_id_table[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xB822),
- .driver_data = (kernel_ulong_t)&rtw8822b_hw_spec
+ .driver_data = (kernel_ulong_t)&rtw_8822be_pci_info
},
{}
};
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822ce.c b/drivers/net/wireless/realtek/rtw88/rtw8822ce.c
index 7ae95415c224..96ec95582bc8 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822ce.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822ce.c
@@ -7,14 +7,19 @@
#include "pci.h"
#include "rtw8822c.h"
+static const struct rtw_pci_info rtw_8822ce_pci_info = {
+ .chip_info = &rtw8822c_hw_spec,
+ .pci_gen = &rtw_pci_gen_new,
+};
+
static const struct pci_device_id rtw_8822ce_id_table[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xC822),
- .driver_data = (kernel_ulong_t)&rtw8822c_hw_spec
+ .driver_data = (kernel_ulong_t)&rtw_8822ce_pci_info
},
{
PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xC82F),
- .driver_data = (kernel_ulong_t)&rtw8822c_hw_spec
+ .driver_data = (kernel_ulong_t)&rtw_8822ce_pci_info
},
{}
};
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH rtw-next 4/5] wifi: rtw88: pci: Add functions to support older chips
2026-10-23 22:57 [PATCH rtw-next 0/5] wifi: rtw88: pci: Support an older design, like RTL8821AE/RTL8812AE Bitterblue Smith
` (2 preceding siblings ...)
2026-10-23 23:01 ` [PATCH rtw-next 3/5] wifi: rtw88: pci: Introduce struct rtw_pci_gen Bitterblue Smith
@ 2026-10-23 23:02 ` Bitterblue Smith
2026-10-24 8:36 ` Luka Gejak
2026-10-23 23:05 ` [PATCH rtw-next 5/5] wifi: rtw88: pci: Handle more RX interrupts for RTL8821AE Bitterblue Smith
4 siblings, 1 reply; 8+ messages in thread
From: Bitterblue Smith @ 2026-10-23 23:02 UTC (permalink / raw)
To: linux-wireless@vger.kernel.org; +Cc: Ping-Ke Shih
Add pci_old.{c,h} to support older chips, like RTL8821AE and RTL8812AE.
They handle the PCI communications differently.
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
drivers/net/wireless/realtek/rtw88/Makefile | 2 +-
drivers/net/wireless/realtek/rtw88/pci.c | 9 +-
drivers/net/wireless/realtek/rtw88/pci.h | 6 +
drivers/net/wireless/realtek/rtw88/pci_old.c | 522 +++++++++++++++++++
drivers/net/wireless/realtek/rtw88/pci_old.h | 39 ++
drivers/net/wireless/realtek/rtw88/reg.h | 3 +
drivers/net/wireless/realtek/rtw88/rx.h | 4 +
drivers/net/wireless/realtek/rtw88/tx.h | 5 +
8 files changed, 584 insertions(+), 6 deletions(-)
create mode 100644 drivers/net/wireless/realtek/rtw88/pci_old.c
create mode 100644 drivers/net/wireless/realtek/rtw88/pci_old.h
diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile
index 6889b3796449..d4007ca2b2ca 100644
--- a/drivers/net/wireless/realtek/rtw88/Makefile
+++ b/drivers/net/wireless/realtek/rtw88/Makefile
@@ -110,7 +110,7 @@ obj-$(CONFIG_RTW88_8814AU) += rtw88_8814au.o
rtw88_8814au-objs := rtw8814au.o
obj-$(CONFIG_RTW88_PCI) += rtw88_pci.o
-rtw88_pci-objs := pci.o
+rtw88_pci-objs := pci.o pci_old.o
obj-$(CONFIG_RTW88_SDIO) += rtw88_sdio.o
rtw88_sdio-objs := sdio.o
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index c51ad15cfba7..35f45e3ede81 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -16,7 +16,7 @@
#include "mac.h"
static bool rtw_disable_msi;
-static bool rtw_pci_disable_aspm;
+bool rtw_pci_disable_aspm;
module_param_named(disable_msi, rtw_disable_msi, bool, 0644);
module_param_named(disable_aspm, rtw_pci_disable_aspm, bool, 0644);
MODULE_PARM_DESC(disable_msi, "Set Y to disable MSI interrupt support");
@@ -32,8 +32,7 @@ static const u32 rtw_pci_tx_queue_idx_addr[] = {
[RTW_TX_QUEUE_H2C] = RTK_PCI_TXBD_IDX_H2CQ,
};
-static u8 rtw_pci_get_tx_qsel(struct sk_buff *skb,
- enum rtw_tx_queue_type queue)
+u8 rtw_pci_get_tx_qsel(struct sk_buff *skb, enum rtw_tx_queue_type queue)
{
switch (queue) {
case RTW_TX_QUEUE_BCN:
@@ -675,8 +674,8 @@ static void rtw_pci_deep_ps(struct rtw_dev *rtwdev, bool enter)
spin_unlock_bh(&rtwpci->irq_lock);
}
-static void rtw_pci_release_rsvd_page(struct rtw_pci *rtwpci,
- struct rtw_pci_tx_ring *ring)
+void rtw_pci_release_rsvd_page(struct rtw_pci *rtwpci,
+ struct rtw_pci_tx_ring *ring)
{
struct sk_buff *prev = skb_dequeue(&ring->queue);
struct rtw_pci_tx_data *tx_data;
diff --git a/drivers/net/wireless/realtek/rtw88/pci.h b/drivers/net/wireless/realtek/rtw88/pci.h
index 5ab78ba3255d..92532f842ebc 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.h
+++ b/drivers/net/wireless/realtek/rtw88/pci.h
@@ -144,6 +144,8 @@
/* IMR 3 */
#define IMR_H2CDOK BIT(16)
+extern bool rtw_pci_disable_aspm;
+
enum rtw_pci_flags {
RTW_PCI_FLAG_NAPI_RUNNING,
@@ -268,6 +270,10 @@ extern const struct dev_pm_ops rtw_pm_ops;
extern const struct pci_error_handlers rtw_pci_err_handler;
extern const struct rtw_pci_gen rtw_pci_gen_new;
+u8 rtw_pci_get_tx_qsel(struct sk_buff *skb, enum rtw_tx_queue_type queue);
+void rtw_pci_release_rsvd_page(struct rtw_pci *rtwpci,
+ struct rtw_pci_tx_ring *ring);
+
int rtw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id);
void rtw_pci_remove(struct pci_dev *pdev);
void rtw_pci_shutdown(struct pci_dev *pdev);
diff --git a/drivers/net/wireless/realtek/rtw88/pci_old.c b/drivers/net/wireless/realtek/rtw88/pci_old.c
new file mode 100644
index 000000000000..464b57d8a629
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/pci_old.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2026 Realtek Corporation
+ */
+
+#include <linux/pci.h>
+#include "main.h"
+#include "debug.h"
+#include "fw.h"
+#include "pci.h"
+#include "pci_old.h"
+#include "reg.h"
+#include "rx.h"
+#include "tx.h"
+
+static int rtw_pci_old_init_tx_ring(struct rtw_dev *rtwdev,
+ struct rtw_pci_tx_ring *tx_ring,
+ u8 desc_size, u32 len)
+{
+ struct pci_dev *pdev = to_pci_dev(rtwdev->dev);
+ struct rtw_tx_desc_pci_old *tx_desc;
+ int ring_sz = desc_size * len;
+ dma_addr_t dma, next;
+ u8 *head;
+ int i;
+
+ head = dma_alloc_coherent(&pdev->dev, ring_sz, &dma, GFP_KERNEL);
+ if (!head) {
+ rtw_err(rtwdev, "failed to allocate tx ring\n");
+ return -ENOMEM;
+ }
+
+ skb_queue_head_init(&tx_ring->queue);
+ tx_ring->r.head = head;
+ tx_ring->r.dma = dma;
+ tx_ring->r.len = len;
+ tx_ring->r.desc_size = desc_size;
+ tx_ring->r.wp = 0;
+ tx_ring->r.rp = 0;
+
+ tx_desc = (struct rtw_tx_desc_pci_old *)head;
+
+ for (i = 0; i < len; i++) {
+ next = dma + ((i + 1) % len) * desc_size;
+
+ le32p_replace_bits(&tx_desc[i].w12, next,
+ RTW_TX_DESC_W12_NEXT_DESC_ADDRESS);
+ }
+
+ return 0;
+}
+
+static int rtw_pci_old_reset_rx_desc(struct rtw_dev *rtwdev,
+ struct sk_buff *skb,
+ struct rtw_pci_rx_ring *rx_ring,
+ u32 idx, u32 desc_sz)
+{
+ struct pci_dev *pdev = to_pci_dev(rtwdev->dev);
+ struct rtw_rx_desc_pci_old *rx_desc;
+ dma_addr_t dma;
+
+ if (!skb)
+ return -EINVAL;
+
+ dma = dma_map_single(&pdev->dev, skb->data, RTK_PCI_RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&pdev->dev, dma))
+ return -EBUSY;
+
+ *((dma_addr_t *)skb->cb) = dma;
+
+ rx_desc = (struct rtw_rx_desc_pci_old *)rx_ring->r.head;
+
+ memset(&rx_desc[idx], 0, sizeof(*rx_desc));
+
+ le32p_replace_bits(&rx_desc[idx].w6, dma, RTW_RX_DESC_W6_BUF_ADDR);
+ le32p_replace_bits(&rx_desc[idx].w0_5.w0, RTK_PCI_RX_BUF_SIZE,
+ RTW_RX_DESC_W0_PKT_LEN);
+ le32p_replace_bits(&rx_desc[idx].w0_5.w0, 1, RTW_RX_DESC_W0_OWN);
+
+ if (idx == rx_ring->r.len - 1)
+ le32p_replace_bits(&rx_desc[idx].w0_5.w0, 1,
+ RTW_RX_DESC_W0_EOR);
+
+ return 0;
+}
+
+static void rtw_pci_old_reset_buf_desc(struct rtw_dev *rtwdev)
+{
+ static const u32 addr[] = {
+ [RTW_TX_QUEUE_BK] = REG_BKQ_DESA,
+ [RTW_TX_QUEUE_BE] = REG_BEQ_DESA,
+ [RTW_TX_QUEUE_VI] = REG_VIQ_DESA,
+ [RTW_TX_QUEUE_VO] = REG_VOQ_DESA,
+ [RTW_TX_QUEUE_BCN] = REG_BCNQ_DESA,
+ [RTW_TX_QUEUE_MGMT] = REG_MGQ_DESA,
+ [RTW_TX_QUEUE_HI0] = REG_HQ_DESA,
+ };
+ struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ struct rtw_pci_tx_ring *ring;
+ u8 queue;
+
+ for (queue = 0; queue <= RTW_TX_QUEUE_HI0; queue++) {
+ ring = &rtwpci->tx_rings[queue];
+
+ ring->r.rp = 0;
+ ring->r.wp = 0;
+ rtw_write32(rtwdev, addr[queue], ring->r.dma);
+ }
+
+ rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.rp = 0;
+ rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.wp = 0;
+ rtw_write32(rtwdev, REG_RX_DESA,
+ rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.dma);
+
+ rtw_write8(rtwdev, RTK_PCI_CTRL + 3, 0x77);
+}
+
+static void rtw_pci_old_reset_dma(struct rtw_dev *rtwdev)
+{
+ u8 mac_power_on, trx_hang, pcie_dma_pause;
+ u32 rxdma;
+
+ mac_power_on = rtw_read8(rtwdev, REG_CR);
+ if (mac_power_on == 0xea)
+ mac_power_on = 0;
+
+ if (!rtw_read8_mask(rtwdev, REG_DBI_CTRL + 3, BIT(2))) {
+ rtw_write8_set(rtwdev, REG_DBI_CTRL + 3, BIT(2));
+ msleep(100);
+ }
+
+ trx_hang = rtw_read8_mask(rtwdev, REG_DBI_CTRL + 3, GENMASK(1, 0));
+
+ if (!trx_hang)
+ return;
+
+ if (trx_hang & BIT(0))
+ rtw_warn(rtwdev, "PCIe TX hang detected, resetting\n");
+
+ if (trx_hang & BIT(1))
+ rtw_warn(rtwdev, "PCIe RX hang detected, resetting\n");
+
+ /* 1. Disable register write lock. */
+ rtw_write8_clr(rtwdev, REG_RSV_CTRL, BIT(1));
+ if (rtwdev->chip->id != RTW_CHIP_TYPE_8812A)
+ rtw_write8_set(rtwdev, REG_PMC_DBG_CTRL2, BIT(2));
+
+ /* 2. Check and pause TRX DMA */
+ rxdma = rtw_read32(rtwdev, REG_RXPKT_NUM);
+ if (!(rxdma & BIT_RW_RELEASE))
+ rtw_write32(rtwdev, REG_RXPKT_NUM, rxdma | BIT_RW_RELEASE);
+
+ pcie_dma_pause = rtw_read8(rtwdev, RTK_PCI_CTRL + 1);
+ if (pcie_dma_pause != 0xFF)
+ rtw_write8(rtwdev, RTK_PCI_CTRL + 1, 0xFF);
+
+ if (mac_power_on)
+ /* 3. reset TRX function */
+ rtw_write8(rtwdev, REG_CR, 0);
+
+ /* 4. Reset PCIe DMA. */
+ rtw_write16_clr(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_PCIED);
+
+ /* 5. Enable PCIe DMA. */
+ rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_PCIED);
+
+ if (mac_power_on)
+ /* 6. enable TRX function */
+ rtw_write8(rtwdev, REG_CR, 0xFF);
+
+ /*
+ * 7. Restore PCIe autoload down bit. 8812AE does not have
+ * the definition.
+ */
+ if (rtwdev->chip->id != RTW_CHIP_TYPE_8812A)
+ rtw_write8_set(rtwdev, REG_SYS_STATUS2 + 2, BIT(1));
+
+ /* 8. release TRX DMA */
+ if (!(rxdma & BIT_RW_RELEASE))
+ rtw_write32_clr(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE);
+
+ rtw_write8(rtwdev, RTK_PCI_CTRL + 1, pcie_dma_pause);
+
+ /* 9. lock system register */
+ if (rtwdev->chip->id != RTW_CHIP_TYPE_8812A)
+ rtw_write8_clr(rtwdev, REG_PMC_DBG_CTRL2, BIT(2));
+}
+
+static int rtw_pci_old_reset(struct rtw_dev *rtwdev)
+{
+ rtw_pci_old_reset_dma(rtwdev);
+
+ rtw_pci_old_reset_buf_desc(rtwdev);
+
+ rtw_write32(rtwdev, REG_INT_MIG, 0);
+
+ rtw_write32(rtwdev, REG_MCUTST_1, 0);
+
+ rtw_write8(rtwdev, REG_MISC_CTRL, BIT_DIS_SECOND_CCA);
+
+ return 0;
+}
+
+static void rtw_pci_old_flush_queue(struct rtw_dev *rtwdev, u8 pci_q, bool drop)
+{
+ struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ struct rtw_pci_tx_ring *ring = &rtwpci->tx_rings[pci_q];
+ u8 i;
+
+ /*
+ * Because the time taked by the I/O in __pci_get_hw_tx_ring_rp is a
+ * bit dynamic, it's hard to define a reasonable fixed total timeout to
+ * use read_poll_timeout* helper. Instead, we can ensure a reasonable
+ * polling times, so we just use for loop with udelay here.
+ */
+ for (i = 0; i < 50; i++) {
+ if (skb_queue_len(&ring->queue) == 0)
+ return;
+
+ udelay(1);
+ }
+
+ if (!drop)
+ rtw_dbg(rtwdev, RTW_DBG_UNEXP,
+ "timed out to flush pci tx ring[%d]\n", pci_q);
+}
+
+static void rtw_pci_old_tx_kick_off_queue(struct rtw_dev *rtwdev,
+ enum rtw_tx_queue_type queue)
+{
+ struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ u16 queue_bit;
+
+ spin_lock_bh(&rtwpci->irq_lock);
+
+ if (queue >= RTW_TX_QUEUE_MGMT)
+ queue_bit = BIT(queue + 1);
+ else
+ queue_bit = BIT(queue);
+
+ rtw_write16(rtwdev, RTK_PCI_CTRL, queue_bit);
+
+ spin_unlock_bh(&rtwpci->irq_lock);
+}
+
+static int rtw_pci_old_tx_write_data(struct rtw_dev *rtwdev,
+ struct rtw_tx_pkt_info *pkt_info,
+ struct sk_buff *skb,
+ enum rtw_tx_queue_type queue)
+{
+ struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ struct rtw_tx_desc_pci_old *tx_desc;
+ struct rtw_tx_desc tmp_tx_desc = {};
+ struct rtw_pci_tx_data *tx_data;
+ struct rtw_pci_tx_ring *ring;
+ dma_addr_t dma;
+ u32 wp;
+
+ ring = &rtwpci->tx_rings[queue];
+
+ wp = ring->r.wp;
+ tx_desc = (struct rtw_tx_desc_pci_old *)ring->r.head;
+
+ if (queue == RTW_TX_QUEUE_BCN)
+ rtw_pci_release_rsvd_page(rtwpci, ring);
+ else if (le32_get_bits(tx_desc[wp].w0_9.w0, RTW_TX_DESC_W0_OWN))
+ return -ENOSPC;
+
+ dma = dma_map_single(&rtwpci->pdev->dev, skb->data, skb->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&rtwpci->pdev->dev, dma))
+ return -EBUSY;
+
+ pkt_info->qsel = rtw_pci_get_tx_qsel(skb, queue);
+
+ rtw_tx_fill_tx_desc(rtwdev, pkt_info, &tmp_tx_desc);
+
+ /*
+ * OWN (same bit as DISQSELSEQ) can only be set when everything
+ * is ready for DMA, or bad things will happen.
+ */
+ le32p_replace_bits(&tmp_tx_desc.w0, 0, RTW_TX_DESC_W0_OWN);
+
+ memcpy(&tx_desc[wp].w0_9, &tmp_tx_desc, sizeof(tmp_tx_desc));
+
+ le32p_replace_bits(&tx_desc[wp].w0_9.w0, 1, RTW_TX_DESC_W0_FS);
+ le32p_replace_bits(&tx_desc[wp].w0_9.w7, skb->len,
+ RTW_TX_DESC_W7_TX_BUFFER_SIZE);
+ le32p_replace_bits(&tx_desc[wp].w10, dma,
+ RTW_TX_DESC_W10_TX_BUFFER_ADDRESS);
+
+ tx_data = rtw_pci_get_tx_data(skb);
+ tx_data->dma = dma;
+ tx_data->sn = pkt_info->sn;
+
+ spin_lock_bh(&rtwpci->irq_lock);
+
+ skb_queue_tail(&ring->queue, skb);
+
+ /* The hardware must see this bit after all of the above. */
+ wmb();
+ le32p_replace_bits(&tx_desc[wp].w0_9.w0, 1, RTW_TX_DESC_W0_OWN);
+
+ if (queue == RTW_TX_QUEUE_BCN)
+ goto out_unlock;
+
+ /* update write-index, and kick it off later */
+ set_bit(queue, rtwpci->tx_queued);
+
+ ring->r.wp = (ring->r.wp + 1) % ring->r.len;
+
+out_unlock:
+ spin_unlock_bh(&rtwpci->irq_lock);
+
+ return 0;
+}
+
+static void rtw_pci_old_kick_beacon_queue(struct rtw_dev *rtwdev)
+{
+ rtw_write16(rtwdev, RTK_PCI_CTRL, BIT(RTW_TX_QUEUE_BCN));
+}
+
+static void rtw_pci_old_tx_isr(struct rtw_dev *rtwdev,
+ struct rtw_pci *rtwpci, u8 hw_queue)
+{
+ struct ieee80211_hw *hw = rtwdev->hw;
+ struct rtw_tx_desc_pci_old *tx_desc;
+ struct rtw_pci_tx_data *tx_data;
+ struct ieee80211_tx_info *info;
+ struct rtw_pci_tx_ring *ring;
+ struct sk_buff *skb;
+ dma_addr_t dma;
+
+ ring = &rtwpci->tx_rings[hw_queue];
+
+ tx_desc = (struct rtw_tx_desc_pci_old *)ring->r.head;
+
+ while (skb_queue_len(&ring->queue)) {
+ if (le32_get_bits(tx_desc[ring->r.rp].w0_9.w0,
+ RTW_TX_DESC_W0_OWN))
+ return;
+
+ skb = skb_dequeue(&ring->queue);
+
+ tx_data = rtw_pci_get_tx_data(skb);
+
+ dma = le32_get_bits(tx_desc[ring->r.rp].w10,
+ RTW_TX_DESC_W10_TX_BUFFER_ADDRESS);
+
+ if (dma != tx_data->dma)
+ rtw_err(rtwdev,
+ "tx_data dma %pad != tx_desc dma %pad\n",
+ &tx_data->dma, &dma);
+
+ dma_unmap_single(&rtwpci->pdev->dev, dma, skb->len,
+ DMA_TO_DEVICE);
+
+ ring->r.rp = (ring->r.rp + 1) % ring->r.len;
+
+ if (ring->queue_stopped &&
+ avail_desc(ring->r.wp, ring->r.rp, ring->r.len) <= 4) {
+ ieee80211_wake_queue(hw, skb_get_queue_mapping(skb));
+ ring->queue_stopped = false;
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+
+ /* enqueue to wait for tx report */
+ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
+ rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn);
+ continue;
+ }
+
+ /* always ACK for others, then they won't be marked as drop */
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
+ else
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
+ ieee80211_tx_info_clear_status(info);
+ ieee80211_tx_status_irqsafe(hw, skb);
+ }
+}
+
+static int rtw_pci_old_get_hw_rx_ring_nr(struct rtw_dev *rtwdev,
+ struct rtw_pci *rtwpci)
+{
+ /*
+ * TODO: is it correct? it seems to work, but should we count
+ * the number of occupied buffers for rtw_pci_napi_poll?
+ */
+ return 0;
+}
+
+static u32 rtw_pci_old_rx_napi(struct rtw_dev *rtwdev,
+ struct rtw_pci *rtwpci,
+ u8 hw_queue, u32 limit)
+{
+ struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU];
+ struct ieee80211_rx_status rx_status;
+ struct rtw_rx_desc_pci_old *rx_desc;
+ struct device *dev = rtwdev->dev;
+ struct rtw_rx_pkt_stat pkt_stat;
+ struct sk_buff *skb, *new;
+ u32 cur_rp = ring->r.rp;
+ u32 count, rx_done = 0;
+ dma_addr_t dma;
+ u32 pkt_offset;
+ u32 new_len;
+ u8 *rx_buf;
+ int ret;
+
+ rx_desc = (struct rtw_rx_desc_pci_old *)ring->r.head;
+
+ count = min(ring->r.len, limit);
+
+ while (count--) {
+ /* Wait for data to be filled by hardware */
+ if (le32_get_bits(rx_desc[cur_rp].w0_5.w0, RTW_RX_DESC_W0_OWN))
+ break;
+
+ skb = ring->buf[cur_rp];
+ dma = *((dma_addr_t *)skb->cb);
+ dma_sync_single_for_cpu(dev, dma, RTK_PCI_RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ rx_buf = skb->data;
+ ret = rtw_rx_query_rx_desc(rtwdev, &rx_desc[cur_rp].w0_5,
+ rx_buf, &pkt_stat, &rx_status);
+ if (ret)
+ goto next_rp;
+
+ pkt_offset = pkt_stat.shift + pkt_stat.drv_info_sz;
+
+ new_len = pkt_stat.pkt_len + pkt_offset;
+ new = dev_alloc_skb(new_len);
+ if (WARN_ONCE(!new, "rx routine starvation\n"))
+ goto next_rp;
+
+ skb_put_data(new, skb->data, new_len);
+
+ if (pkt_stat.is_c2h) {
+ rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, new);
+ } else {
+ /* remove phy_status */
+ skb_pull(new, pkt_offset);
+
+ rtw_rx_stats(rtwdev, pkt_stat.vif, new);
+ memcpy(new->cb, &rx_status, sizeof(rx_status));
+ ieee80211_rx_napi(rtwdev->hw, NULL, new, &rtwpci->napi);
+ rx_done++;
+ }
+
+next_rp:
+ /* new skb delivered to mac80211, re-enable original skb DMA */
+ dma_sync_single_for_device(dev, dma, RTK_PCI_RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ /* Reset the descriptor */
+ le32p_replace_bits(&rx_desc[cur_rp].w6, dma, RTW_RX_DESC_W6_BUF_ADDR);
+ le32p_replace_bits(&rx_desc[cur_rp].w0_5.w0,
+ RTK_PCI_RX_BUF_SIZE, RTW_RX_DESC_W0_PKT_LEN);
+ /* The hardware must see this bit after all of the above. */
+ wmb();
+ le32p_replace_bits(&rx_desc[cur_rp].w0_5.w0, 1,
+ RTW_RX_DESC_W0_OWN);
+
+ if (cur_rp == ring->r.len - 1)
+ le32p_replace_bits(&rx_desc[cur_rp].w0_5.w0, 1,
+ RTW_RX_DESC_W0_EOR);
+
+ cur_rp = (cur_rp + 1) % ring->r.len;
+ }
+
+ ring->r.rp = cur_rp;
+
+ return rx_done;
+}
+
+static void rtw_pci_old_clkreq_set(struct rtw_dev *rtwdev, bool enable)
+{
+}
+
+static void rtw_pci_old_aspm_set(struct rtw_dev *rtwdev, bool enable)
+{
+ struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+ struct pci_dev *pdev = rtwpci->pdev;
+
+ if (rtw_pci_disable_aspm)
+ return;
+
+ if (enable) {
+ pcie_capability_set_word(pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CCC | PCI_EXP_LNKCTL_ASPMC);
+
+ pcie_capability_set_word(pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
+ } else {
+ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
+
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CCC | PCI_EXP_LNKCTL_ASPMC,
+ PCI_EXP_LNKCTL_CCC);
+ }
+}
+
+const struct rtw_pci_gen rtw_pci_gen_old = {
+ .init_tx_ring = rtw_pci_old_init_tx_ring,
+ .reset_rx_desc = rtw_pci_old_reset_rx_desc,
+ .reset = rtw_pci_old_reset,
+ .flush_queue = rtw_pci_old_flush_queue,
+ .tx_kick_off_queue = rtw_pci_old_tx_kick_off_queue,
+ .tx_write_data = rtw_pci_old_tx_write_data,
+ .kick_beacon_queue = rtw_pci_old_kick_beacon_queue,
+ .tx_isr = rtw_pci_old_tx_isr,
+ .get_hw_rx_ring_nr = rtw_pci_old_get_hw_rx_ring_nr,
+ .rx_napi = rtw_pci_old_rx_napi,
+ .clkreq_set = rtw_pci_old_clkreq_set,
+ .aspm_set = rtw_pci_old_aspm_set,
+};
+EXPORT_SYMBOL(rtw_pci_gen_old);
diff --git a/drivers/net/wireless/realtek/rtw88/pci_old.h b/drivers/net/wireless/realtek/rtw88/pci_old.h
new file mode 100644
index 000000000000..56ca3dd1581a
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/pci_old.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2026 Realtek Corporation.*/
+
+#ifndef __RTW_PCI_OLD_H__
+#define __RTW_PCI_OLD_H__
+
+#include "rx.h"
+#include "tx.h"
+
+#define REG_BCNQ_DESA 0x0308
+#define REG_HQ_DESA 0x0310
+#define REG_MGQ_DESA 0x0318
+#define REG_VOQ_DESA 0x0320
+#define REG_VIQ_DESA 0x0328
+#define REG_BEQ_DESA 0x0330
+#define REG_BKQ_DESA 0x0338
+#define REG_RX_DESA 0x0340
+
+#define REG_DBI_CTRL 0x0350
+
+struct rtw_rx_desc_pci_old {
+ struct rtw_rx_desc w0_5;
+ __le32 w6;
+ __le32 w7;
+} __packed;
+
+struct rtw_tx_desc_pci_old {
+ struct rtw_tx_desc w0_9;
+ __le32 w10;
+ __le32 w11;
+ __le32 w12;
+ __le32 w13;
+ __le32 w14;
+ __le32 w15;
+} __packed;
+
+extern const struct rtw_pci_gen rtw_pci_gen_old;
+
+#endif
diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
index afcdb66fdb9a..b406d0729d16 100644
--- a/drivers/net/wireless/realtek/rtw88/reg.h
+++ b/drivers/net/wireless/realtek/rtw88/reg.h
@@ -11,6 +11,7 @@
#define BIT_FEN_PPLL BIT(7)
#define BIT_FEN_PCIEA BIT(6)
#define BIT_FEN_DIO_PCIE BIT(5)
+#define BIT_FEN_PCIED BIT(8)
#define BIT_FEN_CPUEN BIT(2)
#define BIT_FEN_USBA BIT(2)
#define BIT_FEN_BB_GLB_RST BIT(1)
@@ -188,6 +189,8 @@
#define BIT_USB_MODE_U2 1
#define BIT_USB_MODE_U3 2
+#define REG_PMC_DBG_CTRL2 0x00CC
+
#define REG_EFUSE_ACCESS 0x00CF
#define EFUSE_ACCESS_ON 0x69
#define EFUSE_ACCESS_OFF 0x00
diff --git a/drivers/net/wireless/realtek/rtw88/rx.h b/drivers/net/wireless/realtek/rtw88/rx.h
index ebef85a3f848..d5a6f57b5d87 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.h
+++ b/drivers/net/wireless/realtek/rtw88/rx.h
@@ -31,6 +31,8 @@ struct rtw_rx_desc {
#define RTW_RX_DESC_W0_SHIFT GENMASK(25, 24)
#define RTW_RX_DESC_W0_PHYST BIT(26)
#define RTW_RX_DESC_W0_SWDEC BIT(27)
+#define RTW_RX_DESC_W0_EOR BIT(30)
+#define RTW_RX_DESC_W0_OWN BIT(31)
#define RTW_RX_DESC_W1_MACID GENMASK(6, 0)
@@ -43,6 +45,8 @@ struct rtw_rx_desc {
#define RTW_RX_DESC_W5_TSFL GENMASK(31, 0)
+#define RTW_RX_DESC_W6_BUF_ADDR GENMASK(31, 0)
+
void rtw_rx_stats(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
struct sk_buff *skb);
int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
diff --git a/drivers/net/wireless/realtek/rtw88/tx.h b/drivers/net/wireless/realtek/rtw88/tx.h
index af031c02b4b7..b92f03cf7d54 100644
--- a/drivers/net/wireless/realtek/rtw88/tx.h
+++ b/drivers/net/wireless/realtek/rtw88/tx.h
@@ -27,7 +27,9 @@ 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_W0_OWN BIT(31)
#define RTW_TX_DESC_W1_MACID GENMASK(7, 0)
#define RTW_TX_DESC_W1_QSEL GENMASK(12, 8)
#define RTW_TX_DESC_W1_RATE_ID GENMASK(20, 16)
@@ -54,11 +56,14 @@ struct rtw_tx_desc {
#define RTW_TX_DESC_W5_DATA_RTS_SHORT BIT(12)
#define RTW_TX_DESC_W6_SW_DEFINE GENMASK(11, 0)
#define RTW_TX_DESC_W7_TXDESC_CHECKSUM GENMASK(15, 0)
+#define RTW_TX_DESC_W7_TX_BUFFER_SIZE GENMASK(15, 0)
#define RTW_TX_DESC_W7_DMA_TXAGG_NUM GENMASK(31, 24)
#define RTW_TX_DESC_W8_EN_HWSEQ BIT(15)
#define RTW_TX_DESC_W9_SW_SEQ GENMASK(23, 12)
#define RTW_TX_DESC_W9_TIM_EN BIT(7)
#define RTW_TX_DESC_W9_TIM_OFFSET GENMASK(6, 0)
+#define RTW_TX_DESC_W10_TX_BUFFER_ADDRESS GENMASK(31, 0)
+#define RTW_TX_DESC_W12_NEXT_DESC_ADDRESS GENMASK(31, 0)
enum rtw_tx_desc_queue_select {
TX_DESC_QSEL_TID0 = 0,
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH rtw-next 5/5] wifi: rtw88: pci: Handle more RX interrupts for RTL8821AE
2026-10-23 22:57 [PATCH rtw-next 0/5] wifi: rtw88: pci: Support an older design, like RTL8821AE/RTL8812AE Bitterblue Smith
` (3 preceding siblings ...)
2026-10-23 23:02 ` [PATCH rtw-next 4/5] wifi: rtw88: pci: Add functions to support older chips Bitterblue Smith
@ 2026-10-23 23:05 ` Bitterblue Smith
4 siblings, 0 replies; 8+ messages in thread
From: Bitterblue Smith @ 2026-10-23 23:05 UTC (permalink / raw)
To: linux-wireless@vger.kernel.org; +Cc: Ping-Ke Shih
RTL8821AE can raise IMR_RDU (RX descriptor unavailable) and IMR_RXFOVW
(RX FIFO overflow). If they are not handled, RX stops. Based on the
vendor driver and rtlwifi, the way to handle these is the same as the
normal IMR_ROK, so do that.
Move irq_mask from struct rtw_pci to struct rtw_pci_gen in order to
enable these new interrupts only for RTL8821AE and RTL8812AE.
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
Perhaps moving irq_mask is not necessary. The RTL8822CE, RTL8822BE,
RTL8821CE, RTL8723DE, and RTL8814AE vendor drivers enable these
interrupts, so maybe they can be enabled for all the chips.
---
drivers/net/wireless/realtek/rtw88/pci.c | 41 +++++++++-----------
drivers/net/wireless/realtek/rtw88/pci.h | 3 +-
drivers/net/wireless/realtek/rtw88/pci_old.c | 7 ++++
3 files changed, 28 insertions(+), 23 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index 35f45e3ede81..ee693395b8d3 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -373,20 +373,6 @@ static int rtw_pci_init(struct rtw_dev *rtwdev)
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
int ret = 0;
- rtwpci->irq_mask[0] = IMR_HIGHDOK |
- IMR_MGNTDOK |
- IMR_BKDOK |
- IMR_BEDOK |
- IMR_VIDOK |
- IMR_VODOK |
- IMR_ROK |
- IMR_BCNDMAINT_E |
- IMR_C2HCMD |
- 0;
- rtwpci->irq_mask[1] = IMR_TXFOVW |
- 0;
- rtwpci->irq_mask[3] = IMR_H2CDOK |
- 0;
spin_lock_init(&rtwpci->irq_lock);
spin_lock_init(&rtwpci->hwirq_lock);
ret = rtw_pci_init_trx_ring(rtwdev);
@@ -482,15 +468,17 @@ static void rtw_pci_reset_trx_ring(struct rtw_dev *rtwdev)
static void rtw_pci_enable_interrupt(struct rtw_dev *rtwdev,
struct rtw_pci *rtwpci, bool exclude_rx)
{
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
+ u32 imr0_unmask = exclude_rx ? IMR_ROK | IMR_RDU : 0;
+ u32 imr1_unmask = exclude_rx ? IMR_RXFOVW : 0;
unsigned long flags;
- u32 imr0_unmask = exclude_rx ? IMR_ROK : 0;
spin_lock_irqsave(&rtwpci->hwirq_lock, flags);
- rtw_write32(rtwdev, RTK_PCI_HIMR0, rtwpci->irq_mask[0] & ~imr0_unmask);
- rtw_write32(rtwdev, RTK_PCI_HIMR1, rtwpci->irq_mask[1]);
+ rtw_write32(rtwdev, RTK_PCI_HIMR0, pci_gen->irq_mask[0] & ~imr0_unmask);
+ rtw_write32(rtwdev, RTK_PCI_HIMR1, pci_gen->irq_mask[1] & ~imr1_unmask);
if (rtw_chip_wcpu_3081(rtwdev))
- rtw_write32(rtwdev, RTK_PCI_HIMR3, rtwpci->irq_mask[3]);
+ rtw_write32(rtwdev, RTK_PCI_HIMR3, pci_gen->irq_mask[3]);
rtwpci->irq_enabled = true;
@@ -1149,6 +1137,7 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci,
static void rtw_pci_irq_recognized(struct rtw_dev *rtwdev,
struct rtw_pci *rtwpci, u32 *irq_status)
{
+ const struct rtw_pci_gen *pci_gen = rtwpci->gen;
unsigned long flags;
spin_lock_irqsave(&rtwpci->hwirq_lock, flags);
@@ -1159,9 +1148,9 @@ static void rtw_pci_irq_recognized(struct rtw_dev *rtwdev,
irq_status[3] = rtw_read32(rtwdev, RTK_PCI_HISR3);
else
irq_status[3] = 0;
- irq_status[0] &= rtwpci->irq_mask[0];
- irq_status[1] &= rtwpci->irq_mask[1];
- irq_status[3] &= rtwpci->irq_mask[3];
+ irq_status[0] &= pci_gen->irq_mask[0];
+ irq_status[1] &= pci_gen->irq_mask[1];
+ irq_status[3] &= pci_gen->irq_mask[3];
rtw_write32(rtwdev, RTK_PCI_HISR0, irq_status[0]);
rtw_write32(rtwdev, RTK_PCI_HISR1, irq_status[1]);
if (rtw_chip_wcpu_3081(rtwdev))
@@ -1213,7 +1202,8 @@ static irqreturn_t rtw_pci_interrupt_threadfn(int irq, void *dev)
pci_gen->tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_VI);
if (irq_status[3] & IMR_H2CDOK)
pci_gen->tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_H2C);
- if (irq_status[0] & IMR_ROK) {
+ if (irq_status[0] & (IMR_ROK | IMR_RDU) ||
+ irq_status[1] & IMR_RXFOVW) {
rtw_pci_rx_isr(rtwdev);
rx = true;
}
@@ -2005,6 +1995,13 @@ const struct rtw_pci_gen rtw_pci_gen_new = {
.rx_napi = rtw_pci_rx_napi,
.clkreq_set = rtw_pci_clkreq_set,
.aspm_set = rtw_pci_aspm_set,
+
+ .irq_mask = { IMR_HIGHDOK | IMR_MGNTDOK | IMR_BKDOK |
+ IMR_BEDOK | IMR_VIDOK | IMR_VODOK | IMR_ROK |
+ IMR_BCNDMAINT_E | IMR_C2HCMD,
+ IMR_TXFOVW,
+ 0,
+ IMR_H2CDOK, }
};
EXPORT_SYMBOL(rtw_pci_gen_new);
diff --git a/drivers/net/wireless/realtek/rtw88/pci.h b/drivers/net/wireless/realtek/rtw88/pci.h
index 92532f842ebc..74074437e34b 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.h
+++ b/drivers/net/wireless/realtek/rtw88/pci.h
@@ -231,6 +231,8 @@ struct rtw_pci_gen {
u8 hw_queue, u32 limit);
void (*clkreq_set)(struct rtw_dev *rtwdev, bool enable);
void (*aspm_set)(struct rtw_dev *rtwdev, bool enable);
+
+ u32 irq_mask[4];
};
struct rtw_pci {
@@ -241,7 +243,6 @@ struct rtw_pci {
spinlock_t hwirq_lock;
/* Used for PCI TX ring/queueing, and enable INT. */
spinlock_t irq_lock;
- u32 irq_mask[4];
bool irq_enabled;
bool running;
diff --git a/drivers/net/wireless/realtek/rtw88/pci_old.c b/drivers/net/wireless/realtek/rtw88/pci_old.c
index 464b57d8a629..ef2da0adff37 100644
--- a/drivers/net/wireless/realtek/rtw88/pci_old.c
+++ b/drivers/net/wireless/realtek/rtw88/pci_old.c
@@ -518,5 +518,12 @@ const struct rtw_pci_gen rtw_pci_gen_old = {
.rx_napi = rtw_pci_old_rx_napi,
.clkreq_set = rtw_pci_old_clkreq_set,
.aspm_set = rtw_pci_old_aspm_set,
+
+ .irq_mask = { IMR_HIGHDOK | IMR_MGNTDOK | IMR_BKDOK |
+ IMR_BEDOK | IMR_VIDOK | IMR_VODOK | IMR_ROK |
+ IMR_BCNDMAINT_E | IMR_C2HCMD | IMR_RDU,
+ IMR_TXFOVW | IMR_RXFOVW,
+ 0,
+ 0, }
};
EXPORT_SYMBOL(rtw_pci_gen_old);
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH rtw-next 4/5] wifi: rtw88: pci: Add functions to support older chips
2026-10-23 23:02 ` [PATCH rtw-next 4/5] wifi: rtw88: pci: Add functions to support older chips Bitterblue Smith
@ 2026-10-24 8:36 ` Luka Gejak
0 siblings, 0 replies; 8+ messages in thread
From: Luka Gejak @ 2026-10-24 8:36 UTC (permalink / raw)
To: Bitterblue Smith, linux-wireless; +Cc: Ping-Ke Shih
Hi Bitterblue,
Thanks for working on this. I went through pci_old.c and have a few
questions.
October 24, 2026 at 1:02 AM, "Bitterblue Smith" <rtl8821cerfe2@gmail.com> wrote:
> +++ b/drivers/net/wireless/realtek/rtw88/pci_old.c
[...]
> +static void rtw_pci_old_reset_buf_desc(struct rtw_dev *rtwdev)
> +{
[...]
> + rtw_write32(rtwdev, REG_RX_DESA,
> + rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.dma);
> +
> + rtw_write8(rtwdev, RTK_PCI_CTRL + 3, 0x77);
> +}
If I read this right, this only runs on setup, and rtw_pci_stop() still
goes to the new rtw_pci_reset_buf_desc() when the device stops:
rtw_pci_stop()
rtw_pci_dma_release()
rtw_pci_reset_trx_ring()
rtw_pci_reset_buf_desc()
Some of those offsets seem to be different registers on these chips,
for example 0x340 is REG_RX_DESA and 0x338 is REG_BKQ_DESA in
pci_old.h. Since rtw88xxa_power_off() calls rtw_hci_stop() before it
clears REG_CR, I think RX would still be running when they get written.
Would it make sense to send rtw_pci_dma_release() through pci_gen too?
Also, after stop the descriptors keep their OWN bit and buffer address,
and only rp and wp are reset. If a frame was still queued at that point,
could the hardware pick it up again after the next start? Would clearing
them here help?
[...]
> + /*
> + * Because the time taked by the I/O in __pci_get_hw_tx_ring_rp is a
> + * bit dynamic, it's hard to define a reasonable fixed total timeout to
> + * use read_poll_timeout* helper. Instead, we can ensure a reasonable
> + * polling times, so we just use for loop with udelay here.
> + */
This comment looks copied, I don't see any I/O in this loop.
[...]
> + if (queue == RTW_TX_QUEUE_BCN)
> + rtw_pci_release_rsvd_page(rtwpci, ring);
> + else if (le32_get_bits(tx_desc[wp].w0_9.w0, RTW_TX_DESC_W0_OWN))
> + return -ENOSPC;
I'm wondering if checking only OWN is enough here. OWN is cleared when
the hardware is done, but the skb stays in ring->queue until
rtw_pci_old_tx_isr() runs. rtw_pci_tx_write() only stops the mac80211
queue of the current skb, and the MGMT ring can get frames from more
than one queue (BARs use the AC of their TID), so I think the ring could
fill up completely. Then wp == rp, and the next frame would overwrite a
descriptor whose skb isn't freed yet. Would the avail_desc() check from
rtw_pci_tx_write_data() make sense here too?
[...]
> + if (dma != tx_data->dma)
> + rtw_err(rtwdev,
> + "tx_data dma %pad != tx_desc dma %pad\n",
> + &tx_data->dma, &dma);
> +
> + dma_unmap_single(&rtwpci->pdev->dev, dma, skb->len,
> + DMA_TO_DEVICE);
If that can happen, dma here would belong to the next skb, so it would
be unmapped too early and this skb's mapping would leak. Maybe use
tx_data->dma like rtw_pci_tx_isr() does?
[...]
> + if (ring->queue_stopped &&
> + avail_desc(ring->r.wp, ring->r.rp, ring->r.len) <= 4) {
rtw_pci_tx_isr() uses > 4 here, so this seems to be the other way
around. Is that on purpose? I guess it came from rtlwifi.
[...]
> + /*
> + * TODO: is it correct? it seems to work, but should we count
> + * the number of occupied buffers for rtw_pci_napi_poll?
> + */
> + return 0;
About the TODO, I'm not sure returning 0 is enough. rtw_pci_napi_poll()
uses this to pick up frames that are already in the ring when interrupts
are enabled again. rtw_pci_old_rx_napi() doesn't count C2H or bad
frames, so it could return 0 while there's still data in the ring, which
would then wait for the next interrupt. Would checking OWN on the
descriptor at ring->r.rp be enough?
[...]
> + if (le32_get_bits(rx_desc[cur_rp].w0_5.w0, RTW_RX_DESC_W0_OWN))
> + break;
Does this need a dma_rmb() after the OWN check? As far as I understand,
a weakly ordered CPU like arm64 could read the rest of the descriptor
and the buffer before it sees OWN cleared, and dma_sync_single_for_cpu()
doesn't add a barrier when the device is DMA coherent. It looks like the
example in Documentation/memory-barriers.txt. The new design gets this
from the readl() in rtw_pci_get_hw_rx_ring_nr().
Also, I think the wmb() calls could be dma_wmb().
[...]
> + new_len = pkt_stat.pkt_len + pkt_offset;
> + new = dev_alloc_skb(new_len);
> + if (WARN_ONCE(!new, "rx routine starvation\n"))
> + goto next_rp;
> +
> + skb_put_data(new, skb->data, new_len);
rtw_pci_rx_napi() got a length check in 6e76e9ed273d ("wifi: rtw88: fix
OOB read from firmware RX descriptor exceeding DMA buffer"), should it
be added here too? rtw_rx_query_rx_desc() allows pkt_len up to 11454, so
with 32 bytes of drv_info and a shift of 3 new_len could reach 11489,
while the buffer is 11478 bytes.
[...]
> + wmb();
> + le32p_replace_bits(&rx_desc[cur_rp].w0_5.w0, 1,
> + RTW_RX_DESC_W0_OWN);
> +
> + if (cur_rp == ring->r.len - 1)
> + le32p_replace_bits(&rx_desc[cur_rp].w0_5.w0, 1,
> + RTW_RX_DESC_W0_EOR);
Is it safe to set EOR after OWN? le32p_replace_bits() reads and writes
back the whole w0, so if the hardware fills this descriptor in between,
I think the old value with OWN set could end up on top of its status,
and RX would wait there until the hardware wraps around. Setting EOR
before the wmb(), together with PKT_LEN, might avoid that.
[...]
> + pcie_capability_set_word(pdev, PCI_EXP_LNKCTL,
> + PCI_EXP_LNKCTL_CCC | PCI_EXP_LNKCTL_ASPMC);
checkpatch --strict complains about the alignment here.
[...]
> +++ b/drivers/net/wireless/realtek/rtw88/pci_old.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
Is GPL-2.0 only intended for this header? The rest of rtw88, pci_old.c
included, is GPL-2.0 OR BSD-3-Clause.
[...]
> +++ b/drivers/net/wireless/realtek/rtw88/reg.h
> @@ -11,6 +11,7 @@
> #define BIT_FEN_PPLL BIT(7)
> #define BIT_FEN_PCIEA BIT(6)
> #define BIT_FEN_DIO_PCIE BIT(5)
> +#define BIT_FEN_PCIED BIT(8)
Maybe put BIT(8) above BIT_FEN_PPLL, these seem to be sorted high to low.
Best regards,
Luka Gejak
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH rtw-next 3/5] wifi: rtw88: pci: Introduce struct rtw_pci_gen
2026-10-23 23:01 ` [PATCH rtw-next 3/5] wifi: rtw88: pci: Introduce struct rtw_pci_gen Bitterblue Smith
@ 2026-10-24 8:41 ` Luka Gejak
0 siblings, 0 replies; 8+ messages in thread
From: Luka Gejak @ 2026-10-24 8:41 UTC (permalink / raw)
To: Bitterblue Smith, linux-wireless; +Cc: Ping-Ke Shih
October 24, 2026 at 1:01 AM, "Bitterblue Smith" <rtl8821cerfe2@gmail.com> wrote:
>
> Move a few functions into a new struct rtw_pci_gen in order to add
> support for RTL8821AE and RTL8812AE. That is an older generation of
> chips which handle the PCI communications differently.
>
> Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
> ---
> [...]
> rx_ring->r.head = head;
> + rx_ring->r.dma = dma;
> + rx_ring->r.len = len;
> + rx_ring->r.desc_size = desc_size;
> + rx_ring->r.wp = 0;
> + rx_ring->r.rp = 0;
In my opinion the commit message should mention why these were moved
before the loop.
[...]
> {
> + struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
> + const struct rtw_pci_gen *pci_gen = rtwpci->gen;
> +
> u8 q;
The blank line before u8 q can go, so the declarations stay together.
[...]
> int rtw_pci_probe(struct pci_dev *pdev,
> const struct pci_device_id *id)
> {
> + struct rtw_pci_info *pci_info = (struct rtw_pci_info *)id->driver_data;
I think this should be a const pointer, since the pci_info tables are
const.
Best regards,
Luka Gejak
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-10-24 8:41 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-23 22:57 [PATCH rtw-next 0/5] wifi: rtw88: pci: Support an older design, like RTL8821AE/RTL8812AE Bitterblue Smith
2026-10-23 22:59 ` [PATCH rtw-next 1/5] wifi: rtw88: Pass RX buffer to rtw_rx_query_rx_desc() Bitterblue Smith
2026-10-23 23:00 ` [PATCH rtw-next 2/5] wifi: rtw88: Pass TX descriptor to rtw_tx_fill_tx_desc() Bitterblue Smith
2026-10-23 23:01 ` [PATCH rtw-next 3/5] wifi: rtw88: pci: Introduce struct rtw_pci_gen Bitterblue Smith
2026-10-24 8:41 ` Luka Gejak
2026-10-23 23:02 ` [PATCH rtw-next 4/5] wifi: rtw88: pci: Add functions to support older chips Bitterblue Smith
2026-10-24 8:36 ` Luka Gejak
2026-10-23 23:05 ` [PATCH rtw-next 5/5] wifi: rtw88: pci: Handle more RX interrupts for RTL8821AE Bitterblue Smith
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox