162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (C) 2022 MediaTek Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Author: Lorenzo Bianconi <lorenzo@kernel.org>
562306a36Sopenharmony_ci *	   Sujuan Chen <sujuan.chen@mediatek.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/of_irq.h>
1462306a36Sopenharmony_ci#include <linux/bitfield.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "mtk_wed.h"
1762306a36Sopenharmony_ci#include "mtk_wed_regs.h"
1862306a36Sopenharmony_ci#include "mtk_wed_wo.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic u32
2162306a36Sopenharmony_cimtk_wed_mmio_r32(struct mtk_wed_wo *wo, u32 reg)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	u32 val;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	if (regmap_read(wo->mmio.regs, reg, &val))
2662306a36Sopenharmony_ci		val = ~0;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	return val;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void
3262306a36Sopenharmony_cimtk_wed_mmio_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	regmap_write(wo->mmio.regs, reg, val);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic u32
3862306a36Sopenharmony_cimtk_wed_wo_get_isr(struct mtk_wed_wo *wo)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	u32 val = mtk_wed_mmio_r32(wo, MTK_WED_WO_CCIF_RCHNUM);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return val & MTK_WED_WO_CCIF_RCHNUM_MASK;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void
4662306a36Sopenharmony_cimtk_wed_wo_set_isr(struct mtk_wed_wo *wo, u32 mask)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_IRQ0_MASK, mask);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void
5262306a36Sopenharmony_cimtk_wed_wo_set_ack(struct mtk_wed_wo *wo, u32 mask)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_ACK, mask);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void
5862306a36Sopenharmony_cimtk_wed_wo_set_isr_mask(struct mtk_wed_wo *wo, u32 mask, u32 val, bool set)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	unsigned long flags;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	spin_lock_irqsave(&wo->mmio.lock, flags);
6362306a36Sopenharmony_ci	wo->mmio.irq_mask &= ~mask;
6462306a36Sopenharmony_ci	wo->mmio.irq_mask |= val;
6562306a36Sopenharmony_ci	if (set)
6662306a36Sopenharmony_ci		mtk_wed_wo_set_isr(wo, wo->mmio.irq_mask);
6762306a36Sopenharmony_ci	spin_unlock_irqrestore(&wo->mmio.lock, flags);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void
7162306a36Sopenharmony_cimtk_wed_wo_irq_enable(struct mtk_wed_wo *wo, u32 mask)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	mtk_wed_wo_set_isr_mask(wo, 0, mask, false);
7462306a36Sopenharmony_ci	tasklet_schedule(&wo->mmio.irq_tasklet);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void
7862306a36Sopenharmony_cimtk_wed_wo_irq_disable(struct mtk_wed_wo *wo, u32 mask)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	mtk_wed_wo_set_isr_mask(wo, mask, 0, true);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void
8462306a36Sopenharmony_cimtk_wed_wo_kickout(struct mtk_wed_wo *wo)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_BUSY, 1 << MTK_WED_WO_TXCH_NUM);
8762306a36Sopenharmony_ci	mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_TCHNUM, MTK_WED_WO_TXCH_NUM);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void
9162306a36Sopenharmony_cimtk_wed_wo_queue_kick(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
9262306a36Sopenharmony_ci		      u32 val)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	wmb();
9562306a36Sopenharmony_ci	mtk_wed_mmio_w32(wo, q->regs.cpu_idx, val);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void *
9962306a36Sopenharmony_cimtk_wed_wo_dequeue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, u32 *len,
10062306a36Sopenharmony_ci		   bool flush)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	int buf_len = SKB_WITH_OVERHEAD(q->buf_size);
10362306a36Sopenharmony_ci	int index = (q->tail + 1) % q->n_desc;
10462306a36Sopenharmony_ci	struct mtk_wed_wo_queue_entry *entry;
10562306a36Sopenharmony_ci	struct mtk_wed_wo_queue_desc *desc;
10662306a36Sopenharmony_ci	void *buf;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (!q->queued)
10962306a36Sopenharmony_ci		return NULL;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (flush)
11262306a36Sopenharmony_ci		q->desc[index].ctrl |= cpu_to_le32(MTK_WED_WO_CTL_DMA_DONE);
11362306a36Sopenharmony_ci	else if (!(q->desc[index].ctrl & cpu_to_le32(MTK_WED_WO_CTL_DMA_DONE)))
11462306a36Sopenharmony_ci		return NULL;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	q->tail = index;
11762306a36Sopenharmony_ci	q->queued--;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	desc = &q->desc[index];
12062306a36Sopenharmony_ci	entry = &q->entry[index];
12162306a36Sopenharmony_ci	buf = entry->buf;
12262306a36Sopenharmony_ci	if (len)
12362306a36Sopenharmony_ci		*len = FIELD_GET(MTK_WED_WO_CTL_SD_LEN0,
12462306a36Sopenharmony_ci				 le32_to_cpu(READ_ONCE(desc->ctrl)));
12562306a36Sopenharmony_ci	if (buf)
12662306a36Sopenharmony_ci		dma_unmap_single(wo->hw->dev, entry->addr, buf_len,
12762306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
12862306a36Sopenharmony_ci	entry->buf = NULL;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return buf;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int
13462306a36Sopenharmony_cimtk_wed_wo_queue_refill(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
13562306a36Sopenharmony_ci			bool rx)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
13862306a36Sopenharmony_ci	int n_buf = 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	while (q->queued < q->n_desc) {
14162306a36Sopenharmony_ci		struct mtk_wed_wo_queue_entry *entry;
14262306a36Sopenharmony_ci		dma_addr_t addr;
14362306a36Sopenharmony_ci		void *buf;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		buf = page_frag_alloc(&q->cache, q->buf_size, GFP_ATOMIC);
14662306a36Sopenharmony_ci		if (!buf)
14762306a36Sopenharmony_ci			break;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		addr = dma_map_single(wo->hw->dev, buf, q->buf_size, dir);
15062306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(wo->hw->dev, addr))) {
15162306a36Sopenharmony_ci			skb_free_frag(buf);
15262306a36Sopenharmony_ci			break;
15362306a36Sopenharmony_ci		}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		q->head = (q->head + 1) % q->n_desc;
15662306a36Sopenharmony_ci		entry = &q->entry[q->head];
15762306a36Sopenharmony_ci		entry->addr = addr;
15862306a36Sopenharmony_ci		entry->len = q->buf_size;
15962306a36Sopenharmony_ci		q->entry[q->head].buf = buf;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		if (rx) {
16262306a36Sopenharmony_ci			struct mtk_wed_wo_queue_desc *desc = &q->desc[q->head];
16362306a36Sopenharmony_ci			u32 ctrl = MTK_WED_WO_CTL_LAST_SEC0 |
16462306a36Sopenharmony_ci				   FIELD_PREP(MTK_WED_WO_CTL_SD_LEN0,
16562306a36Sopenharmony_ci					      entry->len);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci			WRITE_ONCE(desc->buf0, cpu_to_le32(addr));
16862306a36Sopenharmony_ci			WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci		q->queued++;
17162306a36Sopenharmony_ci		n_buf++;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return n_buf;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void
17862306a36Sopenharmony_cimtk_wed_wo_rx_complete(struct mtk_wed_wo *wo)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	mtk_wed_wo_set_ack(wo, MTK_WED_WO_RXCH_INT_MASK);
18162306a36Sopenharmony_ci	mtk_wed_wo_irq_enable(wo, MTK_WED_WO_RXCH_INT_MASK);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic void
18562306a36Sopenharmony_cimtk_wed_wo_rx_run_queue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	for (;;) {
18862306a36Sopenharmony_ci		struct mtk_wed_mcu_hdr *hdr;
18962306a36Sopenharmony_ci		struct sk_buff *skb;
19062306a36Sopenharmony_ci		void *data;
19162306a36Sopenharmony_ci		u32 len;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		data = mtk_wed_wo_dequeue(wo, q, &len, false);
19462306a36Sopenharmony_ci		if (!data)
19562306a36Sopenharmony_ci			break;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		skb = build_skb(data, q->buf_size);
19862306a36Sopenharmony_ci		if (!skb) {
19962306a36Sopenharmony_ci			skb_free_frag(data);
20062306a36Sopenharmony_ci			continue;
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		__skb_put(skb, len);
20462306a36Sopenharmony_ci		if (mtk_wed_mcu_check_msg(wo, skb)) {
20562306a36Sopenharmony_ci			dev_kfree_skb(skb);
20662306a36Sopenharmony_ci			continue;
20762306a36Sopenharmony_ci		}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		hdr = (struct mtk_wed_mcu_hdr *)skb->data;
21062306a36Sopenharmony_ci		if (hdr->flag & cpu_to_le16(MTK_WED_WARP_CMD_FLAG_RSP))
21162306a36Sopenharmony_ci			mtk_wed_mcu_rx_event(wo, skb);
21262306a36Sopenharmony_ci		else
21362306a36Sopenharmony_ci			mtk_wed_mcu_rx_unsolicited_event(wo, skb);
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (mtk_wed_wo_queue_refill(wo, q, true)) {
21762306a36Sopenharmony_ci		u32 index = (q->head - 1) % q->n_desc;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		mtk_wed_wo_queue_kick(wo, q, index);
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic irqreturn_t
22462306a36Sopenharmony_cimtk_wed_wo_irq_handler(int irq, void *data)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct mtk_wed_wo *wo = data;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	mtk_wed_wo_set_isr(wo, 0);
22962306a36Sopenharmony_ci	tasklet_schedule(&wo->mmio.irq_tasklet);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return IRQ_HANDLED;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic void mtk_wed_wo_irq_tasklet(struct tasklet_struct *t)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct mtk_wed_wo *wo = from_tasklet(wo, t, mmio.irq_tasklet);
23762306a36Sopenharmony_ci	u32 intr, mask;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* disable interrupts */
24062306a36Sopenharmony_ci	mtk_wed_wo_set_isr(wo, 0);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	intr = mtk_wed_wo_get_isr(wo);
24362306a36Sopenharmony_ci	intr &= wo->mmio.irq_mask;
24462306a36Sopenharmony_ci	mask = intr & (MTK_WED_WO_RXCH_INT_MASK | MTK_WED_WO_EXCEPTION_INT_MASK);
24562306a36Sopenharmony_ci	mtk_wed_wo_irq_disable(wo, mask);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (intr & MTK_WED_WO_RXCH_INT_MASK) {
24862306a36Sopenharmony_ci		mtk_wed_wo_rx_run_queue(wo, &wo->q_rx);
24962306a36Sopenharmony_ci		mtk_wed_wo_rx_complete(wo);
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/* mtk wed wo hw queues */
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic int
25662306a36Sopenharmony_cimtk_wed_wo_queue_alloc(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
25762306a36Sopenharmony_ci		       int n_desc, int buf_size, int index,
25862306a36Sopenharmony_ci		       struct mtk_wed_wo_queue_regs *regs)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	q->regs = *regs;
26162306a36Sopenharmony_ci	q->n_desc = n_desc;
26262306a36Sopenharmony_ci	q->buf_size = buf_size;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	q->desc = dmam_alloc_coherent(wo->hw->dev, n_desc * sizeof(*q->desc),
26562306a36Sopenharmony_ci				      &q->desc_dma, GFP_KERNEL);
26662306a36Sopenharmony_ci	if (!q->desc)
26762306a36Sopenharmony_ci		return -ENOMEM;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	q->entry = devm_kzalloc(wo->hw->dev, n_desc * sizeof(*q->entry),
27062306a36Sopenharmony_ci				GFP_KERNEL);
27162306a36Sopenharmony_ci	if (!q->entry)
27262306a36Sopenharmony_ci		return -ENOMEM;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return 0;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void
27862306a36Sopenharmony_cimtk_wed_wo_queue_free(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	mtk_wed_mmio_w32(wo, q->regs.cpu_idx, 0);
28162306a36Sopenharmony_ci	dma_free_coherent(wo->hw->dev, q->n_desc * sizeof(*q->desc), q->desc,
28262306a36Sopenharmony_ci			  q->desc_dma);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic void
28662306a36Sopenharmony_cimtk_wed_wo_queue_tx_clean(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct page *page;
28962306a36Sopenharmony_ci	int i;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	for (i = 0; i < q->n_desc; i++) {
29262306a36Sopenharmony_ci		struct mtk_wed_wo_queue_entry *entry = &q->entry[i];
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		if (!entry->buf)
29562306a36Sopenharmony_ci			continue;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		dma_unmap_single(wo->hw->dev, entry->addr, entry->len,
29862306a36Sopenharmony_ci				 DMA_TO_DEVICE);
29962306a36Sopenharmony_ci		skb_free_frag(entry->buf);
30062306a36Sopenharmony_ci		entry->buf = NULL;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (!q->cache.va)
30462306a36Sopenharmony_ci		return;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	page = virt_to_page(q->cache.va);
30762306a36Sopenharmony_ci	__page_frag_cache_drain(page, q->cache.pagecnt_bias);
30862306a36Sopenharmony_ci	memset(&q->cache, 0, sizeof(q->cache));
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic void
31262306a36Sopenharmony_cimtk_wed_wo_queue_rx_clean(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct page *page;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	for (;;) {
31762306a36Sopenharmony_ci		void *buf = mtk_wed_wo_dequeue(wo, q, NULL, true);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		if (!buf)
32062306a36Sopenharmony_ci			break;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		skb_free_frag(buf);
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (!q->cache.va)
32662306a36Sopenharmony_ci		return;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	page = virt_to_page(q->cache.va);
32962306a36Sopenharmony_ci	__page_frag_cache_drain(page, q->cache.pagecnt_bias);
33062306a36Sopenharmony_ci	memset(&q->cache, 0, sizeof(q->cache));
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic void
33462306a36Sopenharmony_cimtk_wed_wo_queue_reset(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	mtk_wed_mmio_w32(wo, q->regs.cpu_idx, 0);
33762306a36Sopenharmony_ci	mtk_wed_mmio_w32(wo, q->regs.desc_base, q->desc_dma);
33862306a36Sopenharmony_ci	mtk_wed_mmio_w32(wo, q->regs.ring_size, q->n_desc);
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ciint mtk_wed_wo_queue_tx_skb(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
34262306a36Sopenharmony_ci			    struct sk_buff *skb)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct mtk_wed_wo_queue_entry *entry;
34562306a36Sopenharmony_ci	struct mtk_wed_wo_queue_desc *desc;
34662306a36Sopenharmony_ci	int ret = 0, index;
34762306a36Sopenharmony_ci	u32 ctrl;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	q->tail = mtk_wed_mmio_r32(wo, q->regs.dma_idx);
35062306a36Sopenharmony_ci	index = (q->head + 1) % q->n_desc;
35162306a36Sopenharmony_ci	if (q->tail == index) {
35262306a36Sopenharmony_ci		ret = -ENOMEM;
35362306a36Sopenharmony_ci		goto out;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	entry = &q->entry[index];
35762306a36Sopenharmony_ci	if (skb->len > entry->len) {
35862306a36Sopenharmony_ci		ret = -ENOMEM;
35962306a36Sopenharmony_ci		goto out;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	desc = &q->desc[index];
36362306a36Sopenharmony_ci	q->head = index;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	dma_sync_single_for_cpu(wo->hw->dev, entry->addr, skb->len,
36662306a36Sopenharmony_ci				DMA_TO_DEVICE);
36762306a36Sopenharmony_ci	memcpy(entry->buf, skb->data, skb->len);
36862306a36Sopenharmony_ci	dma_sync_single_for_device(wo->hw->dev, entry->addr, skb->len,
36962306a36Sopenharmony_ci				   DMA_TO_DEVICE);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	ctrl = FIELD_PREP(MTK_WED_WO_CTL_SD_LEN0, skb->len) |
37262306a36Sopenharmony_ci	       MTK_WED_WO_CTL_LAST_SEC0 | MTK_WED_WO_CTL_DMA_DONE;
37362306a36Sopenharmony_ci	WRITE_ONCE(desc->buf0, cpu_to_le32(entry->addr));
37462306a36Sopenharmony_ci	WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	mtk_wed_wo_queue_kick(wo, q, q->head);
37762306a36Sopenharmony_ci	mtk_wed_wo_kickout(wo);
37862306a36Sopenharmony_ciout:
37962306a36Sopenharmony_ci	dev_kfree_skb(skb);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return ret;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int
38562306a36Sopenharmony_cimtk_wed_wo_exception_init(struct mtk_wed_wo *wo)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	return 0;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic int
39162306a36Sopenharmony_cimtk_wed_wo_hardware_init(struct mtk_wed_wo *wo)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct mtk_wed_wo_queue_regs regs;
39462306a36Sopenharmony_ci	struct device_node *np;
39562306a36Sopenharmony_ci	int ret;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	np = of_parse_phandle(wo->hw->node, "mediatek,wo-ccif", 0);
39862306a36Sopenharmony_ci	if (!np)
39962306a36Sopenharmony_ci		return -ENODEV;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	wo->mmio.regs = syscon_regmap_lookup_by_phandle(np, NULL);
40262306a36Sopenharmony_ci	if (IS_ERR(wo->mmio.regs)) {
40362306a36Sopenharmony_ci		ret = PTR_ERR(wo->mmio.regs);
40462306a36Sopenharmony_ci		goto error_put;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	wo->mmio.irq = irq_of_parse_and_map(np, 0);
40862306a36Sopenharmony_ci	wo->mmio.irq_mask = MTK_WED_WO_ALL_INT_MASK;
40962306a36Sopenharmony_ci	spin_lock_init(&wo->mmio.lock);
41062306a36Sopenharmony_ci	tasklet_setup(&wo->mmio.irq_tasklet, mtk_wed_wo_irq_tasklet);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	ret = devm_request_irq(wo->hw->dev, wo->mmio.irq,
41362306a36Sopenharmony_ci			       mtk_wed_wo_irq_handler, IRQF_TRIGGER_HIGH,
41462306a36Sopenharmony_ci			       KBUILD_MODNAME, wo);
41562306a36Sopenharmony_ci	if (ret)
41662306a36Sopenharmony_ci		goto error;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	regs.desc_base = MTK_WED_WO_CCIF_DUMMY1;
41962306a36Sopenharmony_ci	regs.ring_size = MTK_WED_WO_CCIF_DUMMY2;
42062306a36Sopenharmony_ci	regs.dma_idx = MTK_WED_WO_CCIF_SHADOW4;
42162306a36Sopenharmony_ci	regs.cpu_idx = MTK_WED_WO_CCIF_DUMMY3;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	ret = mtk_wed_wo_queue_alloc(wo, &wo->q_tx, MTK_WED_WO_RING_SIZE,
42462306a36Sopenharmony_ci				     MTK_WED_WO_CMD_LEN, MTK_WED_WO_TXCH_NUM,
42562306a36Sopenharmony_ci				     &regs);
42662306a36Sopenharmony_ci	if (ret)
42762306a36Sopenharmony_ci		goto error;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	mtk_wed_wo_queue_refill(wo, &wo->q_tx, false);
43062306a36Sopenharmony_ci	mtk_wed_wo_queue_reset(wo, &wo->q_tx);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	regs.desc_base = MTK_WED_WO_CCIF_DUMMY5;
43362306a36Sopenharmony_ci	regs.ring_size = MTK_WED_WO_CCIF_DUMMY6;
43462306a36Sopenharmony_ci	regs.dma_idx = MTK_WED_WO_CCIF_SHADOW8;
43562306a36Sopenharmony_ci	regs.cpu_idx = MTK_WED_WO_CCIF_DUMMY7;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	ret = mtk_wed_wo_queue_alloc(wo, &wo->q_rx, MTK_WED_WO_RING_SIZE,
43862306a36Sopenharmony_ci				     MTK_WED_WO_CMD_LEN, MTK_WED_WO_RXCH_NUM,
43962306a36Sopenharmony_ci				     &regs);
44062306a36Sopenharmony_ci	if (ret)
44162306a36Sopenharmony_ci		goto error;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	mtk_wed_wo_queue_refill(wo, &wo->q_rx, true);
44462306a36Sopenharmony_ci	mtk_wed_wo_queue_reset(wo, &wo->q_rx);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/* rx queue irqmask */
44762306a36Sopenharmony_ci	mtk_wed_wo_set_isr(wo, wo->mmio.irq_mask);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return 0;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cierror:
45262306a36Sopenharmony_ci	devm_free_irq(wo->hw->dev, wo->mmio.irq, wo);
45362306a36Sopenharmony_cierror_put:
45462306a36Sopenharmony_ci	of_node_put(np);
45562306a36Sopenharmony_ci	return ret;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic void
45962306a36Sopenharmony_cimtk_wed_wo_hw_deinit(struct mtk_wed_wo *wo)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	/* disable interrupts */
46262306a36Sopenharmony_ci	mtk_wed_wo_set_isr(wo, 0);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	tasklet_disable(&wo->mmio.irq_tasklet);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	disable_irq(wo->mmio.irq);
46762306a36Sopenharmony_ci	devm_free_irq(wo->hw->dev, wo->mmio.irq, wo);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	mtk_wed_wo_queue_tx_clean(wo, &wo->q_tx);
47062306a36Sopenharmony_ci	mtk_wed_wo_queue_rx_clean(wo, &wo->q_rx);
47162306a36Sopenharmony_ci	mtk_wed_wo_queue_free(wo, &wo->q_tx);
47262306a36Sopenharmony_ci	mtk_wed_wo_queue_free(wo, &wo->q_rx);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ciint mtk_wed_wo_init(struct mtk_wed_hw *hw)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct mtk_wed_wo *wo;
47862306a36Sopenharmony_ci	int ret;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	wo = devm_kzalloc(hw->dev, sizeof(*wo), GFP_KERNEL);
48162306a36Sopenharmony_ci	if (!wo)
48262306a36Sopenharmony_ci		return -ENOMEM;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	hw->wed_wo = wo;
48562306a36Sopenharmony_ci	wo->hw = hw;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ret = mtk_wed_wo_hardware_init(wo);
48862306a36Sopenharmony_ci	if (ret)
48962306a36Sopenharmony_ci		return ret;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	ret = mtk_wed_mcu_init(wo);
49262306a36Sopenharmony_ci	if (ret)
49362306a36Sopenharmony_ci		return ret;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return mtk_wed_wo_exception_init(wo);
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_civoid mtk_wed_wo_deinit(struct mtk_wed_hw *hw)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct mtk_wed_wo *wo = hw->wed_wo;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	mtk_wed_wo_hw_deinit(wo);
50362306a36Sopenharmony_ci}
504