diff --git a/packages/linux/patches/rtlwifi/after-6.12/0036-wifi-rtl8xxxu-Fix-RTL8188EU-firmware-upload-block-si.patch b/packages/linux/patches/rtlwifi/after-6.12/0036-wifi-rtl8xxxu-Fix-RTL8188EU-firmware-upload-block-si.patch new file mode 100644 index 00000000000..8ef4b5b1a36 --- /dev/null +++ b/packages/linux/patches/rtlwifi/after-6.12/0036-wifi-rtl8xxxu-Fix-RTL8188EU-firmware-upload-block-si.patch @@ -0,0 +1,34 @@ +From 93b3a45645f13290745ef58bf99ad0877af29381 Mon Sep 17 00:00:00 2001 +From: Bitterblue Smith +Date: Mon, 23 Dec 2024 17:01:57 +0200 +Subject: [PATCH 36/41] wifi: rtl8xxxu: Fix RTL8188EU firmware upload block + size + +A user reports that the firmware upload consistently fails when it's +uploaded in chunks of 128 bytes, but it works when uploaded in chunks +of 196 bytes. The official driver uses 196 bytes also. + +Link: https://github.com/a5a5aa555oo/rtl8xxxu/issues/2 +Signed-off-by: Bitterblue Smith +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/cba8e2f2-32c4-4174-90ba-0219f29dbdde@gmail.com +--- + drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c +index 3d04df0f5bf4..766a7a7c7d28 100644 +--- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c ++++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c +@@ -1860,7 +1860,7 @@ struct rtl8xxxu_fileops rtl8188eu_fops = { + .set_crystal_cap = rtl8188f_set_crystal_cap, + .cck_rssi = rtl8188e_cck_rssi, + .led_classdev_brightness_set = rtl8188eu_led_brightness_set, +- .writeN_block_size = 128, ++ .writeN_block_size = 196, + .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), + .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), + .has_tx_report = 1, +-- +2.43.0 + diff --git a/packages/linux/patches/rtlwifi/after-6.12/0037-wifi-rtw88-Add-USB-PHY-configuration.patch b/packages/linux/patches/rtlwifi/after-6.12/0037-wifi-rtw88-Add-USB-PHY-configuration.patch new file mode 100644 index 00000000000..e167ccb8559 --- /dev/null +++ b/packages/linux/patches/rtlwifi/after-6.12/0037-wifi-rtw88-Add-USB-PHY-configuration.patch @@ -0,0 +1,148 @@ +From fc57e8bef80bbfaba1ce0554f3b3c8df825e30b9 Mon Sep 17 00:00:00 2001 +From: Bitterblue Smith +Date: Wed, 1 Jan 2025 18:16:32 +0200 +Subject: [PATCH 37/41] wifi: rtw88: Add USB PHY configuration + +Add some extra configuration for USB devices. Currently only RTL8822BU +version (cut) D needs this. The new code makes use of the existing +usb3_param_8822b array from rtw8822b.c. + +A user reported that TP-Link Archer T3U in USB 3 mode was randomly +disconnecting from USB: + +[ 26.036502] usb 2-2: new SuperSpeed USB device number 3 using xhci_hcd +... +[ 27.576491] usb 2-2: USB disconnect, device number 3 +[ 28.621528] usb 2-2: new SuperSpeed USB device number 4 using xhci_hcd +... +[ 45.984521] usb 2-2: USB disconnect, device number 4 +... +[ 46.845585] usb 2-2: new SuperSpeed USB device number 5 using xhci_hcd +... +[ 94.400380] usb 2-2: USB disconnect, device number 5 +... +[ 95.590421] usb 2-2: new SuperSpeed USB device number 6 using xhci_hcd + +This patch fixes that. + +Link: https://github.com/lwfinger/rtw88/issues/262 +Signed-off-by: Bitterblue Smith +Acked-by: Ping-Ke Shih +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/9d312b14-0146-4be8-9c50-ef432234db50@gmail.com +--- + drivers/net/wireless/realtek/rtw88/reg.h | 10 ++++ + drivers/net/wireless/realtek/rtw88/usb.c | 68 ++++++++++++++++++++++++ + 2 files changed, 78 insertions(+) + +diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h +index e4d506cf9c33..95a39ae74cd3 100644 +--- a/drivers/net/wireless/realtek/rtw88/reg.h ++++ b/drivers/net/wireless/realtek/rtw88/reg.h +@@ -871,7 +871,17 @@ + + #define REG_USB_MOD 0xf008 + #define REG_USB3_RXITV 0xf050 ++#define REG_USB2_PHY_ADR 0xfe40 ++#define REG_USB2_PHY_DAT 0xfe41 ++#define REG_USB2_PHY_CMD 0xfe42 ++#define BIT_USB2_PHY_CMD_TRG 0x81 + #define REG_USB_HRPWM 0xfe58 ++#define REG_USB3_PHY_ADR 0xff0c ++#define REG_USB3_PHY_DAT_L 0xff0d ++#define REG_USB3_PHY_DAT_H 0xff0e ++#define BIT_USB3_PHY_ADR_WR BIT(7) ++#define BIT_USB3_PHY_ADR_RD BIT(6) ++#define BIT_USB3_PHY_ADR_MASK GENMASK(5, 0) + + #define RF_MODE 0x00 + #define RF_MODOPT 0x01 +diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c +index 1572b61cf877..c4908db4ff0e 100644 +--- a/drivers/net/wireless/realtek/rtw88/usb.c ++++ b/drivers/net/wireless/realtek/rtw88/usb.c +@@ -1128,6 +1128,71 @@ static int rtw_usb_switch_mode(struct rtw_dev *rtwdev) + return rtw_usb_switch_mode_new(rtwdev); + } + ++#define USB_REG_PAGE 0xf4 ++#define USB_PHY_PAGE0 0x9b ++#define USB_PHY_PAGE1 0xbb ++ ++static void rtw_usb_phy_write(struct rtw_dev *rtwdev, u8 addr, u16 data, ++ enum usb_device_speed speed) ++{ ++ if (speed == USB_SPEED_SUPER) { ++ rtw_write8(rtwdev, REG_USB3_PHY_DAT_L, data & 0xff); ++ rtw_write8(rtwdev, REG_USB3_PHY_DAT_H, data >> 8); ++ rtw_write8(rtwdev, REG_USB3_PHY_ADR, addr | BIT_USB3_PHY_ADR_WR); ++ } else if (speed == USB_SPEED_HIGH) { ++ rtw_write8(rtwdev, REG_USB2_PHY_DAT, data); ++ rtw_write8(rtwdev, REG_USB2_PHY_ADR, addr); ++ rtw_write8(rtwdev, REG_USB2_PHY_CMD, BIT_USB2_PHY_CMD_TRG); ++ } ++} ++ ++static void rtw_usb_page_switch(struct rtw_dev *rtwdev, ++ enum usb_device_speed speed, u8 page) ++{ ++ if (speed == USB_SPEED_SUPER) ++ return; ++ ++ rtw_usb_phy_write(rtwdev, USB_REG_PAGE, page, speed); ++} ++ ++static void rtw_usb_phy_cfg(struct rtw_dev *rtwdev, ++ enum usb_device_speed speed) ++{ ++ const struct rtw_intf_phy_para *para = NULL; ++ u16 offset; ++ ++ if (!rtwdev->chip->intf_table) ++ return; ++ ++ if (speed == USB_SPEED_SUPER) ++ para = rtwdev->chip->intf_table->usb3_para; ++ else if (speed == USB_SPEED_HIGH) ++ para = rtwdev->chip->intf_table->usb2_para; ++ ++ if (!para) ++ return; ++ ++ for ( ; para->offset != 0xffff; para++) { ++ if (!(para->cut_mask & BIT(rtwdev->hal.cut_version))) ++ continue; ++ ++ offset = para->offset; ++ ++ if (para->ip_sel == RTW_IP_SEL_MAC) { ++ rtw_write8(rtwdev, offset, para->value); ++ } else { ++ if (offset > 0x100) ++ rtw_usb_page_switch(rtwdev, speed, USB_PHY_PAGE1); ++ else ++ rtw_usb_page_switch(rtwdev, speed, USB_PHY_PAGE0); ++ ++ offset &= 0xff; ++ ++ rtw_usb_phy_write(rtwdev, offset, para->value, speed); ++ } ++ } ++} ++ + int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) + { + struct rtw_dev *rtwdev; +@@ -1183,6 +1248,9 @@ int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) + goto err_destroy_rxwq; + } + ++ rtw_usb_phy_cfg(rtwdev, USB_SPEED_HIGH); ++ rtw_usb_phy_cfg(rtwdev, USB_SPEED_SUPER); ++ + ret = rtw_usb_switch_mode(rtwdev); + if (ret) { + /* Not a fail, but we do need to skip rtw_register_hw. */ +-- +2.43.0 + diff --git a/packages/linux/patches/rtlwifi/after-6.12/0038-wifi-rtw88-Delete-rf_type-member-of-struct-rtw_sta_i.patch b/packages/linux/patches/rtlwifi/after-6.12/0038-wifi-rtw88-Delete-rf_type-member-of-struct-rtw_sta_i.patch new file mode 100644 index 00000000000..ef76961dcb8 --- /dev/null +++ b/packages/linux/patches/rtlwifi/after-6.12/0038-wifi-rtw88-Delete-rf_type-member-of-struct-rtw_sta_i.patch @@ -0,0 +1,68 @@ +From 254af62d5fc0f34512854eabc46591f48d387cbb Mon Sep 17 00:00:00 2001 +From: Bitterblue Smith +Date: Wed, 1 Jan 2025 18:27:35 +0200 +Subject: [PATCH 38/41] wifi: rtw88: Delete rf_type member of struct + rtw_sta_info + +It's not used for anything. + +Signed-off-by: Bitterblue Smith +Acked-by: Ping-Ke Shih +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/b80f7904-c6b4-4d12-a5f9-69ab9b965732@gmail.com +--- + drivers/net/wireless/realtek/rtw88/main.c | 9 ++------- + drivers/net/wireless/realtek/rtw88/main.h | 1 - + 2 files changed, 2 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c +index e91530ed05a0..6993f93c8f06 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.c ++++ b/drivers/net/wireless/realtek/rtw88/main.c +@@ -1217,7 +1217,6 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, + u8 wireless_set; + u8 bw_mode; + u8 rate_id; +- u8 rf_type = RF_1T1R; + u8 stbc_en = 0; + u8 ldpc_en = 0; + u8 tx_num = 1; +@@ -1302,13 +1301,10 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, + break; + } + +- if (sta->deflink.vht_cap.vht_supported && ra_mask & 0xffc00000) { ++ if (sta->deflink.vht_cap.vht_supported && ra_mask & 0xffc00000) + tx_num = 2; +- rf_type = RF_2T2R; +- } else if (sta->deflink.ht_cap.ht_supported && ra_mask & 0xfff00000) { ++ else if (sta->deflink.ht_cap.ht_supported && ra_mask & 0xfff00000) + tx_num = 2; +- rf_type = RF_2T2R; +- } + + rate_id = get_rate_id(wireless_set, bw_mode, tx_num); + +@@ -1319,7 +1315,6 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, + si->bw_mode = bw_mode; + si->stbc_en = stbc_en; + si->ldpc_en = ldpc_en; +- si->rf_type = rf_type; + si->sgi_enable = is_support_sgi; + si->vht_enable = is_vht_enable; + si->ra_mask = ra_mask; +diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h +index 65c7acea41af..6ba9e0dcf9fd 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.h ++++ b/drivers/net/wireless/realtek/rtw88/main.h +@@ -757,7 +757,6 @@ struct rtw_sta_info { + u8 mac_id; + u8 rate_id; + enum rtw_bandwidth bw_mode; +- enum rtw_rf_type rf_type; + u8 stbc_en:2; + u8 ldpc_en:2; + bool sgi_enable; +-- +2.43.0 + diff --git a/packages/linux/patches/rtlwifi/after-6.12/0039-wifi-rtw88-8703b-Fix-RX-TX-issues.patch b/packages/linux/patches/rtlwifi/after-6.12/0039-wifi-rtw88-8703b-Fix-RX-TX-issues.patch new file mode 100644 index 00000000000..ca3afd6a646 --- /dev/null +++ b/packages/linux/patches/rtlwifi/after-6.12/0039-wifi-rtw88-8703b-Fix-RX-TX-issues.patch @@ -0,0 +1,64 @@ +From 62acd2ac82a8d5d13be9da982aad01bfd8260214 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Thu, 2 Jan 2025 23:50:53 -0800 +Subject: [PATCH 39/41] wifi: rtw88: 8703b: Fix RX/TX issues + +Fix 3 typos in 8703b driver. 2 typos in calibration routines are not +fatal and do not seem to have any impact, just fix them to match vendor +driver. + +However the last one in rtw8703b_set_channel_bb() clears too many bits +in REG_OFDM0_TX_PSD_NOISE, causing TX and RX issues (neither rate goes +above MCS0-MCS1). Vendor driver clears only 2 most significant bits. + +With the last typo fixed, the driver is able to reach MCS7 on Pinebook + +Cc: stable@vger.kernel.org +Fixes: 9bb762b3a957 ("wifi: rtw88: Add definitions for 8703b chip") +Signed-off-by: Vasily Khoruzhick +Acked-by: Ping-Ke Shih +Tested-by: Fiona Klute +Tested-by: Andrey Skvortsov +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/20250103075107.1337533-1-anarsoul@gmail.com +--- + drivers/net/wireless/realtek/rtw88/rtw8703b.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/wireless/realtek/rtw88/rtw8703b.c b/drivers/net/wireless/realtek/rtw88/rtw8703b.c +index a19b94d022ee..1d232adbdd7e 100644 +--- a/drivers/net/wireless/realtek/rtw88/rtw8703b.c ++++ b/drivers/net/wireless/realtek/rtw88/rtw8703b.c +@@ -903,7 +903,7 @@ static void rtw8703b_set_channel_bb(struct rtw_dev *rtwdev, u8 channel, u8 bw, + 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, 20), 0x0); ++ GENMASK(31, 30), 0x0); + rtw_write32(rtwdev, REG_BBRX_DFIR, 0x4A880000); + rtw_write32(rtwdev, REG_OFDM0_A_TX_AFE, 0x19F60000); + break; +@@ -1198,9 +1198,9 @@ static u8 rtw8703b_iqk_rx_path(struct rtw_dev *rtwdev, + 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, 0x8216000f); ++ rtw_write32(rtwdev, REG_TXIQK_PI_A_11N, 0x8214030f); + rtw_write32(rtwdev, REG_RXIQK_PI_A_11N, 0x28110000); +- rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x28110000); ++ rtw_write32(rtwdev, REG_TXIQK_PI_B, 0x82110000); + rtw_write32(rtwdev, REG_RXIQK_PI_B, 0x28110000); + + /* LOK setting */ +@@ -1372,7 +1372,7 @@ void rtw8703b_iqk_fill_a_matrix(struct rtw_dev *rtwdev, const s32 result[]) + return; + + tmp_rx_iqi |= FIELD_PREP(BIT_MASK_RXIQ_S1_X, result[IQK_S1_RX_X]); +- tmp_rx_iqi |= FIELD_PREP(BIT_MASK_RXIQ_S1_Y1, result[IQK_S1_RX_X]); ++ tmp_rx_iqi |= FIELD_PREP(BIT_MASK_RXIQ_S1_Y1, result[IQK_S1_RX_Y]); + rtw_write32(rtwdev, REG_A_RXIQI, tmp_rx_iqi); + rtw_write32_mask(rtwdev, REG_RXIQK_MATRIX_LSB_11N, BIT_MASK_RXIQ_S1_Y2, + BIT_SET_RXIQ_S1_Y2(result[IQK_S1_RX_Y])); +-- +2.43.0 + diff --git a/packages/linux/patches/rtlwifi/after-6.12/0040-wifi-rtw88-sdio-Fix-disconnection-after-beacon-loss.patch b/packages/linux/patches/rtlwifi/after-6.12/0040-wifi-rtw88-sdio-Fix-disconnection-after-beacon-loss.patch new file mode 100644 index 00000000000..3f6837a2590 --- /dev/null +++ b/packages/linux/patches/rtlwifi/after-6.12/0040-wifi-rtw88-sdio-Fix-disconnection-after-beacon-loss.patch @@ -0,0 +1,36 @@ +From f8d47d06677264fbb7d603f1524b9fe4937be0f8 Mon Sep 17 00:00:00 2001 +From: Fiona Klute +Date: Mon, 6 Jan 2025 15:54:34 +0200 +Subject: [PATCH 40/41] wifi: rtw88: sdio: Fix disconnection after beacon loss + +This is the equivalent of commit 28818b4d871b ("wifi: rtw88: usb: Fix +disconnection after beacon loss") for SDIO chips. +Tested on Pinephone (RTL8723CS), random disconnections became rare, +instead of a frequent nuisance. + +Cc: stable@vger.kernel.org +Signed-off-by: Fiona Klute +Tested-by: Vasily Khoruzhick # Tested on Pinebook +Acked-by: Ping-Ke Shih +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/20250106135434.35936-1-fiona.klute@gmx.de +--- + drivers/net/wireless/realtek/rtw88/sdio.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c +index 799230eb5f16..e024061bdbf7 100644 +--- a/drivers/net/wireless/realtek/rtw88/sdio.c ++++ b/drivers/net/wireless/realtek/rtw88/sdio.c +@@ -1192,6 +1192,8 @@ static void rtw_sdio_indicate_tx_status(struct rtw_dev *rtwdev, + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hw *hw = rtwdev->hw; + ++ skb_pull(skb, rtwdev->chip->tx_pkt_desc_sz); ++ + /* enqueue to wait for tx report */ + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) { + rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn); +-- +2.43.0 + diff --git a/packages/linux/patches/rtlwifi/after-6.12/0041-wifi-rtw88-Add-support-for-LED-blinking.patch b/packages/linux/patches/rtlwifi/after-6.12/0041-wifi-rtw88-Add-support-for-LED-blinking.patch new file mode 100644 index 00000000000..4a4422eee9c --- /dev/null +++ b/packages/linux/patches/rtlwifi/after-6.12/0041-wifi-rtw88-Add-support-for-LED-blinking.patch @@ -0,0 +1,451 @@ +From 43b890bc487226f22127d0a328b05ab017cbe9c0 Mon Sep 17 00:00:00 2001 +From: Bitterblue Smith +Date: Wed, 8 Jan 2025 13:41:23 +0200 +Subject: [PATCH 41/41] wifi: rtw88: Add support for LED blinking + +Register a struct led_classdev with the kernel's LED subsystem and +create a throughput-based trigger for it. Then mac80211 makes the LED +blink. + +Tested with Tenda U12 (RTL8812AU), Tenda U9 (RTL8811CU), TP-Link Archer +T2U Nano (RTL8811AU), TP-Link Archer T3U Plus (RTL8812BU), Edimax +EW-7611UCB (RTL8821AU), LM842 (RTL8822CU). + +Also tested with devices which don't have LEDs: the laptop's internal +RTL8822CE and a no-name RTL8723DU. + +Signed-off-by: Bitterblue Smith +Acked-by: Ping-Ke Shih +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/6c43451f-ab2f-4e76-ac6e-ff5a18dd981d@gmail.com +--- + drivers/net/wireless/realtek/rtw88/Makefile | 2 + + drivers/net/wireless/realtek/rtw88/led.c | 73 +++++++++++++++++++ + drivers/net/wireless/realtek/rtw88/led.h | 25 +++++++ + drivers/net/wireless/realtek/rtw88/main.c | 12 ++- + drivers/net/wireless/realtek/rtw88/main.h | 5 ++ + drivers/net/wireless/realtek/rtw88/reg.h | 12 +++ + drivers/net/wireless/realtek/rtw88/rtw8812a.c | 17 +++++ + drivers/net/wireless/realtek/rtw88/rtw8821a.c | 26 +++++++ + drivers/net/wireless/realtek/rtw88/rtw8821c.c | 19 +++++ + drivers/net/wireless/realtek/rtw88/rtw8822b.c | 19 +++++ + drivers/net/wireless/realtek/rtw88/rtw8822c.c | 19 +++++ + 11 files changed, 227 insertions(+), 2 deletions(-) + create mode 100644 drivers/net/wireless/realtek/rtw88/led.c + create mode 100644 drivers/net/wireless/realtek/rtw88/led.h + +diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile +index f0b49f5a8a5a..e8bad9d099a4 100644 +--- a/drivers/net/wireless/realtek/rtw88/Makefile ++++ b/drivers/net/wireless/realtek/rtw88/Makefile +@@ -20,6 +20,8 @@ rtw88_core-y += main.o \ + + rtw88_core-$(CONFIG_PM) += wow.o + ++rtw88_core-$(CONFIG_LEDS_CLASS) += led.o ++ + obj-$(CONFIG_RTW88_8822B) += rtw88_8822b.o + rtw88_8822b-objs := rtw8822b.o rtw8822b_table.o + +diff --git a/drivers/net/wireless/realtek/rtw88/led.c b/drivers/net/wireless/realtek/rtw88/led.c +new file mode 100644 +index 000000000000..25aa6cbaa728 +--- /dev/null ++++ b/drivers/net/wireless/realtek/rtw88/led.c +@@ -0,0 +1,73 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* Copyright(c) 2025 Realtek Corporation ++ */ ++ ++#include "main.h" ++#include "debug.h" ++#include "led.h" ++ ++static int rtw_led_set_blocking(struct led_classdev *led, ++ enum led_brightness brightness) ++{ ++ struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); ++ ++ rtwdev->chip->ops->led_set(led, brightness); ++ ++ return 0; ++} ++ ++void rtw_led_init(struct rtw_dev *rtwdev) ++{ ++ static const struct ieee80211_tpt_blink rtw_tpt_blink[] = { ++ { .throughput = 0 * 1024, .blink_time = 334 }, ++ { .throughput = 1 * 1024, .blink_time = 260 }, ++ { .throughput = 5 * 1024, .blink_time = 220 }, ++ { .throughput = 10 * 1024, .blink_time = 190 }, ++ { .throughput = 20 * 1024, .blink_time = 170 }, ++ { .throughput = 50 * 1024, .blink_time = 150 }, ++ { .throughput = 70 * 1024, .blink_time = 130 }, ++ { .throughput = 100 * 1024, .blink_time = 110 }, ++ { .throughput = 200 * 1024, .blink_time = 80 }, ++ { .throughput = 300 * 1024, .blink_time = 50 }, ++ }; ++ struct led_classdev *led = &rtwdev->led_cdev; ++ int err; ++ ++ if (!rtwdev->chip->ops->led_set) ++ return; ++ ++ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) ++ led->brightness_set = rtwdev->chip->ops->led_set; ++ else ++ led->brightness_set_blocking = rtw_led_set_blocking; ++ ++ snprintf(rtwdev->led_name, sizeof(rtwdev->led_name), ++ "rtw88-%s", dev_name(rtwdev->dev)); ++ ++ led->name = rtwdev->led_name; ++ led->max_brightness = LED_ON; ++ led->default_trigger = ++ ieee80211_create_tpt_led_trigger(rtwdev->hw, ++ IEEE80211_TPT_LEDTRIG_FL_RADIO, ++ rtw_tpt_blink, ++ ARRAY_SIZE(rtw_tpt_blink)); ++ ++ err = led_classdev_register(rtwdev->dev, led); ++ if (err) { ++ rtw_warn(rtwdev, "Failed to register the LED, error %d\n", err); ++ return; ++ } ++ ++ rtwdev->led_registered = true; ++} ++ ++void rtw_led_deinit(struct rtw_dev *rtwdev) ++{ ++ struct led_classdev *led = &rtwdev->led_cdev; ++ ++ if (!rtwdev->led_registered) ++ return; ++ ++ rtwdev->chip->ops->led_set(led, LED_OFF); ++ led_classdev_unregister(led); ++} +diff --git a/drivers/net/wireless/realtek/rtw88/led.h b/drivers/net/wireless/realtek/rtw88/led.h +new file mode 100644 +index 000000000000..c3bb6fe49b49 +--- /dev/null ++++ b/drivers/net/wireless/realtek/rtw88/led.h +@@ -0,0 +1,25 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ ++/* Copyright(c) 2025 Realtek Corporation ++ */ ++ ++#ifndef __RTW_LED_H ++#define __RTW_LED_H ++ ++#ifdef CONFIG_LEDS_CLASS ++ ++void rtw_led_init(struct rtw_dev *rtwdev); ++void rtw_led_deinit(struct rtw_dev *rtwdev); ++ ++#else ++ ++static inline void rtw_led_init(struct rtw_dev *rtwdev) ++{ ++} ++ ++static inline void rtw_led_deinit(struct rtw_dev *rtwdev) ++{ ++} ++ ++#endif ++ ++#endif +diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c +index 6993f93c8f06..0cee0fd8c0ef 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.c ++++ b/drivers/net/wireless/realtek/rtw88/main.c +@@ -19,6 +19,7 @@ + #include "bf.h" + #include "sar.h" + #include "sdio.h" ++#include "led.h" + + bool rtw_disable_lps_deep_mode; + EXPORT_SYMBOL(rtw_disable_lps_deep_mode); +@@ -2292,16 +2293,18 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) + return ret; + } + ++ rtw_led_init(rtwdev); ++ + ret = ieee80211_register_hw(hw); + if (ret) { + rtw_err(rtwdev, "failed to register hw\n"); +- return ret; ++ goto led_deinit; + } + + ret = rtw_regd_hint(rtwdev); + if (ret) { + rtw_err(rtwdev, "failed to hint regd\n"); +- return ret; ++ goto led_deinit; + } + + rtw_debugfs_init(rtwdev); +@@ -2310,6 +2313,10 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) + rtwdev->bf_info.bfer_su_cnt = 0; + + return 0; ++ ++led_deinit: ++ rtw_led_deinit(rtwdev); ++ return ret; + } + EXPORT_SYMBOL(rtw_register_hw); + +@@ -2320,6 +2327,7 @@ void rtw_unregister_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) + ieee80211_unregister_hw(hw); + rtw_unset_supported_band(hw, chip); + rtw_debugfs_deinit(rtwdev); ++ rtw_led_deinit(rtwdev); + } + EXPORT_SYMBOL(rtw_unregister_hw); + +diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h +index 6ba9e0dcf9fd..62cd4c526301 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.h ++++ b/drivers/net/wireless/realtek/rtw88/main.h +@@ -887,6 +887,7 @@ struct rtw_chip_ops { + bool is_tx2_path); + void (*config_txrx_mode)(struct rtw_dev *rtwdev, u8 tx_path, + u8 rx_path, bool is_tx2_path); ++ void (*led_set)(struct led_classdev *led, enum led_brightness brightness); + /* for USB/SDIO only */ + void (*fill_txdesc_checksum)(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, +@@ -2097,6 +2098,10 @@ struct rtw_dev { + struct completion fw_scan_density; + bool ap_active; + ++ bool led_registered; ++ char led_name[32]; ++ struct led_classdev led_cdev; ++ + /* hci related data, must be last */ + u8 priv[] __aligned(sizeof(void *)); + }; +diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h +index 95a39ae74cd3..e438405fba56 100644 +--- a/drivers/net/wireless/realtek/rtw88/reg.h ++++ b/drivers/net/wireless/realtek/rtw88/reg.h +@@ -78,7 +78,19 @@ + #define BIT_PAPE_SEL_EN BIT(25) + #define BIT_DPDT_WL_SEL BIT(24) + #define BIT_DPDT_SEL_EN BIT(23) ++#define BIT_GPIO13_14_WL_CTRL_EN BIT(22) ++#define BIT_LED2_SV BIT(19) ++#define BIT_LED2_CM GENMASK(18, 16) ++#define BIT_LED1_SV BIT(11) ++#define BIT_LED1_CM GENMASK(10, 8) ++#define BIT_LED0_SV BIT(3) ++#define BIT_LED0_CM GENMASK(2, 0) ++#define BIT_LED_MODE_SW_CTRL 0 ++#define BIT_LED_MODE_RX 6 ++#define BIT_LED_MODE_TX 4 ++#define BIT_LED_MODE_TRX 2 + #define REG_LEDCFG2 0x004E ++#define REG_GPIO_PIN_CTRL_2 0x0060 + #define REG_PAD_CTRL1 0x0064 + #define BIT_BT_BTG_SEL BIT(31) + #define BIT_PAPE_WLBT_SEL BIT(29) +diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c +index 21795286a1a0..f9ba2aa2928a 100644 +--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c ++++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c +@@ -868,6 +868,22 @@ static void rtw8812a_pwr_track(struct rtw_dev *rtwdev) + dm_info->pwr_trk_triggered = false; + } + ++static void rtw8812a_led_set(struct led_classdev *led, ++ enum led_brightness brightness) ++{ ++ struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); ++ u8 ledcfg; ++ ++ ledcfg = rtw_read8(rtwdev, REG_LED_CFG); ++ ledcfg &= BIT(6) | BIT(4); ++ ledcfg |= BIT(5); ++ ++ if (brightness == LED_OFF) ++ ledcfg |= BIT(3); ++ ++ rtw_write8(rtwdev, REG_LED_CFG, ledcfg); ++} ++ + static void rtw8812a_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +@@ -916,6 +932,7 @@ static const struct rtw_chip_ops rtw8812a_ops = { + .config_bfee = NULL, + .set_gid_table = NULL, + .cfg_csi_rate = NULL, ++ .led_set = rtw8812a_led_set, + .fill_txdesc_checksum = rtw8812a_fill_txdesc_checksum, + .coex_set_init = rtw8812a_coex_cfg_init, + .coex_set_ant_switch = NULL, +diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c +index dafab2af33bc..f68239b07319 100644 +--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c ++++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c +@@ -706,6 +706,31 @@ static void rtw8821a_pwr_track(struct rtw_dev *rtwdev) + dm_info->pwr_trk_triggered = false; + } + ++static void rtw8821a_led_set(struct led_classdev *led, ++ enum led_brightness brightness) ++{ ++ struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); ++ u32 gpio8_cfg; ++ u8 ledcfg; ++ ++ if (brightness == LED_OFF) { ++ gpio8_cfg = rtw_read32(rtwdev, REG_GPIO_PIN_CTRL_2); ++ gpio8_cfg &= ~BIT(24); ++ gpio8_cfg |= BIT(16) | BIT(8); ++ rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, gpio8_cfg); ++ } else { ++ ledcfg = rtw_read8(rtwdev, REG_LED_CFG + 2); ++ gpio8_cfg = rtw_read32(rtwdev, REG_GPIO_PIN_CTRL_2); ++ ++ ledcfg &= BIT(7) | BIT(6); ++ rtw_write8(rtwdev, REG_LED_CFG + 2, ledcfg); ++ ++ gpio8_cfg &= ~(BIT(24) | BIT(8)); ++ gpio8_cfg |= BIT(16); ++ rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, gpio8_cfg); ++ } ++} ++ + static void rtw8821a_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +@@ -853,6 +878,7 @@ static const struct rtw_chip_ops rtw8821a_ops = { + .config_bfee = NULL, + .set_gid_table = NULL, + .cfg_csi_rate = NULL, ++ .led_set = rtw8821a_led_set, + .fill_txdesc_checksum = rtw8821a_fill_txdesc_checksum, + .coex_set_init = rtw8821a_coex_cfg_init, + .coex_set_ant_switch = rtw8821a_coex_cfg_ant_switch, +diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c +index 0270225b9c20..eb7e34c545d0 100644 +--- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c ++++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c +@@ -1206,6 +1206,24 @@ static void rtw8821c_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) + dm_info->cck_pd_default + new_lvl * 2); + } + ++static void rtw8821c_led_set(struct led_classdev *led, ++ enum led_brightness brightness) ++{ ++ struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); ++ u32 ledcfg; ++ ++ ledcfg = rtw_read32(rtwdev, REG_LED_CFG); ++ u32p_replace_bits(&ledcfg, BIT_LED_MODE_SW_CTRL, BIT_LED2_CM); ++ ledcfg &= ~BIT_GPIO13_14_WL_CTRL_EN; ++ ++ if (brightness == LED_OFF) ++ ledcfg |= BIT_LED2_SV; ++ else ++ ledcfg &= ~BIT_LED2_SV; ++ ++ rtw_write32(rtwdev, REG_LED_CFG, ledcfg); ++} ++ + static void rtw8821c_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +@@ -1655,6 +1673,7 @@ static const struct rtw_chip_ops rtw8821c_ops = { + .config_bfee = rtw8821c_bf_config_bfee, + .set_gid_table = rtw_bf_set_gid_table, + .cfg_csi_rate = rtw_bf_cfg_csi_rate, ++ .led_set = rtw8821c_led_set, + .fill_txdesc_checksum = rtw8821c_fill_txdesc_checksum, + + .coex_set_init = rtw8821c_coex_cfg_init, +diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c +index 739809f4cab5..7f03903ddf4b 100644 +--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c ++++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c +@@ -1566,6 +1566,24 @@ static void rtw8822b_adaptivity(struct rtw_dev *rtwdev) + rtw_phy_set_edcca_th(rtwdev, l2h, h2l); + } + ++static void rtw8822b_led_set(struct led_classdev *led, ++ enum led_brightness brightness) ++{ ++ struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); ++ u32 ledcfg; ++ ++ ledcfg = rtw_read32(rtwdev, REG_LED_CFG); ++ u32p_replace_bits(&ledcfg, BIT_LED_MODE_SW_CTRL, BIT_LED2_CM); ++ ledcfg &= ~BIT_GPIO13_14_WL_CTRL_EN; ++ ++ if (brightness == LED_OFF) ++ ledcfg |= BIT_LED2_SV; ++ else ++ ledcfg &= ~BIT_LED2_SV; ++ ++ rtw_write32(rtwdev, REG_LED_CFG, ledcfg); ++} ++ + static void rtw8822b_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +@@ -2146,6 +2164,7 @@ static const struct rtw_chip_ops rtw8822b_ops = { + .cfg_csi_rate = rtw_bf_cfg_csi_rate, + .adaptivity_init = rtw8822b_adaptivity_init, + .adaptivity = rtw8822b_adaptivity, ++ .led_set = rtw8822b_led_set, + .fill_txdesc_checksum = rtw8822b_fill_txdesc_checksum, + + .coex_set_init = rtw8822b_coex_cfg_init, +diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c +index af6b76937f1d..ec362a817f5f 100644 +--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c ++++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c +@@ -4537,6 +4537,24 @@ static void rtw8822c_adaptivity(struct rtw_dev *rtwdev) + rtw_phy_set_edcca_th(rtwdev, l2h, h2l); + } + ++static void rtw8822c_led_set(struct led_classdev *led, ++ enum led_brightness brightness) ++{ ++ struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); ++ u32 ledcfg; ++ ++ ledcfg = rtw_read32(rtwdev, REG_LED_CFG); ++ u32p_replace_bits(&ledcfg, BIT_LED_MODE_SW_CTRL, BIT_LED2_CM); ++ ledcfg &= ~BIT_GPIO13_14_WL_CTRL_EN; ++ ++ if (brightness == LED_OFF) ++ ledcfg |= BIT_LED2_SV; ++ else ++ ledcfg &= ~BIT_LED2_SV; ++ ++ rtw_write32(rtwdev, REG_LED_CFG, ledcfg); ++} ++ + static void rtw8822c_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +@@ -4964,6 +4982,7 @@ static const struct rtw_chip_ops rtw8822c_ops = { + .cfo_track = rtw8822c_cfo_track, + .config_tx_path = rtw8822c_config_tx_path, + .config_txrx_mode = rtw8822c_config_trx_mode, ++ .led_set = rtw8822c_led_set, + .fill_txdesc_checksum = rtw8822c_fill_txdesc_checksum, + + .coex_set_init = rtw8822c_coex_cfg_init, +-- +2.43.0 +