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: releng/8.4/sys/mips/cavium/octe/ethernet-rgmii.c 215938 2010-11-27 12:26:40Z jchandra $");
   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 int octeon_is_simulation(void);
   53 extern struct ifnet *cvm_oct_device[];
   54 
   55 static struct mtx global_register_lock;
   56 MTX_SYSINIT(global_register_lock, &global_register_lock,
   57             "RGMII Global", MTX_SPIN);
   58 
   59 static int number_rgmii_ports;
   60 
   61 static void cvm_oct_rgmii_poll(struct ifnet *ifp)
   62 {
   63         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
   64         cvmx_helper_link_info_t link_info;
   65 
   66         /* Take the global register lock since we are going to touch
   67            registers that affect more than one port */
   68         mtx_lock_spin(&global_register_lock);
   69 
   70         link_info = cvmx_helper_link_get(priv->port);
   71         if (link_info.u64 == priv->link_info) {
   72 
   73                 /* If the 10Mbps preamble workaround is supported and we're
   74                    at 10Mbps we may need to do some special checking */
   75                 if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
   76 
   77                         /* Read the GMXX_RXX_INT_REG[PCTERR] bit and
   78                            see if we are getting preamble errors */
   79                         int interface = INTERFACE(priv->port);
   80                         int index = INDEX(priv->port);
   81                         cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
   82                         gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
   83                         if (gmxx_rxx_int_reg.s.pcterr) {
   84 
   85                                 /* We are getting preamble errors at 10Mbps.
   86                                    Most likely the PHY is giving us packets
   87                                    with mis aligned preambles. In order to get
   88                                    these packets we need to disable preamble
   89                                    checking and do it in software */
   90                                 cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
   91                                 cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
   92 
   93                                 /* Disable preamble checking */
   94                                 gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
   95                                 gmxx_rxx_frm_ctl.s.pre_chk = 0;
   96                                 cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
   97 
   98                                 /* Disable FCS stripping */
   99                                 ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
  100                                 ipd_sub_port_fcs.s.port_bit &= 0xffffffffull ^ (1ull<<priv->port);
  101                                 cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
  102 
  103                                 /* Clear any error bits */
  104                                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
  105                                 DEBUGPRINT("%s: Using 10Mbps with software preamble removal\n", if_name(ifp));
  106                         }
  107                 }
  108                 mtx_unlock_spin(&global_register_lock);
  109                 return;
  110         }
  111 
  112         /* If the 10Mbps preamble workaround is allowed we need to on
  113            preamble checking, FCS stripping, and clear error bits on
  114            every speed change. If errors occur during 10Mbps operation
  115            the above code will change this stuff */
  116         if (USE_10MBPS_PREAMBLE_WORKAROUND) {
  117 
  118                 cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
  119                 cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
  120                 cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
  121                 int interface = INTERFACE(priv->port);
  122                 int index = INDEX(priv->port);
  123 
  124                 /* Enable preamble checking */
  125                 gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
  126                 gmxx_rxx_frm_ctl.s.pre_chk = 1;
  127                 cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
  128                 /* Enable FCS stripping */
  129                 ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
  130                 ipd_sub_port_fcs.s.port_bit |= 1ull<<priv->port;
  131                 cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
  132                 /* Clear any error bits */
  133                 gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
  134                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
  135         }
  136 
  137         link_info = cvmx_helper_link_autoconf(priv->port);
  138         priv->link_info = link_info.u64;
  139         priv->need_link_update = 1;
  140         mtx_unlock_spin(&global_register_lock);
  141 }
  142 
  143 
  144 static int cvm_oct_rgmii_rml_interrupt(void *dev_id)
  145 {
  146         cvmx_npi_rsl_int_blocks_t rsl_int_blocks;
  147         int index;
  148         int return_status = FILTER_STRAY;
  149 
  150         rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
  151 
  152         /* Check and see if this interrupt was caused by the GMX0 block */
  153         if (rsl_int_blocks.s.gmx0) {
  154 
  155                 int interface = 0;
  156                 /* Loop through every port of this interface */
  157                 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
  158 
  159                         /* Read the GMX interrupt status bits */
  160                         cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
  161                         gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
  162                         gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
  163                         /* Poll the port if inband status changed */
  164                         if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
  165 
  166                                 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
  167                                 if (ifp)
  168                                         cvm_oct_rgmii_poll(ifp);
  169                                 gmx_rx_int_reg.u64 = 0;
  170                                 gmx_rx_int_reg.s.phy_dupx = 1;
  171                                 gmx_rx_int_reg.s.phy_link = 1;
  172                                 gmx_rx_int_reg.s.phy_spd = 1;
  173                                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
  174                                 return_status = FILTER_HANDLED;
  175                         }
  176                 }
  177         }
  178 
  179         /* Check and see if this interrupt was caused by the GMX1 block */
  180         if (rsl_int_blocks.s.gmx1) {
  181 
  182                 int interface = 1;
  183                 /* Loop through every port of this interface */
  184                 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
  185 
  186                         /* Read the GMX interrupt status bits */
  187                         cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
  188                         gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
  189                         gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
  190                         /* Poll the port if inband status changed */
  191                         if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
  192 
  193                                 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
  194                                 if (ifp)
  195                                         cvm_oct_rgmii_poll(ifp);
  196                                 gmx_rx_int_reg.u64 = 0;
  197                                 gmx_rx_int_reg.s.phy_dupx = 1;
  198                                 gmx_rx_int_reg.s.phy_link = 1;
  199                                 gmx_rx_int_reg.s.phy_spd = 1;
  200                                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
  201                                 return_status = FILTER_HANDLED;
  202                         }
  203                 }
  204         }
  205         return return_status;
  206 }
  207 
  208 
  209 static int cvm_oct_rgmii_open(struct ifnet *ifp)
  210 {
  211         cvmx_gmxx_prtx_cfg_t gmx_cfg;
  212         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
  213         int interface = INTERFACE(priv->port);
  214         int index = INDEX(priv->port);
  215         cvmx_helper_link_info_t link_info;
  216 
  217         gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
  218         gmx_cfg.s.en = 1;
  219         cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
  220 
  221         if (!octeon_is_simulation()) {
  222              link_info = cvmx_helper_link_get(priv->port);
  223              if (!link_info.s.link_up)
  224                 if_link_state_change(ifp, LINK_STATE_DOWN);
  225              else
  226                 if_link_state_change(ifp, LINK_STATE_UP);
  227         }
  228 
  229         return 0;
  230 }
  231 
  232 static int cvm_oct_rgmii_stop(struct ifnet *ifp)
  233 {
  234         cvmx_gmxx_prtx_cfg_t gmx_cfg;
  235         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
  236         int interface = INTERFACE(priv->port);
  237         int index = INDEX(priv->port);
  238 
  239         gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
  240         gmx_cfg.s.en = 0;
  241         cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
  242         return 0;
  243 }
  244 
  245 int cvm_oct_rgmii_init(struct ifnet *ifp)
  246 {
  247         struct octebus_softc *sc;
  248         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
  249         int error;
  250         int rid;
  251 
  252         cvm_oct_common_init(ifp);
  253         priv->open = cvm_oct_rgmii_open;
  254         priv->stop = cvm_oct_rgmii_stop;
  255         priv->stop(ifp);
  256 
  257         /* Due to GMX errata in CN3XXX series chips, it is necessary to take the
  258            link down immediately whne the PHY changes state. In order to do this
  259            we call the poll function every time the RGMII inband status changes.
  260            This may cause problems if the PHY doesn't implement inband status
  261            properly */
  262         if (number_rgmii_ports == 0) {
  263                 sc = device_get_softc(device_get_parent(priv->dev));
  264 
  265                 rid = 0;
  266                 sc->sc_rgmii_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ,
  267                                                       &rid, CVMX_IRQ_RML,
  268                                                       CVMX_IRQ_RML, 1,
  269                                                       RF_ACTIVE);
  270                 if (sc->sc_rgmii_irq == NULL) {
  271                         device_printf(sc->sc_dev, "could not allocate RGMII irq");
  272                         return ENXIO;
  273                 }
  274 
  275                 error = bus_setup_intr(sc->sc_dev, sc->sc_rgmii_irq,
  276                                        INTR_TYPE_NET | INTR_MPSAFE,
  277                                        cvm_oct_rgmii_rml_interrupt, NULL,
  278                                        &number_rgmii_ports, NULL);
  279                 if (error != 0) {
  280                         device_printf(sc->sc_dev, "could not setup RGMII irq");
  281                         return error;
  282                 }
  283         }
  284         number_rgmii_ports++;
  285 
  286         /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
  287            a RGMII port */
  288         if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
  289             (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
  290 
  291                 if (!octeon_is_simulation()) {
  292 
  293                         cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
  294                         int interface = INTERFACE(priv->port);
  295                         int index = INDEX(priv->port);
  296 
  297                         /* Enable interrupts on inband status changes for this port */
  298                         gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
  299                         gmx_rx_int_en.s.phy_dupx = 1;
  300                         gmx_rx_int_en.s.phy_link = 1;
  301                         gmx_rx_int_en.s.phy_spd = 1;
  302                         cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
  303                         priv->poll = cvm_oct_rgmii_poll;
  304                 }
  305         }
  306 
  307         return 0;
  308 }
  309 
  310 void cvm_oct_rgmii_uninit(struct ifnet *ifp)
  311 {
  312         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
  313         cvm_oct_common_uninit(ifp);
  314 
  315         /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
  316            a RGMII port */
  317         if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
  318             (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
  319 
  320                 if (!octeon_is_simulation()) {
  321 
  322                         cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
  323                         int interface = INTERFACE(priv->port);
  324                         int index = INDEX(priv->port);
  325 
  326                         /* Disable interrupts on inband status changes for this port */
  327                         gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
  328                         gmx_rx_int_en.s.phy_dupx = 0;
  329                         gmx_rx_int_en.s.phy_link = 0;
  330                         gmx_rx_int_en.s.phy_spd = 0;
  331                         cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
  332                 }
  333         }
  334 
  335         /* Remove the interrupt handler when the last port is removed */
  336         number_rgmii_ports--;
  337         if (number_rgmii_ports == 0)
  338                 panic("%s: need to implement IRQ release.", __func__);
  339 }
  340 

Cache object: 80577134cf288136bad3583b9b0b74ff


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