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


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