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