1d642b012SHaijun Liu // SPDX-License-Identifier: GPL-2.0-only
2d642b012SHaijun Liu /*
3d642b012SHaijun Liu  * Copyright (c) 2021, MediaTek Inc.
4d642b012SHaijun Liu  * Copyright (c) 2021-2022, Intel Corporation.
5d642b012SHaijun Liu  *
6d642b012SHaijun Liu  * Authors:
7d642b012SHaijun Liu  *  Amir Hanania <amir.hanania@intel.com>
8d642b012SHaijun Liu  *  Haijun Liu <haijun.liu@mediatek.com>
9d642b012SHaijun Liu  *  Eliot Lee <eliot.lee@intel.com>
10d642b012SHaijun Liu  *  Moises Veleta <moises.veleta@intel.com>
11d642b012SHaijun Liu  *  Ricardo Martinez <ricardo.martinez@linux.intel.com>
12d642b012SHaijun Liu  *
13d642b012SHaijun Liu  * Contributors:
14d642b012SHaijun Liu  *  Andy Shevchenko <andriy.shevchenko@linux.intel.com>
15d642b012SHaijun Liu  *  Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com>
16d642b012SHaijun Liu  *  Sreehari Kancharla <sreehari.kancharla@intel.com>
17d642b012SHaijun Liu  */
18d642b012SHaijun Liu 
19d642b012SHaijun Liu #include <linux/atomic.h>
20d642b012SHaijun Liu #include <linux/bitfield.h>
21d642b012SHaijun Liu #include <linux/bitops.h>
22d642b012SHaijun Liu #include <linux/device.h>
23d642b012SHaijun Liu #include <linux/dma-direction.h>
24d642b012SHaijun Liu #include <linux/dma-mapping.h>
25d642b012SHaijun Liu #include <linux/gfp.h>
26d642b012SHaijun Liu #include <linux/err.h>
27d642b012SHaijun Liu #include <linux/iopoll.h>
28d642b012SHaijun Liu #include <linux/jiffies.h>
29d642b012SHaijun Liu #include <linux/kernel.h>
30d642b012SHaijun Liu #include <linux/kthread.h>
31d642b012SHaijun Liu #include <linux/list.h>
32d642b012SHaijun Liu #include <linux/minmax.h>
33d642b012SHaijun Liu #include <linux/mm.h>
34d642b012SHaijun Liu #include <linux/netdevice.h>
35d10b3a69SHaijun Liu #include <linux/pm_runtime.h>
36d642b012SHaijun Liu #include <linux/sched.h>
37d642b012SHaijun Liu #include <linux/skbuff.h>
38d642b012SHaijun Liu #include <linux/slab.h>
39d642b012SHaijun Liu #include <linux/spinlock.h>
40d642b012SHaijun Liu #include <linux/string.h>
41d642b012SHaijun Liu #include <linux/types.h>
42d642b012SHaijun Liu #include <linux/wait.h>
43d642b012SHaijun Liu #include <linux/workqueue.h>
44d642b012SHaijun Liu 
45d642b012SHaijun Liu #include "t7xx_dpmaif.h"
46d642b012SHaijun Liu #include "t7xx_hif_dpmaif.h"
47d642b012SHaijun Liu #include "t7xx_hif_dpmaif_rx.h"
485545b7b9SHaijun Liu #include "t7xx_netdev.h"
49d642b012SHaijun Liu #include "t7xx_pci.h"
50d642b012SHaijun Liu 
51d642b012SHaijun Liu #define DPMAIF_BAT_COUNT		8192
52d642b012SHaijun Liu #define DPMAIF_FRG_COUNT		4814
53d642b012SHaijun Liu #define DPMAIF_PIT_COUNT		(DPMAIF_BAT_COUNT * 2)
54d642b012SHaijun Liu 
55d642b012SHaijun Liu #define DPMAIF_BAT_CNT_THRESHOLD	30
56d642b012SHaijun Liu #define DPMAIF_PIT_CNT_THRESHOLD	60
57d642b012SHaijun Liu #define DPMAIF_RX_PUSH_THRESHOLD_MASK	GENMASK(2, 0)
58d642b012SHaijun Liu #define DPMAIF_NOTIFY_RELEASE_COUNT	128
59d642b012SHaijun Liu #define DPMAIF_POLL_PIT_TIME_US		20
60d642b012SHaijun Liu #define DPMAIF_POLL_PIT_MAX_TIME_US	2000
61d642b012SHaijun Liu #define DPMAIF_WQ_TIME_LIMIT_MS		2
62d642b012SHaijun Liu #define DPMAIF_CS_RESULT_PASS		0
63d642b012SHaijun Liu 
64d642b012SHaijun Liu /* Packet type */
65d642b012SHaijun Liu #define DES_PT_PD			0
66d642b012SHaijun Liu #define DES_PT_MSG			1
67d642b012SHaijun Liu /* Buffer type */
68d642b012SHaijun Liu #define PKT_BUF_FRAG			1
69d642b012SHaijun Liu 
t7xx_normal_pit_bid(const struct dpmaif_pit * pit_info)70d642b012SHaijun Liu static unsigned int t7xx_normal_pit_bid(const struct dpmaif_pit *pit_info)
71d642b012SHaijun Liu {
72d642b012SHaijun Liu 	u32 value;
73d642b012SHaijun Liu 
74d642b012SHaijun Liu 	value = FIELD_GET(PD_PIT_H_BID, le32_to_cpu(pit_info->pd.footer));
75d642b012SHaijun Liu 	value <<= 13;
76d642b012SHaijun Liu 	value += FIELD_GET(PD_PIT_BUFFER_ID, le32_to_cpu(pit_info->header));
77d642b012SHaijun Liu 	return value;
78d642b012SHaijun Liu }
79d642b012SHaijun Liu 
t7xx_dpmaif_update_bat_wr_idx(struct dpmaif_ctrl * dpmaif_ctrl,const unsigned int q_num,const unsigned int bat_cnt)80d642b012SHaijun Liu static int t7xx_dpmaif_update_bat_wr_idx(struct dpmaif_ctrl *dpmaif_ctrl,
81d642b012SHaijun Liu 					 const unsigned int q_num, const unsigned int bat_cnt)
82d642b012SHaijun Liu {
83d642b012SHaijun Liu 	struct dpmaif_rx_queue *rxq = &dpmaif_ctrl->rxq[q_num];
84d642b012SHaijun Liu 	struct dpmaif_bat_request *bat_req = rxq->bat_req;
85d642b012SHaijun Liu 	unsigned int old_rl_idx, new_wr_idx, old_wr_idx;
86d642b012SHaijun Liu 
87d642b012SHaijun Liu 	if (!rxq->que_started) {
88d642b012SHaijun Liu 		dev_err(dpmaif_ctrl->dev, "RX queue %d has not been started\n", rxq->index);
89d642b012SHaijun Liu 		return -EINVAL;
90d642b012SHaijun Liu 	}
91d642b012SHaijun Liu 
92d642b012SHaijun Liu 	old_rl_idx = bat_req->bat_release_rd_idx;
93d642b012SHaijun Liu 	old_wr_idx = bat_req->bat_wr_idx;
94d642b012SHaijun Liu 	new_wr_idx = old_wr_idx + bat_cnt;
95d642b012SHaijun Liu 
96d642b012SHaijun Liu 	if (old_rl_idx > old_wr_idx && new_wr_idx >= old_rl_idx)
97d642b012SHaijun Liu 		goto err_flow;
98d642b012SHaijun Liu 
99d642b012SHaijun Liu 	if (new_wr_idx >= bat_req->bat_size_cnt) {
100d642b012SHaijun Liu 		new_wr_idx -= bat_req->bat_size_cnt;
101d642b012SHaijun Liu 		if (new_wr_idx >= old_rl_idx)
102d642b012SHaijun Liu 			goto err_flow;
103d642b012SHaijun Liu 	}
104d642b012SHaijun Liu 
105d642b012SHaijun Liu 	bat_req->bat_wr_idx = new_wr_idx;
106d642b012SHaijun Liu 	return 0;
107d642b012SHaijun Liu 
108d642b012SHaijun Liu err_flow:
109d642b012SHaijun Liu 	dev_err(dpmaif_ctrl->dev, "RX BAT flow check fail\n");
110d642b012SHaijun Liu 	return -EINVAL;
111d642b012SHaijun Liu }
112d642b012SHaijun Liu 
t7xx_alloc_and_map_skb_info(const struct dpmaif_ctrl * dpmaif_ctrl,const unsigned int size,struct dpmaif_bat_skb * cur_skb)113d642b012SHaijun Liu static bool t7xx_alloc_and_map_skb_info(const struct dpmaif_ctrl *dpmaif_ctrl,
114d642b012SHaijun Liu 					const unsigned int size, struct dpmaif_bat_skb *cur_skb)
115d642b012SHaijun Liu {
116d642b012SHaijun Liu 	dma_addr_t data_bus_addr;
117d642b012SHaijun Liu 	struct sk_buff *skb;
118d642b012SHaijun Liu 
119d642b012SHaijun Liu 	skb = __dev_alloc_skb(size, GFP_KERNEL);
120d642b012SHaijun Liu 	if (!skb)
121d642b012SHaijun Liu 		return false;
122d642b012SHaijun Liu 
123262d98b1SRicardo Martinez 	data_bus_addr = dma_map_single(dpmaif_ctrl->dev, skb->data, size, DMA_FROM_DEVICE);
124d642b012SHaijun Liu 	if (dma_mapping_error(dpmaif_ctrl->dev, data_bus_addr)) {
125d642b012SHaijun Liu 		dev_err_ratelimited(dpmaif_ctrl->dev, "DMA mapping error\n");
126d642b012SHaijun Liu 		dev_kfree_skb_any(skb);
127d642b012SHaijun Liu 		return false;
128d642b012SHaijun Liu 	}
129d642b012SHaijun Liu 
130d642b012SHaijun Liu 	cur_skb->skb = skb;
131d642b012SHaijun Liu 	cur_skb->data_bus_addr = data_bus_addr;
132262d98b1SRicardo Martinez 	cur_skb->data_len = size;
133d642b012SHaijun Liu 
134d642b012SHaijun Liu 	return true;
135d642b012SHaijun Liu }
136d642b012SHaijun Liu 
t7xx_unmap_bat_skb(struct device * dev,struct dpmaif_bat_skb * bat_skb_base,unsigned int index)137d642b012SHaijun Liu static void t7xx_unmap_bat_skb(struct device *dev, struct dpmaif_bat_skb *bat_skb_base,
138d642b012SHaijun Liu 			       unsigned int index)
139d642b012SHaijun Liu {
140d642b012SHaijun Liu 	struct dpmaif_bat_skb *bat_skb = bat_skb_base + index;
141d642b012SHaijun Liu 
142d642b012SHaijun Liu 	if (bat_skb->skb) {
143d642b012SHaijun Liu 		dma_unmap_single(dev, bat_skb->data_bus_addr, bat_skb->data_len, DMA_FROM_DEVICE);
144d642b012SHaijun Liu 		dev_kfree_skb(bat_skb->skb);
145d642b012SHaijun Liu 		bat_skb->skb = NULL;
146d642b012SHaijun Liu 	}
147d642b012SHaijun Liu }
148d642b012SHaijun Liu 
149d642b012SHaijun Liu /**
150d642b012SHaijun Liu  * t7xx_dpmaif_rx_buf_alloc() - Allocate buffers for the BAT ring.
151d642b012SHaijun Liu  * @dpmaif_ctrl: Pointer to DPMAIF context structure.
152d642b012SHaijun Liu  * @bat_req: Pointer to BAT request structure.
153d642b012SHaijun Liu  * @q_num: Queue number.
154d642b012SHaijun Liu  * @buf_cnt: Number of buffers to allocate.
155d642b012SHaijun Liu  * @initial: Indicates if the ring is being populated for the first time.
156d642b012SHaijun Liu  *
157d642b012SHaijun Liu  * Allocate skb and store the start address of the data buffer into the BAT ring.
158d642b012SHaijun Liu  * If this is not the initial call, notify the HW about the new entries.
159d642b012SHaijun Liu  *
160d642b012SHaijun Liu  * Return:
161d642b012SHaijun Liu  * * 0		- Success.
162d642b012SHaijun Liu  * * -ERROR	- Error code.
163d642b012SHaijun Liu  */
t7xx_dpmaif_rx_buf_alloc(struct dpmaif_ctrl * dpmaif_ctrl,const struct dpmaif_bat_request * bat_req,const unsigned int q_num,const unsigned int buf_cnt,const bool initial)164d642b012SHaijun Liu int t7xx_dpmaif_rx_buf_alloc(struct dpmaif_ctrl *dpmaif_ctrl,
165d642b012SHaijun Liu 			     const struct dpmaif_bat_request *bat_req,
166d642b012SHaijun Liu 			     const unsigned int q_num, const unsigned int buf_cnt,
167d642b012SHaijun Liu 			     const bool initial)
168d642b012SHaijun Liu {
169d642b012SHaijun Liu 	unsigned int i, bat_cnt, bat_max_cnt, bat_start_idx;
170d642b012SHaijun Liu 	int ret;
171d642b012SHaijun Liu 
172d642b012SHaijun Liu 	if (!buf_cnt || buf_cnt > bat_req->bat_size_cnt)
173d642b012SHaijun Liu 		return -EINVAL;
174d642b012SHaijun Liu 
175d642b012SHaijun Liu 	/* Check BAT buffer space */
176d642b012SHaijun Liu 	bat_max_cnt = bat_req->bat_size_cnt;
177d642b012SHaijun Liu 
178d642b012SHaijun Liu 	bat_cnt = t7xx_ring_buf_rd_wr_count(bat_max_cnt, bat_req->bat_release_rd_idx,
179d642b012SHaijun Liu 					    bat_req->bat_wr_idx, DPMAIF_WRITE);
180d642b012SHaijun Liu 	if (buf_cnt > bat_cnt)
181d642b012SHaijun Liu 		return -ENOMEM;
182d642b012SHaijun Liu 
183d642b012SHaijun Liu 	bat_start_idx = bat_req->bat_wr_idx;
184d642b012SHaijun Liu 
185d642b012SHaijun Liu 	for (i = 0; i < buf_cnt; i++) {
186d642b012SHaijun Liu 		unsigned int cur_bat_idx = bat_start_idx + i;
187d642b012SHaijun Liu 		struct dpmaif_bat_skb *cur_skb;
188d642b012SHaijun Liu 		struct dpmaif_bat *cur_bat;
189d642b012SHaijun Liu 
190d642b012SHaijun Liu 		if (cur_bat_idx >= bat_max_cnt)
191d642b012SHaijun Liu 			cur_bat_idx -= bat_max_cnt;
192d642b012SHaijun Liu 
193d642b012SHaijun Liu 		cur_skb = (struct dpmaif_bat_skb *)bat_req->bat_skb + cur_bat_idx;
194d642b012SHaijun Liu 		if (!cur_skb->skb &&
195d642b012SHaijun Liu 		    !t7xx_alloc_and_map_skb_info(dpmaif_ctrl, bat_req->pkt_buf_sz, cur_skb))
196d642b012SHaijun Liu 			break;
197d642b012SHaijun Liu 
198d642b012SHaijun Liu 		cur_bat = (struct dpmaif_bat *)bat_req->bat_base + cur_bat_idx;
199d642b012SHaijun Liu 		cur_bat->buffer_addr_ext = upper_32_bits(cur_skb->data_bus_addr);
200d642b012SHaijun Liu 		cur_bat->p_buffer_addr = lower_32_bits(cur_skb->data_bus_addr);
201d642b012SHaijun Liu 	}
202d642b012SHaijun Liu 
203d642b012SHaijun Liu 	if (!i)
204d642b012SHaijun Liu 		return -ENOMEM;
205d642b012SHaijun Liu 
206d642b012SHaijun Liu 	ret = t7xx_dpmaif_update_bat_wr_idx(dpmaif_ctrl, q_num, i);
207d642b012SHaijun Liu 	if (ret)
208d642b012SHaijun Liu 		goto err_unmap_skbs;
209d642b012SHaijun Liu 
210d642b012SHaijun Liu 	if (!initial) {
211d642b012SHaijun Liu 		unsigned int hw_wr_idx;
212d642b012SHaijun Liu 
213d642b012SHaijun Liu 		ret = t7xx_dpmaif_dl_snd_hw_bat_cnt(&dpmaif_ctrl->hw_info, i);
214d642b012SHaijun Liu 		if (ret)
215d642b012SHaijun Liu 			goto err_unmap_skbs;
216d642b012SHaijun Liu 
217d642b012SHaijun Liu 		hw_wr_idx = t7xx_dpmaif_dl_get_bat_wr_idx(&dpmaif_ctrl->hw_info,
218d642b012SHaijun Liu 							  DPF_RX_QNO_DFT);
219d642b012SHaijun Liu 		if (hw_wr_idx != bat_req->bat_wr_idx) {
220d642b012SHaijun Liu 			ret = -EFAULT;
221d642b012SHaijun Liu 			dev_err(dpmaif_ctrl->dev, "Write index mismatch in RX ring\n");
222d642b012SHaijun Liu 			goto err_unmap_skbs;
223d642b012SHaijun Liu 		}
224d642b012SHaijun Liu 	}
225d642b012SHaijun Liu 
226d642b012SHaijun Liu 	return 0;
227d642b012SHaijun Liu 
228d642b012SHaijun Liu err_unmap_skbs:
229d642b012SHaijun Liu 	while (--i > 0)
230d642b012SHaijun Liu 		t7xx_unmap_bat_skb(dpmaif_ctrl->dev, bat_req->bat_skb, i);
231d642b012SHaijun Liu 
232d642b012SHaijun Liu 	return ret;
233d642b012SHaijun Liu }
234d642b012SHaijun Liu 
t7xx_dpmaifq_release_pit_entry(struct dpmaif_rx_queue * rxq,const unsigned int rel_entry_num)235d642b012SHaijun Liu static int t7xx_dpmaifq_release_pit_entry(struct dpmaif_rx_queue *rxq,
236d642b012SHaijun Liu 					  const unsigned int rel_entry_num)
237d642b012SHaijun Liu {
238d642b012SHaijun Liu 	struct dpmaif_hw_info *hw_info = &rxq->dpmaif_ctrl->hw_info;
239d642b012SHaijun Liu 	unsigned int old_rel_idx, new_rel_idx, hw_wr_idx;
240d642b012SHaijun Liu 	int ret;
241d642b012SHaijun Liu 
242d642b012SHaijun Liu 	if (!rxq->que_started)
243d642b012SHaijun Liu 		return 0;
244d642b012SHaijun Liu 
245d642b012SHaijun Liu 	if (rel_entry_num >= rxq->pit_size_cnt) {
246d642b012SHaijun Liu 		dev_err(rxq->dpmaif_ctrl->dev, "Invalid PIT release index\n");
247d642b012SHaijun Liu 		return -EINVAL;
248d642b012SHaijun Liu 	}
249d642b012SHaijun Liu 
250d642b012SHaijun Liu 	old_rel_idx = rxq->pit_release_rd_idx;
251d642b012SHaijun Liu 	new_rel_idx = old_rel_idx + rel_entry_num;
252d642b012SHaijun Liu 	hw_wr_idx = rxq->pit_wr_idx;
253d642b012SHaijun Liu 	if (hw_wr_idx < old_rel_idx && new_rel_idx >= rxq->pit_size_cnt)
254d642b012SHaijun Liu 		new_rel_idx -= rxq->pit_size_cnt;
255d642b012SHaijun Liu 
256d642b012SHaijun Liu 	ret = t7xx_dpmaif_dlq_add_pit_remain_cnt(hw_info, rxq->index, rel_entry_num);
257d642b012SHaijun Liu 	if (ret) {
258d642b012SHaijun Liu 		dev_err(rxq->dpmaif_ctrl->dev, "PIT release failure: %d\n", ret);
259d642b012SHaijun Liu 		return ret;
260d642b012SHaijun Liu 	}
261d642b012SHaijun Liu 
262d642b012SHaijun Liu 	rxq->pit_release_rd_idx = new_rel_idx;
263d642b012SHaijun Liu 	return 0;
264d642b012SHaijun Liu }
265d642b012SHaijun Liu 
t7xx_dpmaif_set_bat_mask(struct dpmaif_bat_request * bat_req,unsigned int idx)266d642b012SHaijun Liu static void t7xx_dpmaif_set_bat_mask(struct dpmaif_bat_request *bat_req, unsigned int idx)
267d642b012SHaijun Liu {
268d642b012SHaijun Liu 	unsigned long flags;
269d642b012SHaijun Liu 
270d642b012SHaijun Liu 	spin_lock_irqsave(&bat_req->mask_lock, flags);
271d642b012SHaijun Liu 	set_bit(idx, bat_req->bat_bitmap);
272d642b012SHaijun Liu 	spin_unlock_irqrestore(&bat_req->mask_lock, flags);
273d642b012SHaijun Liu }
274d642b012SHaijun Liu 
t7xx_frag_bat_cur_bid_check(struct dpmaif_rx_queue * rxq,const unsigned int cur_bid)275d642b012SHaijun Liu static int t7xx_frag_bat_cur_bid_check(struct dpmaif_rx_queue *rxq,
276d642b012SHaijun Liu 				       const unsigned int cur_bid)
277d642b012SHaijun Liu {
278d642b012SHaijun Liu 	struct dpmaif_bat_request *bat_frag = rxq->bat_frag;
279d642b012SHaijun Liu 	struct dpmaif_bat_page *bat_page;
280d642b012SHaijun Liu 
281d642b012SHaijun Liu 	if (cur_bid >= DPMAIF_FRG_COUNT)
282d642b012SHaijun Liu 		return -EINVAL;
283d642b012SHaijun Liu 
284d642b012SHaijun Liu 	bat_page = bat_frag->bat_skb + cur_bid;
285d642b012SHaijun Liu 	if (!bat_page->page)
286d642b012SHaijun Liu 		return -EINVAL;
287d642b012SHaijun Liu 
288d642b012SHaijun Liu 	return 0;
289d642b012SHaijun Liu }
290d642b012SHaijun Liu 
t7xx_unmap_bat_page(struct device * dev,struct dpmaif_bat_page * bat_page_base,unsigned int index)291d642b012SHaijun Liu static void t7xx_unmap_bat_page(struct device *dev, struct dpmaif_bat_page *bat_page_base,
292d642b012SHaijun Liu 				unsigned int index)
293d642b012SHaijun Liu {
294d642b012SHaijun Liu 	struct dpmaif_bat_page *bat_page = bat_page_base + index;
295d642b012SHaijun Liu 
296d642b012SHaijun Liu 	if (bat_page->page) {
297d642b012SHaijun Liu 		dma_unmap_page(dev, bat_page->data_bus_addr, bat_page->data_len, DMA_FROM_DEVICE);
298d642b012SHaijun Liu 		put_page(bat_page->page);
299d642b012SHaijun Liu 		bat_page->page = NULL;
300d642b012SHaijun Liu 	}
301d642b012SHaijun Liu }
302d642b012SHaijun Liu 
303d642b012SHaijun Liu /**
304d642b012SHaijun Liu  * t7xx_dpmaif_rx_frag_alloc() - Allocates buffers for the Fragment BAT ring.
305d642b012SHaijun Liu  * @dpmaif_ctrl: Pointer to DPMAIF context structure.
306d642b012SHaijun Liu  * @bat_req: Pointer to BAT request structure.
307d642b012SHaijun Liu  * @buf_cnt: Number of buffers to allocate.
308d642b012SHaijun Liu  * @initial: Indicates if the ring is being populated for the first time.
309d642b012SHaijun Liu  *
310d642b012SHaijun Liu  * Fragment BAT is used when the received packet does not fit in a normal BAT entry.
311d642b012SHaijun Liu  * This function allocates a page fragment and stores the start address of the page
312d642b012SHaijun Liu  * into the Fragment BAT ring.
313d642b012SHaijun Liu  * If this is not the initial call, notify the HW about the new entries.
314d642b012SHaijun Liu  *
315d642b012SHaijun Liu  * Return:
316d642b012SHaijun Liu  * * 0		- Success.
317d642b012SHaijun Liu  * * -ERROR	- Error code.
318d642b012SHaijun Liu  */
t7xx_dpmaif_rx_frag_alloc(struct dpmaif_ctrl * dpmaif_ctrl,struct dpmaif_bat_request * bat_req,const unsigned int buf_cnt,const bool initial)319d642b012SHaijun Liu int t7xx_dpmaif_rx_frag_alloc(struct dpmaif_ctrl *dpmaif_ctrl, struct dpmaif_bat_request *bat_req,
320d642b012SHaijun Liu 			      const unsigned int buf_cnt, const bool initial)
321d642b012SHaijun Liu {
322d642b012SHaijun Liu 	unsigned int buf_space, cur_bat_idx = bat_req->bat_wr_idx;
323d642b012SHaijun Liu 	struct dpmaif_bat_page *bat_skb = bat_req->bat_skb;
324d642b012SHaijun Liu 	int ret = 0, i;
325d642b012SHaijun Liu 
326d642b012SHaijun Liu 	if (!buf_cnt || buf_cnt > bat_req->bat_size_cnt)
327d642b012SHaijun Liu 		return -EINVAL;
328d642b012SHaijun Liu 
329d642b012SHaijun Liu 	buf_space = t7xx_ring_buf_rd_wr_count(bat_req->bat_size_cnt,
330d642b012SHaijun Liu 					      bat_req->bat_release_rd_idx, bat_req->bat_wr_idx,
331d642b012SHaijun Liu 					      DPMAIF_WRITE);
332d642b012SHaijun Liu 	if (buf_cnt > buf_space) {
333d642b012SHaijun Liu 		dev_err(dpmaif_ctrl->dev,
334d642b012SHaijun Liu 			"Requested more buffers than the space available in RX frag ring\n");
335d642b012SHaijun Liu 		return -EINVAL;
336d642b012SHaijun Liu 	}
337d642b012SHaijun Liu 
338d642b012SHaijun Liu 	for (i = 0; i < buf_cnt; i++) {
339d642b012SHaijun Liu 		struct dpmaif_bat_page *cur_page = bat_skb + cur_bat_idx;
340d642b012SHaijun Liu 		struct dpmaif_bat *cur_bat;
341d642b012SHaijun Liu 		dma_addr_t data_base_addr;
342d642b012SHaijun Liu 
343d642b012SHaijun Liu 		if (!cur_page->page) {
344d642b012SHaijun Liu 			unsigned long offset;
345d642b012SHaijun Liu 			struct page *page;
346d642b012SHaijun Liu 			void *data;
347d642b012SHaijun Liu 
348d642b012SHaijun Liu 			data = netdev_alloc_frag(bat_req->pkt_buf_sz);
349d642b012SHaijun Liu 			if (!data)
350d642b012SHaijun Liu 				break;
351d642b012SHaijun Liu 
352d642b012SHaijun Liu 			page = virt_to_head_page(data);
353d642b012SHaijun Liu 			offset = data - page_address(page);
354d642b012SHaijun Liu 
355d642b012SHaijun Liu 			data_base_addr = dma_map_page(dpmaif_ctrl->dev, page, offset,
356d642b012SHaijun Liu 						      bat_req->pkt_buf_sz, DMA_FROM_DEVICE);
357d642b012SHaijun Liu 			if (dma_mapping_error(dpmaif_ctrl->dev, data_base_addr)) {
358d642b012SHaijun Liu 				put_page(virt_to_head_page(data));
359d642b012SHaijun Liu 				dev_err(dpmaif_ctrl->dev, "DMA mapping fail\n");
360d642b012SHaijun Liu 				break;
361d642b012SHaijun Liu 			}
362d642b012SHaijun Liu 
363d642b012SHaijun Liu 			cur_page->page = page;
364d642b012SHaijun Liu 			cur_page->data_bus_addr = data_base_addr;
365d642b012SHaijun Liu 			cur_page->offset = offset;
366d642b012SHaijun Liu 			cur_page->data_len = bat_req->pkt_buf_sz;
367d642b012SHaijun Liu 		}
368d642b012SHaijun Liu 
369d642b012SHaijun Liu 		data_base_addr = cur_page->data_bus_addr;
370d642b012SHaijun Liu 		cur_bat = (struct dpmaif_bat *)bat_req->bat_base + cur_bat_idx;
371d642b012SHaijun Liu 		cur_bat->buffer_addr_ext = upper_32_bits(data_base_addr);
372d642b012SHaijun Liu 		cur_bat->p_buffer_addr = lower_32_bits(data_base_addr);
373d642b012SHaijun Liu 		cur_bat_idx = t7xx_ring_buf_get_next_wr_idx(bat_req->bat_size_cnt, cur_bat_idx);
374d642b012SHaijun Liu 	}
375d642b012SHaijun Liu 
376d642b012SHaijun Liu 	bat_req->bat_wr_idx = cur_bat_idx;
377d642b012SHaijun Liu 
378d642b012SHaijun Liu 	if (!initial)
379d642b012SHaijun Liu 		t7xx_dpmaif_dl_snd_hw_frg_cnt(&dpmaif_ctrl->hw_info, i);
380d642b012SHaijun Liu 
381d642b012SHaijun Liu 	if (i < buf_cnt) {
382d642b012SHaijun Liu 		ret = -ENOMEM;
383d642b012SHaijun Liu 		if (initial) {
384d642b012SHaijun Liu 			while (--i > 0)
385d642b012SHaijun Liu 				t7xx_unmap_bat_page(dpmaif_ctrl->dev, bat_req->bat_skb, i);
386d642b012SHaijun Liu 		}
387d642b012SHaijun Liu 	}
388d642b012SHaijun Liu 
389d642b012SHaijun Liu 	return ret;
390d642b012SHaijun Liu }
391d642b012SHaijun Liu 
t7xx_dpmaif_set_frag_to_skb(const struct dpmaif_rx_queue * rxq,const struct dpmaif_pit * pkt_info,struct sk_buff * skb)392d642b012SHaijun Liu static int t7xx_dpmaif_set_frag_to_skb(const struct dpmaif_rx_queue *rxq,
393d642b012SHaijun Liu 				       const struct dpmaif_pit *pkt_info,
394d642b012SHaijun Liu 				       struct sk_buff *skb)
395d642b012SHaijun Liu {
396d642b012SHaijun Liu 	unsigned long long data_bus_addr, data_base_addr;
397d642b012SHaijun Liu 	struct device *dev = rxq->dpmaif_ctrl->dev;
398d642b012SHaijun Liu 	struct dpmaif_bat_page *page_info;
399d642b012SHaijun Liu 	unsigned int data_len;
400d642b012SHaijun Liu 	int data_offset;
401d642b012SHaijun Liu 
402d642b012SHaijun Liu 	page_info = rxq->bat_frag->bat_skb;
403d642b012SHaijun Liu 	page_info += t7xx_normal_pit_bid(pkt_info);
404d642b012SHaijun Liu 	dma_unmap_page(dev, page_info->data_bus_addr, page_info->data_len, DMA_FROM_DEVICE);
405d642b012SHaijun Liu 
406d642b012SHaijun Liu 	if (!page_info->page)
407d642b012SHaijun Liu 		return -EINVAL;
408d642b012SHaijun Liu 
409d642b012SHaijun Liu 	data_bus_addr = le32_to_cpu(pkt_info->pd.data_addr_h);
410d642b012SHaijun Liu 	data_bus_addr = (data_bus_addr << 32) + le32_to_cpu(pkt_info->pd.data_addr_l);
411d642b012SHaijun Liu 	data_base_addr = page_info->data_bus_addr;
412d642b012SHaijun Liu 	data_offset = data_bus_addr - data_base_addr;
413d642b012SHaijun Liu 	data_offset += page_info->offset;
414d642b012SHaijun Liu 	data_len = FIELD_GET(PD_PIT_DATA_LEN, le32_to_cpu(pkt_info->header));
415d642b012SHaijun Liu 	skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page_info->page,
416d642b012SHaijun Liu 			data_offset, data_len, page_info->data_len);
417d642b012SHaijun Liu 
418d642b012SHaijun Liu 	page_info->page = NULL;
419d642b012SHaijun Liu 	page_info->offset = 0;
420d642b012SHaijun Liu 	page_info->data_len = 0;
421d642b012SHaijun Liu 	return 0;
422d642b012SHaijun Liu }
423d642b012SHaijun Liu 
t7xx_dpmaif_get_frag(struct dpmaif_rx_queue * rxq,const struct dpmaif_pit * pkt_info,const struct dpmaif_cur_rx_skb_info * skb_info)424d642b012SHaijun Liu static int t7xx_dpmaif_get_frag(struct dpmaif_rx_queue *rxq,
425d642b012SHaijun Liu 				const struct dpmaif_pit *pkt_info,
426d642b012SHaijun Liu 				const struct dpmaif_cur_rx_skb_info *skb_info)
427d642b012SHaijun Liu {
428d642b012SHaijun Liu 	unsigned int cur_bid = t7xx_normal_pit_bid(pkt_info);
429d642b012SHaijun Liu 	int ret;
430d642b012SHaijun Liu 
431d642b012SHaijun Liu 	ret = t7xx_frag_bat_cur_bid_check(rxq, cur_bid);
432d642b012SHaijun Liu 	if (ret < 0)
433d642b012SHaijun Liu 		return ret;
434d642b012SHaijun Liu 
435d642b012SHaijun Liu 	ret = t7xx_dpmaif_set_frag_to_skb(rxq, pkt_info, skb_info->cur_skb);
436d642b012SHaijun Liu 	if (ret < 0) {
437d642b012SHaijun Liu 		dev_err(rxq->dpmaif_ctrl->dev, "Failed to set frag data to skb: %d\n", ret);
438d642b012SHaijun Liu 		return ret;
439d642b012SHaijun Liu 	}
440d642b012SHaijun Liu 
441d642b012SHaijun Liu 	t7xx_dpmaif_set_bat_mask(rxq->bat_frag, cur_bid);
442d642b012SHaijun Liu 	return 0;
443d642b012SHaijun Liu }
444d642b012SHaijun Liu 
t7xx_bat_cur_bid_check(struct dpmaif_rx_queue * rxq,const unsigned int cur_bid)445d642b012SHaijun Liu static int t7xx_bat_cur_bid_check(struct dpmaif_rx_queue *rxq, const unsigned int cur_bid)
446d642b012SHaijun Liu {
447d642b012SHaijun Liu 	struct dpmaif_bat_skb *bat_skb = rxq->bat_req->bat_skb;
448d642b012SHaijun Liu 
449d642b012SHaijun Liu 	bat_skb += cur_bid;
450d642b012SHaijun Liu 	if (cur_bid >= DPMAIF_BAT_COUNT || !bat_skb->skb)
451d642b012SHaijun Liu 		return -EINVAL;
452d642b012SHaijun Liu 
453d642b012SHaijun Liu 	return 0;
454d642b012SHaijun Liu }
455d642b012SHaijun Liu 
t7xx_dpmaif_read_pit_seq(const struct dpmaif_pit * pit)456d642b012SHaijun Liu static int t7xx_dpmaif_read_pit_seq(const struct dpmaif_pit *pit)
457d642b012SHaijun Liu {
458d642b012SHaijun Liu 	return FIELD_GET(PD_PIT_PIT_SEQ, le32_to_cpu(pit->pd.footer));
459d642b012SHaijun Liu }
460d642b012SHaijun Liu 
t7xx_dpmaif_check_pit_seq(struct dpmaif_rx_queue * rxq,const struct dpmaif_pit * pit)461d642b012SHaijun Liu static int t7xx_dpmaif_check_pit_seq(struct dpmaif_rx_queue *rxq,
462d642b012SHaijun Liu 				     const struct dpmaif_pit *pit)
463d642b012SHaijun Liu {
464d642b012SHaijun Liu 	unsigned int cur_pit_seq, expect_pit_seq = rxq->expect_pit_seq;
465d642b012SHaijun Liu 
466d642b012SHaijun Liu 	if (read_poll_timeout_atomic(t7xx_dpmaif_read_pit_seq, cur_pit_seq,
467d642b012SHaijun Liu 				     cur_pit_seq == expect_pit_seq, DPMAIF_POLL_PIT_TIME_US,
468d642b012SHaijun Liu 				     DPMAIF_POLL_PIT_MAX_TIME_US, false, pit))
469d642b012SHaijun Liu 		return -EFAULT;
470d642b012SHaijun Liu 
471d642b012SHaijun Liu 	rxq->expect_pit_seq++;
472d642b012SHaijun Liu 	if (rxq->expect_pit_seq >= DPMAIF_DL_PIT_SEQ_VALUE)
473d642b012SHaijun Liu 		rxq->expect_pit_seq = 0;
474d642b012SHaijun Liu 
475d642b012SHaijun Liu 	return 0;
476d642b012SHaijun Liu }
477d642b012SHaijun Liu 
t7xx_dpmaif_avail_pkt_bat_cnt(struct dpmaif_bat_request * bat_req)478d642b012SHaijun Liu static unsigned int t7xx_dpmaif_avail_pkt_bat_cnt(struct dpmaif_bat_request *bat_req)
479d642b012SHaijun Liu {
480d642b012SHaijun Liu 	unsigned int zero_index;
481d642b012SHaijun Liu 	unsigned long flags;
482d642b012SHaijun Liu 
483d642b012SHaijun Liu 	spin_lock_irqsave(&bat_req->mask_lock, flags);
484d642b012SHaijun Liu 
485d642b012SHaijun Liu 	zero_index = find_next_zero_bit(bat_req->bat_bitmap, bat_req->bat_size_cnt,
486d642b012SHaijun Liu 					bat_req->bat_release_rd_idx);
487d642b012SHaijun Liu 
488d642b012SHaijun Liu 	if (zero_index < bat_req->bat_size_cnt) {
489d642b012SHaijun Liu 		spin_unlock_irqrestore(&bat_req->mask_lock, flags);
490d642b012SHaijun Liu 		return zero_index - bat_req->bat_release_rd_idx;
491d642b012SHaijun Liu 	}
492d642b012SHaijun Liu 
493d642b012SHaijun Liu 	/* limiting the search till bat_release_rd_idx */
494d642b012SHaijun Liu 	zero_index = find_first_zero_bit(bat_req->bat_bitmap, bat_req->bat_release_rd_idx);
495d642b012SHaijun Liu 	spin_unlock_irqrestore(&bat_req->mask_lock, flags);
496d642b012SHaijun Liu 	return bat_req->bat_size_cnt - bat_req->bat_release_rd_idx + zero_index;
497d642b012SHaijun Liu }
498d642b012SHaijun Liu 
t7xx_dpmaif_release_bat_entry(const struct dpmaif_rx_queue * rxq,const unsigned int rel_entry_num,const enum bat_type buf_type)499d642b012SHaijun Liu static int t7xx_dpmaif_release_bat_entry(const struct dpmaif_rx_queue *rxq,
500d642b012SHaijun Liu 					 const unsigned int rel_entry_num,
501d642b012SHaijun Liu 					 const enum bat_type buf_type)
502d642b012SHaijun Liu {
503d642b012SHaijun Liu 	struct dpmaif_hw_info *hw_info = &rxq->dpmaif_ctrl->hw_info;
504d642b012SHaijun Liu 	unsigned int old_rel_idx, new_rel_idx, hw_rd_idx, i;
505d642b012SHaijun Liu 	struct dpmaif_bat_request *bat;
506d642b012SHaijun Liu 	unsigned long flags;
507d642b012SHaijun Liu 
508d642b012SHaijun Liu 	if (!rxq->que_started || !rel_entry_num)
509d642b012SHaijun Liu 		return -EINVAL;
510d642b012SHaijun Liu 
511d642b012SHaijun Liu 	if (buf_type == BAT_TYPE_FRAG) {
512d642b012SHaijun Liu 		bat = rxq->bat_frag;
513d642b012SHaijun Liu 		hw_rd_idx = t7xx_dpmaif_dl_get_frg_rd_idx(hw_info, rxq->index);
514d642b012SHaijun Liu 	} else {
515d642b012SHaijun Liu 		bat = rxq->bat_req;
516d642b012SHaijun Liu 		hw_rd_idx = t7xx_dpmaif_dl_get_bat_rd_idx(hw_info, rxq->index);
517d642b012SHaijun Liu 	}
518d642b012SHaijun Liu 
519d642b012SHaijun Liu 	if (rel_entry_num >= bat->bat_size_cnt)
520d642b012SHaijun Liu 		return -EINVAL;
521d642b012SHaijun Liu 
522d642b012SHaijun Liu 	old_rel_idx = bat->bat_release_rd_idx;
523d642b012SHaijun Liu 	new_rel_idx = old_rel_idx + rel_entry_num;
524d642b012SHaijun Liu 
525d642b012SHaijun Liu 	/* Do not need to release if the queue is empty */
526d642b012SHaijun Liu 	if (bat->bat_wr_idx == old_rel_idx)
527d642b012SHaijun Liu 		return 0;
528d642b012SHaijun Liu 
529d642b012SHaijun Liu 	if (hw_rd_idx >= old_rel_idx) {
530d642b012SHaijun Liu 		if (new_rel_idx > hw_rd_idx)
531d642b012SHaijun Liu 			return -EINVAL;
532d642b012SHaijun Liu 	}
533d642b012SHaijun Liu 
534d642b012SHaijun Liu 	if (new_rel_idx >= bat->bat_size_cnt) {
535d642b012SHaijun Liu 		new_rel_idx -= bat->bat_size_cnt;
536d642b012SHaijun Liu 		if (new_rel_idx > hw_rd_idx)
537d642b012SHaijun Liu 			return -EINVAL;
538d642b012SHaijun Liu 	}
539d642b012SHaijun Liu 
540d642b012SHaijun Liu 	spin_lock_irqsave(&bat->mask_lock, flags);
541d642b012SHaijun Liu 	for (i = 0; i < rel_entry_num; i++) {
542d642b012SHaijun Liu 		unsigned int index = bat->bat_release_rd_idx + i;
543d642b012SHaijun Liu 
544d642b012SHaijun Liu 		if (index >= bat->bat_size_cnt)
545d642b012SHaijun Liu 			index -= bat->bat_size_cnt;
546d642b012SHaijun Liu 
547d642b012SHaijun Liu 		clear_bit(index, bat->bat_bitmap);
548d642b012SHaijun Liu 	}
549d642b012SHaijun Liu 	spin_unlock_irqrestore(&bat->mask_lock, flags);
550d642b012SHaijun Liu 
551d642b012SHaijun Liu 	bat->bat_release_rd_idx = new_rel_idx;
552d642b012SHaijun Liu 	return rel_entry_num;
553d642b012SHaijun Liu }
554d642b012SHaijun Liu 
t7xx_dpmaif_pit_release_and_add(struct dpmaif_rx_queue * rxq)555d642b012SHaijun Liu static int t7xx_dpmaif_pit_release_and_add(struct dpmaif_rx_queue *rxq)
556d642b012SHaijun Liu {
557d642b012SHaijun Liu 	int ret;
558d642b012SHaijun Liu 
559d642b012SHaijun Liu 	if (rxq->pit_remain_release_cnt < DPMAIF_PIT_CNT_THRESHOLD)
560d642b012SHaijun Liu 		return 0;
561d642b012SHaijun Liu 
562d642b012SHaijun Liu 	ret = t7xx_dpmaifq_release_pit_entry(rxq, rxq->pit_remain_release_cnt);
563d642b012SHaijun Liu 	if (ret)
564d642b012SHaijun Liu 		return ret;
565d642b012SHaijun Liu 
566d642b012SHaijun Liu 	rxq->pit_remain_release_cnt = 0;
567d642b012SHaijun Liu 	return 0;
568d642b012SHaijun Liu }
569d642b012SHaijun Liu 
t7xx_dpmaif_bat_release_and_add(const struct dpmaif_rx_queue * rxq)570d642b012SHaijun Liu static int t7xx_dpmaif_bat_release_and_add(const struct dpmaif_rx_queue *rxq)
571d642b012SHaijun Liu {
572d642b012SHaijun Liu 	unsigned int bid_cnt;
573d642b012SHaijun Liu 	int ret;
574d642b012SHaijun Liu 
575d642b012SHaijun Liu 	bid_cnt = t7xx_dpmaif_avail_pkt_bat_cnt(rxq->bat_req);
576d642b012SHaijun Liu 	if (bid_cnt < DPMAIF_BAT_CNT_THRESHOLD)
577d642b012SHaijun Liu 		return 0;
578d642b012SHaijun Liu 
579d642b012SHaijun Liu 	ret = t7xx_dpmaif_release_bat_entry(rxq, bid_cnt, BAT_TYPE_NORMAL);
580d642b012SHaijun Liu 	if (ret <= 0) {
581d642b012SHaijun Liu 		dev_err(rxq->dpmaif_ctrl->dev, "Release PKT BAT failed: %d\n", ret);
582d642b012SHaijun Liu 		return ret;
583d642b012SHaijun Liu 	}
584d642b012SHaijun Liu 
585d642b012SHaijun Liu 	ret = t7xx_dpmaif_rx_buf_alloc(rxq->dpmaif_ctrl, rxq->bat_req, rxq->index, bid_cnt, false);
586d642b012SHaijun Liu 	if (ret < 0)
587d642b012SHaijun Liu 		dev_err(rxq->dpmaif_ctrl->dev, "Allocate new RX buffer failed: %d\n", ret);
588d642b012SHaijun Liu 
589d642b012SHaijun Liu 	return ret;
590d642b012SHaijun Liu }
591d642b012SHaijun Liu 
t7xx_dpmaif_frag_bat_release_and_add(const struct dpmaif_rx_queue * rxq)592d642b012SHaijun Liu static int t7xx_dpmaif_frag_bat_release_and_add(const struct dpmaif_rx_queue *rxq)
593d642b012SHaijun Liu {
594d642b012SHaijun Liu 	unsigned int bid_cnt;
595d642b012SHaijun Liu 	int ret;
596d642b012SHaijun Liu 
597d642b012SHaijun Liu 	bid_cnt = t7xx_dpmaif_avail_pkt_bat_cnt(rxq->bat_frag);
598d642b012SHaijun Liu 	if (bid_cnt < DPMAIF_BAT_CNT_THRESHOLD)
599d642b012SHaijun Liu 		return 0;
600d642b012SHaijun Liu 
601d642b012SHaijun Liu 	ret = t7xx_dpmaif_release_bat_entry(rxq, bid_cnt, BAT_TYPE_FRAG);
602d642b012SHaijun Liu 	if (ret <= 0) {
603d642b012SHaijun Liu 		dev_err(rxq->dpmaif_ctrl->dev, "Release BAT entry failed: %d\n", ret);
604d642b012SHaijun Liu 		return ret;
605d642b012SHaijun Liu 	}
606d642b012SHaijun Liu 
607d642b012SHaijun Liu 	return t7xx_dpmaif_rx_frag_alloc(rxq->dpmaif_ctrl, rxq->bat_frag, bid_cnt, false);
608d642b012SHaijun Liu }
609d642b012SHaijun Liu 
t7xx_dpmaif_parse_msg_pit(const struct dpmaif_rx_queue * rxq,const struct dpmaif_pit * msg_pit,struct dpmaif_cur_rx_skb_info * skb_info)610d642b012SHaijun Liu static void t7xx_dpmaif_parse_msg_pit(const struct dpmaif_rx_queue *rxq,
611d642b012SHaijun Liu 				      const struct dpmaif_pit *msg_pit,
612d642b012SHaijun Liu 				      struct dpmaif_cur_rx_skb_info *skb_info)
613d642b012SHaijun Liu {
614d642b012SHaijun Liu 	int header = le32_to_cpu(msg_pit->header);
615d642b012SHaijun Liu 
616d642b012SHaijun Liu 	skb_info->cur_chn_idx = FIELD_GET(MSG_PIT_CHANNEL_ID, header);
617d642b012SHaijun Liu 	skb_info->check_sum = FIELD_GET(MSG_PIT_CHECKSUM, header);
618d642b012SHaijun Liu 	skb_info->pit_dp = FIELD_GET(MSG_PIT_DP, header);
619d642b012SHaijun Liu 	skb_info->pkt_type = FIELD_GET(MSG_PIT_IP, le32_to_cpu(msg_pit->msg.params_3));
620d642b012SHaijun Liu }
621d642b012SHaijun Liu 
t7xx_dpmaif_set_data_to_skb(const struct dpmaif_rx_queue * rxq,const struct dpmaif_pit * pkt_info,struct dpmaif_cur_rx_skb_info * skb_info)622d642b012SHaijun Liu static int t7xx_dpmaif_set_data_to_skb(const struct dpmaif_rx_queue *rxq,
623d642b012SHaijun Liu 				       const struct dpmaif_pit *pkt_info,
624d642b012SHaijun Liu 				       struct dpmaif_cur_rx_skb_info *skb_info)
625d642b012SHaijun Liu {
626d642b012SHaijun Liu 	unsigned long long data_bus_addr, data_base_addr;
627d642b012SHaijun Liu 	struct device *dev = rxq->dpmaif_ctrl->dev;
628d642b012SHaijun Liu 	struct dpmaif_bat_skb *bat_skb;
629d642b012SHaijun Liu 	unsigned int data_len;
630d642b012SHaijun Liu 	struct sk_buff *skb;
631d642b012SHaijun Liu 	int data_offset;
632d642b012SHaijun Liu 
633d642b012SHaijun Liu 	bat_skb = rxq->bat_req->bat_skb;
634d642b012SHaijun Liu 	bat_skb += t7xx_normal_pit_bid(pkt_info);
635d642b012SHaijun Liu 	dma_unmap_single(dev, bat_skb->data_bus_addr, bat_skb->data_len, DMA_FROM_DEVICE);
636d642b012SHaijun Liu 
637d642b012SHaijun Liu 	data_bus_addr = le32_to_cpu(pkt_info->pd.data_addr_h);
638d642b012SHaijun Liu 	data_bus_addr = (data_bus_addr << 32) + le32_to_cpu(pkt_info->pd.data_addr_l);
639d642b012SHaijun Liu 	data_base_addr = bat_skb->data_bus_addr;
640d642b012SHaijun Liu 	data_offset = data_bus_addr - data_base_addr;
641d642b012SHaijun Liu 	data_len = FIELD_GET(PD_PIT_DATA_LEN, le32_to_cpu(pkt_info->header));
642d642b012SHaijun Liu 	skb = bat_skb->skb;
643d642b012SHaijun Liu 	skb->len = 0;
644d642b012SHaijun Liu 	skb_reset_tail_pointer(skb);
645d642b012SHaijun Liu 	skb_reserve(skb, data_offset);
646d642b012SHaijun Liu 
647d642b012SHaijun Liu 	if (skb->tail + data_len > skb->end) {
648d642b012SHaijun Liu 		dev_err(dev, "No buffer space available\n");
649d642b012SHaijun Liu 		return -ENOBUFS;
650d642b012SHaijun Liu 	}
651d642b012SHaijun Liu 
652d642b012SHaijun Liu 	skb_put(skb, data_len);
653d642b012SHaijun Liu 	skb_info->cur_skb = skb;
654d642b012SHaijun Liu 	bat_skb->skb = NULL;
655d642b012SHaijun Liu 	return 0;
656d642b012SHaijun Liu }
657d642b012SHaijun Liu 
t7xx_dpmaif_get_rx_pkt(struct dpmaif_rx_queue * rxq,const struct dpmaif_pit * pkt_info,struct dpmaif_cur_rx_skb_info * skb_info)658d642b012SHaijun Liu static int t7xx_dpmaif_get_rx_pkt(struct dpmaif_rx_queue *rxq,
659d642b012SHaijun Liu 				  const struct dpmaif_pit *pkt_info,
660d642b012SHaijun Liu 				  struct dpmaif_cur_rx_skb_info *skb_info)
661d642b012SHaijun Liu {
662d642b012SHaijun Liu 	unsigned int cur_bid = t7xx_normal_pit_bid(pkt_info);
663d642b012SHaijun Liu 	int ret;
664d642b012SHaijun Liu 
665d642b012SHaijun Liu 	ret = t7xx_bat_cur_bid_check(rxq, cur_bid);
666d642b012SHaijun Liu 	if (ret < 0)
667d642b012SHaijun Liu 		return ret;
668d642b012SHaijun Liu 
669d642b012SHaijun Liu 	ret = t7xx_dpmaif_set_data_to_skb(rxq, pkt_info, skb_info);
670d642b012SHaijun Liu 	if (ret < 0) {
671d642b012SHaijun Liu 		dev_err(rxq->dpmaif_ctrl->dev, "RX set data to skb failed: %d\n", ret);
672d642b012SHaijun Liu 		return ret;
673d642b012SHaijun Liu 	}
674d642b012SHaijun Liu 
675d642b012SHaijun Liu 	t7xx_dpmaif_set_bat_mask(rxq->bat_req, cur_bid);
676d642b012SHaijun Liu 	return 0;
677d642b012SHaijun Liu }
678d642b012SHaijun Liu 
t7xx_dpmaifq_rx_notify_hw(struct dpmaif_rx_queue * rxq)679d642b012SHaijun Liu static int t7xx_dpmaifq_rx_notify_hw(struct dpmaif_rx_queue *rxq)
680d642b012SHaijun Liu {
681d642b012SHaijun Liu 	struct dpmaif_ctrl *dpmaif_ctrl = rxq->dpmaif_ctrl;
682d642b012SHaijun Liu 	int ret;
683d642b012SHaijun Liu 
684d642b012SHaijun Liu 	queue_work(dpmaif_ctrl->bat_release_wq, &dpmaif_ctrl->bat_release_work);
685d642b012SHaijun Liu 
686d642b012SHaijun Liu 	ret = t7xx_dpmaif_pit_release_and_add(rxq);
687d642b012SHaijun Liu 	if (ret < 0)
688d642b012SHaijun Liu 		dev_err(dpmaif_ctrl->dev, "RXQ%u update PIT failed: %d\n", rxq->index, ret);
689d642b012SHaijun Liu 
690d642b012SHaijun Liu 	return ret;
691d642b012SHaijun Liu }
692d642b012SHaijun Liu 
t7xx_dpmaif_rx_skb(struct dpmaif_rx_queue * rxq,struct dpmaif_cur_rx_skb_info * skb_info)693d642b012SHaijun Liu static void t7xx_dpmaif_rx_skb(struct dpmaif_rx_queue *rxq,
694d642b012SHaijun Liu 			       struct dpmaif_cur_rx_skb_info *skb_info)
695d642b012SHaijun Liu {
6965545b7b9SHaijun Liu 	struct dpmaif_ctrl *dpmaif_ctrl = rxq->dpmaif_ctrl;
697d642b012SHaijun Liu 	struct sk_buff *skb = skb_info->cur_skb;
698d642b012SHaijun Liu 	struct t7xx_skb_cb *skb_cb;
699d642b012SHaijun Liu 	u8 netif_id;
700d642b012SHaijun Liu 
701d642b012SHaijun Liu 	skb_info->cur_skb = NULL;
702d642b012SHaijun Liu 
703d642b012SHaijun Liu 	if (skb_info->pit_dp) {
704d642b012SHaijun Liu 		dev_kfree_skb_any(skb);
705d642b012SHaijun Liu 		return;
706d642b012SHaijun Liu 	}
707d642b012SHaijun Liu 
708d642b012SHaijun Liu 	skb->ip_summed = skb_info->check_sum == DPMAIF_CS_RESULT_PASS ? CHECKSUM_UNNECESSARY :
709d642b012SHaijun Liu 									CHECKSUM_NONE;
710d642b012SHaijun Liu 	netif_id = FIELD_GET(NETIF_MASK, skb_info->cur_chn_idx);
711d642b012SHaijun Liu 	skb_cb = T7XX_SKB_CB(skb);
712d642b012SHaijun Liu 	skb_cb->netif_idx = netif_id;
713d642b012SHaijun Liu 	skb_cb->rx_pkt_type = skb_info->pkt_type;
7145545b7b9SHaijun Liu 	dpmaif_ctrl->callbacks->recv_skb(dpmaif_ctrl->t7xx_dev->ccmni_ctlb, skb, &rxq->napi);
715d642b012SHaijun Liu }
716d642b012SHaijun Liu 
t7xx_dpmaif_rx_start(struct dpmaif_rx_queue * rxq,const unsigned int pit_cnt,const unsigned int budget,int * once_more)717d642b012SHaijun Liu static int t7xx_dpmaif_rx_start(struct dpmaif_rx_queue *rxq, const unsigned int pit_cnt,
7185545b7b9SHaijun Liu 				const unsigned int budget, int *once_more)
719d642b012SHaijun Liu {
720d642b012SHaijun Liu 	unsigned int cur_pit, pit_len, rx_cnt, recv_skb_cnt = 0;
721d642b012SHaijun Liu 	struct device *dev = rxq->dpmaif_ctrl->dev;
722d642b012SHaijun Liu 	struct dpmaif_cur_rx_skb_info *skb_info;
723d642b012SHaijun Liu 	int ret = 0;
724d642b012SHaijun Liu 
725d642b012SHaijun Liu 	pit_len = rxq->pit_size_cnt;
726d642b012SHaijun Liu 	skb_info = &rxq->rx_data_info;
727d642b012SHaijun Liu 	cur_pit = rxq->pit_rd_idx;
728d642b012SHaijun Liu 
729d642b012SHaijun Liu 	for (rx_cnt = 0; rx_cnt < pit_cnt; rx_cnt++) {
730d642b012SHaijun Liu 		struct dpmaif_pit *pkt_info;
731d642b012SHaijun Liu 		u32 val;
732d642b012SHaijun Liu 
7335545b7b9SHaijun Liu 		if (!skb_info->msg_pit_received && recv_skb_cnt >= budget)
734d642b012SHaijun Liu 			break;
735d642b012SHaijun Liu 
736d642b012SHaijun Liu 		pkt_info = (struct dpmaif_pit *)rxq->pit_base + cur_pit;
737d642b012SHaijun Liu 		if (t7xx_dpmaif_check_pit_seq(rxq, pkt_info)) {
738d642b012SHaijun Liu 			dev_err_ratelimited(dev, "RXQ%u checks PIT SEQ fail\n", rxq->index);
7395545b7b9SHaijun Liu 			*once_more = 1;
7405545b7b9SHaijun Liu 			return recv_skb_cnt;
741d642b012SHaijun Liu 		}
742d642b012SHaijun Liu 
743d642b012SHaijun Liu 		val = FIELD_GET(PD_PIT_PACKET_TYPE, le32_to_cpu(pkt_info->header));
744d642b012SHaijun Liu 		if (val == DES_PT_MSG) {
745d642b012SHaijun Liu 			if (skb_info->msg_pit_received)
746d642b012SHaijun Liu 				dev_err(dev, "RXQ%u received repeated PIT\n", rxq->index);
747d642b012SHaijun Liu 
748d642b012SHaijun Liu 			skb_info->msg_pit_received = true;
749d642b012SHaijun Liu 			t7xx_dpmaif_parse_msg_pit(rxq, pkt_info, skb_info);
750d642b012SHaijun Liu 		} else { /* DES_PT_PD */
751d642b012SHaijun Liu 			val = FIELD_GET(PD_PIT_BUFFER_TYPE, le32_to_cpu(pkt_info->header));
752d642b012SHaijun Liu 			if (val != PKT_BUF_FRAG)
753d642b012SHaijun Liu 				ret = t7xx_dpmaif_get_rx_pkt(rxq, pkt_info, skb_info);
754d642b012SHaijun Liu 			else if (!skb_info->cur_skb)
755d642b012SHaijun Liu 				ret = -EINVAL;
756d642b012SHaijun Liu 			else
757d642b012SHaijun Liu 				ret = t7xx_dpmaif_get_frag(rxq, pkt_info, skb_info);
758d642b012SHaijun Liu 
759d642b012SHaijun Liu 			if (ret < 0) {
760d642b012SHaijun Liu 				skb_info->err_payload = 1;
761d642b012SHaijun Liu 				dev_err_ratelimited(dev, "RXQ%u error payload\n", rxq->index);
762d642b012SHaijun Liu 			}
763d642b012SHaijun Liu 
764d642b012SHaijun Liu 			val = FIELD_GET(PD_PIT_CONT, le32_to_cpu(pkt_info->header));
765d642b012SHaijun Liu 			if (!val) {
766d642b012SHaijun Liu 				if (!skb_info->err_payload) {
767d642b012SHaijun Liu 					t7xx_dpmaif_rx_skb(rxq, skb_info);
768d642b012SHaijun Liu 				} else if (skb_info->cur_skb) {
769d642b012SHaijun Liu 					dev_kfree_skb_any(skb_info->cur_skb);
770d642b012SHaijun Liu 					skb_info->cur_skb = NULL;
771d642b012SHaijun Liu 				}
772d642b012SHaijun Liu 
773d642b012SHaijun Liu 				memset(skb_info, 0, sizeof(*skb_info));
774d642b012SHaijun Liu 				recv_skb_cnt++;
775d642b012SHaijun Liu 			}
776d642b012SHaijun Liu 		}
777d642b012SHaijun Liu 
778d642b012SHaijun Liu 		cur_pit = t7xx_ring_buf_get_next_wr_idx(pit_len, cur_pit);
779d642b012SHaijun Liu 		rxq->pit_rd_idx = cur_pit;
780d642b012SHaijun Liu 		rxq->pit_remain_release_cnt++;
781d642b012SHaijun Liu 
782d642b012SHaijun Liu 		if (rx_cnt > 0 && !(rx_cnt % DPMAIF_NOTIFY_RELEASE_COUNT)) {
783d642b012SHaijun Liu 			ret = t7xx_dpmaifq_rx_notify_hw(rxq);
784d642b012SHaijun Liu 			if (ret < 0)
785d642b012SHaijun Liu 				break;
786d642b012SHaijun Liu 		}
787d642b012SHaijun Liu 	}
788d642b012SHaijun Liu 
789d642b012SHaijun Liu 	if (!ret)
790d642b012SHaijun Liu 		ret = t7xx_dpmaifq_rx_notify_hw(rxq);
791d642b012SHaijun Liu 
792d642b012SHaijun Liu 	if (ret)
793d642b012SHaijun Liu 		return ret;
794d642b012SHaijun Liu 
7955545b7b9SHaijun Liu 	return recv_skb_cnt;
796d642b012SHaijun Liu }
797d642b012SHaijun Liu 
t7xx_dpmaifq_poll_pit(struct dpmaif_rx_queue * rxq)798d642b012SHaijun Liu static unsigned int t7xx_dpmaifq_poll_pit(struct dpmaif_rx_queue *rxq)
799d642b012SHaijun Liu {
800d642b012SHaijun Liu 	unsigned int hw_wr_idx, pit_cnt;
801d642b012SHaijun Liu 
802d642b012SHaijun Liu 	if (!rxq->que_started)
803d642b012SHaijun Liu 		return 0;
804d642b012SHaijun Liu 
805d642b012SHaijun Liu 	hw_wr_idx = t7xx_dpmaif_dl_dlq_pit_get_wr_idx(&rxq->dpmaif_ctrl->hw_info, rxq->index);
806d642b012SHaijun Liu 	pit_cnt = t7xx_ring_buf_rd_wr_count(rxq->pit_size_cnt, rxq->pit_rd_idx, hw_wr_idx,
807d642b012SHaijun Liu 					    DPMAIF_READ);
808d642b012SHaijun Liu 	rxq->pit_wr_idx = hw_wr_idx;
809d642b012SHaijun Liu 	return pit_cnt;
810d642b012SHaijun Liu }
811d642b012SHaijun Liu 
t7xx_dpmaif_napi_rx_data_collect(struct dpmaif_ctrl * dpmaif_ctrl,const unsigned int q_num,const unsigned int budget,int * once_more)8125545b7b9SHaijun Liu static int t7xx_dpmaif_napi_rx_data_collect(struct dpmaif_ctrl *dpmaif_ctrl,
8135545b7b9SHaijun Liu 					    const unsigned int q_num,
8145545b7b9SHaijun Liu 					    const unsigned int budget, int *once_more)
815d642b012SHaijun Liu {
816d642b012SHaijun Liu 	struct dpmaif_rx_queue *rxq = &dpmaif_ctrl->rxq[q_num];
817d642b012SHaijun Liu 	unsigned int cnt;
8185545b7b9SHaijun Liu 	int ret = 0;
819d642b012SHaijun Liu 
8205545b7b9SHaijun Liu 	cnt = t7xx_dpmaifq_poll_pit(rxq);
8215545b7b9SHaijun Liu 	if (!cnt)
8225545b7b9SHaijun Liu 		return ret;
823d642b012SHaijun Liu 
8245545b7b9SHaijun Liu 	ret = t7xx_dpmaif_rx_start(rxq, cnt, budget, once_more);
8255545b7b9SHaijun Liu 	if (ret < 0)
8265545b7b9SHaijun Liu 		dev_err(dpmaif_ctrl->dev, "dlq%u rx ERR:%d\n", rxq->index, ret);
827d642b012SHaijun Liu 
8285545b7b9SHaijun Liu 	return ret;
829d642b012SHaijun Liu }
830d642b012SHaijun Liu 
t7xx_dpmaif_napi_rx_poll(struct napi_struct * napi,const int budget)8315545b7b9SHaijun Liu int t7xx_dpmaif_napi_rx_poll(struct napi_struct *napi, const int budget)
832d642b012SHaijun Liu {
8335545b7b9SHaijun Liu 	struct dpmaif_rx_queue *rxq = container_of(napi, struct dpmaif_rx_queue, napi);
8345545b7b9SHaijun Liu 	struct t7xx_pci_dev *t7xx_dev = rxq->dpmaif_ctrl->t7xx_dev;
8355545b7b9SHaijun Liu 	int ret, once_more = 0, work_done = 0;
836d642b012SHaijun Liu 
837d642b012SHaijun Liu 	atomic_set(&rxq->rx_processing, 1);
838d642b012SHaijun Liu 	/* Ensure rx_processing is changed to 1 before actually begin RX flow */
839d642b012SHaijun Liu 	smp_mb();
840d642b012SHaijun Liu 
841d642b012SHaijun Liu 	if (!rxq->que_started) {
842d642b012SHaijun Liu 		atomic_set(&rxq->rx_processing, 0);
843*364d0221SKornel Dulęba 		pm_runtime_put_autosuspend(rxq->dpmaif_ctrl->dev);
8445545b7b9SHaijun Liu 		dev_err(rxq->dpmaif_ctrl->dev, "Work RXQ: %d has not been started\n", rxq->index);
8455545b7b9SHaijun Liu 		return work_done;
846d642b012SHaijun Liu 	}
847d642b012SHaijun Liu 
848*364d0221SKornel Dulęba 	if (!rxq->sleep_lock_pending)
8495545b7b9SHaijun Liu 		t7xx_pci_disable_sleep(t7xx_dev);
850d10b3a69SHaijun Liu 
8515545b7b9SHaijun Liu 	ret = try_wait_for_completion(&t7xx_dev->sleep_lock_acquire);
8525545b7b9SHaijun Liu 	if (!ret) {
8535545b7b9SHaijun Liu 		napi_complete_done(napi, work_done);
8545545b7b9SHaijun Liu 		rxq->sleep_lock_pending = true;
8555545b7b9SHaijun Liu 		napi_reschedule(napi);
8565545b7b9SHaijun Liu 		return work_done;
8575545b7b9SHaijun Liu 	}
858d10b3a69SHaijun Liu 
8595545b7b9SHaijun Liu 	rxq->sleep_lock_pending = false;
8605545b7b9SHaijun Liu 	while (work_done < budget) {
8615545b7b9SHaijun Liu 		int each_budget = budget - work_done;
8625545b7b9SHaijun Liu 		int rx_cnt = t7xx_dpmaif_napi_rx_data_collect(rxq->dpmaif_ctrl, rxq->index,
8635545b7b9SHaijun Liu 							      each_budget, &once_more);
8645545b7b9SHaijun Liu 		if (rx_cnt > 0)
8655545b7b9SHaijun Liu 			work_done += rx_cnt;
8665545b7b9SHaijun Liu 		else
8675545b7b9SHaijun Liu 			break;
8685545b7b9SHaijun Liu 	}
8695545b7b9SHaijun Liu 
8705545b7b9SHaijun Liu 	if (once_more) {
8715545b7b9SHaijun Liu 		napi_gro_flush(napi, false);
8725545b7b9SHaijun Liu 		work_done = budget;
8735545b7b9SHaijun Liu 		t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info);
8745545b7b9SHaijun Liu 	} else if (work_done < budget) {
8755545b7b9SHaijun Liu 		napi_complete_done(napi, work_done);
8765545b7b9SHaijun Liu 		t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info);
8775545b7b9SHaijun Liu 		t7xx_dpmaif_dlq_unmask_rx_done(&rxq->dpmaif_ctrl->hw_info, rxq->index);
878*364d0221SKornel Dulęba 		t7xx_pci_enable_sleep(rxq->dpmaif_ctrl->t7xx_dev);
879*364d0221SKornel Dulęba 		pm_runtime_mark_last_busy(rxq->dpmaif_ctrl->dev);
880*364d0221SKornel Dulęba 		pm_runtime_put_autosuspend(rxq->dpmaif_ctrl->dev);
881*364d0221SKornel Dulęba 		atomic_set(&rxq->rx_processing, 0);
8825545b7b9SHaijun Liu 	} else {
8835545b7b9SHaijun Liu 		t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info);
8845545b7b9SHaijun Liu 	}
8855545b7b9SHaijun Liu 
8865545b7b9SHaijun Liu 	return work_done;
887d642b012SHaijun Liu }
888d642b012SHaijun Liu 
t7xx_dpmaif_irq_rx_done(struct dpmaif_ctrl * dpmaif_ctrl,const unsigned int que_mask)889d642b012SHaijun Liu void t7xx_dpmaif_irq_rx_done(struct dpmaif_ctrl *dpmaif_ctrl, const unsigned int que_mask)
890d642b012SHaijun Liu {
891d642b012SHaijun Liu 	struct dpmaif_rx_queue *rxq;
892*364d0221SKornel Dulęba 	struct dpmaif_ctrl *ctrl;
893*364d0221SKornel Dulęba 	int qno, ret;
894d642b012SHaijun Liu 
895d642b012SHaijun Liu 	qno = ffs(que_mask) - 1;
896d642b012SHaijun Liu 	if (qno < 0 || qno > DPMAIF_RXQ_NUM - 1) {
897d642b012SHaijun Liu 		dev_err(dpmaif_ctrl->dev, "Invalid RXQ number: %u\n", qno);
898d642b012SHaijun Liu 		return;
899d642b012SHaijun Liu 	}
900d642b012SHaijun Liu 
901d642b012SHaijun Liu 	rxq = &dpmaif_ctrl->rxq[qno];
902*364d0221SKornel Dulęba 	ctrl = rxq->dpmaif_ctrl;
903*364d0221SKornel Dulęba 	/* We need to make sure that the modem has been resumed before
904*364d0221SKornel Dulęba 	 * calling napi. This can't be done inside the polling function
905*364d0221SKornel Dulęba 	 * as we could be blocked waiting for device to be resumed,
906*364d0221SKornel Dulęba 	 * which can't be done from softirq context the poll function
907*364d0221SKornel Dulęba 	 * is running in.
908*364d0221SKornel Dulęba 	 */
909*364d0221SKornel Dulęba 	ret = pm_runtime_resume_and_get(ctrl->dev);
910*364d0221SKornel Dulęba 	if (ret < 0 && ret != -EACCES) {
911*364d0221SKornel Dulęba 		dev_err(ctrl->dev, "Failed to resume device: %d\n", ret);
912*364d0221SKornel Dulęba 		return;
913*364d0221SKornel Dulęba 	}
9145545b7b9SHaijun Liu 	napi_schedule(&rxq->napi);
915d642b012SHaijun Liu }
916d642b012SHaijun Liu 
t7xx_dpmaif_base_free(const struct dpmaif_ctrl * dpmaif_ctrl,const struct dpmaif_bat_request * bat_req)917d642b012SHaijun Liu static void t7xx_dpmaif_base_free(const struct dpmaif_ctrl *dpmaif_ctrl,
918d642b012SHaijun Liu 				  const struct dpmaif_bat_request *bat_req)
919d642b012SHaijun Liu {
920d642b012SHaijun Liu 	if (bat_req->bat_base)
921d642b012SHaijun Liu 		dma_free_coherent(dpmaif_ctrl->dev,
922d642b012SHaijun Liu 				  bat_req->bat_size_cnt * sizeof(struct dpmaif_bat),
923d642b012SHaijun Liu 				  bat_req->bat_base, bat_req->bat_bus_addr);
924d642b012SHaijun Liu }
925d642b012SHaijun Liu 
926d642b012SHaijun Liu /**
927d642b012SHaijun Liu  * t7xx_dpmaif_bat_alloc() - Allocate the BAT ring buffer.
928d642b012SHaijun Liu  * @dpmaif_ctrl: Pointer to DPMAIF context structure.
929d642b012SHaijun Liu  * @bat_req: Pointer to BAT request structure.
930d642b012SHaijun Liu  * @buf_type: BAT ring type.
931d642b012SHaijun Liu  *
932d642b012SHaijun Liu  * This function allocates the BAT ring buffer shared with the HW device, also allocates
933d642b012SHaijun Liu  * a buffer used to store information about the BAT skbs for further release.
934d642b012SHaijun Liu  *
935d642b012SHaijun Liu  * Return:
936d642b012SHaijun Liu  * * 0		- Success.
937d642b012SHaijun Liu  * * -ERROR	- Error code.
938d642b012SHaijun Liu  */
t7xx_dpmaif_bat_alloc(const struct dpmaif_ctrl * dpmaif_ctrl,struct dpmaif_bat_request * bat_req,const enum bat_type buf_type)939d642b012SHaijun Liu int t7xx_dpmaif_bat_alloc(const struct dpmaif_ctrl *dpmaif_ctrl, struct dpmaif_bat_request *bat_req,
940d642b012SHaijun Liu 			  const enum bat_type buf_type)
941d642b012SHaijun Liu {
942d642b012SHaijun Liu 	int sw_buf_size;
943d642b012SHaijun Liu 
944d642b012SHaijun Liu 	if (buf_type == BAT_TYPE_FRAG) {
945d642b012SHaijun Liu 		sw_buf_size = sizeof(struct dpmaif_bat_page);
946d642b012SHaijun Liu 		bat_req->bat_size_cnt = DPMAIF_FRG_COUNT;
947d642b012SHaijun Liu 		bat_req->pkt_buf_sz = DPMAIF_HW_FRG_PKTBUF;
948d642b012SHaijun Liu 	} else {
949d642b012SHaijun Liu 		sw_buf_size = sizeof(struct dpmaif_bat_skb);
950d642b012SHaijun Liu 		bat_req->bat_size_cnt = DPMAIF_BAT_COUNT;
951d642b012SHaijun Liu 		bat_req->pkt_buf_sz = DPMAIF_HW_BAT_PKTBUF;
952d642b012SHaijun Liu 	}
953d642b012SHaijun Liu 
954d642b012SHaijun Liu 	bat_req->type = buf_type;
955d642b012SHaijun Liu 	bat_req->bat_wr_idx = 0;
956d642b012SHaijun Liu 	bat_req->bat_release_rd_idx = 0;
957d642b012SHaijun Liu 
958d642b012SHaijun Liu 	bat_req->bat_base = dma_alloc_coherent(dpmaif_ctrl->dev,
959d642b012SHaijun Liu 					       bat_req->bat_size_cnt * sizeof(struct dpmaif_bat),
960d642b012SHaijun Liu 					       &bat_req->bat_bus_addr, GFP_KERNEL | __GFP_ZERO);
961d642b012SHaijun Liu 	if (!bat_req->bat_base)
962d642b012SHaijun Liu 		return -ENOMEM;
963d642b012SHaijun Liu 
964d642b012SHaijun Liu 	/* For AP SW to record skb information */
965d642b012SHaijun Liu 	bat_req->bat_skb = devm_kzalloc(dpmaif_ctrl->dev, bat_req->bat_size_cnt * sw_buf_size,
966d642b012SHaijun Liu 					GFP_KERNEL);
967d642b012SHaijun Liu 	if (!bat_req->bat_skb)
968d642b012SHaijun Liu 		goto err_free_dma_mem;
969d642b012SHaijun Liu 
970d642b012SHaijun Liu 	bat_req->bat_bitmap = bitmap_zalloc(bat_req->bat_size_cnt, GFP_KERNEL);
971d642b012SHaijun Liu 	if (!bat_req->bat_bitmap)
972d642b012SHaijun Liu 		goto err_free_dma_mem;
973d642b012SHaijun Liu 
974d642b012SHaijun Liu 	spin_lock_init(&bat_req->mask_lock);
975d642b012SHaijun Liu 	atomic_set(&bat_req->refcnt, 0);
976d642b012SHaijun Liu 	return 0;
977d642b012SHaijun Liu 
978d642b012SHaijun Liu err_free_dma_mem:
979d642b012SHaijun Liu 	t7xx_dpmaif_base_free(dpmaif_ctrl, bat_req);
980d642b012SHaijun Liu 
981d642b012SHaijun Liu 	return -ENOMEM;
982d642b012SHaijun Liu }
983d642b012SHaijun Liu 
t7xx_dpmaif_bat_free(const struct dpmaif_ctrl * dpmaif_ctrl,struct dpmaif_bat_request * bat_req)984d642b012SHaijun Liu void t7xx_dpmaif_bat_free(const struct dpmaif_ctrl *dpmaif_ctrl, struct dpmaif_bat_request *bat_req)
985d642b012SHaijun Liu {
986d642b012SHaijun Liu 	if (!bat_req || !atomic_dec_and_test(&bat_req->refcnt))
987d642b012SHaijun Liu 		return;
988d642b012SHaijun Liu 
989d642b012SHaijun Liu 	bitmap_free(bat_req->bat_bitmap);
990d642b012SHaijun Liu 	bat_req->bat_bitmap = NULL;
991d642b012SHaijun Liu 
992d642b012SHaijun Liu 	if (bat_req->bat_skb) {
993d642b012SHaijun Liu 		unsigned int i;
994d642b012SHaijun Liu 
995d642b012SHaijun Liu 		for (i = 0; i < bat_req->bat_size_cnt; i++) {
996d642b012SHaijun Liu 			if (bat_req->type == BAT_TYPE_FRAG)
997d642b012SHaijun Liu 				t7xx_unmap_bat_page(dpmaif_ctrl->dev, bat_req->bat_skb, i);
998d642b012SHaijun Liu 			else
999d642b012SHaijun Liu 				t7xx_unmap_bat_skb(dpmaif_ctrl->dev, bat_req->bat_skb, i);
1000d642b012SHaijun Liu 		}
1001d642b012SHaijun Liu 	}
1002d642b012SHaijun Liu 
1003d642b012SHaijun Liu 	t7xx_dpmaif_base_free(dpmaif_ctrl, bat_req);
1004d642b012SHaijun Liu }
1005d642b012SHaijun Liu 
t7xx_dpmaif_rx_alloc(struct dpmaif_rx_queue * rxq)1006d642b012SHaijun Liu static int t7xx_dpmaif_rx_alloc(struct dpmaif_rx_queue *rxq)
1007d642b012SHaijun Liu {
1008d642b012SHaijun Liu 	rxq->pit_size_cnt = DPMAIF_PIT_COUNT;
1009d642b012SHaijun Liu 	rxq->pit_rd_idx = 0;
1010d642b012SHaijun Liu 	rxq->pit_wr_idx = 0;
1011d642b012SHaijun Liu 	rxq->pit_release_rd_idx = 0;
1012d642b012SHaijun Liu 	rxq->expect_pit_seq = 0;
1013d642b012SHaijun Liu 	rxq->pit_remain_release_cnt = 0;
1014d642b012SHaijun Liu 	memset(&rxq->rx_data_info, 0, sizeof(rxq->rx_data_info));
1015d642b012SHaijun Liu 
1016d642b012SHaijun Liu 	rxq->pit_base = dma_alloc_coherent(rxq->dpmaif_ctrl->dev,
1017d642b012SHaijun Liu 					   rxq->pit_size_cnt * sizeof(struct dpmaif_pit),
1018d642b012SHaijun Liu 					   &rxq->pit_bus_addr, GFP_KERNEL | __GFP_ZERO);
1019d642b012SHaijun Liu 	if (!rxq->pit_base)
1020d642b012SHaijun Liu 		return -ENOMEM;
1021d642b012SHaijun Liu 
1022d642b012SHaijun Liu 	rxq->bat_req = &rxq->dpmaif_ctrl->bat_req;
1023d642b012SHaijun Liu 	atomic_inc(&rxq->bat_req->refcnt);
1024d642b012SHaijun Liu 
1025d642b012SHaijun Liu 	rxq->bat_frag = &rxq->dpmaif_ctrl->bat_frag;
1026d642b012SHaijun Liu 	atomic_inc(&rxq->bat_frag->refcnt);
1027d642b012SHaijun Liu 	return 0;
1028d642b012SHaijun Liu }
1029d642b012SHaijun Liu 
t7xx_dpmaif_rx_buf_free(const struct dpmaif_rx_queue * rxq)1030d642b012SHaijun Liu static void t7xx_dpmaif_rx_buf_free(const struct dpmaif_rx_queue *rxq)
1031d642b012SHaijun Liu {
1032d642b012SHaijun Liu 	if (!rxq->dpmaif_ctrl)
1033d642b012SHaijun Liu 		return;
1034d642b012SHaijun Liu 
1035d642b012SHaijun Liu 	t7xx_dpmaif_bat_free(rxq->dpmaif_ctrl, rxq->bat_req);
1036d642b012SHaijun Liu 	t7xx_dpmaif_bat_free(rxq->dpmaif_ctrl, rxq->bat_frag);
1037d642b012SHaijun Liu 
1038d642b012SHaijun Liu 	if (rxq->pit_base)
1039d642b012SHaijun Liu 		dma_free_coherent(rxq->dpmaif_ctrl->dev,
1040d642b012SHaijun Liu 				  rxq->pit_size_cnt * sizeof(struct dpmaif_pit),
1041d642b012SHaijun Liu 				  rxq->pit_base, rxq->pit_bus_addr);
1042d642b012SHaijun Liu }
1043d642b012SHaijun Liu 
t7xx_dpmaif_rxq_init(struct dpmaif_rx_queue * queue)1044d642b012SHaijun Liu int t7xx_dpmaif_rxq_init(struct dpmaif_rx_queue *queue)
1045d642b012SHaijun Liu {
1046d642b012SHaijun Liu 	int ret;
1047d642b012SHaijun Liu 
1048d642b012SHaijun Liu 	ret = t7xx_dpmaif_rx_alloc(queue);
10495545b7b9SHaijun Liu 	if (ret < 0)
1050d642b012SHaijun Liu 		dev_err(queue->dpmaif_ctrl->dev, "Failed to allocate RX buffers: %d\n", ret);
1051d642b012SHaijun Liu 
1052d642b012SHaijun Liu 	return ret;
1053d642b012SHaijun Liu }
1054d642b012SHaijun Liu 
t7xx_dpmaif_rxq_free(struct dpmaif_rx_queue * queue)1055d642b012SHaijun Liu void t7xx_dpmaif_rxq_free(struct dpmaif_rx_queue *queue)
1056d642b012SHaijun Liu {
1057d642b012SHaijun Liu 	t7xx_dpmaif_rx_buf_free(queue);
1058d642b012SHaijun Liu }
1059d642b012SHaijun Liu 
t7xx_dpmaif_bat_release_work(struct work_struct * work)1060d642b012SHaijun Liu static void t7xx_dpmaif_bat_release_work(struct work_struct *work)
1061d642b012SHaijun Liu {
1062d642b012SHaijun Liu 	struct dpmaif_ctrl *dpmaif_ctrl = container_of(work, struct dpmaif_ctrl, bat_release_work);
1063d642b012SHaijun Liu 	struct dpmaif_rx_queue *rxq;
1064d10b3a69SHaijun Liu 	int ret;
1065d10b3a69SHaijun Liu 
1066d10b3a69SHaijun Liu 	ret = pm_runtime_resume_and_get(dpmaif_ctrl->dev);
1067d10b3a69SHaijun Liu 	if (ret < 0 && ret != -EACCES)
1068d10b3a69SHaijun Liu 		return;
1069d642b012SHaijun Liu 
1070de49ea38SHaijun Liu 	t7xx_pci_disable_sleep(dpmaif_ctrl->t7xx_dev);
1071de49ea38SHaijun Liu 
1072d642b012SHaijun Liu 	/* ALL RXQ use one BAT table, so choose DPF_RX_QNO_DFT */
1073d642b012SHaijun Liu 	rxq = &dpmaif_ctrl->rxq[DPF_RX_QNO_DFT];
1074de49ea38SHaijun Liu 	if (t7xx_pci_sleep_disable_complete(dpmaif_ctrl->t7xx_dev)) {
1075d642b012SHaijun Liu 		t7xx_dpmaif_bat_release_and_add(rxq);
1076d642b012SHaijun Liu 		t7xx_dpmaif_frag_bat_release_and_add(rxq);
1077de49ea38SHaijun Liu 	}
1078d10b3a69SHaijun Liu 
1079de49ea38SHaijun Liu 	t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev);
1080d10b3a69SHaijun Liu 	pm_runtime_mark_last_busy(dpmaif_ctrl->dev);
1081d10b3a69SHaijun Liu 	pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
1082d642b012SHaijun Liu }
1083d642b012SHaijun Liu 
t7xx_dpmaif_bat_rel_wq_alloc(struct dpmaif_ctrl * dpmaif_ctrl)1084d642b012SHaijun Liu int t7xx_dpmaif_bat_rel_wq_alloc(struct dpmaif_ctrl *dpmaif_ctrl)
1085d642b012SHaijun Liu {
1086d642b012SHaijun Liu 	dpmaif_ctrl->bat_release_wq = alloc_workqueue("dpmaif_bat_release_work_queue",
1087d642b012SHaijun Liu 						      WQ_MEM_RECLAIM, 1);
1088d642b012SHaijun Liu 	if (!dpmaif_ctrl->bat_release_wq)
1089d642b012SHaijun Liu 		return -ENOMEM;
1090d642b012SHaijun Liu 
1091d642b012SHaijun Liu 	INIT_WORK(&dpmaif_ctrl->bat_release_work, t7xx_dpmaif_bat_release_work);
1092d642b012SHaijun Liu 	return 0;
1093d642b012SHaijun Liu }
1094d642b012SHaijun Liu 
t7xx_dpmaif_bat_wq_rel(struct dpmaif_ctrl * dpmaif_ctrl)1095d642b012SHaijun Liu void t7xx_dpmaif_bat_wq_rel(struct dpmaif_ctrl *dpmaif_ctrl)
1096d642b012SHaijun Liu {
1097d642b012SHaijun Liu 	flush_work(&dpmaif_ctrl->bat_release_work);
1098d642b012SHaijun Liu 
1099d642b012SHaijun Liu 	if (dpmaif_ctrl->bat_release_wq) {
1100d642b012SHaijun Liu 		destroy_workqueue(dpmaif_ctrl->bat_release_wq);
1101d642b012SHaijun Liu 		dpmaif_ctrl->bat_release_wq = NULL;
1102d642b012SHaijun Liu 	}
1103d642b012SHaijun Liu }
1104d642b012SHaijun Liu 
1105d642b012SHaijun Liu /**
1106d642b012SHaijun Liu  * t7xx_dpmaif_rx_stop() - Suspend RX flow.
1107d642b012SHaijun Liu  * @dpmaif_ctrl: Pointer to data path control struct dpmaif_ctrl.
1108d642b012SHaijun Liu  *
1109d642b012SHaijun Liu  * Wait for all the RX work to finish executing and mark the RX queue as paused.
1110d642b012SHaijun Liu  */
t7xx_dpmaif_rx_stop(struct dpmaif_ctrl * dpmaif_ctrl)1111d642b012SHaijun Liu void t7xx_dpmaif_rx_stop(struct dpmaif_ctrl *dpmaif_ctrl)
1112d642b012SHaijun Liu {
1113d642b012SHaijun Liu 	unsigned int i;
1114d642b012SHaijun Liu 
1115d642b012SHaijun Liu 	for (i = 0; i < DPMAIF_RXQ_NUM; i++) {
1116d642b012SHaijun Liu 		struct dpmaif_rx_queue *rxq = &dpmaif_ctrl->rxq[i];
1117d642b012SHaijun Liu 		int timeout, value;
1118d642b012SHaijun Liu 
1119d642b012SHaijun Liu 		timeout = readx_poll_timeout_atomic(atomic_read, &rxq->rx_processing, value,
1120d642b012SHaijun Liu 						    !value, 0, DPMAIF_CHECK_INIT_TIMEOUT_US);
1121d642b012SHaijun Liu 		if (timeout)
1122d642b012SHaijun Liu 			dev_err(dpmaif_ctrl->dev, "Stop RX SW failed\n");
1123d642b012SHaijun Liu 
1124d642b012SHaijun Liu 		/* Ensure RX processing has stopped before we set rxq->que_started to false */
1125d642b012SHaijun Liu 		smp_mb();
1126d642b012SHaijun Liu 		rxq->que_started = false;
1127d642b012SHaijun Liu 	}
1128d642b012SHaijun Liu }
1129d642b012SHaijun Liu 
t7xx_dpmaif_stop_rxq(struct dpmaif_rx_queue * rxq)1130d642b012SHaijun Liu static void t7xx_dpmaif_stop_rxq(struct dpmaif_rx_queue *rxq)
1131d642b012SHaijun Liu {
1132d642b012SHaijun Liu 	int cnt, j = 0;
1133d642b012SHaijun Liu 
1134d642b012SHaijun Liu 	rxq->que_started = false;
1135d642b012SHaijun Liu 
1136d642b012SHaijun Liu 	do {
1137d642b012SHaijun Liu 		cnt = t7xx_ring_buf_rd_wr_count(rxq->pit_size_cnt, rxq->pit_rd_idx,
1138d642b012SHaijun Liu 						rxq->pit_wr_idx, DPMAIF_READ);
1139d642b012SHaijun Liu 
1140d642b012SHaijun Liu 		if (++j >= DPMAIF_MAX_CHECK_COUNT) {
1141d642b012SHaijun Liu 			dev_err(rxq->dpmaif_ctrl->dev, "Stop RX SW failed, %d\n", cnt);
1142d642b012SHaijun Liu 			break;
1143d642b012SHaijun Liu 		}
1144d642b012SHaijun Liu 	} while (cnt);
1145d642b012SHaijun Liu 
1146d642b012SHaijun Liu 	memset(rxq->pit_base, 0, rxq->pit_size_cnt * sizeof(struct dpmaif_pit));
1147d642b012SHaijun Liu 	memset(rxq->bat_req->bat_base, 0, rxq->bat_req->bat_size_cnt * sizeof(struct dpmaif_bat));
1148d642b012SHaijun Liu 	bitmap_zero(rxq->bat_req->bat_bitmap, rxq->bat_req->bat_size_cnt);
1149d642b012SHaijun Liu 	memset(&rxq->rx_data_info, 0, sizeof(rxq->rx_data_info));
1150d642b012SHaijun Liu 
1151d642b012SHaijun Liu 	rxq->pit_rd_idx = 0;
1152d642b012SHaijun Liu 	rxq->pit_wr_idx = 0;
1153d642b012SHaijun Liu 	rxq->pit_release_rd_idx = 0;
1154d642b012SHaijun Liu 	rxq->expect_pit_seq = 0;
1155d642b012SHaijun Liu 	rxq->pit_remain_release_cnt = 0;
1156d642b012SHaijun Liu 	rxq->bat_req->bat_release_rd_idx = 0;
1157d642b012SHaijun Liu 	rxq->bat_req->bat_wr_idx = 0;
1158d642b012SHaijun Liu 	rxq->bat_frag->bat_release_rd_idx = 0;
1159d642b012SHaijun Liu 	rxq->bat_frag->bat_wr_idx = 0;
1160d642b012SHaijun Liu }
1161d642b012SHaijun Liu 
t7xx_dpmaif_rx_clear(struct dpmaif_ctrl * dpmaif_ctrl)1162d642b012SHaijun Liu void t7xx_dpmaif_rx_clear(struct dpmaif_ctrl *dpmaif_ctrl)
1163d642b012SHaijun Liu {
1164d642b012SHaijun Liu 	int i;
1165d642b012SHaijun Liu 
1166d642b012SHaijun Liu 	for (i = 0; i < DPMAIF_RXQ_NUM; i++)
1167d642b012SHaijun Liu 		t7xx_dpmaif_stop_rxq(&dpmaif_ctrl->rxq[i]);
1168d642b012SHaijun Liu }
1169