diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 269 |
1 files changed, 157 insertions, 112 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 11fd1c73558..4891e3df205 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -25,6 +25,7 @@ #include <linux/mmc/sdio.h> #include <linux/mmc/sdio_func.h> #include <linux/mmc/card.h> +#include <linux/platform_data/brcmfmac-sdio.h> #include <defs.h> #include <brcm_hw_ids.h> @@ -37,16 +38,15 @@ #define SDIOH_API_ACCESS_RETRY_LIMIT 2 -#ifdef CONFIG_BRCMFMAC_SDIO_OOB -static irqreturn_t brcmf_sdio_irqhandler(int irq, void *dev_id) + +static irqreturn_t brcmf_sdio_oob_irqhandler(int irq, void *dev_id) { struct brcmf_bus *bus_if = dev_get_drvdata(dev_id); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - brcmf_dbg(INTR, "oob intr triggered\n"); + brcmf_dbg(INTR, "OOB intr triggered\n"); - /* - * out-of-band interrupt is level-triggered which won't + /* out-of-band interrupt is level-triggered which won't * be cleared until dpc */ if (sdiodev->irq_en) { @@ -59,72 +59,12 @@ static irqreturn_t brcmf_sdio_irqhandler(int irq, void *dev_id) return IRQ_HANDLED; } -int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) -{ - int ret = 0; - u8 data; - unsigned long flags; - - brcmf_dbg(TRACE, "Entering: irq %d\n", sdiodev->irq); - - ret = request_irq(sdiodev->irq, brcmf_sdio_irqhandler, - sdiodev->irq_flags, "brcmf_oob_intr", - &sdiodev->func[1]->dev); - if (ret != 0) - return ret; - spin_lock_init(&sdiodev->irq_en_lock); - spin_lock_irqsave(&sdiodev->irq_en_lock, flags); - sdiodev->irq_en = true; - spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags); - - ret = enable_irq_wake(sdiodev->irq); - if (ret != 0) - return ret; - sdiodev->irq_wake = true; - - sdio_claim_host(sdiodev->func[1]); - - /* must configure SDIO_CCCR_IENx to enable irq */ - data = brcmf_sdio_regrb(sdiodev, SDIO_CCCR_IENx, &ret); - data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret); - - /* redirect, configure and enable io for interrupt signal */ - data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; - if (sdiodev->irq_flags & IRQF_TRIGGER_HIGH) - data |= SDIO_SEPINT_ACT_HI; - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); - - sdio_release_host(sdiodev->func[1]); - - return 0; -} - -int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) -{ - brcmf_dbg(TRACE, "Entering\n"); - - sdio_claim_host(sdiodev->func[1]); - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); - sdio_release_host(sdiodev->func[1]); - - if (sdiodev->irq_wake) { - disable_irq_wake(sdiodev->irq); - sdiodev->irq_wake = false; - } - free_irq(sdiodev->irq, &sdiodev->func[1]->dev); - sdiodev->irq_en = false; - - return 0; -} -#else /* CONFIG_BRCMFMAC_SDIO_OOB */ -static void brcmf_sdio_irqhandler(struct sdio_func *func) +static void brcmf_sdio_ib_irqhandler(struct sdio_func *func) { struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - brcmf_dbg(INTR, "ib intr triggered\n"); + brcmf_dbg(INTR, "IB intr triggered\n"); brcmf_sdbrcm_isr(sdiodev->bus); } @@ -136,28 +76,89 @@ static void brcmf_sdio_dummy_irqhandler(struct sdio_func *func) int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) { - brcmf_dbg(TRACE, "Entering\n"); + int ret = 0; + u8 data; + unsigned long flags; - sdio_claim_host(sdiodev->func[1]); - sdio_claim_irq(sdiodev->func[1], brcmf_sdio_irqhandler); - sdio_claim_irq(sdiodev->func[2], brcmf_sdio_dummy_irqhandler); - sdio_release_host(sdiodev->func[1]); + if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { + brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n", + sdiodev->pdata->oob_irq_nr); + ret = request_irq(sdiodev->pdata->oob_irq_nr, + brcmf_sdio_oob_irqhandler, + sdiodev->pdata->oob_irq_flags, + "brcmf_oob_intr", + &sdiodev->func[1]->dev); + if (ret != 0) { + brcmf_err("request_irq failed %d\n", ret); + return ret; + } + sdiodev->oob_irq_requested = true; + spin_lock_init(&sdiodev->irq_en_lock); + spin_lock_irqsave(&sdiodev->irq_en_lock, flags); + sdiodev->irq_en = true; + spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags); + + ret = enable_irq_wake(sdiodev->pdata->oob_irq_nr); + if (ret != 0) { + brcmf_err("enable_irq_wake failed %d\n", ret); + return ret; + } + sdiodev->irq_wake = true; + + sdio_claim_host(sdiodev->func[1]); + + /* must configure SDIO_CCCR_IENx to enable irq */ + data = brcmf_sdio_regrb(sdiodev, SDIO_CCCR_IENx, &ret); + data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; + brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret); + + /* redirect, configure and enable io for interrupt signal */ + data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; + if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH) + data |= SDIO_SEPINT_ACT_HI; + brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); + + sdio_release_host(sdiodev->func[1]); + } else { + brcmf_dbg(SDIO, "Entering\n"); + sdio_claim_host(sdiodev->func[1]); + sdio_claim_irq(sdiodev->func[1], brcmf_sdio_ib_irqhandler); + sdio_claim_irq(sdiodev->func[2], brcmf_sdio_dummy_irqhandler); + sdio_release_host(sdiodev->func[1]); + } return 0; } int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) { - brcmf_dbg(TRACE, "Entering\n"); - - sdio_claim_host(sdiodev->func[1]); - sdio_release_irq(sdiodev->func[2]); - sdio_release_irq(sdiodev->func[1]); - sdio_release_host(sdiodev->func[1]); + brcmf_dbg(SDIO, "Entering\n"); + + if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { + sdio_claim_host(sdiodev->func[1]); + brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); + brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); + sdio_release_host(sdiodev->func[1]); + + if (sdiodev->oob_irq_requested) { + sdiodev->oob_irq_requested = false; + if (sdiodev->irq_wake) { + disable_irq_wake(sdiodev->pdata->oob_irq_nr); + sdiodev->irq_wake = false; + } + free_irq(sdiodev->pdata->oob_irq_nr, + &sdiodev->func[1]->dev); + sdiodev->irq_en = false; + } + } else { + sdio_claim_host(sdiodev->func[1]); + sdio_release_irq(sdiodev->func[2]); + sdio_release_irq(sdiodev->func[1]); + sdio_release_host(sdiodev->func[1]); + } return 0; } -#endif /* CONFIG_BRCMFMAC_SDIO_OOB */ int brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) @@ -253,9 +254,9 @@ u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) u8 data; int retval; - brcmf_dbg(INFO, "addr:0x%08x\n", addr); + brcmf_dbg(SDIO, "addr:0x%08x\n", addr); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); - brcmf_dbg(INFO, "data:0x%02x\n", data); + brcmf_dbg(SDIO, "data:0x%02x\n", data); if (ret) *ret = retval; @@ -268,9 +269,9 @@ u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) u32 data; int retval; - brcmf_dbg(INFO, "addr:0x%08x\n", addr); + brcmf_dbg(SDIO, "addr:0x%08x\n", addr); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); - brcmf_dbg(INFO, "data:0x%08x\n", data); + brcmf_dbg(SDIO, "data:0x%08x\n", data); if (ret) *ret = retval; @@ -283,7 +284,7 @@ void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, { int retval; - brcmf_dbg(INFO, "addr:0x%08x, data:0x%02x\n", addr, data); + brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); if (ret) @@ -295,7 +296,7 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, { int retval; - brcmf_dbg(INFO, "addr:0x%08x, data:0x%08x\n", addr, data); + brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); if (ret) @@ -358,7 +359,7 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint width; int err = 0; - brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", + brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; @@ -381,7 +382,7 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint width; int err = 0; - brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", + brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pktq->qlen); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; @@ -428,7 +429,7 @@ brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; int err = 0; - brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", + brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); /* Async not implemented yet */ @@ -457,48 +458,92 @@ done: return err; } -int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr, - u8 *buf, uint nbytes) +int +brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, + u8 *data, uint size) { - struct sk_buff *mypkt; - bool write = rw ? SDIOH_WRITE : SDIOH_READ; - int err; + int bcmerror = 0; + struct sk_buff *pkt; + u32 sdaddr; + uint dsize; + + dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); + pkt = dev_alloc_skb(dsize); + if (!pkt) { + brcmf_err("dev_alloc_skb failed: len %d\n", dsize); + return -EIO; + } + pkt->priority = 0; - addr &= SBSDIO_SB_OFT_ADDR_MASK; - addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + /* Determine initial transfer parameters */ + sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; + if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK) + dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr); + else + dsize = size; - mypkt = brcmu_pkt_buf_get_skb(nbytes); - if (!mypkt) { - brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n", - nbytes); - return -EIO; + sdio_claim_host(sdiodev->func[1]); + + /* Do the transfer(s) */ + while (size) { + /* Set the backplane window to include the start address */ + bcmerror = brcmf_sdcard_set_sbaddr_window(sdiodev, address); + if (bcmerror) + break; + + brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n", + write ? "write" : "read", dsize, + sdaddr, address & SBSDIO_SBWINDOW_MASK); + + sdaddr &= SBSDIO_SB_OFT_ADDR_MASK; + sdaddr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + skb_put(pkt, dsize); + if (write) + memcpy(pkt->data, data, dsize); + bcmerror = brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC, + write, SDIO_FUNC_1, + sdaddr, pkt); + if (bcmerror) { + brcmf_err("membytes transfer failed\n"); + break; + } + if (!write) + memcpy(data, pkt->data, dsize); + skb_trim(pkt, dsize); + + /* Adjust for next transfer (if any) */ + size -= dsize; + if (size) { + data += dsize; + address += dsize; + sdaddr = 0; + dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); + } } - /* For a write, copy the buffer data into the packet. */ - if (write) - memcpy(mypkt->data, buf, nbytes); + dev_kfree_skb(pkt); - err = brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC, write, - SDIO_FUNC_1, addr, mypkt); + /* Return the window to backplane enumeration space for core access */ + if (brcmf_sdcard_set_sbaddr_window(sdiodev, sdiodev->sbwad)) + brcmf_err("FAILED to set window back to 0x%x\n", + sdiodev->sbwad); - /* For a read, copy the packet data back to the buffer. */ - if (!err && !write) - memcpy(buf, mypkt->data, nbytes); + sdio_release_host(sdiodev->func[1]); - brcmu_pkt_buf_free_skb(mypkt); - return err; + return bcmerror; } int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn) { char t_func = (char)fn; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); /* issue abort cmd52 command through F0 */ brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, SDIO_CCCR_ABORT, &t_func); - brcmf_dbg(TRACE, "Exit\n"); + brcmf_dbg(SDIO, "Exit\n"); return 0; } |