ethernet-rgmii.c (4597B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * This file is based on code from OCTEON SDK by Cavium Networks. 4 * 5 * Copyright (c) 2003-2007 Cavium Networks 6 */ 7 8#include <linux/kernel.h> 9#include <linux/netdevice.h> 10#include <linux/interrupt.h> 11#include <linux/phy.h> 12#include <linux/ratelimit.h> 13#include <net/dst.h> 14 15#include "octeon-ethernet.h" 16#include "ethernet-defines.h" 17#include "ethernet-util.h" 18#include "ethernet-mdio.h" 19 20static DEFINE_SPINLOCK(global_register_lock); 21 22static void cvm_oct_set_hw_preamble(struct octeon_ethernet *priv, bool enable) 23{ 24 union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl; 25 union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs; 26 union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg; 27 int interface = INTERFACE(priv->port); 28 int index = INDEX(priv->port); 29 30 /* Set preamble checking. */ 31 gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, 32 interface)); 33 gmxx_rxx_frm_ctl.s.pre_chk = enable; 34 cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), 35 gmxx_rxx_frm_ctl.u64); 36 37 /* Set FCS stripping. */ 38 ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS); 39 if (enable) 40 ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port; 41 else 42 ipd_sub_port_fcs.s.port_bit &= 43 0xffffffffull ^ (1ull << priv->port); 44 cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64); 45 46 /* Clear any error bits. */ 47 gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, 48 interface)); 49 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), 50 gmxx_rxx_int_reg.u64); 51} 52 53static void cvm_oct_check_preamble_errors(struct net_device *dev) 54{ 55 struct octeon_ethernet *priv = netdev_priv(dev); 56 union cvmx_helper_link_info link_info; 57 unsigned long flags; 58 59 link_info.u64 = priv->link_info; 60 61 /* 62 * Take the global register lock since we are going to 63 * touch registers that affect more than one port. 64 */ 65 spin_lock_irqsave(&global_register_lock, flags); 66 67 if (link_info.s.speed == 10 && priv->last_speed == 10) { 68 /* 69 * Read the GMXX_RXX_INT_REG[PCTERR] bit and see if we are 70 * getting preamble errors. 71 */ 72 int interface = INTERFACE(priv->port); 73 int index = INDEX(priv->port); 74 union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg; 75 76 gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG 77 (index, interface)); 78 if (gmxx_rxx_int_reg.s.pcterr) { 79 /* 80 * We are getting preamble errors at 10Mbps. Most 81 * likely the PHY is giving us packets with misaligned 82 * preambles. In order to get these packets we need to 83 * disable preamble checking and do it in software. 84 */ 85 cvm_oct_set_hw_preamble(priv, false); 86 printk_ratelimited("%s: Using 10Mbps with software preamble removal\n", 87 dev->name); 88 } 89 } else { 90 /* 91 * Since the 10Mbps preamble workaround is allowed we need to 92 * enable preamble checking, FCS stripping, and clear error 93 * bits on every speed change. If errors occur during 10Mbps 94 * operation the above code will change this stuff 95 */ 96 if (priv->last_speed != link_info.s.speed) 97 cvm_oct_set_hw_preamble(priv, true); 98 priv->last_speed = link_info.s.speed; 99 } 100 spin_unlock_irqrestore(&global_register_lock, flags); 101} 102 103static void cvm_oct_rgmii_poll(struct net_device *dev) 104{ 105 struct octeon_ethernet *priv = netdev_priv(dev); 106 union cvmx_helper_link_info link_info; 107 bool status_change; 108 109 link_info = cvmx_helper_link_get(priv->port); 110 if (priv->link_info != link_info.u64 && 111 cvmx_helper_link_set(priv->port, link_info)) 112 link_info.u64 = priv->link_info; 113 status_change = priv->link_info != link_info.u64; 114 priv->link_info = link_info.u64; 115 116 cvm_oct_check_preamble_errors(dev); 117 118 if (likely(!status_change)) 119 return; 120 121 /* Tell core. */ 122 if (link_info.s.link_up) { 123 if (!netif_carrier_ok(dev)) 124 netif_carrier_on(dev); 125 } else if (netif_carrier_ok(dev)) { 126 netif_carrier_off(dev); 127 } 128 cvm_oct_note_carrier(priv, link_info); 129} 130 131int cvm_oct_rgmii_open(struct net_device *dev) 132{ 133 struct octeon_ethernet *priv = netdev_priv(dev); 134 int ret; 135 136 ret = cvm_oct_common_open(dev, cvm_oct_rgmii_poll); 137 if (ret) 138 return ret; 139 140 if (dev->phydev) { 141 /* 142 * In phydev mode, we need still periodic polling for the 143 * preamble error checking, and we also need to call this 144 * function on every link state change. 145 * 146 * Only true RGMII ports need to be polled. In GMII mode, port 147 * 0 is really a RGMII port. 148 */ 149 if ((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII && 150 priv->port == 0) || 151 (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) { 152 priv->poll = cvm_oct_check_preamble_errors; 153 cvm_oct_check_preamble_errors(dev); 154 } 155 } 156 157 return 0; 158}