mediatek: Add support for Xiaomi Redmi Router AX6S
[openwrt/openwrt.git] / target / linux / layerscape / patches-5.4 / 701-net-0054-dpaa_eth-workaround-for-ERR010022.patch
1 From fced03d891377fa04153fb0538bc8ca95ba05020 Mon Sep 17 00:00:00 2001
2 From: Madalin Bucur <madalin.bucur@nxp.com>
3 Date: Tue, 14 Nov 2017 08:12:12 +0200
4 Subject: [PATCH] dpaa_eth: workaround for ERR010022
5
6 On LS1043A SoC there is a known erratum ERR010022 that results in split DMA
7 transfers in the FMan under certain conditions. This, combined with a fixed
8 size FIFO of ongoing DMA transfers that may overflow when a split occurs,
9 results in the FMan stalling DMA transfers under high traffic. To avoid the
10 problem, one needs to prevent the DMA transfer splits to occur by preparing
11 the buffers as follows.
12
13 In order to prevent split transactions, all frames need to be aligned to 16
14 bytes and not cross 4K address boundaries. To allow Jumbo frames (up to
15 9.6K), all data must be aligned to 256 byes. This way, 4K boundary crossings
16 will not trigger any transaction splits.
17
18 The errata is prevented from manifesting by realigning all outgoing frames to
19 256 byte boundaries. In the process, all S/G frames are linearized.
20
21 Signed-off-by: Madalin Bucur <madalin.bucur@nxp.com>
22 Signed-off-by: Camelia Groza <camelia.groza@nxp.com>
23 [rebase]
24 Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
25 ---
26 drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 204 +++++++++++++++++++++++--
27 1 file changed, 194 insertions(+), 10 deletions(-)
28
29 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
30 +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
31 @@ -54,6 +54,10 @@
32 #include <linux/phy_fixed.h>
33 #include <soc/fsl/bman.h>
34 #include <soc/fsl/qman.h>
35 +#if !defined(CONFIG_PPC) && defined(CONFIG_SOC_BUS)
36 +#include <linux/sys_soc.h> /* soc_device_match */
37 +#endif
38 +
39 #include "fman.h"
40 #include "fman_port.h"
41 #include "mac.h"
42 @@ -73,6 +77,10 @@ static u16 tx_timeout = 1000;
43 module_param(tx_timeout, ushort, 0444);
44 MODULE_PARM_DESC(tx_timeout, "The Tx timeout in ms");
45
46 +#ifndef CONFIG_PPC
47 +bool dpaa_errata_a010022;
48 +#endif
49 +
50 #define FM_FD_STAT_RX_ERRORS \
51 (FM_FD_ERR_DMA | FM_FD_ERR_PHYSICAL | \
52 FM_FD_ERR_SIZE | FM_FD_ERR_CLS_DISCARD | \
53 @@ -1495,7 +1503,19 @@ static int dpaa_bp_add_8_bufs(const stru
54 u8 i;
55
56 for (i = 0; i < 8; i++) {
57 +#ifndef CONFIG_PPC
58 + if (dpaa_errata_a010022) {
59 + struct page *page = alloc_page(GFP_KERNEL);
60 +
61 + if (unlikely(!page))
62 + goto release_previous_buffs;
63 + new_buf = page_address(page);
64 + } else {
65 + new_buf = netdev_alloc_frag(dpaa_bp->raw_size);
66 + }
67 +#else
68 new_buf = netdev_alloc_frag(dpaa_bp->raw_size);
69 +#endif
70 if (unlikely(!new_buf)) {
71 dev_err(dev, "netdev_alloc_frag() failed, size %zu\n",
72 dpaa_bp->raw_size);
73 @@ -1663,9 +1683,15 @@ static struct sk_buff *dpaa_cleanup_tx_f
74 }
75 }
76
77 - if (qm_fd_get_format(fd) == qm_fd_sg)
78 - /* Free the page frag that we allocated on Tx */
79 - skb_free_frag(phys_to_virt(addr));
80 + if (qm_fd_get_format(fd) == qm_fd_sg) {
81 +#ifndef CONFIG_PPC
82 + if (dpaa_errata_a010022)
83 + put_page(virt_to_page(sgt));
84 + else
85 +#endif
86 + /* Free the page frag that we allocated on Tx */
87 + skb_free_frag(phys_to_virt(addr));
88 + }
89
90 return skb;
91 }
92 @@ -1922,14 +1948,26 @@ static int skb_to_sg_fd(struct dpaa_priv
93 size_t frag_len;
94 void *sgt_buf;
95
96 - /* get a page frag to store the SGTable */
97 - sz = SKB_DATA_ALIGN(priv->tx_headroom + DPAA_SGT_SIZE);
98 - sgt_buf = netdev_alloc_frag(sz);
99 - if (unlikely(!sgt_buf)) {
100 - netdev_err(net_dev, "netdev_alloc_frag() failed for size %d\n",
101 - sz);
102 - return -ENOMEM;
103 +#ifndef CONFIG_PPC
104 + if (unlikely(dpaa_errata_a010022)) {
105 + struct page *page = alloc_page(GFP_ATOMIC);
106 + if (unlikely(!page))
107 + return -ENOMEM;
108 + sgt_buf = page_address(page);
109 + } else {
110 +#endif
111 + /* get a page frag to store the SGTable */
112 + sz = SKB_DATA_ALIGN(priv->tx_headroom + DPAA_SGT_SIZE);
113 + sgt_buf = netdev_alloc_frag(sz);
114 + if (unlikely(!sgt_buf)) {
115 + netdev_err(net_dev,
116 + "netdev_alloc_frag() failed for size %d\n",
117 + sz);
118 + return -ENOMEM;
119 + }
120 +#ifndef CONFIG_PPC
121 }
122 +#endif
123
124 /* Enable L3/L4 hardware checksum computation.
125 *
126 @@ -2049,6 +2087,122 @@ static inline int dpaa_xmit(struct dpaa_
127 return 0;
128 }
129
130 +#ifndef CONFIG_PPC
131 +/* On LS1043A SoC there is a known erratum ERR010022 that results in split DMA
132 + * transfers in the FMan under certain conditions. This, combined with a fixed
133 + * size FIFO of ongoing DMA transfers that may overflow when a split occurs,
134 + * results in the FMan stalling DMA transfers under high traffic. To avoid the
135 + * problem, one needs to prevent the DMA transfer splits to occur by preparing
136 + * the buffers
137 + */
138 +
139 +#define DPAA_A010022_HEADROOM 256
140 +#define CROSS_4K_BOUND(start, size) \
141 + (((start) + (size)) > (((start) + 0x1000) & ~0xFFF))
142 +
143 +static bool dpaa_errata_a010022_has_dma_issue(struct sk_buff *skb,
144 + struct dpaa_priv *priv)
145 +{
146 + int nr_frags, i = 0;
147 + skb_frag_t *frag;
148 +
149 + /* Transfers that do not start at 16B aligned addresses will be split;
150 + * Transfers that cross a 4K page boundary will also be split
151 + */
152 +
153 + /* Check if the frame data is aligned to 16 bytes */
154 + if ((uintptr_t)skb->data % DPAA_FD_DATA_ALIGNMENT)
155 + return true;
156 +
157 + /* Check if the headroom crosses a boundary */
158 + if (CROSS_4K_BOUND((uintptr_t)skb->head, skb_headroom(skb)))
159 + return true;
160 +
161 + /* Check if the non-paged data crosses a boundary */
162 + if (CROSS_4K_BOUND((uintptr_t)skb->data, skb_headlen(skb)))
163 + return true;
164 +
165 + nr_frags = skb_shinfo(skb)->nr_frags;
166 +
167 + while (i < nr_frags) {
168 + frag = &skb_shinfo(skb)->frags[i];
169 +
170 + /* Check if a paged fragment crosses a boundary from its
171 + * offset to its end.
172 + */
173 + if (CROSS_4K_BOUND((uintptr_t)frag->page_offset, frag->size))
174 + return true;
175 +
176 + i++;
177 + }
178 +
179 + return false;
180 +}
181 +
182 +static struct sk_buff *dpaa_errata_a010022_prevent(struct sk_buff *skb,
183 + struct dpaa_priv *priv)
184 +{
185 + int trans_offset = skb_transport_offset(skb);
186 + int net_offset = skb_network_offset(skb);
187 + int nsize, npage_order, headroom;
188 + struct sk_buff *nskb = NULL;
189 + struct page *npage;
190 + void *npage_addr;
191 +
192 + if (!dpaa_errata_a010022_has_dma_issue(skb, priv))
193 + return skb;
194 +
195 + /* For the new skb we only need the old one's data (both non-paged and
196 + * paged). We can skip the old tailroom.
197 + *
198 + * The headroom also needs to fit our private info (64 bytes) but we
199 + * reserve 256 bytes instead in order to guarantee that the data is
200 + * aligned to 256.
201 + */
202 + headroom = DPAA_A010022_HEADROOM;
203 + nsize = headroom + skb->len +
204 + SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
205 +
206 + /* Reserve enough memory to accommodate Jumbo frames */
207 + npage_order = (nsize - 1) / PAGE_SIZE;
208 + npage = alloc_pages(GFP_ATOMIC | __GFP_COMP, npage_order);
209 + if (unlikely(!npage)) {
210 + WARN_ONCE(1, "Memory allocation failure\n");
211 + return NULL;
212 + }
213 + npage_addr = page_address(npage);
214 +
215 + nskb = build_skb(npage_addr, nsize);
216 + if (unlikely(!nskb))
217 + goto err;
218 +
219 + /* Code borrowed and adapted from skb_copy() */
220 + skb_reserve(nskb, headroom);
221 + skb_put(nskb, skb->len);
222 + if (skb_copy_bits(skb, 0, nskb->data, skb->len)) {
223 + WARN_ONCE(1, "skb parsing failure\n");
224 + goto err;
225 + }
226 + copy_skb_header(nskb, skb);
227 +
228 + /* We move the headroom when we align it so we have to reset the
229 + * network and transport header offsets relative to the new data
230 + * pointer. The checksum offload relies on these offsets.
231 + */
232 + skb_set_network_header(nskb, net_offset);
233 + skb_set_transport_header(nskb, trans_offset);
234 +
235 + dev_kfree_skb(skb);
236 + return nskb;
237 +
238 +err:
239 + if (nskb)
240 + dev_kfree_skb(nskb);
241 + put_page(npage);
242 + return NULL;
243 +}
244 +#endif
245 +
246 static netdev_tx_t
247 dpaa_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
248 {
249 @@ -2095,6 +2249,15 @@ dpaa_start_xmit(struct sk_buff *skb, str
250 nonlinear = skb_is_nonlinear(skb);
251 }
252
253 +#ifndef CONFIG_PPC
254 + if (unlikely(dpaa_errata_a010022)) {
255 + skb = dpaa_errata_a010022_prevent(skb, priv);
256 + if (!skb)
257 + goto enomem;
258 + nonlinear = skb_is_nonlinear(skb);
259 + }
260 +#endif
261 +
262 if (nonlinear) {
263 /* Just create a S/G fd based on the skb */
264 err = skb_to_sg_fd(priv, skb, &fd);
265 @@ -2992,6 +3155,23 @@ static int dpaa_remove(struct platform_d
266 return err;
267 }
268
269 +#ifndef CONFIG_PPC
270 +static bool __init soc_has_errata_a010022(void)
271 +{
272 +#ifdef CONFIG_SOC_BUS
273 + const struct soc_device_attribute soc_msi_matches[] = {
274 + { .family = "QorIQ LS1043A",
275 + .data = NULL },
276 + { },
277 + };
278 +
279 + if (!soc_device_match(soc_msi_matches))
280 + return false;
281 +#endif
282 + return true; /* cannot identify SoC or errata applies */
283 +}
284 +#endif
285 +
286 static const struct platform_device_id dpaa_devtype[] = {
287 {
288 .name = "dpaa-ethernet",
289 @@ -3016,6 +3196,10 @@ static int __init dpaa_load(void)
290
291 pr_debug("FSL DPAA Ethernet driver\n");
292
293 +#ifndef CONFIG_PPC
294 + /* Detect if the current SoC requires the DMA transfer alignment workaround */
295 + dpaa_errata_a010022 = soc_has_errata_a010022();
296 +#endif
297 /* initialize dpaa_eth mirror values */
298 dpaa_rx_extra_headroom = fman_get_rx_extra_headroom();
299 dpaa_max_frm = fman_get_max_frm();