wl1271: Add hardware recovery mechanism

There is some probability of hardware failures, which currently go largely
undetected. Attempt to recover from these failures by shutting down the
hardware, and requesting mac80211 to reconfigure it.

Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: Teemu Paasikivi <ext-teemu.3.paasikivi@nokia.com>
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
This commit is contained in:
Juuso Oikarinen 2010-09-21 06:23:31 +02:00 committed by Luciano Coelho
parent 52a2a37550
commit 52b0e7a61f
4 changed files with 36 additions and 3 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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) {