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/octe/ethernet-rgmii.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) 2003-2007  Cavium Networks (support@cavium.com). All rights
    3 reserved.
    4 
    5 
    6 Redistribution and use in source and binary forms, with or without
    7 modification, are permitted provided that the following conditions are
    8 met:
    9 
   10     * Redistributions of source code must retain the above copyright
   11       notice, this list of conditions and the following disclaimer.
   12 
   13     * Redistributions in binary form must reproduce the above
   14       copyright notice, this list of conditions and the following
   15       disclaimer in the documentation and/or other materials provided
   16       with the distribution.
   17 
   18     * Neither the name of Cavium Networks nor the names of
   19       its contributors may be used to endorse or promote products
   20       derived from this software without specific prior written
   21       permission.
   22 
   23 This Software, including technical data, may be subject to U.S. export  control laws, including the U.S. Export Administration Act and its  associated regulations, and may be subject to export or import  regulations in other countries.
   24 
   25 TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
   26 AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
   27 
   28 *************************************************************************/
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/bus.h>
   36 #include <sys/endian.h>
   37 #include <sys/kernel.h>
   38 #include <sys/mbuf.h>
   39 #include <sys/rman.h>
   40 #include <sys/socket.h>
   41 #include <sys/lock.h>
   42 #include <sys/mutex.h>
   43 
   44 #include <net/ethernet.h>
   45 #include <net/if.h>
   46 
   47 #include "wrapper-cvmx-includes.h"
   48 #include "ethernet-headers.h"
   49 
   50 #include "octebusvar.h"
   51 
   52 extern struct ifnet *cvm_oct_device[];
   53 
   54 static struct mtx global_register_lock;
   55 MTX_SYSINIT(global_register_lock, &global_register_lock,
   56             "RGMII Global", MTX_SPIN);
   57 
   58 static int number_rgmii_ports;
   59 
   60 static void cvm_oct_rgmii_poll(struct ifnet *ifp)
   61 {
   62         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
   63         cvmx_helper_link_info_t link_info;
   64 
   65         /* Take the global register lock since we are going to touch
   66            registers that affect more than one port */
   67         mtx_lock_spin(&global_register_lock);
   68 
   69         link_info = cvmx_helper_link_get(priv->port);
   70         if (link_info.u64 == priv->link_info) {
   71 
   72                 /* If the 10Mbps preamble workaround is supported and we're
   73                    at 10Mbps we may need to do some special checking */
   74                 if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
   75 
   76                         /* Read the GMXX_RXX_INT_REG[PCTERR] bit and
   77                            see if we are getting preamble errors */
   78                         int interface = INTERFACE(priv->port);
   79                         int index = INDEX(priv->port);
   80                         cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
   81                         gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
   82                         if (gmxx_rxx_int_reg.s.pcterr) {
   83 
   84                                 /* We are getting preamble errors at 10Mbps.
   85                                    Most likely the PHY is giving us packets
   86                                    with mis aligned preambles. In order to get
   87                                    these packets we need to disable preamble
   88                                    checking and do it in software */
   89                                 cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
   90                                 cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
   91 
   92                                 /* Disable preamble checking */
   93                                 gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
   94                                 gmxx_rxx_frm_ctl.s.pre_chk = 0;
   95                                 cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
   96 
   97                                 /* Disable FCS stripping */
   98                                 ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
   99                                 ipd_sub_port_fcs.s.port_bit &= 0xffffffffull ^ (1ull<<priv->port);
  100                                 cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
  101 
  102                                 /* Clear any error bits */
  103                                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
  104                                 DEBUGPRINT("%s: Using 10Mbps with software preamble removal\n", if_name(ifp));
  105                         }
  106                 }
  107                 mtx_unlock_spin(&global_register_lock);
  108                 return;
  109         }
  110 
  111         /* If the 10Mbps preamble workaround is allowed we need to on
  112            preamble checking, FCS stripping, and clear error bits on
  113            every speed change. If errors occur during 10Mbps operation
  114            the above code will change this stuff */
  115         if (USE_10MBPS_PREAMBLE_WORKAROUND) {
  116 
  117                 cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
  118                 cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
  119                 cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
  120                 int interface = INTERFACE(priv->port);
  121                 int index = INDEX(priv->port);
  122 
  123                 /* Enable preamble checking */
  124                 gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
  125                 gmxx_rxx_frm_ctl.s.pre_chk = 1;
  126                 cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
  127                 /* Enable FCS stripping */
  128                 ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
  129                 ipd_sub_port_fcs.s.port_bit |= 1ull<<priv->port;
  130                 cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
  131                 /* Clear any error bits */
  132                 gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
  133                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
  134         }
  135 
  136         if (priv->miibus == NULL) {
  137                 link_info = cvmx_helper_link_autoconf(priv->port);
  138                 priv->link_info = link_info.u64;
  139                 priv->need_link_update = 1;
  140         }
  141         mtx_unlock_spin(&global_register_lock);
  142 }
  143 
  144 
  145 static int cvm_oct_rgmii_rml_interrupt(void *dev_id)
  146 {
  147         cvmx_npi_rsl_int_blocks_t rsl_int_blocks;
  148         int index;
  149         int return_status = FILTER_STRAY;
  150 
  151         rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
  152 
  153         /* Check and see if this interrupt was caused by the GMX0 block */
  154         if (rsl_int_blocks.s.gmx0) {
  155 
  156                 int interface = 0;
  157                 /* Loop through every port of this interface */
  158                 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
  159 
  160                         /* Read the GMX interrupt status bits */
  161                         cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
  162                         gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
  163                         gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
  164                         /* Poll the port if inband status changed */
  165                         if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
  166 
  167                                 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
  168                                 if (ifp)
  169                                         cvm_oct_rgmii_poll(ifp);
  170                                 gmx_rx_int_reg.u64 = 0;
  171                                 gmx_rx_int_reg.s.phy_dupx = 1;
  172                                 gmx_rx_int_reg.s.phy_link = 1;
  173                                 gmx_rx_int_reg.s.phy_spd = 1;
  174                                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
  175                                 return_status = FILTER_HANDLED;
  176                         }
  177                 }
  178         }
  179 
  180         /* Check and see if this interrupt was caused by the GMX1 block */
  181         if (rsl_int_blocks.s.gmx1) {
  182 
  183                 int interface = 1;
  184                 /* Loop through every port of this interface */
  185                 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
  186 
  187                         /* Read the GMX interrupt status bits */
  188                         cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
  189                         gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
  190                         gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
  191                         /* Poll the port if inband status changed */
  192                         if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
  193 
  194                                 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
  195                                 if (ifp)
  196                                         cvm_oct_rgmii_poll(ifp);
  197                                 gmx_rx_int_reg.u64 = 0;
  198                                 gmx_rx_int_reg.s.phy_dupx = 1;
  199                                 gmx_rx_int_reg.s.phy_link = 1;
  200                                 gmx_rx_int_reg.s.phy_spd = 1;
  201                                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
  202                                 return_status = FILTER_HANDLED;
  203                         }
  204                 }
  205         }
  206         return return_status;
  207 }
  208 
  209 
  210 int cvm_oct_rgmii_init(struct ifnet *ifp)
  211 {
  212         struct octebus_softc *sc;
  213         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
  214         int error;
  215         int rid;
  216 
  217         if (cvm_oct_common_init(ifp) != 0)
  218             return ENXIO;
  219 
  220         priv->open = cvm_oct_common_open;
  221         priv->stop = cvm_oct_common_stop;
  222         priv->stop(ifp);
  223 
  224         /* Due to GMX errata in CN3XXX series chips, it is necessary to take the
  225            link down immediately whne the PHY changes state. In order to do this
  226            we call the poll function every time the RGMII inband status changes.
  227            This may cause problems if the PHY doesn't implement inband status
  228            properly */
  229         if (number_rgmii_ports == 0) {
  230                 sc = device_get_softc(device_get_parent(priv->dev));
  231 
  232                 rid = 0;
  233                 sc->sc_rgmii_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ,
  234                                                       &rid, OCTEON_IRQ_RML,
  235                                                       OCTEON_IRQ_RML, 1,
  236                                                       RF_ACTIVE);
  237                 if (sc->sc_rgmii_irq == NULL) {
  238                         device_printf(sc->sc_dev, "could not allocate RGMII irq");
  239                         return ENXIO;
  240                 }
  241 
  242                 error = bus_setup_intr(sc->sc_dev, sc->sc_rgmii_irq,
  243                                        INTR_TYPE_NET | INTR_MPSAFE,
  244                                        cvm_oct_rgmii_rml_interrupt, NULL,
  245                                        &number_rgmii_ports, NULL);
  246                 if (error != 0) {
  247                         device_printf(sc->sc_dev, "could not setup RGMII irq");
  248                         return error;
  249                 }
  250         }
  251         number_rgmii_ports++;
  252 
  253         /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
  254            a RGMII port */
  255         if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
  256             (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
  257 
  258                 if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) {
  259 
  260                         cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
  261                         int interface = INTERFACE(priv->port);
  262                         int index = INDEX(priv->port);
  263 
  264                         /* Enable interrupts on inband status changes for this port */
  265                         gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
  266                         gmx_rx_int_en.s.phy_dupx = 1;
  267                         gmx_rx_int_en.s.phy_link = 1;
  268                         gmx_rx_int_en.s.phy_spd = 1;
  269                         cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
  270                         priv->poll = cvm_oct_rgmii_poll;
  271                 }
  272         }
  273 
  274         return 0;
  275 }
  276 
  277 void cvm_oct_rgmii_uninit(struct ifnet *ifp)
  278 {
  279         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
  280         cvm_oct_common_uninit(ifp);
  281 
  282         /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
  283            a RGMII port */
  284         if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
  285             (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
  286 
  287                 if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) {
  288 
  289                         cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
  290                         int interface = INTERFACE(priv->port);
  291                         int index = INDEX(priv->port);
  292 
  293                         /* Disable interrupts on inband status changes for this port */
  294                         gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
  295                         gmx_rx_int_en.s.phy_dupx = 0;
  296                         gmx_rx_int_en.s.phy_link = 0;
  297                         gmx_rx_int_en.s.phy_spd = 0;
  298                         cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
  299                 }
  300         }
  301 
  302         /* Remove the interrupt handler when the last port is removed */
  303         number_rgmii_ports--;
  304         if (number_rgmii_ports == 0)
  305                 panic("%s: need to implement IRQ release.", __func__);
  306 }
  307 

Cache object: d29c77e36ff5114c4c945ece7050a8b8


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