diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index cdcb324093a5..f3d332d11f81 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -105,7 +105,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, fail: WARN_ON(1); - ieee80211_queue_work(wl->hw, &wl->recovery_work); + wl12xx_queue_recovery_work(wl); return ret; } @@ -356,7 +356,7 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask); if (ret != 0) { - ieee80211_queue_work(wl->hw, &wl->recovery_work); + wl12xx_queue_recovery_work(wl); return ret; } diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index c3f19463474d..da2127018300 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -306,7 +306,7 @@ static ssize_t start_recovery_write(struct file *file, struct wl1271 *wl = file->private_data; mutex_lock(&wl->mutex); - ieee80211_queue_work(wl->hw, &wl->recovery_work); + wl12xx_queue_recovery_work(wl); mutex_unlock(&wl->mutex); return count; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 2c03b4716d3f..6926d0a3e5c6 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -937,7 +937,7 @@ irqreturn_t wl1271_irq(int irq, void *cookie) if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) { wl1271_error("watchdog interrupt received! " "starting recovery."); - ieee80211_queue_work(wl->hw, &wl->recovery_work); + wl12xx_queue_recovery_work(wl); /* restarting the chip. ignore any other interrupt. */ goto out; @@ -1099,6 +1099,12 @@ out: return ret; } +void wl12xx_queue_recovery_work(struct wl1271 *wl) +{ + if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) + ieee80211_queue_work(wl->hw, &wl->recovery_work); +} + static void wl1271_recovery_work(struct work_struct *work) { struct wl1271 *wl = @@ -1109,6 +1115,9 @@ static void wl1271_recovery_work(struct work_struct *work) if (wl->state != WL1271_STATE_ON) goto out; + /* Avoid a recursive recovery */ + set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); + wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4)); @@ -1125,6 +1134,9 @@ static void wl1271_recovery_work(struct work_struct *work) /* reboot the chipset */ __wl1271_op_remove_interface(wl, false); + + clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); + ieee80211_restart_hw(wl->hw); /* diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index 5116db0826f9..3e68a664c9de 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -118,7 +118,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT)); if (ret == 0) { wl1271_error("ELP wakeup timeout!"); - ieee80211_queue_work(wl->hw, &wl->recovery_work); + wl12xx_queue_recovery_work(wl); ret = -ETIMEDOUT; goto err; } else if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index cb84dd5edf4d..5e5c66dd06d5 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -62,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work) if (wl->scan.failed) { wl1271_info("Scan completed due to error."); - ieee80211_queue_work(wl->hw, &wl->recovery_work); + wl12xx_queue_recovery_work(wl); } out: diff --git a/drivers/net/wireless/wl12xx/testmode.c b/drivers/net/wireless/wl12xx/testmode.c index da351d7cd1f2..5d5e1ef87206 100644 --- a/drivers/net/wireless/wl12xx/testmode.c +++ b/drivers/net/wireless/wl12xx/testmode.c @@ -260,7 +260,7 @@ static int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[]) { wl1271_debug(DEBUG_TESTMODE, "testmode cmd recover"); - ieee80211_queue_work(wl->hw, &wl->recovery_work); + wl12xx_queue_recovery_work(wl); return 0; } diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index bbf4ee6d4102..754a16ce5bc0 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -361,6 +361,7 @@ enum wl12xx_flags { WL1271_FLAG_PENDING_WORK, WL1271_FLAG_SOFT_GEMINI, WL1271_FLAG_RX_STREAMING_STARTED, + WL1271_FLAG_RECOVERY_IN_PROGRESS, }; struct wl1271_link { @@ -612,6 +613,7 @@ struct wl1271_station { int wl1271_plt_start(struct wl1271 *wl); int wl1271_plt_stop(struct wl1271 *wl); int wl1271_recalc_rx_streaming(struct wl1271 *wl); +void wl12xx_queue_recovery_work(struct wl1271 *wl); #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */