• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/staging/octeon/
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/phy.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 = 0;
52	cvmx_helper_link_info_t link_info;
53	int use_global_register_lock = (priv->phydev == NULL);
54
55	BUG_ON(in_interrupt());
56	if (use_global_register_lock) {
57		/*
58		 * Take the global register lock since we are going to
59		 * touch registers that affect more than one port.
60		 */
61		spin_lock_irqsave(&global_register_lock, flags);
62	} else {
63		mutex_lock(&priv->phydev->bus->mdio_lock);
64	}
65
66	link_info = cvmx_helper_link_get(priv->port);
67	if (link_info.u64 == priv->link_info) {
68
69		if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
70
71			/*
72			 * Read the GMXX_RXX_INT_REG[PCTERR] bit and
73			 * see if we are getting preamble errors.
74			 */
75			int interface = INTERFACE(priv->port);
76			int index = INDEX(priv->port);
77			union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
78			gmxx_rxx_int_reg.u64 =
79			    cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
80					  (index, interface));
81			if (gmxx_rxx_int_reg.s.pcterr) {
82
83				/*
84				 * We are getting preamble errors at
85				 * 10Mbps.  Most likely the PHY is
86				 * giving us packets with mis aligned
87				 * preambles. In order to get these
88				 * packets we need to disable preamble
89				 * checking and do it in software.
90				 */
91				union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
92				union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
93
94				/* Disable preamble checking */
95				gmxx_rxx_frm_ctl.u64 =
96				    cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL
97						  (index, interface));
98				gmxx_rxx_frm_ctl.s.pre_chk = 0;
99				cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL
100					       (index, interface),
101					       gmxx_rxx_frm_ctl.u64);
102
103				/* Disable FCS stripping */
104				ipd_sub_port_fcs.u64 =
105				    cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
106				ipd_sub_port_fcs.s.port_bit &=
107				    0xffffffffull ^ (1ull << priv->port);
108				cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS,
109					       ipd_sub_port_fcs.u64);
110
111				/* Clear any error bits */
112				cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
113					       (index, interface),
114					       gmxx_rxx_int_reg.u64);
115				DEBUGPRINT("%s: Using 10Mbps with software "
116					   "preamble removal\n",
117				     dev->name);
118			}
119		}
120
121		if (use_global_register_lock)
122			spin_unlock_irqrestore(&global_register_lock, flags);
123		else
124			mutex_unlock(&priv->phydev->bus->mdio_lock);
125		return;
126	}
127
128	if (USE_10MBPS_PREAMBLE_WORKAROUND) {
129
130		union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
131		union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
132		union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
133		int interface = INTERFACE(priv->port);
134		int index = INDEX(priv->port);
135
136		/* Enable preamble checking */
137		gmxx_rxx_frm_ctl.u64 =
138		    cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
139		gmxx_rxx_frm_ctl.s.pre_chk = 1;
140		cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface),
141			       gmxx_rxx_frm_ctl.u64);
142		/* Enable FCS stripping */
143		ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
144		ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port;
145		cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
146		/* Clear any error bits */
147		gmxx_rxx_int_reg.u64 =
148		    cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
149		cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
150			       gmxx_rxx_int_reg.u64);
151	}
152	if (priv->phydev == NULL) {
153		link_info = cvmx_helper_link_autoconf(priv->port);
154		priv->link_info = link_info.u64;
155	}
156
157	if (use_global_register_lock)
158		spin_unlock_irqrestore(&global_register_lock, flags);
159	else {
160		mutex_unlock(&priv->phydev->bus->mdio_lock);
161	}
162
163	if (priv->phydev == NULL) {
164		/* Tell core. */
165		if (link_info.s.link_up) {
166			if (!netif_carrier_ok(dev))
167				netif_carrier_on(dev);
168			if (priv->queue != -1)
169				DEBUGPRINT("%s: %u Mbps %s duplex, "
170					   "port %2d, queue %2d\n",
171					   dev->name, link_info.s.speed,
172					   (link_info.s.full_duplex) ?
173						"Full" : "Half",
174					   priv->port, priv->queue);
175			else
176				DEBUGPRINT("%s: %u Mbps %s duplex, "
177					   "port %2d, POW\n",
178					   dev->name, link_info.s.speed,
179					   (link_info.s.full_duplex) ?
180						"Full" : "Half",
181					   priv->port);
182		} else {
183			if (netif_carrier_ok(dev))
184				netif_carrier_off(dev);
185			DEBUGPRINT("%s: Link down\n", dev->name);
186		}
187	}
188}
189
190static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
191{
192	union cvmx_npi_rsl_int_blocks rsl_int_blocks;
193	int index;
194	irqreturn_t return_status = IRQ_NONE;
195
196	rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
197
198	/* Check and see if this interrupt was caused by the GMX0 block */
199	if (rsl_int_blocks.s.gmx0) {
200
201		int interface = 0;
202		/* Loop through every port of this interface */
203		for (index = 0;
204		     index < cvmx_helper_ports_on_interface(interface);
205		     index++) {
206
207			/* Read the GMX interrupt status bits */
208			union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
209			gmx_rx_int_reg.u64 =
210			    cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
211					  (index, interface));
212			gmx_rx_int_reg.u64 &=
213			    cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
214					  (index, interface));
215			/* Poll the port if inband status changed */
216			if (gmx_rx_int_reg.s.phy_dupx
217			    || gmx_rx_int_reg.s.phy_link
218			    || gmx_rx_int_reg.s.phy_spd) {
219
220				struct net_device *dev =
221				    cvm_oct_device[cvmx_helper_get_ipd_port
222						   (interface, index)];
223				struct octeon_ethernet *priv = netdev_priv(dev);
224
225				if (dev && !atomic_read(&cvm_oct_poll_queue_stopping))
226					queue_work(cvm_oct_poll_queue, &priv->port_work);
227
228				gmx_rx_int_reg.u64 = 0;
229				gmx_rx_int_reg.s.phy_dupx = 1;
230				gmx_rx_int_reg.s.phy_link = 1;
231				gmx_rx_int_reg.s.phy_spd = 1;
232				cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
233					       (index, interface),
234					       gmx_rx_int_reg.u64);
235				return_status = IRQ_HANDLED;
236			}
237		}
238	}
239
240	/* Check and see if this interrupt was caused by the GMX1 block */
241	if (rsl_int_blocks.s.gmx1) {
242
243		int interface = 1;
244		/* Loop through every port of this interface */
245		for (index = 0;
246		     index < cvmx_helper_ports_on_interface(interface);
247		     index++) {
248
249			/* Read the GMX interrupt status bits */
250			union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
251			gmx_rx_int_reg.u64 =
252			    cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
253					  (index, interface));
254			gmx_rx_int_reg.u64 &=
255			    cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
256					  (index, interface));
257			/* Poll the port if inband status changed */
258			if (gmx_rx_int_reg.s.phy_dupx
259			    || gmx_rx_int_reg.s.phy_link
260			    || gmx_rx_int_reg.s.phy_spd) {
261
262				struct net_device *dev =
263				    cvm_oct_device[cvmx_helper_get_ipd_port
264						   (interface, index)];
265				struct octeon_ethernet *priv = netdev_priv(dev);
266
267				if (dev && !atomic_read(&cvm_oct_poll_queue_stopping))
268					queue_work(cvm_oct_poll_queue, &priv->port_work);
269
270				gmx_rx_int_reg.u64 = 0;
271				gmx_rx_int_reg.s.phy_dupx = 1;
272				gmx_rx_int_reg.s.phy_link = 1;
273				gmx_rx_int_reg.s.phy_spd = 1;
274				cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
275					       (index, interface),
276					       gmx_rx_int_reg.u64);
277				return_status = IRQ_HANDLED;
278			}
279		}
280	}
281	return return_status;
282}
283
284int cvm_oct_rgmii_open(struct net_device *dev)
285{
286	union cvmx_gmxx_prtx_cfg gmx_cfg;
287	struct octeon_ethernet *priv = netdev_priv(dev);
288	int interface = INTERFACE(priv->port);
289	int index = INDEX(priv->port);
290	cvmx_helper_link_info_t link_info;
291
292	gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
293	gmx_cfg.s.en = 1;
294	cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
295
296	if (!octeon_is_simulation()) {
297		link_info = cvmx_helper_link_get(priv->port);
298		if (!link_info.s.link_up)
299			netif_carrier_off(dev);
300	}
301
302	return 0;
303}
304
305int cvm_oct_rgmii_stop(struct net_device *dev)
306{
307	union cvmx_gmxx_prtx_cfg gmx_cfg;
308	struct octeon_ethernet *priv = netdev_priv(dev);
309	int interface = INTERFACE(priv->port);
310	int index = INDEX(priv->port);
311
312	gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
313	gmx_cfg.s.en = 0;
314	cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
315	return 0;
316}
317
318static void cvm_oct_rgmii_immediate_poll(struct work_struct *work)
319{
320	struct octeon_ethernet *priv = container_of(work, struct octeon_ethernet, port_work);
321	cvm_oct_rgmii_poll(cvm_oct_device[priv->port]);
322}
323
324int cvm_oct_rgmii_init(struct net_device *dev)
325{
326	struct octeon_ethernet *priv = netdev_priv(dev);
327	int r;
328
329	cvm_oct_common_init(dev);
330	dev->netdev_ops->ndo_stop(dev);
331	INIT_WORK(&priv->port_work, cvm_oct_rgmii_immediate_poll);
332	/*
333	 * Due to GMX errata in CN3XXX series chips, it is necessary
334	 * to take the link down immediately when the PHY changes
335	 * state. In order to do this we call the poll function every
336	 * time the RGMII inband status changes.  This may cause
337	 * problems if the PHY doesn't implement inband status
338	 * properly.
339	 */
340	if (number_rgmii_ports == 0) {
341		r = request_irq(OCTEON_IRQ_RML, cvm_oct_rgmii_rml_interrupt,
342				IRQF_SHARED, "RGMII", &number_rgmii_ports);
343		if (r != 0)
344			return r;
345	}
346	number_rgmii_ports++;
347
348	/*
349	 * Only true RGMII ports need to be polled. In GMII mode, port
350	 * 0 is really a RGMII port.
351	 */
352	if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
353	     && (priv->port == 0))
354	    || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
355
356		if (!octeon_is_simulation()) {
357
358			union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
359			int interface = INTERFACE(priv->port);
360			int index = INDEX(priv->port);
361
362			/*
363			 * Enable interrupts on inband status changes
364			 * for this port.
365			 */
366			gmx_rx_int_en.u64 =
367			    cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
368					  (index, interface));
369			gmx_rx_int_en.s.phy_dupx = 1;
370			gmx_rx_int_en.s.phy_link = 1;
371			gmx_rx_int_en.s.phy_spd = 1;
372			cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
373				       gmx_rx_int_en.u64);
374			priv->poll = cvm_oct_rgmii_poll;
375		}
376	}
377
378	return 0;
379}
380
381void cvm_oct_rgmii_uninit(struct net_device *dev)
382{
383	struct octeon_ethernet *priv = netdev_priv(dev);
384	cvm_oct_common_uninit(dev);
385
386	/*
387	 * Only true RGMII ports need to be polled. In GMII mode, port
388	 * 0 is really a RGMII port.
389	 */
390	if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
391	     && (priv->port == 0))
392	    || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
393
394		if (!octeon_is_simulation()) {
395
396			union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
397			int interface = INTERFACE(priv->port);
398			int index = INDEX(priv->port);
399
400			/*
401			 * Disable interrupts on inband status changes
402			 * for this port.
403			 */
404			gmx_rx_int_en.u64 =
405			    cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
406					  (index, interface));
407			gmx_rx_int_en.s.phy_dupx = 0;
408			gmx_rx_int_en.s.phy_link = 0;
409			gmx_rx_int_en.s.phy_spd = 0;
410			cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
411				       gmx_rx_int_en.u64);
412		}
413	}
414
415	/* Remove the interrupt handler when the last port is removed. */
416	number_rgmii_ports--;
417	if (number_rgmii_ports == 0)
418		free_irq(OCTEON_IRQ_RML, &number_rgmii_ports);
419	cancel_work_sync(&priv->port_work);
420}
421