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 				     &regs);
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 				     &regs);
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