On Tue, Nov 06, 2007 at 02:33:53AM -0800, akpm@linux-foundation.org wrote:
[mmotm.git] / drivers / staging / octeon / ethernet-rgmii.c
blobfbaa465d2fac3e2424d81f92d1a446b9cb107133
1 /*********************************************************************
2 * Author: Cavium Networks
4 * Contact: support@caviumnetworks.com
5 * This file is part of the OCTEON SDK
7 * Copyright (c) 2003-2007 Cavium Networks
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.
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.
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/.
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>
32 #include <asm/octeon/octeon.h>
34 #include "ethernet-defines.h"
35 #include "octeon-ethernet.h"
36 #include "ethernet-util.h"
38 #include "cvmx-helper.h"
40 #include <asm/octeon/cvmx-ipd-defs.h>
41 #include <asm/octeon/cvmx-npi-defs.h>
42 #include "cvmx-gmxx-defs.h"
44 DEFINE_SPINLOCK(global_register_lock);
46 static int number_rgmii_ports;
48 static void cvm_oct_rgmii_poll(struct net_device *dev)
50 struct octeon_ethernet *priv = netdev_priv(dev);
51 unsigned long flags;
52 cvmx_helper_link_info_t link_info;
55 * Take the global register lock since we are going to touch
56 * registers that affect more than one port.
58 spin_lock_irqsave(&global_register_lock, flags);
60 link_info = cvmx_helper_link_get(priv->port);
61 if (link_info.u64 == priv->link_info) {
64 * If the 10Mbps preamble workaround is supported and we're
65 * at 10Mbps we may need to do some special checking.
67 if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
70 * Read the GMXX_RXX_INT_REG[PCTERR] bit and
71 * see if we are getting preamble errors.
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) {
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.
89 union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
90 union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
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);
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);
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);
118 spin_unlock_irqrestore(&global_register_lock, flags);
119 return;
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) {
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);
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);
151 link_info = cvmx_helper_link_autoconf(priv->port);
152 priv->link_info = link_info.u64;
153 spin_unlock_irqrestore(&global_register_lock, flags);
155 /* Tell Linux */
156 if (link_info.s.link_up) {
158 if (!netif_carrier_ok(dev))
159 netif_carrier_on(dev);
160 if (priv->queue != -1)
161 DEBUGPRINT
162 ("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
163 dev->name, link_info.s.speed,
164 (link_info.s.full_duplex) ? "Full" : "Half",
165 priv->port, priv->queue);
166 else
167 DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, POW\n",
168 dev->name, link_info.s.speed,
169 (link_info.s.full_duplex) ? "Full" : "Half",
170 priv->port);
171 } else {
173 if (netif_carrier_ok(dev))
174 netif_carrier_off(dev);
175 DEBUGPRINT("%s: Link down\n", dev->name);
179 static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
181 union cvmx_npi_rsl_int_blocks rsl_int_blocks;
182 int index;
183 irqreturn_t return_status = IRQ_NONE;
185 rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
187 /* Check and see if this interrupt was caused by the GMX0 block */
188 if (rsl_int_blocks.s.gmx0) {
190 int interface = 0;
191 /* Loop through every port of this interface */
192 for (index = 0;
193 index < cvmx_helper_ports_on_interface(interface);
194 index++) {
196 /* Read the GMX interrupt status bits */
197 union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
198 gmx_rx_int_reg.u64 =
199 cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
200 (index, interface));
201 gmx_rx_int_reg.u64 &=
202 cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
203 (index, interface));
204 /* Poll the port if inband status changed */
205 if (gmx_rx_int_reg.s.phy_dupx
206 || gmx_rx_int_reg.s.phy_link
207 || gmx_rx_int_reg.s.phy_spd) {
209 struct net_device *dev =
210 cvm_oct_device[cvmx_helper_get_ipd_port
211 (interface, index)];
212 if (dev)
213 cvm_oct_rgmii_poll(dev);
214 gmx_rx_int_reg.u64 = 0;
215 gmx_rx_int_reg.s.phy_dupx = 1;
216 gmx_rx_int_reg.s.phy_link = 1;
217 gmx_rx_int_reg.s.phy_spd = 1;
218 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
219 (index, interface),
220 gmx_rx_int_reg.u64);
221 return_status = IRQ_HANDLED;
226 /* Check and see if this interrupt was caused by the GMX1 block */
227 if (rsl_int_blocks.s.gmx1) {
229 int interface = 1;
230 /* Loop through every port of this interface */
231 for (index = 0;
232 index < cvmx_helper_ports_on_interface(interface);
233 index++) {
235 /* Read the GMX interrupt status bits */
236 union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
237 gmx_rx_int_reg.u64 =
238 cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
239 (index, interface));
240 gmx_rx_int_reg.u64 &=
241 cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
242 (index, interface));
243 /* Poll the port if inband status changed */
244 if (gmx_rx_int_reg.s.phy_dupx
245 || gmx_rx_int_reg.s.phy_link
246 || gmx_rx_int_reg.s.phy_spd) {
248 struct net_device *dev =
249 cvm_oct_device[cvmx_helper_get_ipd_port
250 (interface, index)];
251 if (dev)
252 cvm_oct_rgmii_poll(dev);
253 gmx_rx_int_reg.u64 = 0;
254 gmx_rx_int_reg.s.phy_dupx = 1;
255 gmx_rx_int_reg.s.phy_link = 1;
256 gmx_rx_int_reg.s.phy_spd = 1;
257 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
258 (index, interface),
259 gmx_rx_int_reg.u64);
260 return_status = IRQ_HANDLED;
264 return return_status;
267 int cvm_oct_rgmii_open(struct net_device *dev)
269 union cvmx_gmxx_prtx_cfg gmx_cfg;
270 struct octeon_ethernet *priv = netdev_priv(dev);
271 int interface = INTERFACE(priv->port);
272 int index = INDEX(priv->port);
273 cvmx_helper_link_info_t link_info;
275 gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
276 gmx_cfg.s.en = 1;
277 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
279 if (!octeon_is_simulation()) {
280 link_info = cvmx_helper_link_get(priv->port);
281 if (!link_info.s.link_up)
282 netif_carrier_off(dev);
285 return 0;
288 int cvm_oct_rgmii_stop(struct net_device *dev)
290 union cvmx_gmxx_prtx_cfg gmx_cfg;
291 struct octeon_ethernet *priv = netdev_priv(dev);
292 int interface = INTERFACE(priv->port);
293 int index = INDEX(priv->port);
295 gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
296 gmx_cfg.s.en = 0;
297 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
298 return 0;
301 int cvm_oct_rgmii_init(struct net_device *dev)
303 struct octeon_ethernet *priv = netdev_priv(dev);
304 int r;
306 cvm_oct_common_init(dev);
307 dev->netdev_ops->ndo_stop(dev);
310 * Due to GMX errata in CN3XXX series chips, it is necessary
311 * to take the link down immediately when the PHY changes
312 * state. In order to do this we call the poll function every
313 * time the RGMII inband status changes. This may cause
314 * problems if the PHY doesn't implement inband status
315 * properly.
317 if (number_rgmii_ports == 0) {
318 r = request_irq(OCTEON_IRQ_RML, cvm_oct_rgmii_rml_interrupt,
319 IRQF_SHARED, "RGMII", &number_rgmii_ports);
320 if (r != 0)
321 return r;
323 number_rgmii_ports++;
326 * Only true RGMII ports need to be polled. In GMII mode, port
327 * 0 is really a RGMII port.
329 if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
330 && (priv->port == 0))
331 || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
333 if (!octeon_is_simulation()) {
335 union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
336 int interface = INTERFACE(priv->port);
337 int index = INDEX(priv->port);
340 * Enable interrupts on inband status changes
341 * for this port.
343 gmx_rx_int_en.u64 =
344 cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
345 (index, interface));
346 gmx_rx_int_en.s.phy_dupx = 1;
347 gmx_rx_int_en.s.phy_link = 1;
348 gmx_rx_int_en.s.phy_spd = 1;
349 cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
350 gmx_rx_int_en.u64);
351 priv->poll = cvm_oct_rgmii_poll;
355 return 0;
358 void cvm_oct_rgmii_uninit(struct net_device *dev)
360 struct octeon_ethernet *priv = netdev_priv(dev);
361 cvm_oct_common_uninit(dev);
364 * Only true RGMII ports need to be polled. In GMII mode, port
365 * 0 is really a RGMII port.
367 if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
368 && (priv->port == 0))
369 || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
371 if (!octeon_is_simulation()) {
373 union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
374 int interface = INTERFACE(priv->port);
375 int index = INDEX(priv->port);
378 * Disable interrupts on inband status changes
379 * for this port.
381 gmx_rx_int_en.u64 =
382 cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
383 (index, interface));
384 gmx_rx_int_en.s.phy_dupx = 0;
385 gmx_rx_int_en.s.phy_link = 0;
386 gmx_rx_int_en.s.phy_spd = 0;
387 cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
388 gmx_rx_int_en.u64);
392 /* Remove the interrupt handler when the last port is removed. */
393 number_rgmii_ports--;
394 if (number_rgmii_ports == 0)
395 free_irq(OCTEON_IRQ_RML, &number_rgmii_ports);