1 /* Applied Micro X-Gene SoC Ethernet Driver 2 * 3 * Copyright (c) 2014, Applied Micro Circuits Corporation 4 * Authors: Iyappan Subramanian <isubramanian@apm.com> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or (at your 9 * option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <linux/ethtool.h> 21 #include "xgene_enet_main.h" 22 23 struct xgene_gstrings_stats { 24 char name[ETH_GSTRING_LEN]; 25 int offset; 26 }; 27 28 #define XGENE_STAT(m) { #m, offsetof(struct xgene_enet_pdata, stats.m) } 29 30 static const struct xgene_gstrings_stats gstrings_stats[] = { 31 XGENE_STAT(rx_packets), 32 XGENE_STAT(tx_packets), 33 XGENE_STAT(rx_bytes), 34 XGENE_STAT(tx_bytes), 35 XGENE_STAT(rx_errors), 36 XGENE_STAT(tx_errors), 37 XGENE_STAT(rx_length_errors), 38 XGENE_STAT(rx_crc_errors), 39 XGENE_STAT(rx_frame_errors), 40 XGENE_STAT(rx_fifo_errors) 41 }; 42 43 #define XGENE_STATS_LEN ARRAY_SIZE(gstrings_stats) 44 45 static void xgene_get_drvinfo(struct net_device *ndev, 46 struct ethtool_drvinfo *info) 47 { 48 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 49 struct platform_device *pdev = pdata->pdev; 50 51 strcpy(info->driver, "xgene_enet"); 52 strcpy(info->version, XGENE_DRV_VERSION); 53 snprintf(info->fw_version, ETHTOOL_FWVERS_LEN, "N/A"); 54 sprintf(info->bus_info, "%s", pdev->name); 55 } 56 57 static int xgene_get_link_ksettings(struct net_device *ndev, 58 struct ethtool_link_ksettings *cmd) 59 { 60 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 61 struct phy_device *phydev = ndev->phydev; 62 u32 supported; 63 64 if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { 65 if (phydev == NULL) 66 return -ENODEV; 67 68 return phy_ethtool_ksettings_get(phydev, cmd); 69 } else if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { 70 if (pdata->mdio_driver) { 71 if (!phydev) 72 return -ENODEV; 73 74 return phy_ethtool_ksettings_get(phydev, cmd); 75 } 76 77 supported = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | 78 SUPPORTED_MII; 79 ethtool_convert_legacy_u32_to_link_mode( 80 cmd->link_modes.supported, 81 supported); 82 ethtool_convert_legacy_u32_to_link_mode( 83 cmd->link_modes.advertising, 84 supported); 85 86 cmd->base.speed = SPEED_1000; 87 cmd->base.duplex = DUPLEX_FULL; 88 cmd->base.port = PORT_MII; 89 cmd->base.autoneg = AUTONEG_ENABLE; 90 } else { 91 supported = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE; 92 ethtool_convert_legacy_u32_to_link_mode( 93 cmd->link_modes.supported, 94 supported); 95 ethtool_convert_legacy_u32_to_link_mode( 96 cmd->link_modes.advertising, 97 supported); 98 99 cmd->base.speed = SPEED_10000; 100 cmd->base.duplex = DUPLEX_FULL; 101 cmd->base.port = PORT_FIBRE; 102 cmd->base.autoneg = AUTONEG_DISABLE; 103 } 104 105 return 0; 106 } 107 108 static int xgene_set_link_ksettings(struct net_device *ndev, 109 const struct ethtool_link_ksettings *cmd) 110 { 111 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 112 struct phy_device *phydev = ndev->phydev; 113 114 if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { 115 if (!phydev) 116 return -ENODEV; 117 118 return phy_ethtool_ksettings_set(phydev, cmd); 119 } 120 121 if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { 122 if (pdata->mdio_driver) { 123 if (!phydev) 124 return -ENODEV; 125 126 return phy_ethtool_ksettings_set(phydev, cmd); 127 } 128 } 129 130 return -EINVAL; 131 } 132 133 static void xgene_get_strings(struct net_device *ndev, u32 stringset, u8 *data) 134 { 135 int i; 136 u8 *p = data; 137 138 if (stringset != ETH_SS_STATS) 139 return; 140 141 for (i = 0; i < XGENE_STATS_LEN; i++) { 142 memcpy(p, gstrings_stats[i].name, ETH_GSTRING_LEN); 143 p += ETH_GSTRING_LEN; 144 } 145 } 146 147 static int xgene_get_sset_count(struct net_device *ndev, int sset) 148 { 149 if (sset != ETH_SS_STATS) 150 return -EINVAL; 151 152 return XGENE_STATS_LEN; 153 } 154 155 static void xgene_get_ethtool_stats(struct net_device *ndev, 156 struct ethtool_stats *dummy, 157 u64 *data) 158 { 159 void *pdata = netdev_priv(ndev); 160 int i; 161 162 for (i = 0; i < XGENE_STATS_LEN; i++) 163 *data++ = *(u64 *)(pdata + gstrings_stats[i].offset); 164 } 165 166 static void xgene_get_pauseparam(struct net_device *ndev, 167 struct ethtool_pauseparam *pp) 168 { 169 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 170 171 pp->autoneg = pdata->pause_autoneg; 172 pp->tx_pause = pdata->tx_pause; 173 pp->rx_pause = pdata->rx_pause; 174 } 175 176 static int xgene_set_pauseparam(struct net_device *ndev, 177 struct ethtool_pauseparam *pp) 178 { 179 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 180 struct phy_device *phydev = ndev->phydev; 181 u32 oldadv, newadv; 182 183 if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII || 184 pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { 185 if (!phydev) 186 return -EINVAL; 187 188 if (!(phydev->supported & SUPPORTED_Pause) || 189 (!(phydev->supported & SUPPORTED_Asym_Pause) && 190 pp->rx_pause != pp->tx_pause)) 191 return -EINVAL; 192 193 pdata->pause_autoneg = pp->autoneg; 194 pdata->tx_pause = pp->tx_pause; 195 pdata->rx_pause = pp->rx_pause; 196 197 oldadv = phydev->advertising; 198 newadv = oldadv & ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); 199 200 if (pp->rx_pause) 201 newadv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause; 202 203 if (pp->tx_pause) 204 newadv ^= ADVERTISED_Asym_Pause; 205 206 if (oldadv ^ newadv) { 207 phydev->advertising = newadv; 208 209 if (phydev->autoneg) 210 return phy_start_aneg(phydev); 211 212 if (!pp->autoneg) { 213 pdata->mac_ops->flowctl_tx(pdata, 214 pdata->tx_pause); 215 pdata->mac_ops->flowctl_rx(pdata, 216 pdata->rx_pause); 217 } 218 } 219 220 } else { 221 if (pp->autoneg) 222 return -EINVAL; 223 224 pdata->tx_pause = pp->tx_pause; 225 pdata->rx_pause = pp->rx_pause; 226 227 pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause); 228 pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause); 229 } 230 231 return 0; 232 } 233 234 static const struct ethtool_ops xgene_ethtool_ops = { 235 .get_drvinfo = xgene_get_drvinfo, 236 .get_link = ethtool_op_get_link, 237 .get_strings = xgene_get_strings, 238 .get_sset_count = xgene_get_sset_count, 239 .get_ethtool_stats = xgene_get_ethtool_stats, 240 .get_link_ksettings = xgene_get_link_ksettings, 241 .set_link_ksettings = xgene_set_link_ksettings, 242 .get_pauseparam = xgene_get_pauseparam, 243 .set_pauseparam = xgene_set_pauseparam 244 }; 245 246 void xgene_enet_set_ethtool_ops(struct net_device *ndev) 247 { 248 ndev->ethtool_ops = &xgene_ethtool_ops; 249 } 250