Commit c1e0d2be authored by Lorenzo Bianconi's avatar Lorenzo Bianconi Committed by Felix Fietkau

mt76: mmio: introduce mt76x02_check_tx_hang watchdog

Port mt76x02_check_tx_hang watchdog from vendor driver in order to
perform a device reset when tx mac/dma logic hangs. Tx mac/dma stuck
has been observed when the device is heavy loaded or in a noisy
environment. Moreover introduce wdt delayed work in order to run
tx_hang watchdog. For the moment run mt76x02_check_tx_hang watchdog
just on mt76x2 devices since the issue has not been observed on mt76x0
driver yet
Signed-off-by: default avatarLorenzo Bianconi <lorenzo.bianconi@redhat.com>
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent 87e86f90
...@@ -421,6 +421,7 @@ struct mt76_dev { ...@@ -421,6 +421,7 @@ struct mt76_dev {
struct mt76_queue q_tx[__MT_TXQ_MAX]; struct mt76_queue q_tx[__MT_TXQ_MAX];
struct mt76_queue q_rx[__MT_RXQ_MAX]; struct mt76_queue q_rx[__MT_RXQ_MAX];
const struct mt76_queue_ops *queue_ops; const struct mt76_queue_ops *queue_ops;
int tx_dma_idx[4];
wait_queue_head_t tx_wait; wait_queue_head_t tx_wait;
struct sk_buff_head status_list; struct sk_buff_head status_list;
......
...@@ -28,6 +28,9 @@ ...@@ -28,6 +28,9 @@
#define MT_CALIBRATE_INTERVAL HZ #define MT_CALIBRATE_INTERVAL HZ
#define MT_WATCHDOG_TIME (HZ / 10)
#define MT_TX_HANG_TH 10
#define MT_MAX_CHAINS 2 #define MT_MAX_CHAINS 2
struct mt76x02_rx_freq_cal { struct mt76x02_rx_freq_cal {
s8 high_gain[MT_MAX_CHAINS]; s8 high_gain[MT_MAX_CHAINS];
...@@ -79,6 +82,7 @@ struct mt76x02_dev { ...@@ -79,6 +82,7 @@ struct mt76x02_dev {
struct tasklet_struct pre_tbtt_tasklet; struct tasklet_struct pre_tbtt_tasklet;
struct delayed_work cal_work; struct delayed_work cal_work;
struct delayed_work mac_work; struct delayed_work mac_work;
struct delayed_work wdt_work;
u32 aggr_stats[32]; u32 aggr_stats[32];
...@@ -89,6 +93,9 @@ struct mt76x02_dev { ...@@ -89,6 +93,9 @@ struct mt76x02_dev {
u8 tbtt_count; u8 tbtt_count;
u16 beacon_int; u16 beacon_int;
u32 tx_hang_reset;
u8 tx_hang_check;
struct mt76x02_calibration cal; struct mt76x02_calibration cal;
s8 target_power; s8 target_power;
...@@ -142,6 +149,7 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev, ...@@ -142,6 +149,7 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev,
const struct ieee80211_tx_rate *rate); const struct ieee80211_tx_rate *rate);
s8 mt76x02_tx_get_txpwr_adj(struct mt76x02_dev *dev, s8 txpwr, s8 mt76x02_tx_get_txpwr_adj(struct mt76x02_dev *dev, s8 txpwr,
s8 max_txpwr_adj); s8 max_txpwr_adj);
void mt76x02_wdt_work(struct work_struct *work);
void mt76x02_tx_set_txpwr_auto(struct mt76x02_dev *dev, s8 txpwr); void mt76x02_tx_set_txpwr_auto(struct mt76x02_dev *dev, s8 txpwr);
void mt76x02_set_tx_ackto(struct mt76x02_dev *dev); void mt76x02_set_tx_ackto(struct mt76x02_dev *dev);
void mt76x02_set_coverage_class(struct ieee80211_hw *hw, void mt76x02_set_coverage_class(struct ieee80211_hw *hw,
......
...@@ -133,5 +133,7 @@ void mt76x02_init_debugfs(struct mt76x02_dev *dev) ...@@ -133,5 +133,7 @@ void mt76x02_init_debugfs(struct mt76x02_dev *dev)
read_txpower); read_txpower);
debugfs_create_devm_seqfile(dev->mt76.dev, "agc", dir, read_agc); debugfs_create_devm_seqfile(dev->mt76.dev, "agc", dir, read_agc);
debugfs_create_u32("tx_hang_reset", 0400, dir, &dev->tx_hang_reset);
} }
EXPORT_SYMBOL_GPL(mt76x02_init_debugfs); EXPORT_SYMBOL_GPL(mt76x02_init_debugfs);
...@@ -943,7 +943,6 @@ void mt76x02_mac_work(struct work_struct *work) ...@@ -943,7 +943,6 @@ void mt76x02_mac_work(struct work_struct *work)
dev->aggr_stats[idx++] += val >> 16; dev->aggr_stats[idx++] += val >> 16;
} }
/* XXX: check beacon stuck for ap mode */
if (!dev->beacon_mask) if (!dev->beacon_mask)
mt76x02_check_mac_err(dev); mt76x02_check_mac_err(dev);
......
...@@ -385,3 +385,127 @@ void mt76x02_mac_start(struct mt76x02_dev *dev) ...@@ -385,3 +385,127 @@ void mt76x02_mac_start(struct mt76x02_dev *dev)
MT_INT_TX_STAT); MT_INT_TX_STAT);
} }
EXPORT_SYMBOL_GPL(mt76x02_mac_start); EXPORT_SYMBOL_GPL(mt76x02_mac_start);
static bool mt76x02_tx_hang(struct mt76x02_dev *dev)
{
u32 dma_idx, prev_dma_idx;
struct mt76_queue *q;
int i;
for (i = 0; i < 4; i++) {
q = &dev->mt76.q_tx[i];
if (!q->queued)
continue;
prev_dma_idx = dev->mt76.tx_dma_idx[i];
dma_idx = ioread32(&q->regs->dma_idx);
dev->mt76.tx_dma_idx[i] = dma_idx;
if (prev_dma_idx == dma_idx)
break;
}
return i < 4;
}
static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
{
u32 mask = dev->mt76.mmio.irqmask;
int i;
ieee80211_stop_queues(dev->mt76.hw);
set_bit(MT76_RESET, &dev->mt76.state);
tasklet_disable(&dev->pre_tbtt_tasklet);
tasklet_disable(&dev->tx_tasklet);
for (i = 0; i < ARRAY_SIZE(dev->mt76.napi); i++)
napi_disable(&dev->mt76.napi[i]);
mutex_lock(&dev->mt76.mutex);
if (dev->beacon_mask)
mt76_clear(dev, MT_BEACON_TIME_CFG,
MT_BEACON_TIME_CFG_BEACON_TX |
MT_BEACON_TIME_CFG_TBTT_EN);
mt76x02_irq_disable(dev, mask);
/* perform device reset */
mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
mt76_wr(dev, MT_MAC_SYS_CTRL, 0);
mt76_clear(dev, MT_WPDMA_GLO_CFG,
MT_WPDMA_GLO_CFG_TX_DMA_EN | MT_WPDMA_GLO_CFG_RX_DMA_EN);
usleep_range(5000, 10000);
mt76_wr(dev, MT_INT_SOURCE_CSR, 0xffffffff);
/* let fw reset DMA */
mt76_set(dev, 0x734, 0x3);
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);
if (dev->ed_monitor)
mt76_set(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
if (dev->beacon_mask)
mt76_set(dev, MT_BEACON_TIME_CFG,
MT_BEACON_TIME_CFG_BEACON_TX |
MT_BEACON_TIME_CFG_TBTT_EN);
mt76x02_irq_enable(dev, mask);
mutex_unlock(&dev->mt76.mutex);
clear_bit(MT76_RESET, &dev->mt76.state);
tasklet_enable(&dev->tx_tasklet);
tasklet_schedule(&dev->tx_tasklet);
tasklet_enable(&dev->pre_tbtt_tasklet);
for (i = 0; i < ARRAY_SIZE(dev->mt76.napi); i++) {
napi_enable(&dev->mt76.napi[i]);
napi_schedule(&dev->mt76.napi[i]);
}
ieee80211_wake_queues(dev->mt76.hw);
mt76_txq_schedule_all(&dev->mt76);
}
static void mt76x02_check_tx_hang(struct mt76x02_dev *dev)
{
if (mt76x02_tx_hang(dev)) {
if (++dev->tx_hang_check < MT_TX_HANG_TH)
return;
mt76x02_watchdog_reset(dev);
dev->tx_hang_reset++;
dev->tx_hang_check = 0;
memset(dev->mt76.tx_dma_idx, 0xff,
sizeof(dev->mt76.tx_dma_idx));
} else {
dev->tx_hang_check = 0;
}
}
void mt76x02_wdt_work(struct work_struct *work)
{
struct mt76x02_dev *dev = container_of(work, struct mt76x02_dev,
wdt_work.work);
mt76x02_check_tx_hang(dev);
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->wdt_work,
MT_WATCHDOG_TIME);
}
...@@ -93,6 +93,8 @@ void mt76x02_init_device(struct mt76x02_dev *dev) ...@@ -93,6 +93,8 @@ void mt76x02_init_device(struct mt76x02_dev *dev)
MT_DMA_HDR_LEN; MT_DMA_HDR_LEN;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
} else { } else {
INIT_DELAYED_WORK(&dev->wdt_work, mt76x02_wdt_work);
mt76x02_dfs_init_detector(dev); mt76x02_dfs_init_detector(dev);
wiphy->reg_notifier = mt76x02_regd_notifier; wiphy->reg_notifier = mt76x02_regd_notifier;
......
...@@ -301,6 +301,7 @@ void mt76x2_stop_hardware(struct mt76x02_dev *dev) ...@@ -301,6 +301,7 @@ void mt76x2_stop_hardware(struct mt76x02_dev *dev)
{ {
cancel_delayed_work_sync(&dev->cal_work); cancel_delayed_work_sync(&dev->cal_work);
cancel_delayed_work_sync(&dev->mac_work); cancel_delayed_work_sync(&dev->mac_work);
cancel_delayed_work_sync(&dev->wdt_work);
mt76x02_mcu_set_radio_state(dev, false); mt76x02_mcu_set_radio_state(dev, false);
mt76x2_mac_stop(dev, false); mt76x2_mac_stop(dev, false);
} }
......
...@@ -34,6 +34,8 @@ mt76x2_start(struct ieee80211_hw *hw) ...@@ -34,6 +34,8 @@ mt76x2_start(struct ieee80211_hw *hw)
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work, ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work,
MT_CALIBRATE_INTERVAL); MT_CALIBRATE_INTERVAL);
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->wdt_work,
MT_WATCHDOG_TIME);
set_bit(MT76_STATE_RUNNING, &dev->mt76.state); set_bit(MT76_STATE_RUNNING, &dev->mt76.state);
......
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