diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 272cff44ab53..3576c1cb067f 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -408,6 +408,9 @@ struct wl1271 { /* The target interrupt mask */ struct work_struct irq_work; + /* Hardware recovery work */ + struct work_struct recovery_work; + /* The mbox event mask */ u32 event_mask; diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 06b14f2abf55..170b5a8bdabc 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -94,6 +94,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, status = le16_to_cpu(cmd->status); if (status != CMD_STATUS_SUCCESS) { wl1271_error("command execute failure %d", status); + ieee80211_queue_work(wl->hw, &wl->recovery_work); ret = -EIO; } @@ -182,8 +183,10 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) timeout = jiffies + msecs_to_jiffies(WL1271_EVENT_TIMEOUT); do { - if (time_after(jiffies, timeout)) + if (time_after(jiffies, timeout)) { + ieee80211_queue_work(wl->hw, &wl->recovery_work); return -ETIMEDOUT; + } msleep(1); diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index e7f096fb6212..fecb0c313a1d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -235,6 +235,9 @@ static struct conf_drv_settings default_conf = { } }; +static void __wl1271_op_remove_interface(struct wl1271 *wl); + + static void wl1271_device_release(struct device *dev) { @@ -612,6 +615,26 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) return ret; } +static void wl1271_recovery_work(struct work_struct *work) +{ + struct wl1271 *wl = + container_of(work, struct wl1271, recovery_work); + + mutex_lock(&wl->mutex); + + if (wl->state != WL1271_STATE_ON) + goto out; + + wl1271_info("Hardware recovery in progress."); + + /* reboot the chipset */ + __wl1271_op_remove_interface(wl); + ieee80211_restart_hw(wl->hw); + +out: + mutex_unlock(&wl->mutex); +} + static void wl1271_fw_wakeup(struct wl1271 *wl) { u32 elp_reg; @@ -635,6 +658,7 @@ static int wl1271_setup(struct wl1271 *wl) INIT_WORK(&wl->irq_work, wl1271_irq_work); INIT_WORK(&wl->tx_work, wl1271_tx_work); INIT_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); + INIT_WORK(&wl->recovery_work, wl1271_recovery_work); return 0; } @@ -793,11 +817,11 @@ int wl1271_plt_stop(struct wl1271 *wl) mutex_unlock(&wl->mutex); cancel_work_sync(&wl->irq_work); + cancel_work_sync(&wl->recovery_work); return ret; } - static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1271 *wl = hw->priv; @@ -1046,6 +1070,8 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, WARN_ON(wl->vif != vif); __wl1271_op_remove_interface(wl); mutex_unlock(&wl->mutex); + + cancel_work_sync(&wl->recovery_work); } static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters) diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c index 150dc674d8b4..e3c332e2f97c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_ps.c +++ b/drivers/net/wireless/wl12xx/wl1271_ps.c @@ -64,7 +64,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) test_bit(WL1271_FLAG_IDLE, &wl->flags)) { cancel_delayed_work(&wl->elp_work); ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, - msecs_to_jiffies(ELP_ENTRY_DELAY)); + msecs_to_jiffies(ELP_ENTRY_DELAY)); } } @@ -99,6 +99,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT)); if (ret == 0) { wl1271_error("ELP wakeup timeout!"); + ieee80211_queue_work(wl->hw, &wl->recovery_work); ret = -ETIMEDOUT; goto err; } else if (ret < 0) {