diff options
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c')
-rw-r--r-- | drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c | 282 |
1 files changed, 171 insertions, 111 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c b/drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c index 443a4ecdad3a..2486cda3243b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c @@ -19,21 +19,40 @@ #include "sdio.h" #include "mac.h" -static void mt7663s_refill_sched_quota(struct mt7615_dev *dev, u32 *data) +static int mt7663s_refill_sched_quota(struct mt76_dev *dev, u32 *data) { - struct mt76_sdio *sdio = &dev->mt76.sdio; + u32 ple_ac_data_quota[] = { + FIELD_GET(TXQ_CNT_L, data[4]), /* VO */ + FIELD_GET(TXQ_CNT_H, data[3]), /* VI */ + FIELD_GET(TXQ_CNT_L, data[3]), /* BE */ + FIELD_GET(TXQ_CNT_H, data[2]), /* BK */ + }; + u32 pse_ac_data_quota[] = { + FIELD_GET(TXQ_CNT_H, data[1]), /* VO */ + FIELD_GET(TXQ_CNT_L, data[1]), /* VI */ + FIELD_GET(TXQ_CNT_H, data[0]), /* BE */ + FIELD_GET(TXQ_CNT_L, data[0]), /* BK */ + }; + u32 pse_mcu_quota = FIELD_GET(TXQ_CNT_L, data[2]); + u32 pse_data_quota = 0, ple_data_quota = 0; + struct mt76_sdio *sdio = &dev->sdio; + int i; + + for (i = 0; i < ARRAY_SIZE(pse_ac_data_quota); i++) { + pse_data_quota += pse_ac_data_quota[i]; + ple_data_quota += ple_ac_data_quota[i]; + } + + if (!pse_data_quota && !ple_data_quota && !pse_mcu_quota) + return 0; mutex_lock(&sdio->sched.lock); - sdio->sched.pse_data_quota += FIELD_GET(TXQ_CNT_L, data[0]) + /* BK */ - FIELD_GET(TXQ_CNT_H, data[0]) + /* BE */ - FIELD_GET(TXQ_CNT_L, data[1]) + /* VI */ - FIELD_GET(TXQ_CNT_H, data[1]); /* VO */ - sdio->sched.ple_data_quota += FIELD_GET(TXQ_CNT_H, data[2]) + /* BK */ - FIELD_GET(TXQ_CNT_L, data[3]) + /* BE */ - FIELD_GET(TXQ_CNT_H, data[3]) + /* VI */ - FIELD_GET(TXQ_CNT_L, data[4]); /* VO */ - sdio->sched.pse_mcu_quota += FIELD_GET(TXQ_CNT_L, data[2]); + sdio->sched.pse_mcu_quota += pse_mcu_quota; + sdio->sched.pse_data_quota += pse_data_quota; + sdio->sched.ple_data_quota += ple_data_quota; mutex_unlock(&sdio->sched.lock); + + return pse_data_quota + ple_data_quota + pse_mcu_quota; } static struct sk_buff *mt7663s_build_rx_skb(void *data, int data_len, @@ -61,11 +80,11 @@ static struct sk_buff *mt7663s_build_rx_skb(void *data, int data_len, return skb; } -static int mt7663s_rx_run_queue(struct mt7615_dev *dev, enum mt76_rxq_id qid, +static int mt7663s_rx_run_queue(struct mt76_dev *dev, enum mt76_rxq_id qid, struct mt76s_intr *intr) { - struct mt76_queue *q = &dev->mt76.q_rx[qid]; - struct mt76_sdio *sdio = &dev->mt76.sdio; + struct mt76_queue *q = &dev->q_rx[qid]; + struct mt76_sdio *sdio = &dev->sdio; int len = 0, err, i, order; struct page *page; u8 *buf; @@ -86,15 +105,18 @@ static int mt7663s_rx_run_queue(struct mt7615_dev *dev, enum mt76_rxq_id qid, buf = page_address(page); + sdio_claim_host(sdio->func); err = sdio_readsb(sdio->func, buf, MCR_WRDR(qid), len); + sdio_release_host(sdio->func); + if (err < 0) { - dev_err(dev->mt76.dev, "sdio read data failed:%d\n", err); + dev_err(dev->dev, "sdio read data failed:%d\n", err); __free_pages(page, order); return err; } for (i = 0; i < intr->rx.num[qid]; i++) { - int index = (q->tail + i) % q->ndesc; + int index = (q->head + i) % q->ndesc; struct mt76_queue_entry *e = &q->entry[index]; len = intr->rx.len[qid][i]; @@ -109,160 +131,198 @@ static int mt7663s_rx_run_queue(struct mt7615_dev *dev, enum mt76_rxq_id qid, __free_pages(page, order); spin_lock_bh(&q->lock); - q->tail = (q->tail + i) % q->ndesc; + q->head = (q->head + i) % q->ndesc; q->queued += i; spin_unlock_bh(&q->lock); - return err; + return i; } -static int mt7663s_tx_update_sched(struct mt7615_dev *dev, - struct mt76_queue_entry *e, - bool mcu) +static int mt7663s_tx_pick_quota(struct mt76_sdio *sdio, enum mt76_txq_id qid, + int buf_sz, int *pse_size, int *ple_size) { - struct mt76_sdio *sdio = &dev->mt76.sdio; - struct mt76_phy *mphy = &dev->mt76.phy; - struct ieee80211_hdr *hdr; - int size, ret = -EBUSY; - - size = DIV_ROUND_UP(e->buf_sz + sdio->sched.deficit, MT_PSE_PAGE_SZ); + int pse_sz; - if (mcu) { - if (!test_bit(MT76_STATE_MCU_RUNNING, &mphy->state)) - return 0; + pse_sz = DIV_ROUND_UP(buf_sz + sdio->sched.deficit, MT_PSE_PAGE_SZ); - mutex_lock(&sdio->sched.lock); - if (sdio->sched.pse_mcu_quota > size) { - sdio->sched.pse_mcu_quota -= size; - ret = 0; - } - mutex_unlock(&sdio->sched.lock); + if (qid == MT_TXQ_MCU) { + if (sdio->sched.pse_mcu_quota < *pse_size + pse_sz) + return -EBUSY; + } else { + if (sdio->sched.pse_data_quota < *pse_size + pse_sz || + sdio->sched.ple_data_quota < *ple_size) + return -EBUSY; - return ret; + *ple_size = *ple_size + 1; } + *pse_size = *pse_size + pse_sz; - hdr = (struct ieee80211_hdr *)(e->skb->data + MT_USB_TXD_SIZE); - if (ieee80211_is_ctl(hdr->frame_control)) - return 0; + return 0; +} +static void mt7663s_tx_update_quota(struct mt76_sdio *sdio, enum mt76_txq_id qid, + int pse_size, int ple_size) +{ mutex_lock(&sdio->sched.lock); - if (sdio->sched.pse_data_quota > size && - sdio->sched.ple_data_quota > 0) { - sdio->sched.pse_data_quota -= size; - sdio->sched.ple_data_quota--; - ret = 0; + if (qid == MT_TXQ_MCU) { + sdio->sched.pse_mcu_quota -= pse_size; + } else { + sdio->sched.pse_data_quota -= pse_size; + sdio->sched.ple_data_quota -= ple_size; } mutex_unlock(&sdio->sched.lock); +} + +static int __mt7663s_xmit_queue(struct mt76_dev *dev, u8 *data, int len) +{ + struct mt76_sdio *sdio = &dev->sdio; + int err; + + if (len > sdio->func->cur_blksize) + len = roundup(len, sdio->func->cur_blksize); + + sdio_claim_host(sdio->func); + err = sdio_writesb(sdio->func, MCR_WTDR1, data, len); + sdio_release_host(sdio->func); + + if (err) + dev_err(dev->dev, "sdio write failed: %d\n", err); - return ret; + return err; } -static int mt7663s_tx_run_queue(struct mt7615_dev *dev, struct mt76_queue *q) +static int mt7663s_tx_run_queue(struct mt76_dev *dev, enum mt76_txq_id qid) { - bool mcu = q == dev->mt76.q_tx[MT_TXQ_MCU].q; - struct mt76_sdio *sdio = &dev->mt76.sdio; - int nframes = 0; + int err, nframes = 0, len = 0, pse_sz = 0, ple_sz = 0; + struct mt76_queue *q = dev->q_tx[qid]; + struct mt76_sdio *sdio = &dev->sdio; - while (q->first != q->tail) { + while (q->first != q->head) { struct mt76_queue_entry *e = &q->entry[q->first]; - int err, len = e->skb->len; + struct sk_buff *iter; - if (mt7663s_tx_update_sched(dev, e, mcu)) + if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) { + __skb_put_zero(e->skb, 4); + err = __mt7663s_xmit_queue(dev, e->skb->data, + e->skb->len); + if (err) + return err; + + goto next; + } + + if (len + e->skb->len + 4 > MT76S_XMIT_BUF_SZ) break; - if (len > sdio->func->cur_blksize) - len = roundup(len, sdio->func->cur_blksize); + if (mt7663s_tx_pick_quota(sdio, qid, e->buf_sz, &pse_sz, + &ple_sz)) + break; - /* TODO: skb_walk_frags and then write to SDIO port */ - err = sdio_writesb(sdio->func, MCR_WTDR1, e->skb->data, len); - if (err) { - dev_err(dev->mt76.dev, "sdio write failed: %d\n", err); - return -EIO; - } + memcpy(sdio->xmit_buf[qid] + len, e->skb->data, + skb_headlen(e->skb)); + len += skb_headlen(e->skb); + nframes++; - e->done = true; + skb_walk_frags(e->skb, iter) { + memcpy(sdio->xmit_buf[qid] + len, iter->data, + iter->len); + len += iter->len; + nframes++; + } +next: q->first = (q->first + 1) % q->ndesc; - nframes++; + e->done = true; + } + + if (nframes) { + memset(sdio->xmit_buf[qid] + len, 0, 4); + err = __mt7663s_xmit_queue(dev, sdio->xmit_buf[qid], len + 4); + if (err) + return err; } + mt7663s_tx_update_quota(sdio, qid, pse_sz, ple_sz); return nframes; } -static int mt7663s_tx_run_queues(struct mt7615_dev *dev) +void mt7663s_tx_work(struct work_struct *work) { + struct mt76_sdio *sdio = container_of(work, struct mt76_sdio, + tx.xmit_work); + struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio); int i, nframes = 0; for (i = 0; i < MT_TXQ_MCU_WA; i++) { int ret; - ret = mt7663s_tx_run_queue(dev, dev->mt76.q_tx[i].q); + ret = mt7663s_tx_run_queue(dev, i); if (ret < 0) - return ret; + break; nframes += ret; } + if (nframes) + queue_work(sdio->txrx_wq, &sdio->tx.xmit_work); - return nframes; + queue_work(sdio->txrx_wq, &sdio->tx.status_work); } -int mt7663s_kthread_run(void *data) +void mt7663s_rx_work(struct work_struct *work) { - struct mt7615_dev *dev = data; - struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_sdio *sdio = container_of(work, struct mt76_sdio, + rx.recv_work); + struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio); + struct mt76s_intr *intr = sdio->intr_data; + int nframes = 0, ret; - while (!kthread_should_stop()) { - int ret; + /* disable interrupt */ + sdio_claim_host(sdio->func); + sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL); + ret = sdio_readsb(sdio->func, intr, MCR_WHISR, sizeof(*intr)); + sdio_release_host(sdio->func); - cond_resched(); + if (ret < 0) + goto out; - sdio_claim_host(dev->mt76.sdio.func); - ret = mt7663s_tx_run_queues(dev); - sdio_release_host(dev->mt76.sdio.func); + trace_dev_irq(dev, intr->isr, 0); - if (ret <= 0 || !test_bit(MT76_STATE_RUNNING, &mphy->state)) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } else { - wake_up_process(dev->mt76.sdio.kthread); + if (intr->isr & WHIER_RX0_DONE_INT_EN) { + ret = mt7663s_rx_run_queue(dev, 0, intr); + if (ret > 0) { + queue_work(sdio->txrx_wq, &sdio->rx.net_work); + nframes += ret; } } - return 0; + if (intr->isr & WHIER_RX1_DONE_INT_EN) { + ret = mt7663s_rx_run_queue(dev, 1, intr); + if (ret > 0) { + queue_work(sdio->txrx_wq, &sdio->rx.net_work); + nframes += ret; + } + } + + if (mt7663s_refill_sched_quota(dev, intr->tx.wtqcr)) + queue_work(sdio->txrx_wq, &sdio->tx.xmit_work); + + if (nframes) { + queue_work(sdio->txrx_wq, &sdio->rx.recv_work); + return; + } +out: + /* enable interrupt */ + sdio_claim_host(sdio->func); + sdio_writel(sdio->func, WHLPCR_INT_EN_SET, MCR_WHLPCR, NULL); + sdio_release_host(sdio->func); } void mt7663s_sdio_irq(struct sdio_func *func) { struct mt7615_dev *dev = sdio_get_drvdata(func); struct mt76_sdio *sdio = &dev->mt76.sdio; - struct mt76s_intr intr; - - /* disable interrupt */ - sdio_writel(func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, 0); - - do { - sdio_readsb(func, &intr, MCR_WHISR, sizeof(struct mt76s_intr)); - trace_dev_irq(&dev->mt76, intr.isr, 0); - if (!test_bit(MT76_STATE_INITIALIZED, &dev->mt76.phy.state)) - goto out; + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mt76.phy.state)) + return; - if (intr.isr & WHIER_RX0_DONE_INT_EN) { - mt7663s_rx_run_queue(dev, 0, &intr); - wake_up_process(sdio->kthread); - } - - if (intr.isr & WHIER_RX1_DONE_INT_EN) { - mt7663s_rx_run_queue(dev, 1, &intr); - wake_up_process(sdio->kthread); - } - - if (intr.isr & WHIER_TX_DONE_INT_EN) { - mt7663s_refill_sched_quota(dev, intr.tx.wtqcr); - mt7663s_tx_run_queues(dev); - wake_up_process(sdio->kthread); - } - } while (intr.isr); -out: - /* enable interrupt */ - sdio_writel(func, WHLPCR_INT_EN_SET, MCR_WHLPCR, 0); + queue_work(sdio->txrx_wq, &sdio->rx.recv_work); } |