Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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