1 | /* |
2 | * FCC driver for Motorola MPC82xx (PQ2). |
3 | * |
4 | * Copyright (c) 2003 Intracom S.A. |
5 | * by Pantelis Antoniou <panto@intracom.gr> |
6 | * |
7 | * 2005 (c) MontaVista Software, Inc. |
8 | * Vitaly Bordug <vbordug@ru.mvista.com> |
9 | * |
10 | * This file is licensed under the terms of the GNU General Public License |
11 | * version 2. This program is licensed "as is" without any warranty of any |
12 | * kind, whether express or implied. |
13 | */ |
14 | |
15 | #include <linux/module.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/types.h> |
18 | #include <linux/string.h> |
19 | #include <linux/ptrace.h> |
20 | #include <linux/errno.h> |
21 | #include <linux/ioport.h> |
22 | #include <linux/interrupt.h> |
23 | #include <linux/delay.h> |
24 | #include <linux/netdevice.h> |
25 | #include <linux/etherdevice.h> |
26 | #include <linux/skbuff.h> |
27 | #include <linux/spinlock.h> |
28 | #include <linux/mii.h> |
29 | #include <linux/ethtool.h> |
30 | #include <linux/bitops.h> |
31 | #include <linux/fs.h> |
32 | #include <linux/platform_device.h> |
33 | #include <linux/phy.h> |
34 | #include <linux/of_address.h> |
35 | #include <linux/of_irq.h> |
36 | #include <linux/gfp.h> |
37 | #include <linux/pgtable.h> |
38 | |
39 | #include <asm/immap_cpm2.h> |
40 | #include <asm/cpm2.h> |
41 | |
42 | #include <asm/irq.h> |
43 | #include <linux/uaccess.h> |
44 | |
45 | #include "fs_enet.h" |
46 | |
47 | /*************************************************/ |
48 | |
49 | /* FCC access macros */ |
50 | |
51 | /* write, read, set bits, clear bits */ |
52 | #define W32(_p, _m, _v) out_be32(&(_p)->_m, (_v)) |
53 | #define R32(_p, _m) in_be32(&(_p)->_m) |
54 | #define S32(_p, _m, _v) W32(_p, _m, R32(_p, _m) | (_v)) |
55 | #define C32(_p, _m, _v) W32(_p, _m, R32(_p, _m) & ~(_v)) |
56 | |
57 | #define W16(_p, _m, _v) out_be16(&(_p)->_m, (_v)) |
58 | #define R16(_p, _m) in_be16(&(_p)->_m) |
59 | #define S16(_p, _m, _v) W16(_p, _m, R16(_p, _m) | (_v)) |
60 | #define C16(_p, _m, _v) W16(_p, _m, R16(_p, _m) & ~(_v)) |
61 | |
62 | #define W8(_p, _m, _v) out_8(&(_p)->_m, (_v)) |
63 | #define R8(_p, _m) in_8(&(_p)->_m) |
64 | #define S8(_p, _m, _v) W8(_p, _m, R8(_p, _m) | (_v)) |
65 | #define C8(_p, _m, _v) W8(_p, _m, R8(_p, _m) & ~(_v)) |
66 | |
67 | /*************************************************/ |
68 | |
69 | #define FCC_MAX_MULTICAST_ADDRS 64 |
70 | |
71 | #define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18)) |
72 | #define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | (VAL & 0xffff)) |
73 | #define mk_mii_end 0 |
74 | |
75 | #define MAX_CR_CMD_LOOPS 10000 |
76 | |
77 | static inline int fcc_cr_cmd(struct fs_enet_private *fep, u32 op) |
78 | { |
79 | const struct fs_platform_info *fpi = fep->fpi; |
80 | |
81 | return cpm_command(fpi->cp_command, op); |
82 | } |
83 | |
84 | static int do_pd_setup(struct fs_enet_private *fep) |
85 | { |
86 | struct platform_device *ofdev = to_platform_device(fep->dev); |
87 | struct fs_platform_info *fpi = fep->fpi; |
88 | int ret = -EINVAL; |
89 | |
90 | fep->interrupt = irq_of_parse_and_map(node: ofdev->dev.of_node, index: 0); |
91 | if (!fep->interrupt) |
92 | goto out; |
93 | |
94 | fep->fcc.fccp = of_iomap(node: ofdev->dev.of_node, index: 0); |
95 | if (!fep->fcc.fccp) |
96 | goto out; |
97 | |
98 | fep->fcc.ep = of_iomap(node: ofdev->dev.of_node, index: 1); |
99 | if (!fep->fcc.ep) |
100 | goto out_fccp; |
101 | |
102 | fep->fcc.fcccp = of_iomap(node: ofdev->dev.of_node, index: 2); |
103 | if (!fep->fcc.fcccp) |
104 | goto out_ep; |
105 | |
106 | fep->fcc.mem = (void __iomem *)cpm2_immr; |
107 | fpi->dpram_offset = cpm_muram_alloc(128, 32); |
108 | if (IS_ERR_VALUE(fpi->dpram_offset)) { |
109 | ret = fpi->dpram_offset; |
110 | goto out_fcccp; |
111 | } |
112 | |
113 | return 0; |
114 | |
115 | out_fcccp: |
116 | iounmap(addr: fep->fcc.fcccp); |
117 | out_ep: |
118 | iounmap(addr: fep->fcc.ep); |
119 | out_fccp: |
120 | iounmap(addr: fep->fcc.fccp); |
121 | out: |
122 | return ret; |
123 | } |
124 | |
125 | #define FCC_NAPI_EVENT_MSK (FCC_ENET_RXF | FCC_ENET_RXB | FCC_ENET_TXB) |
126 | #define FCC_EVENT (FCC_ENET_RXF | FCC_ENET_TXB) |
127 | #define FCC_ERR_EVENT_MSK (FCC_ENET_TXE) |
128 | |
129 | static int setup_data(struct net_device *dev) |
130 | { |
131 | struct fs_enet_private *fep = netdev_priv(dev); |
132 | |
133 | if (do_pd_setup(fep) != 0) |
134 | return -EINVAL; |
135 | |
136 | fep->ev_napi = FCC_NAPI_EVENT_MSK; |
137 | fep->ev = FCC_EVENT; |
138 | fep->ev_err = FCC_ERR_EVENT_MSK; |
139 | |
140 | return 0; |
141 | } |
142 | |
143 | static int allocate_bd(struct net_device *dev) |
144 | { |
145 | struct fs_enet_private *fep = netdev_priv(dev); |
146 | const struct fs_platform_info *fpi = fep->fpi; |
147 | |
148 | fep->ring_base = (void __iomem __force *)dma_alloc_coherent(fep->dev, |
149 | (fpi->tx_ring + fpi->rx_ring) * |
150 | sizeof(cbd_t), &fep->ring_mem_addr, |
151 | GFP_KERNEL); |
152 | if (fep->ring_base == NULL) |
153 | return -ENOMEM; |
154 | |
155 | return 0; |
156 | } |
157 | |
158 | static void free_bd(struct net_device *dev) |
159 | { |
160 | struct fs_enet_private *fep = netdev_priv(dev); |
161 | const struct fs_platform_info *fpi = fep->fpi; |
162 | |
163 | if (fep->ring_base) |
164 | dma_free_coherent(fep->dev, |
165 | (fpi->tx_ring + fpi->rx_ring) * sizeof(cbd_t), |
166 | (void __force *)fep->ring_base, fep->ring_mem_addr); |
167 | } |
168 | |
169 | static void cleanup_data(struct net_device *dev) |
170 | { |
171 | /* nothing */ |
172 | } |
173 | |
174 | static void set_promiscuous_mode(struct net_device *dev) |
175 | { |
176 | struct fs_enet_private *fep = netdev_priv(dev); |
177 | fcc_t __iomem *fccp = fep->fcc.fccp; |
178 | |
179 | S32(fccp, fcc_fpsmr, FCC_PSMR_PRO); |
180 | } |
181 | |
182 | static void set_multicast_start(struct net_device *dev) |
183 | { |
184 | struct fs_enet_private *fep = netdev_priv(dev); |
185 | fcc_enet_t __iomem *ep = fep->fcc.ep; |
186 | |
187 | W32(ep, fen_gaddrh, 0); |
188 | W32(ep, fen_gaddrl, 0); |
189 | } |
190 | |
191 | static void set_multicast_one(struct net_device *dev, const u8 *mac) |
192 | { |
193 | struct fs_enet_private *fep = netdev_priv(dev); |
194 | fcc_enet_t __iomem *ep = fep->fcc.ep; |
195 | u16 taddrh, taddrm, taddrl; |
196 | |
197 | taddrh = ((u16)mac[5] << 8) | mac[4]; |
198 | taddrm = ((u16)mac[3] << 8) | mac[2]; |
199 | taddrl = ((u16)mac[1] << 8) | mac[0]; |
200 | |
201 | W16(ep, fen_taddrh, taddrh); |
202 | W16(ep, fen_taddrm, taddrm); |
203 | W16(ep, fen_taddrl, taddrl); |
204 | fcc_cr_cmd(fep, op: CPM_CR_SET_GADDR); |
205 | } |
206 | |
207 | static void set_multicast_finish(struct net_device *dev) |
208 | { |
209 | struct fs_enet_private *fep = netdev_priv(dev); |
210 | fcc_t __iomem *fccp = fep->fcc.fccp; |
211 | fcc_enet_t __iomem *ep = fep->fcc.ep; |
212 | |
213 | /* clear promiscuous always */ |
214 | C32(fccp, fcc_fpsmr, FCC_PSMR_PRO); |
215 | |
216 | /* if all multi or too many multicasts; just enable all */ |
217 | if ((dev->flags & IFF_ALLMULTI) != 0 || |
218 | netdev_mc_count(dev) > FCC_MAX_MULTICAST_ADDRS) { |
219 | |
220 | W32(ep, fen_gaddrh, 0xffffffff); |
221 | W32(ep, fen_gaddrl, 0xffffffff); |
222 | } |
223 | |
224 | /* read back */ |
225 | fep->fcc.gaddrh = R32(ep, fen_gaddrh); |
226 | fep->fcc.gaddrl = R32(ep, fen_gaddrl); |
227 | } |
228 | |
229 | static void set_multicast_list(struct net_device *dev) |
230 | { |
231 | struct netdev_hw_addr *ha; |
232 | |
233 | if ((dev->flags & IFF_PROMISC) == 0) { |
234 | set_multicast_start(dev); |
235 | netdev_for_each_mc_addr(ha, dev) |
236 | set_multicast_one(dev, mac: ha->addr); |
237 | set_multicast_finish(dev); |
238 | } else |
239 | set_promiscuous_mode(dev); |
240 | } |
241 | |
242 | static void restart(struct net_device *dev) |
243 | { |
244 | struct fs_enet_private *fep = netdev_priv(dev); |
245 | const struct fs_platform_info *fpi = fep->fpi; |
246 | fcc_t __iomem *fccp = fep->fcc.fccp; |
247 | fcc_c_t __iomem *fcccp = fep->fcc.fcccp; |
248 | fcc_enet_t __iomem *ep = fep->fcc.ep; |
249 | dma_addr_t rx_bd_base_phys, tx_bd_base_phys; |
250 | u16 paddrh, paddrm, paddrl; |
251 | const unsigned char *mac; |
252 | int i; |
253 | |
254 | C32(fccp, fcc_gfmr, FCC_GFMR_ENR | FCC_GFMR_ENT); |
255 | |
256 | /* clear everything (slow & steady does it) */ |
257 | for (i = 0; i < sizeof(*ep); i++) |
258 | out_8((u8 __iomem *)ep + i, 0); |
259 | |
260 | /* get physical address */ |
261 | rx_bd_base_phys = fep->ring_mem_addr; |
262 | tx_bd_base_phys = rx_bd_base_phys + sizeof(cbd_t) * fpi->rx_ring; |
263 | |
264 | /* point to bds */ |
265 | W32(ep, fen_genfcc.fcc_rbase, rx_bd_base_phys); |
266 | W32(ep, fen_genfcc.fcc_tbase, tx_bd_base_phys); |
267 | |
268 | /* Set maximum bytes per receive buffer. |
269 | * It must be a multiple of 32. |
270 | */ |
271 | W16(ep, fen_genfcc.fcc_mrblr, PKT_MAXBLR_SIZE); |
272 | |
273 | W32(ep, fen_genfcc.fcc_rstate, (CPMFCR_GBL | CPMFCR_EB) << 24); |
274 | W32(ep, fen_genfcc.fcc_tstate, (CPMFCR_GBL | CPMFCR_EB) << 24); |
275 | |
276 | /* Allocate space in the reserved FCC area of DPRAM for the |
277 | * internal buffers. No one uses this space (yet), so we |
278 | * can do this. Later, we will add resource management for |
279 | * this area. |
280 | */ |
281 | |
282 | W16(ep, fen_genfcc.fcc_riptr, fpi->dpram_offset); |
283 | W16(ep, fen_genfcc.fcc_tiptr, fpi->dpram_offset + 32); |
284 | |
285 | W16(ep, fen_padptr, fpi->dpram_offset + 64); |
286 | |
287 | /* fill with special symbol... */ |
288 | memset_io(fep->fcc.mem + fpi->dpram_offset + 64, 0x88, 32); |
289 | |
290 | W32(ep, fen_genfcc.fcc_rbptr, 0); |
291 | W32(ep, fen_genfcc.fcc_tbptr, 0); |
292 | W32(ep, fen_genfcc.fcc_rcrc, 0); |
293 | W32(ep, fen_genfcc.fcc_tcrc, 0); |
294 | W16(ep, fen_genfcc.fcc_res1, 0); |
295 | W32(ep, fen_genfcc.fcc_res2, 0); |
296 | |
297 | /* no CAM */ |
298 | W32(ep, fen_camptr, 0); |
299 | |
300 | /* Set CRC preset and mask */ |
301 | W32(ep, fen_cmask, 0xdebb20e3); |
302 | W32(ep, fen_cpres, 0xffffffff); |
303 | |
304 | W32(ep, fen_crcec, 0); /* CRC Error counter */ |
305 | W32(ep, fen_alec, 0); /* alignment error counter */ |
306 | W32(ep, fen_disfc, 0); /* discard frame counter */ |
307 | W16(ep, fen_retlim, 15); /* Retry limit threshold */ |
308 | W16(ep, fen_pper, 0); /* Normal persistence */ |
309 | |
310 | /* set group address */ |
311 | W32(ep, fen_gaddrh, fep->fcc.gaddrh); |
312 | W32(ep, fen_gaddrl, fep->fcc.gaddrh); |
313 | |
314 | /* Clear hash filter tables */ |
315 | W32(ep, fen_iaddrh, 0); |
316 | W32(ep, fen_iaddrl, 0); |
317 | |
318 | /* Clear the Out-of-sequence TxBD */ |
319 | W16(ep, fen_tfcstat, 0); |
320 | W16(ep, fen_tfclen, 0); |
321 | W32(ep, fen_tfcptr, 0); |
322 | |
323 | W16(ep, fen_mflr, PKT_MAXBUF_SIZE); /* maximum frame length register */ |
324 | W16(ep, fen_minflr, PKT_MINBUF_SIZE); /* minimum frame length register */ |
325 | |
326 | /* set address */ |
327 | mac = dev->dev_addr; |
328 | paddrh = ((u16)mac[5] << 8) | mac[4]; |
329 | paddrm = ((u16)mac[3] << 8) | mac[2]; |
330 | paddrl = ((u16)mac[1] << 8) | mac[0]; |
331 | |
332 | W16(ep, fen_paddrh, paddrh); |
333 | W16(ep, fen_paddrm, paddrm); |
334 | W16(ep, fen_paddrl, paddrl); |
335 | |
336 | W16(ep, fen_taddrh, 0); |
337 | W16(ep, fen_taddrm, 0); |
338 | W16(ep, fen_taddrl, 0); |
339 | |
340 | W16(ep, fen_maxd1, 1520); /* maximum DMA1 length */ |
341 | W16(ep, fen_maxd2, 1520); /* maximum DMA2 length */ |
342 | |
343 | /* Clear stat counters, in case we ever enable RMON */ |
344 | W32(ep, fen_octc, 0); |
345 | W32(ep, fen_colc, 0); |
346 | W32(ep, fen_broc, 0); |
347 | W32(ep, fen_mulc, 0); |
348 | W32(ep, fen_uspc, 0); |
349 | W32(ep, fen_frgc, 0); |
350 | W32(ep, fen_ospc, 0); |
351 | W32(ep, fen_jbrc, 0); |
352 | W32(ep, fen_p64c, 0); |
353 | W32(ep, fen_p65c, 0); |
354 | W32(ep, fen_p128c, 0); |
355 | W32(ep, fen_p256c, 0); |
356 | W32(ep, fen_p512c, 0); |
357 | W32(ep, fen_p1024c, 0); |
358 | |
359 | W16(ep, fen_rfthr, 0); /* Suggested by manual */ |
360 | W16(ep, fen_rfcnt, 0); |
361 | W16(ep, fen_cftype, 0); |
362 | |
363 | fs_init_bds(dev); |
364 | |
365 | /* adjust to speed (for RMII mode) */ |
366 | if (fpi->use_rmii) { |
367 | if (dev->phydev->speed == 100) |
368 | C8(fcccp, fcc_gfemr, 0x20); |
369 | else |
370 | S8(fcccp, fcc_gfemr, 0x20); |
371 | } |
372 | |
373 | fcc_cr_cmd(fep, CPM_CR_INIT_TRX); |
374 | |
375 | /* clear events */ |
376 | W16(fccp, fcc_fcce, 0xffff); |
377 | |
378 | /* Enable interrupts we wish to service */ |
379 | W16(fccp, fcc_fccm, FCC_ENET_TXE | FCC_ENET_RXF | FCC_ENET_TXB); |
380 | |
381 | /* Set GFMR to enable Ethernet operating mode */ |
382 | W32(fccp, fcc_gfmr, FCC_GFMR_TCI | FCC_GFMR_MODE_ENET); |
383 | |
384 | /* set sync/delimiters */ |
385 | W16(fccp, fcc_fdsr, 0xd555); |
386 | |
387 | W32(fccp, fcc_fpsmr, FCC_PSMR_ENCRC); |
388 | |
389 | if (fpi->use_rmii) |
390 | S32(fccp, fcc_fpsmr, FCC_PSMR_RMII); |
391 | |
392 | /* adjust to duplex mode */ |
393 | if (dev->phydev->duplex) |
394 | S32(fccp, fcc_fpsmr, FCC_PSMR_FDE | FCC_PSMR_LPB); |
395 | else |
396 | C32(fccp, fcc_fpsmr, FCC_PSMR_FDE | FCC_PSMR_LPB); |
397 | |
398 | /* Restore multicast and promiscuous settings */ |
399 | set_multicast_list(dev); |
400 | |
401 | S32(fccp, fcc_gfmr, FCC_GFMR_ENR | FCC_GFMR_ENT); |
402 | } |
403 | |
404 | static void stop(struct net_device *dev) |
405 | { |
406 | struct fs_enet_private *fep = netdev_priv(dev); |
407 | fcc_t __iomem *fccp = fep->fcc.fccp; |
408 | |
409 | /* stop ethernet */ |
410 | C32(fccp, fcc_gfmr, FCC_GFMR_ENR | FCC_GFMR_ENT); |
411 | |
412 | /* clear events */ |
413 | W16(fccp, fcc_fcce, 0xffff); |
414 | |
415 | /* clear interrupt mask */ |
416 | W16(fccp, fcc_fccm, 0); |
417 | |
418 | fs_cleanup_bds(dev); |
419 | } |
420 | |
421 | static void napi_clear_event_fs(struct net_device *dev) |
422 | { |
423 | struct fs_enet_private *fep = netdev_priv(dev); |
424 | fcc_t __iomem *fccp = fep->fcc.fccp; |
425 | |
426 | W16(fccp, fcc_fcce, FCC_NAPI_EVENT_MSK); |
427 | } |
428 | |
429 | static void napi_enable_fs(struct net_device *dev) |
430 | { |
431 | struct fs_enet_private *fep = netdev_priv(dev); |
432 | fcc_t __iomem *fccp = fep->fcc.fccp; |
433 | |
434 | S16(fccp, fcc_fccm, FCC_NAPI_EVENT_MSK); |
435 | } |
436 | |
437 | static void napi_disable_fs(struct net_device *dev) |
438 | { |
439 | struct fs_enet_private *fep = netdev_priv(dev); |
440 | fcc_t __iomem *fccp = fep->fcc.fccp; |
441 | |
442 | C16(fccp, fcc_fccm, FCC_NAPI_EVENT_MSK); |
443 | } |
444 | |
445 | static void rx_bd_done(struct net_device *dev) |
446 | { |
447 | /* nothing */ |
448 | } |
449 | |
450 | static void tx_kickstart(struct net_device *dev) |
451 | { |
452 | struct fs_enet_private *fep = netdev_priv(dev); |
453 | fcc_t __iomem *fccp = fep->fcc.fccp; |
454 | |
455 | S16(fccp, fcc_ftodr, 0x8000); |
456 | } |
457 | |
458 | static u32 get_int_events(struct net_device *dev) |
459 | { |
460 | struct fs_enet_private *fep = netdev_priv(dev); |
461 | fcc_t __iomem *fccp = fep->fcc.fccp; |
462 | |
463 | return (u32)R16(fccp, fcc_fcce); |
464 | } |
465 | |
466 | static void clear_int_events(struct net_device *dev, u32 int_events) |
467 | { |
468 | struct fs_enet_private *fep = netdev_priv(dev); |
469 | fcc_t __iomem *fccp = fep->fcc.fccp; |
470 | |
471 | W16(fccp, fcc_fcce, int_events & 0xffff); |
472 | } |
473 | |
474 | static void ev_error(struct net_device *dev, u32 int_events) |
475 | { |
476 | struct fs_enet_private *fep = netdev_priv(dev); |
477 | |
478 | dev_warn(fep->dev, "FS_ENET ERROR(s) 0x%x\n" , int_events); |
479 | } |
480 | |
481 | static int get_regs(struct net_device *dev, void *p, int *sizep) |
482 | { |
483 | struct fs_enet_private *fep = netdev_priv(dev); |
484 | |
485 | if (*sizep < sizeof(fcc_t) + sizeof(fcc_enet_t) + 1) |
486 | return -EINVAL; |
487 | |
488 | memcpy_fromio(p, fep->fcc.fccp, sizeof(fcc_t)); |
489 | p = (char *)p + sizeof(fcc_t); |
490 | |
491 | memcpy_fromio(p, fep->fcc.ep, sizeof(fcc_enet_t)); |
492 | p = (char *)p + sizeof(fcc_enet_t); |
493 | |
494 | memcpy_fromio(p, fep->fcc.fcccp, 1); |
495 | return 0; |
496 | } |
497 | |
498 | static int get_regs_len(struct net_device *dev) |
499 | { |
500 | return sizeof(fcc_t) + sizeof(fcc_enet_t) + 1; |
501 | } |
502 | |
503 | /* Some transmit errors cause the transmitter to shut |
504 | * down. We now issue a restart transmit. |
505 | * Also, to workaround 8260 device erratum CPM37, we must |
506 | * disable and then re-enable the transmitterfollowing a |
507 | * Late Collision, Underrun, or Retry Limit error. |
508 | * In addition, tbptr may point beyond BDs beyond still marked |
509 | * as ready due to internal pipelining, so we need to look back |
510 | * through the BDs and adjust tbptr to point to the last BD |
511 | * marked as ready. This may result in some buffers being |
512 | * retransmitted. |
513 | */ |
514 | static void tx_restart(struct net_device *dev) |
515 | { |
516 | struct fs_enet_private *fep = netdev_priv(dev); |
517 | fcc_t __iomem *fccp = fep->fcc.fccp; |
518 | const struct fs_platform_info *fpi = fep->fpi; |
519 | fcc_enet_t __iomem *ep = fep->fcc.ep; |
520 | cbd_t __iomem *curr_tbptr; |
521 | cbd_t __iomem *recheck_bd; |
522 | cbd_t __iomem *prev_bd; |
523 | cbd_t __iomem *last_tx_bd; |
524 | |
525 | last_tx_bd = fep->tx_bd_base + (fpi->tx_ring - 1); |
526 | |
527 | /* get the current bd held in TBPTR and scan back from this point */ |
528 | recheck_bd = curr_tbptr = (cbd_t __iomem *) |
529 | ((R32(ep, fen_genfcc.fcc_tbptr) - fep->ring_mem_addr) + |
530 | fep->ring_base); |
531 | |
532 | prev_bd = (recheck_bd == fep->tx_bd_base) ? last_tx_bd : recheck_bd - 1; |
533 | |
534 | /* Move through the bds in reverse, look for the earliest buffer |
535 | * that is not ready. Adjust TBPTR to the following buffer */ |
536 | while ((CBDR_SC(prev_bd) & BD_ENET_TX_READY) != 0) { |
537 | /* Go back one buffer */ |
538 | recheck_bd = prev_bd; |
539 | |
540 | /* update the previous buffer */ |
541 | prev_bd = (prev_bd == fep->tx_bd_base) ? last_tx_bd : prev_bd - 1; |
542 | |
543 | /* We should never see all bds marked as ready, check anyway */ |
544 | if (recheck_bd == curr_tbptr) |
545 | break; |
546 | } |
547 | /* Now update the TBPTR and dirty flag to the current buffer */ |
548 | W32(ep, fen_genfcc.fcc_tbptr, |
549 | (uint)(((void __iomem *)recheck_bd - fep->ring_base) + |
550 | fep->ring_mem_addr)); |
551 | fep->dirty_tx = recheck_bd; |
552 | |
553 | C32(fccp, fcc_gfmr, FCC_GFMR_ENT); |
554 | udelay(10); |
555 | S32(fccp, fcc_gfmr, FCC_GFMR_ENT); |
556 | |
557 | fcc_cr_cmd(fep, CPM_CR_RESTART_TX); |
558 | } |
559 | |
560 | /*************************************************************************/ |
561 | |
562 | const struct fs_ops fs_fcc_ops = { |
563 | .setup_data = setup_data, |
564 | .cleanup_data = cleanup_data, |
565 | .set_multicast_list = set_multicast_list, |
566 | .restart = restart, |
567 | .stop = stop, |
568 | .napi_clear_event = napi_clear_event_fs, |
569 | .napi_enable = napi_enable_fs, |
570 | .napi_disable = napi_disable_fs, |
571 | .rx_bd_done = rx_bd_done, |
572 | .tx_kickstart = tx_kickstart, |
573 | .get_int_events = get_int_events, |
574 | .clear_int_events = clear_int_events, |
575 | .ev_error = ev_error, |
576 | .get_regs = get_regs, |
577 | .get_regs_len = get_regs_len, |
578 | .tx_restart = tx_restart, |
579 | .allocate_bd = allocate_bd, |
580 | .free_bd = free_bd, |
581 | }; |
582 | |