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 u32 addr; 27 u32 mask; 28 }; 29 30 #define XGENE_STAT(m) { #m, offsetof(struct rtnl_link_stats64, m) } 31 #define XGENE_EXTD_STAT(s, a, m) \ 32 { \ 33 .name = #s, \ 34 .addr = a ## _ADDR, \ 35 .mask = m \ 36 } 37 38 static const struct xgene_gstrings_stats gstrings_stats[] = { 39 XGENE_STAT(rx_packets), 40 XGENE_STAT(tx_packets), 41 XGENE_STAT(rx_bytes), 42 XGENE_STAT(tx_bytes), 43 XGENE_STAT(rx_errors), 44 XGENE_STAT(tx_errors), 45 XGENE_STAT(rx_length_errors), 46 XGENE_STAT(rx_crc_errors), 47 XGENE_STAT(rx_frame_errors), 48 XGENE_STAT(rx_fifo_errors) 49 }; 50 51 static const struct xgene_gstrings_stats gstrings_extd_stats[] = { 52 XGENE_EXTD_STAT(tx_rx_64b_frame_cntr, TR64, 31), 53 XGENE_EXTD_STAT(tx_rx_127b_frame_cntr, TR127, 31), 54 XGENE_EXTD_STAT(tx_rx_255b_frame_cntr, TR255, 31), 55 XGENE_EXTD_STAT(tx_rx_511b_frame_cntr, TR511, 31), 56 XGENE_EXTD_STAT(tx_rx_1023b_frame_cntr, TR1K, 31), 57 XGENE_EXTD_STAT(tx_rx_1518b_frame_cntr, TRMAX, 31), 58 XGENE_EXTD_STAT(tx_rx_1522b_frame_cntr, TRMGV, 31), 59 XGENE_EXTD_STAT(rx_fcs_error_cntr, RFCS, 16), 60 XGENE_EXTD_STAT(rx_multicast_pkt_cntr, RMCA, 31), 61 XGENE_EXTD_STAT(rx_broadcast_pkt_cntr, RBCA, 31), 62 XGENE_EXTD_STAT(rx_ctrl_frame_pkt_cntr, RXCF, 16), 63 XGENE_EXTD_STAT(rx_pause_frame_pkt_cntr, RXPF, 16), 64 XGENE_EXTD_STAT(rx_unk_opcode_cntr, RXUO, 16), 65 XGENE_EXTD_STAT(rx_align_err_cntr, RALN, 16), 66 XGENE_EXTD_STAT(rx_frame_len_err_cntr, RFLR, 16), 67 XGENE_EXTD_STAT(rx_code_err_cntr, RCDE, 16), 68 XGENE_EXTD_STAT(rx_carrier_sense_err_cntr, RCSE, 16), 69 XGENE_EXTD_STAT(rx_undersize_pkt_cntr, RUND, 16), 70 XGENE_EXTD_STAT(rx_oversize_pkt_cntr, ROVR, 16), 71 XGENE_EXTD_STAT(rx_fragments_cntr, RFRG, 16), 72 XGENE_EXTD_STAT(rx_jabber_cntr, RJBR, 16), 73 XGENE_EXTD_STAT(rx_dropped_pkt_cntr, RDRP, 16), 74 XGENE_EXTD_STAT(tx_multicast_pkt_cntr, TMCA, 31), 75 XGENE_EXTD_STAT(tx_broadcast_pkt_cntr, TBCA, 31), 76 XGENE_EXTD_STAT(tx_pause_ctrl_frame_cntr, TXPF, 16), 77 XGENE_EXTD_STAT(tx_defer_pkt_cntr, TDFR, 31), 78 XGENE_EXTD_STAT(tx_excv_defer_pkt_cntr, TEDF, 31), 79 XGENE_EXTD_STAT(tx_single_col_pkt_cntr, TSCL, 31), 80 XGENE_EXTD_STAT(tx_multi_col_pkt_cntr, TMCL, 31), 81 XGENE_EXTD_STAT(tx_late_col_pkt_cntr, TLCL, 31), 82 XGENE_EXTD_STAT(tx_excv_col_pkt_cntr, TXCL, 31), 83 XGENE_EXTD_STAT(tx_total_col_cntr, TNCL, 31), 84 XGENE_EXTD_STAT(tx_pause_frames_hnrd_cntr, TPFH, 16), 85 XGENE_EXTD_STAT(tx_drop_frame_cntr, TDRP, 16), 86 XGENE_EXTD_STAT(tx_jabber_frame_cntr, TJBR, 12), 87 XGENE_EXTD_STAT(tx_fcs_error_cntr, TFCS, 12), 88 XGENE_EXTD_STAT(tx_ctrl_frame_cntr, TXCF, 12), 89 XGENE_EXTD_STAT(tx_oversize_frame_cntr, TOVR, 12), 90 XGENE_EXTD_STAT(tx_undersize_frame_cntr, TUND, 12), 91 XGENE_EXTD_STAT(tx_fragments_cntr, TFRG, 12) 92 }; 93 94 #define XGENE_STATS_LEN ARRAY_SIZE(gstrings_stats) 95 #define XGENE_EXTD_STATS_LEN ARRAY_SIZE(gstrings_extd_stats) 96 97 static void xgene_get_drvinfo(struct net_device *ndev, 98 struct ethtool_drvinfo *info) 99 { 100 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 101 struct platform_device *pdev = pdata->pdev; 102 103 strcpy(info->driver, "xgene_enet"); 104 strcpy(info->version, XGENE_DRV_VERSION); 105 snprintf(info->fw_version, ETHTOOL_FWVERS_LEN, "N/A"); 106 sprintf(info->bus_info, "%s", pdev->name); 107 } 108 109 static int xgene_get_link_ksettings(struct net_device *ndev, 110 struct ethtool_link_ksettings *cmd) 111 { 112 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 113 struct phy_device *phydev = ndev->phydev; 114 u32 supported; 115 116 if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { 117 if (phydev == NULL) 118 return -ENODEV; 119 120 return phy_ethtool_ksettings_get(phydev, cmd); 121 } else 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_get(phydev, cmd); 127 } 128 129 supported = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | 130 SUPPORTED_MII; 131 ethtool_convert_legacy_u32_to_link_mode( 132 cmd->link_modes.supported, 133 supported); 134 ethtool_convert_legacy_u32_to_link_mode( 135 cmd->link_modes.advertising, 136 supported); 137 138 cmd->base.speed = SPEED_1000; 139 cmd->base.duplex = DUPLEX_FULL; 140 cmd->base.port = PORT_MII; 141 cmd->base.autoneg = AUTONEG_ENABLE; 142 } else { 143 supported = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE; 144 ethtool_convert_legacy_u32_to_link_mode( 145 cmd->link_modes.supported, 146 supported); 147 ethtool_convert_legacy_u32_to_link_mode( 148 cmd->link_modes.advertising, 149 supported); 150 151 cmd->base.speed = SPEED_10000; 152 cmd->base.duplex = DUPLEX_FULL; 153 cmd->base.port = PORT_FIBRE; 154 cmd->base.autoneg = AUTONEG_DISABLE; 155 } 156 157 return 0; 158 } 159 160 static int xgene_set_link_ksettings(struct net_device *ndev, 161 const struct ethtool_link_ksettings *cmd) 162 { 163 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 164 struct phy_device *phydev = ndev->phydev; 165 166 if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { 167 if (!phydev) 168 return -ENODEV; 169 170 return phy_ethtool_ksettings_set(phydev, cmd); 171 } 172 173 if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { 174 if (pdata->mdio_driver) { 175 if (!phydev) 176 return -ENODEV; 177 178 return phy_ethtool_ksettings_set(phydev, cmd); 179 } 180 } 181 182 return -EINVAL; 183 } 184 185 static void xgene_get_strings(struct net_device *ndev, u32 stringset, u8 *data) 186 { 187 int i; 188 u8 *p = data; 189 190 if (stringset != ETH_SS_STATS) 191 return; 192 193 for (i = 0; i < XGENE_STATS_LEN; i++) { 194 memcpy(p, gstrings_stats[i].name, ETH_GSTRING_LEN); 195 p += ETH_GSTRING_LEN; 196 } 197 198 for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) { 199 memcpy(p, gstrings_extd_stats[i].name, ETH_GSTRING_LEN); 200 p += ETH_GSTRING_LEN; 201 } 202 } 203 204 static int xgene_get_sset_count(struct net_device *ndev, int sset) 205 { 206 if (sset != ETH_SS_STATS) 207 return -EINVAL; 208 209 return XGENE_STATS_LEN + XGENE_EXTD_STATS_LEN; 210 } 211 212 static void xgene_get_extd_stats(struct xgene_enet_pdata *pdata) 213 { 214 u32 tmp; 215 int i; 216 217 for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) { 218 tmp = xgene_enet_rd_stat(pdata, gstrings_extd_stats[i].addr); 219 pdata->extd_stats[i] += tmp & 220 GENMASK(gstrings_extd_stats[i].mask - 1, 0); 221 } 222 } 223 224 int xgene_extd_stats_init(struct xgene_enet_pdata *pdata) 225 { 226 pdata->extd_stats = devm_kmalloc_array(&pdata->pdev->dev, 227 XGENE_EXTD_STATS_LEN, sizeof(u64), GFP_KERNEL); 228 if (!pdata->extd_stats) 229 return -ENOMEM; 230 231 xgene_get_extd_stats(pdata); 232 memset(pdata->extd_stats, 0, XGENE_EXTD_STATS_LEN * sizeof(u64)); 233 234 return 0; 235 } 236 237 static void xgene_get_ethtool_stats(struct net_device *ndev, 238 struct ethtool_stats *dummy, 239 u64 *data) 240 { 241 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 242 struct rtnl_link_stats64 stats; 243 int i; 244 245 dev_get_stats(ndev, &stats); 246 for (i = 0; i < XGENE_STATS_LEN; i++) 247 data[i] = *(u64 *)((char *)&stats + gstrings_stats[i].offset); 248 249 xgene_get_extd_stats(pdata); 250 for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) 251 data[i + XGENE_STATS_LEN] = pdata->extd_stats[i]; 252 } 253 254 static void xgene_get_pauseparam(struct net_device *ndev, 255 struct ethtool_pauseparam *pp) 256 { 257 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 258 259 pp->autoneg = pdata->pause_autoneg; 260 pp->tx_pause = pdata->tx_pause; 261 pp->rx_pause = pdata->rx_pause; 262 } 263 264 static int xgene_set_pauseparam(struct net_device *ndev, 265 struct ethtool_pauseparam *pp) 266 { 267 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 268 struct phy_device *phydev = ndev->phydev; 269 u32 oldadv, newadv; 270 271 if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII || 272 pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { 273 if (!phydev) 274 return -EINVAL; 275 276 if (!(phydev->supported & SUPPORTED_Pause) || 277 (!(phydev->supported & SUPPORTED_Asym_Pause) && 278 pp->rx_pause != pp->tx_pause)) 279 return -EINVAL; 280 281 pdata->pause_autoneg = pp->autoneg; 282 pdata->tx_pause = pp->tx_pause; 283 pdata->rx_pause = pp->rx_pause; 284 285 oldadv = phydev->advertising; 286 newadv = oldadv & ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); 287 288 if (pp->rx_pause) 289 newadv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause; 290 291 if (pp->tx_pause) 292 newadv ^= ADVERTISED_Asym_Pause; 293 294 if (oldadv ^ newadv) { 295 phydev->advertising = newadv; 296 297 if (phydev->autoneg) 298 return phy_start_aneg(phydev); 299 300 if (!pp->autoneg) { 301 pdata->mac_ops->flowctl_tx(pdata, 302 pdata->tx_pause); 303 pdata->mac_ops->flowctl_rx(pdata, 304 pdata->rx_pause); 305 } 306 } 307 308 } else { 309 if (pp->autoneg) 310 return -EINVAL; 311 312 pdata->tx_pause = pp->tx_pause; 313 pdata->rx_pause = pp->rx_pause; 314 315 pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause); 316 pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause); 317 } 318 319 return 0; 320 } 321 322 static const struct ethtool_ops xgene_ethtool_ops = { 323 .get_drvinfo = xgene_get_drvinfo, 324 .get_link = ethtool_op_get_link, 325 .get_strings = xgene_get_strings, 326 .get_sset_count = xgene_get_sset_count, 327 .get_ethtool_stats = xgene_get_ethtool_stats, 328 .get_link_ksettings = xgene_get_link_ksettings, 329 .set_link_ksettings = xgene_set_link_ksettings, 330 .get_pauseparam = xgene_get_pauseparam, 331 .set_pauseparam = xgene_set_pauseparam 332 }; 333 334 void xgene_enet_set_ethtool_ops(struct net_device *ndev) 335 { 336 ndev->ethtool_ops = &xgene_ethtool_ops; 337 } 338