On Mon, Dec 9, 2019 at 3:21 PM <yhchuang@xxxxxxxxxxx> wrote: > > From: Chin-Yen Lee <timlee@xxxxxxxxxxx> > > Wake on WLAN(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. > > If wowlan firmware detects that specific wlan event > happens, it will issue wakeup signal to trigger resume > process. Driver will load normal firmware and let wifi > chip return to the original state. > > Currently supported wlan events include receiving magic packet, > rekey packet and deauth packet, and disconnecting from AP. > > Signed-off-by: Chin-Yen Lee <timlee@xxxxxxxxxxx> > Signed-off-by: Yan-Hsuan Chuang <yhchuang@xxxxxxxxxxx> > --- > > v1 -> v2 > * no change > > diff --git a/drivers/net/wireless/realtek/rtw88/wow.c b/drivers/net/wireless/realtek/rtw88/wow.c > new file mode 100644 > index 000000000000..e1657d3cb615 > --- /dev/null > +++ b/drivers/net/wireless/realtek/rtw88/wow.c > @@ -0,0 +1,531 @@ > +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause > +/* Copyright(c) 2018-2019 Realtek Corporation > + */ > + > +#include "main.h" > +#include "fw.h" > +#include "wow.h" > +#include "reg.h" > +#include "debug.h" > +#include "mac.h" > +#include "ps.h" > + > +static void rtw_wow_show_wakeup_reason(struct rtw_dev *rtwdev) > +{ > + u8 reason; > + > + reason = rtw_read8(rtwdev, REG_WOWLAN_WAKE_REASON); > + > + if (reason == RTW_WOW_RSN_RX_DEAUTH) > + rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx deauth\n"); > + else if (reason == RTW_WOW_RSN_DISCONNECT) > + rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: AP is off\n"); > + else if (reason == RTW_WOW_RSN_RX_MAGIC_PKT) > + rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx magic packet\n"); > + else if (reason == RTW_WOW_RSN_RX_GTK_REKEY) > + rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx gtk rekey\n"); > + else if (reason == RTW_WOW_RSN_RX_PTK_REKEY) > + rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx ptk rekey\n"); > + else > + rtw_warn(rtwdev, "Unknown wakeup reason %x\n", reason); > +} > + > +static void rtw_wow_bb_stop(struct rtw_dev *rtwdev) > +{ > + struct rtw_wow_param *rtw_wow = &rtwdev->wow; > + > + /* wait 100ms for firmware to finish TX */ > + msleep(100); > + > + if (!rtw_read32_mask(rtwdev, REG_BCNQ_INFO, BIT_MGQ_CPU_EMPTY)) > + rtw_warn(rtwdev, "Wrong status of MGQ_CPU empty!\n"); > + > + rtw_wow->txpause = rtw_read8(rtwdev, REG_TXPAUSE); > + rtw_write8(rtwdev, REG_TXPAUSE, 0xff); > + rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); > +} > + > +static void rtw_wow_bb_start(struct rtw_dev *rtwdev) > +{ > + struct rtw_wow_param *rtw_wow = &rtwdev->wow; > + > + rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); > + rtw_write8(rtwdev, REG_TXPAUSE, rtw_wow->txpause); > +} > + > +static void rtw_wow_rx_dma_stop(struct rtw_dev *rtwdev) > +{ > + /* wait 100ms for HW to finish rx dma */ > + msleep(100); > + > + rtw_write32_set(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE); > + > + if (!check_hw_ready(rtwdev, REG_RXPKT_NUM, BIT_RXDMA_IDLE, 1)) > + rtw_err(rtwdev, "failed to stop rx dma\n"); > +} > + > +static void rtw_wow_rx_dma_start(struct rtw_dev *rtwdev) > +{ > + rtw_write32_clr(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE); > +} > + > +static bool rtw_wow_check_fw_status(struct rtw_dev *rtwdev, bool wow_enable) > +{ > + bool ret; > + > + /* wait 100ms for wow firmware to finish work */ > + msleep(100); > + > + if (wow_enable) { > + if (!rtw_read8(rtwdev, REG_WOWLAN_WAKE_REASON)) > + ret = 0; > + } else { > + if (rtw_read32_mask(rtwdev, REG_FE1IMR, BIT_FS_RXDONE) == 0 && > + rtw_read32_mask(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE) == 0) > + ret = 0; > + } > + > + if (ret) > + rtw_err(rtwdev, "failed to check wow status %s\n", > + wow_enable ? "enabled" : "disabled"); > + > + return ret; > +} > + > +static void rtw_wow_fw_security_type_iter(struct ieee80211_hw *hw, > + struct ieee80211_vif *vif, > + struct ieee80211_sta *sta, > + struct ieee80211_key_conf *key, > + void *data) > +{ > + struct rtw_fw_key_type_iter_data *iter_data = data; > + struct rtw_dev *rtwdev = hw->priv; > + u8 hw_key_type; > + > + if (vif != rtwdev->wow.wow_vif) > + return; > + > + switch (key->cipher) { > + case WLAN_CIPHER_SUITE_WEP40: > + hw_key_type = RTW_CAM_WEP40; > + break; > + case WLAN_CIPHER_SUITE_WEP104: > + hw_key_type = RTW_CAM_WEP104; > + break; > + case WLAN_CIPHER_SUITE_TKIP: > + hw_key_type = RTW_CAM_TKIP; > + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; > + break; > + case WLAN_CIPHER_SUITE_CCMP: > + hw_key_type = RTW_CAM_AES; > + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; > + break; > + default: > + rtw_err(rtwdev, "Unsupported key type for wowlan mode\n"); > + hw_key_type = 0; > + break; > + } > + > + if (sta) > + iter_data->pairwise_key_type = hw_key_type; > + else > + iter_data->group_key_type = hw_key_type; > +} > + > +static void rtw_wow_fw_security_type(struct rtw_dev *rtwdev) > +{ > + struct rtw_fw_key_type_iter_data data = {}; > + struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; > + > + data.rtwdev = rtwdev; > + rtw_iterate_keys(rtwdev, wow_vif, > + rtw_wow_fw_security_type_iter, &data); > + rtw_fw_set_aoac_global_info_cmd(rtwdev, data.pairwise_key_type, > + data.group_key_type); > +} > + > +static int rtw_wow_fw_start(struct rtw_dev *rtwdev) > +{ > + if (rtw_wow_mgd_linked(rtwdev)) { > + rtw_send_rsvd_page_h2c(rtwdev); > + rtw_wow_fw_security_type(rtwdev); > + rtw_fw_set_disconnect_decision_cmd(rtwdev, true); > + rtw_fw_set_keep_alive_cmd(rtwdev, true); > + } > + > + rtw_fw_set_wowlan_ctrl_cmd(rtwdev, true); > + rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, true); > + > + return rtw_wow_check_fw_status(rtwdev, true); > +} > + > +static int rtw_wow_fw_stop(struct rtw_dev *rtwdev) > +{ > + if (rtw_wow_mgd_linked(rtwdev)) { > + rtw_fw_set_disconnect_decision_cmd(rtwdev, false); > + rtw_fw_set_keep_alive_cmd(rtwdev, false); > + } > + > + rtw_fw_set_wowlan_ctrl_cmd(rtwdev, false); > + rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, false); > + > + return rtw_wow_check_fw_status(rtwdev, false); > +} > + > +static void rtw_wow_avoid_reset_mac(struct rtw_dev *rtwdev) > +{ > + /* When resuming from wowlan mode, some hosts issue signal > + * (PCIE: PREST, USB: SE0RST) to device, and lead to reset > + * mac core. If it happens, the connection to AP will be lost. > + * Setting REG_RSV_CTRL Register can avoid this process. > + */ > + switch (rtw_hci_type(rtwdev)) { > + case RTW_HCI_TYPE_PCIE: > + case RTW_HCI_TYPE_USB: > + rtw_write8(rtwdev, REG_RSV_CTRL, BIT_WLOCK_1C_B6); > + rtw_write8(rtwdev, REG_RSV_CTRL, > + BIT_WLOCK_1C_B6 | BIT_R_DIS_PRST); > + break; > + default: > + rtw_warn(rtwdev, "Unsupported hci type to disable reset MAC\n"); > + break; > + } > +} > + > +static void rtw_wow_fw_media_status_iter(void *data, struct ieee80211_sta *sta) > +{ > + struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; > + struct rtw_fw_media_status_iter_data *iter_data = data; > + struct rtw_dev *rtwdev = iter_data->rtwdev; > + > + rtw_fw_media_status_report(rtwdev, si->mac_id, iter_data->connect); > +} > + > +static void rtw_wow_fw_media_status(struct rtw_dev *rtwdev, bool connect) > +{ > + struct rtw_fw_media_status_iter_data data; > + > + data.rtwdev = rtwdev; > + data.connect = connect; > + > + rtw_iterate_stas_atomic(rtwdev, rtw_wow_fw_media_status_iter, &data); > +} > + > +void __rtw_wow_config_linked_rsvd_page(struct rtw_dev *rtwdev) > +{ > + rtw_add_rsvd_page(rtwdev, RSVD_PS_POLL, true); > + rtw_add_rsvd_page(rtwdev, RSVD_QOS_NULL, true); > + rtw_add_rsvd_page(rtwdev, RSVD_NULL, true); > + rtw_add_rsvd_page(rtwdev, RSVD_LPS_PG_DPK, true); > + rtw_add_rsvd_page(rtwdev, RSVD_LPS_PG_INFO, true); > +} > + > +static void rtw_wow_config_rsvd_page(struct rtw_dev *rtwdev) > +{ > + rtw_reset_rsvd_page(rtwdev); > + > + if (rtw_wow_mgd_linked(rtwdev)) > + __rtw_wow_config_linked_rsvd_page(rtwdev); > +} > + > +static int rtw_wow_dl_fw_rsvd_page(struct rtw_dev *rtwdev) > +{ > + struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; > + > + rtw_wow_config_rsvd_page(rtwdev); > + > + return rtw_fw_download_rsvd_page(rtwdev, wow_vif); > +} > + > +static int rtw_wow_swap_fw(struct rtw_dev *rtwdev, enum rtw_fw_type type) > +{ > + struct rtw_fw_state *fw; > + int ret; > + > + switch (type) { > + case RTW_WOWLAN_FW: > + fw = &rtwdev->wow_fw; > + break; > + > + case RTW_NORMAL_FW: > + fw = &rtwdev->fw; > + break; > + > + default: > + rtw_warn(rtwdev, "unsupported firmware type to swap\n"); > + return -ENOENT; > + } > + > + ret = rtw_download_firmware(rtwdev, fw); > + if (ret) > + goto out; > + > + rtw_fw_send_general_info(rtwdev); > + rtw_fw_send_phydm_info(rtwdev); > + rtw_wow_fw_media_status(rtwdev, true); > + > +out: > + return ret; > +} > + > +static int __rtw_wow_leave_linked_ps(struct rtw_dev *rtwdev) > +{ > + if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) > + cancel_delayed_work_sync(&rtwdev->watch_dog_work); > + > + return 0; > +} > + > +static int rtw_wow_leave_ps(struct rtw_dev *rtwdev) > +{ > + int ret = 0; > + > + if (rtw_wow_mgd_linked(rtwdev)) > + ret = __rtw_wow_leave_linked_ps(rtwdev); > + > + return ret; > +} > + > +static int __rtw_wow_enter_linked_ps(struct rtw_dev *rtwdev) > +{ > + struct rtw_wow_param *rtw_wow = &rtwdev->wow; > + struct ieee80211_vif *wow_vif = rtw_wow->wow_vif; > + struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv; > + > + rtw_enter_lps(rtwdev, rtwvif->port); > + > + return 0; > +} > + > +static int rtw_wow_enter_ps(struct rtw_dev *rtwdev) > +{ > + int ret = 0; > + > + if (rtw_wow_mgd_linked(rtwdev)) > + ret = __rtw_wow_enter_linked_ps(rtwdev); > + > + return ret; > +} > + I don't like __underscore_means_inner_function(). The function void __rtw_wow_config_linked_rsvd_page does not even prefix with static. I strongly prefer function_has_a_proper_name() so please come up with something that describes what it is really doing and name it like that. The rest of the code looks OK to me. > -- > 2.17.1 >