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$
   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         { 0, 0 }
  103 };
  104 
  105 static driver_t octm_driver = {
  106         "octm",
  107         octm_methods,
  108         sizeof (struct octm_softc),
  109 };
  110 
  111 static devclass_t octm_devclass;
  112 
  113 DRIVER_MODULE(octm, ciu, octm_driver, octm_devclass, 0, 0);
  114 
  115 static void
  116 octm_identify(driver_t *drv, device_t parent)
  117 {
  118         unsigned i;
  119 
  120         if (!octeon_has_feature(OCTEON_FEATURE_MGMT_PORT))
  121                 return;
  122 
  123         for (i = 0; i < CVMX_MGMT_PORT_NUM_PORTS; i++)
  124                 BUS_ADD_CHILD(parent, 0, "octm", i);
  125 }
  126 
  127 static int
  128 octm_probe(device_t dev)
  129 {
  130         cvmx_mgmt_port_result_t result;
  131 
  132         result = cvmx_mgmt_port_initialize(device_get_unit(dev));
  133         switch (result) {
  134         case CVMX_MGMT_PORT_SUCCESS:
  135                 break;
  136         case CVMX_MGMT_PORT_NO_MEMORY:
  137                 return (ENOBUFS);
  138         case CVMX_MGMT_PORT_INVALID_PARAM:
  139                 return (ENXIO);
  140         case CVMX_MGMT_PORT_INIT_ERROR:
  141                 return (EIO);
  142         }
  143 
  144         device_set_desc(dev, "Cavium Octeon Management Ethernet");
  145 
  146         return (0);
  147 }
  148 
  149 static int
  150 octm_attach(device_t dev)
  151 {
  152         struct ifnet *ifp;
  153         struct octm_softc *sc;
  154         cvmx_mixx_irhwm_t mixx_irhwm;
  155         cvmx_mixx_intena_t mixx_intena;
  156         uint64_t mac;
  157         int error;
  158         int irq;
  159         int rid;
  160 
  161         sc = device_get_softc(dev);
  162         sc->sc_dev = dev;
  163         sc->sc_port = device_get_unit(dev);
  164 
  165         switch (sc->sc_port) {
  166         case 0:
  167                 irq = OCTEON_IRQ_MII;
  168                 break;
  169         case 1:
  170                 irq = OCTEON_IRQ_MII1;
  171                 break;
  172         default:
  173                 device_printf(dev, "unsupported management port %u.\n", sc->sc_port);
  174                 return (ENXIO);
  175         }
  176 
  177         /*
  178          * Set MAC address for this management port.
  179          */
  180         mac = 0;
  181         memcpy((u_int8_t *)&mac + 2, cvmx_sysinfo_get()->mac_addr_base, 6);
  182         mac += sc->sc_port;
  183 
  184         cvmx_mgmt_port_set_mac(sc->sc_port, mac);
  185 
  186         /* No watermark for input ring.  */
  187         mixx_irhwm.u64 = 0;
  188         cvmx_write_csr(CVMX_MIXX_IRHWM(sc->sc_port), mixx_irhwm.u64);
  189 
  190         /* Enable input ring interrupts.  */
  191         mixx_intena.u64 = 0;
  192         mixx_intena.s.ithena = 1;
  193         cvmx_write_csr(CVMX_MIXX_INTENA(sc->sc_port), mixx_intena.u64);
  194 
  195         /* Allocate and establish interrupt.  */
  196         rid = 0;
  197         sc->sc_intr = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ, &rid,
  198             irq, irq, 1, RF_ACTIVE);
  199         if (sc->sc_intr == NULL) {
  200                 device_printf(dev, "unable to allocate IRQ.\n");
  201                 return (ENXIO);
  202         }
  203 
  204         error = bus_setup_intr(sc->sc_dev, sc->sc_intr, INTR_TYPE_NET, NULL,
  205             octm_rx_intr, sc, &sc->sc_intr_cookie);
  206         if (error != 0) {
  207                 device_printf(dev, "unable to setup interrupt.\n");
  208                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
  209                 return (ENXIO);
  210         }
  211 
  212         bus_describe_intr(sc->sc_dev, sc->sc_intr, sc->sc_intr_cookie, "rx");
  213 
  214         /* XXX Possibly should enable TX interrupts.  */
  215 
  216         ifp = if_alloc(IFT_ETHER);
  217         if (ifp == NULL) {
  218                 device_printf(dev, "cannot allocate ifnet.\n");
  219                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
  220                 return (ENOMEM);
  221         }
  222 
  223         if_initname(ifp, device_get_name(dev), device_get_unit(dev));
  224         ifp->if_mtu = ETHERMTU;
  225         ifp->if_init = octm_init;
  226         ifp->if_softc = sc;
  227         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI;
  228         ifp->if_ioctl = octm_ioctl;
  229 
  230         sc->sc_ifp = ifp;
  231         sc->sc_flags = ifp->if_flags;
  232 
  233         ifmedia_init(&sc->sc_ifmedia, 0, octm_medchange, octm_medstat);
  234 
  235         ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
  236         ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
  237 
  238         ether_ifattach(ifp, (const u_int8_t *)&mac + 2);
  239 
  240         ifp->if_transmit = octm_transmit;
  241 
  242         ifp->if_hdrlen = sizeof(struct ether_vlan_header);
  243         ifp->if_capabilities = IFCAP_VLAN_MTU;
  244         ifp->if_capenable = ifp->if_capabilities;
  245 
  246         IFQ_SET_MAXLEN(&ifp->if_snd, CVMX_MGMT_PORT_NUM_TX_BUFFERS);
  247         ifp->if_snd.ifq_drv_maxlen = CVMX_MGMT_PORT_NUM_TX_BUFFERS;
  248         IFQ_SET_READY(&ifp->if_snd);
  249 
  250         return (bus_generic_attach(dev));
  251 }
  252 
  253 static int
  254 octm_detach(device_t dev)
  255 {
  256         struct octm_softc *sc;
  257         cvmx_mgmt_port_result_t result;
  258 
  259         sc = device_get_softc(dev);
  260 
  261         result = cvmx_mgmt_port_initialize(sc->sc_port);
  262         switch (result) {
  263         case CVMX_MGMT_PORT_SUCCESS:
  264                 break;
  265         case CVMX_MGMT_PORT_NO_MEMORY:
  266                 return (ENOBUFS);
  267         case CVMX_MGMT_PORT_INVALID_PARAM:
  268                 return (ENXIO);
  269         case CVMX_MGMT_PORT_INIT_ERROR:
  270                 return (EIO);
  271         }
  272 
  273         bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
  274         /* XXX Incomplete.  */
  275 
  276         return (0);
  277 }
  278 
  279 static int
  280 octm_shutdown(device_t dev)
  281 {
  282         return (octm_detach(dev));
  283 }
  284 
  285 static void
  286 octm_init(void *arg)
  287 {
  288         struct ifnet *ifp;
  289         struct octm_softc *sc;
  290         cvmx_mgmt_port_netdevice_flags_t flags;
  291         uint64_t mac;
  292 
  293         sc = arg;
  294         ifp = sc->sc_ifp;
  295 
  296         if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
  297                 cvmx_mgmt_port_disable(sc->sc_port);
  298 
  299                 ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
  300         }
  301 
  302         /*
  303          * NB:
  304          * MAC must be set before allmulti and promisc below, as
  305          * cvmx_mgmt_port_set_mac will always enable the CAM, and turning on
  306          * promiscuous mode only works with the CAM disabled.
  307          */
  308         mac = 0;
  309         memcpy((u_int8_t *)&mac + 2, IF_LLADDR(ifp), 6);
  310         cvmx_mgmt_port_set_mac(sc->sc_port, mac);
  311 
  312         /*
  313          * This is done unconditionally, rather than only if sc_flags have
  314          * changed because of set_mac's effect on the CAM noted above.
  315          */
  316         flags = 0;
  317         if ((ifp->if_flags & IFF_ALLMULTI) != 0)
  318                 flags |= CVMX_IFF_ALLMULTI;
  319         if ((ifp->if_flags & IFF_PROMISC) != 0)
  320                 flags |= CVMX_IFF_PROMISC;
  321         cvmx_mgmt_port_set_multicast_list(sc->sc_port, flags);
  322 
  323         /* XXX link state?  */
  324 
  325         if ((ifp->if_flags & IFF_UP) != 0)
  326                 cvmx_mgmt_port_enable(sc->sc_port);
  327 
  328         ifp->if_drv_flags |= IFF_DRV_RUNNING;
  329         ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
  330 }
  331 
  332 static int
  333 octm_transmit(struct ifnet *ifp, struct mbuf *m)
  334 {
  335         struct octm_softc *sc;
  336         cvmx_mgmt_port_result_t result;
  337 
  338         sc = ifp->if_softc;
  339 
  340         if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
  341             IFF_DRV_RUNNING) {
  342                 m_freem(m);
  343                 return (0);
  344         }
  345 
  346         result = cvmx_mgmt_port_sendm(sc->sc_port, m);
  347 
  348         if (result == CVMX_MGMT_PORT_SUCCESS) {
  349                 ETHER_BPF_MTAP(ifp, m);
  350 
  351                 if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
  352                 if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
  353         } else
  354                 if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
  355 
  356         m_freem(m);
  357 
  358         switch (result) {
  359         case CVMX_MGMT_PORT_SUCCESS:
  360                 return (0);
  361         case CVMX_MGMT_PORT_NO_MEMORY:
  362                 return (ENOBUFS);
  363         case CVMX_MGMT_PORT_INVALID_PARAM:
  364                 return (ENXIO);
  365         case CVMX_MGMT_PORT_INIT_ERROR:
  366                 return (EIO);
  367         default:
  368                 return (EDOOFUS);
  369         }
  370 }
  371 
  372 static int
  373 octm_medchange(struct ifnet *ifp)
  374 {
  375         return (ENOTSUP);
  376 }
  377 
  378 static void
  379 octm_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
  380 {
  381         struct octm_softc *sc;
  382         cvmx_helper_link_info_t link_info;
  383 
  384         sc = ifp->if_softc;
  385 
  386         ifm->ifm_status = IFM_AVALID;
  387         ifm->ifm_active = IFT_ETHER;
  388 
  389         link_info = cvmx_mgmt_port_link_get(sc->sc_port);
  390         if (!link_info.s.link_up)
  391                 return;
  392 
  393         ifm->ifm_status |= IFM_ACTIVE;
  394 
  395         switch (link_info.s.speed) {
  396         case 10:
  397                 ifm->ifm_active |= IFM_10_T;
  398                 break;
  399         case 100:
  400                 ifm->ifm_active |= IFM_100_TX;
  401                 break;
  402         case 1000:
  403                 ifm->ifm_active |= IFM_1000_T;
  404                 break;
  405         case 10000:
  406                 ifm->ifm_active |= IFM_10G_T;
  407                 break;
  408         }
  409 
  410         if (link_info.s.full_duplex)
  411                 ifm->ifm_active |= IFM_FDX;
  412         else
  413                 ifm->ifm_active |= IFM_HDX;
  414 }
  415 
  416 static int
  417 octm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  418 {
  419         struct octm_softc *sc;
  420         struct ifreq *ifr;
  421 #ifdef INET
  422         struct ifaddr *ifa;
  423 #endif
  424         int error;
  425 
  426         sc = ifp->if_softc;
  427         ifr = (struct ifreq *)data;
  428 #ifdef INET
  429         ifa = (struct ifaddr *)data;
  430 #endif
  431 
  432         switch (cmd) {
  433         case SIOCSIFADDR:
  434 #ifdef INET
  435                 /*
  436                  * Avoid reinitialization unless it's necessary.
  437                  */
  438                 if (ifa->ifa_addr->sa_family == AF_INET) {
  439                         ifp->if_flags |= IFF_UP;
  440                         if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
  441                                 octm_init(sc);
  442                         arp_ifinit(ifp, ifa);
  443 
  444                         return (0);
  445                 }
  446 #endif
  447                 error = ether_ioctl(ifp, cmd, data);
  448                 if (error != 0)
  449                         return (error);
  450                 return (0);
  451 
  452         case SIOCSIFFLAGS:
  453                 if (ifp->if_flags == sc->sc_flags)
  454                         return (0);
  455                 if ((ifp->if_flags & IFF_UP) != 0) {
  456                         octm_init(sc);
  457                 } else {
  458                         if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
  459                                 cvmx_mgmt_port_disable(sc->sc_port);
  460 
  461                                 ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
  462                         }
  463                 }
  464                 sc->sc_flags = ifp->if_flags;
  465                 return (0);
  466 
  467         case SIOCSIFCAP:
  468                 /*
  469                  * Just change the capabilities in software, currently none
  470                  * require reprogramming hardware, they just toggle whether we
  471                  * make use of already-present facilities in software.
  472                  */
  473                 ifp->if_capenable = ifr->ifr_reqcap;
  474                 return (0);
  475 
  476         case SIOCSIFMTU:
  477                 cvmx_mgmt_port_set_max_packet_size(sc->sc_port, ifr->ifr_mtu + ifp->if_hdrlen);
  478                 return (0);
  479 
  480         case SIOCSIFMEDIA:
  481         case SIOCGIFMEDIA:
  482                 error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd);
  483                 if (error != 0)
  484                         return (error);
  485                 return (0);
  486 
  487         default:
  488                 error = ether_ioctl(ifp, cmd, data);
  489                 if (error != 0)
  490                         return (error);
  491                 return (0);
  492         }
  493 }
  494 
  495 static void
  496 octm_rx_intr(void *arg)
  497 {
  498         struct octm_softc *sc = arg;
  499         cvmx_mixx_isr_t mixx_isr;
  500         int len;
  501 
  502         mixx_isr.u64 = cvmx_read_csr(CVMX_MIXX_ISR(sc->sc_port));
  503         if (!mixx_isr.s.irthresh) {
  504                 device_printf(sc->sc_dev, "stray interrupt.\n");
  505                 return;
  506         }
  507 
  508         for (;;) {
  509                 struct mbuf *m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
  510                 if (m == NULL) {
  511                         device_printf(sc->sc_dev, "no memory for receive mbuf.\n");
  512                         return;
  513                 }
  514 
  515                 len = cvmx_mgmt_port_receive(sc->sc_port, MCLBYTES, m->m_data);
  516                 if (len > 0) {
  517                         m->m_pkthdr.rcvif = sc->sc_ifp;
  518                         m->m_pkthdr.len = m->m_len = len;
  519 
  520                         if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1);
  521 
  522                         (*sc->sc_ifp->if_input)(sc->sc_ifp, m);
  523 
  524                         continue;
  525                 }
  526 
  527                 m_freem(m);
  528 
  529                 if (len == 0)
  530                         break;
  531 
  532                 if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1);
  533         }
  534 
  535         /* Acknowledge interrupts.  */
  536         cvmx_write_csr(CVMX_MIXX_ISR(sc->sc_port), mixx_isr.u64);
  537         cvmx_read_csr(CVMX_MIXX_ISR(sc->sc_port));
  538 }

Cache object: 6718c47385f48d1bb9a07ab195d1fe56


[ 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.