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