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 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
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
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
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
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 }
Cache object: 35283e3e82a9092764f177e0bf18823b
|