|
30 | 30 | #include "rt2800lib.h"
|
31 | 31 | #include "rt2800.h"
|
32 | 32 |
|
33 |
| -static bool modparam_watchdog; |
34 |
| -module_param_named(watchdog, modparam_watchdog, bool, S_IRUGO); |
35 |
| -MODULE_PARM_DESC(watchdog, "Enable watchdog to detect tx/rx hangs and reset hardware if detected"); |
| 33 | +static unsigned int modparam_watchdog = RT2800_WATCHDOG_DMA_BUSY; |
| 34 | +module_param_named(watchdog, modparam_watchdog, uint, 0444); |
| 35 | +MODULE_PARM_DESC(watchdog, "Enable watchdog to recover tx/rx hangs.\n" |
| 36 | + "\t\t(0=disabled, 1=hang watchdog, 2=DMA watchdog(default), 3=both)"); |
36 | 37 |
|
37 | 38 | /*
|
38 | 39 | * Register access.
|
@@ -1261,15 +1262,12 @@ static void rt2800_update_survey(struct rt2x00_dev *rt2x00dev)
|
1261 | 1262 | chan_survey->time_ext_busy += rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC);
|
1262 | 1263 | }
|
1263 | 1264 |
|
1264 |
| -void rt2800_watchdog(struct rt2x00_dev *rt2x00dev) |
| 1265 | +static bool rt2800_watchdog_hung(struct rt2x00_dev *rt2x00dev) |
1265 | 1266 | {
|
1266 | 1267 | struct data_queue *queue;
|
1267 | 1268 | bool hung_tx = false;
|
1268 | 1269 | bool hung_rx = false;
|
1269 | 1270 |
|
1270 |
| - if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) |
1271 |
| - return; |
1272 |
| - |
1273 | 1271 | rt2800_update_survey(rt2x00dev);
|
1274 | 1272 |
|
1275 | 1273 | queue_for_each(rt2x00dev, queue) {
|
@@ -1297,18 +1295,72 @@ void rt2800_watchdog(struct rt2x00_dev *rt2x00dev)
|
1297 | 1295 | }
|
1298 | 1296 | }
|
1299 | 1297 |
|
| 1298 | + if (!hung_tx && !hung_rx) |
| 1299 | + return false; |
| 1300 | + |
1300 | 1301 | if (hung_tx)
|
1301 | 1302 | rt2x00_warn(rt2x00dev, "Watchdog TX hung detected\n");
|
1302 | 1303 |
|
1303 | 1304 | if (hung_rx)
|
1304 | 1305 | rt2x00_warn(rt2x00dev, "Watchdog RX hung detected\n");
|
1305 | 1306 |
|
1306 |
| - if (hung_tx || hung_rx) { |
1307 |
| - queue_for_each(rt2x00dev, queue) |
1308 |
| - queue->wd_count = 0; |
| 1307 | + queue_for_each(rt2x00dev, queue) |
| 1308 | + queue->wd_count = 0; |
| 1309 | + |
| 1310 | + return true; |
| 1311 | +} |
| 1312 | + |
| 1313 | +static bool rt2800_watchdog_dma_busy(struct rt2x00_dev *rt2x00dev) |
| 1314 | +{ |
| 1315 | + bool busy_rx, busy_tx; |
| 1316 | + u32 reg_cfg = rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG); |
| 1317 | + u32 reg_int = rt2800_register_read(rt2x00dev, INT_SOURCE_CSR); |
| 1318 | + |
| 1319 | + if (rt2x00_get_field32(reg_cfg, WPDMA_GLO_CFG_RX_DMA_BUSY) && |
| 1320 | + rt2x00_get_field32(reg_int, INT_SOURCE_CSR_RX_COHERENT)) |
| 1321 | + rt2x00dev->rxdma_busy++; |
| 1322 | + else |
| 1323 | + rt2x00dev->rxdma_busy = 0; |
1309 | 1324 |
|
| 1325 | + if (rt2x00_get_field32(reg_cfg, WPDMA_GLO_CFG_TX_DMA_BUSY) && |
| 1326 | + rt2x00_get_field32(reg_int, INT_SOURCE_CSR_TX_COHERENT)) |
| 1327 | + rt2x00dev->txdma_busy++; |
| 1328 | + else |
| 1329 | + rt2x00dev->txdma_busy = 0; |
| 1330 | + |
| 1331 | + busy_rx = rt2x00dev->rxdma_busy > 30 ? true : false; |
| 1332 | + busy_tx = rt2x00dev->txdma_busy > 30 ? true : false; |
| 1333 | + |
| 1334 | + if (!busy_rx && !busy_tx) |
| 1335 | + return false; |
| 1336 | + |
| 1337 | + if (busy_rx) |
| 1338 | + rt2x00_warn(rt2x00dev, "Watchdog RX DMA busy detected\n"); |
| 1339 | + |
| 1340 | + if (busy_tx) |
| 1341 | + rt2x00_warn(rt2x00dev, "Watchdog TX DMA busy detected\n"); |
| 1342 | + |
| 1343 | + rt2x00dev->rxdma_busy = 0; |
| 1344 | + rt2x00dev->txdma_busy = 0; |
| 1345 | + |
| 1346 | + return true; |
| 1347 | +} |
| 1348 | + |
| 1349 | +void rt2800_watchdog(struct rt2x00_dev *rt2x00dev) |
| 1350 | +{ |
| 1351 | + bool reset = false; |
| 1352 | + |
| 1353 | + if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) |
| 1354 | + return; |
| 1355 | + |
| 1356 | + if (modparam_watchdog & RT2800_WATCHDOG_DMA_BUSY) |
| 1357 | + reset = rt2800_watchdog_dma_busy(rt2x00dev); |
| 1358 | + |
| 1359 | + if (modparam_watchdog & RT2800_WATCHDOG_HANG) |
| 1360 | + reset = rt2800_watchdog_hung(rt2x00dev) || reset; |
| 1361 | + |
| 1362 | + if (reset) |
1310 | 1363 | ieee80211_restart_hw(rt2x00dev->hw);
|
1311 |
| - } |
1312 | 1364 | }
|
1313 | 1365 | EXPORT_SYMBOL_GPL(rt2800_watchdog);
|
1314 | 1366 |
|
@@ -12013,6 +12065,9 @@ int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev)
|
12013 | 12065 | __set_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags);
|
12014 | 12066 | }
|
12015 | 12067 |
|
| 12068 | + /* USB NICs don't support DMA watchdog as INT_SOURCE_CSR is invalid */ |
| 12069 | + if (rt2x00_is_usb(rt2x00dev)) |
| 12070 | + modparam_watchdog &= ~RT2800_WATCHDOG_DMA_BUSY; |
12016 | 12071 | if (modparam_watchdog) {
|
12017 | 12072 | __set_bit(CAPABILITY_RESTART_HW, &rt2x00dev->cap_flags);
|
12018 | 12073 | rt2x00dev->link.watchdog_interval = msecs_to_jiffies(100);
|
|
0 commit comments