mt76: mt7615: add WoW support
Introduce WoW support to mt7615 driver. Current supported trigger are: - magic-packet - disconnect - user-pattern Co-developed-by: Wan-Feng Jiang <Wan-Feng.Jiang@mediatek.com> Signed-off-by: Wan-Feng Jiang <Wan-Feng.Jiang@mediatek.com> Co-developed-by: Soul Huang <Soul.Huang@mediatek.com> Signed-off-by: Soul Huang <Soul.Huang@mediatek.com> Co-developed-by: Sean Wang <sean.wang@mediatek.com> Signed-off-by: Sean Wang <sean.wang@mediatek.com> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
6f11785200
commit
c6bf20109a
@ -287,6 +287,7 @@ enum {
|
||||
MT76_READING_STATS,
|
||||
MT76_STATE_POWER_OFF,
|
||||
MT76_STATE_PS,
|
||||
MT76_STATE_SUSPEND,
|
||||
};
|
||||
|
||||
struct mt76_hw_cap {
|
||||
|
@ -864,6 +864,80 @@ mt7615_stop_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
||||
return mt7615_mcu_sched_scan_enable(mphy->priv, vif, false);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mt7615_suspend(struct ieee80211_hw *hw,
|
||||
struct cfg80211_wowlan *wowlan)
|
||||
{
|
||||
struct mt7615_dev *dev = mt7615_hw_dev(hw);
|
||||
struct mt7615_phy *phy = mt7615_hw_phy(hw);
|
||||
bool ext_phy = phy != &dev->phy;
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&dev->mt76.mutex);
|
||||
|
||||
clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
|
||||
cancel_delayed_work_sync(&phy->scan_work);
|
||||
cancel_delayed_work_sync(&phy->mac_work);
|
||||
|
||||
mt76_set(dev, MT_WF_RFCR(ext_phy), MT_WF_RFCR_DROP_OTHER_BEACON);
|
||||
|
||||
set_bit(MT76_STATE_SUSPEND, &phy->mt76->state);
|
||||
ieee80211_iterate_active_interfaces(hw,
|
||||
IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
mt7615_mcu_set_suspend_iter, phy);
|
||||
|
||||
if (!mt7615_dev_running(dev))
|
||||
err = mt7615_mcu_set_hif_suspend(dev, true);
|
||||
|
||||
mutex_unlock(&dev->mt76.mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mt7615_resume(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct mt7615_dev *dev = mt7615_hw_dev(hw);
|
||||
struct mt7615_phy *phy = mt7615_hw_phy(hw);
|
||||
bool running, ext_phy = phy != &dev->phy;
|
||||
|
||||
mutex_lock(&dev->mt76.mutex);
|
||||
|
||||
running = mt7615_dev_running(dev);
|
||||
set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
|
||||
|
||||
if (!running) {
|
||||
int err;
|
||||
|
||||
err = mt7615_mcu_set_hif_suspend(dev, false);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&dev->mt76.mutex);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
clear_bit(MT76_STATE_SUSPEND, &phy->mt76->state);
|
||||
ieee80211_iterate_active_interfaces(hw,
|
||||
IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
mt7615_mcu_set_suspend_iter, phy);
|
||||
|
||||
ieee80211_queue_delayed_work(hw, &phy->mac_work,
|
||||
MT7615_WATCHDOG_TIME);
|
||||
mt76_clear(dev, MT_WF_RFCR(ext_phy), MT_WF_RFCR_DROP_OTHER_BEACON);
|
||||
|
||||
mutex_unlock(&dev->mt76.mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mt7615_set_wakeup(struct ieee80211_hw *hw, bool enabled)
|
||||
{
|
||||
struct mt7615_dev *dev = mt7615_hw_dev(hw);
|
||||
struct mt76_dev *mdev = &dev->mt76;
|
||||
|
||||
device_set_wakeup_enable(mdev->dev, enabled);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
const struct ieee80211_ops mt7615_ops = {
|
||||
.tx = mt7615_tx,
|
||||
.start = mt7615_start,
|
||||
@ -897,6 +971,11 @@ const struct ieee80211_ops mt7615_ops = {
|
||||
.cancel_hw_scan = mt7615_cancel_hw_scan,
|
||||
.sched_scan_start = mt7615_start_sched_scan,
|
||||
.sched_scan_stop = mt7615_stop_sched_scan,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = mt7615_suspend,
|
||||
.resume = mt7615_resume,
|
||||
.set_wakeup = mt7615_set_wakeup,
|
||||
#endif /* CONFIG_PM */
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(mt7615_ops);
|
||||
|
||||
|
@ -195,7 +195,8 @@ mt7615_mcu_parse_response(struct mt7615_dev *dev, int cmd,
|
||||
case MCU_UNI_CMD_DEV_INFO_UPDATE:
|
||||
case MCU_UNI_CMD_BSS_INFO_UPDATE:
|
||||
case MCU_UNI_CMD_STA_REC_UPDATE:
|
||||
case MCU_UNI_CMD_HIF_CTRL: {
|
||||
case MCU_UNI_CMD_HIF_CTRL:
|
||||
case MCU_UNI_CMD_SUSPEND: {
|
||||
struct mt7615_mcu_uni_event *event;
|
||||
|
||||
skb_pull(skb, sizeof(*rxd));
|
||||
@ -1835,6 +1836,13 @@ mt7615_mcu_send_ram_firmware(struct mt7615_dev *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wiphy_wowlan_support mt7615_wowlan_support = {
|
||||
.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
|
||||
.n_patterns = 1,
|
||||
.pattern_min_len = 1,
|
||||
.pattern_max_len = MT7615_WOW_PATTEN_MAX_LEN,
|
||||
};
|
||||
|
||||
static int mt7615_load_n9(struct mt7615_dev *dev, const char *name)
|
||||
{
|
||||
const struct mt7615_fw_trailer *hdr;
|
||||
@ -2157,6 +2165,11 @@ int __mt7663_load_firmware(struct mt7615_dev *dev)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
if (mt7615_firmware_offload(dev))
|
||||
dev->mt76.hw->wiphy->wowlan = &mt7615_wowlan_support;
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
dev_dbg(dev->mt76.dev, "Firmware init done\n");
|
||||
|
||||
return 0;
|
||||
@ -2653,47 +2666,6 @@ void m7615_mcu_set_ps_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
|
||||
&req, sizeof(req), false);
|
||||
}
|
||||
|
||||
int mt7615_mcu_set_bss_pm(struct mt7615_dev *dev, struct ieee80211_vif *vif,
|
||||
bool enable)
|
||||
{
|
||||
struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
|
||||
struct {
|
||||
u8 bss_idx;
|
||||
u8 dtim_period;
|
||||
__le16 aid;
|
||||
__le16 bcn_interval;
|
||||
__le16 atim_window;
|
||||
u8 uapsd;
|
||||
u8 bmc_delivered_ac;
|
||||
u8 bmc_triggered_ac;
|
||||
u8 pad;
|
||||
} req = {
|
||||
.bss_idx = mvif->idx,
|
||||
.aid = cpu_to_le16(vif->bss_conf.aid),
|
||||
.dtim_period = vif->bss_conf.dtim_period,
|
||||
.bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
|
||||
};
|
||||
struct {
|
||||
u8 bss_idx;
|
||||
u8 pad[3];
|
||||
} req_hdr = {
|
||||
.bss_idx = mvif->idx,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION ||
|
||||
!mt7615_firmware_offload(dev))
|
||||
return -ENOTSUPP;
|
||||
|
||||
err = __mt76_mcu_send_msg(&dev->mt76, MCU_CMD_SET_BSS_ABORT,
|
||||
&req_hdr, sizeof(req_hdr), false);
|
||||
if (err < 0 || !enable)
|
||||
return err;
|
||||
|
||||
return __mt76_mcu_send_msg(&dev->mt76, MCU_CMD_SET_BSS_CONNECTED,
|
||||
&req, sizeof(req), false);
|
||||
}
|
||||
|
||||
int mt7615_mcu_set_channel_domain(struct mt7615_phy *phy)
|
||||
{
|
||||
struct mt76_phy *mphy = phy->mt76;
|
||||
@ -3222,6 +3194,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int mt7615_mcu_set_hif_suspend(struct mt7615_dev *dev, bool suspend)
|
||||
{
|
||||
struct {
|
||||
@ -3254,3 +3227,166 @@ int mt7615_mcu_set_hif_suspend(struct mt7615_dev *dev, bool suspend)
|
||||
&req, sizeof(req), true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mt7615_mcu_set_hif_suspend);
|
||||
|
||||
static int
|
||||
mt7615_mcu_set_bss_pm(struct mt7615_dev *dev, struct ieee80211_vif *vif,
|
||||
bool enable)
|
||||
{
|
||||
struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
|
||||
struct {
|
||||
u8 bss_idx;
|
||||
u8 dtim_period;
|
||||
__le16 aid;
|
||||
__le16 bcn_interval;
|
||||
__le16 atim_window;
|
||||
u8 uapsd;
|
||||
u8 bmc_delivered_ac;
|
||||
u8 bmc_triggered_ac;
|
||||
u8 pad;
|
||||
} req = {
|
||||
.bss_idx = mvif->idx,
|
||||
.aid = cpu_to_le16(vif->bss_conf.aid),
|
||||
.dtim_period = vif->bss_conf.dtim_period,
|
||||
.bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
|
||||
};
|
||||
struct {
|
||||
u8 bss_idx;
|
||||
u8 pad[3];
|
||||
} req_hdr = {
|
||||
.bss_idx = mvif->idx,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION ||
|
||||
!mt7615_firmware_offload(dev))
|
||||
return -ENOTSUPP;
|
||||
|
||||
err = __mt76_mcu_send_msg(&dev->mt76, MCU_CMD_SET_BSS_ABORT,
|
||||
&req_hdr, sizeof(req_hdr), false);
|
||||
if (err < 0 || !enable)
|
||||
return err;
|
||||
|
||||
return __mt76_mcu_send_msg(&dev->mt76, MCU_CMD_SET_BSS_CONNECTED,
|
||||
&req, sizeof(req), false);
|
||||
}
|
||||
|
||||
static int
|
||||
mt7615_mcu_set_wow_ctrl(struct mt7615_dev *dev, struct ieee80211_vif *vif,
|
||||
bool suspend, struct cfg80211_wowlan *wowlan)
|
||||
{
|
||||
struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
|
||||
struct {
|
||||
struct {
|
||||
u8 bss_idx;
|
||||
u8 pad[3];
|
||||
} __packed hdr;
|
||||
struct mt7615_wow_ctrl_tlv wow_ctrl_tlv;
|
||||
} req = {
|
||||
.hdr = {
|
||||
.bss_idx = mvif->idx,
|
||||
},
|
||||
.wow_ctrl_tlv = {
|
||||
.tag = cpu_to_le16(UNI_SUSPEND_WOW_CTRL),
|
||||
.len = cpu_to_le16(sizeof(struct mt7615_wow_ctrl_tlv)),
|
||||
.cmd = suspend ? 1 : 2,
|
||||
},
|
||||
};
|
||||
|
||||
if (wowlan->magic_pkt)
|
||||
req.wow_ctrl_tlv.trigger |= BIT(0);
|
||||
if (wowlan->disconnect)
|
||||
req.wow_ctrl_tlv.trigger |= BIT(2);
|
||||
|
||||
if (mt76_is_mmio(&dev->mt76))
|
||||
req.wow_ctrl_tlv.wakeup_hif = 2;
|
||||
else if (mt76_is_usb(&dev->mt76))
|
||||
req.wow_ctrl_tlv.wakeup_hif = 1;
|
||||
|
||||
return __mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD_SUSPEND,
|
||||
&req, sizeof(req), true);
|
||||
}
|
||||
|
||||
static int
|
||||
mt7615_mcu_set_wow_pattern(struct mt7615_dev *dev,
|
||||
struct ieee80211_vif *vif,
|
||||
u8 index, bool enable,
|
||||
struct cfg80211_pkt_pattern *pattern)
|
||||
{
|
||||
struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
|
||||
struct mt7615_wow_pattern_tlv *ptlv;
|
||||
struct sk_buff *skb;
|
||||
struct req_hdr {
|
||||
u8 bss_idx;
|
||||
u8 pad[3];
|
||||
} __packed hdr = {
|
||||
.bss_idx = mvif->idx,
|
||||
};
|
||||
|
||||
skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
|
||||
sizeof(hdr) + sizeof(*ptlv));
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb_put_data(skb, &hdr, sizeof(hdr));
|
||||
ptlv = (struct mt7615_wow_pattern_tlv *)skb_put(skb, sizeof(*ptlv));
|
||||
ptlv->tag = cpu_to_le16(UNI_SUSPEND_WOW_PATTERN);
|
||||
ptlv->len = cpu_to_le16(sizeof(*ptlv));
|
||||
ptlv->data_len = pattern->pattern_len;
|
||||
ptlv->enable = enable;
|
||||
ptlv->index = index;
|
||||
|
||||
memcpy(ptlv->pattern, pattern->pattern, pattern->pattern_len);
|
||||
memcpy(ptlv->mask, pattern->mask, pattern->pattern_len / 8);
|
||||
|
||||
return __mt76_mcu_skb_send_msg(&dev->mt76, skb,
|
||||
MCU_UNI_CMD_SUSPEND, true);
|
||||
}
|
||||
|
||||
static int
|
||||
mt7615_mcu_set_suspend_mode(struct mt7615_dev *dev,
|
||||
struct ieee80211_vif *vif,
|
||||
bool enable, u8 mdtim, bool wow_suspend)
|
||||
{
|
||||
struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
|
||||
struct {
|
||||
struct {
|
||||
u8 bss_idx;
|
||||
u8 pad[3];
|
||||
} __packed hdr;
|
||||
struct mt7615_suspend_tlv suspend_tlv;
|
||||
} req = {
|
||||
.hdr = {
|
||||
.bss_idx = mvif->idx,
|
||||
},
|
||||
.suspend_tlv = {
|
||||
.tag = cpu_to_le16(UNI_SUSPEND_MODE_SETTING),
|
||||
.len = cpu_to_le16(sizeof(struct mt7615_suspend_tlv)),
|
||||
.enable = enable,
|
||||
.mdtim = mdtim,
|
||||
.wow_suspend = wow_suspend,
|
||||
},
|
||||
};
|
||||
|
||||
return __mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD_SUSPEND,
|
||||
&req, sizeof(req), true);
|
||||
}
|
||||
|
||||
void mt7615_mcu_set_suspend_iter(void *priv, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct mt7615_phy *phy = priv;
|
||||
bool suspend = test_bit(MT76_STATE_SUSPEND, &phy->mt76->state);
|
||||
struct ieee80211_hw *hw = phy->mt76->hw;
|
||||
struct cfg80211_wowlan *wowlan = hw->wiphy->wowlan_config;
|
||||
int i;
|
||||
|
||||
mt7615_mcu_set_bss_pm(phy->dev, vif, suspend);
|
||||
|
||||
mt7615_mcu_set_suspend_mode(phy->dev, vif, suspend, 1, true);
|
||||
|
||||
for (i = 0; i < wowlan->n_patterns; i++)
|
||||
mt7615_mcu_set_wow_pattern(phy->dev, vif, i, suspend,
|
||||
&wowlan->patterns[i]);
|
||||
mt7615_mcu_set_wow_ctrl(phy->dev, vif, suspend, wowlan);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
@ -280,6 +280,7 @@ enum {
|
||||
MCU_UNI_CMD_DEV_INFO_UPDATE = MCU_UNI_PREFIX | 0x01,
|
||||
MCU_UNI_CMD_BSS_INFO_UPDATE = MCU_UNI_PREFIX | 0x02,
|
||||
MCU_UNI_CMD_STA_REC_UPDATE = MCU_UNI_PREFIX | 0x03,
|
||||
MCU_UNI_CMD_SUSPEND = MCU_UNI_PREFIX | 0x05,
|
||||
MCU_UNI_CMD_HIF_CTRL = MCU_UNI_PREFIX | 0x07,
|
||||
};
|
||||
|
||||
@ -415,6 +416,59 @@ struct mt7615_mcu_bss_event {
|
||||
u8 pad;
|
||||
} __packed;
|
||||
|
||||
struct mt7615_wow_ctrl_tlv {
|
||||
__le16 tag;
|
||||
__le16 len;
|
||||
u8 cmd; /* 0x1: PM_WOWLAN_REQ_START
|
||||
* 0x2: PM_WOWLAN_REQ_STOP
|
||||
* 0x3: PM_WOWLAN_PARAM_CLEAR
|
||||
*/
|
||||
u8 trigger; /* 0: NONE
|
||||
* BIT(0): NL80211_WOWLAN_TRIG_MAGIC_PKT
|
||||
* BIT(1): NL80211_WOWLAN_TRIG_ANY
|
||||
* BIT(2): NL80211_WOWLAN_TRIG_DISCONNECT
|
||||
* BIT(3): NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE
|
||||
* BIT(4): BEACON_LOST
|
||||
* BIT(5): NL80211_WOWLAN_TRIG_NET_DETECT
|
||||
*/
|
||||
u8 wakeup_hif; /* 0x0: HIF_SDIO
|
||||
* 0x1: HIF_USB
|
||||
* 0x2: HIF_PCIE
|
||||
* 0x3: HIF_GPIO
|
||||
*/
|
||||
u8 pad;
|
||||
u8 rsv[4];
|
||||
} __packed;
|
||||
|
||||
#define MT7615_WOW_MASK_MAX_LEN 16
|
||||
#define MT7615_WOW_PATTEN_MAX_LEN 128
|
||||
struct mt7615_wow_pattern_tlv {
|
||||
__le16 tag;
|
||||
__le16 len;
|
||||
u8 index; /* pattern index */
|
||||
u8 enable; /* 0: disable
|
||||
* 1: enable
|
||||
*/
|
||||
u8 data_len; /* pattern length */
|
||||
u8 pad;
|
||||
u8 mask[MT7615_WOW_MASK_MAX_LEN];
|
||||
u8 pattern[MT7615_WOW_PATTEN_MAX_LEN];
|
||||
u8 rsv[4];
|
||||
} __packed;
|
||||
|
||||
struct mt7615_suspend_tlv {
|
||||
__le16 tag;
|
||||
__le16 len;
|
||||
u8 enable; /* 0: suspend mode disabled
|
||||
* 1: suspend mode enabled
|
||||
*/
|
||||
u8 mdtim; /* LP parameter */
|
||||
u8 wow_suspend; /* 0: update by origin policy
|
||||
* 1: update by wow dtim
|
||||
*/
|
||||
u8 pad[5];
|
||||
} __packed;
|
||||
|
||||
/* offload mcu commands */
|
||||
enum {
|
||||
MCU_CMD_START_HW_SCAN = MCU_CE_PREFIX | 0x03,
|
||||
@ -439,6 +493,14 @@ enum {
|
||||
UNI_BSS_INFO_BCN_CONTENT = 7,
|
||||
};
|
||||
|
||||
enum {
|
||||
UNI_SUSPEND_MODE_SETTING,
|
||||
UNI_SUSPEND_WOW_CTRL,
|
||||
UNI_SUSPEND_WOW_GPIO_PARAM,
|
||||
UNI_SUSPEND_WOW_WAKEUP_PORT,
|
||||
UNI_SUSPEND_WOW_PATTERN,
|
||||
};
|
||||
|
||||
enum {
|
||||
PATCH_SEM_RELEASE = 0x0,
|
||||
PATCH_SEM_GET = 0x1
|
||||
|
@ -474,8 +474,6 @@ int mt7615_mac_wtbl_update_key(struct mt7615_dev *dev,
|
||||
enum set_key_cmd cmd);
|
||||
void mt7615_mac_reset_work(struct work_struct *work);
|
||||
|
||||
int mt7615_mcu_set_bss_pm(struct mt7615_dev *dev, struct ieee80211_vif *vif,
|
||||
bool enable);
|
||||
int mt7615_mcu_wait_response(struct mt7615_dev *dev, int cmd, int seq);
|
||||
int mt7615_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data,
|
||||
int len, bool wait_resp);
|
||||
@ -532,6 +530,8 @@ int mt7615_init_debugfs(struct mt7615_dev *dev);
|
||||
int mt7615_mcu_wait_response(struct mt7615_dev *dev, int cmd, int seq);
|
||||
|
||||
int mt7615_mcu_set_hif_suspend(struct mt7615_dev *dev, bool suspend);
|
||||
void mt7615_mcu_set_suspend_iter(void *priv, u8 *mac,
|
||||
struct ieee80211_vif *vif);
|
||||
|
||||
int __mt7663_load_firmware(struct mt7615_dev *dev);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user