diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 109 |
1 files changed, 80 insertions, 29 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 2bf5dda2929..149ee67beb2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -574,6 +574,8 @@ struct brcmf_sdio { struct task_struct *dpc_tsk; struct completion dpc_wait; + struct list_head dpc_tsklst; + spinlock_t dpc_tl_lock; struct semaphore sdsem; @@ -2350,6 +2352,24 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev) up(&bus->sdsem); } +#ifdef CONFIG_BRCMFMAC_SDIO_OOB +static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) +{ + unsigned long flags; + + spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags); + if (!bus->sdiodev->irq_en && !bus->ipend) { + enable_irq(bus->sdiodev->irq); + bus->sdiodev->irq_en = true; + } + spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags); +} +#else +static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) +{ +} +#endif /* CONFIG_BRCMFMAC_SDIO_OOB */ + static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) { u32 intstatus, newstatus = 0; @@ -2507,6 +2527,8 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) bus->intstatus = intstatus; clkwait: + brcmf_sdbrcm_clrintr(bus); + if (data_ok(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { int ret, i; @@ -2594,29 +2616,59 @@ clkwait: return resched; } +static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus) +{ + struct list_head *new_hd; + unsigned long flags; + + if (in_interrupt()) + new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC); + else + new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (new_hd == NULL) + return; + + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + list_add_tail(new_hd, &bus->dpc_tsklst); + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); +} + static int brcmf_sdbrcm_dpc_thread(void *data) { struct brcmf_sdio *bus = (struct brcmf_sdio *) data; + struct list_head *cur_hd, *tmp_hd; + unsigned long flags; allow_signal(SIGTERM); /* Run until signal received */ while (1) { if (kthread_should_stop()) break; - if (!wait_for_completion_interruptible(&bus->dpc_wait)) { - /* Call bus dpc unless it indicated down - (then clean stop) */ - if (bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN) { - if (brcmf_sdbrcm_dpc(bus)) - complete(&bus->dpc_wait); - } else { + + if (list_empty(&bus->dpc_tsklst)) + if (wait_for_completion_interruptible(&bus->dpc_wait)) + break; + + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) { + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); + + if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { /* after stopping the bus, exit thread */ brcmf_sdbrcm_bus_stop(bus->sdiodev->dev); bus->dpc_tsk = NULL; + spin_lock_irqsave(&bus->dpc_tl_lock, flags); break; } - } else - break; + + if (brcmf_sdbrcm_dpc(bus)) + brcmf_sdbrcm_adddpctsk(bus); + + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + list_del(cur_hd); + kfree(cur_hd); + } + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } return 0; } @@ -2669,8 +2721,10 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) /* Schedule DPC if needed to send queued packet(s) */ if (!bus->dpc_sched) { bus->dpc_sched = true; - if (bus->dpc_tsk) + if (bus->dpc_tsk) { + brcmf_sdbrcm_adddpctsk(bus); complete(&bus->dpc_wait); + } } return ret; @@ -3474,8 +3528,14 @@ static int brcmf_sdbrcm_bus_init(struct device *dev) brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err); + if (ret == 0) { + ret = brcmf_sdio_intr_register(bus->sdiodev); + if (ret != 0) + brcmf_dbg(ERROR, "intr register failed:%d\n", ret); + } + /* If we didn't come up, turn off backplane clock */ - if (!ret) + if (bus_if->state != BRCMF_BUS_DATA) brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); exit: @@ -3514,8 +3574,10 @@ void brcmf_sdbrcm_isr(void *arg) brcmf_dbg(ERROR, "isr w/o interrupt configured!\n"); bus->dpc_sched = true; - if (bus->dpc_tsk) + if (bus->dpc_tsk) { + brcmf_sdbrcm_adddpctsk(bus); complete(&bus->dpc_wait); + } } static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) @@ -3559,8 +3621,10 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) bus->ipend = true; bus->dpc_sched = true; - if (bus->dpc_tsk) + if (bus->dpc_tsk) { + brcmf_sdbrcm_adddpctsk(bus); complete(&bus->dpc_wait); + } } } @@ -3829,7 +3893,7 @@ static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) if (bus) { /* De-register interrupt handler */ - brcmf_sdcard_intr_dereg(bus->sdiodev); + brcmf_sdio_intr_unregister(bus->sdiodev); if (bus->sdiodev->bus_if->drvr) { brcmf_detach(bus->sdiodev->dev); @@ -3897,6 +3961,8 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) } /* Initialize DPC thread */ init_completion(&bus->dpc_wait); + INIT_LIST_HEAD(&bus->dpc_tsklst); + spin_lock_init(&bus->dpc_tl_lock); bus->dpc_tsk = kthread_run(brcmf_sdbrcm_dpc_thread, bus, "brcmf_dpc"); if (IS_ERR(bus->dpc_tsk)) { @@ -3928,15 +3994,6 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) goto fail; } - /* Register interrupt callback, but mask it (not operational yet). */ - brcmf_dbg(INTR, "disable SDIO interrupts (not interested yet)\n"); - ret = brcmf_sdcard_intr_reg(bus->sdiodev); - if (ret != 0) { - brcmf_dbg(ERROR, "FAILED: sdcard_intr_reg returned %d\n", ret); - goto fail; - } - brcmf_dbg(INTR, "registered SDIO interrupt function ok\n"); - brcmf_dbg(INFO, "completed!!\n"); /* if firmware path present try to download and bring up bus */ @@ -3948,12 +4005,6 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) } } - /* add interface and open for business */ - if (brcmf_add_if(bus->sdiodev->dev, 0, "wlan%d", NULL)) { - brcmf_dbg(ERROR, "Add primary net device interface failed!!\n"); - goto fail; - } - return bus; fail: |