1/*************************************************************************
2Copyright (c) 2003-2007  Cavium Networks (support@cavium.com). All rights
3reserved.
4
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are
8met:
9
10    * Redistributions of source code must retain the above copyright
11      notice, this list of conditions and the following disclaimer.
12
13    * Redistributions in binary form must reproduce the above
14      copyright notice, this list of conditions and the following
15      disclaimer in the documentation and/or other materials provided
16      with the distribution.
17
18    * Neither the name of Cavium Networks nor the names of
19      its contributors may be used to endorse or promote products
20      derived from this software without specific prior written
21      permission.
22
23This Software, including technical data, may be subject to U.S. export  control laws, including the U.S. Export Administration Act and its  associated regulations, and may be subject to export or import  regulations in other countries.
24
25TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
26AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
27
28*************************************************************************/
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/bus.h>
36#include <sys/endian.h>
37#include <sys/kernel.h>
38#include <sys/mbuf.h>
39#include <sys/rman.h>
40#include <sys/socket.h>
41#include <sys/lock.h>
42#include <sys/mutex.h>
43
44#include <net/ethernet.h>
45#include <net/if.h>
46
47#include "wrapper-cvmx-includes.h"
48#include "ethernet-headers.h"
49
50#include "octebusvar.h"
51
52extern int octeon_is_simulation(void);
53extern struct ifnet *cvm_oct_device[];
54
55static struct mtx global_register_lock;
56MTX_SYSINIT(global_register_lock, &global_register_lock,
57	    "RGMII Global", MTX_SPIN);
58
59static int number_rgmii_ports;
60
61static void cvm_oct_rgmii_poll(struct ifnet *ifp)
62{
63	cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
64	cvmx_helper_link_info_t link_info;
65
66	/* Take the global register lock since we are going to touch
67	   registers that affect more than one port */
68	mtx_lock_spin(&global_register_lock);
69
70	link_info = cvmx_helper_link_get(priv->port);
71	if (link_info.u64 == priv->link_info) {
72
73		/* If the 10Mbps preamble workaround is supported and we're
74		   at 10Mbps we may need to do some special checking */
75		if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
76
77			/* Read the GMXX_RXX_INT_REG[PCTERR] bit and
78			   see if we are getting preamble errors */
79			int interface = INTERFACE(priv->port);
80			int index = INDEX(priv->port);
81			cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
82			gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
83			if (gmxx_rxx_int_reg.s.pcterr) {
84
85				/* We are getting preamble errors at 10Mbps.
86				   Most likely the PHY is giving us packets
87				   with mis aligned preambles. In order to get
88				   these packets we need to disable preamble
89				   checking and do it in software */
90				cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
91				cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
92
93				/* Disable preamble checking */
94				gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
95				gmxx_rxx_frm_ctl.s.pre_chk = 0;
96				cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
97
98				/* Disable FCS stripping */
99				ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
100				ipd_sub_port_fcs.s.port_bit &= 0xffffffffull ^ (1ull<<priv->port);
101				cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
102
103				/* Clear any error bits */
104				cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
105				DEBUGPRINT("%s: Using 10Mbps with software preamble removal\n", if_name(ifp));
106			}
107		}
108		mtx_unlock_spin(&global_register_lock);
109		return;
110	}
111
112	/* If the 10Mbps preamble workaround is allowed we need to on
113	   preamble checking, FCS stripping, and clear error bits on
114	   every speed change. If errors occur during 10Mbps operation
115	   the above code will change this stuff */
116	if (USE_10MBPS_PREAMBLE_WORKAROUND) {
117
118		cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
119		cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
120		cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
121		int interface = INTERFACE(priv->port);
122		int index = INDEX(priv->port);
123
124		/* Enable preamble checking */
125		gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
126		gmxx_rxx_frm_ctl.s.pre_chk = 1;
127		cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
128		/* Enable FCS stripping */
129		ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
130		ipd_sub_port_fcs.s.port_bit |= 1ull<<priv->port;
131		cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
132		/* Clear any error bits */
133		gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
134		cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
135	}
136
137	if (priv->miibus == NULL) {
138		link_info = cvmx_helper_link_autoconf(priv->port);
139		priv->link_info = link_info.u64;
140		priv->need_link_update = 1;
141	}
142	mtx_unlock_spin(&global_register_lock);
143}
144
145
146static int cvm_oct_rgmii_rml_interrupt(void *dev_id)
147{
148	cvmx_npi_rsl_int_blocks_t rsl_int_blocks;
149	int index;
150	int return_status = FILTER_STRAY;
151
152	rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
153
154	/* Check and see if this interrupt was caused by the GMX0 block */
155	if (rsl_int_blocks.s.gmx0) {
156
157		int interface = 0;
158		/* Loop through every port of this interface */
159		for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
160
161			/* Read the GMX interrupt status bits */
162			cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
163			gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
164			gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
165			/* Poll the port if inband status changed */
166			if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
167
168				struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
169				if (ifp)
170					cvm_oct_rgmii_poll(ifp);
171				gmx_rx_int_reg.u64 = 0;
172				gmx_rx_int_reg.s.phy_dupx = 1;
173				gmx_rx_int_reg.s.phy_link = 1;
174				gmx_rx_int_reg.s.phy_spd = 1;
175				cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
176				return_status = FILTER_HANDLED;
177			}
178		}
179	}
180
181	/* Check and see if this interrupt was caused by the GMX1 block */
182	if (rsl_int_blocks.s.gmx1) {
183
184		int interface = 1;
185		/* Loop through every port of this interface */
186		for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
187
188			/* Read the GMX interrupt status bits */
189			cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
190			gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
191			gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
192			/* Poll the port if inband status changed */
193			if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
194
195				struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
196				if (ifp)
197					cvm_oct_rgmii_poll(ifp);
198				gmx_rx_int_reg.u64 = 0;
199				gmx_rx_int_reg.s.phy_dupx = 1;
200				gmx_rx_int_reg.s.phy_link = 1;
201				gmx_rx_int_reg.s.phy_spd = 1;
202				cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
203				return_status = FILTER_HANDLED;
204			}
205		}
206	}
207	return return_status;
208}
209
210
211int cvm_oct_rgmii_init(struct ifnet *ifp)
212{
213	struct octebus_softc *sc;
214	cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
215	int error;
216	int rid;
217
218	cvm_oct_common_init(ifp);
219	priv->open = cvm_oct_common_open;
220	priv->stop = cvm_oct_common_stop;
221	priv->stop(ifp);
222
223	/* Due to GMX errata in CN3XXX series chips, it is necessary to take the
224	   link down immediately whne the PHY changes state. In order to do this
225	   we call the poll function every time the RGMII inband status changes.
226	   This may cause problems if the PHY doesn't implement inband status
227	   properly */
228	if (number_rgmii_ports == 0) {
229		sc = device_get_softc(device_get_parent(priv->dev));
230
231		rid = 0;
232		sc->sc_rgmii_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ,
233						      &rid, CVMX_IRQ_RML,
234						      CVMX_IRQ_RML, 1,
235						      RF_ACTIVE);
236		if (sc->sc_rgmii_irq == NULL) {
237			device_printf(sc->sc_dev, "could not allocate RGMII irq");
238			return ENXIO;
239		}
240
241		error = bus_setup_intr(sc->sc_dev, sc->sc_rgmii_irq,
242				       INTR_TYPE_NET | INTR_MPSAFE,
243				       cvm_oct_rgmii_rml_interrupt, NULL,
244				       &number_rgmii_ports, NULL);
245		if (error != 0) {
246			device_printf(sc->sc_dev, "could not setup RGMII irq");
247			return error;
248		}
249	}
250	number_rgmii_ports++;
251
252	/* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
253	   a RGMII port */
254	if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
255	    (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
256
257		if (!octeon_is_simulation()) {
258
259			cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
260			int interface = INTERFACE(priv->port);
261			int index = INDEX(priv->port);
262
263			/* Enable interrupts on inband status changes for this port */
264			gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
265			gmx_rx_int_en.s.phy_dupx = 1;
266			gmx_rx_int_en.s.phy_link = 1;
267			gmx_rx_int_en.s.phy_spd = 1;
268			cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
269			priv->poll = cvm_oct_rgmii_poll;
270		}
271	}
272
273	return 0;
274}
275
276void cvm_oct_rgmii_uninit(struct ifnet *ifp)
277{
278	cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
279	cvm_oct_common_uninit(ifp);
280
281	/* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
282	   a RGMII port */
283	if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
284	    (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
285
286		if (!octeon_is_simulation()) {
287
288			cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
289			int interface = INTERFACE(priv->port);
290			int index = INDEX(priv->port);
291
292			/* Disable interrupts on inband status changes for this port */
293			gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
294			gmx_rx_int_en.s.phy_dupx = 0;
295			gmx_rx_int_en.s.phy_link = 0;
296			gmx_rx_int_en.s.phy_spd = 0;
297			cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
298		}
299	}
300
301	/* Remove the interrupt handler when the last port is removed */
302	number_rgmii_ports--;
303	if (number_rgmii_ports == 0)
304		panic("%s: need to implement IRQ release.", __func__);
305}
306
307