source: rtems-libbsd/rtemsbsd/sys/powerpc/drivers/net/ethernet/freescale/dpaa/if_fmanmac.c @ 5aa6ee5

5
Last change on this file since 5aa6ee5 was 5aa6ee5, checked in by Sebastian Huber <sebastian.huber@…>, on Jan 15, 2019 at 6:56:36 AM

dpaa: Use if_transmit instead of legacy if_start

This avoids a lock contention on the send queue.

  • Property mode set to 100644
File size: 13.1 KB
Line 
1#include <machine/rtems-bsd-kernel-space.h>
2#include <rtems/bsd/local/opt_dpaa.h>
3
4/*
5 * Copyright 2012 - 2015 Freescale Semiconductor Inc.
6 * Copyright (c) 2016 embedded brains GmbH
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *     * Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *     * Redistributions in binary form must reproduce the above copyright
13 *      notice, this list of conditions and the following disclaimer in the
14 *      documentation and/or other materials provided with the distribution.
15 *     * Neither the name of Freescale Semiconductor nor the
16 *      names of its contributors may be used to endorse or promote products
17 *      derived from this software without specific prior written permission.
18 *
19 * ALTERNATIVELY, this software may be distributed under the terms of the
20 * GNU General Public License ("GPL") as published by the Free Software
21 * Foundation, either version 2 of that License or (at your option) any
22 * later version.
23 *
24 * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
25 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
28 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#include <machine/rtems-bsd-kernel-space.h>
37
38#include "if_fmanmac.h"
39
40#include <sys/sockio.h>
41
42#include <net/if_vlan_var.h>
43#include <netinet/ip.h>
44
45#include <linux/phy.h>
46
47#include <soc/fsl/dpaa.h>
48
49#include "../../../../../../../../linux/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h"
50
51#define FMAN_MAC_LOCK(sc)               mtx_lock(&(sc)->mtx)
52#define FMAN_MAC_UNLOCK(sc)             mtx_unlock(&(sc)->mtx)
53#define FMAN_MAC_ASSERT_LOCKED(sc)      mtx_assert(&(sc)->mtx, MA_OWNED)
54
55#define FMAN_MAC_CSUM (CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_TCP_IPV6 | \
56    CSUM_UDP_IPV6)
57
58struct fman_mac_sgt {
59        char priv[DPAA_TX_PRIV_DATA_SIZE];
60        struct fman_prs_result prs;
61        struct qm_sg_entry sg[DPAA_SGT_MAX_ENTRIES];
62        struct mbuf *m;
63};
64
65static uma_zone_t fman_mac_sgt_zone;
66
67static void
68fman_mac_sgt_init(void)
69{
70
71        fman_mac_sgt_zone = uma_zcreate("FMan MAC SGT", sizeof(struct fman_mac_sgt),
72            NULL, NULL, NULL, NULL, 16 - 1, 0);
73        if (fman_mac_sgt_zone == NULL) {
74                panic("Cannot create FMan MAC SGT zone");
75        }
76}
77SYSINIT(fman_mac_sgt, SI_SUB_MBUF, SI_ORDER_ANY, fman_mac_sgt_init, NULL);
78
79static void
80fman_mac_enable_tx_csum(struct mbuf *m, struct qm_fd *fd,
81    struct fman_prs_result *prs)
82{
83        int csum_flags = m->m_pkthdr.csum_flags;
84
85        if ((csum_flags & FMAN_MAC_CSUM) == 0) {
86                return;
87        }
88
89        memset(prs, 0, sizeof(*prs));
90
91        if ((csum_flags & FMAN_MAC_CSUM) == CSUM_IP) {
92                prs->l3r = FM_L3_PARSE_RESULT_IPV4;
93        } else if ((csum_flags & CSUM_TCP) != 0) {
94                prs->l3r = FM_L3_PARSE_RESULT_IPV4;
95                prs->l4r = FM_L4_PARSE_RESULT_TCP;
96        } else if ((csum_flags & CSUM_UDP) != 0) {
97                prs->l3r = FM_L3_PARSE_RESULT_IPV4;
98                prs->l4r = FM_L4_PARSE_RESULT_UDP;
99        } else if ((csum_flags & CSUM_TCP_IPV6) != 0) {
100                prs->l3r = FM_L3_PARSE_RESULT_IPV6;
101                prs->l4r = FM_L4_PARSE_RESULT_TCP;
102        } else if ((csum_flags & CSUM_UDP_IPV6) != 0) {
103                prs->l3r = FM_L3_PARSE_RESULT_IPV6;
104                prs->l4r = FM_L4_PARSE_RESULT_UDP;
105        } else {
106                BSD_ASSERT(0);
107        }
108
109        /* FIXME: VLAN */
110        prs->ip_off[0] = (u8)sizeof(struct ether_header);
111        prs->l4_off = (u8)(sizeof(struct ether_header) + sizeof(struct ip));
112
113        fd->cmd |= FM_FD_CMD_RPD | FM_FD_CMD_DTC;
114}
115
116static int
117fman_mac_tx(struct ifnet *ifp, struct mbuf *m)
118{
119        struct fman_mac_softc *sc;
120        struct fman_mac_sgt *sgt;
121        struct mbuf *n;
122        struct qm_fd fd;
123        struct dpaa_priv *priv;
124        struct qman_fq *egress_fq;
125        int queue = 0;
126        size_t i;
127        int err;
128
129        sc = ifp->if_softc;
130
131        sgt = uma_zalloc(fman_mac_sgt_zone, M_NOWAIT);
132        if (unlikely(sgt == NULL)) {
133                if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
134                m_freem(m);
135                return (ENOBUFS);
136        }
137
138        qm_fd_clear_fd(&fd);
139        qm_fd_set_sg(&fd, offsetof(struct fman_mac_sgt, sg), m->m_pkthdr.len);
140        fd.bpid = FSL_DPAA_BPID_INV;
141        fd.cmd |= cpu_to_be32(FM_FD_CMD_FCO);
142        qm_fd_addr_set64(&fd, (uintptr_t)sgt);
143        fman_mac_enable_tx_csum(m, &fd, &sgt->prs);
144
145repeat_with_collapsed_mbuf_chain:
146
147        i = 0;
148        n = m;
149
150        while (n != NULL && i < DPAA_SGT_MAX_ENTRIES) {
151                int len = n->m_len;
152
153                if (len > 0) {
154                        qm_sg_entry_set_len(&sgt->sg[i], len);
155                        sgt->sg[i].bpid = FSL_DPAA_BPID_INV;
156                        sgt->sg[i].offset = 0;
157                        qm_sg_entry_set64(&sgt->sg[i],
158                            mtod(n, uintptr_t));
159                        ++i;
160                }
161
162                n = n->m_next;
163        }
164
165        if (unlikely(n != NULL && i == DPAA_SGT_MAX_ENTRIES)) {
166                struct mbuf *c;
167
168                c = m_collapse(m, M_NOWAIT, DPAA_SGT_MAX_ENTRIES);
169                if (c == NULL) {
170                        if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
171                        m_freem(m);
172                        uma_zfree(fman_mac_sgt_zone, sgt);
173                        return (ENOBUFS);
174                }
175
176                m = c;
177                goto repeat_with_collapsed_mbuf_chain;
178        }
179
180        sgt->sg[i - 1].cfg |= cpu_to_be32(QM_SG_FIN);
181        sgt->m = m;
182        priv = netdev_priv(&sc->mac_dev.net_dev);
183        egress_fq = priv->egress_fqs[queue];
184        fd.cmd |= cpu_to_be32(qman_fq_fqid(priv->conf_fqs[queue]));
185
186        for (i = 0; i < DPAA_ENQUEUE_RETRIES; ++i) {
187                err = qman_enqueue(egress_fq, &fd);
188                if (likely(err != -EBUSY)) {
189                        break;
190                }
191        }
192
193        if (unlikely(err < 0)) {
194                if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
195                m_freem(m);
196                return (ENOBUFS);
197        }
198
199        return (0);
200}
201
202static void
203fman_mac_tick(void *arg)
204{
205        struct fman_mac_softc *sc;
206        struct ifnet *ifp;
207
208        sc = arg;
209        ifp = sc->ifp;
210
211        FMAN_MAC_ASSERT_LOCKED(sc);
212
213        if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
214                return;
215        }
216
217        mii_tick(sc->mii_softc);
218        callout_reset(&sc->fman_mac_callout, hz, fman_mac_tick, sc);
219}
220
221static void
222fman_mac_set_multi(struct fman_mac_softc *sc)
223{
224        struct mac_device *mac_dev;
225
226        FMAN_MAC_ASSERT_LOCKED(sc);
227        mac_dev = &sc->mac_dev;
228        (*mac_dev->set_multi)(&mac_dev->net_dev, mac_dev);
229}
230
231static void
232fman_mac_set_promisc(struct fman_mac_softc *sc, int if_flags)
233{
234        struct mac_device *mac_dev;
235
236        FMAN_MAC_ASSERT_LOCKED(sc);
237        mac_dev = &sc->mac_dev;
238        (*mac_dev->set_promisc)(mac_dev->fman_mac,
239            (if_flags & IFF_PROMISC) != 0);
240}
241
242static int
243fman_mac_set_mtu(struct fman_mac_softc *sc, int mtu)
244{
245        struct ifnet *ifp;
246        int real_mtu;
247
248        ifp = sc->ifp;
249        real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
250        if (real_mtu > fman_get_max_frm() ||
251            real_mtu < ETHER_MIN_LEN) {
252                return (EINVAL);
253        }
254
255        ifp->if_mtu = mtu;
256        return (0);
257}
258
259static void
260fman_mac_init_locked(struct fman_mac_softc *sc)
261{
262        struct ifnet *ifp;
263        int error;
264
265        FMAN_MAC_ASSERT_LOCKED(sc);
266
267        ifp = sc->ifp;
268        if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
269                return;
270        }
271
272        ifp->if_drv_flags |= IFF_DRV_RUNNING;
273
274        error = dpa_eth_priv_start(&sc->mac_dev.net_dev);
275        BSD_ASSERT(error == 0);
276
277        if (sc->mii_softc != NULL) {
278                mii_mediachg(sc->mii_softc);
279                callout_reset(&sc->fman_mac_callout, hz, fman_mac_tick, sc);
280        }
281
282        fman_mac_set_multi(sc);
283}
284
285static void
286fman_mac_stop_locked(struct fman_mac_softc *sc)
287{
288        struct ifnet *ifp;
289        int error;
290
291        FMAN_MAC_ASSERT_LOCKED(sc);
292
293        ifp = sc->ifp;
294        ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
295
296        error = dpa_eth_priv_stop(&sc->mac_dev.net_dev);
297        BSD_ASSERT(error == 0);
298}
299
300static void
301fman_mac_init(void *if_softc)
302{
303        struct fman_mac_softc *sc;
304
305        sc = if_softc;
306        FMAN_MAC_LOCK(sc);
307        fman_mac_init_locked(sc);
308        FMAN_MAC_UNLOCK(sc);
309}
310
311static int
312fman_mac_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
313{
314        struct fman_mac_softc *sc;
315        struct mii_data *mii;
316        struct ifreq *ifr;
317        int error;
318
319        sc = ifp->if_softc;
320        ifr = (struct ifreq *)data;
321
322        error = 0;
323        switch (cmd) {
324        case SIOCSIFFLAGS:
325                FMAN_MAC_LOCK(sc);
326                if (ifp->if_flags & IFF_UP) {
327                        if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
328                                if ((ifp->if_flags ^ sc->if_flags) &
329                                    IFF_PROMISC)
330                                        fman_mac_set_promisc(sc,
331                                            ifp->if_flags);
332                        } else {
333                                fman_mac_init_locked(sc);
334                        }
335                } else {
336                        if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
337                                fman_mac_stop_locked(sc);
338                        }
339                }
340                sc->if_flags = ifp->if_flags;
341                FMAN_MAC_UNLOCK(sc);
342                break;
343        case SIOCSIFMTU:
344                error = fman_mac_set_mtu(sc, ifr->ifr_mtu);
345                break;
346        case SIOCADDMULTI:
347        case SIOCDELMULTI:
348                if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
349                        FMAN_MAC_LOCK(sc);
350                        fman_mac_set_multi(sc);
351                        FMAN_MAC_UNLOCK(sc);
352                }
353                break;
354        case SIOCSIFMEDIA:
355        case SIOCGIFMEDIA:
356                mii = sc->mii_softc;
357
358                if (mii != NULL) {
359                        error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
360                } else {
361                        error = EINVAL;
362                }
363
364                break;
365        default:
366                error = ether_ioctl(ifp, cmd, data);
367                break;
368        }
369
370        return (error);
371}
372
373static int
374fman_mac_media_change(struct ifnet *ifp)
375{
376        struct fman_mac_softc *sc;
377        int error;
378
379        sc = ifp->if_softc;
380        FMAN_MAC_LOCK(sc);
381        error = mii_mediachg(sc->mii_softc);
382        FMAN_MAC_UNLOCK(sc);
383        return (error);
384}
385
386static void
387fman_mac_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
388{
389        struct fman_mac_softc *sc;
390        struct mii_data *mii;
391
392        sc = ifp->if_softc;
393        mii = sc->mii_softc;
394        FMAN_MAC_LOCK(sc);
395        mii_pollstat(mii);
396        ifmr->ifm_active = mii->mii_media_active;
397        ifmr->ifm_status = mii->mii_media_status;
398        FMAN_MAC_UNLOCK(sc);
399}
400
401int
402fman_mac_dev_attach(device_t dev)
403{
404        struct fman_mac_softc *sc;
405        struct ifnet *ifp;
406        struct phy_device *phy_dev;
407        int error;
408
409        sc = device_get_softc(dev);
410
411        mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
412            MTX_DEF);
413
414        callout_init_mtx(&sc->fman_mac_callout, &sc->mtx, 0);
415
416        /* Set up the Ethernet interface */
417        sc->ifp = ifp = if_alloc(IFT_ETHER);
418        if (sc->ifp == NULL) {
419                goto error;
420        }
421
422        snprintf(&sc->name[0], sizeof(sc->name), "fm%im",
423            device_get_unit(device_get_parent(dev)));
424
425        ifp->if_softc = sc;
426        if_initname(ifp, &sc->name[0], sc->mac_dev.data.mac_hw_id);
427        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
428        ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6 |
429            IFCAP_VLAN_MTU | IFCAP_JUMBO_MTU;
430        ifp->if_capenable = ifp->if_capabilities;
431        ifp->if_hwassist = FMAN_MAC_CSUM;
432        ifp->if_transmit = fman_mac_tx;
433        ifp->if_qflush = if_qflush;
434        ifp->if_ioctl = fman_mac_ioctl;
435        ifp->if_init = fman_mac_init;
436        IFQ_SET_MAXLEN(&ifp->if_snd, 128);
437        ifp->if_snd.ifq_drv_maxlen = 128;
438        IFQ_SET_READY(&ifp->if_snd);
439        ifp->if_hdrlen = sizeof(struct ether_vlan_header);
440
441        /* Attach the MII driver if necessary */
442        phy_dev = sc->mac_dev.phy_dev;
443        if (phy_dev != NULL) {
444                error = mii_attach(dev, &sc->miibus, ifp,
445                    fman_mac_media_change, fman_mac_media_status,
446                    BMSR_DEFCAPMASK, phy_dev->mdio.addr, MII_OFFSET_ANY, 0);
447                if (error == 0) {
448                        sc->mii_softc = device_get_softc(sc->miibus);
449                }
450        }
451
452        sc->mac_dev.net_dev.ifp = ifp;
453
454        ether_ifattach(ifp, &sc->mac_dev.addr[0]);
455#if 0
456        fman_mac_set_mtu(sc, ETHERMTU_JUMBO);
457#endif
458
459        return (0);
460
461        if_free(ifp);
462error:
463        mtx_destroy(&sc->mtx);
464        return (ENXIO);
465}
466
467int
468fman_mac_dev_detach(device_t _dev)
469{
470        struct fman_mac_softc *sc = device_get_softc(_dev);
471
472        ether_ifdetach(sc->ifp);
473
474        FMAN_MAC_LOCK(sc);
475        fman_mac_stop_locked(sc);
476        FMAN_MAC_UNLOCK(sc);
477
478        if_free(sc->ifp);
479        mtx_destroy(&sc->mtx);
480
481        return (bus_generic_detach(_dev));
482}
483
484int
485fman_mac_miibus_read_reg(device_t dev, int phy, int reg)
486{
487        struct fman_mac_softc *sc;
488        struct phy_device *phy_dev;
489
490        sc = device_get_softc(dev);
491        phy_dev = sc->mac_dev.phy_dev;
492        BSD_ASSERT(phy == phy_dev->mdio.addr);
493        return (phy_read(phy_dev, reg));
494}
495
496int
497fman_mac_miibus_write_reg(device_t dev, int phy, int reg, int val)
498{
499        struct fman_mac_softc *sc;
500        struct phy_device *phy_dev;
501
502        sc = device_get_softc(dev);
503        phy_dev = sc->mac_dev.phy_dev;
504        BSD_ASSERT(phy == phy_dev->mdio.addr);
505        return (phy_write(phy_dev, reg, val));
506}
507
508void
509fman_mac_miibus_statchg(device_t dev)
510{
511        struct fman_mac_softc *sc;
512        struct mac_device *mac_dev;
513        struct mii_data *mii;
514        u16 speed;
515
516        sc = device_get_softc(dev);
517        mac_dev = &sc->mac_dev;
518        mii = sc->mii_softc;
519
520        FMAN_MAC_ASSERT_LOCKED(sc);
521
522        switch (IFM_SUBTYPE(mii->mii_media_active)) {
523        case IFM_10_T:
524        case IFM_10_2:
525        case IFM_10_5:
526        case IFM_10_STP:
527        case IFM_10_FL:
528                speed = SPEED_10;
529                break;
530        case IFM_100_TX:
531        case IFM_100_FX:
532        case IFM_100_T4:
533        case IFM_100_VG:
534        case IFM_100_T2:
535                speed = SPEED_100;
536                break;
537        case IFM_1000_SX:
538        case IFM_1000_LX:
539        case IFM_1000_CX:
540        case IFM_1000_T:
541                speed = SPEED_1000;
542                break;
543        case IFM_10G_LR:
544        case IFM_10G_SR:
545        case IFM_10G_CX4:
546        case IFM_10G_TWINAX:
547        case IFM_10G_TWINAX_LONG:
548        case IFM_10G_LRM:
549                speed = SPEED_10000;
550                break;
551        default:
552                speed = 0;
553                break;
554        }
555
556        (*mac_dev->adjust_link)(mac_dev, speed);
557}
558
559void dpaa_cleanup_tx_fd(struct ifnet *ifp, const struct qm_fd *fd)
560{
561        struct fman_mac_sgt *sgt;
562
563        BSD_ASSERT(qm_fd_get_format(fd) == qm_fd_sg);
564
565        sgt = (struct fman_mac_sgt *)qm_fd_addr(fd);
566
567        m_freem(sgt->m);
568        uma_zfree(fman_mac_sgt_zone, sgt);
569}
570
571struct dpaa_priv *
572dpaa_get_priv_of_ifp(struct ifnet *ifp)
573{
574        struct fman_mac_softc *sc;
575
576        sc = ifp->if_softc;
577        return (netdev_priv(&sc->mac_dev.net_dev));
578}
579
580int
581dpaa_get_qman_portal_irq_of_ifp(struct ifnet *ifp)
582{
583        struct fman_mac_softc *sc;
584
585        sc = ifp->if_softc;
586        return (qman_portal_get_irq(sc->mac_dev.portal));
587}
Note: See TracBrowser for help on using the repository browser.