diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 186 |
1 files changed, 106 insertions, 80 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index e13b1a65c65..3e10b801eee 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -26,7 +26,6 @@ #include <linux/mmc/sdio.h> #include <linux/mmc/sdio_func.h> #include <linux/mmc/card.h> -#include <linux/mmc/host.h> #include <linux/platform_data/brcmfmac-sdio.h> #include <defs.h> @@ -239,7 +238,9 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, func_num = SDIO_FUNC_1; reg_size = 4; - brcmf_sdio_addrprep(sdiodev, reg_size, &addr); + ret = brcmf_sdio_addrprep(sdiodev, reg_size, &addr); + if (ret) + goto done; } do { @@ -255,6 +256,7 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, func_num, addr, data, 4); } while (ret != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); +done: if (ret != 0) brcmf_err("failed with %d\n", ret); @@ -315,8 +317,36 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, *ret = retval; } +static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, + bool write, u32 addr, struct sk_buff *pkt) +{ + unsigned int req_sz; + + brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); + if (brcmf_pm_resume_error(sdiodev)) + return -EIO; + + /* Single skb use the standard mmc interface */ + req_sz = pkt->len + 3; + req_sz &= (uint)~3; + + if (write) + return sdio_memcpy_toio(sdiodev->func[fn], addr, + ((u8 *)(pkt->data)), + req_sz); + else if (fn == 1) + return sdio_memcpy_fromio(sdiodev->func[fn], + ((u8 *)(pkt->data)), + addr, req_sz); + else + /* function 2 read is FIFO operation */ + return sdio_readsb(sdiodev->func[fn], + ((u8 *)(pkt->data)), addr, + req_sz); +} + /** - * brcmf_sdio_buffrw - SDIO interface function for block data access + * brcmf_sdio_sglist_rw - SDIO interface function for block data access * @sdiodev: brcmfmac sdio device * @fn: SDIO function number * @write: direction flag @@ -327,12 +357,13 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, * stack for block data access. It assumes that the skb passed down by the * caller has already been padded and aligned. */ -static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, - bool write, u32 addr, struct sk_buff_head *pktlist) +static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, + bool write, u32 addr, + struct sk_buff_head *pktlist) { unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset; - unsigned int max_blks, max_req_sz, orig_offset, dst_offset; - unsigned short max_seg_sz, seg_sz; + unsigned int max_req_sz, orig_offset, dst_offset; + unsigned short max_seg_cnt, seg_sz; unsigned char *pkt_data, *orig_data, *dst_data; struct sk_buff *pkt_next = NULL, *local_pkt_next; struct sk_buff_head local_list, *target_list; @@ -341,7 +372,6 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, struct mmc_data mmc_dat; struct sg_table st; struct scatterlist *sgl; - struct mmc_host *host; int ret = 0; if (!pktlist->qlen) @@ -351,27 +381,6 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, if (brcmf_pm_resume_error(sdiodev)) return -EIO; - /* Single skb use the standard mmc interface */ - if (pktlist->qlen == 1) { - pkt_next = pktlist->next; - req_sz = pkt_next->len + 3; - req_sz &= (uint)~3; - - if (write) - return sdio_memcpy_toio(sdiodev->func[fn], addr, - ((u8 *)(pkt_next->data)), - req_sz); - else if (fn == 1) - return sdio_memcpy_fromio(sdiodev->func[fn], - ((u8 *)(pkt_next->data)), - addr, req_sz); - else - /* function 2 read is FIFO operation */ - return sdio_readsb(sdiodev->func[fn], - ((u8 *)(pkt_next->data)), addr, - req_sz); - } - target_list = pktlist; /* for host with broken sg support, prepare a page aligned list */ __skb_queue_head_init(&local_list); @@ -398,38 +407,46 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, target_list = &local_list; } - host = sdiodev->func[fn]->card->host; func_blk_sz = sdiodev->func[fn]->cur_blksize; - /* Blocks per command is limited by host count, host transfer - * size and the maximum for IO_RW_EXTENDED of 511 blocks. - */ - max_blks = min_t(unsigned int, host->max_blk_count, 511u); - max_req_sz = min_t(unsigned int, host->max_req_size, - max_blks * func_blk_sz); - max_seg_sz = min_t(unsigned short, host->max_segs, SG_MAX_SINGLE_ALLOC); - max_seg_sz = min_t(unsigned short, max_seg_sz, target_list->qlen); + max_req_sz = sdiodev->max_request_size; + max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count, + target_list->qlen); seg_sz = target_list->qlen; pkt_offset = 0; pkt_next = target_list->next; - if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL)) { + if (sg_alloc_table(&st, max_seg_cnt, GFP_KERNEL)) { ret = -ENOMEM; goto exit; } + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + mmc_dat.sg = st.sgl; + mmc_dat.blksz = func_blk_sz; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + mmc_cmd.opcode = SD_IO_RW_EXTENDED; + mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ + mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */ + mmc_cmd.arg |= 1<<27; /* block mode */ + /* for function 1 the addr will be incremented */ + mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + while (seg_sz) { req_sz = 0; sg_cnt = 0; - memset(&mmc_req, 0, sizeof(struct mmc_request)); - memset(&mmc_cmd, 0, sizeof(struct mmc_command)); - memset(&mmc_dat, 0, sizeof(struct mmc_data)); sgl = st.sgl; /* prep sg table */ while (pkt_next != (struct sk_buff *)target_list) { pkt_data = pkt_next->data + pkt_offset; sg_data_sz = pkt_next->len - pkt_offset; - if (sg_data_sz > host->max_seg_size) - sg_data_sz = host->max_seg_size; + if (sg_data_sz > sdiodev->max_segment_size) + sg_data_sz = sdiodev->max_segment_size; if (sg_data_sz > max_req_sz - req_sz) sg_data_sz = max_req_sz - req_sz; @@ -444,7 +461,7 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, pkt_next = pkt_next->next; } - if (req_sz >= max_req_sz || sg_cnt >= max_seg_sz) + if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt) break; } seg_sz -= sg_cnt; @@ -455,27 +472,17 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, ret = -ENOTBLK; goto exit; } - mmc_dat.sg = st.sgl; + mmc_dat.sg_len = sg_cnt; - mmc_dat.blksz = func_blk_sz; mmc_dat.blocks = req_sz / func_blk_sz; - mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; - mmc_cmd.opcode = SD_IO_RW_EXTENDED; - mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ - mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */ - mmc_cmd.arg |= 1<<27; /* block mode */ - /* incrementing addr for function 1 */ - mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0; mmc_cmd.arg |= (addr & 0x1FFFF) << 9; /* address */ mmc_cmd.arg |= mmc_dat.blocks & 0x1FF; /* block count */ - mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; - mmc_req.cmd = &mmc_cmd; - mmc_req.data = &mmc_dat; + /* incrementing addr for function 1 */ if (fn == 1) addr += req_sz; mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card); - mmc_wait_for_req(host, &mmc_req); + mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req); ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; if (ret != 0) { @@ -546,7 +553,6 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, { uint width; int err = 0; - struct sk_buff_head pkt_list; brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); @@ -556,19 +562,17 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, if (err) goto done; - skb_queue_head_init(&pkt_list); - skb_queue_tail(&pkt_list, pkt); - err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, &pkt_list); - skb_dequeue_tail(&pkt_list); + err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pkt); done: return err; } int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq) + uint flags, struct sk_buff_head *pktq, uint totlen) { - uint incr_fix; + struct sk_buff *glom_skb; + struct sk_buff *skb; uint width; int err = 0; @@ -580,8 +584,22 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, if (err) goto done; - incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; - err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq); + if (pktq->qlen == 1) + err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq->next); + else if (!sdiodev->sg_support) { + glom_skb = brcmu_pkt_buf_get_skb(totlen); + if (!glom_skb) + return -ENOMEM; + err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, glom_skb); + if (err) + goto done; + + skb_queue_walk(pktq, skb) { + memcpy(skb->data, glom_skb->data, skb->len); + skb_pull(glom_skb, skb->len); + } + } else + err = brcmf_sdio_sglist_rw(sdiodev, fn, false, addr, pktq); done: return err; @@ -592,7 +610,7 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes) { struct sk_buff *mypkt; - struct sk_buff_head pktq; + uint width; int err; mypkt = brcmu_pkt_buf_get_skb(nbytes); @@ -603,10 +621,12 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, } memcpy(mypkt->data, buf, nbytes); - __skb_queue_head_init(&pktq); - __skb_queue_tail(&pktq, mypkt); - err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq); - __skb_dequeue_tail(&pktq); + + width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; + err = brcmf_sdio_addrprep(sdiodev, width, &addr); + + if (!err) + err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, mypkt); brcmu_pkt_buf_free_skb(mypkt); return err; @@ -617,16 +637,26 @@ int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, struct sk_buff_head *pktq) { + struct sk_buff *skb; uint width; - int err = 0; + int err; brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pktq->qlen); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - brcmf_sdio_addrprep(sdiodev, width, &addr); + err = brcmf_sdio_addrprep(sdiodev, width, &addr); + if (err) + return err; - err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq); + if (pktq->qlen == 1 || !sdiodev->sg_support) + skb_queue_walk(pktq, skb) { + err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, skb); + if (err) + break; + } + else + err = brcmf_sdio_sglist_rw(sdiodev, fn, true, addr, pktq); return err; } @@ -639,7 +669,6 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, struct sk_buff *pkt; u32 sdaddr; uint dsize; - struct sk_buff_head pkt_list; dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); pkt = dev_alloc_skb(dsize); @@ -648,7 +677,6 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, return -EIO; } pkt->priority = 0; - skb_queue_head_init(&pkt_list); /* Determine initial transfer parameters */ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; @@ -676,10 +704,8 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, skb_put(pkt, dsize); if (write) memcpy(pkt->data, data, dsize); - skb_queue_tail(&pkt_list, pkt); bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write, - sdaddr, &pkt_list); - skb_dequeue_tail(&pkt_list); + sdaddr, pkt); if (bcmerror) { brcmf_err("membytes transfer failed\n"); break; |