Commit 00496042 authored by Felix Fietkau's avatar Felix Fietkau

mt76: mt76x2: implement full device restart on watchdog reset

Restart the firmware and re-initialize the MAC to be able to recover
from more kinds of hang states
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent de3c2af1
...@@ -144,6 +144,7 @@ struct mt76_mcu_ops { ...@@ -144,6 +144,7 @@ struct mt76_mcu_ops {
const struct mt76_reg_pair *rp, int len); const struct mt76_reg_pair *rp, int len);
int (*mcu_rd_rp)(struct mt76_dev *dev, u32 base, int (*mcu_rd_rp)(struct mt76_dev *dev, u32 base,
struct mt76_reg_pair *rp, int len); struct mt76_reg_pair *rp, int len);
int (*mcu_restart)(struct mt76_dev *dev);
}; };
struct mt76_queue_ops { struct mt76_queue_ops {
......
...@@ -67,6 +67,32 @@ int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx, ...@@ -67,6 +67,32 @@ int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx,
} }
EXPORT_SYMBOL_GPL(mt76x02_mac_shared_key_setup); EXPORT_SYMBOL_GPL(mt76x02_mac_shared_key_setup);
void mt76x02_mac_wcid_sync_pn(struct mt76x02_dev *dev, u8 idx,
struct ieee80211_key_conf *key)
{
enum mt76x02_cipher_type cipher;
u8 key_data[32];
u32 iv, eiv;
u64 pn;
cipher = mt76x02_mac_get_key_info(key, key_data);
iv = mt76_rr(dev, MT_WCID_IV(idx));
eiv = mt76_rr(dev, MT_WCID_IV(idx) + 4);
pn = (u64)eiv << 16;
if (cipher == MT_CIPHER_TKIP) {
pn |= (iv >> 16) & 0xff;
pn |= (iv & 0xff) << 8;
} else if (cipher >= MT_CIPHER_AES_CCMP) {
pn |= iv & 0xffff;
} else {
return;
}
atomic64_set(&key->tx_pn, pn);
}
int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx, int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
struct ieee80211_key_conf *key) struct ieee80211_key_conf *key)
{ {
......
...@@ -177,6 +177,8 @@ int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx, ...@@ -177,6 +177,8 @@ int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx,
u8 key_idx, struct ieee80211_key_conf *key); u8 key_idx, struct ieee80211_key_conf *key);
int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx, int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
struct ieee80211_key_conf *key); struct ieee80211_key_conf *key);
void mt76x02_mac_wcid_sync_pn(struct mt76x02_dev *dev, u8 idx,
struct ieee80211_key_conf *key);
void mt76x02_mac_wcid_setup(struct mt76x02_dev *dev, u8 idx, u8 vif_idx, void mt76x02_mac_wcid_setup(struct mt76x02_dev *dev, u8 idx, u8 vif_idx,
u8 *mac); u8 *mac);
void mt76x02_mac_wcid_set_drop(struct mt76x02_dev *dev, u8 idx, bool drop); void mt76x02_mac_wcid_set_drop(struct mt76x02_dev *dev, u8 idx, bool drop);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include "mt76x02.h" #include "mt76x02.h"
#include "mt76x02_mcu.h"
#include "mt76x02_trace.h" #include "mt76x02_trace.h"
struct beacon_bc_data { struct beacon_bc_data {
...@@ -418,9 +419,65 @@ static bool mt76x02_tx_hang(struct mt76x02_dev *dev) ...@@ -418,9 +419,65 @@ static bool mt76x02_tx_hang(struct mt76x02_dev *dev)
return i < 4; return i < 4;
} }
static void mt76x02_key_sync(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key, void *data)
{
struct mt76x02_dev *dev = hw->priv;
struct mt76_wcid *wcid;
if (!sta)
return;
wcid = (struct mt76_wcid *) sta->drv_priv;
if (wcid->hw_key_idx != key->keyidx || wcid->sw_iv)
return;
mt76x02_mac_wcid_sync_pn(dev, wcid->idx, key);
}
static void mt76x02_reset_state(struct mt76x02_dev *dev)
{
int i;
clear_bit(MT76_STATE_RUNNING, &dev->mt76.state);
rcu_read_lock();
ieee80211_iter_keys_rcu(dev->mt76.hw, NULL, mt76x02_key_sync, NULL);
for (i = 0; i < ARRAY_SIZE(dev->mt76.wcid); i++) {
struct mt76_wcid *wcid = rcu_dereference(dev->mt76.wcid[i]);
struct mt76x02_sta *msta;
struct ieee80211_sta *sta;
struct ieee80211_vif *vif;
void *priv;
if (!wcid)
continue;
priv = msta = container_of(wcid, struct mt76x02_sta, wcid);
sta = container_of(priv, struct ieee80211_sta, drv_priv);
priv = msta->vif;
vif = container_of(priv, struct ieee80211_vif, drv_priv);
mt76_sta_state(dev->mt76.hw, vif, sta,
IEEE80211_STA_NONE, IEEE80211_STA_NOTEXIST);
memset(msta, 0, sizeof(*msta));
}
rcu_read_unlock();
dev->vif_mask = 0;
dev->beacon_mask = 0;
}
static void mt76x02_watchdog_reset(struct mt76x02_dev *dev) static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
{ {
u32 mask = dev->mt76.mmio.irqmask; u32 mask = dev->mt76.mmio.irqmask;
bool restart = dev->mt76.mcu_ops->mcu_restart;
int i; int i;
ieee80211_stop_queues(dev->mt76.hw); ieee80211_stop_queues(dev->mt76.hw);
...@@ -432,6 +489,9 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev) ...@@ -432,6 +489,9 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
for (i = 0; i < ARRAY_SIZE(dev->mt76.napi); i++) for (i = 0; i < ARRAY_SIZE(dev->mt76.napi); i++)
napi_disable(&dev->mt76.napi[i]); napi_disable(&dev->mt76.napi[i]);
if (restart)
mt76x02_reset_state(dev);
mutex_lock(&dev->mt76.mutex); mutex_lock(&dev->mt76.mutex);
if (dev->beacon_mask) if (dev->beacon_mask)
...@@ -452,20 +512,21 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev) ...@@ -452,20 +512,21 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
/* let fw reset DMA */ /* let fw reset DMA */
mt76_set(dev, 0x734, 0x3); mt76_set(dev, 0x734, 0x3);
if (restart)
dev->mt76.mcu_ops->mcu_restart(&dev->mt76);
for (i = 0; i < ARRAY_SIZE(dev->mt76.q_tx); i++) for (i = 0; i < ARRAY_SIZE(dev->mt76.q_tx); i++)
mt76_queue_tx_cleanup(dev, i, true); mt76_queue_tx_cleanup(dev, i, true);
for (i = 0; i < ARRAY_SIZE(dev->mt76.q_rx); i++) for (i = 0; i < ARRAY_SIZE(dev->mt76.q_rx); i++)
mt76_queue_rx_reset(dev, i); mt76_queue_rx_reset(dev, i);
mt76_wr(dev, MT_MAC_SYS_CTRL, mt76x02_mac_start(dev);
MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX);
mt76_set(dev, MT_WPDMA_GLO_CFG,
MT_WPDMA_GLO_CFG_TX_DMA_EN | MT_WPDMA_GLO_CFG_RX_DMA_EN);
if (dev->ed_monitor) if (dev->ed_monitor)
mt76_set(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN); mt76_set(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
if (dev->beacon_mask) if (dev->beacon_mask && !restart)
mt76_set(dev, MT_BEACON_TIME_CFG, mt76_set(dev, MT_BEACON_TIME_CFG,
MT_BEACON_TIME_CFG_BEACON_TX | MT_BEACON_TIME_CFG_BEACON_TX |
MT_BEACON_TIME_CFG_TBTT_EN); MT_BEACON_TIME_CFG_TBTT_EN);
...@@ -486,9 +547,13 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev) ...@@ -486,9 +547,13 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
napi_schedule(&dev->mt76.napi[i]); napi_schedule(&dev->mt76.napi[i]);
} }
ieee80211_wake_queues(dev->mt76.hw); if (restart) {
mt76x02_mcu_function_select(dev, Q_SELECT, 1);
mt76_txq_schedule_all(&dev->mt76); ieee80211_restart_hw(dev->mt76.hw);
} else {
ieee80211_wake_queues(dev->mt76.hw);
mt76_txq_schedule_all(&dev->mt76);
}
} }
static void mt76x02_check_tx_hang(struct mt76x02_dev *dev) static void mt76x02_check_tx_hang(struct mt76x02_dev *dev)
......
...@@ -237,6 +237,8 @@ int mt76x02_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, ...@@ -237,6 +237,8 @@ int mt76x02_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
int idx = 0; int idx = 0;
memset(msta, 0, sizeof(*msta));
idx = mt76_wcid_alloc(dev->mt76.wcid_mask, ARRAY_SIZE(dev->mt76.wcid)); idx = mt76_wcid_alloc(dev->mt76.wcid_mask, ARRAY_SIZE(dev->mt76.wcid));
if (idx < 0) if (idx < 0)
return -ENOSPC; return -ENOSPC;
...@@ -274,6 +276,8 @@ mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif, ...@@ -274,6 +276,8 @@ mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif,
struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
struct mt76_txq *mtxq; struct mt76_txq *mtxq;
memset(mvif, 0, sizeof(*mvif));
mvif->idx = idx; mvif->idx = idx;
mvif->group_wcid.idx = MT_VIF_WCID(idx); mvif->group_wcid.idx = MT_VIF_WCID(idx);
mvif->group_wcid.hw_key_idx = -1; mvif->group_wcid.hw_key_idx = -1;
......
...@@ -71,6 +71,7 @@ int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level, ...@@ -71,6 +71,7 @@ int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level,
void mt76x2_cleanup(struct mt76x02_dev *dev); void mt76x2_cleanup(struct mt76x02_dev *dev);
int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard);
void mt76x2_reset_wlan(struct mt76x02_dev *dev, bool enable); void mt76x2_reset_wlan(struct mt76x02_dev *dev, bool enable);
void mt76x2_init_txpower(struct mt76x02_dev *dev, void mt76x2_init_txpower(struct mt76x02_dev *dev,
struct ieee80211_supported_band *sband); struct ieee80211_supported_band *sband);
......
...@@ -77,7 +77,7 @@ mt76x2_fixup_xtal(struct mt76x02_dev *dev) ...@@ -77,7 +77,7 @@ mt76x2_fixup_xtal(struct mt76x02_dev *dev)
} }
} }
static int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard) int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
{ {
const u8 *macaddr = dev->mt76.macaddr; const u8 *macaddr = dev->mt76.macaddr;
u32 val; u32 val;
......
...@@ -165,9 +165,30 @@ mt76pci_load_firmware(struct mt76x02_dev *dev) ...@@ -165,9 +165,30 @@ mt76pci_load_firmware(struct mt76x02_dev *dev)
return -ENOENT; return -ENOENT;
} }
static int
mt76pci_mcu_restart(struct mt76_dev *mdev)
{
struct mt76x02_dev *dev;
int ret;
dev = container_of(mdev, struct mt76x02_dev, mt76);
mt76x02_mcu_cleanup(dev);
mt76x2_mac_reset(dev, true);
ret = mt76pci_load_firmware(dev);
if (ret)
return ret;
mt76_wr(dev, MT_WPDMA_RST_IDX, ~0);
return 0;
}
int mt76x2_mcu_init(struct mt76x02_dev *dev) int mt76x2_mcu_init(struct mt76x02_dev *dev)
{ {
static const struct mt76_mcu_ops mt76x2_mcu_ops = { static const struct mt76_mcu_ops mt76x2_mcu_ops = {
.mcu_restart = mt76pci_mcu_restart,
.mcu_send_msg = mt76x02_mcu_msg_send, .mcu_send_msg = mt76x02_mcu_msg_send,
}; };
int ret; int ret;
......
...@@ -580,6 +580,9 @@ void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) ...@@ -580,6 +580,9 @@ void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv; struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
struct mt76_queue *hwq = mtxq->hwq; struct mt76_queue *hwq = mtxq->hwq;
if (!test_bit(MT76_STATE_RUNNING, &dev->state))
return;
spin_lock_bh(&hwq->lock); spin_lock_bh(&hwq->lock);
if (list_empty(&mtxq->list)) if (list_empty(&mtxq->list))
list_add_tail(&mtxq->list, &hwq->swq); list_add_tail(&mtxq->list, &hwq->swq);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment