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 ®s); 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 ®s); 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