1 /*************************************************************************
2 SPDX-License-Identifier: BSD-3-Clause
4 Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are
12 * Redistributions of source code must retain the above copyright
13 notice, this list of conditions and the following disclaimer.
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.
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
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.
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.
30 *************************************************************************/
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
36 #include <sys/systm.h>
38 #include <sys/endian.h>
39 #include <sys/kernel.h>
42 #include <sys/socket.h>
44 #include <sys/mutex.h>
46 #include <net/ethernet.h>
48 #include <net/if_var.h>
50 #include "wrapper-cvmx-includes.h"
51 #include "ethernet-headers.h"
53 #include "octebusvar.h"
55 extern struct ifnet *cvm_oct_device[];
57 static struct mtx global_register_lock;
58 MTX_SYSINIT(global_register_lock, &global_register_lock,
59 "RGMII Global", MTX_SPIN);
61 static int number_rgmii_ports;
63 static void cvm_oct_rgmii_poll(struct ifnet *ifp)
65 cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
66 cvmx_helper_link_info_t link_info;
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);
72 link_info = cvmx_helper_link_get(priv->port);
73 if (link_info.u64 == priv->link_info) {
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)) {
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) {
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;
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);
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);
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));
110 mtx_unlock_spin(&global_register_lock);
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) {
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);
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);
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;
144 mtx_unlock_spin(&global_register_lock);
148 static int cvm_oct_rgmii_rml_interrupt(void *dev_id)
150 cvmx_npi_rsl_int_blocks_t rsl_int_blocks;
152 int return_status = FILTER_STRAY;
154 rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
156 /* Check and see if this interrupt was caused by the GMX0 block */
157 if (rsl_int_blocks.s.gmx0) {
160 /* Loop through every port of this interface */
161 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
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) {
170 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
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;
183 /* Check and see if this interrupt was caused by the GMX1 block */
184 if (rsl_int_blocks.s.gmx1) {
187 /* Loop through every port of this interface */
188 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
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) {
197 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
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;
209 return return_status;
213 int cvm_oct_rgmii_init(struct ifnet *ifp)
215 struct octebus_softc *sc;
216 cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
220 if (cvm_oct_common_init(ifp) != 0)
223 priv->open = cvm_oct_common_open;
224 priv->stop = cvm_oct_common_stop;
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
232 if (number_rgmii_ports == 0) {
233 sc = device_get_softc(device_get_parent(priv->dev));
236 sc->sc_rgmii_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ,
237 &rid, OCTEON_IRQ_RML,
240 if (sc->sc_rgmii_irq == NULL) {
241 device_printf(sc->sc_dev, "could not allocate RGMII irq");
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);
250 device_printf(sc->sc_dev, "could not setup RGMII irq");
254 number_rgmii_ports++;
256 /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
258 if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
259 (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
261 if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) {
263 cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
264 int interface = INTERFACE(priv->port);
265 int index = INDEX(priv->port);
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;
280 void cvm_oct_rgmii_uninit(struct ifnet *ifp)
282 cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
283 cvm_oct_common_uninit(ifp);
285 /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
287 if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
288 (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
290 if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) {
292 cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
293 int interface = INTERFACE(priv->port);
294 int index = INDEX(priv->port);
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);
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__);