The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/mips/cavium/if_octm.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2010-2011 Juli Mallett <jmallett@FreeBSD.org>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. 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  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  * $FreeBSD: releng/12.0/sys/mips/cavium/if_octm.c 326259 2017-11-27 15:07:26Z pfg $
   29  */
   30 
   31 /*
   32  * Cavium Octeon management port Ethernet devices.
   33  */
   34 
   35 #include "opt_inet.h"
   36 
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/bus.h>
   40 #include <sys/endian.h>
   41 #include <sys/kernel.h>
   42 #include <sys/mbuf.h>
   43 #include <sys/lock.h>
   44 #include <sys/module.h>
   45 #include <sys/mutex.h>
   46 #include <sys/rman.h>
   47 #include <sys/socket.h>
   48 #include <sys/sockio.h>
   49 #include <sys/sysctl.h>
   50 
   51 #include <net/bpf.h>
   52 #include <net/ethernet.h>
   53 #include <net/if.h>
   54 #include <net/if_dl.h>
   55 #include <net/if_media.h>
   56 #include <net/if_types.h>
   57 #include <net/if_var.h>
   58 #include <net/if_vlan_var.h>
   59 
   60 #ifdef INET
   61 #include <netinet/in.h>
   62 #include <netinet/if_ether.h>
   63 #endif
   64 
   65 #include <contrib/octeon-sdk/cvmx.h>
   66 #include <mips/cavium/octeon_irq.h>
   67 #include <contrib/octeon-sdk/cvmx-mgmt-port.h>
   68 
   69 struct octm_softc {
   70         struct ifnet *sc_ifp;
   71         device_t sc_dev;
   72         unsigned sc_port;
   73         int sc_flags;
   74         struct ifmedia sc_ifmedia;
   75         struct resource *sc_intr;
   76         void *sc_intr_cookie;
   77 };
   78 
   79 static void     octm_identify(driver_t *, device_t);
   80 static int      octm_probe(device_t);
   81 static int      octm_attach(device_t);
   82 static int      octm_detach(device_t);
   83 static int      octm_shutdown(device_t);
   84 
   85 static void     octm_init(void *);
   86 static int      octm_transmit(struct ifnet *, struct mbuf *);
   87 
   88 static int      octm_medchange(struct ifnet *);
   89 static void     octm_medstat(struct ifnet *, struct ifmediareq *);
   90 
   91 static int      octm_ioctl(struct ifnet *, u_long, caddr_t);
   92 
   93 static void     octm_rx_intr(void *);
   94 
   95 static device_method_t octm_methods[] = {
   96         /* Device interface */
   97         DEVMETHOD(device_identify,      octm_identify),
   98         DEVMETHOD(device_probe,         octm_probe),
   99         DEVMETHOD(device_attach,        octm_attach),
  100         DEVMETHOD(device_detach,        octm_detach),
  101         DEVMETHOD(device_shutdown,      octm_shutdown),
  102 
  103         { 0, 0 }
  104 };
  105 
  106 static driver_t octm_driver = {
  107         "octm",
  108         octm_methods,
  109         sizeof (struct octm_softc),
  110 };
  111 
  112 static devclass_t octm_devclass;
  113 
  114 DRIVER_MODULE(octm, ciu, octm_driver, octm_devclass, 0, 0);
  115 
  116 static void
  117 octm_identify(driver_t *drv, device_t parent)
  118 {
  119         unsigned i;
  120 
  121         if (!octeon_has_feature(OCTEON_FEATURE_MGMT_PORT))
  122                 return;
  123 
  124         for (i = 0; i < CVMX_MGMT_PORT_NUM_PORTS; i++)
  125                 BUS_ADD_CHILD(parent, 0, "octm", i);
  126 }
  127 
  128 static int
  129 octm_probe(device_t dev)
  130 {
  131         cvmx_mgmt_port_result_t result;
  132 
  133         result = cvmx_mgmt_port_initialize(device_get_unit(dev));
  134         switch (result) {
  135         case CVMX_MGMT_PORT_SUCCESS:
  136                 break;
  137         case CVMX_MGMT_PORT_NO_MEMORY:
  138                 return (ENOBUFS);
  139         case CVMX_MGMT_PORT_INVALID_PARAM:
  140                 return (ENXIO);
  141         case CVMX_MGMT_PORT_INIT_ERROR:
  142                 return (EIO);
  143         }
  144 
  145         device_set_desc(dev, "Cavium Octeon Management Ethernet");
  146 
  147         return (0);
  148 }
  149 
  150 static int
  151 octm_attach(device_t dev)
  152 {
  153         struct ifnet *ifp;
  154         struct octm_softc *sc;
  155         cvmx_mixx_irhwm_t mixx_irhwm;
  156         cvmx_mixx_intena_t mixx_intena;
  157         uint64_t mac;
  158         int error;
  159         int irq;
  160         int rid;
  161 
  162         sc = device_get_softc(dev);
  163         sc->sc_dev = dev;
  164         sc->sc_port = device_get_unit(dev);
  165 
  166         switch (sc->sc_port) {
  167         case 0:
  168                 irq = OCTEON_IRQ_MII;
  169                 break;
  170         case 1:
  171                 irq = OCTEON_IRQ_MII1;
  172                 break;
  173         default:
  174                 device_printf(dev, "unsupported management port %u.\n", sc->sc_port);
  175                 return (ENXIO);
  176         }
  177 
  178         /*
  179          * Set MAC address for this management port.
  180          */
  181         mac = 0;
  182         memcpy((u_int8_t *)&mac + 2, cvmx_sysinfo_get()->mac_addr_base, 6);
  183         mac += sc->sc_port;
  184 
  185         cvmx_mgmt_port_set_mac(sc->sc_port, mac);
  186 
  187         /* No watermark for input ring.  */
  188         mixx_irhwm.u64 = 0;
  189         cvmx_write_csr(CVMX_MIXX_IRHWM(sc->sc_port), mixx_irhwm.u64);
  190 
  191         /* Enable input ring interrupts.  */
  192         mixx_intena.u64 = 0;
  193         mixx_intena.s.ithena = 1;
  194         cvmx_write_csr(CVMX_MIXX_INTENA(sc->sc_port), mixx_intena.u64);
  195 
  196         /* Allocate and establish interrupt.  */
  197         rid = 0;
  198         sc->sc_intr = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ, &rid,
  199             irq, irq, 1, RF_ACTIVE);
  200         if (sc->sc_intr == NULL) {
  201                 device_printf(dev, "unable to allocate IRQ.\n");
  202                 return (ENXIO);
  203         }
  204 
  205         error = bus_setup_intr(sc->sc_dev, sc->sc_intr, INTR_TYPE_NET, NULL,
  206             octm_rx_intr, sc, &sc->sc_intr_cookie);
  207         if (error != 0) {
  208                 device_printf(dev, "unable to setup interrupt.\n");
  209                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
  210                 return (ENXIO);
  211         }
  212 
  213         bus_describe_intr(sc->sc_dev, sc->sc_intr, sc->sc_intr_cookie, "rx");
  214 
  215         /* XXX Possibly should enable TX interrupts.  */
  216 
  217         ifp = if_alloc(IFT_ETHER);
  218         if (ifp == NULL) {
  219                 device_printf(dev, "cannot allocate ifnet.\n");
  220                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
  221                 return (ENOMEM);
  222         }
  223 
  224         if_initname(ifp, device_get_name(dev), device_get_unit(dev));
  225         ifp->if_mtu = ETHERMTU;
  226         ifp->if_init = octm_init;
  227         ifp->if_softc = sc;
  228         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI;
  229         ifp->if_ioctl = octm_ioctl;
  230 
  231         sc->sc_ifp = ifp;
  232         sc->sc_flags = ifp->if_flags;
  233 
  234         ifmedia_init(&sc->sc_ifmedia, 0, octm_medchange, octm_medstat);
  235 
  236         ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
  237         ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
  238 
  239         ether_ifattach(ifp, (const u_int8_t *)&mac + 2);
  240 
  241         ifp->if_transmit = octm_transmit;
  242 
  243         ifp->if_hdrlen = sizeof(struct ether_vlan_header);
  244         ifp->if_capabilities = IFCAP_VLAN_MTU;
  245         ifp->if_capenable = ifp->if_capabilities;
  246 
  247         IFQ_SET_MAXLEN(&ifp->if_snd, CVMX_MGMT_PORT_NUM_TX_BUFFERS);
  248         ifp->if_snd.ifq_drv_maxlen = CVMX_MGMT_PORT_NUM_TX_BUFFERS;
  249         IFQ_SET_READY(&ifp->if_snd);
  250 
  251         return (bus_generic_attach(dev));
  252 }
  253 
  254 static int
  255 octm_detach(device_t dev)
  256 {
  257         struct octm_softc *sc;
  258         cvmx_mgmt_port_result_t result;
  259 
  260         sc = device_get_softc(dev);
  261 
  262         result = cvmx_mgmt_port_initialize(sc->sc_port);
  263         switch (result) {
  264         case CVMX_MGMT_PORT_SUCCESS:
  265                 break;
  266         case CVMX_MGMT_PORT_NO_MEMORY:
  267                 return (ENOBUFS);
  268         case CVMX_MGMT_PORT_INVALID_PARAM:
  269                 return (ENXIO);
  270         case CVMX_MGMT_PORT_INIT_ERROR:
  271                 return (EIO);
  272         }
  273 
  274         bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
  275         /* XXX Incomplete.  */
  276 
  277         return (0);
  278 }
  279 
  280 static int
  281 octm_shutdown(device_t dev)
  282 {
  283         return (octm_detach(dev));
  284 }
  285 
  286 static void
  287 octm_init(void *arg)
  288 {
  289         struct ifnet *ifp;
  290         struct octm_softc *sc;
  291         cvmx_mgmt_port_netdevice_flags_t flags;
  292         uint64_t mac;
  293 
  294         sc = arg;
  295         ifp = sc->sc_ifp;
  296 
  297         if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
  298                 cvmx_mgmt_port_disable(sc->sc_port);
  299 
  300                 ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
  301         }
  302 
  303         /*
  304          * NB:
  305          * MAC must be set before allmulti and promisc below, as
  306          * cvmx_mgmt_port_set_mac will always enable the CAM, and turning on
  307          * promiscuous mode only works with the CAM disabled.
  308          */
  309         mac = 0;
  310         memcpy((u_int8_t *)&mac + 2, IF_LLADDR(ifp), 6);
  311         cvmx_mgmt_port_set_mac(sc->sc_port, mac);
  312 
  313         /*
  314          * This is done unconditionally, rather than only if sc_flags have
  315          * changed because of set_mac's effect on the CAM noted above.
  316          */
  317         flags = 0;
  318         if ((ifp->if_flags & IFF_ALLMULTI) != 0)
  319                 flags |= CVMX_IFF_ALLMULTI;
  320         if ((ifp->if_flags & IFF_PROMISC) != 0)
  321                 flags |= CVMX_IFF_PROMISC;
  322         cvmx_mgmt_port_set_multicast_list(sc->sc_port, flags);
  323 
  324         /* XXX link state?  */
  325 
  326         if ((ifp->if_flags & IFF_UP) != 0)
  327                 cvmx_mgmt_port_enable(sc->sc_port);
  328 
  329         ifp->if_drv_flags |= IFF_DRV_RUNNING;
  330         ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
  331 }
  332 
  333 static int
  334 octm_transmit(struct ifnet *ifp, struct mbuf *m)
  335 {
  336         struct octm_softc *sc;
  337         cvmx_mgmt_port_result_t result;
  338 
  339         sc = ifp->if_softc;
  340 
  341         if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
  342             IFF_DRV_RUNNING) {
  343                 m_freem(m);
  344                 return (0);
  345         }
  346 
  347         result = cvmx_mgmt_port_sendm(sc->sc_port, m);
  348 
  349         if (result == CVMX_MGMT_PORT_SUCCESS) {
  350                 ETHER_BPF_MTAP(ifp, m);
  351 
  352                 if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
  353                 if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
  354         } else
  355                 if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
  356 
  357         m_freem(m);
  358 
  359         switch (result) {
  360         case CVMX_MGMT_PORT_SUCCESS:
  361                 return (0);
  362         case CVMX_MGMT_PORT_NO_MEMORY:
  363                 return (ENOBUFS);
  364         case CVMX_MGMT_PORT_INVALID_PARAM:
  365                 return (ENXIO);
  366         case CVMX_MGMT_PORT_INIT_ERROR:
  367                 return (EIO);
  368         default:
  369                 return (EDOOFUS);
  370         }
  371 }
  372 
  373 static int
  374 octm_medchange(struct ifnet *ifp)
  375 {
  376         return (ENOTSUP);
  377 }
  378 
  379 static void
  380 octm_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
  381 {
  382         struct octm_softc *sc;
  383         cvmx_helper_link_info_t link_info;
  384 
  385         sc = ifp->if_softc;
  386 
  387         ifm->ifm_status = IFM_AVALID;
  388         ifm->ifm_active = IFT_ETHER;
  389 
  390         link_info = cvmx_mgmt_port_link_get(sc->sc_port);
  391         if (!link_info.s.link_up)
  392                 return;
  393 
  394         ifm->ifm_status |= IFM_ACTIVE;
  395 
  396         switch (link_info.s.speed) {
  397         case 10:
  398                 ifm->ifm_active |= IFM_10_T;
  399                 break;
  400         case 100:
  401                 ifm->ifm_active |= IFM_100_TX;
  402                 break;
  403         case 1000:
  404                 ifm->ifm_active |= IFM_1000_T;
  405                 break;
  406         case 10000:
  407                 ifm->ifm_active |= IFM_10G_T;
  408                 break;
  409         }
  410 
  411         if (link_info.s.full_duplex)
  412                 ifm->ifm_active |= IFM_FDX;
  413         else
  414                 ifm->ifm_active |= IFM_HDX;
  415 }
  416 
  417 static int
  418 octm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  419 {
  420         struct octm_softc *sc;
  421         struct ifreq *ifr;
  422 #ifdef INET
  423         struct ifaddr *ifa;
  424 #endif
  425         int error;
  426 
  427         sc = ifp->if_softc;
  428         ifr = (struct ifreq *)data;
  429 #ifdef INET
  430         ifa = (struct ifaddr *)data;
  431 #endif
  432 
  433         switch (cmd) {
  434         case SIOCSIFADDR:
  435 #ifdef INET
  436                 /*
  437                  * Avoid reinitialization unless it's necessary.
  438                  */
  439                 if (ifa->ifa_addr->sa_family == AF_INET) {
  440                         ifp->if_flags |= IFF_UP;
  441                         if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
  442                                 octm_init(sc);
  443                         arp_ifinit(ifp, ifa);
  444 
  445                         return (0);
  446                 }
  447 #endif
  448                 error = ether_ioctl(ifp, cmd, data);
  449                 if (error != 0)
  450                         return (error);
  451                 return (0);
  452 
  453         case SIOCSIFFLAGS:
  454                 if (ifp->if_flags == sc->sc_flags)
  455                         return (0);
  456                 if ((ifp->if_flags & IFF_UP) != 0) {
  457                         octm_init(sc);
  458                 } else {
  459                         if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
  460                                 cvmx_mgmt_port_disable(sc->sc_port);
  461 
  462                                 ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
  463                         }
  464                 }
  465                 sc->sc_flags = ifp->if_flags;
  466                 return (0);
  467         
  468         case SIOCSIFCAP:
  469                 /*
  470                  * Just change the capabilities in software, currently none
  471                  * require reprogramming hardware, they just toggle whether we
  472                  * make use of already-present facilities in software.
  473                  */
  474                 ifp->if_capenable = ifr->ifr_reqcap;
  475                 return (0);
  476 
  477         case SIOCSIFMTU:
  478                 cvmx_mgmt_port_set_max_packet_size(sc->sc_port, ifr->ifr_mtu + ifp->if_hdrlen);
  479                 return (0);
  480 
  481         case SIOCSIFMEDIA:
  482         case SIOCGIFMEDIA:
  483                 error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd);
  484                 if (error != 0)
  485                         return (error);
  486                 return (0);
  487         
  488         default:
  489                 error = ether_ioctl(ifp, cmd, data);
  490                 if (error != 0)
  491                         return (error);
  492                 return (0);
  493         }
  494 }
  495 
  496 static void
  497 octm_rx_intr(void *arg)
  498 {
  499         struct octm_softc *sc = arg;
  500         cvmx_mixx_isr_t mixx_isr;
  501         int len;
  502 
  503         mixx_isr.u64 = cvmx_read_csr(CVMX_MIXX_ISR(sc->sc_port));
  504         if (!mixx_isr.s.irthresh) {
  505                 device_printf(sc->sc_dev, "stray interrupt.\n");
  506                 return;
  507         }
  508 
  509         for (;;) {
  510                 struct mbuf *m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
  511                 if (m == NULL) {
  512                         device_printf(sc->sc_dev, "no memory for receive mbuf.\n");
  513                         return;
  514                 }
  515 
  516 
  517                 len = cvmx_mgmt_port_receive(sc->sc_port, MCLBYTES, m->m_data);
  518                 if (len > 0) {
  519                         m->m_pkthdr.rcvif = sc->sc_ifp;
  520                         m->m_pkthdr.len = m->m_len = len;
  521 
  522                         if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1);
  523 
  524                         (*sc->sc_ifp->if_input)(sc->sc_ifp, m);
  525 
  526                         continue;
  527                 }
  528 
  529                 m_freem(m);
  530 
  531                 if (len == 0)
  532                         break;
  533 
  534                 if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1);
  535         }
  536 
  537         /* Acknowledge interrupts.  */
  538         cvmx_write_csr(CVMX_MIXX_ISR(sc->sc_port), mixx_isr.u64);
  539         cvmx_read_csr(CVMX_MIXX_ISR(sc->sc_port));
  540 }

Cache object: 269b91c1f8821e8f6b9c85973bb53741


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.