Skip to content

Commit

Permalink
wifi: rtw89: add WoWLAN function support
Browse files Browse the repository at this point in the history
WoWLAN is a feature which allows devices to be woken up from suspend
state through WLAN events.

When user enables WoWLAN feature and then let the device enter suspend
state, WoWLAN firmware will be loaded by the driver and periodically
monitors WiFi packets. Power consumption of WiFi chip will be reduced
in this state.

We now implement WoWLAN function in rtw8852ae and rtw8852ce chip.
Currently supported WLAN events include receiving magic packet,
rekey packet and deauth packet, and disconnecting from AP.

Signed-off-by: Chin-Yen Lee <timlee@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20221027052707.14605-7-pkshih@realtek.com
  • Loading branch information
Chin-Yen Lee authored and Kalle Valo committed Nov 1, 2022
1 parent ee88d74 commit 19e28c7
Show file tree
Hide file tree
Showing 15 changed files with 874 additions and 7 deletions.
2 changes: 2 additions & 0 deletions drivers/net/wireless/realtek/rtw89/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ rtw89_core-y += core.o \
chan.o \
ser.o

rtw89_core-$(CONFIG_PM) += wow.o

obj-$(CONFIG_RTW89_8852A) += rtw89_8852a.o
rtw89_8852a-objs := rtw8852a.o \
rtw8852a_table.o \
Expand Down
8 changes: 8 additions & 0 deletions drivers/net/wireless/realtek/rtw89/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2204,6 +2204,9 @@ static void rtw89_track_work(struct work_struct *work)
track_work.work);
bool tfc_changed;

if (test_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags))
return;

mutex_lock(&rtwdev->mutex);

if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
Expand Down Expand Up @@ -3042,6 +3045,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
continue;
INIT_LIST_HEAD(&rtwdev->scan_info.pkt_list[band]);
}
INIT_LIST_HEAD(&rtwdev->wow.pkt_list);
INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work);
INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work);
INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
Expand Down Expand Up @@ -3273,6 +3277,10 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
hw->wiphy->max_scan_ssids = RTW89_SCANOFLD_MAX_SSID;
hw->wiphy->max_scan_ie_len = RTW89_SCANOFLD_MAX_IE_LEN;

#ifdef CONFIG_PM
hw->wiphy->wowlan = rtwdev->chip->wowlan_stub;
#endif

hw->wiphy->tid_config_support.vif |= BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL);
hw->wiphy->tid_config_support.peer |= BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL);
hw->wiphy->tid_config_support.vif |= BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL);
Expand Down
77 changes: 76 additions & 1 deletion drivers/net/wireless/realtek/rtw89/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ enum rtw89_upd_mode {
RTW89_ROLE_REMOVE,
RTW89_ROLE_TYPE_CHANGE,
RTW89_ROLE_INFO_CHANGE,
RTW89_ROLE_CON_DISCONN
RTW89_ROLE_CON_DISCONN,
RTW89_ROLE_BAND_SW,
RTW89_ROLE_FW_RESTORE,
};

