diff options
author | Hante Meuleman <meuleman@broadcom.com> | 2015-01-25 14:31:31 -0500 |
---|---|---|
committer | Kalle Valo <kvalo@codeaurora.org> | 2015-01-29 02:58:54 -0500 |
commit | 69d03ee0b709a282f81e9f81c1359cffe1edcd2b (patch) | |
tree | 454532da01623c1e81b2f6fbacf2df8dc164cb02 /drivers/net/wireless/brcm80211 | |
parent | 5ef1e604194ee629c5d9fd6b9a4d3c424cfd2a84 (diff) |
brcmfmac: prevent possible deadlock on resuming SDIO device.
When the system is resumed a deadlock can occur when DPC gets
entered before resume is complete. This patch fixes this by
properly checking the suspend state outside the claim_host code
block.
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Diffstat (limited to 'drivers/net/wireless/brcm80211')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 33 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 18 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/sdio.h | 2 |
3 files changed, 18 insertions, 35 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 00ba90b89455..8ba60f6f4915 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | |||
@@ -97,25 +97,6 @@ static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func) | |||
97 | { | 97 | { |
98 | } | 98 | } |
99 | 99 | ||
100 | static bool brcmf_sdiod_pm_resume_error(struct brcmf_sdio_dev *sdiodev) | ||
101 | { | ||
102 | bool is_err = false; | ||
103 | #ifdef CONFIG_PM_SLEEP | ||
104 | is_err = atomic_read(&sdiodev->suspend); | ||
105 | #endif | ||
106 | return is_err; | ||
107 | } | ||
108 | |||
109 | static void brcmf_sdiod_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, | ||
110 | wait_queue_head_t *wq) | ||
111 | { | ||
112 | #ifdef CONFIG_PM_SLEEP | ||
113 | int retry = 0; | ||
114 | while (atomic_read(&sdiodev->suspend) && retry++ != 30) | ||
115 | wait_event_timeout(*wq, false, HZ/100); | ||
116 | #endif | ||
117 | } | ||
118 | |||
119 | int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) | 100 | int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) |
120 | { | 101 | { |
121 | int ret = 0; | 102 | int ret = 0; |
@@ -244,10 +225,6 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, | |||
244 | brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", | 225 | brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", |
245 | write, fn, addr, regsz); | 226 | write, fn, addr, regsz); |
246 | 227 | ||
247 | brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); | ||
248 | if (brcmf_sdiod_pm_resume_error(sdiodev)) | ||
249 | return -EIO; | ||
250 | |||
251 | /* only allow byte access on F0 */ | 228 | /* only allow byte access on F0 */ |
252 | if (WARN_ON(regsz > 1 && !fn)) | 229 | if (WARN_ON(regsz > 1 && !fn)) |
253 | return -EINVAL; | 230 | return -EINVAL; |
@@ -462,10 +439,6 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, | |||
462 | unsigned int req_sz; | 439 | unsigned int req_sz; |
463 | int err; | 440 | int err; |
464 | 441 | ||
465 | brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); | ||
466 | if (brcmf_sdiod_pm_resume_error(sdiodev)) | ||
467 | return -EIO; | ||
468 | |||
469 | /* Single skb use the standard mmc interface */ | 442 | /* Single skb use the standard mmc interface */ |
470 | req_sz = pkt->len + 3; | 443 | req_sz = pkt->len + 3; |
471 | req_sz &= (uint)~3; | 444 | req_sz &= (uint)~3; |
@@ -516,10 +489,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, | |||
516 | if (!pktlist->qlen) | 489 | if (!pktlist->qlen) |
517 | return -EINVAL; | 490 | return -EINVAL; |
518 | 491 | ||
519 | brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); | ||
520 | if (brcmf_sdiod_pm_resume_error(sdiodev)) | ||
521 | return -EIO; | ||
522 | |||
523 | target_list = pktlist; | 492 | target_list = pktlist; |
524 | /* for host with broken sg support, prepare a page aligned list */ | 493 | /* for host with broken sg support, prepare a page aligned list */ |
525 | __skb_queue_head_init(&local_list); | 494 | __skb_queue_head_init(&local_list); |
@@ -1077,8 +1046,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, | |||
1077 | #endif | 1046 | #endif |
1078 | 1047 | ||
1079 | atomic_set(&sdiodev->suspend, false); | 1048 | atomic_set(&sdiodev->suspend, false); |
1080 | init_waitqueue_head(&sdiodev->request_word_wait); | ||
1081 | init_waitqueue_head(&sdiodev->request_buffer_wait); | ||
1082 | 1049 | ||
1083 | brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); | 1050 | brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); |
1084 | err = brcmf_sdiod_probe(sdiodev); | 1051 | err = brcmf_sdiod_probe(sdiodev); |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 99a37765888d..2c4f0ccd3b70 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | |||
@@ -2609,6 +2609,21 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) | |||
2609 | return ret; | 2609 | return ret; |
2610 | } | 2610 | } |
2611 | 2611 | ||
2612 | static int brcmf_sdio_pm_resume_wait(struct brcmf_sdio_dev *sdiodev) | ||
2613 | { | ||
2614 | #ifdef CONFIG_PM_SLEEP | ||
2615 | int retry; | ||
2616 | |||
2617 | /* Wait for possible resume to complete */ | ||
2618 | retry = 0; | ||
2619 | while ((atomic_read(&sdiodev->suspend)) && (retry++ != 50)) | ||
2620 | msleep(20); | ||
2621 | if (atomic_read(&sdiodev->suspend)) | ||
2622 | return -EIO; | ||
2623 | #endif | ||
2624 | return 0; | ||
2625 | } | ||
2626 | |||
2612 | static void brcmf_sdio_dpc(struct brcmf_sdio *bus) | 2627 | static void brcmf_sdio_dpc(struct brcmf_sdio *bus) |
2613 | { | 2628 | { |
2614 | u32 newstatus = 0; | 2629 | u32 newstatus = 0; |
@@ -2619,6 +2634,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) | |||
2619 | 2634 | ||
2620 | brcmf_dbg(TRACE, "Enter\n"); | 2635 | brcmf_dbg(TRACE, "Enter\n"); |
2621 | 2636 | ||
2637 | if (brcmf_sdio_pm_resume_wait(bus->sdiodev)) | ||
2638 | return; | ||
2639 | |||
2622 | sdio_claim_host(bus->sdiodev->func[1]); | 2640 | sdio_claim_host(bus->sdiodev->func[1]); |
2623 | 2641 | ||
2624 | /* If waiting for HTAVAIL, check status */ | 2642 | /* If waiting for HTAVAIL, check status */ |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h index 8eb42620129c..82494df541d4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h | |||
@@ -169,8 +169,6 @@ struct brcmf_sdio_dev { | |||
169 | u32 sbwad; /* Save backplane window address */ | 169 | u32 sbwad; /* Save backplane window address */ |
170 | struct brcmf_sdio *bus; | 170 | struct brcmf_sdio *bus; |
171 | atomic_t suspend; /* suspend flag */ | 171 | atomic_t suspend; /* suspend flag */ |
172 | wait_queue_head_t request_word_wait; | ||
173 | wait_queue_head_t request_buffer_wait; | ||
174 | struct device *dev; | 172 | struct device *dev; |
175 | struct brcmf_bus *bus_if; | 173 | struct brcmf_bus *bus_if; |
176 | struct brcmfmac_sdio_platform_data *pdata; | 174 | struct brcmfmac_sdio_platform_data *pdata; |