Skip to content

Commit

Permalink
wifi: rtw88: Add support for LED blinking
Browse files Browse the repository at this point in the history
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 <rtl8821cerfe2@gmail.com>
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/6c43451f-ab2f-4e76-ac6e-ff5a18dd981d@gmail.com
  • Loading branch information
Bitterblue Smith authored and Ping-Ke Shih committed Jan 12, 2025
1 parent fb2fcfb commit 4b6652b
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 2 deletions.
2 changes: 2 additions & 0 deletions drivers/net/wireless/realtek/rtw88/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
73 changes: 73 additions & 0 deletions drivers/net/wireless/realtek/rtw88/led.c
Original file line number Diff line number Diff line change
@@ -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);
}
25 changes: 25 additions & 0 deletions drivers/net/wireless/realtek/rtw88/led.h
Original file line number Diff line number Diff line change
@@ -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
12 changes: 10 additions & 2 deletions drivers/net/wireless/realtek/rtw88/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -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);

Expand Down
5 changes: 5 additions & 0 deletions drivers/net/wireless/realtek/rtw88/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 *));
};
Expand Down
12 changes: 12 additions & 0 deletions drivers/net/wireless/realtek/rtw88/reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 17 additions & 0 deletions drivers/net/wireless/realtek/rtw88/rtw8812a.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
26 changes: 26 additions & 0 deletions drivers/net/wireless/realtek/rtw88/rtw8821a.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 19 additions & 0 deletions drivers/net/wireless/realtek/rtw88/rtw8821c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 19 additions & 0 deletions drivers/net/wireless/realtek/rtw88/rtw8822b.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 19 additions & 0 deletions drivers/net/wireless/realtek/rtw88/rtw8822c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 4b6652b

Please sign in to comment.