Staging: octeon-ethernet: Convert to use PHY Abstraction Layer.
[linux-2.6-block.git] / drivers / staging / octeon / ethernet-rgmii.c
CommitLineData
80ff0fd3
DD
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"
80ff0fd3
DD
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 }
f6ed1b3b
DD
150 if (priv->phydev == NULL) {
151 link_info = cvmx_helper_link_autoconf(priv->port);
152 priv->link_info = link_info.u64;
153 }
80ff0fd3
DD
154 spin_unlock_irqrestore(&global_register_lock, flags);
155
f6ed1b3b
DD
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 }
80ff0fd3
DD
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
f696a108 271int cvm_oct_rgmii_open(struct net_device *dev)
80ff0fd3
DD
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
f696a108 292int cvm_oct_rgmii_stop(struct net_device *dev)
80ff0fd3
DD
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);
f696a108 311 dev->netdev_ops->ndo_stop(dev);
80ff0fd3
DD
312
313 /*
314 * Due to GMX errata in CN3XXX series chips, it is necessary
82c7c11f 315 * to take the link down immediately when the PHY changes
80ff0fd3
DD
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);
82c7c11f
RK
324 if (r != 0)
325 return r;
80ff0fd3
DD
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}