Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * This file is based on code from OCTEON SDK by Cavium Networks.
0004  *
0005  * Copyright (c) 2003-2007 Cavium Networks
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/netdevice.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/phy.h>
0012 #include <linux/ratelimit.h>
0013 #include <net/dst.h>
0014 
0015 #include "octeon-ethernet.h"
0016 #include "ethernet-defines.h"
0017 #include "ethernet-util.h"
0018 #include "ethernet-mdio.h"
0019 
0020 static DEFINE_SPINLOCK(global_register_lock);
0021 
0022 static void cvm_oct_set_hw_preamble(struct octeon_ethernet *priv, bool enable)
0023 {
0024     union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
0025     union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
0026     union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
0027     int interface = INTERFACE(priv->port);
0028     int index = INDEX(priv->port);
0029 
0030     /* Set preamble checking. */
0031     gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index,
0032                                    interface));
0033     gmxx_rxx_frm_ctl.s.pre_chk = enable;
0034     cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface),
0035                gmxx_rxx_frm_ctl.u64);
0036 
0037     /* Set FCS stripping. */
0038     ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
0039     if (enable)
0040         ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port;
0041     else
0042         ipd_sub_port_fcs.s.port_bit &=
0043                     0xffffffffull ^ (1ull << priv->port);
0044     cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
0045 
0046     /* Clear any error bits. */
0047     gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index,
0048                                    interface));
0049     cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
0050                gmxx_rxx_int_reg.u64);
0051 }
0052 
0053 static void cvm_oct_check_preamble_errors(struct net_device *dev)
0054 {
0055     struct octeon_ethernet *priv = netdev_priv(dev);
0056     union cvmx_helper_link_info link_info;
0057     unsigned long flags;
0058 
0059     link_info.u64 = priv->link_info;
0060 
0061     /*
0062      * Take the global register lock since we are going to
0063      * touch registers that affect more than one port.
0064      */
0065     spin_lock_irqsave(&global_register_lock, flags);
0066 
0067     if (link_info.s.speed == 10 && priv->last_speed == 10) {
0068         /*
0069          * Read the GMXX_RXX_INT_REG[PCTERR] bit and see if we are
0070          * getting preamble errors.
0071          */
0072         int interface = INTERFACE(priv->port);
0073         int index = INDEX(priv->port);
0074         union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
0075 
0076         gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
0077                             (index, interface));
0078         if (gmxx_rxx_int_reg.s.pcterr) {
0079             /*
0080              * We are getting preamble errors at 10Mbps. Most
0081              * likely the PHY is giving us packets with misaligned
0082              * preambles. In order to get these packets we need to
0083              * disable preamble checking and do it in software.
0084              */
0085             cvm_oct_set_hw_preamble(priv, false);
0086             printk_ratelimited("%s: Using 10Mbps with software preamble removal\n",
0087                        dev->name);
0088         }
0089     } else {
0090         /*
0091          * Since the 10Mbps preamble workaround is allowed we need to
0092          * enable preamble checking, FCS stripping, and clear error
0093          * bits on every speed change. If errors occur during 10Mbps
0094          * operation the above code will change this stuff
0095          */
0096         if (priv->last_speed != link_info.s.speed)
0097             cvm_oct_set_hw_preamble(priv, true);
0098         priv->last_speed = link_info.s.speed;
0099     }
0100     spin_unlock_irqrestore(&global_register_lock, flags);
0101 }
0102 
0103 static void cvm_oct_rgmii_poll(struct net_device *dev)
0104 {
0105     struct octeon_ethernet *priv = netdev_priv(dev);
0106     union cvmx_helper_link_info link_info;
0107     bool status_change;
0108 
0109     link_info = cvmx_helper_link_get(priv->port);
0110     if (priv->link_info != link_info.u64 &&
0111         cvmx_helper_link_set(priv->port, link_info))
0112         link_info.u64 = priv->link_info;
0113     status_change = priv->link_info != link_info.u64;
0114     priv->link_info = link_info.u64;
0115 
0116     cvm_oct_check_preamble_errors(dev);
0117 
0118     if (likely(!status_change))
0119         return;
0120 
0121     /* Tell core. */
0122     if (link_info.s.link_up) {
0123         if (!netif_carrier_ok(dev))
0124             netif_carrier_on(dev);
0125     } else if (netif_carrier_ok(dev)) {
0126         netif_carrier_off(dev);
0127     }
0128     cvm_oct_note_carrier(priv, link_info);
0129 }
0130 
0131 int cvm_oct_rgmii_open(struct net_device *dev)
0132 {
0133     struct octeon_ethernet *priv = netdev_priv(dev);
0134     int ret;
0135 
0136     ret = cvm_oct_common_open(dev, cvm_oct_rgmii_poll);
0137     if (ret)
0138         return ret;
0139 
0140     if (dev->phydev) {
0141         /*
0142          * In phydev mode, we need still periodic polling for the
0143          * preamble error checking, and we also need to call this
0144          * function on every link state change.
0145          *
0146          * Only true RGMII ports need to be polled. In GMII mode, port
0147          * 0 is really a RGMII port.
0148          */
0149         if ((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII &&
0150              priv->port  == 0) ||
0151             (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
0152             priv->poll = cvm_oct_check_preamble_errors;
0153             cvm_oct_check_preamble_errors(dev);
0154         }
0155     }
0156 
0157     return 0;
0158 }