xref: /linux/drivers/staging/octeon/ethernet-rgmii.c (revision ead5d1f4d877e92c051e1a1ade623d0d30e71619)
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 
20 static DEFINE_SPINLOCK(global_register_lock);
21 
cvm_oct_set_hw_preamble(struct octeon_ethernet * priv,bool enable)22 static 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 
cvm_oct_check_preamble_errors(struct net_device * dev)53 static 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 
cvm_oct_rgmii_poll(struct net_device * dev)103 static 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 
cvm_oct_rgmii_open(struct net_device * dev)131 int 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 }
159