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