1753a026cSClément Léger // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2753a026cSClément Léger /*
3753a026cSClément Léger  * Microsemi SoCs FDMA driver
4753a026cSClément Léger  *
5753a026cSClément Léger  * Copyright (c) 2021 Microchip
6753a026cSClément Léger  *
7753a026cSClément Léger  * Page recycling code is mostly taken from gianfar driver.
8753a026cSClément Léger  */
9753a026cSClément Léger 
10753a026cSClément Léger #include <linux/align.h>
11753a026cSClément Léger #include <linux/bitops.h>
12753a026cSClément Léger #include <linux/dmapool.h>
13753a026cSClément Léger #include <linux/dsa/ocelot.h>
14753a026cSClément Léger #include <linux/netdevice.h>
15753a026cSClément Léger #include <linux/skbuff.h>
16753a026cSClément Léger 
17753a026cSClément Léger #include "ocelot_fdma.h"
18753a026cSClément Léger #include "ocelot_qs.h"
19753a026cSClément Léger 
20753a026cSClément Léger DEFINE_STATIC_KEY_FALSE(ocelot_fdma_enabled);
21753a026cSClément Léger 
ocelot_fdma_writel(struct ocelot * ocelot,u32 reg,u32 data)22753a026cSClément Léger static void ocelot_fdma_writel(struct ocelot *ocelot, u32 reg, u32 data)
23753a026cSClément Léger {
24753a026cSClément Léger 	regmap_write(ocelot->targets[FDMA], reg, data);
25753a026cSClément Léger }
26753a026cSClément Léger 
ocelot_fdma_readl(struct ocelot * ocelot,u32 reg)27753a026cSClément Léger static u32 ocelot_fdma_readl(struct ocelot *ocelot, u32 reg)
28753a026cSClément Léger {
29753a026cSClément Léger 	u32 retval;
30753a026cSClément Léger 
31753a026cSClément Léger 	regmap_read(ocelot->targets[FDMA], reg, &retval);
32753a026cSClément Léger 
33753a026cSClément Léger 	return retval;
34753a026cSClément Léger }
35753a026cSClément Léger 
ocelot_fdma_idx_dma(dma_addr_t base,u16 idx)36753a026cSClément Léger static dma_addr_t ocelot_fdma_idx_dma(dma_addr_t base, u16 idx)
37753a026cSClément Léger {
38753a026cSClément Léger 	return base + idx * sizeof(struct ocelot_fdma_dcb);
39753a026cSClément Léger }
40753a026cSClément Léger 
ocelot_fdma_dma_idx(dma_addr_t base,dma_addr_t dma)41753a026cSClément Léger static u16 ocelot_fdma_dma_idx(dma_addr_t base, dma_addr_t dma)
42753a026cSClément Léger {
43753a026cSClément Léger 	return (dma - base) / sizeof(struct ocelot_fdma_dcb);
44753a026cSClément Léger }
45753a026cSClément Léger 
ocelot_fdma_idx_next(u16 idx,u16 ring_sz)46753a026cSClément Léger static u16 ocelot_fdma_idx_next(u16 idx, u16 ring_sz)
47753a026cSClément Léger {
48753a026cSClément Léger 	return unlikely(idx == ring_sz - 1) ? 0 : idx + 1;
49753a026cSClément Léger }
50753a026cSClément Léger 
ocelot_fdma_idx_prev(u16 idx,u16 ring_sz)51753a026cSClément Léger static u16 ocelot_fdma_idx_prev(u16 idx, u16 ring_sz)
52753a026cSClément Léger {
53753a026cSClément Léger 	return unlikely(idx == 0) ? ring_sz - 1 : idx - 1;
54753a026cSClément Léger }
55753a026cSClément Léger 
ocelot_fdma_rx_ring_free(struct ocelot_fdma * fdma)56753a026cSClément Léger static int ocelot_fdma_rx_ring_free(struct ocelot_fdma *fdma)
57753a026cSClément Léger {
58753a026cSClément Léger 	struct ocelot_fdma_rx_ring *rx_ring = &fdma->rx_ring;
59753a026cSClément Léger 
60753a026cSClément Léger 	if (rx_ring->next_to_use >= rx_ring->next_to_clean)
61753a026cSClément Léger 		return OCELOT_FDMA_RX_RING_SIZE -
62753a026cSClément Léger 		       (rx_ring->next_to_use - rx_ring->next_to_clean) - 1;
63753a026cSClément Léger 	else
64753a026cSClément Léger 		return rx_ring->next_to_clean - rx_ring->next_to_use - 1;
65753a026cSClément Léger }
66753a026cSClément Léger 
ocelot_fdma_tx_ring_free(struct ocelot_fdma * fdma)67753a026cSClément Léger static int ocelot_fdma_tx_ring_free(struct ocelot_fdma *fdma)
68753a026cSClément Léger {
69753a026cSClément Léger 	struct ocelot_fdma_tx_ring *tx_ring = &fdma->tx_ring;
70753a026cSClément Léger 
71753a026cSClément Léger 	if (tx_ring->next_to_use >= tx_ring->next_to_clean)
72753a026cSClément Léger 		return OCELOT_FDMA_TX_RING_SIZE -
73753a026cSClément Léger 		       (tx_ring->next_to_use - tx_ring->next_to_clean) - 1;
74753a026cSClément Léger 	else
75753a026cSClément Léger 		return tx_ring->next_to_clean - tx_ring->next_to_use - 1;
76753a026cSClément Léger }
77753a026cSClément Léger 
ocelot_fdma_tx_ring_empty(struct ocelot_fdma * fdma)78753a026cSClément Léger static bool ocelot_fdma_tx_ring_empty(struct ocelot_fdma *fdma)
79753a026cSClément Léger {
80753a026cSClément Léger 	struct ocelot_fdma_tx_ring *tx_ring = &fdma->tx_ring;
81753a026cSClément Léger 
82753a026cSClément Léger 	return tx_ring->next_to_clean == tx_ring->next_to_use;
83753a026cSClément Léger }
84753a026cSClément Léger 
ocelot_fdma_activate_chan(struct ocelot * ocelot,dma_addr_t dma,int chan)85753a026cSClément Léger static void ocelot_fdma_activate_chan(struct ocelot *ocelot, dma_addr_t dma,
86753a026cSClément Léger 				      int chan)
87753a026cSClément Léger {
88753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_DCB_LLP(chan), dma);
89753a026cSClément Léger 	/* Barrier to force memory writes to DCB to be completed before starting
90753a026cSClément Léger 	 * the channel.
91753a026cSClément Léger 	 */
92753a026cSClément Léger 	wmb();
93753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_CH_ACTIVATE, BIT(chan));
94753a026cSClément Léger }
95753a026cSClément Léger 
ocelot_fdma_read_ch_safe(struct ocelot * ocelot)96f46fd3d7SPavel Skripkin static u32 ocelot_fdma_read_ch_safe(struct ocelot *ocelot)
97f46fd3d7SPavel Skripkin {
98f46fd3d7SPavel Skripkin 	return ocelot_fdma_readl(ocelot, MSCC_FDMA_CH_SAFE);
99f46fd3d7SPavel Skripkin }
100f46fd3d7SPavel Skripkin 
ocelot_fdma_wait_chan_safe(struct ocelot * ocelot,int chan)101753a026cSClément Léger static int ocelot_fdma_wait_chan_safe(struct ocelot *ocelot, int chan)
102753a026cSClément Léger {
103753a026cSClément Léger 	u32 safe;
104753a026cSClément Léger 
105f46fd3d7SPavel Skripkin 	return readx_poll_timeout_atomic(ocelot_fdma_read_ch_safe, ocelot, safe,
106f46fd3d7SPavel Skripkin 					 safe & BIT(chan), 0,
107f46fd3d7SPavel Skripkin 					 OCELOT_FDMA_CH_SAFE_TIMEOUT_US);
108753a026cSClément Léger }
109753a026cSClément Léger 
ocelot_fdma_dcb_set_data(struct ocelot_fdma_dcb * dcb,dma_addr_t dma_addr,size_t size)110753a026cSClément Léger static void ocelot_fdma_dcb_set_data(struct ocelot_fdma_dcb *dcb,
111753a026cSClément Léger 				     dma_addr_t dma_addr,
112753a026cSClément Léger 				     size_t size)
113753a026cSClément Léger {
114753a026cSClément Léger 	u32 offset = dma_addr & 0x3;
115753a026cSClément Léger 
116753a026cSClément Léger 	dcb->llp = 0;
117753a026cSClément Léger 	dcb->datap = ALIGN_DOWN(dma_addr, 4);
118753a026cSClément Léger 	dcb->datal = ALIGN_DOWN(size, 4);
119753a026cSClément Léger 	dcb->stat = MSCC_FDMA_DCB_STAT_BLOCKO(offset);
120753a026cSClément Léger }
121753a026cSClément Léger 
ocelot_fdma_rx_alloc_page(struct ocelot * ocelot,struct ocelot_fdma_rx_buf * rxb)122753a026cSClément Léger static bool ocelot_fdma_rx_alloc_page(struct ocelot *ocelot,
123753a026cSClément Léger 				      struct ocelot_fdma_rx_buf *rxb)
124753a026cSClément Léger {
125753a026cSClément Léger 	dma_addr_t mapping;
126753a026cSClément Léger 	struct page *page;
127753a026cSClément Léger 
128753a026cSClément Léger 	page = dev_alloc_page();
129753a026cSClément Léger 	if (unlikely(!page))
130753a026cSClément Léger 		return false;
131753a026cSClément Léger 
132753a026cSClément Léger 	mapping = dma_map_page(ocelot->dev, page, 0, PAGE_SIZE,
133753a026cSClément Léger 			       DMA_FROM_DEVICE);
134753a026cSClément Léger 	if (unlikely(dma_mapping_error(ocelot->dev, mapping))) {
135753a026cSClément Léger 		__free_page(page);
136753a026cSClément Léger 		return false;
137753a026cSClément Léger 	}
138753a026cSClément Léger 
139753a026cSClément Léger 	rxb->page = page;
140753a026cSClément Léger 	rxb->page_offset = 0;
141753a026cSClément Léger 	rxb->dma_addr = mapping;
142753a026cSClément Léger 
143753a026cSClément Léger 	return true;
144753a026cSClément Léger }
145753a026cSClément Léger 
ocelot_fdma_alloc_rx_buffs(struct ocelot * ocelot,u16 alloc_cnt)146753a026cSClément Léger static int ocelot_fdma_alloc_rx_buffs(struct ocelot *ocelot, u16 alloc_cnt)
147753a026cSClément Léger {
148753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
149753a026cSClément Léger 	struct ocelot_fdma_rx_ring *rx_ring;
150753a026cSClément Léger 	struct ocelot_fdma_rx_buf *rxb;
151753a026cSClément Léger 	struct ocelot_fdma_dcb *dcb;
152753a026cSClément Léger 	dma_addr_t dma_addr;
153753a026cSClément Léger 	int ret = 0;
154753a026cSClément Léger 	u16 idx;
155753a026cSClément Léger 
156753a026cSClément Léger 	rx_ring = &fdma->rx_ring;
157753a026cSClément Léger 	idx = rx_ring->next_to_use;
158753a026cSClément Léger 
159753a026cSClément Léger 	while (alloc_cnt--) {
160753a026cSClément Léger 		rxb = &rx_ring->bufs[idx];
161753a026cSClément Léger 		/* try reuse page */
162753a026cSClément Léger 		if (unlikely(!rxb->page)) {
163753a026cSClément Léger 			if (unlikely(!ocelot_fdma_rx_alloc_page(ocelot, rxb))) {
164753a026cSClément Léger 				dev_err_ratelimited(ocelot->dev,
165753a026cSClément Léger 						    "Failed to allocate rx\n");
166753a026cSClément Léger 				ret = -ENOMEM;
167753a026cSClément Léger 				break;
168753a026cSClément Léger 			}
169753a026cSClément Léger 		}
170753a026cSClément Léger 
171753a026cSClément Léger 		dcb = &rx_ring->dcbs[idx];
172753a026cSClément Léger 		dma_addr = rxb->dma_addr + rxb->page_offset;
173753a026cSClément Léger 		ocelot_fdma_dcb_set_data(dcb, dma_addr, OCELOT_FDMA_RXB_SIZE);
174753a026cSClément Léger 
175753a026cSClément Léger 		idx = ocelot_fdma_idx_next(idx, OCELOT_FDMA_RX_RING_SIZE);
176753a026cSClément Léger 		/* Chain the DCB to the next one */
177753a026cSClément Léger 		dcb->llp = ocelot_fdma_idx_dma(rx_ring->dcbs_dma, idx);
178753a026cSClément Léger 	}
179753a026cSClément Léger 
180753a026cSClément Léger 	rx_ring->next_to_use = idx;
181753a026cSClément Léger 	rx_ring->next_to_alloc = idx;
182753a026cSClément Léger 
183753a026cSClément Léger 	return ret;
184753a026cSClément Léger }
185753a026cSClément Léger 
ocelot_fdma_tx_dcb_set_skb(struct ocelot * ocelot,struct ocelot_fdma_tx_buf * tx_buf,struct ocelot_fdma_dcb * dcb,struct sk_buff * skb)186753a026cSClément Léger static bool ocelot_fdma_tx_dcb_set_skb(struct ocelot *ocelot,
187753a026cSClément Léger 				       struct ocelot_fdma_tx_buf *tx_buf,
188753a026cSClément Léger 				       struct ocelot_fdma_dcb *dcb,
189753a026cSClément Léger 				       struct sk_buff *skb)
190753a026cSClément Léger {
191753a026cSClément Léger 	dma_addr_t mapping;
192753a026cSClément Léger 
193753a026cSClément Léger 	mapping = dma_map_single(ocelot->dev, skb->data, skb->len,
194753a026cSClément Léger 				 DMA_TO_DEVICE);
195753a026cSClément Léger 	if (unlikely(dma_mapping_error(ocelot->dev, mapping)))
196753a026cSClément Léger 		return false;
197753a026cSClément Léger 
198753a026cSClément Léger 	dma_unmap_addr_set(tx_buf, dma_addr, mapping);
199753a026cSClément Léger 
200753a026cSClément Léger 	ocelot_fdma_dcb_set_data(dcb, mapping, OCELOT_FDMA_RX_SIZE);
201753a026cSClément Léger 	tx_buf->skb = skb;
202753a026cSClément Léger 	dcb->stat |= MSCC_FDMA_DCB_STAT_BLOCKL(skb->len);
203753a026cSClément Léger 	dcb->stat |= MSCC_FDMA_DCB_STAT_SOF | MSCC_FDMA_DCB_STAT_EOF;
204753a026cSClément Léger 
205753a026cSClément Léger 	return true;
206753a026cSClément Léger }
207753a026cSClément Léger 
ocelot_fdma_check_stop_rx(struct ocelot * ocelot)208753a026cSClément Léger static bool ocelot_fdma_check_stop_rx(struct ocelot *ocelot)
209753a026cSClément Léger {
210753a026cSClément Léger 	u32 llp;
211753a026cSClément Léger 
212753a026cSClément Léger 	/* Check if the FDMA hits the DCB with LLP == NULL */
213753a026cSClément Léger 	llp = ocelot_fdma_readl(ocelot, MSCC_FDMA_DCB_LLP(MSCC_FDMA_XTR_CHAN));
214753a026cSClément Léger 	if (unlikely(llp))
215753a026cSClément Léger 		return false;
216753a026cSClément Léger 
217753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_CH_DISABLE,
218753a026cSClément Léger 			   BIT(MSCC_FDMA_XTR_CHAN));
219753a026cSClément Léger 
220753a026cSClément Léger 	return true;
221753a026cSClément Léger }
222753a026cSClément Léger 
ocelot_fdma_rx_set_llp(struct ocelot_fdma_rx_ring * rx_ring)223753a026cSClément Léger static void ocelot_fdma_rx_set_llp(struct ocelot_fdma_rx_ring *rx_ring)
224753a026cSClément Léger {
225753a026cSClément Léger 	struct ocelot_fdma_dcb *dcb;
226753a026cSClément Léger 	unsigned int idx;
227753a026cSClément Léger 
228753a026cSClément Léger 	idx = ocelot_fdma_idx_prev(rx_ring->next_to_use,
229753a026cSClément Léger 				   OCELOT_FDMA_RX_RING_SIZE);
230753a026cSClément Léger 	dcb = &rx_ring->dcbs[idx];
231753a026cSClément Léger 	dcb->llp = 0;
232753a026cSClément Léger }
233753a026cSClément Léger 
ocelot_fdma_rx_restart(struct ocelot * ocelot)234753a026cSClément Léger static void ocelot_fdma_rx_restart(struct ocelot *ocelot)
235753a026cSClément Léger {
236753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
237753a026cSClément Léger 	struct ocelot_fdma_rx_ring *rx_ring;
238753a026cSClément Léger 	const u8 chan = MSCC_FDMA_XTR_CHAN;
239753a026cSClément Léger 	dma_addr_t new_llp, dma_base;
240753a026cSClément Léger 	unsigned int idx;
241753a026cSClément Léger 	u32 llp_prev;
242753a026cSClément Léger 	int ret;
243753a026cSClément Léger 
244753a026cSClément Léger 	rx_ring = &fdma->rx_ring;
245753a026cSClément Léger 	ret = ocelot_fdma_wait_chan_safe(ocelot, chan);
246753a026cSClément Léger 	if (ret) {
247753a026cSClément Léger 		dev_err_ratelimited(ocelot->dev,
248753a026cSClément Léger 				    "Unable to stop RX channel\n");
249753a026cSClément Léger 		return;
250753a026cSClément Léger 	}
251753a026cSClément Léger 
252753a026cSClément Léger 	ocelot_fdma_rx_set_llp(rx_ring);
253753a026cSClément Léger 
254753a026cSClément Léger 	/* FDMA stopped on the last DCB that contained a NULL LLP, since
255753a026cSClément Léger 	 * we processed some DCBs in RX, there is free space, and  we must set
256753a026cSClément Léger 	 * DCB_LLP to point to the next DCB
257753a026cSClément Léger 	 */
258753a026cSClément Léger 	llp_prev = ocelot_fdma_readl(ocelot, MSCC_FDMA_DCB_LLP_PREV(chan));
259753a026cSClément Léger 	dma_base = rx_ring->dcbs_dma;
260753a026cSClément Léger 
261753a026cSClément Léger 	/* Get the next DMA addr located after LLP == NULL DCB */
262753a026cSClément Léger 	idx = ocelot_fdma_dma_idx(dma_base, llp_prev);
263753a026cSClément Léger 	idx = ocelot_fdma_idx_next(idx, OCELOT_FDMA_RX_RING_SIZE);
264753a026cSClément Léger 	new_llp = ocelot_fdma_idx_dma(dma_base, idx);
265753a026cSClément Léger 
266753a026cSClément Léger 	/* Finally reactivate the channel */
267753a026cSClément Léger 	ocelot_fdma_activate_chan(ocelot, new_llp, chan);
268753a026cSClément Léger }
269753a026cSClément Léger 
ocelot_fdma_add_rx_frag(struct ocelot_fdma_rx_buf * rxb,u32 stat,struct sk_buff * skb,bool first)270753a026cSClément Léger static bool ocelot_fdma_add_rx_frag(struct ocelot_fdma_rx_buf *rxb, u32 stat,
271753a026cSClément Léger 				    struct sk_buff *skb, bool first)
272753a026cSClément Léger {
273753a026cSClément Léger 	int size = MSCC_FDMA_DCB_STAT_BLOCKL(stat);
274753a026cSClément Léger 	struct page *page = rxb->page;
275753a026cSClément Léger 
276753a026cSClément Léger 	if (likely(first)) {
277753a026cSClément Léger 		skb_put(skb, size);
278753a026cSClément Léger 	} else {
279753a026cSClément Léger 		skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
280753a026cSClément Léger 				rxb->page_offset, size, OCELOT_FDMA_RX_SIZE);
281753a026cSClément Léger 	}
282753a026cSClément Léger 
283753a026cSClément Léger 	/* Try to reuse page */
284753a026cSClément Léger 	if (unlikely(page_ref_count(page) != 1 || page_is_pfmemalloc(page)))
285753a026cSClément Léger 		return false;
286753a026cSClément Léger 
287753a026cSClément Léger 	/* Change offset to the other half */
288753a026cSClément Léger 	rxb->page_offset ^= OCELOT_FDMA_RX_SIZE;
289753a026cSClément Léger 
290753a026cSClément Léger 	page_ref_inc(page);
291753a026cSClément Léger 
292753a026cSClément Léger 	return true;
293753a026cSClément Léger }
294753a026cSClément Léger 
ocelot_fdma_reuse_rx_page(struct ocelot * ocelot,struct ocelot_fdma_rx_buf * old_rxb)295753a026cSClément Léger static void ocelot_fdma_reuse_rx_page(struct ocelot *ocelot,
296753a026cSClément Léger 				      struct ocelot_fdma_rx_buf *old_rxb)
297753a026cSClément Léger {
298753a026cSClément Léger 	struct ocelot_fdma_rx_ring *rx_ring = &ocelot->fdma->rx_ring;
299753a026cSClément Léger 	struct ocelot_fdma_rx_buf *new_rxb;
300753a026cSClément Léger 
301753a026cSClément Léger 	new_rxb = &rx_ring->bufs[rx_ring->next_to_alloc];
302753a026cSClément Léger 	rx_ring->next_to_alloc = ocelot_fdma_idx_next(rx_ring->next_to_alloc,
303753a026cSClément Léger 						      OCELOT_FDMA_RX_RING_SIZE);
304753a026cSClément Léger 
305753a026cSClément Léger 	/* Copy page reference */
306753a026cSClément Léger 	*new_rxb = *old_rxb;
307753a026cSClément Léger 
308753a026cSClément Léger 	/* Sync for use by the device */
309753a026cSClément Léger 	dma_sync_single_range_for_device(ocelot->dev, old_rxb->dma_addr,
310753a026cSClément Léger 					 old_rxb->page_offset,
311753a026cSClément Léger 					 OCELOT_FDMA_RX_SIZE, DMA_FROM_DEVICE);
312753a026cSClément Léger }
313753a026cSClément Léger 
ocelot_fdma_get_skb(struct ocelot * ocelot,u32 stat,struct ocelot_fdma_rx_buf * rxb,struct sk_buff * skb)314753a026cSClément Léger static struct sk_buff *ocelot_fdma_get_skb(struct ocelot *ocelot, u32 stat,
315753a026cSClément Léger 					   struct ocelot_fdma_rx_buf *rxb,
316753a026cSClément Léger 					   struct sk_buff *skb)
317753a026cSClément Léger {
318753a026cSClément Léger 	bool first = false;
319753a026cSClément Léger 
320753a026cSClément Léger 	/* Allocate skb head and data */
321753a026cSClément Léger 	if (likely(!skb)) {
322753a026cSClément Léger 		void *buff_addr = page_address(rxb->page) +
323753a026cSClément Léger 				  rxb->page_offset;
324753a026cSClément Léger 
325753a026cSClément Léger 		skb = build_skb(buff_addr, OCELOT_FDMA_SKBFRAG_SIZE);
326753a026cSClément Léger 		if (unlikely(!skb)) {
327753a026cSClément Léger 			dev_err_ratelimited(ocelot->dev,
328753a026cSClément Léger 					    "build_skb failed !\n");
329753a026cSClément Léger 			return NULL;
330753a026cSClément Léger 		}
331753a026cSClément Léger 		first = true;
332753a026cSClément Léger 	}
333753a026cSClément Léger 
334753a026cSClément Léger 	dma_sync_single_range_for_cpu(ocelot->dev, rxb->dma_addr,
335753a026cSClément Léger 				      rxb->page_offset, OCELOT_FDMA_RX_SIZE,
336753a026cSClément Léger 				      DMA_FROM_DEVICE);
337753a026cSClément Léger 
338753a026cSClément Léger 	if (ocelot_fdma_add_rx_frag(rxb, stat, skb, first)) {
339753a026cSClément Léger 		/* Reuse the free half of the page for the next_to_alloc DCB*/
340753a026cSClément Léger 		ocelot_fdma_reuse_rx_page(ocelot, rxb);
341753a026cSClément Léger 	} else {
342753a026cSClément Léger 		/* page cannot be reused, unmap it */
343753a026cSClément Léger 		dma_unmap_page(ocelot->dev, rxb->dma_addr, PAGE_SIZE,
344753a026cSClément Léger 			       DMA_FROM_DEVICE);
345753a026cSClément Léger 	}
346753a026cSClément Léger 
347753a026cSClément Léger 	/* clear rx buff content */
348753a026cSClément Léger 	rxb->page = NULL;
349753a026cSClément Léger 
350753a026cSClément Léger 	return skb;
351753a026cSClément Léger }
352753a026cSClément Léger 
ocelot_fdma_receive_skb(struct ocelot * ocelot,struct sk_buff * skb)353753a026cSClément Léger static bool ocelot_fdma_receive_skb(struct ocelot *ocelot, struct sk_buff *skb)
354753a026cSClément Léger {
355753a026cSClément Léger 	struct net_device *ndev;
356753a026cSClément Léger 	void *xfh = skb->data;
357753a026cSClément Léger 	u64 timestamp;
358753a026cSClément Léger 	u64 src_port;
359753a026cSClément Léger 
360753a026cSClément Léger 	skb_pull(skb, OCELOT_TAG_LEN);
361753a026cSClément Léger 
362753a026cSClément Léger 	ocelot_xfh_get_src_port(xfh, &src_port);
363753a026cSClément Léger 	if (unlikely(src_port >= ocelot->num_phys_ports))
364753a026cSClément Léger 		return false;
365753a026cSClément Léger 
366753a026cSClément Léger 	ndev = ocelot_port_to_netdev(ocelot, src_port);
367753a026cSClément Léger 	if (unlikely(!ndev))
368753a026cSClément Léger 		return false;
369753a026cSClément Léger 
370*bce56033SYuanjun Gong 	if (pskb_trim(skb, skb->len - ETH_FCS_LEN))
371*bce56033SYuanjun Gong 		return false;
372753a026cSClément Léger 
373753a026cSClément Léger 	skb->dev = ndev;
374753a026cSClément Léger 	skb->protocol = eth_type_trans(skb, skb->dev);
375753a026cSClément Léger 	skb->dev->stats.rx_bytes += skb->len;
376753a026cSClément Léger 	skb->dev->stats.rx_packets++;
377753a026cSClément Léger 
378753a026cSClément Léger 	if (ocelot->ptp) {
379753a026cSClément Léger 		ocelot_xfh_get_rew_val(xfh, &timestamp);
380753a026cSClément Léger 		ocelot_ptp_rx_timestamp(ocelot, skb, timestamp);
381753a026cSClément Léger 	}
382753a026cSClément Léger 
383753a026cSClément Léger 	if (likely(!skb_defer_rx_timestamp(skb)))
384753a026cSClément Léger 		netif_receive_skb(skb);
385753a026cSClément Léger 
386753a026cSClément Léger 	return true;
387753a026cSClément Léger }
388753a026cSClément Léger 
ocelot_fdma_rx_get(struct ocelot * ocelot,int budget)389753a026cSClément Léger static int ocelot_fdma_rx_get(struct ocelot *ocelot, int budget)
390753a026cSClément Léger {
391753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
392753a026cSClément Léger 	struct ocelot_fdma_rx_ring *rx_ring;
393753a026cSClément Léger 	struct ocelot_fdma_rx_buf *rxb;
394753a026cSClément Léger 	struct ocelot_fdma_dcb *dcb;
395753a026cSClément Léger 	struct sk_buff *skb;
396753a026cSClément Léger 	int work_done = 0;
397753a026cSClément Léger 	int cleaned_cnt;
398753a026cSClément Léger 	u32 stat;
399753a026cSClément Léger 	u16 idx;
400753a026cSClément Léger 
401753a026cSClément Léger 	cleaned_cnt = ocelot_fdma_rx_ring_free(fdma);
402753a026cSClément Léger 	rx_ring = &fdma->rx_ring;
403753a026cSClément Léger 	skb = rx_ring->skb;
404753a026cSClément Léger 
405753a026cSClément Léger 	while (budget--) {
406753a026cSClément Léger 		idx = rx_ring->next_to_clean;
407753a026cSClément Léger 		dcb = &rx_ring->dcbs[idx];
408753a026cSClément Léger 		stat = dcb->stat;
409753a026cSClément Léger 		if (MSCC_FDMA_DCB_STAT_BLOCKL(stat) == 0)
410753a026cSClément Léger 			break;
411753a026cSClément Léger 
412753a026cSClément Léger 		/* New packet is a start of frame but we already got a skb set,
413753a026cSClément Léger 		 * we probably lost an EOF packet, free skb
414753a026cSClément Léger 		 */
415753a026cSClément Léger 		if (unlikely(skb && (stat & MSCC_FDMA_DCB_STAT_SOF))) {
416753a026cSClément Léger 			dev_kfree_skb(skb);
417753a026cSClément Léger 			skb = NULL;
418753a026cSClément Léger 		}
419753a026cSClément Léger 
420753a026cSClément Léger 		rxb = &rx_ring->bufs[idx];
421753a026cSClément Léger 		/* Fetch next to clean buffer from the rx_ring */
422753a026cSClément Léger 		skb = ocelot_fdma_get_skb(ocelot, stat, rxb, skb);
423753a026cSClément Léger 		if (unlikely(!skb))
424753a026cSClément Léger 			break;
425753a026cSClément Léger 
426753a026cSClément Léger 		work_done++;
427753a026cSClément Léger 		cleaned_cnt++;
428753a026cSClément Léger 
429753a026cSClément Léger 		idx = ocelot_fdma_idx_next(idx, OCELOT_FDMA_RX_RING_SIZE);
430753a026cSClément Léger 		rx_ring->next_to_clean = idx;
431753a026cSClément Léger 
432753a026cSClément Léger 		if (unlikely(stat & MSCC_FDMA_DCB_STAT_ABORT ||
433753a026cSClément Léger 			     stat & MSCC_FDMA_DCB_STAT_PD)) {
434753a026cSClément Léger 			dev_err_ratelimited(ocelot->dev,
435753a026cSClément Léger 					    "DCB aborted or pruned\n");
436753a026cSClément Léger 			dev_kfree_skb(skb);
437753a026cSClément Léger 			skb = NULL;
438753a026cSClément Léger 			continue;
439753a026cSClément Léger 		}
440753a026cSClément Léger 
441753a026cSClément Léger 		/* We still need to process the other fragment of the packet
442753a026cSClément Léger 		 * before delivering it to the network stack
443753a026cSClément Léger 		 */
444753a026cSClément Léger 		if (!(stat & MSCC_FDMA_DCB_STAT_EOF))
445753a026cSClément Léger 			continue;
446753a026cSClément Léger 
447753a026cSClément Léger 		if (unlikely(!ocelot_fdma_receive_skb(ocelot, skb)))
448753a026cSClément Léger 			dev_kfree_skb(skb);
449753a026cSClément Léger 
450753a026cSClément Léger 		skb = NULL;
451753a026cSClément Léger 	}
452753a026cSClément Léger 
453753a026cSClément Léger 	rx_ring->skb = skb;
454753a026cSClément Léger 
455753a026cSClément Léger 	if (cleaned_cnt)
456753a026cSClément Léger 		ocelot_fdma_alloc_rx_buffs(ocelot, cleaned_cnt);
457753a026cSClément Léger 
458753a026cSClément Léger 	return work_done;
459753a026cSClément Léger }
460753a026cSClément Léger 
ocelot_fdma_wakeup_netdev(struct ocelot * ocelot)461753a026cSClément Léger static void ocelot_fdma_wakeup_netdev(struct ocelot *ocelot)
462753a026cSClément Léger {
463753a026cSClément Léger 	struct ocelot_port_private *priv;
464753a026cSClément Léger 	struct ocelot_port *ocelot_port;
465753a026cSClément Léger 	struct net_device *dev;
466753a026cSClément Léger 	int port;
467753a026cSClément Léger 
468753a026cSClément Léger 	for (port = 0; port < ocelot->num_phys_ports; port++) {
469753a026cSClément Léger 		ocelot_port = ocelot->ports[port];
470753a026cSClément Léger 		if (!ocelot_port)
471753a026cSClément Léger 			continue;
472753a026cSClément Léger 		priv = container_of(ocelot_port, struct ocelot_port_private,
473753a026cSClément Léger 				    port);
474753a026cSClément Léger 		dev = priv->dev;
475753a026cSClément Léger 
476753a026cSClément Léger 		if (unlikely(netif_queue_stopped(dev)))
477753a026cSClément Léger 			netif_wake_queue(dev);
478753a026cSClément Léger 	}
479753a026cSClément Léger }
480753a026cSClément Léger 
ocelot_fdma_tx_cleanup(struct ocelot * ocelot,int budget)481753a026cSClément Léger static void ocelot_fdma_tx_cleanup(struct ocelot *ocelot, int budget)
482753a026cSClément Léger {
483753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
484753a026cSClément Léger 	struct ocelot_fdma_tx_ring *tx_ring;
485753a026cSClément Léger 	struct ocelot_fdma_tx_buf *buf;
486753a026cSClément Léger 	unsigned int new_null_llp_idx;
487753a026cSClément Léger 	struct ocelot_fdma_dcb *dcb;
488753a026cSClément Léger 	bool end_of_list = false;
489753a026cSClément Léger 	struct sk_buff *skb;
490753a026cSClément Léger 	dma_addr_t dma;
491753a026cSClément Léger 	u32 dcb_llp;
492753a026cSClément Léger 	u16 ntc;
493753a026cSClément Léger 	int ret;
494753a026cSClément Léger 
495753a026cSClément Léger 	tx_ring = &fdma->tx_ring;
496753a026cSClément Léger 
497753a026cSClément Léger 	/* Purge the TX packets that have been sent up to the NULL llp or the
498753a026cSClément Léger 	 * end of done list.
499753a026cSClément Léger 	 */
500753a026cSClément Léger 	while (!ocelot_fdma_tx_ring_empty(fdma)) {
501753a026cSClément Léger 		ntc = tx_ring->next_to_clean;
502753a026cSClément Léger 		dcb = &tx_ring->dcbs[ntc];
503753a026cSClément Léger 		if (!(dcb->stat & MSCC_FDMA_DCB_STAT_PD))
504753a026cSClément Léger 			break;
505753a026cSClément Léger 
506753a026cSClément Léger 		buf = &tx_ring->bufs[ntc];
507753a026cSClément Léger 		skb = buf->skb;
508753a026cSClément Léger 		dma_unmap_single(ocelot->dev, dma_unmap_addr(buf, dma_addr),
509753a026cSClément Léger 				 skb->len, DMA_TO_DEVICE);
510753a026cSClément Léger 		napi_consume_skb(skb, budget);
511753a026cSClément Léger 		dcb_llp = dcb->llp;
512753a026cSClément Léger 
513753a026cSClément Léger 		/* Only update after accessing all dcb fields */
514753a026cSClément Léger 		tx_ring->next_to_clean = ocelot_fdma_idx_next(ntc,
515753a026cSClément Léger 							      OCELOT_FDMA_TX_RING_SIZE);
516753a026cSClément Léger 
517753a026cSClément Léger 		/* If we hit the NULL LLP, stop, we might need to reload FDMA */
518753a026cSClément Léger 		if (dcb_llp == 0) {
519753a026cSClément Léger 			end_of_list = true;
520753a026cSClément Léger 			break;
521753a026cSClément Léger 		}
522753a026cSClément Léger 	}
523753a026cSClément Léger 
524753a026cSClément Léger 	/* No need to try to wake if there were no TX cleaned_cnt up. */
525753a026cSClément Léger 	if (ocelot_fdma_tx_ring_free(fdma))
526753a026cSClément Léger 		ocelot_fdma_wakeup_netdev(ocelot);
527753a026cSClément Léger 
528753a026cSClément Léger 	/* If there is still some DCBs to be processed by the FDMA or if the
529753a026cSClément Léger 	 * pending list is empty, there is no need to restart the FDMA.
530753a026cSClément Léger 	 */
531753a026cSClément Léger 	if (!end_of_list || ocelot_fdma_tx_ring_empty(fdma))
532753a026cSClément Léger 		return;
533753a026cSClément Léger 
534753a026cSClément Léger 	ret = ocelot_fdma_wait_chan_safe(ocelot, MSCC_FDMA_INJ_CHAN);
535753a026cSClément Léger 	if (ret) {
536753a026cSClément Léger 		dev_warn(ocelot->dev,
537753a026cSClément Léger 			 "Failed to wait for TX channel to stop\n");
538753a026cSClément Léger 		return;
539753a026cSClément Léger 	}
540753a026cSClément Léger 
541753a026cSClément Léger 	/* Set NULL LLP to be the last DCB used */
542753a026cSClément Léger 	new_null_llp_idx = ocelot_fdma_idx_prev(tx_ring->next_to_use,
543753a026cSClément Léger 						OCELOT_FDMA_TX_RING_SIZE);
544753a026cSClément Léger 	dcb = &tx_ring->dcbs[new_null_llp_idx];
545753a026cSClément Léger 	dcb->llp = 0;
546753a026cSClément Léger 
547753a026cSClément Léger 	dma = ocelot_fdma_idx_dma(tx_ring->dcbs_dma, tx_ring->next_to_clean);
548753a026cSClément Léger 	ocelot_fdma_activate_chan(ocelot, dma, MSCC_FDMA_INJ_CHAN);
549753a026cSClément Léger }
550753a026cSClément Léger 
ocelot_fdma_napi_poll(struct napi_struct * napi,int budget)551753a026cSClément Léger static int ocelot_fdma_napi_poll(struct napi_struct *napi, int budget)
552753a026cSClément Léger {
553753a026cSClément Léger 	struct ocelot_fdma *fdma = container_of(napi, struct ocelot_fdma, napi);
554753a026cSClément Léger 	struct ocelot *ocelot = fdma->ocelot;
555753a026cSClément Léger 	int work_done = 0;
556753a026cSClément Léger 	bool rx_stopped;
557753a026cSClément Léger 
558753a026cSClément Léger 	ocelot_fdma_tx_cleanup(ocelot, budget);
559753a026cSClément Léger 
560753a026cSClément Léger 	rx_stopped = ocelot_fdma_check_stop_rx(ocelot);
561753a026cSClément Léger 
562753a026cSClément Léger 	work_done = ocelot_fdma_rx_get(ocelot, budget);
563753a026cSClément Léger 
564753a026cSClément Léger 	if (rx_stopped)
565753a026cSClément Léger 		ocelot_fdma_rx_restart(ocelot);
566753a026cSClément Léger 
567753a026cSClément Léger 	if (work_done < budget) {
568753a026cSClément Léger 		napi_complete_done(&fdma->napi, work_done);
569753a026cSClément Léger 		ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_ENA,
570753a026cSClément Léger 				   BIT(MSCC_FDMA_INJ_CHAN) |
571753a026cSClément Léger 				   BIT(MSCC_FDMA_XTR_CHAN));
572753a026cSClément Léger 	}
573753a026cSClément Léger 
574753a026cSClément Léger 	return work_done;
575753a026cSClément Léger }
576753a026cSClément Léger 
ocelot_fdma_interrupt(int irq,void * dev_id)577753a026cSClément Léger static irqreturn_t ocelot_fdma_interrupt(int irq, void *dev_id)
578753a026cSClément Léger {
579753a026cSClément Léger 	u32 ident, llp, frm, err, err_code;
580753a026cSClément Léger 	struct ocelot *ocelot = dev_id;
581753a026cSClément Léger 
582753a026cSClément Léger 	ident = ocelot_fdma_readl(ocelot, MSCC_FDMA_INTR_IDENT);
583753a026cSClément Léger 	frm = ocelot_fdma_readl(ocelot, MSCC_FDMA_INTR_FRM);
584753a026cSClément Léger 	llp = ocelot_fdma_readl(ocelot, MSCC_FDMA_INTR_LLP);
585753a026cSClément Léger 
586753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_LLP, llp & ident);
587753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_FRM, frm & ident);
588753a026cSClément Léger 	if (frm || llp) {
589753a026cSClément Léger 		ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_ENA, 0);
590753a026cSClément Léger 		napi_schedule(&ocelot->fdma->napi);
591753a026cSClément Léger 	}
592753a026cSClément Léger 
593753a026cSClément Léger 	err = ocelot_fdma_readl(ocelot, MSCC_FDMA_EVT_ERR);
594753a026cSClément Léger 	if (unlikely(err)) {
595753a026cSClément Léger 		err_code = ocelot_fdma_readl(ocelot, MSCC_FDMA_EVT_ERR_CODE);
596753a026cSClément Léger 		dev_err_ratelimited(ocelot->dev,
597753a026cSClément Léger 				    "Error ! chans mask: %#x, code: %#x\n",
598753a026cSClément Léger 				    err, err_code);
599753a026cSClément Léger 
600753a026cSClément Léger 		ocelot_fdma_writel(ocelot, MSCC_FDMA_EVT_ERR, err);
601753a026cSClément Léger 		ocelot_fdma_writel(ocelot, MSCC_FDMA_EVT_ERR_CODE, err_code);
602753a026cSClément Léger 	}
603753a026cSClément Léger 
604753a026cSClément Léger 	return IRQ_HANDLED;
605753a026cSClément Léger }
606753a026cSClément Léger 
ocelot_fdma_send_skb(struct ocelot * ocelot,struct ocelot_fdma * fdma,struct sk_buff * skb)607753a026cSClément Léger static void ocelot_fdma_send_skb(struct ocelot *ocelot,
608753a026cSClément Léger 				 struct ocelot_fdma *fdma, struct sk_buff *skb)
609753a026cSClément Léger {
610753a026cSClément Léger 	struct ocelot_fdma_tx_ring *tx_ring = &fdma->tx_ring;
611753a026cSClément Léger 	struct ocelot_fdma_tx_buf *tx_buf;
612753a026cSClément Léger 	struct ocelot_fdma_dcb *dcb;
613753a026cSClément Léger 	dma_addr_t dma;
614753a026cSClément Léger 	u16 next_idx;
615753a026cSClément Léger 
616753a026cSClément Léger 	dcb = &tx_ring->dcbs[tx_ring->next_to_use];
617753a026cSClément Léger 	tx_buf = &tx_ring->bufs[tx_ring->next_to_use];
618753a026cSClément Léger 	if (!ocelot_fdma_tx_dcb_set_skb(ocelot, tx_buf, dcb, skb)) {
619753a026cSClément Léger 		dev_kfree_skb_any(skb);
620753a026cSClément Léger 		return;
621753a026cSClément Léger 	}
622753a026cSClément Léger 
623753a026cSClément Léger 	next_idx = ocelot_fdma_idx_next(tx_ring->next_to_use,
624753a026cSClément Léger 					OCELOT_FDMA_TX_RING_SIZE);
625753a026cSClément Léger 	skb_tx_timestamp(skb);
626753a026cSClément Léger 
627753a026cSClément Léger 	/* If the FDMA TX chan is empty, then enqueue the DCB directly */
628753a026cSClément Léger 	if (ocelot_fdma_tx_ring_empty(fdma)) {
629753a026cSClément Léger 		dma = ocelot_fdma_idx_dma(tx_ring->dcbs_dma,
630753a026cSClément Léger 					  tx_ring->next_to_use);
631753a026cSClément Léger 		ocelot_fdma_activate_chan(ocelot, dma, MSCC_FDMA_INJ_CHAN);
632753a026cSClément Léger 	} else {
633753a026cSClément Léger 		/* Chain the DCBs */
634753a026cSClément Léger 		dcb->llp = ocelot_fdma_idx_dma(tx_ring->dcbs_dma, next_idx);
635753a026cSClément Léger 	}
636753a026cSClément Léger 
637753a026cSClément Léger 	tx_ring->next_to_use = next_idx;
638753a026cSClément Léger }
639753a026cSClément Léger 
ocelot_fdma_prepare_skb(struct ocelot * ocelot,int port,u32 rew_op,struct sk_buff * skb,struct net_device * dev)640753a026cSClément Léger static int ocelot_fdma_prepare_skb(struct ocelot *ocelot, int port, u32 rew_op,
641753a026cSClément Léger 				   struct sk_buff *skb, struct net_device *dev)
642753a026cSClément Léger {
643753a026cSClément Léger 	int needed_headroom = max_t(int, OCELOT_TAG_LEN - skb_headroom(skb), 0);
644753a026cSClément Léger 	int needed_tailroom = max_t(int, ETH_FCS_LEN - skb_tailroom(skb), 0);
645753a026cSClément Léger 	void *ifh;
646753a026cSClément Léger 	int err;
647753a026cSClément Léger 
648753a026cSClément Léger 	if (unlikely(needed_headroom || needed_tailroom ||
649753a026cSClément Léger 		     skb_header_cloned(skb))) {
650753a026cSClément Léger 		err = pskb_expand_head(skb, needed_headroom, needed_tailroom,
651753a026cSClément Léger 				       GFP_ATOMIC);
652753a026cSClément Léger 		if (unlikely(err)) {
653753a026cSClément Léger 			dev_kfree_skb_any(skb);
654753a026cSClément Léger 			return 1;
655753a026cSClément Léger 		}
656753a026cSClément Léger 	}
657753a026cSClément Léger 
658753a026cSClément Léger 	err = skb_linearize(skb);
659753a026cSClément Léger 	if (err) {
660753a026cSClément Léger 		net_err_ratelimited("%s: skb_linearize error (%d)!\n",
661753a026cSClément Léger 				    dev->name, err);
662753a026cSClément Léger 		dev_kfree_skb_any(skb);
663753a026cSClément Léger 		return 1;
664753a026cSClément Léger 	}
665753a026cSClément Léger 
666753a026cSClément Léger 	ifh = skb_push(skb, OCELOT_TAG_LEN);
667753a026cSClément Léger 	skb_put(skb, ETH_FCS_LEN);
668753a026cSClément Léger 	memset(ifh, 0, OCELOT_TAG_LEN);
669753a026cSClément Léger 	ocelot_ifh_port_set(ifh, port, rew_op, skb_vlan_tag_get(skb));
670753a026cSClément Léger 
671753a026cSClément Léger 	return 0;
672753a026cSClément Léger }
673753a026cSClément Léger 
ocelot_fdma_inject_frame(struct ocelot * ocelot,int port,u32 rew_op,struct sk_buff * skb,struct net_device * dev)674753a026cSClément Léger int ocelot_fdma_inject_frame(struct ocelot *ocelot, int port, u32 rew_op,
675753a026cSClément Léger 			     struct sk_buff *skb, struct net_device *dev)
676753a026cSClément Léger {
677753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
678753a026cSClément Léger 	int ret = NETDEV_TX_OK;
679753a026cSClément Léger 
680753a026cSClément Léger 	spin_lock(&fdma->tx_ring.xmit_lock);
681753a026cSClément Léger 
682753a026cSClément Léger 	if (ocelot_fdma_tx_ring_free(fdma) == 0) {
683753a026cSClément Léger 		netif_stop_queue(dev);
684753a026cSClément Léger 		ret = NETDEV_TX_BUSY;
685753a026cSClément Léger 		goto out;
686753a026cSClément Léger 	}
687753a026cSClément Léger 
688753a026cSClément Léger 	if (ocelot_fdma_prepare_skb(ocelot, port, rew_op, skb, dev))
689753a026cSClément Léger 		goto out;
690753a026cSClément Léger 
691753a026cSClément Léger 	ocelot_fdma_send_skb(ocelot, fdma, skb);
692753a026cSClément Léger 
693753a026cSClément Léger out:
694753a026cSClément Léger 	spin_unlock(&fdma->tx_ring.xmit_lock);
695753a026cSClément Léger 
696753a026cSClément Léger 	return ret;
697753a026cSClément Léger }
698753a026cSClément Léger 
ocelot_fdma_free_rx_ring(struct ocelot * ocelot)699753a026cSClément Léger static void ocelot_fdma_free_rx_ring(struct ocelot *ocelot)
700753a026cSClément Léger {
701753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
702753a026cSClément Léger 	struct ocelot_fdma_rx_ring *rx_ring;
703753a026cSClément Léger 	struct ocelot_fdma_rx_buf *rxb;
704753a026cSClément Léger 	u16 idx;
705753a026cSClément Léger 
706753a026cSClément Léger 	rx_ring = &fdma->rx_ring;
707753a026cSClément Léger 	idx = rx_ring->next_to_clean;
708753a026cSClément Léger 
709753a026cSClément Léger 	/* Free the pages held in the RX ring */
710753a026cSClément Léger 	while (idx != rx_ring->next_to_use) {
711753a026cSClément Léger 		rxb = &rx_ring->bufs[idx];
712753a026cSClément Léger 		dma_unmap_page(ocelot->dev, rxb->dma_addr, PAGE_SIZE,
713753a026cSClément Léger 			       DMA_FROM_DEVICE);
714753a026cSClément Léger 		__free_page(rxb->page);
715753a026cSClément Léger 		idx = ocelot_fdma_idx_next(idx, OCELOT_FDMA_RX_RING_SIZE);
716753a026cSClément Léger 	}
717753a026cSClément Léger 
718753a026cSClément Léger 	if (fdma->rx_ring.skb)
719753a026cSClément Léger 		dev_kfree_skb_any(fdma->rx_ring.skb);
720753a026cSClément Léger }
721753a026cSClément Léger 
ocelot_fdma_free_tx_ring(struct ocelot * ocelot)722753a026cSClément Léger static void ocelot_fdma_free_tx_ring(struct ocelot *ocelot)
723753a026cSClément Léger {
724753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
725753a026cSClément Léger 	struct ocelot_fdma_tx_ring *tx_ring;
726753a026cSClément Léger 	struct ocelot_fdma_tx_buf *txb;
727753a026cSClément Léger 	struct sk_buff *skb;
728753a026cSClément Léger 	u16 idx;
729753a026cSClément Léger 
730753a026cSClément Léger 	tx_ring = &fdma->tx_ring;
731753a026cSClément Léger 	idx = tx_ring->next_to_clean;
732753a026cSClément Léger 
733753a026cSClément Léger 	while (idx != tx_ring->next_to_use) {
734753a026cSClément Léger 		txb = &tx_ring->bufs[idx];
735753a026cSClément Léger 		skb = txb->skb;
7363cfcda2aSClément Léger 		dma_unmap_single(ocelot->dev, dma_unmap_addr(txb, dma_addr),
7373cfcda2aSClément Léger 				 skb->len, DMA_TO_DEVICE);
738753a026cSClément Léger 		dev_kfree_skb_any(skb);
739753a026cSClément Léger 		idx = ocelot_fdma_idx_next(idx, OCELOT_FDMA_TX_RING_SIZE);
740753a026cSClément Léger 	}
741753a026cSClément Léger }
742753a026cSClément Léger 
ocelot_fdma_rings_alloc(struct ocelot * ocelot)743753a026cSClément Léger static int ocelot_fdma_rings_alloc(struct ocelot *ocelot)
744753a026cSClément Léger {
745753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
746753a026cSClément Léger 	struct ocelot_fdma_dcb *dcbs;
747753a026cSClément Léger 	unsigned int adjust;
748753a026cSClément Léger 	dma_addr_t dcbs_dma;
749753a026cSClément Léger 	int ret;
750753a026cSClément Léger 
751753a026cSClément Léger 	/* Create a pool of consistent memory blocks for hardware descriptors */
752753a026cSClément Léger 	fdma->dcbs_base = dmam_alloc_coherent(ocelot->dev,
753753a026cSClément Léger 					      OCELOT_DCBS_HW_ALLOC_SIZE,
754753a026cSClément Léger 					      &fdma->dcbs_dma_base, GFP_KERNEL);
755753a026cSClément Léger 	if (!fdma->dcbs_base)
756753a026cSClément Léger 		return -ENOMEM;
757753a026cSClément Léger 
758753a026cSClément Léger 	/* DCBs must be aligned on a 32bit boundary */
759753a026cSClément Léger 	dcbs = fdma->dcbs_base;
760753a026cSClément Léger 	dcbs_dma = fdma->dcbs_dma_base;
761753a026cSClément Léger 	if (!IS_ALIGNED(dcbs_dma, 4)) {
762753a026cSClément Léger 		adjust = dcbs_dma & 0x3;
763753a026cSClément Léger 		dcbs_dma = ALIGN(dcbs_dma, 4);
764753a026cSClément Léger 		dcbs = (void *)dcbs + adjust;
765753a026cSClément Léger 	}
766753a026cSClément Léger 
767753a026cSClément Léger 	/* TX queue */
768753a026cSClément Léger 	fdma->tx_ring.dcbs = dcbs;
769753a026cSClément Léger 	fdma->tx_ring.dcbs_dma = dcbs_dma;
770753a026cSClément Léger 	spin_lock_init(&fdma->tx_ring.xmit_lock);
771753a026cSClément Léger 
772753a026cSClément Léger 	/* RX queue */
773753a026cSClément Léger 	fdma->rx_ring.dcbs = dcbs + OCELOT_FDMA_TX_RING_SIZE;
774753a026cSClément Léger 	fdma->rx_ring.dcbs_dma = dcbs_dma + OCELOT_FDMA_TX_DCB_SIZE;
775753a026cSClément Léger 	ret = ocelot_fdma_alloc_rx_buffs(ocelot,
776753a026cSClément Léger 					 ocelot_fdma_tx_ring_free(fdma));
777753a026cSClément Léger 	if (ret) {
778753a026cSClément Léger 		ocelot_fdma_free_rx_ring(ocelot);
779753a026cSClément Léger 		return ret;
780753a026cSClément Léger 	}
781753a026cSClément Léger 
782753a026cSClément Léger 	/* Set the last DCB LLP as NULL, this is normally done when restarting
783753a026cSClément Léger 	 * the RX chan, but this is for the first run
784753a026cSClément Léger 	 */
785753a026cSClément Léger 	ocelot_fdma_rx_set_llp(&fdma->rx_ring);
786753a026cSClément Léger 
787753a026cSClément Léger 	return 0;
788753a026cSClément Léger }
789753a026cSClément Léger 
ocelot_fdma_netdev_init(struct ocelot * ocelot,struct net_device * dev)790753a026cSClément Léger void ocelot_fdma_netdev_init(struct ocelot *ocelot, struct net_device *dev)
791753a026cSClément Léger {
792753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
793753a026cSClément Léger 
794753a026cSClément Léger 	dev->needed_headroom = OCELOT_TAG_LEN;
795753a026cSClément Léger 	dev->needed_tailroom = ETH_FCS_LEN;
796753a026cSClément Léger 
797753a026cSClément Léger 	if (fdma->ndev)
798753a026cSClément Léger 		return;
799753a026cSClément Léger 
800753a026cSClément Léger 	fdma->ndev = dev;
801b707b89fSJakub Kicinski 	netif_napi_add_weight(dev, &fdma->napi, ocelot_fdma_napi_poll,
802753a026cSClément Léger 			      OCELOT_FDMA_WEIGHT);
803753a026cSClément Léger }
804753a026cSClément Léger 
ocelot_fdma_netdev_deinit(struct ocelot * ocelot,struct net_device * dev)805753a026cSClément Léger void ocelot_fdma_netdev_deinit(struct ocelot *ocelot, struct net_device *dev)
806753a026cSClément Léger {
807753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
808753a026cSClément Léger 
809753a026cSClément Léger 	if (fdma->ndev == dev) {
810753a026cSClément Léger 		netif_napi_del(&fdma->napi);
811753a026cSClément Léger 		fdma->ndev = NULL;
812753a026cSClément Léger 	}
813753a026cSClément Léger }
814753a026cSClément Léger 
ocelot_fdma_init(struct platform_device * pdev,struct ocelot * ocelot)815753a026cSClément Léger void ocelot_fdma_init(struct platform_device *pdev, struct ocelot *ocelot)
816753a026cSClément Léger {
817753a026cSClément Léger 	struct device *dev = ocelot->dev;
818753a026cSClément Léger 	struct ocelot_fdma *fdma;
819753a026cSClément Léger 	int ret;
820753a026cSClément Léger 
821753a026cSClément Léger 	fdma = devm_kzalloc(dev, sizeof(*fdma), GFP_KERNEL);
822753a026cSClément Léger 	if (!fdma)
823753a026cSClément Léger 		return;
824753a026cSClément Léger 
825753a026cSClément Léger 	ocelot->fdma = fdma;
826753a026cSClément Léger 	ocelot->dev->coherent_dma_mask = DMA_BIT_MASK(32);
827753a026cSClément Léger 
828753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_ENA, 0);
829753a026cSClément Léger 
830753a026cSClément Léger 	fdma->ocelot = ocelot;
831753a026cSClément Léger 	fdma->irq = platform_get_irq_byname(pdev, "fdma");
832753a026cSClément Léger 	ret = devm_request_irq(dev, fdma->irq, ocelot_fdma_interrupt, 0,
833753a026cSClément Léger 			       dev_name(dev), ocelot);
834753a026cSClément Léger 	if (ret)
835753a026cSClément Léger 		goto err_free_fdma;
836753a026cSClément Léger 
837753a026cSClément Léger 	ret = ocelot_fdma_rings_alloc(ocelot);
838753a026cSClément Léger 	if (ret)
839753a026cSClément Léger 		goto err_free_irq;
840753a026cSClément Léger 
841753a026cSClément Léger 	static_branch_enable(&ocelot_fdma_enabled);
842753a026cSClément Léger 
843753a026cSClément Léger 	return;
844753a026cSClément Léger 
845753a026cSClément Léger err_free_irq:
846753a026cSClément Léger 	devm_free_irq(dev, fdma->irq, fdma);
847753a026cSClément Léger err_free_fdma:
848753a026cSClément Léger 	devm_kfree(dev, fdma);
849753a026cSClément Léger 
850753a026cSClément Léger 	ocelot->fdma = NULL;
851753a026cSClément Léger }
852753a026cSClément Léger 
ocelot_fdma_start(struct ocelot * ocelot)853753a026cSClément Léger void ocelot_fdma_start(struct ocelot *ocelot)
854753a026cSClément Léger {
855753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
856753a026cSClément Léger 
857753a026cSClément Léger 	/* Reconfigure for extraction and injection using DMA */
858753a026cSClément Léger 	ocelot_write_rix(ocelot, QS_INJ_GRP_CFG_MODE(2), QS_INJ_GRP_CFG, 0);
859753a026cSClément Léger 	ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(0), QS_INJ_CTRL, 0);
860753a026cSClément Léger 
861753a026cSClément Léger 	ocelot_write_rix(ocelot, QS_XTR_GRP_CFG_MODE(2), QS_XTR_GRP_CFG, 0);
862753a026cSClément Léger 
863753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_LLP, 0xffffffff);
864753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_FRM, 0xffffffff);
865753a026cSClément Léger 
866753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_LLP_ENA,
867753a026cSClément Léger 			   BIT(MSCC_FDMA_INJ_CHAN) | BIT(MSCC_FDMA_XTR_CHAN));
868753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_FRM_ENA,
869753a026cSClément Léger 			   BIT(MSCC_FDMA_XTR_CHAN));
870753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_ENA,
871753a026cSClément Léger 			   BIT(MSCC_FDMA_INJ_CHAN) | BIT(MSCC_FDMA_XTR_CHAN));
872753a026cSClément Léger 
873753a026cSClément Léger 	napi_enable(&fdma->napi);
874753a026cSClément Léger 
875753a026cSClément Léger 	ocelot_fdma_activate_chan(ocelot, ocelot->fdma->rx_ring.dcbs_dma,
876753a026cSClément Léger 				  MSCC_FDMA_XTR_CHAN);
877753a026cSClément Léger }
878753a026cSClément Léger 
ocelot_fdma_deinit(struct ocelot * ocelot)879753a026cSClément Léger void ocelot_fdma_deinit(struct ocelot *ocelot)
880753a026cSClément Léger {
881753a026cSClément Léger 	struct ocelot_fdma *fdma = ocelot->fdma;
882753a026cSClément Léger 
883753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_ENA, 0);
884753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_CH_FORCEDIS,
885753a026cSClément Léger 			   BIT(MSCC_FDMA_XTR_CHAN));
886753a026cSClément Léger 	ocelot_fdma_writel(ocelot, MSCC_FDMA_CH_FORCEDIS,
887753a026cSClément Léger 			   BIT(MSCC_FDMA_INJ_CHAN));
888753a026cSClément Léger 	napi_synchronize(&fdma->napi);
889753a026cSClément Léger 	napi_disable(&fdma->napi);
890753a026cSClément Léger 
891753a026cSClément Léger 	ocelot_fdma_free_rx_ring(ocelot);
892753a026cSClément Léger 	ocelot_fdma_free_tx_ring(ocelot);
893753a026cSClément Léger }
894