Index: head/sys/mips/cavium/octe/ethernet-common.c =================================================================== --- head/sys/mips/cavium/octe/ethernet-common.c (revision 216070) +++ head/sys/mips/cavium/octe/ethernet-common.c (revision 216071) @@ -1,287 +1,314 @@ /************************************************************************* Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Cavium Networks nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 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. *************************************************************************/ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "wrapper-cvmx-includes.h" #include "ethernet-headers.h" extern int octeon_is_simulation(void); extern cvmx_bootinfo_t *octeon_bootinfo; /** * Set the multicast list. Currently unimplemented. * * @param dev Device to work on */ void cvm_oct_common_set_multicast_list(struct ifnet *ifp) { cvmx_gmxx_prtx_cfg_t gmx_cfg; cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; int interface = INTERFACE(priv->port); int index = INDEX(priv->port); if ((interface < 2) && (cvmx_helper_interface_get_mode(interface) != CVMX_HELPER_INTERFACE_MODE_SPI)) { cvmx_gmxx_rxx_adr_ctl_t control; control.u64 = 0; control.s.bcst = 1; /* Allow broadcast MAC addresses */ if (/*ifp->mc_list || */(ifp->if_flags&IFF_ALLMULTI) || (ifp->if_flags & IFF_PROMISC)) control.s.mcst = 2; /* Force accept multicast packets */ else control.s.mcst = 1; /* Force reject multicat packets */ if (ifp->if_flags & IFF_PROMISC) control.s.cam_mode = 0; /* Reject matches if promisc. Since CAM is shut off, should accept everything */ else control.s.cam_mode = 1; /* Filter packets based on the CAM */ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64 & ~1ull); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CTL(index, interface), control.u64); if (ifp->if_flags&IFF_PROMISC) cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN(index, interface), 0); else cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN(index, interface), 1); cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); } } /** * Set the hardware MAC address for a device * * @param dev Device to change the MAC address for * @param addr Address structure to change it too. */ void cvm_oct_common_set_mac_address(struct ifnet *ifp, const void *addr) { cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; cvmx_gmxx_prtx_cfg_t gmx_cfg; int interface = INTERFACE(priv->port); int index = INDEX(priv->port); memcpy(priv->mac, addr, 6); if ((interface < 2) && (cvmx_helper_interface_get_mode(interface) != CVMX_HELPER_INTERFACE_MODE_SPI)) { int i; const uint8_t *ptr = addr; uint64_t mac = 0; for (i = 0; i < 6; i++) mac = (mac<<8) | (uint64_t)(ptr[i]); gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64 & ~1ull); cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface), ptr[0]); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface), ptr[1]); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface), ptr[2]); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface), ptr[3]); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface), ptr[4]); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface), ptr[5]); cvm_oct_common_set_multicast_list(ifp); cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); } } /** * Change the link MTU. Unimplemented * * @param dev Device to change * @param new_mtu The new MTU * @return Zero on success */ int cvm_oct_common_change_mtu(struct ifnet *ifp, int new_mtu) { cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; int interface = INTERFACE(priv->port); int index = INDEX(priv->port); int vlan_bytes = 4; /* Limit the MTU to make sure the ethernet packets are between 64 bytes and 65535 bytes */ if ((new_mtu + 14 + 4 + vlan_bytes < 64) || (new_mtu + 14 + 4 + vlan_bytes > 65392)) { printf("MTU must be between %d and %d.\n", 64-14-4-vlan_bytes, 65392-14-4-vlan_bytes); return -EINVAL; } ifp->if_mtu = new_mtu; if ((interface < 2) && (cvmx_helper_interface_get_mode(interface) != CVMX_HELPER_INTERFACE_MODE_SPI)) { int max_packet = new_mtu + 14 + 4 + vlan_bytes; /* Add ethernet header and FCS, and VLAN if configured. */ if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) { /* Signal errors on packets larger than the MTU */ cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(index, interface), max_packet); } else { /* Set the hardware to truncate packets larger than the MTU and smaller the 64 bytes */ cvmx_pip_frm_len_chkx_t frm_len_chk; frm_len_chk.u64 = 0; frm_len_chk.s.minlen = 64; frm_len_chk.s.maxlen = max_packet; cvmx_write_csr(CVMX_PIP_FRM_LEN_CHKX(interface), frm_len_chk.u64); } /* Set the hardware to truncate packets larger than the MTU. The jabber register must be set to a multiple of 8 bytes, so round up */ cvmx_write_csr(CVMX_GMXX_RXX_JABBER(index, interface), (max_packet + 7) & ~7u); } return 0; } /** * Enable port. */ int cvm_oct_common_open(struct ifnet *ifp) { cvmx_gmxx_prtx_cfg_t gmx_cfg; cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; int interface = INTERFACE(priv->port); int index = INDEX(priv->port); cvmx_helper_link_info_t link_info; gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); gmx_cfg.s.en = 1; cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); - if (!octeon_is_simulation()) { + /* + * Set the link state unless we are using MII. + */ + if (!octeon_is_simulation() && priv->miibus == NULL) { link_info = cvmx_helper_link_get(priv->port); if (!link_info.s.link_up) if_link_state_change(ifp, LINK_STATE_DOWN); else if_link_state_change(ifp, LINK_STATE_UP); } return 0; } /** * Disable port. */ int cvm_oct_common_stop(struct ifnet *ifp) { cvmx_gmxx_prtx_cfg_t gmx_cfg; cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; int interface = INTERFACE(priv->port); int index = INDEX(priv->port); gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); gmx_cfg.s.en = 0; cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); return 0; } /** * Poll for link status change. */ void cvm_oct_common_poll(struct ifnet *ifp) { cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; cvmx_helper_link_info_t link_info; + /* + * If this is a simulation, do nothing. + */ + if (octeon_is_simulation()) + return; + + /* + * If there is a device-specific poll method, use it. + */ + if (priv->poll != NULL) { + priv->poll(ifp); + return; + } + + /* + * If an MII bus is attached, don't use the Simple Executive's link + * state routines. + */ + if (priv->miibus != NULL) + return; + + /* + * Use the Simple Executive's link state routines. + */ link_info = cvmx_helper_link_get(priv->port); if (link_info.u64 == priv->link_info) return; link_info = cvmx_helper_link_autoconf(priv->port); priv->link_info = link_info.u64; priv->need_link_update = 1; } /** * Per network device initialization * * @param dev Device to initialize * @return Zero on success */ int cvm_oct_common_init(struct ifnet *ifp) { static int count; char mac[6] = { octeon_bootinfo->mac_addr_base[0], octeon_bootinfo->mac_addr_base[1], octeon_bootinfo->mac_addr_base[2], octeon_bootinfo->mac_addr_base[3], octeon_bootinfo->mac_addr_base[4], octeon_bootinfo->mac_addr_base[5] + count}; cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; ifp->if_mtu = ETHERMTU; count++; cvm_oct_mdio_setup_device(ifp); cvm_oct_common_set_mac_address(ifp, mac); cvm_oct_common_change_mtu(ifp, ifp->if_mtu); /* * Do any last-minute board-specific initialization. */ switch (cvmx_sysinfo_get()->board_type) { #if defined(OCTEON_VENDOR_LANNER) case CVMX_BOARD_TYPE_CUST_LANNER_MR320: if (priv->phy_id == 16) cvm_oct_mv88e61xx_setup_device(ifp); break; #endif default: break; } device_attach(priv->dev); return 0; } void cvm_oct_common_uninit(struct ifnet *ifp) { /* Currently nothing to do */ } Index: head/sys/mips/cavium/octe/ethernet-common.h =================================================================== --- head/sys/mips/cavium/octe/ethernet-common.h (revision 216070) +++ head/sys/mips/cavium/octe/ethernet-common.h (revision 216071) @@ -1,55 +1,53 @@ /************************************************************************* Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Cavium Networks nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 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. *************************************************************************/ /* $FreeBSD$ */ int cvm_oct_common_open(struct ifnet *ifp); int cvm_oct_common_stop(struct ifnet *ifp); void cvm_oct_common_poll(struct ifnet *ifp); int cvm_oct_common_init(struct ifnet *ifp); void cvm_oct_common_uninit(struct ifnet *ifp); int cvm_oct_common_change_mtu(struct ifnet *ifp, int new_mtu); void cvm_oct_common_set_multicast_list(struct ifnet *ifp); void cvm_oct_common_set_mac_address(struct ifnet *ifp, const void *); int cvm_oct_init_module(device_t); void cvm_oct_cleanup_module(void); /* * XXX/juli * These belong elsewhere but we can't stomach the nested extern. */ int cvm_oct_rgmii_init(struct ifnet *ifp); void cvm_oct_rgmii_uninit(struct ifnet *ifp); int cvm_oct_sgmii_init(struct ifnet *ifp); -void cvm_oct_sgmii_uninit(struct ifnet *ifp); int cvm_oct_spi_init(struct ifnet *ifp); void cvm_oct_spi_uninit(struct ifnet *ifp); int cvm_oct_xaui_init(struct ifnet *ifp); -void cvm_oct_xaui_uninit(struct ifnet *ifp); Index: head/sys/mips/cavium/octe/ethernet-rgmii.c =================================================================== --- head/sys/mips/cavium/octe/ethernet-rgmii.c (revision 216070) +++ head/sys/mips/cavium/octe/ethernet-rgmii.c (revision 216071) @@ -1,340 +1,306 @@ /************************************************************************* Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Cavium Networks nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 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. *************************************************************************/ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "wrapper-cvmx-includes.h" #include "ethernet-headers.h" #include "octebusvar.h" extern int octeon_is_simulation(void); extern struct ifnet *cvm_oct_device[]; static struct mtx global_register_lock; MTX_SYSINIT(global_register_lock, &global_register_lock, "RGMII Global", MTX_SPIN); static int number_rgmii_ports; static void cvm_oct_rgmii_poll(struct ifnet *ifp) { cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; cvmx_helper_link_info_t link_info; /* Take the global register lock since we are going to touch registers that affect more than one port */ mtx_lock_spin(&global_register_lock); link_info = cvmx_helper_link_get(priv->port); if (link_info.u64 == priv->link_info) { /* If the 10Mbps preamble workaround is supported and we're at 10Mbps we may need to do some special checking */ if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) { /* Read the GMXX_RXX_INT_REG[PCTERR] bit and see if we are getting preamble errors */ int interface = INTERFACE(priv->port); int index = INDEX(priv->port); cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg; gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface)); if (gmxx_rxx_int_reg.s.pcterr) { /* We are getting preamble errors at 10Mbps. Most likely the PHY is giving us packets with mis aligned preambles. In order to get these packets we need to disable preamble checking and do it in software */ cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl; cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs; /* Disable preamble checking */ gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface)); gmxx_rxx_frm_ctl.s.pre_chk = 0; cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64); /* Disable FCS stripping */ ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS); ipd_sub_port_fcs.s.port_bit &= 0xffffffffull ^ (1ull<port); cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64); /* Clear any error bits */ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64); DEBUGPRINT("%s: Using 10Mbps with software preamble removal\n", if_name(ifp)); } } mtx_unlock_spin(&global_register_lock); return; } /* If the 10Mbps preamble workaround is allowed we need to on preamble checking, FCS stripping, and clear error bits on every speed change. If errors occur during 10Mbps operation the above code will change this stuff */ if (USE_10MBPS_PREAMBLE_WORKAROUND) { cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl; cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs; cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg; int interface = INTERFACE(priv->port); int index = INDEX(priv->port); /* Enable preamble checking */ gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface)); gmxx_rxx_frm_ctl.s.pre_chk = 1; cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64); /* Enable FCS stripping */ ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS); ipd_sub_port_fcs.s.port_bit |= 1ull<port; cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64); /* Clear any error bits */ gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface)); cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64); } - link_info = cvmx_helper_link_autoconf(priv->port); - priv->link_info = link_info.u64; - priv->need_link_update = 1; + if (priv->miibus == NULL) { + link_info = cvmx_helper_link_autoconf(priv->port); + priv->link_info = link_info.u64; + priv->need_link_update = 1; + } mtx_unlock_spin(&global_register_lock); } static int cvm_oct_rgmii_rml_interrupt(void *dev_id) { cvmx_npi_rsl_int_blocks_t rsl_int_blocks; int index; int return_status = FILTER_STRAY; rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS); /* Check and see if this interrupt was caused by the GMX0 block */ if (rsl_int_blocks.s.gmx0) { int interface = 0; /* Loop through every port of this interface */ for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) { /* Read the GMX interrupt status bits */ cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg; gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface)); gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface)); /* Poll the port if inband status changed */ if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) { struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)]; if (ifp) cvm_oct_rgmii_poll(ifp); gmx_rx_int_reg.u64 = 0; gmx_rx_int_reg.s.phy_dupx = 1; gmx_rx_int_reg.s.phy_link = 1; gmx_rx_int_reg.s.phy_spd = 1; cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64); return_status = FILTER_HANDLED; } } } /* Check and see if this interrupt was caused by the GMX1 block */ if (rsl_int_blocks.s.gmx1) { int interface = 1; /* Loop through every port of this interface */ for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) { /* Read the GMX interrupt status bits */ cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg; gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface)); gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface)); /* Poll the port if inband status changed */ if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) { struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)]; if (ifp) cvm_oct_rgmii_poll(ifp); gmx_rx_int_reg.u64 = 0; gmx_rx_int_reg.s.phy_dupx = 1; gmx_rx_int_reg.s.phy_link = 1; gmx_rx_int_reg.s.phy_spd = 1; cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64); return_status = FILTER_HANDLED; } } } return return_status; } -static int cvm_oct_rgmii_open(struct ifnet *ifp) -{ - cvmx_gmxx_prtx_cfg_t gmx_cfg; - cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; - int interface = INTERFACE(priv->port); - int index = INDEX(priv->port); - cvmx_helper_link_info_t link_info; - - gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); - gmx_cfg.s.en = 1; - cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); - - if (!octeon_is_simulation()) { - link_info = cvmx_helper_link_get(priv->port); - if (!link_info.s.link_up) - if_link_state_change(ifp, LINK_STATE_DOWN); - else - if_link_state_change(ifp, LINK_STATE_UP); - } - - return 0; -} - -static int cvm_oct_rgmii_stop(struct ifnet *ifp) -{ - cvmx_gmxx_prtx_cfg_t gmx_cfg; - cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; - int interface = INTERFACE(priv->port); - int index = INDEX(priv->port); - - gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); - gmx_cfg.s.en = 0; - cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); - return 0; -} - int cvm_oct_rgmii_init(struct ifnet *ifp) { struct octebus_softc *sc; cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; int error; int rid; cvm_oct_common_init(ifp); - priv->open = cvm_oct_rgmii_open; - priv->stop = cvm_oct_rgmii_stop; + priv->open = cvm_oct_common_open; + priv->stop = cvm_oct_common_stop; priv->stop(ifp); /* Due to GMX errata in CN3XXX series chips, it is necessary to take the link down immediately whne the PHY changes state. In order to do this we call the poll function every time the RGMII inband status changes. This may cause problems if the PHY doesn't implement inband status properly */ if (number_rgmii_ports == 0) { sc = device_get_softc(device_get_parent(priv->dev)); rid = 0; sc->sc_rgmii_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ, &rid, CVMX_IRQ_RML, CVMX_IRQ_RML, 1, RF_ACTIVE); if (sc->sc_rgmii_irq == NULL) { device_printf(sc->sc_dev, "could not allocate RGMII irq"); return ENXIO; } error = bus_setup_intr(sc->sc_dev, sc->sc_rgmii_irq, INTR_TYPE_NET | INTR_MPSAFE, cvm_oct_rgmii_rml_interrupt, NULL, &number_rgmii_ports, NULL); if (error != 0) { device_printf(sc->sc_dev, "could not setup RGMII irq"); return error; } } number_rgmii_ports++; /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really a RGMII port */ if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) { if (!octeon_is_simulation()) { cvmx_gmxx_rxx_int_en_t gmx_rx_int_en; int interface = INTERFACE(priv->port); int index = INDEX(priv->port); /* Enable interrupts on inband status changes for this port */ gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface)); gmx_rx_int_en.s.phy_dupx = 1; gmx_rx_int_en.s.phy_link = 1; gmx_rx_int_en.s.phy_spd = 1; cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64); priv->poll = cvm_oct_rgmii_poll; } } return 0; } void cvm_oct_rgmii_uninit(struct ifnet *ifp) { cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; cvm_oct_common_uninit(ifp); /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really a RGMII port */ if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) { if (!octeon_is_simulation()) { cvmx_gmxx_rxx_int_en_t gmx_rx_int_en; int interface = INTERFACE(priv->port); int index = INDEX(priv->port); /* Disable interrupts on inband status changes for this port */ gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface)); gmx_rx_int_en.s.phy_dupx = 0; gmx_rx_int_en.s.phy_link = 0; gmx_rx_int_en.s.phy_spd = 0; cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64); } } /* Remove the interrupt handler when the last port is removed */ number_rgmii_ports--; if (number_rgmii_ports == 0) panic("%s: need to implement IRQ release.", __func__); } Index: head/sys/mips/cavium/octe/ethernet-sgmii.c =================================================================== --- head/sys/mips/cavium/octe/ethernet-sgmii.c (revision 216070) +++ head/sys/mips/cavium/octe/ethernet-sgmii.c (revision 216071) @@ -1,61 +1,59 @@ /************************************************************************* Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Cavium Networks nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 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. *************************************************************************/ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "wrapper-cvmx-includes.h" #include "ethernet-headers.h" extern int octeon_is_simulation(void); int cvm_oct_sgmii_init(struct ifnet *ifp) { cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; cvm_oct_common_init(ifp); priv->open = cvm_oct_common_open; priv->stop = cvm_oct_common_stop; priv->stop(ifp); - if (!octeon_is_simulation()) - priv->poll = cvm_oct_common_poll; /* FIXME: Need autoneg logic */ return 0; } Index: head/sys/mips/cavium/octe/ethernet-xaui.c =================================================================== --- head/sys/mips/cavium/octe/ethernet-xaui.c (revision 216070) +++ head/sys/mips/cavium/octe/ethernet-xaui.c (revision 216071) @@ -1,60 +1,58 @@ /************************************************************************* Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Cavium Networks nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 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. *************************************************************************/ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "wrapper-cvmx-includes.h" #include "ethernet-headers.h" extern int octeon_is_simulation(void); int cvm_oct_xaui_init(struct ifnet *ifp) { cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc; cvm_oct_common_init(ifp); priv->open = cvm_oct_common_open; priv->stop = cvm_oct_common_stop; priv->stop(ifp); - if (!octeon_is_simulation()) - priv->poll = cvm_oct_common_poll; - return 0; + return 0; } Index: head/sys/mips/cavium/octe/ethernet.c =================================================================== --- head/sys/mips/cavium/octe/ethernet.c (revision 216070) +++ head/sys/mips/cavium/octe/ethernet.c (revision 216071) @@ -1,488 +1,481 @@ /************************************************************************* Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Cavium Networks nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 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. *************************************************************************/ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wrapper-cvmx-includes.h" #include "ethernet-headers.h" #include "octebusvar.h" /* * XXX/juli * Convert 0444 to tunables, 0644 to sysctls. */ #if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS int num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS; #else int num_packet_buffers = 1024; #endif TUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers); /* "\t\tNumber of packet buffers to allocate and store in the\n" "\t\tFPA. By default, 1024 packet buffers are used unless\n" "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */ int pow_receive_group = 15; TUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group); /* "\t\tPOW group to receive packets from. All ethernet hardware\n" "\t\twill be configured to send incomming packets to this POW\n" "\t\tgroup. Also any other software can submit packets to this\n" "\t\tgroup for the kernel to process." */ extern int octeon_is_simulation(void); /** * Exported from the kernel so we can determine board information. It is * passed by the bootloader to the kernel. */ extern cvmx_bootinfo_t *octeon_bootinfo; /** * Periodic timer to check auto negotiation */ static struct callout cvm_oct_poll_timer; /** * Array of every ethernet device owned by this driver indexed by * the ipd input port number. */ struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; /** * Task to handle link status changes. */ static struct taskqueue *cvm_oct_link_taskq; /** * Function to update link status. */ static void cvm_oct_update_link(void *context, int pending) { cvm_oct_private_t *priv = (cvm_oct_private_t *)context; struct ifnet *ifp = priv->ifp; cvmx_helper_link_info_t link_info; link_info.u64 = priv->link_info; if (link_info.s.link_up) { if_link_state_change(ifp, LINK_STATE_UP); DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n", if_name(ifp), link_info.s.speed, (link_info.s.full_duplex) ? "Full" : "Half", priv->port, priv->queue); } else { if_link_state_change(ifp, LINK_STATE_DOWN); DEBUGPRINT("%s: Link down\n", if_name(ifp)); } priv->need_link_update = 0; } /** * Periodic timer tick for slow management operations * * @param arg Device to check */ static void cvm_do_timer(void *arg) { static int port; static int updated; if (port < CVMX_PIP_NUM_INPUT_PORTS) { if (cvm_oct_device[port]) { int queues_per_port; int qos; cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc; - if (priv->poll) - { - /* skip polling if we don't get the lock */ - if (MDIO_TRYLOCK()) { - priv->poll(cvm_oct_device[port]); - MDIO_UNLOCK(); - if (priv->need_link_update) { - updated++; - taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task); - } - } + cvm_oct_common_poll(priv->ifp); + if (priv->need_link_update) { + updated++; + taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task); } queues_per_port = cvmx_pko_get_num_queues(port); /* Drain any pending packets in the free list */ for (qos = 0; qos < queues_per_port; qos++) { if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) { IF_LOCK(&priv->tx_free_queue[qos]); while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) { struct mbuf *m; _IF_DEQUEUE(&priv->tx_free_queue[qos], m); m_freem(m); } IF_UNLOCK(&priv->tx_free_queue[qos]); /* * XXX locking! */ priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } } } port++; /* Poll the next port in a 50th of a second. This spreads the polling of ports out a little bit */ callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); } else { port = 0; /* If any updates were made in this run, continue iterating at * 1/50th of a second, so that if a link has merely gone down * temporarily (e.g. because of interface reinitialization) it * will not be forced to stay down for an entire second. */ if (updated > 0) { updated = 0; callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); } else { /* All ports have been polled. Start the next iteration through the ports in one second */ callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); } } } /** * Configure common hardware for all interfaces */ static void cvm_oct_configure_common_hw(device_t bus) { struct octebus_softc *sc; int error; int rid; sc = device_get_softc(bus); /* Setup the FPA */ cvmx_fpa_enable(); cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128); if (USE_RED) cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8); /* Enable the MII interface */ if (!octeon_is_simulation()) cvmx_write_csr(CVMX_SMI_EN, 1); /* Register an IRQ hander for to receive POW interrupts */ rid = 0; sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid, CVMX_IRQ_WORKQ0 + pow_receive_group, CVMX_IRQ_WORKQ0 + pow_receive_group, 1, RF_ACTIVE); if (sc->sc_rx_irq == NULL) { device_printf(bus, "could not allocate workq irq"); return; } error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE, cvm_oct_do_interrupt, NULL, cvm_oct_device, NULL); if (error != 0) { device_printf(bus, "could not setup workq irq"); return; } #ifdef SMP if (USE_MULTICORE_RECEIVE) { critical_enter(); { int cpu; for (cpu = 0; cpu < mp_maxid; cpu++) { if (!CPU_ABSENT(cpu) && (cpu != PCPU_GET(cpuid))) { cvmx_ciu_intx0_t en; en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2)); en.s.workq |= (1<word2.s.bufs; cvmx_buf_ptr_t segment_ptr = work->packet_ptr; while (segments--) { cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8); if (__predict_false(!segment_ptr.s.i)) cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128)); segment_ptr = next_ptr; } cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1)); return 0; } /** * Module/ driver initialization. Creates the linux network * devices. * * @return Zero on success */ int cvm_oct_init_module(device_t bus) { device_t dev; int ifnum; int num_interfaces; int interface; int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE; int qos; printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING); cvm_oct_rx_initialize(); cvm_oct_configure_common_hw(bus); cvmx_helper_initialize_packet_io_global(); /* Change the input group for all ports before input is enabled */ num_interfaces = cvmx_helper_get_number_of_interfaces(); for (interface = 0; interface < num_interfaces; interface++) { int num_ports = cvmx_helper_ports_on_interface(interface); int port; for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) { cvmx_pip_prt_tagx_t pip_prt_tagx; pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port)); pip_prt_tagx.s.grp = pow_receive_group; cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64); } } cvmx_helper_ipd_and_packet_input_enable(); memset(cvm_oct_device, 0, sizeof(cvm_oct_device)); cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT, taskqueue_thread_enqueue, &cvm_oct_link_taskq); taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET, "octe link taskq"); /* Initialize the FAU used for counting packet buffers that need to be freed */ cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); ifnum = 0; num_interfaces = cvmx_helper_get_number_of_interfaces(); for (interface = 0; interface < num_interfaces; interface++) { cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface); int num_ports = cvmx_helper_ports_on_interface(interface); int port; for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) { cvm_oct_private_t *priv; struct ifnet *ifp; dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++); if (dev != NULL) ifp = if_alloc(IFT_ETHER); if (dev == NULL || ifp == NULL) { printf("\t\tFailed to allocate ethernet device for port %d\n", port); continue; } /* Initialize the device private structure. */ device_probe(dev); priv = device_get_softc(dev); priv->dev = dev; priv->ifp = ifp; priv->imode = imode; priv->port = port; priv->queue = cvmx_pko_get_base_queue(priv->port); priv->fau = fau - cvmx_pko_get_num_queues(port) * 4; for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++) cvmx_fau_atomic_write32(priv->fau+qos*4, 0); TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv); switch (priv->imode) { /* These types don't support ports to IPD/PKO */ case CVMX_HELPER_INTERFACE_MODE_DISABLED: case CVMX_HELPER_INTERFACE_MODE_PCIE: case CVMX_HELPER_INTERFACE_MODE_PICMG: break; case CVMX_HELPER_INTERFACE_MODE_NPI: priv->init = cvm_oct_common_init; priv->uninit = cvm_oct_common_uninit; device_set_desc(dev, "Cavium Octeon NPI Ethernet"); break; case CVMX_HELPER_INTERFACE_MODE_XAUI: priv->init = cvm_oct_xaui_init; priv->uninit = cvm_oct_common_uninit; device_set_desc(dev, "Cavium Octeon XAUI Ethernet"); break; case CVMX_HELPER_INTERFACE_MODE_LOOP: priv->init = cvm_oct_common_init; priv->uninit = cvm_oct_common_uninit; device_set_desc(dev, "Cavium Octeon LOOP Ethernet"); break; case CVMX_HELPER_INTERFACE_MODE_SGMII: priv->init = cvm_oct_sgmii_init; priv->uninit = cvm_oct_common_uninit; device_set_desc(dev, "Cavium Octeon SGMII Ethernet"); break; case CVMX_HELPER_INTERFACE_MODE_SPI: priv->init = cvm_oct_spi_init; priv->uninit = cvm_oct_spi_uninit; device_set_desc(dev, "Cavium Octeon SPI Ethernet"); break; case CVMX_HELPER_INTERFACE_MODE_RGMII: priv->init = cvm_oct_rgmii_init; priv->uninit = cvm_oct_rgmii_uninit; device_set_desc(dev, "Cavium Octeon RGMII Ethernet"); break; case CVMX_HELPER_INTERFACE_MODE_GMII: priv->init = cvm_oct_rgmii_init; priv->uninit = cvm_oct_rgmii_uninit; device_set_desc(dev, "Cavium Octeon GMII Ethernet"); break; } ifp->if_softc = priv; if (!priv->init) { panic("%s: unsupported device type, need to free ifp.", __func__); } else if (priv->init(ifp) < 0) { printf("\t\tFailed to register ethernet device for interface %d, port %d\n", interface, priv->port); panic("%s: init failed, need to free ifp.", __func__); } else { cvm_oct_device[priv->port] = ifp; fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t); } } } if (INTERRUPT_LIMIT) { /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */ cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8); /* Enable POW timer interrupt. It will count when there are packets available */ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24); } else { /* Enable POW interrupt when our port has at least one packet */ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001); } callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE); callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); return 0; } /** * Module / driver shutdown * * @return Zero on success */ void cvm_oct_cleanup_module(void) { int port; /* Disable POW interrupt */ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0); #if 0 /* Free the interrupt handler */ free_irq(8 + pow_receive_group, cvm_oct_device); #endif callout_stop(&cvm_oct_poll_timer); cvm_oct_rx_shutdown(); cvmx_helper_shutdown_packet_io_global(); /* Free the ethernet devices */ for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { if (cvm_oct_device[port]) { cvm_oct_tx_shutdown(cvm_oct_device[port]); #if 0 unregister_netdev(cvm_oct_device[port]); kfree(cvm_oct_device[port]); #else panic("%s: need to detach and free interface.", __func__); #endif cvm_oct_device[port] = NULL; } } } Index: head/sys/mips/cavium/octe/octe.c =================================================================== --- head/sys/mips/cavium/octe/octe.c (revision 216070) +++ head/sys/mips/cavium/octe/octe.c (revision 216071) @@ -1,495 +1,495 @@ /*- * Copyright (c) 2010 Juli Mallett * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Cavium Octeon Ethernet devices. * * XXX This file should be moved to if_octe.c * XXX The driver may have sufficient locking but we need locking to protect * the interfaces presented here, right? */ #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif #include #include #include "wrapper-cvmx-includes.h" #include "cavium-ethernet.h" #include "ethernet-common.h" #include "ethernet-defines.h" #include "ethernet-mdio.h" #include "ethernet-tx.h" #include "miibus_if.h" #define OCTE_TX_LOCK(priv) mtx_lock(&(priv)->tx_mtx) #define OCTE_TX_UNLOCK(priv) mtx_unlock(&(priv)->tx_mtx) static int octe_probe(device_t); static int octe_attach(device_t); static int octe_detach(device_t); static int octe_shutdown(device_t); static int octe_miibus_readreg(device_t, int, int); static int octe_miibus_writereg(device_t, int, int, int); static void octe_init(void *); static void octe_stop(void *); static int octe_transmit(struct ifnet *, struct mbuf *); static int octe_mii_medchange(struct ifnet *); static void octe_mii_medstat(struct ifnet *, struct ifmediareq *); static int octe_medchange(struct ifnet *); static void octe_medstat(struct ifnet *, struct ifmediareq *); static int octe_ioctl(struct ifnet *, u_long, caddr_t); static device_method_t octe_methods[] = { /* Device interface */ DEVMETHOD(device_probe, octe_probe), DEVMETHOD(device_attach, octe_attach), DEVMETHOD(device_detach, octe_detach), DEVMETHOD(device_shutdown, octe_shutdown), /* MII interface */ DEVMETHOD(miibus_readreg, octe_miibus_readreg), DEVMETHOD(miibus_writereg, octe_miibus_writereg), { 0, 0 } }; static driver_t octe_driver = { "octe", octe_methods, sizeof (cvm_oct_private_t), }; static devclass_t octe_devclass; DRIVER_MODULE(octe, octebus, octe_driver, octe_devclass, 0, 0); DRIVER_MODULE(miibus, octe, miibus_driver, miibus_devclass, 0, 0); static int octe_probe(device_t dev) { return (0); } static int octe_attach(device_t dev) { struct ifnet *ifp; cvm_oct_private_t *priv; device_t child; unsigned qos; int error; priv = device_get_softc(dev); ifp = priv->ifp; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); if (priv->phy_id != -1) { if (priv->phy_device == NULL) { error = mii_attach(dev, &priv->miibus, ifp, octe_mii_medchange, octe_mii_medstat, BMSR_DEFCAPMASK, priv->phy_id, MII_OFFSET_ANY, 0); if (error != 0) device_printf(dev, "attaching PHYs failed\n"); } else { child = device_add_child(dev, priv->phy_device, -1); if (child == NULL) device_printf(dev, "missing phy %u device %s\n", priv->phy_id, priv->phy_device); } } if (priv->miibus == NULL) { ifmedia_init(&priv->media, 0, octe_medchange, octe_medstat); ifmedia_add(&priv->media, IFM_ETHER | IFM_AUTO, 0, NULL); ifmedia_set(&priv->media, IFM_ETHER | IFM_AUTO); } /* * XXX * We don't support programming the multicast filter right now, although it * ought to be easy enough. (Presumably it's just a matter of putting * multicast addresses in the CAM?) */ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI; ifp->if_init = octe_init; ifp->if_ioctl = octe_ioctl; priv->if_flags = ifp->if_flags; mtx_init(&priv->tx_mtx, ifp->if_xname, "octe tx send queue", MTX_DEF); for (qos = 0; qos < 16; qos++) { mtx_init(&priv->tx_free_queue[qos].ifq_mtx, ifp->if_xname, "octe tx free queue", MTX_DEF); IFQ_SET_MAXLEN(&priv->tx_free_queue[qos], MAX_OUT_QUEUE_DEPTH); } ether_ifattach(ifp, priv->mac); ifp->if_transmit = octe_transmit; ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM; ifp->if_capenable = ifp->if_capabilities; ifp->if_hwassist = CSUM_TCP | CSUM_UDP; OCTE_TX_LOCK(priv); IFQ_SET_MAXLEN(&ifp->if_snd, MAX_OUT_QUEUE_DEPTH); ifp->if_snd.ifq_drv_maxlen = MAX_OUT_QUEUE_DEPTH; IFQ_SET_READY(&ifp->if_snd); OCTE_TX_UNLOCK(priv); return (bus_generic_attach(dev)); } static int octe_detach(device_t dev) { return (0); } static int octe_shutdown(device_t dev) { return (octe_detach(dev)); } static int octe_miibus_readreg(device_t dev, int phy, int reg) { cvm_oct_private_t *priv; priv = device_get_softc(dev); /* * Try interface-specific MII routine. */ if (priv->mdio_read != NULL) return (priv->mdio_read(priv->ifp, phy, reg)); /* * Try generic MII routine. */ KASSERT(phy == priv->phy_id, ("read from phy %u but our phy is %u", phy, priv->phy_id)); return (cvm_oct_mdio_read(priv->ifp, phy, reg)); } static int octe_miibus_writereg(device_t dev, int phy, int reg, int val) { cvm_oct_private_t *priv; priv = device_get_softc(dev); /* * Try interface-specific MII routine. */ if (priv->mdio_write != NULL) { priv->mdio_write(priv->ifp, phy, reg, val); return (0); } /* * Try generic MII routine. */ KASSERT(phy == priv->phy_id, ("write to phy %u but our phy is %u", phy, priv->phy_id)); cvm_oct_mdio_write(priv->ifp, phy, reg, val); return (0); } static void octe_init(void *arg) { struct ifnet *ifp; cvm_oct_private_t *priv; priv = arg; ifp = priv->ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) octe_stop(priv); if (priv->open != NULL) priv->open(ifp); if (((ifp->if_flags ^ priv->if_flags) & (IFF_ALLMULTI | IFF_MULTICAST | IFF_PROMISC)) != 0) cvm_oct_common_set_multicast_list(ifp); cvm_oct_common_set_mac_address(ifp, IF_LLADDR(ifp)); - if (priv->poll != NULL) - priv->poll(ifp); + cvm_oct_common_poll(ifp); + if (priv->miibus != NULL) mii_mediachg(device_get_softc(priv->miibus)); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static void octe_stop(void *arg) { struct ifnet *ifp; cvm_oct_private_t *priv; priv = arg; ifp = priv->ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; if (priv->stop != NULL) priv->stop(ifp); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } static int octe_transmit(struct ifnet *ifp, struct mbuf *m) { cvm_oct_private_t *priv; priv = ifp->if_softc; if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) { m_freem(m); return (0); } return (cvm_oct_xmit(m, ifp)); } static int octe_mii_medchange(struct ifnet *ifp) { cvm_oct_private_t *priv; struct mii_data *mii; priv = ifp->if_softc; mii = device_get_softc(priv->miibus); if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } mii_mediachg(mii); return (0); } static void octe_mii_medstat(struct ifnet *ifp, struct ifmediareq *ifm) { cvm_oct_private_t *priv; struct mii_data *mii; priv = ifp->if_softc; mii = device_get_softc(priv->miibus); mii_pollstat(mii); ifm->ifm_active = mii->mii_media_active; ifm->ifm_status = mii->mii_media_status; } static int octe_medchange(struct ifnet *ifp) { return (ENOTSUP); } static void octe_medstat(struct ifnet *ifp, struct ifmediareq *ifm) { cvm_oct_private_t *priv; cvmx_helper_link_info_t link_info; priv = ifp->if_softc; ifm->ifm_status = IFM_AVALID; ifm->ifm_active = IFT_ETHER; if (priv->poll == NULL) return; priv->poll(ifp); link_info.u64 = priv->link_info; if (!link_info.s.link_up) return; ifm->ifm_status |= IFM_ACTIVE; switch (link_info.s.speed) { case 10: ifm->ifm_active |= IFM_10_T; break; case 100: ifm->ifm_active |= IFM_100_TX; break; case 1000: ifm->ifm_active |= IFM_1000_T; break; case 10000: ifm->ifm_active |= IFM_10G_T; break; } if (link_info.s.full_duplex) ifm->ifm_active |= IFM_FDX; else ifm->ifm_active |= IFM_HDX; } static int octe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { cvm_oct_private_t *priv; struct mii_data *mii; struct ifreq *ifr; #ifdef INET struct ifaddr *ifa; #endif int error; priv = ifp->if_softc; ifr = (struct ifreq *)data; #ifdef INET ifa = (struct ifaddr *)data; #endif switch (cmd) { case SIOCSIFADDR: #ifdef INET /* * Avoid reinitialization unless it's necessary. */ if (ifa->ifa_addr->sa_family == AF_INET) { ifp->if_flags |= IFF_UP; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) octe_init(priv); arp_ifinit(ifp, ifa); return (0); } #endif error = ether_ioctl(ifp, cmd, data); if (error != 0) return (error); return (0); case SIOCSIFFLAGS: if (ifp->if_flags == priv->if_flags) return (0); if ((ifp->if_flags & IFF_UP) != 0) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) octe_init(priv); } else { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) octe_stop(priv); } priv->if_flags = ifp->if_flags; return (0); case SIOCSIFCAP: /* * Just change the capabilities in software, currently none * require reprogramming hardware, they just toggle whether we * make use of already-present facilities in software. */ ifp->if_capenable = ifr->ifr_reqcap; return (0); case SIOCSIFMTU: error = cvm_oct_common_change_mtu(ifp, ifr->ifr_mtu); if (error != 0) return (EINVAL); return (0); case SIOCSIFMEDIA: case SIOCGIFMEDIA: if (priv->miibus != NULL) { mii = device_get_softc(priv->miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); if (error != 0) return (error); return (0); } error = ifmedia_ioctl(ifp, ifr, &priv->media, cmd); if (error != 0) return (error); return (0); default: error = ether_ioctl(ifp, cmd, data); if (error != 0) return (error); return (0); } }