staging: octeon-ethernet: rgmii: refactor gmx block interrupt handling
[linux/fpc-iii.git] / drivers / staging / octeon / ethernet-rgmii.c
blob0101fcd5d348ecf234eb35e65ce976acd47abfe2
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/interrupt.h>
30 #include <linux/phy.h>
31 #include <linux/ratelimit.h>
32 #include <net/dst.h>
34 #include <asm/octeon/octeon.h>
36 #include "ethernet-defines.h"
37 #include "octeon-ethernet.h"
38 #include "ethernet-util.h"
39 #include "ethernet-mdio.h"
41 #include <asm/octeon/cvmx-helper.h>
43 #include <asm/octeon/cvmx-ipd-defs.h>
44 #include <asm/octeon/cvmx-npi-defs.h>
45 #include <asm/octeon/cvmx-gmxx-defs.h>
47 static DEFINE_SPINLOCK(global_register_lock);
49 static int number_rgmii_ports;
51 static void cvm_oct_rgmii_poll(struct net_device *dev)
53 struct octeon_ethernet *priv = netdev_priv(dev);
54 unsigned long flags = 0;
55 cvmx_helper_link_info_t link_info;
56 int use_global_register_lock = (priv->phydev == NULL);
58 BUG_ON(in_interrupt());
59 if (use_global_register_lock) {
61 * Take the global register lock since we are going to
62 * touch registers that affect more than one port.
64 spin_lock_irqsave(&global_register_lock, flags);
65 } else {
66 mutex_lock(&priv->phydev->bus->mdio_lock);
69 link_info = cvmx_helper_link_get(priv->port);
70 if (link_info.u64 == priv->link_info) {
73 * If the 10Mbps preamble workaround is supported and we're
74 * at 10Mbps we may need to do some special checking.
76 if (USE_10MBPS_PREAMBLE_WORKAROUND &&
77 (link_info.s.speed == 10)) {
80 * Read the GMXX_RXX_INT_REG[PCTERR] bit and
81 * see if we are getting preamble errors.
83 int interface = INTERFACE(priv->port);
84 int index = INDEX(priv->port);
85 union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
87 gmxx_rxx_int_reg.u64 =
88 cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
89 (index, interface));
90 if (gmxx_rxx_int_reg.s.pcterr) {
93 * We are getting preamble errors at
94 * 10Mbps. Most likely the PHY is
95 * giving us packets with mis aligned
96 * preambles. In order to get these
97 * packets we need to disable preamble
98 * checking and do it in software.
100 union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
101 union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
103 /* Disable preamble checking */
104 gmxx_rxx_frm_ctl.u64 =
105 cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL
106 (index, interface));
107 gmxx_rxx_frm_ctl.s.pre_chk = 0;
108 cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL
109 (index, interface),
110 gmxx_rxx_frm_ctl.u64);
112 /* Disable FCS stripping */
113 ipd_sub_port_fcs.u64 =
114 cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
115 ipd_sub_port_fcs.s.port_bit &=
116 0xffffffffull ^ (1ull << priv->port);
117 cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS,
118 ipd_sub_port_fcs.u64);
120 /* Clear any error bits */
121 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
122 (index, interface),
123 gmxx_rxx_int_reg.u64);
124 printk_ratelimited("%s: Using 10Mbps with software preamble removal\n",
125 dev->name);
129 if (use_global_register_lock)
130 spin_unlock_irqrestore(&global_register_lock, flags);
131 else
132 mutex_unlock(&priv->phydev->bus->mdio_lock);
133 return;
136 /* If the 10Mbps preamble workaround is allowed we need to on
137 preamble checking, FCS stripping, and clear error bits on
138 every speed change. If errors occur during 10Mbps operation
139 the above code will change this stuff */
140 if (USE_10MBPS_PREAMBLE_WORKAROUND) {
142 union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
143 union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
144 union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
145 int interface = INTERFACE(priv->port);
146 int index = INDEX(priv->port);
148 /* Enable preamble checking */
149 gmxx_rxx_frm_ctl.u64 =
150 cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
151 gmxx_rxx_frm_ctl.s.pre_chk = 1;
152 cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface),
153 gmxx_rxx_frm_ctl.u64);
154 /* Enable FCS stripping */
155 ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
156 ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port;
157 cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
158 /* Clear any error bits */
159 gmxx_rxx_int_reg.u64 =
160 cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
161 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
162 gmxx_rxx_int_reg.u64);
164 if (priv->phydev == NULL) {
165 link_info = cvmx_helper_link_autoconf(priv->port);
166 priv->link_info = link_info.u64;
169 if (use_global_register_lock)
170 spin_unlock_irqrestore(&global_register_lock, flags);
171 else
172 mutex_unlock(&priv->phydev->bus->mdio_lock);
174 if (priv->phydev == NULL) {
175 /* Tell core. */
176 if (link_info.s.link_up) {
177 if (!netif_carrier_ok(dev))
178 netif_carrier_on(dev);
179 } else if (netif_carrier_ok(dev)) {
180 netif_carrier_off(dev);
182 cvm_oct_note_carrier(priv, link_info);
186 static int cmv_oct_rgmii_gmx_interrupt(int interface)
188 int index;
189 int count = 0;
191 /* Loop through every port of this interface */
192 for (index = 0;
193 index < cvmx_helper_ports_on_interface(interface);
194 index++) {
195 union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
197 /* Read the GMX interrupt status bits */
198 gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
199 (index, interface));
200 gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
201 (index, interface));
203 /* Poll the port if inband status changed */
204 if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link ||
205 gmx_rx_int_reg.s.phy_spd) {
206 struct net_device *dev =
207 cvm_oct_device[cvmx_helper_get_ipd_port
208 (interface, index)];
209 struct octeon_ethernet *priv = netdev_priv(dev);
211 if (dev && !atomic_read(&cvm_oct_poll_queue_stopping))
212 queue_work(cvm_oct_poll_queue,
213 &priv->port_work);
215 gmx_rx_int_reg.u64 = 0;
216 gmx_rx_int_reg.s.phy_dupx = 1;
217 gmx_rx_int_reg.s.phy_link = 1;
218 gmx_rx_int_reg.s.phy_spd = 1;
219 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
220 gmx_rx_int_reg.u64);
221 count++;
224 return count;
227 static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
229 union cvmx_npi_rsl_int_blocks rsl_int_blocks;
230 int count = 0;
232 rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
234 /* Check and see if this interrupt was caused by the GMX0 block */
235 if (rsl_int_blocks.s.gmx0)
236 count += cmv_oct_rgmii_gmx_interrupt(0);
238 /* Check and see if this interrupt was caused by the GMX1 block */
239 if (rsl_int_blocks.s.gmx1)
240 count += cmv_oct_rgmii_gmx_interrupt(1);
242 return count ? IRQ_HANDLED : IRQ_NONE;
245 int cvm_oct_rgmii_open(struct net_device *dev)
247 return cvm_oct_common_open(dev, cvm_oct_rgmii_poll, false);
250 static void cvm_oct_rgmii_immediate_poll(struct work_struct *work)
252 struct octeon_ethernet *priv =
253 container_of(work, struct octeon_ethernet, port_work);
254 cvm_oct_rgmii_poll(cvm_oct_device[priv->port]);
257 int cvm_oct_rgmii_init(struct net_device *dev)
259 struct octeon_ethernet *priv = netdev_priv(dev);
260 int r;
262 cvm_oct_common_init(dev);
263 INIT_WORK(&priv->port_work, cvm_oct_rgmii_immediate_poll);
265 * Due to GMX errata in CN3XXX series chips, it is necessary
266 * to take the link down immediately when the PHY changes
267 * state. In order to do this we call the poll function every
268 * time the RGMII inband status changes. This may cause
269 * problems if the PHY doesn't implement inband status
270 * properly.
272 if (number_rgmii_ports == 0) {
273 r = request_irq(OCTEON_IRQ_RML, cvm_oct_rgmii_rml_interrupt,
274 IRQF_SHARED, "RGMII", &number_rgmii_ports);
275 if (r != 0)
276 return r;
278 number_rgmii_ports++;
281 * Only true RGMII ports need to be polled. In GMII mode, port
282 * 0 is really a RGMII port.
284 if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
285 && (priv->port == 0))
286 || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
288 if (!octeon_is_simulation()) {
290 union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
291 int interface = INTERFACE(priv->port);
292 int index = INDEX(priv->port);
295 * Enable interrupts on inband status changes
296 * for this port.
298 gmx_rx_int_en.u64 = 0;
299 gmx_rx_int_en.s.phy_dupx = 1;
300 gmx_rx_int_en.s.phy_link = 1;
301 gmx_rx_int_en.s.phy_spd = 1;
302 cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
303 gmx_rx_int_en.u64);
307 return 0;
310 void cvm_oct_rgmii_uninit(struct net_device *dev)
312 struct octeon_ethernet *priv = netdev_priv(dev);
314 cvm_oct_common_uninit(dev);
317 * Only true RGMII ports need to be polled. In GMII mode, port
318 * 0 is really a RGMII port.
320 if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
321 && (priv->port == 0))
322 || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
324 if (!octeon_is_simulation()) {
326 union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
327 int interface = INTERFACE(priv->port);
328 int index = INDEX(priv->port);
331 * Disable interrupts on inband status changes
332 * for this port.
334 gmx_rx_int_en.u64 =
335 cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
336 (index, interface));
337 gmx_rx_int_en.s.phy_dupx = 0;
338 gmx_rx_int_en.s.phy_link = 0;
339 gmx_rx_int_en.s.phy_spd = 0;
340 cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
341 gmx_rx_int_en.u64);
345 /* Remove the interrupt handler when the last port is removed. */
346 number_rgmii_ports--;
347 if (number_rgmii_ports == 0)
348 free_irq(OCTEON_IRQ_RML, &number_rgmii_ports);
349 cancel_work_sync(&priv->port_work);