]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/mips/cavium/octe/ethernet-rgmii.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / mips / cavium / octe / ethernet-rgmii.c
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 struct ifnet *cvm_oct_device[];
53
54 static struct mtx global_register_lock;
55 MTX_SYSINIT(global_register_lock, &global_register_lock,
56             "RGMII Global", MTX_SPIN);
57
58 static int number_rgmii_ports;
59
60 static void cvm_oct_rgmii_poll(struct ifnet *ifp)
61 {
62         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
63         cvmx_helper_link_info_t link_info;
64
65         /* Take the global register lock since we are going to touch
66            registers that affect more than one port */
67         mtx_lock_spin(&global_register_lock);
68
69         link_info = cvmx_helper_link_get(priv->port);
70         if (link_info.u64 == priv->link_info) {
71
72                 /* If the 10Mbps preamble workaround is supported and we're
73                    at 10Mbps we may need to do some special checking */
74                 if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
75
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
84                                 /* We are getting preamble errors at 10Mbps.
85                                    Most likely the PHY is giving us packets
86                                    with mis aligned preambles. In order to get
87                                    these packets we need to disable preamble
88                                    checking and do it in software */
89                                 cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
90                                 cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
91
92                                 /* Disable preamble checking */
93                                 gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
94                                 gmxx_rxx_frm_ctl.s.pre_chk = 0;
95                                 cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
96
97                                 /* Disable FCS stripping */
98                                 ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
99                                 ipd_sub_port_fcs.s.port_bit &= 0xffffffffull ^ (1ull<<priv->port);
100                                 cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
101
102                                 /* Clear any error bits */
103                                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
104                                 DEBUGPRINT("%s: Using 10Mbps with software preamble removal\n", if_name(ifp));
105                         }
106                 }
107                 mtx_unlock_spin(&global_register_lock);
108                 return;
109         }
110
111         /* If the 10Mbps preamble workaround is allowed we need to on
112            preamble checking, FCS stripping, and clear error bits on
113            every speed change. If errors occur during 10Mbps operation
114            the above code will change this stuff */
115         if (USE_10MBPS_PREAMBLE_WORKAROUND) {
116
117                 cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
118                 cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
119                 cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
120                 int interface = INTERFACE(priv->port);
121                 int index = INDEX(priv->port);
122
123                 /* Enable preamble checking */
124                 gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
125                 gmxx_rxx_frm_ctl.s.pre_chk = 1;
126                 cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
127                 /* Enable FCS stripping */
128                 ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
129                 ipd_sub_port_fcs.s.port_bit |= 1ull<<priv->port;
130                 cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
131                 /* Clear any error bits */
132                 gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
133                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
134         }
135
136         if (priv->miibus == NULL) {
137                 link_info = cvmx_helper_link_autoconf(priv->port);
138                 priv->link_info = link_info.u64;
139                 priv->need_link_update = 1;
140         }
141         mtx_unlock_spin(&global_register_lock);
142 }
143
144
145 static int cvm_oct_rgmii_rml_interrupt(void *dev_id)
146 {
147         cvmx_npi_rsl_int_blocks_t rsl_int_blocks;
148         int index;
149         int return_status = FILTER_STRAY;
150
151         rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
152
153         /* Check and see if this interrupt was caused by the GMX0 block */
154         if (rsl_int_blocks.s.gmx0) {
155
156                 int interface = 0;
157                 /* Loop through every port of this interface */
158                 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
159
160                         /* Read the GMX interrupt status bits */
161                         cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
162                         gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
163                         gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
164                         /* Poll the port if inband status changed */
165                         if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
166
167                                 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
168                                 if (ifp)
169                                         cvm_oct_rgmii_poll(ifp);
170                                 gmx_rx_int_reg.u64 = 0;
171                                 gmx_rx_int_reg.s.phy_dupx = 1;
172                                 gmx_rx_int_reg.s.phy_link = 1;
173                                 gmx_rx_int_reg.s.phy_spd = 1;
174                                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
175                                 return_status = FILTER_HANDLED;
176                         }
177                 }
178         }
179
180         /* Check and see if this interrupt was caused by the GMX1 block */
181         if (rsl_int_blocks.s.gmx1) {
182
183                 int interface = 1;
184                 /* Loop through every port of this interface */
185                 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
186
187                         /* Read the GMX interrupt status bits */
188                         cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
189                         gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
190                         gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
191                         /* Poll the port if inband status changed */
192                         if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
193
194                                 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
195                                 if (ifp)
196                                         cvm_oct_rgmii_poll(ifp);
197                                 gmx_rx_int_reg.u64 = 0;
198                                 gmx_rx_int_reg.s.phy_dupx = 1;
199                                 gmx_rx_int_reg.s.phy_link = 1;
200                                 gmx_rx_int_reg.s.phy_spd = 1;
201                                 cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
202                                 return_status = FILTER_HANDLED;
203                         }
204                 }
205         }
206         return return_status;
207 }
208
209
210 int cvm_oct_rgmii_init(struct ifnet *ifp)
211 {
212         struct octebus_softc *sc;
213         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
214         int error;
215         int rid;
216
217         if (cvm_oct_common_init(ifp) != 0)
218             return ENXIO;
219
220         priv->open = cvm_oct_common_open;
221         priv->stop = cvm_oct_common_stop;
222         priv->stop(ifp);
223
224         /* Due to GMX errata in CN3XXX series chips, it is necessary to take the
225            link down immediately whne the PHY changes state. In order to do this
226            we call the poll function every time the RGMII inband status changes.
227            This may cause problems if the PHY doesn't implement inband status
228            properly */
229         if (number_rgmii_ports == 0) {
230                 sc = device_get_softc(device_get_parent(priv->dev));
231
232                 rid = 0;
233                 sc->sc_rgmii_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ,
234                                                       &rid, OCTEON_IRQ_RML,
235                                                       OCTEON_IRQ_RML, 1,
236                                                       RF_ACTIVE);
237                 if (sc->sc_rgmii_irq == NULL) {
238                         device_printf(sc->sc_dev, "could not allocate RGMII irq");
239                         return ENXIO;
240                 }
241
242                 error = bus_setup_intr(sc->sc_dev, sc->sc_rgmii_irq,
243                                        INTR_TYPE_NET | INTR_MPSAFE,
244                                        cvm_oct_rgmii_rml_interrupt, NULL,
245                                        &number_rgmii_ports, NULL);
246                 if (error != 0) {
247                         device_printf(sc->sc_dev, "could not setup RGMII irq");
248                         return error;
249                 }
250         }
251         number_rgmii_ports++;
252
253         /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
254            a RGMII port */
255         if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
256             (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
257
258                 if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) {
259
260                         cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
261                         int interface = INTERFACE(priv->port);
262                         int index = INDEX(priv->port);
263
264                         /* Enable interrupts on inband status changes for this port */
265                         gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
266                         gmx_rx_int_en.s.phy_dupx = 1;
267                         gmx_rx_int_en.s.phy_link = 1;
268                         gmx_rx_int_en.s.phy_spd = 1;
269                         cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
270                         priv->poll = cvm_oct_rgmii_poll;
271                 }
272         }
273
274         return 0;
275 }
276
277 void cvm_oct_rgmii_uninit(struct ifnet *ifp)
278 {
279         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
280         cvm_oct_common_uninit(ifp);
281
282         /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
283            a RGMII port */
284         if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
285             (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
286
287                 if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) {
288
289                         cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
290                         int interface = INTERFACE(priv->port);
291                         int index = INDEX(priv->port);
292
293                         /* Disable interrupts on inband status changes for this port */
294                         gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
295                         gmx_rx_int_en.s.phy_dupx = 0;
296                         gmx_rx_int_en.s.phy_link = 0;
297                         gmx_rx_int_en.s.phy_spd = 0;
298                         cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
299                 }
300         }
301
302         /* Remove the interrupt handler when the last port is removed */
303         number_rgmii_ports--;
304         if (number_rgmii_ports == 0)
305                 panic("%s: need to implement IRQ release.", __func__);
306 }
307