Commit 69d03ee0 authored by Hante Meuleman's avatar Hante Meuleman Committed by Kalle Valo

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: default avatarArend Van Spriel <arend@broadcom.com>
Reviewed-by: default avatarFranky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: default avatarDaniel (Deognyoun) Kim <dekim@broadcom.com>
Signed-off-by: default avatarHante Meuleman <meuleman@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 5ef1e604
...@@ -97,25 +97,6 @@ static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func) ...@@ -97,25 +97,6 @@ static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func)
{ {
} }
static bool brcmf_sdiod_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
{
bool is_err = false;
#ifdef CONFIG_PM_SLEEP
is_err = atomic_read(&sdiodev->suspend);
#endif
return is_err;
}
static void brcmf_sdiod_pm_resume_wait(struct brcmf_sdio_dev *sdiodev,
wait_queue_head_t *wq)
{
#ifdef CONFIG_PM_SLEEP
int retry = 0;
while (atomic_read(&sdiodev->suspend) && retry++ != 30)
wait_event_timeout(*wq, false, HZ/100);
#endif
}
int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
{ {
int ret = 0; int ret = 0;
...@@ -244,10 +225,6 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, ...@@ -244,10 +225,6 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn,
brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
write, fn, addr, regsz); write, fn, addr, regsz);
brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_word_wait);
if (brcmf_sdiod_pm_resume_error(sdiodev))
return -EIO;
/* only allow byte access on F0 */ /* only allow byte access on F0 */
if (WARN_ON(regsz > 1 && !fn)) if (WARN_ON(regsz > 1 && !fn))
return -EINVAL; return -EINVAL;
...@@ -462,10 +439,6 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, ...@@ -462,10 +439,6 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
unsigned int req_sz; unsigned int req_sz;
int err; int err;
brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
if (brcmf_sdiod_pm_resume_error(sdiodev))
return -EIO;
/* Single skb use the standard mmc interface */ /* Single skb use the standard mmc interface */
req_sz = pkt->len + 3; req_sz = pkt->len + 3;
req_sz &= (uint)~3; req_sz &= (uint)~3;
...@@ -516,10 +489,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, ...@@ -516,10 +489,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
if (!pktlist->qlen) if (!pktlist->qlen)
return -EINVAL; return -EINVAL;
brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
if (brcmf_sdiod_pm_resume_error(sdiodev))
return -EIO;
target_list = pktlist; target_list = pktlist;
/* for host with broken sg support, prepare a page aligned list */ /* for host with broken sg support, prepare a page aligned list */
__skb_queue_head_init(&local_list); __skb_queue_head_init(&local_list);
...@@ -1077,8 +1046,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, ...@@ -1077,8 +1046,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
#endif #endif
atomic_set(&sdiodev->suspend, false); atomic_set(&sdiodev->suspend, false);
init_waitqueue_head(&sdiodev->request_word_wait);
init_waitqueue_head(&sdiodev->request_buffer_wait);
brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
err = brcmf_sdiod_probe(sdiodev); err = brcmf_sdiod_probe(sdiodev);
......
...@@ -2609,6 +2609,21 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) ...@@ -2609,6 +2609,21 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
return ret; return ret;
} }
static int brcmf_sdio_pm_resume_wait(struct brcmf_sdio_dev *sdiodev)
{
#ifdef CONFIG_PM_SLEEP
int retry;
/* Wait for possible resume to complete */
retry = 0;
while ((atomic_read(&sdiodev->suspend)) && (retry++ != 50))
msleep(20);
if (atomic_read(&sdiodev->suspend))
return -EIO;
#endif
return 0;
}
static void brcmf_sdio_dpc(struct brcmf_sdio *bus) static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
{ {
u32 newstatus = 0; u32 newstatus = 0;
...@@ -2619,6 +2634,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) ...@@ -2619,6 +2634,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
brcmf_dbg(TRACE, "Enter\n"); brcmf_dbg(TRACE, "Enter\n");
if (brcmf_sdio_pm_resume_wait(bus->sdiodev))
return;
sdio_claim_host(bus->sdiodev->func[1]); sdio_claim_host(bus->sdiodev->func[1]);
/* If waiting for HTAVAIL, check status */ /* If waiting for HTAVAIL, check status */
......
...@@ -169,8 +169,6 @@ struct brcmf_sdio_dev { ...@@ -169,8 +169,6 @@ struct brcmf_sdio_dev {
u32 sbwad; /* Save backplane window address */ u32 sbwad; /* Save backplane window address */
struct brcmf_sdio *bus; struct brcmf_sdio *bus;
atomic_t suspend; /* suspend flag */ atomic_t suspend; /* suspend flag */
wait_queue_head_t request_word_wait;
wait_queue_head_t request_buffer_wait;
struct device *dev; struct device *dev;
struct brcmf_bus *bus_if; struct brcmf_bus *bus_if;
struct brcmfmac_sdio_platform_data *pdata; struct brcmfmac_sdio_platform_data *pdata;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment