1 /*************************************************************************
2 SPDX-License-Identifier: BSD-3-Clause
3
4 Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
5 reserved.
6
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are
10 met:
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
25 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.
26
27 TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
28 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.
29
30 *************************************************************************/
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD: releng/12.0/sys/mips/cavium/octe/ethernet-rgmii.c 326023 2017-11-20 19:43:44Z pfg $");
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
55 extern struct ifnet *cvm_oct_device[];
56
57 static struct mtx global_register_lock;
58 MTX_SYSINIT(global_register_lock, &global_register_lock,
59 "RGMII Global", MTX_SPIN);
60
61 static int number_rgmii_ports;
62
63 static 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
148 static 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
213 int 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
280 void 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
Cache object: 21e82b99282c217f59fe2bddebf7279a
|