enum rtw89_self_role {
Expand Down Expand Up @@ -2307,6 +2309,16 @@ struct rtw89_hci_ops {
*/
void (*recovery_start)(struct rtw89_dev *rtwdev);
void (*recovery_complete)(struct rtw89_dev *rtwdev);

void (*ctrl_txdma_ch)(struct rtw89_dev *rtwdev, bool enable);
void (*ctrl_txdma_fw_ch)(struct rtw89_dev *rtwdev, bool enable);
void (*ctrl_trxhci)(struct rtw89_dev *rtwdev, bool enable);
int (*poll_txdma_ch)(struct rtw89_dev *rtwdev);
void (*clr_idx_all)(struct rtw89_dev *rtwdev);
void (*clear)(struct rtw89_dev *rtwdev, struct pci_dev *pdev);
void (*disable_intr)(struct rtw89_dev *rtwdev);
void (*enable_intr)(struct rtw89_dev *rtwdev);
int (*rst_bdram)(struct rtw89_dev *rtwdev);
};

struct rtw89_hci_info {
Expand Down Expand Up @@ -2748,6 +2760,7 @@ struct rtw89_chip_info {
const struct rtw89_imr_info *imr_info;
const struct rtw89_rrsr_cfgs *rrsr_cfgs;
u32 dma_ch_mask;
const struct wiphy_wowlan_support *wowlan_stub;
};

union rtw89_bus_info {
Expand Down Expand Up @@ -2944,6 +2957,8 @@ enum rtw89_flags {
RTW89_FLAG_LOW_POWER_MODE,
RTW89_FLAG_INACTIVE_PS,
RTW89_FLAG_CRASH_SIMULATING,
RTW89_FLAG_WOWLAN,
RTW89_FLAG_FORBIDDEN_TRACK_WROK,

NUM_OF_RTW89_FLAGS,
};
Expand Down Expand Up @@ -3653,6 +3668,66 @@ static inline void rtw89_hci_recovery_complete(struct rtw89_dev *rtwdev)
rtwdev->hci.ops->recovery_complete(rtwdev);
}

static inline void rtw89_hci_enable_intr(struct rtw89_dev *rtwdev)
{
if (rtwdev->hci.ops->enable_intr)
rtwdev->hci.ops->enable_intr(rtwdev);
}

static inline void rtw89_hci_disable_intr(struct rtw89_dev *rtwdev)
{
if (rtwdev->hci.ops->disable_intr)
rtwdev->hci.ops->disable_intr(rtwdev);
}

static inline void rtw89_hci_ctrl_txdma_ch(struct rtw89_dev *rtwdev, bool enable)
{
if (rtwdev->hci.ops->ctrl_txdma_ch)
rtwdev->hci.ops->ctrl_txdma_ch(rtwdev, enable);
}

static inline void rtw89_hci_ctrl_txdma_fw_ch(struct rtw89_dev *rtwdev, bool enable)
{
if (rtwdev->hci.ops->ctrl_txdma_fw_ch)
rtwdev->hci.ops->ctrl_txdma_fw_ch(rtwdev, enable);
}

static inline void rtw89_hci_ctrl_trxhci(struct rtw89_dev *rtwdev, bool enable)
{
if (rtwdev->hci.ops->ctrl_trxhci)
rtwdev->hci.ops->ctrl_trxhci(rtwdev, enable);
}

static inline int rtw89_hci_poll_txdma_ch(struct rtw89_dev *rtwdev)
{
int ret = 0;

if (rtwdev->hci.ops->poll_txdma_ch)
ret = rtwdev->hci.ops->poll_txdma_ch(rtwdev);
return ret;
}

static inline void rtw89_hci_clr_idx_all(struct rtw89_dev *rtwdev)
{
if (rtwdev->hci.ops->clr_idx_all)
rtwdev->hci.ops->clr_idx_all(rtwdev);
}

static inline int rtw89_hci_rst_bdram(struct rtw89_dev *rtwdev)
{
int ret = 0;

if (rtwdev->hci.ops->rst_bdram)
ret = rtwdev->hci.ops->rst_bdram(rtwdev);
return ret;
}

static inline void rtw89_hci_clear(struct rtw89_dev *rtwdev, struct pci_dev *pdev)
{
if (rtwdev->hci.ops->clear)
rtwdev->hci.ops->clear(rtwdev, pdev);
}

static inline u8 rtw89_read8(struct rtw89_dev *rtwdev, u32 addr)
{
return rtwdev->hci.ops->read8(rtwdev, addr);
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/realtek/rtw89/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ enum rtw89_debug_mask {
RTW89_DBG_HW_SCAN = BIT(15),
RTW89_DBG_SAR = BIT(16),
RTW89_DBG_STATE = BIT(17),
RTW89_DBG_WOW = BIT(18),

RTW89_DBG_UNEXP = BIT(31),
};
Expand Down
18 changes: 14 additions & 4 deletions drivers/net/wireless/realtek/rtw89/mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -1566,6 +1566,16 @@ int rtw89_mac_resize_ple_rx_quota(struct rtw89_dev *rtwdev, bool wow)
}
#undef SET_QUOTA

void rtw89_mac_hw_mgnt_sec(struct rtw89_dev *rtwdev, bool enable)
{
u32 msk32 = B_AX_UC_MGNT_DEC | B_AX_BMC_MGNT_DEC;

if (enable)
rtw89_write32_set(rtwdev, R_AX_SEC_ENG_CTRL, msk32);
else
rtw89_write32_clr(rtwdev, R_AX_SEC_ENG_CTRL, msk32);
}

static void dle_quota_cfg(struct rtw89_dev *rtwdev,
const struct rtw89_dle_mem *cfg,
u16 ext_wde_min_qt_wcpu)
Expand Down Expand Up @@ -1915,10 +1925,10 @@ static int scheduler_init(struct rtw89_dev *rtwdev, u8 mac_idx)
return 0;
}

static int rtw89_mac_typ_fltr_opt(struct rtw89_dev *rtwdev,
enum rtw89_machdr_frame_type type,
enum rtw89_mac_fwd_target fwd_target,
u8 mac_idx)
int rtw89_mac_typ_fltr_opt(struct rtw89_dev *rtwdev,
enum rtw89_machdr_frame_type type,
enum rtw89_mac_fwd_target fwd_target,
u8 mac_idx)
{
u32 reg;
u32 val;
Expand Down
14 changes: 14 additions & 0 deletions drivers/net/wireless/realtek/rtw89/mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,16 @@ static inline void rtw89_mac_ctrl_hci_dma_trx(struct rtw89_dev *rtwdev,
B_AX_HCI_TXDMA_EN | B_AX_HCI_RXDMA_EN);
}

static inline bool rtw89_mac_get_power_state(struct rtw89_dev *rtwdev)
{
u32 val;

val = rtw89_read32_mask(rtwdev, R_AX_IC_PWR_STATE,
B_AX_WLMAC_PWR_STE_MASK);

return !!val;
}

int rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,
bool resume, u32 tx_time);
int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,
Expand Down Expand Up @@ -1038,8 +1048,12 @@ void rtw89_mac_pkt_drop_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);
u16 rtw89_mac_dle_buf_req(struct rtw89_dev *rtwdev, u16 buf_len, bool wd);
int rtw89_mac_set_cpuio(struct rtw89_dev *rtwdev,
struct rtw89_cpuio_ctrl *ctrl_para, bool wd);
int rtw89_mac_typ_fltr_opt(struct rtw89_dev *rtwdev,
enum rtw89_machdr_frame_type type,
enum rtw89_mac_fwd_target fwd_target, u8 mac_idx);
int rtw89_mac_resize_ple_rx_quota(struct rtw89_dev *rtwdev, bool wow);
int rtw89_mac_ptk_drop_by_band_and_wait(struct rtw89_dev *rtwdev,
enum rtw89_mac_idx band);
void rtw89_mac_hw_mgnt_sec(struct rtw89_dev *rtwdev, bool wow);

