ethernet-rgmii.c revision f6ed1b3b3579db5c8c3aaf6fd3010c706973a35d
1/*********************************************************************
2 * Author: Cavium Networks
3 *
4 * Contact: support@caviumnetworks.com
5 * This file is part of the OCTEON SDK
6 *
7 * Copyright (c) 2003-2007 Cavium Networks
8 *
9 * This file is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License, Version 2, as
11 * published by the Free Software Foundation.
12 *
13 * This file is distributed in the hope that it will be useful, but
14 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
16 * NONINFRINGEMENT.  See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this file; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 * or visit http://www.gnu.org/licenses/.
23 *
24 * This file may also be available under a different license from Cavium.
25 * Contact Cavium Networks for more information
26**********************************************************************/
27#include <linux/kernel.h>
28#include <linux/netdevice.h>
29#include <linux/mii.h>
30#include <net/dst.h>
31
32#include <asm/octeon/octeon.h>
33
34#include "ethernet-defines.h"
35#include "octeon-ethernet.h"
36#include "ethernet-util.h"
37
38#include "cvmx-helper.h"
39
40#include <asm/octeon/cvmx-ipd-defs.h>
41#include <asm/octeon/cvmx-npi-defs.h>
42#include "cvmx-gmxx-defs.h"
43
44DEFINE_SPINLOCK(global_register_lock);
45
46static int number_rgmii_ports;
47
48static void cvm_oct_rgmii_poll(struct net_device *dev)
49{
50	struct octeon_ethernet *priv = netdev_priv(dev);
51	unsigned long flags;
52	cvmx_helper_link_info_t link_info;
53
54	/*
55	 * Take the global register lock since we are going to touch
56	 * registers that affect more than one port.
57	 */
58	spin_lock_irqsave(&global_register_lock, flags);
59
60	link_info = cvmx_helper_link_get(priv->port);
61	if (link_info.u64 == priv->link_info) {
62
63		/*
64		 * If the 10Mbps preamble workaround is supported and we're
65		 * at 10Mbps we may need to do some special checking.
66		 */
67		if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
68
69			/*
70			 * Read the GMXX_RXX_INT_REG[PCTERR] bit and
71			 * see if we are getting preamble errors.
72			 */
73			int interface = INTERFACE(priv->port);
74			int index = INDEX(priv->port);
75			union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
76			gmxx_rxx_int_reg.u64 =
77			    cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
78					  (index, interface));
79			if (gmxx_rxx_int_reg.s.pcterr) {
80
81				/*
82				 * We are getting preamble errors at
83				 * 10Mbps.  Most likely the PHY is
84				 * giving us packets with mis aligned
85				 * preambles. In order to get these
86				 * packets we need to disable preamble
87				 * checking and do it in software.
88				 */
89				union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
90				union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
91
92				/* Disable preamble checking */
93				gmxx_rxx_frm_ctl.u64 =
94				    cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL
95						  (index, interface));
96				gmxx_rxx_frm_ctl.s.pre_chk = 0;
97				cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL
98					       (index, interface),
99					       gmxx_rxx_frm_ctl.u64);
100
101				/* Disable FCS stripping */
102				ipd_sub_port_fcs.u64 =
103				    cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
104				ipd_sub_port_fcs.s.port_bit &=
105				    0xffffffffull ^ (1ull << priv->port);
106				cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS,
107					       ipd_sub_port_fcs.u64);
108
109				/* Clear any error bits */
110				cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
111					       (index, interface),
112					       gmxx_rxx_int_reg.u64);
113				DEBUGPRINT("%s: Using 10Mbps with software "
114					   "preamble removal\n",
115				     dev->name);
116			}
117		}
118		spin_unlock_irqrestore(&global_register_lock, flags);
119		return;
120	}
121
122	/* If the 10Mbps preamble workaround is allowed we need to on
123	   preamble checking, FCS stripping, and clear error bits on
124	   every speed change. If errors occur during 10Mbps operation
125	   the above code will change this stuff */
126	if (USE_10MBPS_PREAMBLE_WORKAROUND) {
127
128		union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
129		union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
130		union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
131		int interface = INTERFACE(priv->port);
132		int index = INDEX(priv->port);
133
134		/* Enable preamble checking */
135		gmxx_rxx_frm_ctl.u64 =
136		    cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
137		gmxx_rxx_frm_ctl.s.pre_chk = 1;
138		cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface),
139			       gmxx_rxx_frm_ctl.u64);
140		/* Enable FCS stripping */
141		ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
142		ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port;
143		cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
144		/* Clear any error bits */
145		gmxx_rxx_int_reg.u64 =
146		    cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
147		cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
148			       gmxx_rxx_int_reg.u64);
149	}
150	if (priv->phydev == NULL) {
151		link_info = cvmx_helper_link_autoconf(priv->port);
152		priv->link_info = link_info.u64;
153	}
154	spin_unlock_irqrestore(&global_register_lock, flags);
155
156	if (priv->phydev == NULL) {
157		/* Tell core. */
158		if (link_info.s.link_up) {
159			if (!netif_carrier_ok(dev))
160				netif_carrier_on(dev);
161			if (priv->queue != -1)
162				DEBUGPRINT("%s: %u Mbps %s duplex, "
163					   "port %2d, queue %2d\n",
164					   dev->name, link_info.s.speed,
165					   (link_info.s.full_duplex) ?
166						"Full" : "Half",
167					   priv->port, priv->queue);
168			else
169				DEBUGPRINT("%s: %u Mbps %s duplex, "
170					   "port %2d, POW\n",
171					   dev->name, link_info.s.speed,
172					   (link_info.s.full_duplex) ?
173						"Full" : "Half",
174					   priv->port);
175		} else {
176			if (netif_carrier_ok(dev))
177				netif_carrier_off(dev);
178			DEBUGPRINT("%s: Link down\n", dev->name);
179		}
180	}
181}
182
183static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
184{
185	union cvmx_npi_rsl_int_blocks rsl_int_blocks;
186	int index;
187	irqreturn_t return_status = IRQ_NONE;
188
189	rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
190
191	/* Check and see if this interrupt was caused by the GMX0 block */
192	if (rsl_int_blocks.s.gmx0) {
193
194		int interface = 0;
195		/* Loop through every port of this interface */
196		for (index = 0;
197		     index < cvmx_helper_ports_on_interface(interface);
198		     index++) {
199
200			/* Read the GMX interrupt status bits */
201			union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
202			gmx_rx_int_reg.u64 =
203			    cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
204					  (index, interface));
205			gmx_rx_int_reg.u64 &=
206			    cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
207					  (index, interface));
208			/* Poll the port if inband status changed */
209			if (gmx_rx_int_reg.s.phy_dupx
210			    || gmx_rx_int_reg.s.phy_link
211			    || gmx_rx_int_reg.s.phy_spd) {
212
213				struct net_device *dev =
214				    cvm_oct_device[cvmx_helper_get_ipd_port
215						   (interface, index)];
216				if (dev)
217					cvm_oct_rgmii_poll(dev);
218				gmx_rx_int_reg.u64 = 0;
219				gmx_rx_int_reg.s.phy_dupx = 1;
220				gmx_rx_int_reg.s.phy_link = 1;
221				gmx_rx_int_reg.s.phy_spd = 1;
222				cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
223					       (index, interface),
224					       gmx_rx_int_reg.u64);
225				return_status = IRQ_HANDLED;
226			}
227		}
228	}
229
230	/* Check and see if this interrupt was caused by the GMX1 block */
231	if (rsl_int_blocks.s.gmx1) {
232
233		int interface = 1;
234		/* Loop through every port of this interface */
235		for (index = 0;
236		     index < cvmx_helper_ports_on_interface(interface);
237		     index++) {
238
239			/* Read the GMX interrupt status bits */
240			union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
241			gmx_rx_int_reg.u64 =
242			    cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
243					  (index, interface));
244			gmx_rx_int_reg.u64 &=
245			    cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
246					  (index, interface));
247			/* Poll the port if inband status changed */
248			if (gmx_rx_int_reg.s.phy_dupx
249			    || gmx_rx_int_reg.s.phy_link
250			    || gmx_rx_int_reg.s.phy_spd) {
251
252				struct net_device *dev =
253				    cvm_oct_device[cvmx_helper_get_ipd_port
254						   (interface, index)];
255				if (dev)
256					cvm_oct_rgmii_poll(dev);
257				gmx_rx_int_reg.u64 = 0;
258				gmx_rx_int_reg.s.phy_dupx = 1;
259				gmx_rx_int_reg.s.phy_link = 1;
260				gmx_rx_int_reg.s.phy_spd = 1;
261				cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
262					       (index, interface),
263					       gmx_rx_int_reg.u64);
264				return_status = IRQ_HANDLED;
265			}
266		}
267	}
268	return return_status;
269}
270
271int cvm_oct_rgmii_open(struct net_device *dev)
272{
273	union cvmx_gmxx_prtx_cfg gmx_cfg;
274	struct octeon_ethernet *priv = netdev_priv(dev);
275	int interface = INTERFACE(priv->port);
276	int index = INDEX(priv->port);
277	cvmx_helper_link_info_t link_info;
278
279	gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
280	gmx_cfg.s.en = 1;
281	cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
282
283	if (!octeon_is_simulation()) {
284		link_info = cvmx_helper_link_get(priv->port);
285		if (!link_info.s.link_up)
286			netif_carrier_off(dev);
287	}
288
289	return 0;
290}
291
292int cvm_oct_rgmii_stop(struct net_device *dev)
293{
294	union cvmx_gmxx_prtx_cfg gmx_cfg;
295	struct octeon_ethernet *priv = netdev_priv(dev);
296	int interface = INTERFACE(priv->port);
297	int index = INDEX(priv->port);
298
299	gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
300	gmx_cfg.s.en = 0;
301	cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
302	return 0;
303}
304
305int cvm_oct_rgmii_init(struct net_device *dev)
306{
307	struct octeon_ethernet *priv = netdev_priv(dev);
308	int r;
309
310	cvm_oct_common_init(dev);
311	dev->netdev_ops->ndo_stop(dev);
312
313	/*
314	 * Due to GMX errata in CN3XXX series chips, it is necessary
315	 * to take the link down immediately when the PHY changes
316	 * state. In order to do this we call the poll function every
317	 * time the RGMII inband status changes.  This may cause
318	 * problems if the PHY doesn't implement inband status
319	 * properly.
320	 */
321	if (number_rgmii_ports == 0) {
322		r = request_irq(OCTEON_IRQ_RML, cvm_oct_rgmii_rml_interrupt,
323				IRQF_SHARED, "RGMII", &number_rgmii_ports);
324		if (r != 0)
325			return r;
326	}
327	number_rgmii_ports++;
328
329	/*
330	 * Only true RGMII ports need to be polled. In GMII mode, port
331	 * 0 is really a RGMII port.
332	 */
333	if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
334	     && (priv->port == 0))
335	    || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
336
337		if (!octeon_is_simulation()) {
338
339			union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
340			int interface = INTERFACE(priv->port);
341			int index = INDEX(priv->port);
342
343			/*
344			 * Enable interrupts on inband status changes
345			 * for this port.
346			 */
347			gmx_rx_int_en.u64 =
348			    cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
349					  (index, interface));
350			gmx_rx_int_en.s.phy_dupx = 1;
351			gmx_rx_int_en.s.phy_link = 1;
352			gmx_rx_int_en.s.phy_spd = 1;
353			cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
354				       gmx_rx_int_en.u64);
355			priv->poll = cvm_oct_rgmii_poll;
356		}
357	}
358
359	return 0;
360}
361
362void cvm_oct_rgmii_uninit(struct net_device *dev)
363{
364	struct octeon_ethernet *priv = netdev_priv(dev);
365	cvm_oct_common_uninit(dev);
366
367	/*
368	 * Only true RGMII ports need to be polled. In GMII mode, port
369	 * 0 is really a RGMII port.
370	 */
371	if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
372	     && (priv->port == 0))
373	    || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
374
375		if (!octeon_is_simulation()) {
376
377			union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
378			int interface = INTERFACE(priv->port);
379			int index = INDEX(priv->port);
380
381			/*
382			 * Disable interrupts on inband status changes
383			 * for this port.
384			 */
385			gmx_rx_int_en.u64 =
386			    cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
387					  (index, interface));
388			gmx_rx_int_en.s.phy_dupx = 0;
389			gmx_rx_int_en.s.phy_link = 0;
390			gmx_rx_int_en.s.phy_spd = 0;
391			cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
392				       gmx_rx_int_en.u64);
393		}
394	}
395
396	/* Remove the interrupt handler when the last port is removed. */
397	number_rgmii_ports--;
398	if (number_rgmii_ports == 0)
399		free_irq(OCTEON_IRQ_RML, &number_rgmii_ports);
400}
401