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