1 /*************************************************************************
2 SPDX-License-Identifier: BSD-3-Clause
3 
4 Copyright (c) 2003-2007  Cavium Networks ([email protected]). All rights
5 reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are
9 met:
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 
24 This 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 
26 TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
27 AND 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 
54 extern struct ifnet *cvm_oct_device[];
55 
56 static struct mtx global_register_lock;
57 MTX_SYSINIT(global_register_lock, &global_register_lock,
58 	    "RGMII Global", MTX_SPIN);
59 
60 static int number_rgmii_ports;
61 
cvm_oct_rgmii_poll(struct ifnet * ifp)62 static 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 
cvm_oct_rgmii_rml_interrupt(void * dev_id)142 static 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 
cvm_oct_rgmii_init(struct ifnet * ifp)200 int 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 
cvm_oct_rgmii_uninit(struct ifnet * ifp)265 void 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