/* <:copyright-BRCM:2013:DUAL/GPL:standard Copyright (c) 2013 Broadcom All Rights Reserved This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation (the "GPL"). This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. :> */ #include #include "bcmenet_common.h" #if !defined(CONFIG_BCM_RDPA) && !defined(CONFIG_BCM_RDPA_MODULE) #include "bcmenet_dma.h" #else #include "bcmenet_runner.h" #endif #include #include "bcmnet.h" #include #if defined(SUPPORT_ETHTOOL) enum { ET_TX_BYTES = 0, ET_TX_PACKETS, ET_TX_ERRORS, ET_TX_CAPACITY, ET_RX_BYTES, ET_RX_PACKETS, ET_RX_ERRORS, MAC_RX_PKTS_GOOD, MAC_TX_PKTS_GOOD, MAC_RX_BYTES_GOOD, MAC_TX_BYTES_GOOD, //MAC_RX_PKTS_PAUSE, //MAC_TX_PKTS_PAUSE, MAC_RX_PKTS_DROPPED, MAC_TX_PKTS_DROPPED, //MAC_RX_BYTES_ERROR, NETIF_RX_PKTS_GOOD, NETIF_TX_PKTS_GOOD, NETIF_RX_BYTES_GOOD, NETIF_TX_BYTES_GOOD, NETIF_RX_PKTS_DROPPED, NETIF_TX_PKTS_DROPPED, NETIF_RX_PKTS_ERROR, NETIF_TX_PKTS_ERROR, ET_MAX } Bcm63xxEnetStats; static char ethtool_stats_strings[][ETH_GSTRING_LEN] = { [ET_TX_BYTES] = "TxOctetsOK ", [ET_TX_PACKETS] = "TxPacketsOK ", [ET_TX_ERRORS] = "TxErrors ", [ET_TX_CAPACITY] = "TxCapacity ", [ET_RX_BYTES] = "RxOctetsOK ", [ET_RX_PACKETS] = "RxPacketsOK ", [ET_RX_ERRORS] = "RxErrors ", [MAC_RX_PKTS_GOOD] = "mac_rx_pkts_good", [MAC_TX_PKTS_GOOD] = "mac_tx_pkts_good", [MAC_RX_BYTES_GOOD] = "mac_rx_bytes_good", [MAC_TX_BYTES_GOOD] = "mac_tx_bytes_good", //[MAC_RX_PKTS_PAUSE] = "mac_rx_pkts_pause", //[MAC_TX_PKTS_PAUSE] = "mac_tx_pkts_pause", [MAC_RX_PKTS_DROPPED] = "mac_rx_pkts_dropped", [MAC_TX_PKTS_DROPPED] = "mac_tx_pkts_dropped", //[MAC_RX_BYTES_ERROR] = "mac_rx_bytes_error", [NETIF_RX_PKTS_GOOD] = "netif_rx_pkts_good", [NETIF_TX_PKTS_GOOD] = "netif_tx_pkts_good", [NETIF_RX_BYTES_GOOD] = "netif_rx_bytes_good", [NETIF_TX_BYTES_GOOD] = "netif_tx_bytes_good", [NETIF_RX_PKTS_DROPPED] = "netif_rx_pkts_dropped", [NETIF_TX_PKTS_DROPPED] = "netif_tx_pkts_dropped", [NETIF_RX_PKTS_ERROR] = "netif_rx_pkts_error", [NETIF_TX_PKTS_ERROR] = "netif_tx_pkts_error", }; enum { ET_PF_802_1Q_VLAN = 0, ET_PF_EBRIDGE, ET_PF_SLAVE_INACTIVE, ET_PF_MASTER_8023AD, ET_PF_MASTER_ALB, ET_PF_BONDING, ET_PF_SLAVE_NEEDARP, ET_PF_ISATAP, ET_PF_MASTER_ARPMON, ET_PF_WAN_HDLC, ET_PF_XMIT_DST_RELEASE, ET_PF_DONT_BRIDGE, ET_PF_DISABLE_NETPOLL, ET_PF_MACVLAN_PORT, ET_PF_BRIDGE_PORT, ET_PF_OVS_DATAPATH, ET_PF_TX_SKB_SHARING, ET_PF_UNICAST_FLT, ET_PF_TEAM_PORT, ET_PF_SUPP_NOFCS, ET_PF_LIVE_ADDR_CHANGE, ET_PF_MACVLAN, ET_PF_XMIT_DST_RELEASE_PERM, ET_PF_IPVLAN_MASTER, ET_PF_IPVLAN_SLAVE, ET_PF_DUMMY_UNUSED, #if defined(CONFIG_BCM_KF_WANDEV) ET_PF_WANDEV, #endif #if defined(CONFIG_BCM_KF_VLAN) ET_PF_BCM_VLAN, #endif #if 1 || defined(CONFIG_BCM_KF_PPP) ET_PF_PPP, #endif #if defined(CONFIG_BCM_KF_IP) ET_PF_EPON_IF, #endif #if defined(CONFIG_BCM_KF_ENET_SWITCH) ET_PF_HW_SWITCH, ET_PF_EXT_SWITCH, #endif /* CONFIG_BCM_KF_ENET_SWITCH */ ET_PF_MAX }; static char ethtool_pflags_strings[][ETH_GSTRING_LEN] = { [ET_PF_802_1Q_VLAN] = "802_1Q_VLAN ", [ET_PF_EBRIDGE] = "EBRIDGE ", [ET_PF_SLAVE_INACTIVE] = "SLAVE_INACTIVE ", [ET_PF_MASTER_8023AD] = "MASTER_8023AD ", [ET_PF_MASTER_ALB] = "MASTER_ALB ", [ET_PF_BONDING] = "BONDING ", [ET_PF_SLAVE_NEEDARP] = "SLAVE_NEEDARP ", [ET_PF_ISATAP] = "ISATAP ", [ET_PF_MASTER_ARPMON] = "MASTER_ARPMON ", [ET_PF_WAN_HDLC] = "WAN_HDLC ", [ET_PF_XMIT_DST_RELEASE] = "XMIT_DST_RELEASE ", [ET_PF_DONT_BRIDGE] = "DONT_BRIDGE ", [ET_PF_DISABLE_NETPOLL] = "DISABLE_NETPOLL ", [ET_PF_MACVLAN_PORT] = "MACVLAN_PORT ", [ET_PF_BRIDGE_PORT] = "BRIDGE_PORT ", [ET_PF_OVS_DATAPATH] = "OVS_DATAPATH ", [ET_PF_TX_SKB_SHARING] = "TX_SKB_SHARING ", [ET_PF_UNICAST_FLT] = "UNICAST_FLT ", [ET_PF_TEAM_PORT] = "TEAM_PORT ", [ET_PF_SUPP_NOFCS] = "SUPP_NOFCS ", [ET_PF_LIVE_ADDR_CHANGE] = "LIVE_ADDR_CHANGE ", [ET_PF_MACVLAN] = "UNICAST_FLT ", [ET_PF_XMIT_DST_RELEASE_PERM] = "XMIT_DST_RELEASE_PERM", [ET_PF_IPVLAN_MASTER] = "IPVLAN_MASTER ", [ET_PF_IPVLAN_SLAVE] = "IPVLAN_SLAVE ", [ET_PF_DUMMY_UNUSED] = "DUMMY_UNUSED ", #if defined(CONFIG_BCM_KF_WANDEV) [ET_PF_WANDEV] = "WANDEV ", #endif #if defined(CONFIG_BCM_KF_VLAN) [ET_PF_BCM_VLAN] = "BCM_VLAN ", #endif #if 1 || defined(CONFIG_BCM_KF_PPP) [ET_PF_PPP] = "PPP ", #endif #if defined(CONFIG_BCM_KF_IP) [ET_PF_EPON_IF] = "EPON_IF ", #endif #if defined(CONFIG_BCM_KF_ENET_SWITCH) [ET_PF_HW_SWITCH] = "HW_SWITCH ", [ET_PF_EXT_SWITCH] = "EXT_SWITCH ", #endif /* CONFIG_BCM_KF_ENET_SWITCH */ }; #define ET_PF_2_IFF(x) (1 << (x)) #define COMPILE_TIME_CHECK(condition) ((void)sizeof(char[1 - 2*(!(condition))])) static int bcm63xx_ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd); static int bcm63xx_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd); static void bcm63xx_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data); static int bcm63xx_get_ethtool_sset_count(struct net_device *dev, int sset); static void bcm63xx_get_ethtool_strings(struct net_device *netdev, u32 stringset, u8 *data); static u32 bcm63xx_ethtool_get_priv_flags(struct net_device *dev); const struct ethtool_ops bcm63xx_enet_ethtool_ops = { .get_settings = bcm63xx_ethtool_get_settings, .set_settings = bcm63xx_ethtool_set_settings, .get_ethtool_stats = bcm63xx_get_ethtool_stats, .get_sset_count = bcm63xx_get_ethtool_sset_count, .get_strings = bcm63xx_get_ethtool_strings, .get_link = ethtool_op_get_link, .get_priv_flags = bcm63xx_ethtool_get_priv_flags, }; static u32 bcm63xx_ethtool_get_priv_flags(struct net_device *dev) { return (u32)(dev->priv_flags); } void bcm63xx_ethtool_dummy(void) { // this function is never invoked. It is being used as a placeholder for the // compile time check COMPILE_TIME_CHECK(ARRAY_SIZE(ethtool_stats_strings) == ET_MAX); // these two should be kept in sync COMPILE_TIME_CHECK(ARRAY_SIZE(ethtool_pflags_strings) == ET_PF_MAX); // these two should be kept in sync COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_802_1Q_VLAN) == IFF_802_1Q_VLAN); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_EBRIDGE) == IFF_EBRIDGE); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_SLAVE_INACTIVE) == IFF_SLAVE_INACTIVE); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_MASTER_8023AD) == IFF_MASTER_8023AD); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_MASTER_ALB) == IFF_MASTER_ALB); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_BONDING) == IFF_BONDING); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_SLAVE_NEEDARP) == IFF_SLAVE_NEEDARP); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_ISATAP) == IFF_ISATAP); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_MASTER_ARPMON) == IFF_MASTER_ARPMON); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_WAN_HDLC) == IFF_WAN_HDLC); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_XMIT_DST_RELEASE) == IFF_XMIT_DST_RELEASE); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_DONT_BRIDGE) == IFF_DONT_BRIDGE); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_DISABLE_NETPOLL) == IFF_DISABLE_NETPOLL); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_MACVLAN_PORT) == IFF_MACVLAN_PORT); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_BRIDGE_PORT) == IFF_BRIDGE_PORT); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_OVS_DATAPATH) == IFF_OVS_DATAPATH); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_TX_SKB_SHARING) == IFF_TX_SKB_SHARING); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_UNICAST_FLT) == IFF_UNICAST_FLT); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_TEAM_PORT) == IFF_TEAM_PORT); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_SUPP_NOFCS) == IFF_SUPP_NOFCS); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_LIVE_ADDR_CHANGE) == IFF_LIVE_ADDR_CHANGE); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_MACVLAN) == IFF_MACVLAN); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_XMIT_DST_RELEASE_PERM) == IFF_XMIT_DST_RELEASE_PERM); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_IPVLAN_MASTER) == IFF_IPVLAN_MASTER); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_IPVLAN_SLAVE) == IFF_IPVLAN_SLAVE); #if defined(CONFIG_BCM_KF_WANDEV) COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_WANDEV) == IFF_WANDEV); #endif #if defined(CONFIG_BCM_KF_VLAN) COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_BCM_VLAN) == IFF_BCM_VLAN); #endif #if defined(CONFIG_BCM_KF_PPP) COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_PPP) == IFF_PPP); #endif #if defined(CONFIG_BCM_KF_IP) COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_EPON_IF) == IFF_EPON_IF); #endif #if defined(CONFIG_BCM_KF_ENET_SWITCH) COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_HW_SWITCH) == IFF_HW_SWITCH); COMPILE_TIME_CHECK(ET_PF_2_IFF(ET_PF_EXT_SWITCH) == IFF_EXT_SWITCH); #endif } static int bcm63xx_ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) { BcmEnet_devctrl *pDevCtrl = netdev_priv(dev); unsigned int bmcr, advertise, ctrl1000; ecmd->supported = SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; ecmd->port = PORT_TP; bmcr = bcm63xx_read_mii(dev, MII_BMCR); advertise = bcm63xx_read_mii(dev, MII_ADVERTISE); ctrl1000 = bcm63xx_read_mii(dev, MII_CTRL1000); if (bmcr & BMCR_ANENABLE) { ecmd->autoneg = AUTONEG_ENABLE; if (advertise & ADVERTISE_10HALF) ecmd->advertising |= ADVERTISED_10baseT_Half; if (advertise & ADVERTISE_10FULL) ecmd->advertising |= ADVERTISED_10baseT_Full; if (advertise & ADVERTISE_100HALF) ecmd->advertising |= ADVERTISED_100baseT_Half; if (advertise & ADVERTISE_100FULL) ecmd->advertising |= ADVERTISED_100baseT_Full; if (ctrl1000 & ADVERTISE_1000HALF) ecmd->advertising |= ADVERTISED_1000baseT_Half; if (ctrl1000 & ADVERTISE_1000FULL) ecmd->advertising |= ADVERTISED_1000baseT_Full; if (advertise & ADVERTISE_PAUSE_ASYM) ecmd->advertising |= ADVERTISED_Asym_Pause; if (advertise & ADVERTISE_PAUSE_CAP) ecmd->advertising |= ADVERTISED_Pause; ecmd->advertising |= (ADVERTISED_TP | ADVERTISED_Autoneg); } else { ecmd->autoneg = AUTONEG_DISABLE; } switch(pDevCtrl->MibInfo.ulIfSpeed) { case SPEED_2500MBIT: ecmd->speed = SPEED_2500; break; case SPEED_1000MBIT: ethtool_cmd_speed_set(ecmd, SPEED_1000); break; case SPEED_100MBIT: ethtool_cmd_speed_set(ecmd, SPEED_100); break; case SPEED_10MBIT: ethtool_cmd_speed_set(ecmd, SPEED_10); break; case 0: ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); break; // it is possible the enet is not fully up yet. // return -1; default: // unsupported speed WARN_ONCE(1, "Unknown ethernet speed (%d)\n",pDevCtrl->MibInfo.ulIfSpeed); return -1; } if (pDevCtrl->MibInfo.ulIfSpeed) { if (pDevCtrl->MibInfo.ulIfDuplex) { ecmd->duplex = DUPLEX_FULL; }else { ecmd->duplex = DUPLEX_HALF; } } else { ecmd->duplex = DUPLEX_UNKNOWN; } return 0; } static int bcm63xx_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) { unsigned int bmcr, advertise, ctrl1000; bmcr = bcm63xx_read_mii(dev, MII_BMCR); bmcr &= ~(BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000 | BMCR_SPEED100); advertise = bcm63xx_read_mii(dev, MII_ADVERTISE); ctrl1000 = bcm63xx_read_mii(dev, MII_CTRL1000); ctrl1000 &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); if(ecmd->advertising & ADVERTISED_1000baseT_Half) ctrl1000 |= ADVERTISE_1000HALF; if(ecmd->advertising & ADVERTISED_1000baseT_Full) ctrl1000 |= ADVERTISE_1000FULL; if (ecmd->autoneg == AUTONEG_ENABLE) { bmcr |= BMCR_ANENABLE | BMCR_ANRESTART; } else { if (ecmd->duplex) bmcr |= BMCR_FULLDPLX; switch (ecmd->speed) { case SPEED_10: break; case SPEED_100: bmcr |= BMCR_SPEED100; break; case SPEED_1000: bmcr |= BMCR_SPEED1000; break; default: break; } } bmcr = bcm63xx_write_mii(dev, MII_BMCR, bmcr); bmcr = bcm63xx_write_mii(dev, MII_ADVERTISE, advertise); bmcr = bcm63xx_write_mii(dev, MII_CTRL1000, ctrl1000); return 0; } static int bcm63xx_get_ethtool_sset_count(struct net_device *dev, int sset) { switch (sset) { case ETH_SS_STATS: return ARRAY_SIZE(ethtool_stats_strings); case ETH_SS_PRIV_FLAGS: return ARRAY_SIZE(ethtool_pflags_strings); default: return -EOPNOTSUPP; } } static void bcm63xx_get_ethtool_strings(struct net_device *netdev, u32 stringset, u8 *data) { switch (stringset) { case ETH_SS_STATS: memcpy(data, *ethtool_stats_strings, sizeof(ethtool_stats_strings)); break; case ETH_SS_PRIV_FLAGS: memcpy(data, *ethtool_pflags_strings, sizeof(ethtool_pflags_strings)); break; } } static void bcm63xx_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { const struct rtnl_link_stats64 *ethStats; struct rtnl_link_stats64 temp; BcmEnet_devctrl *pDevCtrl = netdev_priv(dev); struct rtnl_link_stats64 *swStats = &pDevCtrl->stats; ethStats = dev_get_stats(dev, &temp); data[ET_TX_BYTES] = ethStats->tx_bytes; /* Note: capacity is in bytes per second */ switch(pDevCtrl->MibInfo.ulIfSpeed) { case SPEED_1000MBIT: data[ET_TX_CAPACITY] = 1000000000L/8; break; case SPEED_100MBIT: data[ET_TX_CAPACITY] = 100000000L/8; break; case SPEED_10MBIT: data[ET_TX_CAPACITY] = 10000000L/8; break; case 0: data[ET_TX_CAPACITY] = 0; break; default: data[ET_TX_CAPACITY] = pDevCtrl->MibInfo.ulIfSpeed/8; WARN_ONCE(1, "[%s.%d]: Unrecognized speed for %p (%5s): %d\n", __func__, __LINE__, dev, dev->name, pDevCtrl->MibInfo.ulIfSpeed); break; } data[ET_TX_PACKETS] = ethStats->tx_packets; data[ET_TX_ERRORS] = ethStats->tx_errors; data[ET_RX_BYTES] = ethStats->rx_bytes; data[ET_RX_PACKETS] = ethStats->rx_packets; data[ET_RX_ERRORS] = ethStats->rx_errors; data[MAC_RX_PKTS_GOOD] = ethStats->rx_packets; data[MAC_TX_PKTS_GOOD] = ethStats->tx_packets; data[MAC_RX_BYTES_GOOD] = ethStats->rx_bytes; data[MAC_TX_BYTES_GOOD] = ethStats->tx_bytes; //data[MAC_RX_PKTS_PAUSE] = 0; //gibts nicht //data[MAC_TX_PKTS_PAUSE] = 0; //gibts nicht data[MAC_RX_PKTS_DROPPED] = ethStats->rx_dropped; data[MAC_TX_PKTS_DROPPED] = ethStats->tx_dropped; //data[MAC_RX_BYTES_ERROR] = 0; //gibts nicht data[NETIF_RX_PKTS_GOOD] = swStats->rx_packets; data[NETIF_TX_PKTS_GOOD] = swStats->tx_packets; data[NETIF_RX_BYTES_GOOD] = swStats->rx_bytes; data[NETIF_TX_BYTES_GOOD] = swStats->tx_bytes; data[NETIF_RX_PKTS_DROPPED] = swStats->rx_dropped; data[NETIF_TX_PKTS_DROPPED] = swStats->tx_dropped; data[NETIF_RX_PKTS_ERROR] = swStats->rx_errors; data[NETIF_TX_PKTS_ERROR] = swStats->tx_errors; } #endif // ETHTOOL_SUPPORT