diff options
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/cfg80211.c | 17 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/core.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/core.h | 18 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/debug.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/hif.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/init.c | 19 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/recovery.c | 62 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/sdio.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/txrx.c | 3 |
10 files changed, 126 insertions, 3 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index 8cae8886f17d..cab0ec0d5380 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile | |||
@@ -34,6 +34,7 @@ ath6kl_core-y += main.o | |||
34 | ath6kl_core-y += txrx.o | 34 | ath6kl_core-y += txrx.o |
35 | ath6kl_core-y += wmi.o | 35 | ath6kl_core-y += wmi.o |
36 | ath6kl_core-y += core.o | 36 | ath6kl_core-y += core.o |
37 | ath6kl_core-y += recovery.o | ||
37 | ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o | 38 | ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o |
38 | 39 | ||
39 | obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o | 40 | obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o |
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index b58878648d22..8cf146b5c57f 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c | |||
@@ -2503,14 +2503,23 @@ static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy, | |||
2503 | { | 2503 | { |
2504 | struct ath6kl *ar = wiphy_priv(wiphy); | 2504 | struct ath6kl *ar = wiphy_priv(wiphy); |
2505 | 2505 | ||
2506 | ath6kl_recovery_suspend(ar); | ||
2507 | |||
2506 | return ath6kl_hif_suspend(ar, wow); | 2508 | return ath6kl_hif_suspend(ar, wow); |
2507 | } | 2509 | } |
2508 | 2510 | ||
2509 | static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) | 2511 | static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) |
2510 | { | 2512 | { |
2511 | struct ath6kl *ar = wiphy_priv(wiphy); | 2513 | struct ath6kl *ar = wiphy_priv(wiphy); |
2514 | int err; | ||
2515 | |||
2516 | err = ath6kl_hif_resume(ar); | ||
2517 | if (err) | ||
2518 | return err; | ||
2512 | 2519 | ||
2513 | return ath6kl_hif_resume(ar); | 2520 | ar->fw_recovery.enable = true; |
2521 | |||
2522 | return 0; | ||
2514 | } | 2523 | } |
2515 | 2524 | ||
2516 | /* | 2525 | /* |
@@ -3434,6 +3443,10 @@ void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) | |||
3434 | clear_bit(CONNECTED, &vif->flags); | 3443 | clear_bit(CONNECTED, &vif->flags); |
3435 | clear_bit(CONNECT_PEND, &vif->flags); | 3444 | clear_bit(CONNECT_PEND, &vif->flags); |
3436 | 3445 | ||
3446 | /* Stop netdev queues, needed during recovery */ | ||
3447 | netif_stop_queue(vif->ndev); | ||
3448 | netif_carrier_off(vif->ndev); | ||
3449 | |||
3437 | /* disable scanning */ | 3450 | /* disable scanning */ |
3438 | if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF, | 3451 | if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF, |
3439 | 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0) | 3452 | 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0) |
@@ -3447,7 +3460,7 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar) | |||
3447 | struct ath6kl_vif *vif; | 3460 | struct ath6kl_vif *vif; |
3448 | 3461 | ||
3449 | vif = ath6kl_vif_first(ar); | 3462 | vif = ath6kl_vif_first(ar); |
3450 | if (!vif) { | 3463 | if (!vif && ar->state != ATH6KL_STATE_RECOVERY) { |
3451 | /* save the current power mode before enabling power save */ | 3464 | /* save the current power mode before enabling power save */ |
3452 | ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; | 3465 | ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; |
3453 | 3466 | ||
diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 82c4dd2a960e..adcaa965486b 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c | |||
@@ -202,6 +202,8 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) | |||
202 | ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", | 202 | ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", |
203 | __func__, wdev->netdev->name, wdev->netdev, ar); | 203 | __func__, wdev->netdev->name, wdev->netdev, ar); |
204 | 204 | ||
205 | ath6kl_recovery_init(ar); | ||
206 | |||
205 | return ret; | 207 | return ret; |
206 | 208 | ||
207 | err_rxbuf_cleanup: | 209 | err_rxbuf_cleanup: |
@@ -291,6 +293,8 @@ void ath6kl_core_cleanup(struct ath6kl *ar) | |||
291 | { | 293 | { |
292 | ath6kl_hif_power_off(ar); | 294 | ath6kl_hif_power_off(ar); |
293 | 295 | ||
296 | ath6kl_recovery_cleanup(ar); | ||
297 | |||
294 | destroy_workqueue(ar->ath6kl_wq); | 298 | destroy_workqueue(ar->ath6kl_wq); |
295 | 299 | ||
296 | if (ar->htc_target) | 300 | if (ar->htc_target) |
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 8b31b9a0f38f..c7dcdadd8b83 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h | |||
@@ -645,6 +645,12 @@ enum ath6kl_state { | |||
645 | ATH6KL_STATE_DEEPSLEEP, | 645 | ATH6KL_STATE_DEEPSLEEP, |
646 | ATH6KL_STATE_CUTPOWER, | 646 | ATH6KL_STATE_CUTPOWER, |
647 | ATH6KL_STATE_WOW, | 647 | ATH6KL_STATE_WOW, |
648 | ATH6KL_STATE_RECOVERY, | ||
649 | }; | ||
650 | |||
651 | /* Fw error recovery */ | ||
652 | enum ath6kl_fw_err { | ||
653 | ATH6KL_FW_ASSERT, | ||
648 | }; | 654 | }; |
649 | 655 | ||
650 | struct ath6kl { | 656 | struct ath6kl { |
@@ -790,6 +796,12 @@ struct ath6kl { | |||
790 | 796 | ||
791 | bool wiphy_registered; | 797 | bool wiphy_registered; |
792 | 798 | ||
799 | struct ath6kl_fw_recovery { | ||
800 | bool enable; | ||
801 | struct work_struct recovery_work; | ||
802 | unsigned long err_reason; | ||
803 | } fw_recovery; | ||
804 | |||
793 | #ifdef CONFIG_ATH6KL_DEBUG | 805 | #ifdef CONFIG_ATH6KL_DEBUG |
794 | struct { | 806 | struct { |
795 | struct sk_buff_head fwlog_queue; | 807 | struct sk_buff_head fwlog_queue; |
@@ -925,4 +937,10 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type); | |||
925 | void ath6kl_core_cleanup(struct ath6kl *ar); | 937 | void ath6kl_core_cleanup(struct ath6kl *ar); |
926 | void ath6kl_core_destroy(struct ath6kl *ar); | 938 | void ath6kl_core_destroy(struct ath6kl *ar); |
927 | 939 | ||
940 | /* Fw error recovery */ | ||
941 | void ath6kl_init_hw_restart(struct ath6kl *ar); | ||
942 | void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason); | ||
943 | void ath6kl_recovery_init(struct ath6kl *ar); | ||
944 | void ath6kl_recovery_cleanup(struct ath6kl *ar); | ||
945 | void ath6kl_recovery_suspend(struct ath6kl *ar); | ||
928 | #endif /* CORE_H */ | 946 | #endif /* CORE_H */ |
diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 49639d8266c2..f97cd4ead543 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h | |||
@@ -44,6 +44,7 @@ enum ATH6K_DEBUG_MASK { | |||
44 | ATH6KL_DBG_SUSPEND = BIT(20), | 44 | ATH6KL_DBG_SUSPEND = BIT(20), |
45 | ATH6KL_DBG_USB = BIT(21), | 45 | ATH6KL_DBG_USB = BIT(21), |
46 | ATH6KL_DBG_USB_BULK = BIT(22), | 46 | ATH6KL_DBG_USB_BULK = BIT(22), |
47 | ATH6KL_DBG_RECOVERY = BIT(23), | ||
47 | ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ | 48 | ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ |
48 | }; | 49 | }; |
49 | 50 | ||
diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c index 68ed6c2665b7..029914a22ea0 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.c +++ b/drivers/net/wireless/ath/ath6kl/hif.c | |||
@@ -136,6 +136,7 @@ static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) | |||
136 | 136 | ||
137 | ath6kl_hif_dump_fw_crash(dev->ar); | 137 | ath6kl_hif_dump_fw_crash(dev->ar); |
138 | ath6kl_read_fwlogs(dev->ar); | 138 | ath6kl_read_fwlogs(dev->ar); |
139 | ath6kl_recovery_err_notify(dev->ar, ATH6KL_FW_ASSERT); | ||
139 | 140 | ||
140 | return ret; | 141 | return ret; |
141 | } | 142 | } |
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index be27ebec9052..301443c9f9ee 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c | |||
@@ -1695,6 +1695,25 @@ int ath6kl_init_hw_stop(struct ath6kl *ar) | |||
1695 | return 0; | 1695 | return 0; |
1696 | } | 1696 | } |
1697 | 1697 | ||
1698 | void ath6kl_init_hw_restart(struct ath6kl *ar) | ||
1699 | { | ||
1700 | |||
1701 | ar->state = ATH6KL_STATE_RECOVERY; | ||
1702 | |||
1703 | ath6kl_cfg80211_stop_all(ar); | ||
1704 | |||
1705 | if (__ath6kl_init_hw_stop(ar)) | ||
1706 | return; | ||
1707 | |||
1708 | if (__ath6kl_init_hw_start(ar)) { | ||
1709 | ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n"); | ||
1710 | return; | ||
1711 | } | ||
1712 | |||
1713 | ar->state = ATH6KL_STATE_ON; | ||
1714 | ar->fw_recovery.err_reason = 0; | ||
1715 | } | ||
1716 | |||
1698 | /* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */ | 1717 | /* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */ |
1699 | void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) | 1718 | void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) |
1700 | { | 1719 | { |
diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c new file mode 100644 index 000000000000..c225fc4d3d56 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/recovery.c | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include "core.h" | ||
18 | #include "cfg80211.h" | ||
19 | #include "debug.h" | ||
20 | |||
21 | static void ath6kl_recovery_work(struct work_struct *work) | ||
22 | { | ||
23 | struct ath6kl *ar = container_of(work, struct ath6kl, | ||
24 | fw_recovery.recovery_work); | ||
25 | |||
26 | ath6kl_init_hw_restart(ar); | ||
27 | } | ||
28 | |||
29 | void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) | ||
30 | { | ||
31 | ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Fw error detected, reason:%d\n", | ||
32 | reason); | ||
33 | |||
34 | set_bit(reason, &ar->fw_recovery.err_reason); | ||
35 | |||
36 | if (ar->fw_recovery.enable && ar->state != ATH6KL_STATE_RECOVERY) | ||
37 | queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work); | ||
38 | } | ||
39 | |||
40 | void ath6kl_recovery_init(struct ath6kl *ar) | ||
41 | { | ||
42 | struct ath6kl_fw_recovery *recovery = &ar->fw_recovery; | ||
43 | |||
44 | recovery->enable = true; | ||
45 | INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work); | ||
46 | } | ||
47 | |||
48 | void ath6kl_recovery_cleanup(struct ath6kl *ar) | ||
49 | { | ||
50 | ar->fw_recovery.enable = false; | ||
51 | |||
52 | cancel_work_sync(&ar->fw_recovery.recovery_work); | ||
53 | } | ||
54 | |||
55 | void ath6kl_recovery_suspend(struct ath6kl *ar) | ||
56 | { | ||
57 | ath6kl_recovery_cleanup(ar); | ||
58 | |||
59 | /* Process pending fw error detection */ | ||
60 | if (ar->fw_recovery.err_reason) | ||
61 | ath6kl_init_hw_restart(ar); | ||
62 | } | ||
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index cc17fe02bdad..a72a4d02a4c8 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c | |||
@@ -931,6 +931,9 @@ static int ath6kl_sdio_resume(struct ath6kl *ar) | |||
931 | 931 | ||
932 | case ATH6KL_STATE_RESUMING: | 932 | case ATH6KL_STATE_RESUMING: |
933 | break; | 933 | break; |
934 | |||
935 | case ATH6KL_STATE_RECOVERY: | ||
936 | break; | ||
934 | } | 937 | } |
935 | 938 | ||
936 | ath6kl_cfg80211_resume(ar); | 939 | ath6kl_cfg80211_resume(ar); |
diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 740a488ef504..cbe1a9d89112 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c | |||
@@ -288,7 +288,8 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, | |||
288 | int status = 0; | 288 | int status = 0; |
289 | struct ath6kl_cookie *cookie = NULL; | 289 | struct ath6kl_cookie *cookie = NULL; |
290 | 290 | ||
291 | if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) { | 291 | if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW) || |
292 | ar->state == ATH6KL_STATE_RECOVERY) { | ||
292 | dev_kfree_skb(skb); | 293 | dev_kfree_skb(skb); |
293 | return -EACCES; | 294 | return -EACCES; |
294 | } | 295 | } |