Commit 00496042 authored by Felix Fietkau's avatar Felix Fietkau
Browse files

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
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -144,6 +144,7 @@ struct mt76_mcu_ops {
			 const struct mt76_reg_pair *rp, int len);
	int (*mcu_rd_rp)(struct mt76_dev *dev, u32 base,
			 struct mt76_reg_pair *rp, int len);
	int (*mcu_restart)(struct mt76_dev *dev);
};

struct mt76_queue_ops {
+26 −0
Original line number Diff line number Diff line
@@ -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);

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,
			     struct ieee80211_key_conf *key)
{
+2 −0
Original line number Diff line number Diff line
@@ -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);
int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
			     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,
			    u8 *mac);
void mt76x02_mac_wcid_set_drop(struct mt76x02_dev *dev, u8 idx, bool drop);
+73 −8
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/irq.h>

#include "mt76x02.h"
#include "mt76x02_mcu.h"
#include "mt76x02_trace.h"

struct beacon_bc_data {
@@ -418,9 +419,65 @@ static bool mt76x02_tx_hang(struct mt76x02_dev *dev)
	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)
{
	u32 mask = dev->mt76.mmio.irqmask;
	bool restart = dev->mt76.mcu_ops->mcu_restart;
	int i;

	ieee80211_stop_queues(dev->mt76.hw);
@@ -432,6 +489,9 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
	for (i = 0; i < ARRAY_SIZE(dev->mt76.napi); i++)
		napi_disable(&dev->mt76.napi[i]);

	if (restart)
		mt76x02_reset_state(dev);

	mutex_lock(&dev->mt76.mutex);

	if (dev->beacon_mask)
@@ -452,20 +512,21 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
	/* let fw reset DMA */
	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++)
		mt76_queue_tx_cleanup(dev, i, true);

	for (i = 0; i < ARRAY_SIZE(dev->mt76.q_rx); i++)
		mt76_queue_rx_reset(dev, i);

	mt76_wr(dev, MT_MAC_SYS_CTRL,
		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);
	mt76x02_mac_start(dev);

	if (dev->ed_monitor)
		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,
			 MT_BEACON_TIME_CFG_BEACON_TX |
			 MT_BEACON_TIME_CFG_TBTT_EN);
@@ -486,10 +547,14 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
		napi_schedule(&dev->mt76.napi[i]);
	}

	if (restart) {
		mt76x02_mcu_function_select(dev, Q_SELECT, 1);
		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)
{
+4 −0
Original line number Diff line number Diff line
@@ -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;
	int idx = 0;

	memset(msta, 0, sizeof(*msta));

	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, ARRAY_SIZE(dev->mt76.wcid));
	if (idx < 0)
		return -ENOSPC;
@@ -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 mt76_txq *mtxq;

	memset(mvif, 0, sizeof(*mvif));

	mvif->idx = idx;
	mvif->group_wcid.idx = MT_VIF_WCID(idx);
	mvif->group_wcid.hw_key_idx = -1;
Loading