#endif
55 changes: 55 additions & 0 deletions drivers/net/wireless/realtek/rtw89/mac80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "sar.h"
#include "ser.h"
#include "util.h"
#include "wow.h"

static void rtw89_ops_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *control,
Expand Down Expand Up @@ -916,6 +917,55 @@ static int rtw89_ops_set_tid_config(struct ieee80211_hw *hw,
return 0;
}

#ifdef CONFIG_PM
static int rtw89_ops_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
{
struct rtw89_dev *rtwdev = hw->priv;
int ret;

set_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags);
cancel_delayed_work_sync(&rtwdev->track_work);

mutex_lock(&rtwdev->mutex);
ret = rtw89_wow_suspend(rtwdev, wowlan);
mutex_unlock(&rtwdev->mutex);

if (ret) {
rtw89_warn(rtwdev, "failed to suspend for wow %d\n", ret);
clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags);
return 1;
}

return 0;
}

static int rtw89_ops_resume(struct ieee80211_hw *hw)
{
struct rtw89_dev *rtwdev = hw->priv;
int ret;

mutex_lock(&rtwdev->mutex);
ret = rtw89_wow_resume(rtwdev);
if (ret)
rtw89_warn(rtwdev, "failed to resume for wow %d\n", ret);
mutex_unlock(&rtwdev->mutex);

clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags);
ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
RTW89_TRACK_WORK_PERIOD);

return ret ? 1 : 0;
}

static void rtw89_ops_set_wakeup(struct ieee80211_hw *hw, bool enabled)
{
struct rtw89_dev *rtwdev = hw->priv;

device_set_wakeup_enable(rtwdev->dev, enabled);
}
#endif

const struct ieee80211_ops rtw89_ops = {
.tx = rtw89_ops_tx,
.wake_tx_queue = rtw89_ops_wake_tx_queue,
Expand Down Expand Up @@ -953,5 +1003,10 @@ const struct ieee80211_ops rtw89_ops = {
.set_sar_specs = rtw89_ops_set_sar_specs,
.sta_rc_update = rtw89_ops_sta_rc_update,
.set_tid_config = rtw89_ops_set_tid_config,
#ifdef CONFIG_PM
.suspend = rtw89_ops_suspend,
.resume = rtw89_ops_resume,
.set_wakeup = rtw89_ops_set_wakeup,
#endif
};
EXPORT_SYMBOL(rtw89_ops);
23 changes: 22 additions & 1 deletion drivers/net/wireless/realtek/rtw89/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,17 @@ static void rtw89_pci_ctrl_txdma_ch_pcie(struct rtw89_dev *rtwdev, bool enable)
}
}

