mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-18 12:30:31 +00:00
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 <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
This commit is contained in:
parent
fb2fcfbe5e
commit
4b6652bc6d
11 changed files with 227 additions and 2 deletions
|
@ -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
|
||||
|
||||
|
|
73
drivers/net/wireless/realtek/rtw88/led.c
Normal file
73
drivers/net/wireless/realtek/rtw88/led.c
Normal file
|
@ -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
drivers/net/wireless/realtek/rtw88/led.h
Normal file
25
drivers/net/wireless/realtek/rtw88/led.h
Normal file
|
@ -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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 *));
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue