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$");
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 if (priv->miibus == NULL) {
138 link_info = cvmx_helper_link_autoconf(priv->port);
139 priv->link_info = link_info.u64;
140 priv->need_link_update = 1;
141 }
142 mtx_unlock_spin(&global_register_lock);
143 }
144
145
146 static int cvm_oct_rgmii_rml_interrupt(void *dev_id)
147 {
148 cvmx_npi_rsl_int_blocks_t rsl_int_blocks;
149 int index;
150 int return_status = FILTER_STRAY;
151
152 rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
153
154 /* Check and see if this interrupt was caused by the GMX0 block */
155 if (rsl_int_blocks.s.gmx0) {
156
157 int interface = 0;
158 /* Loop through every port of this interface */
159 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
160
161 /* Read the GMX interrupt status bits */
162 cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
163 gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
164 gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
165 /* Poll the port if inband status changed */
166 if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
167
168 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
169 if (ifp)
170 cvm_oct_rgmii_poll(ifp);
171 gmx_rx_int_reg.u64 = 0;
172 gmx_rx_int_reg.s.phy_dupx = 1;
173 gmx_rx_int_reg.s.phy_link = 1;
174 gmx_rx_int_reg.s.phy_spd = 1;
175 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
176 return_status = FILTER_HANDLED;
177 }
178 }
179 }
180
181 /* Check and see if this interrupt was caused by the GMX1 block */
182 if (rsl_int_blocks.s.gmx1) {
183
184 int interface = 1;
185 /* Loop through every port of this interface */
186 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
187
188 /* Read the GMX interrupt status bits */
189 cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
190 gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
191 gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
192 /* Poll the port if inband status changed */
193 if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
194
195 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
196 if (ifp)
197 cvm_oct_rgmii_poll(ifp);
198 gmx_rx_int_reg.u64 = 0;
199 gmx_rx_int_reg.s.phy_dupx = 1;
200 gmx_rx_int_reg.s.phy_link = 1;
201 gmx_rx_int_reg.s.phy_spd = 1;
202 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
203 return_status = FILTER_HANDLED;
204 }
205 }
206 }
207 return return_status;
208 }
209
210
211 int cvm_oct_rgmii_init(struct ifnet *ifp)
212 {
213 struct octebus_softc *sc;
214 cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
215 int error;
216 int rid;
217
218 cvm_oct_common_init(ifp);
219 priv->open = cvm_oct_common_open;
220 priv->stop = cvm_oct_common_stop;
221 priv->stop(ifp);
222
223 /* Due to GMX errata in CN3XXX series chips, it is necessary to take the
224 link down immediately whne the PHY changes state. In order to do this
225 we call the poll function every time the RGMII inband status changes.
226 This may cause problems if the PHY doesn't implement inband status
227 properly */
228 if (number_rgmii_ports == 0) {
229 sc = device_get_softc(device_get_parent(priv->dev));
230
231 rid = 0;
232 sc->sc_rgmii_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ,
233 &rid, CVMX_IRQ_RML,
234 CVMX_IRQ_RML, 1,
235 RF_ACTIVE);
236 if (sc->sc_rgmii_irq == NULL) {
237 device_printf(sc->sc_dev, "could not allocate RGMII irq");
238 return ENXIO;
239 }
240
241 error = bus_setup_intr(sc->sc_dev, sc->sc_rgmii_irq,
242 INTR_TYPE_NET | INTR_MPSAFE,
243 cvm_oct_rgmii_rml_interrupt, NULL,
244 &number_rgmii_ports, NULL);
245 if (error != 0) {
246 device_printf(sc->sc_dev, "could not setup RGMII irq");
247 return error;
248 }
249 }
250 number_rgmii_ports++;
251
252 /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
253 a RGMII port */
254 if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
255 (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
256
257 if (!octeon_is_simulation()) {
258
259 cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
260 int interface = INTERFACE(priv->port);
261 int index = INDEX(priv->port);
262
263 /* Enable interrupts on inband status changes for this port */
264 gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
265 gmx_rx_int_en.s.phy_dupx = 1;
266 gmx_rx_int_en.s.phy_link = 1;
267 gmx_rx_int_en.s.phy_spd = 1;
268 cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
269 priv->poll = cvm_oct_rgmii_poll;
270 }
271 }
272
273 return 0;
274 }
275
276 void cvm_oct_rgmii_uninit(struct ifnet *ifp)
277 {
278 cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
279 cvm_oct_common_uninit(ifp);
280
281 /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
282 a RGMII port */
283 if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
284 (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
285
286 if (!octeon_is_simulation()) {
287
288 cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
289 int interface = INTERFACE(priv->port);
290 int index = INDEX(priv->port);
291
292 /* Disable interrupts on inband status changes for this port */
293 gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
294 gmx_rx_int_en.s.phy_dupx = 0;
295 gmx_rx_int_en.s.phy_link = 0;
296 gmx_rx_int_en.s.phy_spd = 0;
297 cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
298 }
299 }
300
301 /* Remove the interrupt handler when the last port is removed */
302 number_rgmii_ports--;
303 if (number_rgmii_ports == 0)
304 panic("%s: need to implement IRQ release.", __func__);
305 }
306
Cache object: 3134e21b2879b5a9c8b016fcd1ea1e1f
|