File Index
Files and directories
Sign in
FILE

ethernet-rgmii.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/octeon/ethernet-rgmii.c C
  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}
~/ Read-only file preview.