1/************************************************************************* 2SPDX-License-Identifier: BSD-3-Clause 3 4Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights 5reserved. 6 7 8Redistribution and use in source and binary forms, with or without 9modification, are permitted provided that the following conditions are 10met: 11 12 * Redistributions of source code must retain the above copyright 13 notice, this list of conditions and the following disclaimer. 14 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. 19 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 23 permission. 24 25This 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. 26 27TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 28AND 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. 29 30*************************************************************************/ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD$"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/bus.h> 38#include <sys/endian.h> 39#include <sys/kernel.h> 40#include <sys/mbuf.h> 41#include <sys/rman.h> 42#include <sys/socket.h> 43#include <sys/lock.h> 44#include <sys/mutex.h> 45 46#include <net/ethernet.h> 47#include <net/if.h> 48#include <net/if_var.h> 49 50#include "wrapper-cvmx-includes.h" 51#include "ethernet-headers.h" 52 53#include "octebusvar.h" 54 55extern struct ifnet *cvm_oct_device[]; 56 57static struct mtx global_register_lock; 58MTX_SYSINIT(global_register_lock, &global_register_lock, 59 "RGMII Global", MTX_SPIN); 60 61static int number_rgmii_ports; 62 63static void cvm_oct_rgmii_poll(struct ifnet *ifp) 64{ 65 cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; 66 cvmx_helper_link_info_t link_info; 67 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); 71 72 link_info = cvmx_helper_link_get(priv->port); 73 if (link_info.u64 == priv->link_info) { 74 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)) { 78 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) { 86 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; 94 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); 99 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); 104 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)); 108 } 109 } 110 mtx_unlock_spin(&global_register_lock); 111 return; 112 } 113 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) { 119 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); 125 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); 137 } 138 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; 143 } 144 mtx_unlock_spin(&global_register_lock); 145} 146 147 148static int cvm_oct_rgmii_rml_interrupt(void *dev_id) 149{ 150 cvmx_npi_rsl_int_blocks_t rsl_int_blocks; 151 int index; 152 int return_status = FILTER_STRAY; 153 154 rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS); 155 156 /* Check and see if this interrupt was caused by the GMX0 block */ 157 if (rsl_int_blocks.s.gmx0) { 158 159 int interface = 0; 160 /* Loop through every port of this interface */ 161 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) { 162 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) { 169 170 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)]; 171 if (ifp) 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; 179 } 180 } 181 } 182 183 /* Check and see if this interrupt was caused by the GMX1 block */ 184 if (rsl_int_blocks.s.gmx1) { 185 186 int interface = 1; 187 /* Loop through every port of this interface */ 188 for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) { 189 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) { 196 197 struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)]; 198 if (ifp) 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; 206 } 207 } 208 } 209 return return_status; 210} 211 212 213int cvm_oct_rgmii_init(struct ifnet *ifp) 214{ 215 struct octebus_softc *sc; 216 cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; 217 int error; 218 int rid; 219 220 if (cvm_oct_common_init(ifp) != 0) 221 return ENXIO; 222 223 priv->open = cvm_oct_common_open; 224 priv->stop = cvm_oct_common_stop; 225 priv->stop(ifp); 226 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 231 properly */ 232 if (number_rgmii_ports == 0) { 233 sc = device_get_softc(device_get_parent(priv->dev)); 234 235 rid = 0; 236 sc->sc_rgmii_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ, 237 &rid, OCTEON_IRQ_RML, 238 OCTEON_IRQ_RML, 1, 239 RF_ACTIVE); 240 if (sc->sc_rgmii_irq == NULL) { 241 device_printf(sc->sc_dev, "could not allocate RGMII irq"); 242 return ENXIO; 243 } 244 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); 249 if (error != 0) { 250 device_printf(sc->sc_dev, "could not setup RGMII irq"); 251 return error; 252 } 253 } 254 number_rgmii_ports++; 255 256 /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really 257 a RGMII port */ 258 if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) || 259 (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) { 260 261 if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) { 262 263 cvmx_gmxx_rxx_int_en_t gmx_rx_int_en; 264 int interface = INTERFACE(priv->port); 265 int index = INDEX(priv->port); 266 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; 274 } 275 } 276 277 return 0; 278} 279 280void cvm_oct_rgmii_uninit(struct ifnet *ifp) 281{ 282 cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; 283 cvm_oct_common_uninit(ifp); 284 285 /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really 286 a RGMII port */ 287 if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) || 288 (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) { 289 290 if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) { 291 292 cvmx_gmxx_rxx_int_en_t gmx_rx_int_en; 293 int interface = INTERFACE(priv->port); 294 int index = INDEX(priv->port); 295 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); 302 } 303 } 304 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__); 309} 310 311