162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2021, MediaTek Inc.
462306a36Sopenharmony_ci * Copyright (c) 2021-2022, Intel Corporation.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Authors:
762306a36Sopenharmony_ci *  Amir Hanania <amir.hanania@intel.com>
862306a36Sopenharmony_ci *  Haijun Liu <haijun.liu@mediatek.com>
962306a36Sopenharmony_ci *  Moises Veleta <moises.veleta@intel.com>
1062306a36Sopenharmony_ci *  Ricardo Martinez <ricardo.martinez@linux.intel.com>
1162306a36Sopenharmony_ci *  Sreehari Kancharla <sreehari.kancharla@intel.com>
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Contributors:
1462306a36Sopenharmony_ci *  Andy Shevchenko <andriy.shevchenko@linux.intel.com>
1562306a36Sopenharmony_ci *  Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com>
1662306a36Sopenharmony_ci *  Eliot Lee <eliot.lee@intel.com>
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/bits.h>
2062306a36Sopenharmony_ci#include <linux/bitops.h>
2162306a36Sopenharmony_ci#include <linux/delay.h>
2262306a36Sopenharmony_ci#include <linux/device.h>
2362306a36Sopenharmony_ci#include <linux/dmapool.h>
2462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2562306a36Sopenharmony_ci#include <linux/dma-direction.h>
2662306a36Sopenharmony_ci#include <linux/gfp.h>
2762306a36Sopenharmony_ci#include <linux/io.h>
2862306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h>
2962306a36Sopenharmony_ci#include <linux/iopoll.h>
3062306a36Sopenharmony_ci#include <linux/irqreturn.h>
3162306a36Sopenharmony_ci#include <linux/kernel.h>
3262306a36Sopenharmony_ci#include <linux/kthread.h>
3362306a36Sopenharmony_ci#include <linux/list.h>
3462306a36Sopenharmony_ci#include <linux/netdevice.h>
3562306a36Sopenharmony_ci#include <linux/pci.h>
3662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
3762306a36Sopenharmony_ci#include <linux/sched.h>
3862306a36Sopenharmony_ci#include <linux/skbuff.h>
3962306a36Sopenharmony_ci#include <linux/slab.h>
4062306a36Sopenharmony_ci#include <linux/spinlock.h>
4162306a36Sopenharmony_ci#include <linux/types.h>
4262306a36Sopenharmony_ci#include <linux/wait.h>
4362306a36Sopenharmony_ci#include <linux/workqueue.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include "t7xx_cldma.h"
4662306a36Sopenharmony_ci#include "t7xx_hif_cldma.h"
4762306a36Sopenharmony_ci#include "t7xx_mhccif.h"
4862306a36Sopenharmony_ci#include "t7xx_pci.h"
4962306a36Sopenharmony_ci#include "t7xx_pcie_mac.h"
5062306a36Sopenharmony_ci#include "t7xx_port_proxy.h"
5162306a36Sopenharmony_ci#include "t7xx_reg.h"
5262306a36Sopenharmony_ci#include "t7xx_state_monitor.h"
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define MAX_TX_BUDGET			16
5562306a36Sopenharmony_ci#define MAX_RX_BUDGET			16
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define CHECK_Q_STOP_TIMEOUT_US		1000000
5862306a36Sopenharmony_ci#define CHECK_Q_STOP_STEP_US		10000
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define CLDMA_JUMBO_BUFF_SZ		(63 * 1024 + sizeof(struct ccci_header))
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void md_cd_queue_struct_reset(struct cldma_queue *queue, struct cldma_ctrl *md_ctrl,
6362306a36Sopenharmony_ci				     enum mtk_txrx tx_rx, unsigned int index)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	queue->dir = tx_rx;
6662306a36Sopenharmony_ci	queue->index = index;
6762306a36Sopenharmony_ci	queue->md_ctrl = md_ctrl;
6862306a36Sopenharmony_ci	queue->tr_ring = NULL;
6962306a36Sopenharmony_ci	queue->tr_done = NULL;
7062306a36Sopenharmony_ci	queue->tx_next = NULL;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void md_cd_queue_struct_init(struct cldma_queue *queue, struct cldma_ctrl *md_ctrl,
7462306a36Sopenharmony_ci				    enum mtk_txrx tx_rx, unsigned int index)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	md_cd_queue_struct_reset(queue, md_ctrl, tx_rx, index);
7762306a36Sopenharmony_ci	init_waitqueue_head(&queue->req_wq);
7862306a36Sopenharmony_ci	spin_lock_init(&queue->ring_lock);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic void t7xx_cldma_gpd_set_data_ptr(struct cldma_gpd *gpd, dma_addr_t data_ptr)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	gpd->data_buff_bd_ptr_h = cpu_to_le32(upper_32_bits(data_ptr));
8462306a36Sopenharmony_ci	gpd->data_buff_bd_ptr_l = cpu_to_le32(lower_32_bits(data_ptr));
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void t7xx_cldma_gpd_set_next_ptr(struct cldma_gpd *gpd, dma_addr_t next_ptr)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	gpd->next_gpd_ptr_h = cpu_to_le32(upper_32_bits(next_ptr));
9062306a36Sopenharmony_ci	gpd->next_gpd_ptr_l = cpu_to_le32(lower_32_bits(next_ptr));
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int t7xx_cldma_alloc_and_map_skb(struct cldma_ctrl *md_ctrl, struct cldma_request *req,
9462306a36Sopenharmony_ci					size_t size, gfp_t gfp_mask)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	req->skb = __dev_alloc_skb(size, gfp_mask);
9762306a36Sopenharmony_ci	if (!req->skb)
9862306a36Sopenharmony_ci		return -ENOMEM;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	req->mapped_buff = dma_map_single(md_ctrl->dev, req->skb->data, size, DMA_FROM_DEVICE);
10162306a36Sopenharmony_ci	if (dma_mapping_error(md_ctrl->dev, req->mapped_buff)) {
10262306a36Sopenharmony_ci		dev_kfree_skb_any(req->skb);
10362306a36Sopenharmony_ci		req->skb = NULL;
10462306a36Sopenharmony_ci		req->mapped_buff = 0;
10562306a36Sopenharmony_ci		dev_err(md_ctrl->dev, "DMA mapping failed\n");
10662306a36Sopenharmony_ci		return -ENOMEM;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int t7xx_cldma_gpd_rx_from_q(struct cldma_queue *queue, int budget, bool *over_budget)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = queue->md_ctrl;
11562306a36Sopenharmony_ci	unsigned int hwo_polling_count = 0;
11662306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info;
11762306a36Sopenharmony_ci	bool rx_not_done = true;
11862306a36Sopenharmony_ci	unsigned long flags;
11962306a36Sopenharmony_ci	int count = 0;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	hw_info = &md_ctrl->hw_info;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	do {
12462306a36Sopenharmony_ci		struct cldma_request *req;
12562306a36Sopenharmony_ci		struct cldma_gpd *gpd;
12662306a36Sopenharmony_ci		struct sk_buff *skb;
12762306a36Sopenharmony_ci		int ret;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		req = queue->tr_done;
13062306a36Sopenharmony_ci		if (!req)
13162306a36Sopenharmony_ci			return -ENODATA;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		gpd = req->gpd;
13462306a36Sopenharmony_ci		if ((gpd->flags & GPD_FLAGS_HWO) || !req->skb) {
13562306a36Sopenharmony_ci			dma_addr_t gpd_addr;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci			if (!pci_device_is_present(to_pci_dev(md_ctrl->dev))) {
13862306a36Sopenharmony_ci				dev_err(md_ctrl->dev, "PCIe Link disconnected\n");
13962306a36Sopenharmony_ci				return -ENODEV;
14062306a36Sopenharmony_ci			}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci			gpd_addr = ioread64(hw_info->ap_pdn_base + REG_CLDMA_DL_CURRENT_ADDRL_0 +
14362306a36Sopenharmony_ci					    queue->index * sizeof(u64));
14462306a36Sopenharmony_ci			if (req->gpd_addr == gpd_addr || hwo_polling_count++ >= 100)
14562306a36Sopenharmony_ci				return 0;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci			udelay(1);
14862306a36Sopenharmony_ci			continue;
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		hwo_polling_count = 0;
15262306a36Sopenharmony_ci		skb = req->skb;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		if (req->mapped_buff) {
15562306a36Sopenharmony_ci			dma_unmap_single(md_ctrl->dev, req->mapped_buff,
15662306a36Sopenharmony_ci					 queue->tr_ring->pkt_size, DMA_FROM_DEVICE);
15762306a36Sopenharmony_ci			req->mapped_buff = 0;
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		skb->len = 0;
16162306a36Sopenharmony_ci		skb_reset_tail_pointer(skb);
16262306a36Sopenharmony_ci		skb_put(skb, le16_to_cpu(gpd->data_buff_len));
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		ret = md_ctrl->recv_skb(queue, skb);
16562306a36Sopenharmony_ci		/* Break processing, will try again later */
16662306a36Sopenharmony_ci		if (ret < 0)
16762306a36Sopenharmony_ci			return ret;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci		req->skb = NULL;
17062306a36Sopenharmony_ci		t7xx_cldma_gpd_set_data_ptr(gpd, 0);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		spin_lock_irqsave(&queue->ring_lock, flags);
17362306a36Sopenharmony_ci		queue->tr_done = list_next_entry_circular(req, &queue->tr_ring->gpd_ring, entry);
17462306a36Sopenharmony_ci		spin_unlock_irqrestore(&queue->ring_lock, flags);
17562306a36Sopenharmony_ci		req = queue->rx_refill;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		ret = t7xx_cldma_alloc_and_map_skb(md_ctrl, req, queue->tr_ring->pkt_size, GFP_KERNEL);
17862306a36Sopenharmony_ci		if (ret)
17962306a36Sopenharmony_ci			return ret;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		gpd = req->gpd;
18262306a36Sopenharmony_ci		t7xx_cldma_gpd_set_data_ptr(gpd, req->mapped_buff);
18362306a36Sopenharmony_ci		gpd->data_buff_len = 0;
18462306a36Sopenharmony_ci		gpd->flags = GPD_FLAGS_IOC | GPD_FLAGS_HWO;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		spin_lock_irqsave(&queue->ring_lock, flags);
18762306a36Sopenharmony_ci		queue->rx_refill = list_next_entry_circular(req, &queue->tr_ring->gpd_ring, entry);
18862306a36Sopenharmony_ci		spin_unlock_irqrestore(&queue->ring_lock, flags);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci		rx_not_done = ++count < budget || !need_resched();
19162306a36Sopenharmony_ci	} while (rx_not_done);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	*over_budget = true;
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int t7xx_cldma_gpd_rx_collect(struct cldma_queue *queue, int budget)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = queue->md_ctrl;
20062306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info;
20162306a36Sopenharmony_ci	unsigned int pending_rx_int;
20262306a36Sopenharmony_ci	bool over_budget = false;
20362306a36Sopenharmony_ci	unsigned long flags;
20462306a36Sopenharmony_ci	int ret;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	hw_info = &md_ctrl->hw_info;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	do {
20962306a36Sopenharmony_ci		ret = t7xx_cldma_gpd_rx_from_q(queue, budget, &over_budget);
21062306a36Sopenharmony_ci		if (ret == -ENODATA)
21162306a36Sopenharmony_ci			return 0;
21262306a36Sopenharmony_ci		else if (ret)
21362306a36Sopenharmony_ci			return ret;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		pending_rx_int = 0;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
21862306a36Sopenharmony_ci		if (md_ctrl->rxq_active & BIT(queue->index)) {
21962306a36Sopenharmony_ci			if (!t7xx_cldma_hw_queue_status(hw_info, queue->index, MTK_RX))
22062306a36Sopenharmony_ci				t7xx_cldma_hw_resume_queue(hw_info, queue->index, MTK_RX);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci			pending_rx_int = t7xx_cldma_hw_int_status(hw_info, BIT(queue->index),
22362306a36Sopenharmony_ci								  MTK_RX);
22462306a36Sopenharmony_ci			if (pending_rx_int) {
22562306a36Sopenharmony_ci				t7xx_cldma_hw_rx_done(hw_info, pending_rx_int);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci				if (over_budget) {
22862306a36Sopenharmony_ci					spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
22962306a36Sopenharmony_ci					return -EAGAIN;
23062306a36Sopenharmony_ci				}
23162306a36Sopenharmony_ci			}
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci		spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
23462306a36Sopenharmony_ci	} while (pending_rx_int);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	return 0;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void t7xx_cldma_rx_done(struct work_struct *work)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct cldma_queue *queue = container_of(work, struct cldma_queue, cldma_work);
24262306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = queue->md_ctrl;
24362306a36Sopenharmony_ci	int value;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	value = t7xx_cldma_gpd_rx_collect(queue, queue->budget);
24662306a36Sopenharmony_ci	if (value && md_ctrl->rxq_active & BIT(queue->index)) {
24762306a36Sopenharmony_ci		queue_work(queue->worker, &queue->cldma_work);
24862306a36Sopenharmony_ci		return;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	t7xx_cldma_clear_ip_busy(&md_ctrl->hw_info);
25262306a36Sopenharmony_ci	t7xx_cldma_hw_irq_en_txrx(&md_ctrl->hw_info, queue->index, MTK_RX);
25362306a36Sopenharmony_ci	t7xx_cldma_hw_irq_en_eq(&md_ctrl->hw_info, queue->index, MTK_RX);
25462306a36Sopenharmony_ci	pm_runtime_mark_last_busy(md_ctrl->dev);
25562306a36Sopenharmony_ci	pm_runtime_put_autosuspend(md_ctrl->dev);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int t7xx_cldma_gpd_tx_collect(struct cldma_queue *queue)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = queue->md_ctrl;
26162306a36Sopenharmony_ci	unsigned int dma_len, count = 0;
26262306a36Sopenharmony_ci	struct cldma_request *req;
26362306a36Sopenharmony_ci	struct cldma_gpd *gpd;
26462306a36Sopenharmony_ci	unsigned long flags;
26562306a36Sopenharmony_ci	dma_addr_t dma_free;
26662306a36Sopenharmony_ci	struct sk_buff *skb;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	while (!kthread_should_stop()) {
26962306a36Sopenharmony_ci		spin_lock_irqsave(&queue->ring_lock, flags);
27062306a36Sopenharmony_ci		req = queue->tr_done;
27162306a36Sopenharmony_ci		if (!req) {
27262306a36Sopenharmony_ci			spin_unlock_irqrestore(&queue->ring_lock, flags);
27362306a36Sopenharmony_ci			break;
27462306a36Sopenharmony_ci		}
27562306a36Sopenharmony_ci		gpd = req->gpd;
27662306a36Sopenharmony_ci		if ((gpd->flags & GPD_FLAGS_HWO) || !req->skb) {
27762306a36Sopenharmony_ci			spin_unlock_irqrestore(&queue->ring_lock, flags);
27862306a36Sopenharmony_ci			break;
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci		queue->budget++;
28162306a36Sopenharmony_ci		dma_free = req->mapped_buff;
28262306a36Sopenharmony_ci		dma_len = le16_to_cpu(gpd->data_buff_len);
28362306a36Sopenharmony_ci		skb = req->skb;
28462306a36Sopenharmony_ci		req->skb = NULL;
28562306a36Sopenharmony_ci		queue->tr_done = list_next_entry_circular(req, &queue->tr_ring->gpd_ring, entry);
28662306a36Sopenharmony_ci		spin_unlock_irqrestore(&queue->ring_lock, flags);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		count++;
28962306a36Sopenharmony_ci		dma_unmap_single(md_ctrl->dev, dma_free, dma_len, DMA_TO_DEVICE);
29062306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (count)
29462306a36Sopenharmony_ci		wake_up_nr(&queue->req_wq, count);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return count;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void t7xx_cldma_txq_empty_hndl(struct cldma_queue *queue)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = queue->md_ctrl;
30262306a36Sopenharmony_ci	struct cldma_request *req;
30362306a36Sopenharmony_ci	dma_addr_t ul_curr_addr;
30462306a36Sopenharmony_ci	unsigned long flags;
30562306a36Sopenharmony_ci	bool pending_gpd;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (!(md_ctrl->txq_active & BIT(queue->index)))
30862306a36Sopenharmony_ci		return;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	spin_lock_irqsave(&queue->ring_lock, flags);
31162306a36Sopenharmony_ci	req = list_prev_entry_circular(queue->tx_next, &queue->tr_ring->gpd_ring, entry);
31262306a36Sopenharmony_ci	spin_unlock_irqrestore(&queue->ring_lock, flags);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	pending_gpd = (req->gpd->flags & GPD_FLAGS_HWO) && req->skb;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
31762306a36Sopenharmony_ci	if (pending_gpd) {
31862306a36Sopenharmony_ci		struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		/* Check current processing TGPD, 64-bit address is in a table by Q index */
32162306a36Sopenharmony_ci		ul_curr_addr = ioread64(hw_info->ap_pdn_base + REG_CLDMA_UL_CURRENT_ADDRL_0 +
32262306a36Sopenharmony_ci					queue->index * sizeof(u64));
32362306a36Sopenharmony_ci		if (req->gpd_addr != ul_curr_addr) {
32462306a36Sopenharmony_ci			spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
32562306a36Sopenharmony_ci			dev_err(md_ctrl->dev, "CLDMA%d queue %d is not empty\n",
32662306a36Sopenharmony_ci				md_ctrl->hif_id, queue->index);
32762306a36Sopenharmony_ci			return;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		t7xx_cldma_hw_resume_queue(hw_info, queue->index, MTK_TX);
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic void t7xx_cldma_tx_done(struct work_struct *work)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct cldma_queue *queue = container_of(work, struct cldma_queue, cldma_work);
33862306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = queue->md_ctrl;
33962306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info;
34062306a36Sopenharmony_ci	unsigned int l2_tx_int;
34162306a36Sopenharmony_ci	unsigned long flags;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	hw_info = &md_ctrl->hw_info;
34462306a36Sopenharmony_ci	t7xx_cldma_gpd_tx_collect(queue);
34562306a36Sopenharmony_ci	l2_tx_int = t7xx_cldma_hw_int_status(hw_info, BIT(queue->index) | EQ_STA_BIT(queue->index),
34662306a36Sopenharmony_ci					     MTK_TX);
34762306a36Sopenharmony_ci	if (l2_tx_int & EQ_STA_BIT(queue->index)) {
34862306a36Sopenharmony_ci		t7xx_cldma_hw_tx_done(hw_info, EQ_STA_BIT(queue->index));
34962306a36Sopenharmony_ci		t7xx_cldma_txq_empty_hndl(queue);
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (l2_tx_int & BIT(queue->index)) {
35362306a36Sopenharmony_ci		t7xx_cldma_hw_tx_done(hw_info, BIT(queue->index));
35462306a36Sopenharmony_ci		queue_work(queue->worker, &queue->cldma_work);
35562306a36Sopenharmony_ci		return;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
35962306a36Sopenharmony_ci	if (md_ctrl->txq_active & BIT(queue->index)) {
36062306a36Sopenharmony_ci		t7xx_cldma_clear_ip_busy(hw_info);
36162306a36Sopenharmony_ci		t7xx_cldma_hw_irq_en_eq(hw_info, queue->index, MTK_TX);
36262306a36Sopenharmony_ci		t7xx_cldma_hw_irq_en_txrx(hw_info, queue->index, MTK_TX);
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	pm_runtime_mark_last_busy(md_ctrl->dev);
36762306a36Sopenharmony_ci	pm_runtime_put_autosuspend(md_ctrl->dev);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void t7xx_cldma_ring_free(struct cldma_ctrl *md_ctrl,
37162306a36Sopenharmony_ci				 struct cldma_ring *ring, enum dma_data_direction tx_rx)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct cldma_request *req_cur, *req_next;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	list_for_each_entry_safe(req_cur, req_next, &ring->gpd_ring, entry) {
37662306a36Sopenharmony_ci		if (req_cur->mapped_buff && req_cur->skb) {
37762306a36Sopenharmony_ci			dma_unmap_single(md_ctrl->dev, req_cur->mapped_buff,
37862306a36Sopenharmony_ci					 ring->pkt_size, tx_rx);
37962306a36Sopenharmony_ci			req_cur->mapped_buff = 0;
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		dev_kfree_skb_any(req_cur->skb);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		if (req_cur->gpd)
38562306a36Sopenharmony_ci			dma_pool_free(md_ctrl->gpd_dmapool, req_cur->gpd, req_cur->gpd_addr);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		list_del(&req_cur->entry);
38862306a36Sopenharmony_ci		kfree(req_cur);
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic struct cldma_request *t7xx_alloc_rx_request(struct cldma_ctrl *md_ctrl, size_t pkt_size)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct cldma_request *req;
39562306a36Sopenharmony_ci	int val;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
39862306a36Sopenharmony_ci	if (!req)
39962306a36Sopenharmony_ci		return NULL;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	req->gpd = dma_pool_zalloc(md_ctrl->gpd_dmapool, GFP_KERNEL, &req->gpd_addr);
40262306a36Sopenharmony_ci	if (!req->gpd)
40362306a36Sopenharmony_ci		goto err_free_req;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	val = t7xx_cldma_alloc_and_map_skb(md_ctrl, req, pkt_size, GFP_KERNEL);
40662306a36Sopenharmony_ci	if (val)
40762306a36Sopenharmony_ci		goto err_free_pool;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return req;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cierr_free_pool:
41262306a36Sopenharmony_ci	dma_pool_free(md_ctrl->gpd_dmapool, req->gpd, req->gpd_addr);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cierr_free_req:
41562306a36Sopenharmony_ci	kfree(req);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return NULL;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic int t7xx_cldma_rx_ring_init(struct cldma_ctrl *md_ctrl, struct cldma_ring *ring)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct cldma_request *req;
42362306a36Sopenharmony_ci	struct cldma_gpd *gpd;
42462306a36Sopenharmony_ci	int i;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	INIT_LIST_HEAD(&ring->gpd_ring);
42762306a36Sopenharmony_ci	ring->length = MAX_RX_BUDGET;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	for (i = 0; i < ring->length; i++) {
43062306a36Sopenharmony_ci		req = t7xx_alloc_rx_request(md_ctrl, ring->pkt_size);
43162306a36Sopenharmony_ci		if (!req) {
43262306a36Sopenharmony_ci			t7xx_cldma_ring_free(md_ctrl, ring, DMA_FROM_DEVICE);
43362306a36Sopenharmony_ci			return -ENOMEM;
43462306a36Sopenharmony_ci		}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		gpd = req->gpd;
43762306a36Sopenharmony_ci		t7xx_cldma_gpd_set_data_ptr(gpd, req->mapped_buff);
43862306a36Sopenharmony_ci		gpd->rx_data_allow_len = cpu_to_le16(ring->pkt_size);
43962306a36Sopenharmony_ci		gpd->flags = GPD_FLAGS_IOC | GPD_FLAGS_HWO;
44062306a36Sopenharmony_ci		INIT_LIST_HEAD(&req->entry);
44162306a36Sopenharmony_ci		list_add_tail(&req->entry, &ring->gpd_ring);
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* Link previous GPD to next GPD, circular */
44562306a36Sopenharmony_ci	list_for_each_entry(req, &ring->gpd_ring, entry) {
44662306a36Sopenharmony_ci		t7xx_cldma_gpd_set_next_ptr(gpd, req->gpd_addr);
44762306a36Sopenharmony_ci		gpd = req->gpd;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	return 0;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic struct cldma_request *t7xx_alloc_tx_request(struct cldma_ctrl *md_ctrl)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	struct cldma_request *req;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
45862306a36Sopenharmony_ci	if (!req)
45962306a36Sopenharmony_ci		return NULL;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	req->gpd = dma_pool_zalloc(md_ctrl->gpd_dmapool, GFP_KERNEL, &req->gpd_addr);
46262306a36Sopenharmony_ci	if (!req->gpd) {
46362306a36Sopenharmony_ci		kfree(req);
46462306a36Sopenharmony_ci		return NULL;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	return req;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int t7xx_cldma_tx_ring_init(struct cldma_ctrl *md_ctrl, struct cldma_ring *ring)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct cldma_request *req;
47362306a36Sopenharmony_ci	struct cldma_gpd *gpd;
47462306a36Sopenharmony_ci	int i;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	INIT_LIST_HEAD(&ring->gpd_ring);
47762306a36Sopenharmony_ci	ring->length = MAX_TX_BUDGET;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	for (i = 0; i < ring->length; i++) {
48062306a36Sopenharmony_ci		req = t7xx_alloc_tx_request(md_ctrl);
48162306a36Sopenharmony_ci		if (!req) {
48262306a36Sopenharmony_ci			t7xx_cldma_ring_free(md_ctrl, ring, DMA_TO_DEVICE);
48362306a36Sopenharmony_ci			return -ENOMEM;
48462306a36Sopenharmony_ci		}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		gpd = req->gpd;
48762306a36Sopenharmony_ci		gpd->flags = GPD_FLAGS_IOC;
48862306a36Sopenharmony_ci		INIT_LIST_HEAD(&req->entry);
48962306a36Sopenharmony_ci		list_add_tail(&req->entry, &ring->gpd_ring);
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/* Link previous GPD to next GPD, circular */
49362306a36Sopenharmony_ci	list_for_each_entry(req, &ring->gpd_ring, entry) {
49462306a36Sopenharmony_ci		t7xx_cldma_gpd_set_next_ptr(gpd, req->gpd_addr);
49562306a36Sopenharmony_ci		gpd = req->gpd;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	return 0;
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci/**
50262306a36Sopenharmony_ci * t7xx_cldma_q_reset() - Reset CLDMA request pointers to their initial values.
50362306a36Sopenharmony_ci * @queue: Pointer to the queue structure.
50462306a36Sopenharmony_ci *
50562306a36Sopenharmony_ci * Called with ring_lock (unless called during initialization phase)
50662306a36Sopenharmony_ci */
50762306a36Sopenharmony_cistatic void t7xx_cldma_q_reset(struct cldma_queue *queue)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct cldma_request *req;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	req = list_first_entry(&queue->tr_ring->gpd_ring, struct cldma_request, entry);
51262306a36Sopenharmony_ci	queue->tr_done = req;
51362306a36Sopenharmony_ci	queue->budget = queue->tr_ring->length;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (queue->dir == MTK_TX)
51662306a36Sopenharmony_ci		queue->tx_next = req;
51762306a36Sopenharmony_ci	else
51862306a36Sopenharmony_ci		queue->rx_refill = req;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic void t7xx_cldma_rxq_init(struct cldma_queue *queue)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = queue->md_ctrl;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	queue->dir = MTK_RX;
52662306a36Sopenharmony_ci	queue->tr_ring = &md_ctrl->rx_ring[queue->index];
52762306a36Sopenharmony_ci	t7xx_cldma_q_reset(queue);
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic void t7xx_cldma_txq_init(struct cldma_queue *queue)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = queue->md_ctrl;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	queue->dir = MTK_TX;
53562306a36Sopenharmony_ci	queue->tr_ring = &md_ctrl->tx_ring[queue->index];
53662306a36Sopenharmony_ci	t7xx_cldma_q_reset(queue);
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic void t7xx_cldma_enable_irq(struct cldma_ctrl *md_ctrl)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	t7xx_pcie_mac_set_int(md_ctrl->t7xx_dev, md_ctrl->hw_info.phy_interrupt_id);
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic void t7xx_cldma_disable_irq(struct cldma_ctrl *md_ctrl)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	t7xx_pcie_mac_clear_int(md_ctrl->t7xx_dev, md_ctrl->hw_info.phy_interrupt_id);
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void t7xx_cldma_irq_work_cb(struct cldma_ctrl *md_ctrl)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	unsigned long l2_tx_int_msk, l2_rx_int_msk, l2_tx_int, l2_rx_int, val;
55262306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
55362306a36Sopenharmony_ci	int i;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* L2 raw interrupt status */
55662306a36Sopenharmony_ci	l2_tx_int = ioread32(hw_info->ap_pdn_base + REG_CLDMA_L2TISAR0);
55762306a36Sopenharmony_ci	l2_rx_int = ioread32(hw_info->ap_pdn_base + REG_CLDMA_L2RISAR0);
55862306a36Sopenharmony_ci	l2_tx_int_msk = ioread32(hw_info->ap_pdn_base + REG_CLDMA_L2TIMR0);
55962306a36Sopenharmony_ci	l2_rx_int_msk = ioread32(hw_info->ap_ao_base + REG_CLDMA_L2RIMR0);
56062306a36Sopenharmony_ci	l2_tx_int &= ~l2_tx_int_msk;
56162306a36Sopenharmony_ci	l2_rx_int &= ~l2_rx_int_msk;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (l2_tx_int) {
56462306a36Sopenharmony_ci		if (l2_tx_int & (TQ_ERR_INT_BITMASK | TQ_ACTIVE_START_ERR_INT_BITMASK)) {
56562306a36Sopenharmony_ci			/* Read and clear L3 TX interrupt status */
56662306a36Sopenharmony_ci			val = ioread32(hw_info->ap_pdn_base + REG_CLDMA_L3TISAR0);
56762306a36Sopenharmony_ci			iowrite32(val, hw_info->ap_pdn_base + REG_CLDMA_L3TISAR0);
56862306a36Sopenharmony_ci			val = ioread32(hw_info->ap_pdn_base + REG_CLDMA_L3TISAR1);
56962306a36Sopenharmony_ci			iowrite32(val, hw_info->ap_pdn_base + REG_CLDMA_L3TISAR1);
57062306a36Sopenharmony_ci		}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci		t7xx_cldma_hw_tx_done(hw_info, l2_tx_int);
57362306a36Sopenharmony_ci		if (l2_tx_int & (TXRX_STATUS_BITMASK | EMPTY_STATUS_BITMASK)) {
57462306a36Sopenharmony_ci			for_each_set_bit(i, &l2_tx_int, L2_INT_BIT_COUNT) {
57562306a36Sopenharmony_ci				if (i < CLDMA_TXQ_NUM) {
57662306a36Sopenharmony_ci					pm_runtime_get(md_ctrl->dev);
57762306a36Sopenharmony_ci					t7xx_cldma_hw_irq_dis_eq(hw_info, i, MTK_TX);
57862306a36Sopenharmony_ci					t7xx_cldma_hw_irq_dis_txrx(hw_info, i, MTK_TX);
57962306a36Sopenharmony_ci					queue_work(md_ctrl->txq[i].worker,
58062306a36Sopenharmony_ci						   &md_ctrl->txq[i].cldma_work);
58162306a36Sopenharmony_ci				} else {
58262306a36Sopenharmony_ci					t7xx_cldma_txq_empty_hndl(&md_ctrl->txq[i - CLDMA_TXQ_NUM]);
58362306a36Sopenharmony_ci				}
58462306a36Sopenharmony_ci			}
58562306a36Sopenharmony_ci		}
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (l2_rx_int) {
58962306a36Sopenharmony_ci		if (l2_rx_int & (RQ_ERR_INT_BITMASK | RQ_ACTIVE_START_ERR_INT_BITMASK)) {
59062306a36Sopenharmony_ci			/* Read and clear L3 RX interrupt status */
59162306a36Sopenharmony_ci			val = ioread32(hw_info->ap_pdn_base + REG_CLDMA_L3RISAR0);
59262306a36Sopenharmony_ci			iowrite32(val, hw_info->ap_pdn_base + REG_CLDMA_L3RISAR0);
59362306a36Sopenharmony_ci			val = ioread32(hw_info->ap_pdn_base + REG_CLDMA_L3RISAR1);
59462306a36Sopenharmony_ci			iowrite32(val, hw_info->ap_pdn_base + REG_CLDMA_L3RISAR1);
59562306a36Sopenharmony_ci		}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		t7xx_cldma_hw_rx_done(hw_info, l2_rx_int);
59862306a36Sopenharmony_ci		if (l2_rx_int & (TXRX_STATUS_BITMASK | EMPTY_STATUS_BITMASK)) {
59962306a36Sopenharmony_ci			l2_rx_int |= l2_rx_int >> CLDMA_RXQ_NUM;
60062306a36Sopenharmony_ci			for_each_set_bit(i, &l2_rx_int, CLDMA_RXQ_NUM) {
60162306a36Sopenharmony_ci				pm_runtime_get(md_ctrl->dev);
60262306a36Sopenharmony_ci				t7xx_cldma_hw_irq_dis_eq(hw_info, i, MTK_RX);
60362306a36Sopenharmony_ci				t7xx_cldma_hw_irq_dis_txrx(hw_info, i, MTK_RX);
60462306a36Sopenharmony_ci				queue_work(md_ctrl->rxq[i].worker, &md_ctrl->rxq[i].cldma_work);
60562306a36Sopenharmony_ci			}
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic bool t7xx_cldma_qs_are_active(struct cldma_ctrl *md_ctrl)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
61362306a36Sopenharmony_ci	unsigned int tx_active;
61462306a36Sopenharmony_ci	unsigned int rx_active;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (!pci_device_is_present(to_pci_dev(md_ctrl->dev)))
61762306a36Sopenharmony_ci		return false;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	tx_active = t7xx_cldma_hw_queue_status(hw_info, CLDMA_ALL_Q, MTK_TX);
62062306a36Sopenharmony_ci	rx_active = t7xx_cldma_hw_queue_status(hw_info, CLDMA_ALL_Q, MTK_RX);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	return tx_active || rx_active;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci/**
62662306a36Sopenharmony_ci * t7xx_cldma_stop() - Stop CLDMA.
62762306a36Sopenharmony_ci * @md_ctrl: CLDMA context structure.
62862306a36Sopenharmony_ci *
62962306a36Sopenharmony_ci * Stop TX and RX queues. Disable L1 and L2 interrupts.
63062306a36Sopenharmony_ci * Clear status registers.
63162306a36Sopenharmony_ci *
63262306a36Sopenharmony_ci * Return:
63362306a36Sopenharmony_ci * * 0		- Success.
63462306a36Sopenharmony_ci * * -ERROR	- Error code from polling cldma_queues_active.
63562306a36Sopenharmony_ci */
63662306a36Sopenharmony_ciint t7xx_cldma_stop(struct cldma_ctrl *md_ctrl)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
63962306a36Sopenharmony_ci	bool active;
64062306a36Sopenharmony_ci	int i, ret;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	md_ctrl->rxq_active = 0;
64362306a36Sopenharmony_ci	t7xx_cldma_hw_stop_all_qs(hw_info, MTK_RX);
64462306a36Sopenharmony_ci	md_ctrl->txq_active = 0;
64562306a36Sopenharmony_ci	t7xx_cldma_hw_stop_all_qs(hw_info, MTK_TX);
64662306a36Sopenharmony_ci	md_ctrl->txq_started = 0;
64762306a36Sopenharmony_ci	t7xx_cldma_disable_irq(md_ctrl);
64862306a36Sopenharmony_ci	t7xx_cldma_hw_stop(hw_info, MTK_RX);
64962306a36Sopenharmony_ci	t7xx_cldma_hw_stop(hw_info, MTK_TX);
65062306a36Sopenharmony_ci	t7xx_cldma_hw_tx_done(hw_info, CLDMA_L2TISAR0_ALL_INT_MASK);
65162306a36Sopenharmony_ci	t7xx_cldma_hw_rx_done(hw_info, CLDMA_L2RISAR0_ALL_INT_MASK);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (md_ctrl->is_late_init) {
65462306a36Sopenharmony_ci		for (i = 0; i < CLDMA_TXQ_NUM; i++)
65562306a36Sopenharmony_ci			flush_work(&md_ctrl->txq[i].cldma_work);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		for (i = 0; i < CLDMA_RXQ_NUM; i++)
65862306a36Sopenharmony_ci			flush_work(&md_ctrl->rxq[i].cldma_work);
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	ret = read_poll_timeout(t7xx_cldma_qs_are_active, active, !active, CHECK_Q_STOP_STEP_US,
66262306a36Sopenharmony_ci				CHECK_Q_STOP_TIMEOUT_US, true, md_ctrl);
66362306a36Sopenharmony_ci	if (ret)
66462306a36Sopenharmony_ci		dev_err(md_ctrl->dev, "Could not stop CLDMA%d queues", md_ctrl->hif_id);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	return ret;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic void t7xx_cldma_late_release(struct cldma_ctrl *md_ctrl)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	int i;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (!md_ctrl->is_late_init)
67462306a36Sopenharmony_ci		return;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	for (i = 0; i < CLDMA_TXQ_NUM; i++)
67762306a36Sopenharmony_ci		t7xx_cldma_ring_free(md_ctrl, &md_ctrl->tx_ring[i], DMA_TO_DEVICE);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	for (i = 0; i < CLDMA_RXQ_NUM; i++)
68062306a36Sopenharmony_ci		t7xx_cldma_ring_free(md_ctrl, &md_ctrl->rx_ring[i], DMA_FROM_DEVICE);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	dma_pool_destroy(md_ctrl->gpd_dmapool);
68362306a36Sopenharmony_ci	md_ctrl->gpd_dmapool = NULL;
68462306a36Sopenharmony_ci	md_ctrl->is_late_init = false;
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_civoid t7xx_cldma_reset(struct cldma_ctrl *md_ctrl)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	unsigned long flags;
69062306a36Sopenharmony_ci	int i;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
69362306a36Sopenharmony_ci	md_ctrl->txq_active = 0;
69462306a36Sopenharmony_ci	md_ctrl->rxq_active = 0;
69562306a36Sopenharmony_ci	t7xx_cldma_disable_irq(md_ctrl);
69662306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	for (i = 0; i < CLDMA_TXQ_NUM; i++) {
69962306a36Sopenharmony_ci		cancel_work_sync(&md_ctrl->txq[i].cldma_work);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
70262306a36Sopenharmony_ci		md_cd_queue_struct_reset(&md_ctrl->txq[i], md_ctrl, MTK_TX, i);
70362306a36Sopenharmony_ci		spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	for (i = 0; i < CLDMA_RXQ_NUM; i++) {
70762306a36Sopenharmony_ci		cancel_work_sync(&md_ctrl->rxq[i].cldma_work);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
71062306a36Sopenharmony_ci		md_cd_queue_struct_reset(&md_ctrl->rxq[i], md_ctrl, MTK_RX, i);
71162306a36Sopenharmony_ci		spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	t7xx_cldma_late_release(md_ctrl);
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci/**
71862306a36Sopenharmony_ci * t7xx_cldma_start() - Start CLDMA.
71962306a36Sopenharmony_ci * @md_ctrl: CLDMA context structure.
72062306a36Sopenharmony_ci *
72162306a36Sopenharmony_ci * Set TX/RX start address.
72262306a36Sopenharmony_ci * Start all RX queues and enable L2 interrupt.
72362306a36Sopenharmony_ci */
72462306a36Sopenharmony_civoid t7xx_cldma_start(struct cldma_ctrl *md_ctrl)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	unsigned long flags;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
72962306a36Sopenharmony_ci	if (md_ctrl->is_late_init) {
73062306a36Sopenharmony_ci		struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
73162306a36Sopenharmony_ci		int i;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		t7xx_cldma_enable_irq(md_ctrl);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		for (i = 0; i < CLDMA_TXQ_NUM; i++) {
73662306a36Sopenharmony_ci			if (md_ctrl->txq[i].tr_done)
73762306a36Sopenharmony_ci				t7xx_cldma_hw_set_start_addr(hw_info, i,
73862306a36Sopenharmony_ci							     md_ctrl->txq[i].tr_done->gpd_addr,
73962306a36Sopenharmony_ci							     MTK_TX);
74062306a36Sopenharmony_ci		}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		for (i = 0; i < CLDMA_RXQ_NUM; i++) {
74362306a36Sopenharmony_ci			if (md_ctrl->rxq[i].tr_done)
74462306a36Sopenharmony_ci				t7xx_cldma_hw_set_start_addr(hw_info, i,
74562306a36Sopenharmony_ci							     md_ctrl->rxq[i].tr_done->gpd_addr,
74662306a36Sopenharmony_ci							     MTK_RX);
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		/* Enable L2 interrupt */
75062306a36Sopenharmony_ci		t7xx_cldma_hw_start_queue(hw_info, CLDMA_ALL_Q, MTK_RX);
75162306a36Sopenharmony_ci		t7xx_cldma_hw_start(hw_info);
75262306a36Sopenharmony_ci		md_ctrl->txq_started = 0;
75362306a36Sopenharmony_ci		md_ctrl->txq_active |= TXRX_STATUS_BITMASK;
75462306a36Sopenharmony_ci		md_ctrl->rxq_active |= TXRX_STATUS_BITMASK;
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic void t7xx_cldma_clear_txq(struct cldma_ctrl *md_ctrl, int qnum)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	struct cldma_queue *txq = &md_ctrl->txq[qnum];
76262306a36Sopenharmony_ci	struct cldma_request *req;
76362306a36Sopenharmony_ci	struct cldma_gpd *gpd;
76462306a36Sopenharmony_ci	unsigned long flags;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	spin_lock_irqsave(&txq->ring_lock, flags);
76762306a36Sopenharmony_ci	t7xx_cldma_q_reset(txq);
76862306a36Sopenharmony_ci	list_for_each_entry(req, &txq->tr_ring->gpd_ring, entry) {
76962306a36Sopenharmony_ci		gpd = req->gpd;
77062306a36Sopenharmony_ci		gpd->flags &= ~GPD_FLAGS_HWO;
77162306a36Sopenharmony_ci		t7xx_cldma_gpd_set_data_ptr(gpd, 0);
77262306a36Sopenharmony_ci		gpd->data_buff_len = 0;
77362306a36Sopenharmony_ci		dev_kfree_skb_any(req->skb);
77462306a36Sopenharmony_ci		req->skb = NULL;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci	spin_unlock_irqrestore(&txq->ring_lock, flags);
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistatic int t7xx_cldma_clear_rxq(struct cldma_ctrl *md_ctrl, int qnum)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct cldma_queue *rxq = &md_ctrl->rxq[qnum];
78262306a36Sopenharmony_ci	struct cldma_request *req;
78362306a36Sopenharmony_ci	struct cldma_gpd *gpd;
78462306a36Sopenharmony_ci	unsigned long flags;
78562306a36Sopenharmony_ci	int ret = 0;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	spin_lock_irqsave(&rxq->ring_lock, flags);
78862306a36Sopenharmony_ci	t7xx_cldma_q_reset(rxq);
78962306a36Sopenharmony_ci	list_for_each_entry(req, &rxq->tr_ring->gpd_ring, entry) {
79062306a36Sopenharmony_ci		gpd = req->gpd;
79162306a36Sopenharmony_ci		gpd->flags = GPD_FLAGS_IOC | GPD_FLAGS_HWO;
79262306a36Sopenharmony_ci		gpd->data_buff_len = 0;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		if (req->skb) {
79562306a36Sopenharmony_ci			req->skb->len = 0;
79662306a36Sopenharmony_ci			skb_reset_tail_pointer(req->skb);
79762306a36Sopenharmony_ci		}
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	list_for_each_entry(req, &rxq->tr_ring->gpd_ring, entry) {
80162306a36Sopenharmony_ci		if (req->skb)
80262306a36Sopenharmony_ci			continue;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		ret = t7xx_cldma_alloc_and_map_skb(md_ctrl, req, rxq->tr_ring->pkt_size, GFP_ATOMIC);
80562306a36Sopenharmony_ci		if (ret)
80662306a36Sopenharmony_ci			break;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci		t7xx_cldma_gpd_set_data_ptr(req->gpd, req->mapped_buff);
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci	spin_unlock_irqrestore(&rxq->ring_lock, flags);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return ret;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_civoid t7xx_cldma_clear_all_qs(struct cldma_ctrl *md_ctrl, enum mtk_txrx tx_rx)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	int i;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (tx_rx == MTK_TX) {
82062306a36Sopenharmony_ci		for (i = 0; i < CLDMA_TXQ_NUM; i++)
82162306a36Sopenharmony_ci			t7xx_cldma_clear_txq(md_ctrl, i);
82262306a36Sopenharmony_ci	} else {
82362306a36Sopenharmony_ci		for (i = 0; i < CLDMA_RXQ_NUM; i++)
82462306a36Sopenharmony_ci			t7xx_cldma_clear_rxq(md_ctrl, i);
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_civoid t7xx_cldma_stop_all_qs(struct cldma_ctrl *md_ctrl, enum mtk_txrx tx_rx)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
83162306a36Sopenharmony_ci	unsigned long flags;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
83462306a36Sopenharmony_ci	t7xx_cldma_hw_irq_dis_eq(hw_info, CLDMA_ALL_Q, tx_rx);
83562306a36Sopenharmony_ci	t7xx_cldma_hw_irq_dis_txrx(hw_info, CLDMA_ALL_Q, tx_rx);
83662306a36Sopenharmony_ci	if (tx_rx == MTK_RX)
83762306a36Sopenharmony_ci		md_ctrl->rxq_active &= ~TXRX_STATUS_BITMASK;
83862306a36Sopenharmony_ci	else
83962306a36Sopenharmony_ci		md_ctrl->txq_active &= ~TXRX_STATUS_BITMASK;
84062306a36Sopenharmony_ci	t7xx_cldma_hw_stop_all_qs(hw_info, tx_rx);
84162306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
84262306a36Sopenharmony_ci}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_cistatic int t7xx_cldma_gpd_handle_tx_request(struct cldma_queue *queue, struct cldma_request *tx_req,
84562306a36Sopenharmony_ci					    struct sk_buff *skb)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = queue->md_ctrl;
84862306a36Sopenharmony_ci	struct cldma_gpd *gpd = tx_req->gpd;
84962306a36Sopenharmony_ci	unsigned long flags;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	/* Update GPD */
85262306a36Sopenharmony_ci	tx_req->mapped_buff = dma_map_single(md_ctrl->dev, skb->data, skb->len, DMA_TO_DEVICE);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (dma_mapping_error(md_ctrl->dev, tx_req->mapped_buff)) {
85562306a36Sopenharmony_ci		dev_err(md_ctrl->dev, "DMA mapping failed\n");
85662306a36Sopenharmony_ci		return -ENOMEM;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	t7xx_cldma_gpd_set_data_ptr(gpd, tx_req->mapped_buff);
86062306a36Sopenharmony_ci	gpd->data_buff_len = cpu_to_le16(skb->len);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	/* This lock must cover TGPD setting, as even without a resume operation,
86362306a36Sopenharmony_ci	 * CLDMA can send next HWO=1 if last TGPD just finished.
86462306a36Sopenharmony_ci	 */
86562306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
86662306a36Sopenharmony_ci	if (md_ctrl->txq_active & BIT(queue->index))
86762306a36Sopenharmony_ci		gpd->flags |= GPD_FLAGS_HWO;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	tx_req->skb = skb;
87262306a36Sopenharmony_ci	return 0;
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci/* Called with cldma_lock */
87662306a36Sopenharmony_cistatic void t7xx_cldma_hw_start_send(struct cldma_ctrl *md_ctrl, int qno,
87762306a36Sopenharmony_ci				     struct cldma_request *prev_req)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* Check whether the device was powered off (CLDMA start address is not set) */
88262306a36Sopenharmony_ci	if (!t7xx_cldma_tx_addr_is_set(hw_info, qno)) {
88362306a36Sopenharmony_ci		t7xx_cldma_hw_init(hw_info);
88462306a36Sopenharmony_ci		t7xx_cldma_hw_set_start_addr(hw_info, qno, prev_req->gpd_addr, MTK_TX);
88562306a36Sopenharmony_ci		md_ctrl->txq_started &= ~BIT(qno);
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (!t7xx_cldma_hw_queue_status(hw_info, qno, MTK_TX)) {
88962306a36Sopenharmony_ci		if (md_ctrl->txq_started & BIT(qno))
89062306a36Sopenharmony_ci			t7xx_cldma_hw_resume_queue(hw_info, qno, MTK_TX);
89162306a36Sopenharmony_ci		else
89262306a36Sopenharmony_ci			t7xx_cldma_hw_start_queue(hw_info, qno, MTK_TX);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci		md_ctrl->txq_started |= BIT(qno);
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci/**
89962306a36Sopenharmony_ci * t7xx_cldma_set_recv_skb() - Set the callback to handle RX packets.
90062306a36Sopenharmony_ci * @md_ctrl: CLDMA context structure.
90162306a36Sopenharmony_ci * @recv_skb: Receiving skb callback.
90262306a36Sopenharmony_ci */
90362306a36Sopenharmony_civoid t7xx_cldma_set_recv_skb(struct cldma_ctrl *md_ctrl,
90462306a36Sopenharmony_ci			     int (*recv_skb)(struct cldma_queue *queue, struct sk_buff *skb))
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	md_ctrl->recv_skb = recv_skb;
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci/**
91062306a36Sopenharmony_ci * t7xx_cldma_send_skb() - Send control data to modem.
91162306a36Sopenharmony_ci * @md_ctrl: CLDMA context structure.
91262306a36Sopenharmony_ci * @qno: Queue number.
91362306a36Sopenharmony_ci * @skb: Socket buffer.
91462306a36Sopenharmony_ci *
91562306a36Sopenharmony_ci * Return:
91662306a36Sopenharmony_ci * * 0		- Success.
91762306a36Sopenharmony_ci * * -ENOMEM	- Allocation failure.
91862306a36Sopenharmony_ci * * -EINVAL	- Invalid queue request.
91962306a36Sopenharmony_ci * * -EIO	- Queue is not active.
92062306a36Sopenharmony_ci * * -ETIMEDOUT	- Timeout waiting for the device to wake up.
92162306a36Sopenharmony_ci */
92262306a36Sopenharmony_ciint t7xx_cldma_send_skb(struct cldma_ctrl *md_ctrl, int qno, struct sk_buff *skb)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct cldma_request *tx_req;
92562306a36Sopenharmony_ci	struct cldma_queue *queue;
92662306a36Sopenharmony_ci	unsigned long flags;
92762306a36Sopenharmony_ci	int ret;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if (qno >= CLDMA_TXQ_NUM)
93062306a36Sopenharmony_ci		return -EINVAL;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(md_ctrl->dev);
93362306a36Sopenharmony_ci	if (ret < 0 && ret != -EACCES)
93462306a36Sopenharmony_ci		return ret;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	t7xx_pci_disable_sleep(md_ctrl->t7xx_dev);
93762306a36Sopenharmony_ci	queue = &md_ctrl->txq[qno];
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
94062306a36Sopenharmony_ci	if (!(md_ctrl->txq_active & BIT(qno))) {
94162306a36Sopenharmony_ci		ret = -EIO;
94262306a36Sopenharmony_ci		spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
94362306a36Sopenharmony_ci		goto allow_sleep;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	do {
94862306a36Sopenharmony_ci		spin_lock_irqsave(&queue->ring_lock, flags);
94962306a36Sopenharmony_ci		tx_req = queue->tx_next;
95062306a36Sopenharmony_ci		if (queue->budget > 0 && !tx_req->skb) {
95162306a36Sopenharmony_ci			struct list_head *gpd_ring = &queue->tr_ring->gpd_ring;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci			queue->budget--;
95462306a36Sopenharmony_ci			t7xx_cldma_gpd_handle_tx_request(queue, tx_req, skb);
95562306a36Sopenharmony_ci			queue->tx_next = list_next_entry_circular(tx_req, gpd_ring, entry);
95662306a36Sopenharmony_ci			spin_unlock_irqrestore(&queue->ring_lock, flags);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci			if (!t7xx_pci_sleep_disable_complete(md_ctrl->t7xx_dev)) {
95962306a36Sopenharmony_ci				ret = -ETIMEDOUT;
96062306a36Sopenharmony_ci				break;
96162306a36Sopenharmony_ci			}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci			/* Protect the access to the modem for queues operations (resume/start)
96462306a36Sopenharmony_ci			 * which access shared locations by all the queues.
96562306a36Sopenharmony_ci			 * cldma_lock is independent of ring_lock which is per queue.
96662306a36Sopenharmony_ci			 */
96762306a36Sopenharmony_ci			spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
96862306a36Sopenharmony_ci			t7xx_cldma_hw_start_send(md_ctrl, qno, tx_req);
96962306a36Sopenharmony_ci			spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci			break;
97262306a36Sopenharmony_ci		}
97362306a36Sopenharmony_ci		spin_unlock_irqrestore(&queue->ring_lock, flags);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		if (!t7xx_pci_sleep_disable_complete(md_ctrl->t7xx_dev)) {
97662306a36Sopenharmony_ci			ret = -ETIMEDOUT;
97762306a36Sopenharmony_ci			break;
97862306a36Sopenharmony_ci		}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci		if (!t7xx_cldma_hw_queue_status(&md_ctrl->hw_info, qno, MTK_TX)) {
98162306a36Sopenharmony_ci			spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
98262306a36Sopenharmony_ci			t7xx_cldma_hw_resume_queue(&md_ctrl->hw_info, qno, MTK_TX);
98362306a36Sopenharmony_ci			spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
98462306a36Sopenharmony_ci		}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		ret = wait_event_interruptible_exclusive(queue->req_wq, queue->budget > 0);
98762306a36Sopenharmony_ci	} while (!ret);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ciallow_sleep:
99062306a36Sopenharmony_ci	t7xx_pci_enable_sleep(md_ctrl->t7xx_dev);
99162306a36Sopenharmony_ci	pm_runtime_mark_last_busy(md_ctrl->dev);
99262306a36Sopenharmony_ci	pm_runtime_put_autosuspend(md_ctrl->dev);
99362306a36Sopenharmony_ci	return ret;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic int t7xx_cldma_late_init(struct cldma_ctrl *md_ctrl)
99762306a36Sopenharmony_ci{
99862306a36Sopenharmony_ci	char dma_pool_name[32];
99962306a36Sopenharmony_ci	int i, j, ret;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	if (md_ctrl->is_late_init) {
100262306a36Sopenharmony_ci		dev_err(md_ctrl->dev, "CLDMA late init was already done\n");
100362306a36Sopenharmony_ci		return -EALREADY;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	snprintf(dma_pool_name, sizeof(dma_pool_name), "cldma_req_hif%d", md_ctrl->hif_id);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	md_ctrl->gpd_dmapool = dma_pool_create(dma_pool_name, md_ctrl->dev,
100962306a36Sopenharmony_ci					       sizeof(struct cldma_gpd), GPD_DMAPOOL_ALIGN, 0);
101062306a36Sopenharmony_ci	if (!md_ctrl->gpd_dmapool) {
101162306a36Sopenharmony_ci		dev_err(md_ctrl->dev, "DMA pool alloc fail\n");
101262306a36Sopenharmony_ci		return -ENOMEM;
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	for (i = 0; i < CLDMA_TXQ_NUM; i++) {
101662306a36Sopenharmony_ci		ret = t7xx_cldma_tx_ring_init(md_ctrl, &md_ctrl->tx_ring[i]);
101762306a36Sopenharmony_ci		if (ret) {
101862306a36Sopenharmony_ci			dev_err(md_ctrl->dev, "control TX ring init fail\n");
101962306a36Sopenharmony_ci			goto err_free_tx_ring;
102062306a36Sopenharmony_ci		}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci		md_ctrl->tx_ring[i].pkt_size = CLDMA_MTU;
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	for (j = 0; j < CLDMA_RXQ_NUM; j++) {
102662306a36Sopenharmony_ci		md_ctrl->rx_ring[j].pkt_size = CLDMA_MTU;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		if (j == CLDMA_RXQ_NUM - 1)
102962306a36Sopenharmony_ci			md_ctrl->rx_ring[j].pkt_size = CLDMA_JUMBO_BUFF_SZ;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci		ret = t7xx_cldma_rx_ring_init(md_ctrl, &md_ctrl->rx_ring[j]);
103262306a36Sopenharmony_ci		if (ret) {
103362306a36Sopenharmony_ci			dev_err(md_ctrl->dev, "Control RX ring init fail\n");
103462306a36Sopenharmony_ci			goto err_free_rx_ring;
103562306a36Sopenharmony_ci		}
103662306a36Sopenharmony_ci	}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	for (i = 0; i < CLDMA_TXQ_NUM; i++)
103962306a36Sopenharmony_ci		t7xx_cldma_txq_init(&md_ctrl->txq[i]);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	for (j = 0; j < CLDMA_RXQ_NUM; j++)
104262306a36Sopenharmony_ci		t7xx_cldma_rxq_init(&md_ctrl->rxq[j]);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	md_ctrl->is_late_init = true;
104562306a36Sopenharmony_ci	return 0;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cierr_free_rx_ring:
104862306a36Sopenharmony_ci	while (j--)
104962306a36Sopenharmony_ci		t7xx_cldma_ring_free(md_ctrl, &md_ctrl->rx_ring[j], DMA_FROM_DEVICE);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cierr_free_tx_ring:
105262306a36Sopenharmony_ci	while (i--)
105362306a36Sopenharmony_ci		t7xx_cldma_ring_free(md_ctrl, &md_ctrl->tx_ring[i], DMA_TO_DEVICE);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	return ret;
105662306a36Sopenharmony_ci}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_cistatic void __iomem *t7xx_pcie_addr_transfer(void __iomem *addr, u32 addr_trs1, u32 phy_addr)
105962306a36Sopenharmony_ci{
106062306a36Sopenharmony_ci	return addr + phy_addr - addr_trs1;
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic void t7xx_hw_info_init(struct cldma_ctrl *md_ctrl)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct t7xx_addr_base *pbase = &md_ctrl->t7xx_dev->base_addr;
106662306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
106762306a36Sopenharmony_ci	u32 phy_ao_base, phy_pd_base;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	hw_info->hw_mode = MODE_BIT_64;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	if (md_ctrl->hif_id == CLDMA_ID_MD) {
107262306a36Sopenharmony_ci		phy_ao_base = CLDMA1_AO_BASE;
107362306a36Sopenharmony_ci		phy_pd_base = CLDMA1_PD_BASE;
107462306a36Sopenharmony_ci		hw_info->phy_interrupt_id = CLDMA1_INT;
107562306a36Sopenharmony_ci	} else {
107662306a36Sopenharmony_ci		phy_ao_base = CLDMA0_AO_BASE;
107762306a36Sopenharmony_ci		phy_pd_base = CLDMA0_PD_BASE;
107862306a36Sopenharmony_ci		hw_info->phy_interrupt_id = CLDMA0_INT;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	hw_info->ap_ao_base = t7xx_pcie_addr_transfer(pbase->pcie_ext_reg_base,
108262306a36Sopenharmony_ci						      pbase->pcie_dev_reg_trsl_addr, phy_ao_base);
108362306a36Sopenharmony_ci	hw_info->ap_pdn_base = t7xx_pcie_addr_transfer(pbase->pcie_ext_reg_base,
108462306a36Sopenharmony_ci						       pbase->pcie_dev_reg_trsl_addr, phy_pd_base);
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_cistatic int t7xx_cldma_default_recv_skb(struct cldma_queue *queue, struct sk_buff *skb)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
109062306a36Sopenharmony_ci	return 0;
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ciint t7xx_cldma_alloc(enum cldma_id hif_id, struct t7xx_pci_dev *t7xx_dev)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	struct device *dev = &t7xx_dev->pdev->dev;
109662306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	md_ctrl = devm_kzalloc(dev, sizeof(*md_ctrl), GFP_KERNEL);
109962306a36Sopenharmony_ci	if (!md_ctrl)
110062306a36Sopenharmony_ci		return -ENOMEM;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	md_ctrl->t7xx_dev = t7xx_dev;
110362306a36Sopenharmony_ci	md_ctrl->dev = dev;
110462306a36Sopenharmony_ci	md_ctrl->hif_id = hif_id;
110562306a36Sopenharmony_ci	md_ctrl->recv_skb = t7xx_cldma_default_recv_skb;
110662306a36Sopenharmony_ci	t7xx_hw_info_init(md_ctrl);
110762306a36Sopenharmony_ci	t7xx_dev->md->md_ctrl[hif_id] = md_ctrl;
110862306a36Sopenharmony_ci	return 0;
110962306a36Sopenharmony_ci}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_cistatic void t7xx_cldma_resume_early(struct t7xx_pci_dev *t7xx_dev, void *entity_param)
111262306a36Sopenharmony_ci{
111362306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = entity_param;
111462306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info;
111562306a36Sopenharmony_ci	unsigned long flags;
111662306a36Sopenharmony_ci	int qno_t;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	hw_info = &md_ctrl->hw_info;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
112162306a36Sopenharmony_ci	t7xx_cldma_hw_restore(hw_info);
112262306a36Sopenharmony_ci	for (qno_t = 0; qno_t < CLDMA_TXQ_NUM; qno_t++) {
112362306a36Sopenharmony_ci		t7xx_cldma_hw_set_start_addr(hw_info, qno_t, md_ctrl->txq[qno_t].tx_next->gpd_addr,
112462306a36Sopenharmony_ci					     MTK_TX);
112562306a36Sopenharmony_ci		t7xx_cldma_hw_set_start_addr(hw_info, qno_t, md_ctrl->rxq[qno_t].tr_done->gpd_addr,
112662306a36Sopenharmony_ci					     MTK_RX);
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci	t7xx_cldma_enable_irq(md_ctrl);
112962306a36Sopenharmony_ci	t7xx_cldma_hw_start_queue(hw_info, CLDMA_ALL_Q, MTK_RX);
113062306a36Sopenharmony_ci	md_ctrl->rxq_active |= TXRX_STATUS_BITMASK;
113162306a36Sopenharmony_ci	t7xx_cldma_hw_irq_en_eq(hw_info, CLDMA_ALL_Q, MTK_RX);
113262306a36Sopenharmony_ci	t7xx_cldma_hw_irq_en_txrx(hw_info, CLDMA_ALL_Q, MTK_RX);
113362306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cistatic int t7xx_cldma_resume(struct t7xx_pci_dev *t7xx_dev, void *entity_param)
113762306a36Sopenharmony_ci{
113862306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = entity_param;
113962306a36Sopenharmony_ci	unsigned long flags;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
114262306a36Sopenharmony_ci	md_ctrl->txq_active |= TXRX_STATUS_BITMASK;
114362306a36Sopenharmony_ci	t7xx_cldma_hw_irq_en_txrx(&md_ctrl->hw_info, CLDMA_ALL_Q, MTK_TX);
114462306a36Sopenharmony_ci	t7xx_cldma_hw_irq_en_eq(&md_ctrl->hw_info, CLDMA_ALL_Q, MTK_TX);
114562306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (md_ctrl->hif_id == CLDMA_ID_MD)
114862306a36Sopenharmony_ci		t7xx_mhccif_mask_clr(t7xx_dev, D2H_SW_INT_MASK);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	return 0;
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cistatic void t7xx_cldma_suspend_late(struct t7xx_pci_dev *t7xx_dev, void *entity_param)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = entity_param;
115662306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info;
115762306a36Sopenharmony_ci	unsigned long flags;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	hw_info = &md_ctrl->hw_info;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
116262306a36Sopenharmony_ci	t7xx_cldma_hw_irq_dis_eq(hw_info, CLDMA_ALL_Q, MTK_RX);
116362306a36Sopenharmony_ci	t7xx_cldma_hw_irq_dis_txrx(hw_info, CLDMA_ALL_Q, MTK_RX);
116462306a36Sopenharmony_ci	md_ctrl->rxq_active &= ~TXRX_STATUS_BITMASK;
116562306a36Sopenharmony_ci	t7xx_cldma_hw_stop_all_qs(hw_info, MTK_RX);
116662306a36Sopenharmony_ci	t7xx_cldma_clear_ip_busy(hw_info);
116762306a36Sopenharmony_ci	t7xx_cldma_disable_irq(md_ctrl);
116862306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
116962306a36Sopenharmony_ci}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_cistatic int t7xx_cldma_suspend(struct t7xx_pci_dev *t7xx_dev, void *entity_param)
117262306a36Sopenharmony_ci{
117362306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = entity_param;
117462306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info;
117562306a36Sopenharmony_ci	unsigned long flags;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (md_ctrl->hif_id == CLDMA_ID_MD)
117862306a36Sopenharmony_ci		t7xx_mhccif_mask_set(t7xx_dev, D2H_SW_INT_MASK);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	hw_info = &md_ctrl->hw_info;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
118362306a36Sopenharmony_ci	t7xx_cldma_hw_irq_dis_eq(hw_info, CLDMA_ALL_Q, MTK_TX);
118462306a36Sopenharmony_ci	t7xx_cldma_hw_irq_dis_txrx(hw_info, CLDMA_ALL_Q, MTK_TX);
118562306a36Sopenharmony_ci	md_ctrl->txq_active &= ~TXRX_STATUS_BITMASK;
118662306a36Sopenharmony_ci	t7xx_cldma_hw_stop_all_qs(hw_info, MTK_TX);
118762306a36Sopenharmony_ci	md_ctrl->txq_started = 0;
118862306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return 0;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic int t7xx_cldma_pm_init(struct cldma_ctrl *md_ctrl)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci	md_ctrl->pm_entity = kzalloc(sizeof(*md_ctrl->pm_entity), GFP_KERNEL);
119662306a36Sopenharmony_ci	if (!md_ctrl->pm_entity)
119762306a36Sopenharmony_ci		return -ENOMEM;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	md_ctrl->pm_entity->entity_param = md_ctrl;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	if (md_ctrl->hif_id == CLDMA_ID_MD)
120262306a36Sopenharmony_ci		md_ctrl->pm_entity->id = PM_ENTITY_ID_CTRL1;
120362306a36Sopenharmony_ci	else
120462306a36Sopenharmony_ci		md_ctrl->pm_entity->id = PM_ENTITY_ID_CTRL2;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	md_ctrl->pm_entity->suspend = t7xx_cldma_suspend;
120762306a36Sopenharmony_ci	md_ctrl->pm_entity->suspend_late = t7xx_cldma_suspend_late;
120862306a36Sopenharmony_ci	md_ctrl->pm_entity->resume = t7xx_cldma_resume;
120962306a36Sopenharmony_ci	md_ctrl->pm_entity->resume_early = t7xx_cldma_resume_early;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	return t7xx_pci_pm_entity_register(md_ctrl->t7xx_dev, md_ctrl->pm_entity);
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_cistatic int t7xx_cldma_pm_uninit(struct cldma_ctrl *md_ctrl)
121562306a36Sopenharmony_ci{
121662306a36Sopenharmony_ci	if (!md_ctrl->pm_entity)
121762306a36Sopenharmony_ci		return -EINVAL;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	t7xx_pci_pm_entity_unregister(md_ctrl->t7xx_dev, md_ctrl->pm_entity);
122062306a36Sopenharmony_ci	kfree(md_ctrl->pm_entity);
122162306a36Sopenharmony_ci	md_ctrl->pm_entity = NULL;
122262306a36Sopenharmony_ci	return 0;
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_civoid t7xx_cldma_hif_hw_init(struct cldma_ctrl *md_ctrl)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
122862306a36Sopenharmony_ci	unsigned long flags;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	spin_lock_irqsave(&md_ctrl->cldma_lock, flags);
123162306a36Sopenharmony_ci	t7xx_cldma_hw_stop(hw_info, MTK_TX);
123262306a36Sopenharmony_ci	t7xx_cldma_hw_stop(hw_info, MTK_RX);
123362306a36Sopenharmony_ci	t7xx_cldma_hw_rx_done(hw_info, EMPTY_STATUS_BITMASK | TXRX_STATUS_BITMASK);
123462306a36Sopenharmony_ci	t7xx_cldma_hw_tx_done(hw_info, EMPTY_STATUS_BITMASK | TXRX_STATUS_BITMASK);
123562306a36Sopenharmony_ci	t7xx_cldma_hw_init(hw_info);
123662306a36Sopenharmony_ci	spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic irqreturn_t t7xx_cldma_isr_handler(int irq, void *data)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	struct cldma_ctrl *md_ctrl = data;
124262306a36Sopenharmony_ci	u32 interrupt;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	interrupt = md_ctrl->hw_info.phy_interrupt_id;
124562306a36Sopenharmony_ci	t7xx_pcie_mac_clear_int(md_ctrl->t7xx_dev, interrupt);
124662306a36Sopenharmony_ci	t7xx_cldma_irq_work_cb(md_ctrl);
124762306a36Sopenharmony_ci	t7xx_pcie_mac_clear_int_status(md_ctrl->t7xx_dev, interrupt);
124862306a36Sopenharmony_ci	t7xx_pcie_mac_set_int(md_ctrl->t7xx_dev, interrupt);
124962306a36Sopenharmony_ci	return IRQ_HANDLED;
125062306a36Sopenharmony_ci}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic void t7xx_cldma_destroy_wqs(struct cldma_ctrl *md_ctrl)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	int i;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	for (i = 0; i < CLDMA_TXQ_NUM; i++) {
125762306a36Sopenharmony_ci		if (md_ctrl->txq[i].worker) {
125862306a36Sopenharmony_ci			destroy_workqueue(md_ctrl->txq[i].worker);
125962306a36Sopenharmony_ci			md_ctrl->txq[i].worker = NULL;
126062306a36Sopenharmony_ci		}
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	for (i = 0; i < CLDMA_RXQ_NUM; i++) {
126462306a36Sopenharmony_ci		if (md_ctrl->rxq[i].worker) {
126562306a36Sopenharmony_ci			destroy_workqueue(md_ctrl->rxq[i].worker);
126662306a36Sopenharmony_ci			md_ctrl->rxq[i].worker = NULL;
126762306a36Sopenharmony_ci		}
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci/**
127262306a36Sopenharmony_ci * t7xx_cldma_init() - Initialize CLDMA.
127362306a36Sopenharmony_ci * @md_ctrl: CLDMA context structure.
127462306a36Sopenharmony_ci *
127562306a36Sopenharmony_ci * Allocate and initialize device power management entity.
127662306a36Sopenharmony_ci * Initialize HIF TX/RX queue structure.
127762306a36Sopenharmony_ci * Register CLDMA callback ISR with PCIe driver.
127862306a36Sopenharmony_ci *
127962306a36Sopenharmony_ci * Return:
128062306a36Sopenharmony_ci * * 0		- Success.
128162306a36Sopenharmony_ci * * -ERROR	- Error code from failure sub-initializations.
128262306a36Sopenharmony_ci */
128362306a36Sopenharmony_ciint t7xx_cldma_init(struct cldma_ctrl *md_ctrl)
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
128662306a36Sopenharmony_ci	int ret, i;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	md_ctrl->txq_active = 0;
128962306a36Sopenharmony_ci	md_ctrl->rxq_active = 0;
129062306a36Sopenharmony_ci	md_ctrl->is_late_init = false;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	ret = t7xx_cldma_pm_init(md_ctrl);
129362306a36Sopenharmony_ci	if (ret)
129462306a36Sopenharmony_ci		return ret;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	spin_lock_init(&md_ctrl->cldma_lock);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	for (i = 0; i < CLDMA_TXQ_NUM; i++) {
129962306a36Sopenharmony_ci		md_cd_queue_struct_init(&md_ctrl->txq[i], md_ctrl, MTK_TX, i);
130062306a36Sopenharmony_ci		md_ctrl->txq[i].worker =
130162306a36Sopenharmony_ci			alloc_ordered_workqueue("md_hif%d_tx%d_worker",
130262306a36Sopenharmony_ci					WQ_MEM_RECLAIM | (i ? 0 : WQ_HIGHPRI),
130362306a36Sopenharmony_ci					md_ctrl->hif_id, i);
130462306a36Sopenharmony_ci		if (!md_ctrl->txq[i].worker)
130562306a36Sopenharmony_ci			goto err_workqueue;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci		INIT_WORK(&md_ctrl->txq[i].cldma_work, t7xx_cldma_tx_done);
130862306a36Sopenharmony_ci	}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	for (i = 0; i < CLDMA_RXQ_NUM; i++) {
131162306a36Sopenharmony_ci		md_cd_queue_struct_init(&md_ctrl->rxq[i], md_ctrl, MTK_RX, i);
131262306a36Sopenharmony_ci		INIT_WORK(&md_ctrl->rxq[i].cldma_work, t7xx_cldma_rx_done);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci		md_ctrl->rxq[i].worker =
131562306a36Sopenharmony_ci			alloc_ordered_workqueue("md_hif%d_rx%d_worker",
131662306a36Sopenharmony_ci						WQ_MEM_RECLAIM,
131762306a36Sopenharmony_ci						md_ctrl->hif_id, i);
131862306a36Sopenharmony_ci		if (!md_ctrl->rxq[i].worker)
131962306a36Sopenharmony_ci			goto err_workqueue;
132062306a36Sopenharmony_ci	}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	t7xx_pcie_mac_clear_int(md_ctrl->t7xx_dev, hw_info->phy_interrupt_id);
132362306a36Sopenharmony_ci	md_ctrl->t7xx_dev->intr_handler[hw_info->phy_interrupt_id] = t7xx_cldma_isr_handler;
132462306a36Sopenharmony_ci	md_ctrl->t7xx_dev->intr_thread[hw_info->phy_interrupt_id] = NULL;
132562306a36Sopenharmony_ci	md_ctrl->t7xx_dev->callback_param[hw_info->phy_interrupt_id] = md_ctrl;
132662306a36Sopenharmony_ci	t7xx_pcie_mac_clear_int_status(md_ctrl->t7xx_dev, hw_info->phy_interrupt_id);
132762306a36Sopenharmony_ci	return 0;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cierr_workqueue:
133062306a36Sopenharmony_ci	t7xx_cldma_destroy_wqs(md_ctrl);
133162306a36Sopenharmony_ci	t7xx_cldma_pm_uninit(md_ctrl);
133262306a36Sopenharmony_ci	return -ENOMEM;
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_civoid t7xx_cldma_switch_cfg(struct cldma_ctrl *md_ctrl)
133662306a36Sopenharmony_ci{
133762306a36Sopenharmony_ci	t7xx_cldma_late_release(md_ctrl);
133862306a36Sopenharmony_ci	t7xx_cldma_late_init(md_ctrl);
133962306a36Sopenharmony_ci}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_civoid t7xx_cldma_exit(struct cldma_ctrl *md_ctrl)
134262306a36Sopenharmony_ci{
134362306a36Sopenharmony_ci	t7xx_cldma_stop(md_ctrl);
134462306a36Sopenharmony_ci	t7xx_cldma_late_release(md_ctrl);
134562306a36Sopenharmony_ci	t7xx_cldma_destroy_wqs(md_ctrl);
134662306a36Sopenharmony_ci	t7xx_cldma_pm_uninit(md_ctrl);
134762306a36Sopenharmony_ci}
1348