diff options
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76/mt7921/mcu.c')
-rw-r--r-- | drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 210 |
1 files changed, 156 insertions, 54 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index b5cc72e7e81c..5f3d56d570a5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -4,6 +4,7 @@ #include <linux/firmware.h> #include <linux/fs.h> #include "mt7921.h" +#include "mt7921_trace.h" #include "mcu.h" #include "mac.h" @@ -159,8 +160,10 @@ mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd, int ret = 0; if (!skb) { - dev_err(mdev->dev, "Message %d (seq %d) timeout\n", + dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", cmd, seq); + mt7921_reset(mdev); + return -ETIMEDOUT; } @@ -222,8 +225,16 @@ mt7921_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, u32 val; u8 seq; - /* TODO: make dynamic based on msg type */ - mdev->mcu.timeout = 20 * HZ; + switch (cmd) { + case MCU_UNI_CMD_HIF_CTRL: + case MCU_UNI_CMD_SUSPEND: + case MCU_UNI_CMD_OFFLOAD: + mdev->mcu.timeout = HZ / 3; + break; + default: + mdev->mcu.timeout = 3 * HZ; + break; + } seq = ++dev->mt76.mcu.msg_seq & 0xf; if (!seq) @@ -404,9 +415,12 @@ mt7921_mcu_tx_rate_report(struct mt7921_dev *dev, struct sk_buff *skb, if (wlan_idx >= MT76_N_WCIDS) return; + + rcu_read_lock(); + wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]); if (!wcid) - return; + goto out; msta = container_of(wcid, struct mt7921_sta, wcid); stats = &msta->stats; @@ -414,6 +428,8 @@ mt7921_mcu_tx_rate_report(struct mt7921_dev *dev, struct sk_buff *skb, /* current rate */ mt7921_mcu_tx_rate_parse(mphy, &peer, &rate, curr); stats->tx_rate = rate; +out: + rcu_read_unlock(); } static void @@ -466,33 +482,45 @@ mt7921_mcu_bss_event(struct mt7921_dev *dev, struct sk_buff *skb) static void mt7921_mcu_debug_msg_event(struct mt7921_dev *dev, struct sk_buff *skb) { - struct mt7921_mcu_rxd *rxd = (struct mt7921_mcu_rxd *)skb->data; - struct debug_msg { + struct mt7921_debug_msg { __le16 id; u8 type; u8 flag; __le32 value; __le16 len; u8 content[512]; - } __packed * debug_msg; - u16 cur_len; - int i; - - skb_pull(skb, sizeof(*rxd)); - debug_msg = (struct debug_msg *)skb->data; + } __packed * msg; - cur_len = min_t(u16, le16_to_cpu(debug_msg->len), 512); + skb_pull(skb, sizeof(struct mt7921_mcu_rxd)); + msg = (struct mt7921_debug_msg *)skb->data; - if (debug_msg->type == 0x3) { - for (i = 0 ; i < cur_len; i++) - if (!debug_msg->content[i]) - debug_msg->content[i] = ' '; + if (msg->type == 3) { /* fw log */ + u16 len = min_t(u16, le16_to_cpu(msg->len), 512); + int i; - dev_dbg(dev->mt76.dev, "%s", debug_msg->content); + for (i = 0 ; i < len; i++) { + if (!msg->content[i]) + msg->content[i] = ' '; + } + wiphy_info(mt76_hw(dev)->wiphy, "%.*s", len, msg->content); } } static void +mt7921_mcu_low_power_event(struct mt7921_dev *dev, struct sk_buff *skb) +{ + struct mt7921_mcu_lp_event { + u8 state; + u8 reserved[3]; + } __packed * event; + + skb_pull(skb, sizeof(struct mt7921_mcu_rxd)); + event = (struct mt7921_mcu_lp_event *)skb->data; + + trace_lp_event(dev, event->state); +} + +static void mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb) { struct mt7921_mcu_rxd *rxd = (struct mt7921_mcu_rxd *)skb->data; @@ -515,6 +543,9 @@ mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb) mt76_connac_mcu_coredump_event(&dev->mt76, skb, &dev->coredump); return; + case MCU_EVENT_LP_INFO: + mt7921_mcu_low_power_event(dev, skb); + break; default: break; } @@ -537,6 +568,7 @@ void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb) rxd->eid == MCU_EVENT_SCAN_DONE || rxd->eid == MCU_EVENT_DBG_MSG || rxd->eid == MCU_EVENT_COREDUMP || + rxd->eid == MCU_EVENT_LP_INFO || !rxd->seq) mt7921_mcu_rx_unsolicited_event(dev, skb); else @@ -919,6 +951,24 @@ int mt7921_mcu_fw_log_2_host(struct mt7921_dev *dev, u8 ctrl) sizeof(data), false); } +int mt7921_run_firmware(struct mt7921_dev *dev) +{ + int err; + + err = mt7921_driver_own(dev); + if (err) + return err; + + err = mt7921_load_firmware(dev); + if (err) + return err; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + mt7921_mcu_fw_log_2_host(dev, 1); + + return 0; +} + int mt7921_mcu_init(struct mt7921_dev *dev) { static const struct mt76_mcu_ops mt7921_mcu_ops = { @@ -927,38 +977,15 @@ int mt7921_mcu_init(struct mt7921_dev *dev) .mcu_parse_response = mt7921_mcu_parse_response, .mcu_restart = mt7921_mcu_restart, }; - int ret; dev->mt76.mcu_ops = &mt7921_mcu_ops; - ret = mt7921_driver_own(dev); - if (ret) - return ret; - - ret = mt7921_load_firmware(dev); - if (ret) - return ret; - - set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); - mt7921_mcu_fw_log_2_host(dev, 1); - - return 0; + return mt7921_run_firmware(dev); } void mt7921_mcu_exit(struct mt7921_dev *dev) { - u32 reg = mt7921_reg_map_l1(dev, MT_TOP_MISC); - - __mt76_mcu_restart(&dev->mt76); - if (!mt76_poll_msec(dev, reg, MT_TOP_MISC_FW_STATE, - FIELD_PREP(MT_TOP_MISC_FW_STATE, - FW_STATE_FW_DOWNLOAD), 1000)) { - dev_err(dev->mt76.dev, "Failed to exit mcu\n"); - return; - } - - reg = mt7921_reg_map_l1(dev, MT_TOP_LPCR_HOST_BAND0); - mt76_wr(dev, reg, MT_TOP_LPCR_HOST_FW_OWN); + mt7921_wfsys_reset(dev); skb_queue_purge(&dev->mt76.mcu.res_q); } @@ -1238,12 +1265,35 @@ int mt7921_mcu_set_bss_pm(struct mt7921_dev *dev, struct ieee80211_vif *vif, sizeof(req), false); } +int mt7921_mcu_sta_add(struct mt7921_dev *dev, struct ieee80211_sta *sta, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + int rssi = -ewma_rssi_read(&mvif->rssi); + struct mt76_sta_cmd_info info = { + .sta = sta, + .vif = vif, + .enable = enable, + .cmd = MCU_UNI_CMD_STA_REC_UPDATE, + .rcpi = to_rcpi(rssi), + }; + struct mt7921_sta *msta; + + msta = sta ? (struct mt7921_sta *)sta->drv_priv : NULL; + info.wcid = msta ? &msta->wcid : &mvif->sta.wcid; + + return mt76_connac_mcu_add_sta_cmd(&dev->mphy, &info); +} + int mt7921_mcu_drv_pmctrl(struct mt7921_dev *dev) { struct mt76_phy *mphy = &dev->mt76.phy; - int i; + struct mt76_connac_pm *pm = &dev->pm; + int i, err = 0; + + mutex_lock(&pm->mutex); - if (!test_and_clear_bit(MT76_STATE_PM, &mphy->state)) + if (!test_bit(MT76_STATE_PM, &mphy->state)) goto out; for (i = 0; i < MT7921_DRV_OWN_RETRY_COUNT; i++) { @@ -1255,22 +1305,35 @@ int mt7921_mcu_drv_pmctrl(struct mt7921_dev *dev) if (i == MT7921_DRV_OWN_RETRY_COUNT) { dev_err(dev->mt76.dev, "driver own failed\n"); - return -EIO; + err = -EIO; + goto out; } + mt7921_wpdma_reinit_cond(dev); + clear_bit(MT76_STATE_PM, &mphy->state); + + pm->stats.last_wake_event = jiffies; + pm->stats.doze_time += pm->stats.last_wake_event - + pm->stats.last_doze_event; out: - dev->pm.last_activity = jiffies; + mutex_unlock(&pm->mutex); - return 0; + if (err) + mt7921_reset(&dev->mt76); + + return err; } int mt7921_mcu_fw_pmctrl(struct mt7921_dev *dev) { struct mt76_phy *mphy = &dev->mt76.phy; - int i; + struct mt76_connac_pm *pm = &dev->pm; + int i, err = 0; - if (test_and_set_bit(MT76_STATE_PM, &mphy->state)) - return 0; + mutex_lock(&pm->mutex); + + if (mt76_connac_skip_fw_pmctrl(mphy, pm)) + goto out; for (i = 0; i < MT7921_DRV_OWN_RETRY_COUNT; i++) { mt76_wr(dev, MT_CONN_ON_LPCTL, PCIE_LPCR_HOST_SET_OWN); @@ -1281,10 +1344,20 @@ int mt7921_mcu_fw_pmctrl(struct mt7921_dev *dev) if (i == MT7921_DRV_OWN_RETRY_COUNT) { dev_err(dev->mt76.dev, "firmware own failed\n"); - return -EIO; + clear_bit(MT76_STATE_PM, &mphy->state); + err = -EIO; } - return 0; + pm->stats.last_doze_event = jiffies; + pm->stats.awake_time += pm->stats.last_doze_event - + pm->stats.last_wake_event; +out: + mutex_unlock(&pm->mutex); + + if (err) + mt7921_reset(&dev->mt76); + + return err; } void @@ -1292,8 +1365,14 @@ mt7921_pm_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { struct mt7921_phy *phy = priv; struct mt7921_dev *dev = phy->dev; + int ret; - if (mt7921_mcu_set_bss_pm(dev, vif, dev->pm.enable)) + if (dev->pm.enable) + ret = mt7921_mcu_uni_bss_bcnft(dev, vif, true); + else + ret = mt7921_mcu_set_bss_pm(dev, vif, false); + + if (ret) return; if (dev->pm.enable) { @@ -1304,3 +1383,26 @@ mt7921_pm_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) mt76_clear(dev, MT_WF_RFCR(0), MT_WF_RFCR_DROP_OTHER_BEACON); } } + +int mt7921_get_txpwr_info(struct mt7921_dev *dev, struct mt7921_txpwr *txpwr) +{ + struct mt7921_txpwr_event *event; + struct mt7921_txpwr_req req = { + .dbdc_idx = 0, + }; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_CMD_GET_TXPWR, + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + event = (struct mt7921_txpwr_event *)skb->data; + WARN_ON(skb->len != le16_to_cpu(event->len)); + memcpy(txpwr, &event->txpwr, sizeof(event->txpwr)); + + dev_kfree_skb(skb); + + return 0; +} |