diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac/sdio.c')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 90 |
1 files changed, 50 insertions, 40 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index f3c49c4201e9..01b34a37dde0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -515,6 +515,7 @@ struct brcmf_sdio { bool txoff; /* Transmit flow-controlled */ struct brcmf_sdio_count sdcnt; bool sr_enabled; /* SaveRestore enabled */ + bool sleeping; u8 tx_hdrlen; /* sdio bus header length for tx packet */ bool txglom; /* host tx glomming enable flag */ @@ -1013,12 +1014,12 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) brcmf_dbg(SDIO, "Enter: request %s currently %s\n", (sleep ? "SLEEP" : "WAKE"), - (bus->sdiodev->sleeping ? "SLEEP" : "WAKE")); + (bus->sleeping ? "SLEEP" : "WAKE")); /* If SR is enabled control bus state with KSO */ if (bus->sr_enabled) { /* Done if we're already in the requested state */ - if (sleep == bus->sdiodev->sleeping) + if (sleep == bus->sleeping) goto end; /* Going to sleep */ @@ -1065,9 +1066,7 @@ end: } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); } - bus->sdiodev->sleeping = sleep; - if (sleep) - wake_up(&bus->sdiodev->idle_wait); + bus->sleeping = sleep; brcmf_dbg(SDIO, "new state %s\n", (sleep ? "SLEEP" : "WAKE")); done: @@ -2603,21 +2602,6 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) 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) { u32 newstatus = 0; @@ -2628,9 +2612,6 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); - if (brcmf_sdio_pm_resume_wait(bus->sdiodev)) - return; - sdio_claim_host(bus->sdiodev->func[1]); /* If waiting for HTAVAIL, check status */ @@ -2862,11 +2843,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) qcount[prec] = pktq_plen(&bus->txq, prec); #endif - if (atomic_read(&bus->dpc_tskcnt) == 0) { - atomic_inc(&bus->dpc_tskcnt); - queue_work(bus->brcmf_wq, &bus->datawork); - } - + brcmf_sdio_trigger_dpc(bus); return ret; } @@ -2964,11 +2941,8 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) bus->ctrl_frame_buf = msg; bus->ctrl_frame_len = msglen; bus->ctrl_frame_stat = true; - if (atomic_read(&bus->dpc_tskcnt) == 0) { - atomic_inc(&bus->dpc_tskcnt); - queue_work(bus->brcmf_wq, &bus->datawork); - } + brcmf_sdio_trigger_dpc(bus); wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, msecs_to_jiffies(CTL_DONE_TIMEOUT)); @@ -3548,6 +3522,14 @@ done: return err; } +void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus) +{ + if (atomic_read(&bus->dpc_tskcnt) == 0) { + atomic_inc(&bus->dpc_tskcnt); + queue_work(bus->brcmf_wq, &bus->datawork); + } +} + void brcmf_sdio_isr(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); @@ -3602,9 +3584,8 @@ static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) SDIO_CCCR_INTx, NULL); sdio_release_host(bus->sdiodev->func[1]); - intstatus = - devpend & (INTR_STATUS_FUNC1 | - INTR_STATUS_FUNC2); + intstatus = devpend & (INTR_STATUS_FUNC1 | + INTR_STATUS_FUNC2); } /* If there is something, make like the ISR and @@ -3667,6 +3648,11 @@ static void brcmf_sdio_dataworker(struct work_struct *work) atomic_set(&bus->dpc_tskcnt, 0); brcmf_sdio_dpc(bus); } + if (brcmf_sdiod_freezing(bus->sdiodev)) { + brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN); + brcmf_sdiod_try_freeze(bus->sdiodev); + brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA); + } } static void @@ -3944,13 +3930,19 @@ static int brcmf_sdio_watchdog_thread(void *data) { struct brcmf_sdio *bus = (struct brcmf_sdio *)data; + int wait; allow_signal(SIGTERM); /* Run until signal received */ + brcmf_sdiod_freezer_count(bus->sdiodev); while (1) { if (kthread_should_stop()) break; - if (!wait_for_completion_interruptible(&bus->watchdog_wait)) { + brcmf_sdiod_freezer_uncount(bus->sdiodev); + wait = wait_for_completion_interruptible(&bus->watchdog_wait); + brcmf_sdiod_freezer_count(bus->sdiodev); + brcmf_sdiod_try_freeze(bus->sdiodev); + if (!wait) { brcmf_sdio_bus_watchdog(bus); /* Count the tick for reference */ bus->sdcnt.tickcnt++; @@ -4089,6 +4081,7 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) { int ret; struct brcmf_sdio *bus; + struct workqueue_struct *wq; brcmf_dbg(TRACE, "Enter\n"); @@ -4117,12 +4110,16 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->sgentry_align = sdiodev->pdata->sd_sgentry_align; } - INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); - bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); - if (bus->brcmf_wq == NULL) { + /* single-threaded workqueue */ + wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM, + dev_name(&sdiodev->func[1]->dev)); + if (!wq) { brcmf_err("insufficient memory to create txworkqueue\n"); goto fail; } + brcmf_sdiod_freezer_count(sdiodev); + INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); + bus->brcmf_wq = wq; /* attempt to attach to the dongle */ if (!(brcmf_sdio_probe_attach(bus))) { @@ -4143,7 +4140,8 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) /* Initialize watchdog thread */ init_completion(&bus->watchdog_wait); bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread, - bus, "brcmf_watchdog"); + bus, "brcmf_wdog/%s", + dev_name(&sdiodev->func[1]->dev)); if (IS_ERR(bus->watchdog_tsk)) { pr_warn("brcmf_watchdog thread failed to start\n"); bus->watchdog_tsk = NULL; @@ -4303,3 +4301,15 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) bus->save_ms = wdtick; } } + +int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep) +{ + int ret; + + sdio_claim_host(bus->sdiodev->func[1]); + ret = brcmf_sdio_bus_sleep(bus, sleep, false); + sdio_release_host(bus->sdiodev->func[1]); + + return ret; +} + |