static void rtw89_pci_ctrl_txdma_fw_ch_pcie(struct rtw89_dev *rtwdev, bool enable)
{
const struct rtw89_pci_info *info = rtwdev->pci_info;
const struct rtw89_reg_def *dma_stop1 = &info->dma_stop1;

if (enable)
rtw89_write32_clr(rtwdev, dma_stop1->addr, B_AX_STOP_CH12);
else
rtw89_write32_set(rtwdev, dma_stop1->addr, B_AX_STOP_CH12);
}

static bool
rtw89_skb_put_rx_data(struct rtw89_dev *rtwdev, bool fs, bool ls,
struct sk_buff *new,
Expand Down Expand Up @@ -2513,7 +2524,7 @@ static int rtw89_pci_ops_mac_pre_init(struct rtw89_dev *rtwdev)

/* disable all channels except to FW CMD channel to download firmware */
rtw89_pci_ctrl_txdma_ch_pcie(rtwdev, false);
rtw89_write32_clr(rtwdev, info->dma_stop1.addr, B_AX_STOP_CH12);
rtw89_pci_ctrl_txdma_fw_ch_pcie(rtwdev, true);

/* start DMA activities */
rtw89_pci_ctrl_dma_all(rtwdev, true);
Expand Down Expand Up @@ -3771,6 +3782,16 @@ static const struct rtw89_hci_ops rtw89_pci_ops = {

.recovery_start = rtw89_pci_ops_recovery_start,
.recovery_complete = rtw89_pci_ops_recovery_complete,

.ctrl_txdma_ch = rtw89_pci_ctrl_txdma_ch_pcie,
.ctrl_txdma_fw_ch = rtw89_pci_ctrl_txdma_fw_ch_pcie,
.ctrl_trxhci = rtw89_pci_ctrl_dma_trx,
.poll_txdma_ch = rtw89_poll_txdma_ch_idle_pcie,
.clr_idx_all = rtw89_pci_clr_idx_all,
.clear = rtw89_pci_clear_resource,
.disable_intr = rtw89_pci_disable_intr_lock,
.enable_intr = rtw89_pci_enable_intr_lock,
.rst_bdram = rtw89_pci_rst_bdram_pcie,
};

int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/wireless/realtek/rtw89/ps.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ static void rtw89_ps_power_mode_change(struct rtw89_dev *rtwdev, bool enter)
rtw89_mac_power_mode_change(rtwdev, enter);
}

static void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
{
if (rtwvif->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT)
return;
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/realtek/rtw89/ps.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);
void rtw89_leave_lps(struct rtw89_dev *rtwdev);
void __rtw89_leave_ps_mode(struct rtw89_dev *rtwdev);
void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);
void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev);
void rtw89_enter_ips(struct rtw89_dev *rtwdev);
void rtw89_leave_ips(struct rtw89_dev *rtwdev);
Expand Down
8 changes: 8 additions & 0 deletions drivers/net/wireless/realtek/rtw89/reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,9 @@
B_AX_STF_OQT_OVERFLOW_ERR_INT_EN | \
B_AX_STF_OQT_UNDERFLOW_ERR_INT_EN)

#define R_AX_RX_FUNCTION_STOP 0x8920
#define B_AX_HDR_RX_STOP BIT(0)

#define R_AX_HCI_FC_CTRL 0x8A00
#define B_AX_HCI_FC_CH12_FULL_COND_MASK GENMASK(11, 10)
#define B_AX_HCI_FC_WP_CH811_FULL_COND_MASK GENMASK(9, 8)
Expand Down Expand Up @@ -1570,6 +1573,8 @@
#define R_AX_ACTION_FWD0 0x9C04
#define TRXCFG_MPDU_PROC_ACT_FRWD 0x02A95A95

#define R_AX_ACTION_FWD1 0x9C08

#define R_AX_TF_FWD 0x9C14
#define TRXCFG_MPDU_PROC_TF_FRWD 0x0000AA55

Expand All @@ -1581,6 +1586,9 @@
#define R_AX_CUT_AMSDU_CTRL 0x9C40
#define TRXCFG_MPDU_PROC_CUT_CTRL 0x010E05F0

#define R_AX_WOW_CTRL 0x9C50
#define B_AX_WOW_WOWEN BIT(1)

#define R_AX_MPDU_RX_ERR_ISR 0x9CF0
#define R_AX_MPDU_RX_ERR_IMR 0x9CF4
#define B_AX_RPT_ERR_INT_EN BIT(3)
Expand Down
Loading

0 comments on commit 19e28c7

Please sign in to comment.