179968444SLorenzo Bianconi // SPDX-License-Identifier: GPL-2.0-only
279968444SLorenzo Bianconi /* Copyright (C) 2022 MediaTek Inc.
379968444SLorenzo Bianconi *
479968444SLorenzo Bianconi * Author: Lorenzo Bianconi <lorenzo@kernel.org>
579968444SLorenzo Bianconi * Sujuan Chen <sujuan.chen@mediatek.com>
679968444SLorenzo Bianconi */
779968444SLorenzo Bianconi
879968444SLorenzo Bianconi #include <linux/kernel.h>
979968444SLorenzo Bianconi #include <linux/dma-mapping.h>
1079968444SLorenzo Bianconi #include <linux/interrupt.h>
1179968444SLorenzo Bianconi #include <linux/mfd/syscon.h>
123d40aed8SRob Herring #include <linux/of.h>
1379968444SLorenzo Bianconi #include <linux/of_irq.h>
1479968444SLorenzo Bianconi #include <linux/bitfield.h>
1579968444SLorenzo Bianconi
1679968444SLorenzo Bianconi #include "mtk_wed.h"
1779968444SLorenzo Bianconi #include "mtk_wed_regs.h"
1879968444SLorenzo Bianconi #include "mtk_wed_wo.h"
1979968444SLorenzo Bianconi
2079968444SLorenzo Bianconi static u32
mtk_wed_mmio_r32(struct mtk_wed_wo * wo,u32 reg)2179968444SLorenzo Bianconi mtk_wed_mmio_r32(struct mtk_wed_wo *wo, u32 reg)
2279968444SLorenzo Bianconi {
2379968444SLorenzo Bianconi u32 val;
2479968444SLorenzo Bianconi
2579968444SLorenzo Bianconi if (regmap_read(wo->mmio.regs, reg, &val))
2679968444SLorenzo Bianconi val = ~0;
2779968444SLorenzo Bianconi
2879968444SLorenzo Bianconi return val;
2979968444SLorenzo Bianconi }
3079968444SLorenzo Bianconi
3179968444SLorenzo Bianconi static void
mtk_wed_mmio_w32(struct mtk_wed_wo * wo,u32 reg,u32 val)3279968444SLorenzo Bianconi mtk_wed_mmio_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
3379968444SLorenzo Bianconi {
3479968444SLorenzo Bianconi regmap_write(wo->mmio.regs, reg, val);
3579968444SLorenzo Bianconi }
3679968444SLorenzo Bianconi
3779968444SLorenzo Bianconi static u32
mtk_wed_wo_get_isr(struct mtk_wed_wo * wo)3879968444SLorenzo Bianconi mtk_wed_wo_get_isr(struct mtk_wed_wo *wo)
3979968444SLorenzo Bianconi {
4079968444SLorenzo Bianconi u32 val = mtk_wed_mmio_r32(wo, MTK_WED_WO_CCIF_RCHNUM);
4179968444SLorenzo Bianconi
4279968444SLorenzo Bianconi return val & MTK_WED_WO_CCIF_RCHNUM_MASK;
4379968444SLorenzo Bianconi }
4479968444SLorenzo Bianconi
4579968444SLorenzo Bianconi static void
mtk_wed_wo_set_isr(struct mtk_wed_wo * wo,u32 mask)4679968444SLorenzo Bianconi mtk_wed_wo_set_isr(struct mtk_wed_wo *wo, u32 mask)
4779968444SLorenzo Bianconi {
4879968444SLorenzo Bianconi mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_IRQ0_MASK, mask);
4979968444SLorenzo Bianconi }
5079968444SLorenzo Bianconi
5179968444SLorenzo Bianconi static void
mtk_wed_wo_set_ack(struct mtk_wed_wo * wo,u32 mask)5279968444SLorenzo Bianconi mtk_wed_wo_set_ack(struct mtk_wed_wo *wo, u32 mask)
5379968444SLorenzo Bianconi {
5479968444SLorenzo Bianconi mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_ACK, mask);
5579968444SLorenzo Bianconi }
5679968444SLorenzo Bianconi
5779968444SLorenzo Bianconi static void
mtk_wed_wo_set_isr_mask(struct mtk_wed_wo * wo,u32 mask,u32 val,bool set)5879968444SLorenzo Bianconi mtk_wed_wo_set_isr_mask(struct mtk_wed_wo *wo, u32 mask, u32 val, bool set)
5979968444SLorenzo Bianconi {
6079968444SLorenzo Bianconi unsigned long flags;
6179968444SLorenzo Bianconi
6279968444SLorenzo Bianconi spin_lock_irqsave(&wo->mmio.lock, flags);
6379968444SLorenzo Bianconi wo->mmio.irq_mask &= ~mask;
6479968444SLorenzo Bianconi wo->mmio.irq_mask |= val;
6579968444SLorenzo Bianconi if (set)
6679968444SLorenzo Bianconi mtk_wed_wo_set_isr(wo, wo->mmio.irq_mask);
6779968444SLorenzo Bianconi spin_unlock_irqrestore(&wo->mmio.lock, flags);
6879968444SLorenzo Bianconi }
6979968444SLorenzo Bianconi
7079968444SLorenzo Bianconi static void
mtk_wed_wo_irq_enable(struct mtk_wed_wo * wo,u32 mask)7179968444SLorenzo Bianconi mtk_wed_wo_irq_enable(struct mtk_wed_wo *wo, u32 mask)
7279968444SLorenzo Bianconi {
7379968444SLorenzo Bianconi mtk_wed_wo_set_isr_mask(wo, 0, mask, false);
7479968444SLorenzo Bianconi tasklet_schedule(&wo->mmio.irq_tasklet);
7579968444SLorenzo Bianconi }
7679968444SLorenzo Bianconi
7779968444SLorenzo Bianconi static void
mtk_wed_wo_irq_disable(struct mtk_wed_wo * wo,u32 mask)7879968444SLorenzo Bianconi mtk_wed_wo_irq_disable(struct mtk_wed_wo *wo, u32 mask)
7979968444SLorenzo Bianconi {
8079968444SLorenzo Bianconi mtk_wed_wo_set_isr_mask(wo, mask, 0, true);
8179968444SLorenzo Bianconi }
8279968444SLorenzo Bianconi
8379968444SLorenzo Bianconi static void
mtk_wed_wo_kickout(struct mtk_wed_wo * wo)8479968444SLorenzo Bianconi mtk_wed_wo_kickout(struct mtk_wed_wo *wo)
8579968444SLorenzo Bianconi {
8679968444SLorenzo Bianconi mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_BUSY, 1 << MTK_WED_WO_TXCH_NUM);
8779968444SLorenzo Bianconi mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_TCHNUM, MTK_WED_WO_TXCH_NUM);
8879968444SLorenzo Bianconi }
8979968444SLorenzo Bianconi
9079968444SLorenzo Bianconi static void
mtk_wed_wo_queue_kick(struct mtk_wed_wo * wo,struct mtk_wed_wo_queue * q,u32 val)9179968444SLorenzo Bianconi mtk_wed_wo_queue_kick(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
9279968444SLorenzo Bianconi u32 val)
9379968444SLorenzo Bianconi {
9479968444SLorenzo Bianconi wmb();
9579968444SLorenzo Bianconi mtk_wed_mmio_w32(wo, q->regs.cpu_idx, val);
9679968444SLorenzo Bianconi }
9779968444SLorenzo Bianconi
9879968444SLorenzo Bianconi static void *
mtk_wed_wo_dequeue(struct mtk_wed_wo * wo,struct mtk_wed_wo_queue * q,u32 * len,bool flush)9979968444SLorenzo Bianconi mtk_wed_wo_dequeue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, u32 *len,
10079968444SLorenzo Bianconi bool flush)
10179968444SLorenzo Bianconi {
10279968444SLorenzo Bianconi int buf_len = SKB_WITH_OVERHEAD(q->buf_size);
10379968444SLorenzo Bianconi int index = (q->tail + 1) % q->n_desc;
10479968444SLorenzo Bianconi struct mtk_wed_wo_queue_entry *entry;
10579968444SLorenzo Bianconi struct mtk_wed_wo_queue_desc *desc;
10679968444SLorenzo Bianconi void *buf;
10779968444SLorenzo Bianconi
10879968444SLorenzo Bianconi if (!q->queued)
10979968444SLorenzo Bianconi return NULL;
11079968444SLorenzo Bianconi
11179968444SLorenzo Bianconi if (flush)
11279968444SLorenzo Bianconi q->desc[index].ctrl |= cpu_to_le32(MTK_WED_WO_CTL_DMA_DONE);
11379968444SLorenzo Bianconi else if (!(q->desc[index].ctrl & cpu_to_le32(MTK_WED_WO_CTL_DMA_DONE)))
11479968444SLorenzo Bianconi return NULL;
11579968444SLorenzo Bianconi
11679968444SLorenzo Bianconi q->tail = index;
11779968444SLorenzo Bianconi q->queued--;
11879968444SLorenzo Bianconi
11979968444SLorenzo Bianconi desc = &q->desc[index];
12079968444SLorenzo Bianconi entry = &q->entry[index];
12179968444SLorenzo Bianconi buf = entry->buf;
12279968444SLorenzo Bianconi if (len)
12379968444SLorenzo Bianconi *len = FIELD_GET(MTK_WED_WO_CTL_SD_LEN0,
12479968444SLorenzo Bianconi le32_to_cpu(READ_ONCE(desc->ctrl)));
12579968444SLorenzo Bianconi if (buf)
12679968444SLorenzo Bianconi dma_unmap_single(wo->hw->dev, entry->addr, buf_len,
12779968444SLorenzo Bianconi DMA_FROM_DEVICE);
12879968444SLorenzo Bianconi entry->buf = NULL;
12979968444SLorenzo Bianconi
13079968444SLorenzo Bianconi return buf;
13179968444SLorenzo Bianconi }
13279968444SLorenzo Bianconi
13379968444SLorenzo Bianconi static int
mtk_wed_wo_queue_refill(struct mtk_wed_wo * wo,struct mtk_wed_wo_queue * q,bool rx)13479968444SLorenzo Bianconi mtk_wed_wo_queue_refill(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
13565e6af6cSLorenzo Bianconi bool rx)
13679968444SLorenzo Bianconi {
13779968444SLorenzo Bianconi enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
13879968444SLorenzo Bianconi int n_buf = 0;
13979968444SLorenzo Bianconi
14079968444SLorenzo Bianconi while (q->queued < q->n_desc) {
14179968444SLorenzo Bianconi struct mtk_wed_wo_queue_entry *entry;
14279968444SLorenzo Bianconi dma_addr_t addr;
14365e6af6cSLorenzo Bianconi void *buf;
14479968444SLorenzo Bianconi
14531c54867SLorenzo Bianconi buf = page_frag_alloc(&q->cache, q->buf_size,
14631c54867SLorenzo Bianconi GFP_ATOMIC | GFP_DMA32);
14779968444SLorenzo Bianconi if (!buf)
14879968444SLorenzo Bianconi break;
14979968444SLorenzo Bianconi
15079968444SLorenzo Bianconi addr = dma_map_single(wo->hw->dev, buf, q->buf_size, dir);
15179968444SLorenzo Bianconi if (unlikely(dma_mapping_error(wo->hw->dev, addr))) {
15279968444SLorenzo Bianconi skb_free_frag(buf);
15379968444SLorenzo Bianconi break;
15479968444SLorenzo Bianconi }
15579968444SLorenzo Bianconi
15679968444SLorenzo Bianconi q->head = (q->head + 1) % q->n_desc;
15779968444SLorenzo Bianconi entry = &q->entry[q->head];
15879968444SLorenzo Bianconi entry->addr = addr;
15979968444SLorenzo Bianconi entry->len = q->buf_size;
16079968444SLorenzo Bianconi q->entry[q->head].buf = buf;
16179968444SLorenzo Bianconi
16279968444SLorenzo Bianconi if (rx) {
16379968444SLorenzo Bianconi struct mtk_wed_wo_queue_desc *desc = &q->desc[q->head];
16479968444SLorenzo Bianconi u32 ctrl = MTK_WED_WO_CTL_LAST_SEC0 |
16579968444SLorenzo Bianconi FIELD_PREP(MTK_WED_WO_CTL_SD_LEN0,
16679968444SLorenzo Bianconi entry->len);
16779968444SLorenzo Bianconi
16879968444SLorenzo Bianconi WRITE_ONCE(desc->buf0, cpu_to_le32(addr));
16979968444SLorenzo Bianconi WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
17079968444SLorenzo Bianconi }
17179968444SLorenzo Bianconi q->queued++;
17279968444SLorenzo Bianconi n_buf++;
17379968444SLorenzo Bianconi }
17479968444SLorenzo Bianconi
17579968444SLorenzo Bianconi return n_buf;
17679968444SLorenzo Bianconi }
17779968444SLorenzo Bianconi
17879968444SLorenzo Bianconi static void
mtk_wed_wo_rx_complete(struct mtk_wed_wo * wo)17979968444SLorenzo Bianconi mtk_wed_wo_rx_complete(struct mtk_wed_wo *wo)
18079968444SLorenzo Bianconi {
18179968444SLorenzo Bianconi mtk_wed_wo_set_ack(wo, MTK_WED_WO_RXCH_INT_MASK);
18279968444SLorenzo Bianconi mtk_wed_wo_irq_enable(wo, MTK_WED_WO_RXCH_INT_MASK);
18379968444SLorenzo Bianconi }
18479968444SLorenzo Bianconi
18579968444SLorenzo Bianconi static void
mtk_wed_wo_rx_run_queue(struct mtk_wed_wo * wo,struct mtk_wed_wo_queue * q)18679968444SLorenzo Bianconi mtk_wed_wo_rx_run_queue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
18779968444SLorenzo Bianconi {
18879968444SLorenzo Bianconi for (;;) {
18979968444SLorenzo Bianconi struct mtk_wed_mcu_hdr *hdr;
19079968444SLorenzo Bianconi struct sk_buff *skb;
19179968444SLorenzo Bianconi void *data;
19279968444SLorenzo Bianconi u32 len;
19379968444SLorenzo Bianconi
19479968444SLorenzo Bianconi data = mtk_wed_wo_dequeue(wo, q, &len, false);
19579968444SLorenzo Bianconi if (!data)
19679968444SLorenzo Bianconi break;
19779968444SLorenzo Bianconi
19879968444SLorenzo Bianconi skb = build_skb(data, q->buf_size);
19979968444SLorenzo Bianconi if (!skb) {
20079968444SLorenzo Bianconi skb_free_frag(data);
20179968444SLorenzo Bianconi continue;
20279968444SLorenzo Bianconi }
20379968444SLorenzo Bianconi
20479968444SLorenzo Bianconi __skb_put(skb, len);
20579968444SLorenzo Bianconi if (mtk_wed_mcu_check_msg(wo, skb)) {
20679968444SLorenzo Bianconi dev_kfree_skb(skb);
20779968444SLorenzo Bianconi continue;
20879968444SLorenzo Bianconi }
20979968444SLorenzo Bianconi
21079968444SLorenzo Bianconi hdr = (struct mtk_wed_mcu_hdr *)skb->data;
21179968444SLorenzo Bianconi if (hdr->flag & cpu_to_le16(MTK_WED_WARP_CMD_FLAG_RSP))
21279968444SLorenzo Bianconi mtk_wed_mcu_rx_event(wo, skb);
21379968444SLorenzo Bianconi else
21479968444SLorenzo Bianconi mtk_wed_mcu_rx_unsolicited_event(wo, skb);
21579968444SLorenzo Bianconi }
21679968444SLorenzo Bianconi
21765e6af6cSLorenzo Bianconi if (mtk_wed_wo_queue_refill(wo, q, true)) {
21879968444SLorenzo Bianconi u32 index = (q->head - 1) % q->n_desc;
21979968444SLorenzo Bianconi
22079968444SLorenzo Bianconi mtk_wed_wo_queue_kick(wo, q, index);
22179968444SLorenzo Bianconi }
22279968444SLorenzo Bianconi }
22379968444SLorenzo Bianconi
22479968444SLorenzo Bianconi static irqreturn_t
mtk_wed_wo_irq_handler(int irq,void * data)22579968444SLorenzo Bianconi mtk_wed_wo_irq_handler(int irq, void *data)
22679968444SLorenzo Bianconi {
22779968444SLorenzo Bianconi struct mtk_wed_wo *wo = data;
22879968444SLorenzo Bianconi
22979968444SLorenzo Bianconi mtk_wed_wo_set_isr(wo, 0);
23079968444SLorenzo Bianconi tasklet_schedule(&wo->mmio.irq_tasklet);
23179968444SLorenzo Bianconi
23279968444SLorenzo Bianconi return IRQ_HANDLED;
23379968444SLorenzo Bianconi }
23479968444SLorenzo Bianconi
mtk_wed_wo_irq_tasklet(struct tasklet_struct * t)23579968444SLorenzo Bianconi static void mtk_wed_wo_irq_tasklet(struct tasklet_struct *t)
23679968444SLorenzo Bianconi {
23779968444SLorenzo Bianconi struct mtk_wed_wo *wo = from_tasklet(wo, t, mmio.irq_tasklet);
23879968444SLorenzo Bianconi u32 intr, mask;
23979968444SLorenzo Bianconi
24079968444SLorenzo Bianconi /* disable interrupts */
24179968444SLorenzo Bianconi mtk_wed_wo_set_isr(wo, 0);
24279968444SLorenzo Bianconi
24379968444SLorenzo Bianconi intr = mtk_wed_wo_get_isr(wo);
24479968444SLorenzo Bianconi intr &= wo->mmio.irq_mask;
24579968444SLorenzo Bianconi mask = intr & (MTK_WED_WO_RXCH_INT_MASK | MTK_WED_WO_EXCEPTION_INT_MASK);
24679968444SLorenzo Bianconi mtk_wed_wo_irq_disable(wo, mask);
24779968444SLorenzo Bianconi
24879968444SLorenzo Bianconi if (intr & MTK_WED_WO_RXCH_INT_MASK) {
24979968444SLorenzo Bianconi mtk_wed_wo_rx_run_queue(wo, &wo->q_rx);
25079968444SLorenzo Bianconi mtk_wed_wo_rx_complete(wo);
25179968444SLorenzo Bianconi }
25279968444SLorenzo Bianconi }
25379968444SLorenzo Bianconi
25479968444SLorenzo Bianconi /* mtk wed wo hw queues */
25579968444SLorenzo Bianconi
25679968444SLorenzo Bianconi static int
mtk_wed_wo_queue_alloc(struct mtk_wed_wo * wo,struct mtk_wed_wo_queue * q,int n_desc,int buf_size,int index,struct mtk_wed_wo_queue_regs * regs)25779968444SLorenzo Bianconi mtk_wed_wo_queue_alloc(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
25879968444SLorenzo Bianconi int n_desc, int buf_size, int index,
25979968444SLorenzo Bianconi struct mtk_wed_wo_queue_regs *regs)
26079968444SLorenzo Bianconi {
26179968444SLorenzo Bianconi q->regs = *regs;
26279968444SLorenzo Bianconi q->n_desc = n_desc;
26379968444SLorenzo Bianconi q->buf_size = buf_size;
26479968444SLorenzo Bianconi
26579968444SLorenzo Bianconi q->desc = dmam_alloc_coherent(wo->hw->dev, n_desc * sizeof(*q->desc),
26679968444SLorenzo Bianconi &q->desc_dma, GFP_KERNEL);
26779968444SLorenzo Bianconi if (!q->desc)
26879968444SLorenzo Bianconi return -ENOMEM;
26979968444SLorenzo Bianconi
27079968444SLorenzo Bianconi q->entry = devm_kzalloc(wo->hw->dev, n_desc * sizeof(*q->entry),
27179968444SLorenzo Bianconi GFP_KERNEL);
27279968444SLorenzo Bianconi if (!q->entry)
27379968444SLorenzo Bianconi return -ENOMEM;
27479968444SLorenzo Bianconi
27579968444SLorenzo Bianconi return 0;
27679968444SLorenzo Bianconi }
27779968444SLorenzo Bianconi
27879968444SLorenzo Bianconi static void
mtk_wed_wo_queue_free(struct mtk_wed_wo * wo,struct mtk_wed_wo_queue * q)27979968444SLorenzo Bianconi mtk_wed_wo_queue_free(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
28079968444SLorenzo Bianconi {
28179968444SLorenzo Bianconi mtk_wed_mmio_w32(wo, q->regs.cpu_idx, 0);
28279968444SLorenzo Bianconi dma_free_coherent(wo->hw->dev, q->n_desc * sizeof(*q->desc), q->desc,
28379968444SLorenzo Bianconi q->desc_dma);
28479968444SLorenzo Bianconi }
28579968444SLorenzo Bianconi
28679968444SLorenzo Bianconi static void
mtk_wed_wo_queue_tx_clean(struct mtk_wed_wo * wo,struct mtk_wed_wo_queue * q)28779968444SLorenzo Bianconi mtk_wed_wo_queue_tx_clean(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
28879968444SLorenzo Bianconi {
28979968444SLorenzo Bianconi int i;
29079968444SLorenzo Bianconi
29179968444SLorenzo Bianconi for (i = 0; i < q->n_desc; i++) {
29279968444SLorenzo Bianconi struct mtk_wed_wo_queue_entry *entry = &q->entry[i];
29379968444SLorenzo Bianconi
2947cb8cd4dSLorenzo Bianconi if (!entry->buf)
2957cb8cd4dSLorenzo Bianconi continue;
2967cb8cd4dSLorenzo Bianconi
29779968444SLorenzo Bianconi dma_unmap_single(wo->hw->dev, entry->addr, entry->len,
29879968444SLorenzo Bianconi DMA_TO_DEVICE);
29979968444SLorenzo Bianconi skb_free_frag(entry->buf);
30079968444SLorenzo Bianconi entry->buf = NULL;
30179968444SLorenzo Bianconi }
30279968444SLorenzo Bianconi
303*a0727489SYunsheng Lin page_frag_cache_drain(&q->cache);
30479968444SLorenzo Bianconi }
30579968444SLorenzo Bianconi
30679968444SLorenzo Bianconi static void
mtk_wed_wo_queue_rx_clean(struct mtk_wed_wo * wo,struct mtk_wed_wo_queue * q)30779968444SLorenzo Bianconi mtk_wed_wo_queue_rx_clean(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
30879968444SLorenzo Bianconi {
30979968444SLorenzo Bianconi for (;;) {
31079968444SLorenzo Bianconi void *buf = mtk_wed_wo_dequeue(wo, q, NULL, true);
31179968444SLorenzo Bianconi
31279968444SLorenzo Bianconi if (!buf)
31379968444SLorenzo Bianconi break;
31479968444SLorenzo Bianconi
31579968444SLorenzo Bianconi skb_free_frag(buf);
31679968444SLorenzo Bianconi }
31779968444SLorenzo Bianconi
318*a0727489SYunsheng Lin page_frag_cache_drain(&q->cache);
31979968444SLorenzo Bianconi }
32079968444SLorenzo Bianconi
32179968444SLorenzo Bianconi static void
mtk_wed_wo_queue_reset(struct mtk_wed_wo * wo,struct mtk_wed_wo_queue * q)32279968444SLorenzo Bianconi mtk_wed_wo_queue_reset(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
32379968444SLorenzo Bianconi {
32479968444SLorenzo Bianconi mtk_wed_mmio_w32(wo, q->regs.cpu_idx, 0);
32579968444SLorenzo Bianconi mtk_wed_mmio_w32(wo, q->regs.desc_base, q->desc_dma);
32679968444SLorenzo Bianconi mtk_wed_mmio_w32(wo, q->regs.ring_size, q->n_desc);
32779968444SLorenzo Bianconi }
32879968444SLorenzo Bianconi
mtk_wed_wo_queue_tx_skb(struct mtk_wed_wo * wo,struct mtk_wed_wo_queue * q,struct sk_buff * skb)32979968444SLorenzo Bianconi int mtk_wed_wo_queue_tx_skb(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
33079968444SLorenzo Bianconi struct sk_buff *skb)
33179968444SLorenzo Bianconi {
33279968444SLorenzo Bianconi struct mtk_wed_wo_queue_entry *entry;
33379968444SLorenzo Bianconi struct mtk_wed_wo_queue_desc *desc;
33479968444SLorenzo Bianconi int ret = 0, index;
33579968444SLorenzo Bianconi u32 ctrl;
33679968444SLorenzo Bianconi
33779968444SLorenzo Bianconi q->tail = mtk_wed_mmio_r32(wo, q->regs.dma_idx);
33879968444SLorenzo Bianconi index = (q->head + 1) % q->n_desc;
33979968444SLorenzo Bianconi if (q->tail == index) {
34079968444SLorenzo Bianconi ret = -ENOMEM;
34179968444SLorenzo Bianconi goto out;
34279968444SLorenzo Bianconi }
34379968444SLorenzo Bianconi
34479968444SLorenzo Bianconi entry = &q->entry[index];
34579968444SLorenzo Bianconi if (skb->len > entry->len) {
34679968444SLorenzo Bianconi ret = -ENOMEM;
34779968444SLorenzo Bianconi goto out;
34879968444SLorenzo Bianconi }
34979968444SLorenzo Bianconi
35079968444SLorenzo Bianconi desc = &q->desc[index];
35179968444SLorenzo Bianconi q->head = index;
35279968444SLorenzo Bianconi
35379968444SLorenzo Bianconi dma_sync_single_for_cpu(wo->hw->dev, entry->addr, skb->len,
35479968444SLorenzo Bianconi DMA_TO_DEVICE);
35579968444SLorenzo Bianconi memcpy(entry->buf, skb->data, skb->len);
35679968444SLorenzo Bianconi dma_sync_single_for_device(wo->hw->dev, entry->addr, skb->len,
35779968444SLorenzo Bianconi DMA_TO_DEVICE);
35879968444SLorenzo Bianconi
35979968444SLorenzo Bianconi ctrl = FIELD_PREP(MTK_WED_WO_CTL_SD_LEN0, skb->len) |
36079968444SLorenzo Bianconi MTK_WED_WO_CTL_LAST_SEC0 | MTK_WED_WO_CTL_DMA_DONE;
36179968444SLorenzo Bianconi WRITE_ONCE(desc->buf0, cpu_to_le32(entry->addr));
36279968444SLorenzo Bianconi WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
36379968444SLorenzo Bianconi
36479968444SLorenzo Bianconi mtk_wed_wo_queue_kick(wo, q, q->head);
36579968444SLorenzo Bianconi mtk_wed_wo_kickout(wo);
36679968444SLorenzo Bianconi out:
36779968444SLorenzo Bianconi dev_kfree_skb(skb);
36879968444SLorenzo Bianconi
36979968444SLorenzo Bianconi return ret;
37079968444SLorenzo Bianconi }
37179968444SLorenzo Bianconi
37279968444SLorenzo Bianconi static int
mtk_wed_wo_exception_init(struct mtk_wed_wo * wo)37379968444SLorenzo Bianconi mtk_wed_wo_exception_init(struct mtk_wed_wo *wo)
37479968444SLorenzo Bianconi {
37579968444SLorenzo Bianconi return 0;
37679968444SLorenzo Bianconi }
37779968444SLorenzo Bianconi
37879968444SLorenzo Bianconi static int
mtk_wed_wo_hardware_init(struct mtk_wed_wo * wo)37979968444SLorenzo Bianconi mtk_wed_wo_hardware_init(struct mtk_wed_wo *wo)
38079968444SLorenzo Bianconi {
38179968444SLorenzo Bianconi struct mtk_wed_wo_queue_regs regs;
38279968444SLorenzo Bianconi struct device_node *np;
38379968444SLorenzo Bianconi int ret;
38479968444SLorenzo Bianconi
38579968444SLorenzo Bianconi np = of_parse_phandle(wo->hw->node, "mediatek,wo-ccif", 0);
38679968444SLorenzo Bianconi if (!np)
38779968444SLorenzo Bianconi return -ENODEV;
38879968444SLorenzo Bianconi
38979968444SLorenzo Bianconi wo->mmio.regs = syscon_regmap_lookup_by_phandle(np, NULL);
390e22dcbc9SYuan Can if (IS_ERR(wo->mmio.regs)) {
391e22dcbc9SYuan Can ret = PTR_ERR(wo->mmio.regs);
392e22dcbc9SYuan Can goto error_put;
393e22dcbc9SYuan Can }
39479968444SLorenzo Bianconi
39579968444SLorenzo Bianconi wo->mmio.irq = irq_of_parse_and_map(np, 0);
39679968444SLorenzo Bianconi wo->mmio.irq_mask = MTK_WED_WO_ALL_INT_MASK;
39779968444SLorenzo Bianconi spin_lock_init(&wo->mmio.lock);
39879968444SLorenzo Bianconi tasklet_setup(&wo->mmio.irq_tasklet, mtk_wed_wo_irq_tasklet);
39979968444SLorenzo Bianconi
40079968444SLorenzo Bianconi ret = devm_request_irq(wo->hw->dev, wo->mmio.irq,
40179968444SLorenzo Bianconi mtk_wed_wo_irq_handler, IRQF_TRIGGER_HIGH,
40279968444SLorenzo Bianconi KBUILD_MODNAME, wo);
40379968444SLorenzo Bianconi if (ret)
40479968444SLorenzo Bianconi goto error;
40579968444SLorenzo Bianconi
40679968444SLorenzo Bianconi regs.desc_base = MTK_WED_WO_CCIF_DUMMY1;
40779968444SLorenzo Bianconi regs.ring_size = MTK_WED_WO_CCIF_DUMMY2;
40879968444SLorenzo Bianconi regs.dma_idx = MTK_WED_WO_CCIF_SHADOW4;
40979968444SLorenzo Bianconi regs.cpu_idx = MTK_WED_WO_CCIF_DUMMY3;
41079968444SLorenzo Bianconi
41179968444SLorenzo Bianconi ret = mtk_wed_wo_queue_alloc(wo, &wo->q_tx, MTK_WED_WO_RING_SIZE,
41279968444SLorenzo Bianconi MTK_WED_WO_CMD_LEN, MTK_WED_WO_TXCH_NUM,
41379968444SLorenzo Bianconi ®s);
41479968444SLorenzo Bianconi if (ret)
41579968444SLorenzo Bianconi goto error;
41679968444SLorenzo Bianconi
41765e6af6cSLorenzo Bianconi mtk_wed_wo_queue_refill(wo, &wo->q_tx, false);
41879968444SLorenzo Bianconi mtk_wed_wo_queue_reset(wo, &wo->q_tx);
41979968444SLorenzo Bianconi
42079968444SLorenzo Bianconi regs.desc_base = MTK_WED_WO_CCIF_DUMMY5;
42179968444SLorenzo Bianconi regs.ring_size = MTK_WED_WO_CCIF_DUMMY6;
42279968444SLorenzo Bianconi regs.dma_idx = MTK_WED_WO_CCIF_SHADOW8;
42379968444SLorenzo Bianconi regs.cpu_idx = MTK_WED_WO_CCIF_DUMMY7;
42479968444SLorenzo Bianconi
42579968444SLorenzo Bianconi ret = mtk_wed_wo_queue_alloc(wo, &wo->q_rx, MTK_WED_WO_RING_SIZE,
42679968444SLorenzo Bianconi MTK_WED_WO_CMD_LEN, MTK_WED_WO_RXCH_NUM,
42779968444SLorenzo Bianconi ®s);
42879968444SLorenzo Bianconi if (ret)
42979968444SLorenzo Bianconi goto error;
43079968444SLorenzo Bianconi
43165e6af6cSLorenzo Bianconi mtk_wed_wo_queue_refill(wo, &wo->q_rx, true);
43279968444SLorenzo Bianconi mtk_wed_wo_queue_reset(wo, &wo->q_rx);
43379968444SLorenzo Bianconi
43479968444SLorenzo Bianconi /* rx queue irqmask */
43579968444SLorenzo Bianconi mtk_wed_wo_set_isr(wo, wo->mmio.irq_mask);
43679968444SLorenzo Bianconi
43779968444SLorenzo Bianconi return 0;
43879968444SLorenzo Bianconi
43979968444SLorenzo Bianconi error:
44079968444SLorenzo Bianconi devm_free_irq(wo->hw->dev, wo->mmio.irq, wo);
441e22dcbc9SYuan Can error_put:
442e22dcbc9SYuan Can of_node_put(np);
44379968444SLorenzo Bianconi return ret;
44479968444SLorenzo Bianconi }
44579968444SLorenzo Bianconi
44679968444SLorenzo Bianconi static void
mtk_wed_wo_hw_deinit(struct mtk_wed_wo * wo)44779968444SLorenzo Bianconi mtk_wed_wo_hw_deinit(struct mtk_wed_wo *wo)
44879968444SLorenzo Bianconi {
44979968444SLorenzo Bianconi /* disable interrupts */
45079968444SLorenzo Bianconi mtk_wed_wo_set_isr(wo, 0);
45179968444SLorenzo Bianconi
45279968444SLorenzo Bianconi tasklet_disable(&wo->mmio.irq_tasklet);
45379968444SLorenzo Bianconi
45479968444SLorenzo Bianconi disable_irq(wo->mmio.irq);
45579968444SLorenzo Bianconi devm_free_irq(wo->hw->dev, wo->mmio.irq, wo);
45679968444SLorenzo Bianconi
45779968444SLorenzo Bianconi mtk_wed_wo_queue_tx_clean(wo, &wo->q_tx);
45879968444SLorenzo Bianconi mtk_wed_wo_queue_rx_clean(wo, &wo->q_rx);
45979968444SLorenzo Bianconi mtk_wed_wo_queue_free(wo, &wo->q_tx);
46079968444SLorenzo Bianconi mtk_wed_wo_queue_free(wo, &wo->q_rx);
46179968444SLorenzo Bianconi }
46279968444SLorenzo Bianconi
mtk_wed_wo_init(struct mtk_wed_hw * hw)46379968444SLorenzo Bianconi int mtk_wed_wo_init(struct mtk_wed_hw *hw)
46479968444SLorenzo Bianconi {
46579968444SLorenzo Bianconi struct mtk_wed_wo *wo;
46679968444SLorenzo Bianconi int ret;
46779968444SLorenzo Bianconi
46879968444SLorenzo Bianconi wo = devm_kzalloc(hw->dev, sizeof(*wo), GFP_KERNEL);
46979968444SLorenzo Bianconi if (!wo)
47079968444SLorenzo Bianconi return -ENOMEM;
47179968444SLorenzo Bianconi
47279968444SLorenzo Bianconi hw->wed_wo = wo;
47379968444SLorenzo Bianconi wo->hw = hw;
47479968444SLorenzo Bianconi
47579968444SLorenzo Bianconi ret = mtk_wed_wo_hardware_init(wo);
47679968444SLorenzo Bianconi if (ret)
47779968444SLorenzo Bianconi return ret;
47879968444SLorenzo Bianconi
47979968444SLorenzo Bianconi ret = mtk_wed_mcu_init(wo);
48079968444SLorenzo Bianconi if (ret)
48179968444SLorenzo Bianconi return ret;
48279968444SLorenzo Bianconi
48379968444SLorenzo Bianconi return mtk_wed_wo_exception_init(wo);
48479968444SLorenzo Bianconi }
48579968444SLorenzo Bianconi
mtk_wed_wo_deinit(struct mtk_wed_hw * hw)48679968444SLorenzo Bianconi void mtk_wed_wo_deinit(struct mtk_wed_hw *hw)
48779968444SLorenzo Bianconi {
48879968444SLorenzo Bianconi struct mtk_wed_wo *wo = hw->wed_wo;
48979968444SLorenzo Bianconi
49079968444SLorenzo Bianconi mtk_wed_wo_hw_deinit(wo);
49179968444SLorenzo Bianconi }
492