• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
31 static DEFINE_SPINLOCK(global_register_lock);
32 
cvm_oct_set_hw_preamble(struct octeon_ethernet * priv,bool enable)33 static 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 
cvm_oct_check_preamble_errors(struct net_device * dev)64 static 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 
cvm_oct_rgmii_poll(struct net_device * dev)114 static 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 
cvm_oct_rgmii_open(struct net_device * dev)142 int 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 }
170