cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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}