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_frame_len_err_recov_cntr, DUMP, 0), 68 XGENE_EXTD_STAT(rx_code_err_cntr, RCDE, 16), 69 XGENE_EXTD_STAT(rx_carrier_sense_err_cntr, RCSE, 16), 70 XGENE_EXTD_STAT(rx_undersize_pkt_cntr, RUND, 16), 71 XGENE_EXTD_STAT(rx_oversize_pkt_cntr, ROVR, 16), 72 XGENE_EXTD_STAT(rx_fragments_cntr, RFRG, 16), 73 XGENE_EXTD_STAT(rx_jabber_cntr, RJBR, 16), 74 XGENE_EXTD_STAT(rx_dropped_pkt_cntr, RDRP, 16), 75 XGENE_EXTD_STAT(rx_overrun_cntr, DUMP, 0), 76 XGENE_EXTD_STAT(tx_multicast_pkt_cntr, TMCA, 31), 77 XGENE_EXTD_STAT(tx_broadcast_pkt_cntr, TBCA, 31), 78 XGENE_EXTD_STAT(tx_pause_ctrl_frame_cntr, TXPF, 16), 79 XGENE_EXTD_STAT(tx_defer_pkt_cntr, TDFR, 31), 80 XGENE_EXTD_STAT(tx_excv_defer_pkt_cntr, TEDF, 31), 81 XGENE_EXTD_STAT(tx_single_col_pkt_cntr, TSCL, 31), 82 XGENE_EXTD_STAT(tx_multi_col_pkt_cntr, TMCL, 31), 83 XGENE_EXTD_STAT(tx_late_col_pkt_cntr, TLCL, 31), 84 XGENE_EXTD_STAT(tx_excv_col_pkt_cntr, TXCL, 31), 85 XGENE_EXTD_STAT(tx_total_col_cntr, TNCL, 31), 86 XGENE_EXTD_STAT(tx_pause_frames_hnrd_cntr, TPFH, 16), 87 XGENE_EXTD_STAT(tx_drop_frame_cntr, TDRP, 16), 88 XGENE_EXTD_STAT(tx_jabber_frame_cntr, TJBR, 12), 89 XGENE_EXTD_STAT(tx_fcs_error_cntr, TFCS, 12), 90 XGENE_EXTD_STAT(tx_ctrl_frame_cntr, TXCF, 12), 91 XGENE_EXTD_STAT(tx_oversize_frame_cntr, TOVR, 12), 92 XGENE_EXTD_STAT(tx_undersize_frame_cntr, TUND, 12), 93 XGENE_EXTD_STAT(tx_fragments_cntr, TFRG, 12), 94 XGENE_EXTD_STAT(tx_underrun_cntr, DUMP, 0) 95 }; 96 97 #define XGENE_STATS_LEN ARRAY_SIZE(gstrings_stats) 98 #define XGENE_EXTD_STATS_LEN ARRAY_SIZE(gstrings_extd_stats) 99 #define FALSE_RFLR_IDX 15 100 #define RX_OVERRUN_IDX 23 101 #define TX_UNDERRUN_IDX 42 102 103 static void xgene_get_drvinfo(struct net_device *ndev, 104 struct ethtool_drvinfo *info) 105 { 106 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 107 struct platform_device *pdev = pdata->pdev; 108 109 strcpy(info->driver, "xgene_enet"); 110 strcpy(info->version, XGENE_DRV_VERSION); 111 snprintf(info->fw_version, ETHTOOL_FWVERS_LEN, "N/A"); 112 sprintf(info->bus_info, "%s", pdev->name); 113 } 114 115 static int xgene_get_link_ksettings(struct net_device *ndev, 116 struct ethtool_link_ksettings *cmd) 117 { 118 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 119 struct phy_device *phydev = ndev->phydev; 120 u32 supported; 121 122 if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { 123 if (phydev == NULL) 124 return -ENODEV; 125 126 return phy_ethtool_ksettings_get(phydev, cmd); 127 } else if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { 128 if (pdata->mdio_driver) { 129 if (!phydev) 130 return -ENODEV; 131 132 return phy_ethtool_ksettings_get(phydev, cmd); 133 } 134 135 supported = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | 136 SUPPORTED_MII; 137 ethtool_convert_legacy_u32_to_link_mode( 138 cmd->link_modes.supported, 139 supported); 140 ethtool_convert_legacy_u32_to_link_mode( 141 cmd->link_modes.advertising, 142 supported); 143 144 cmd->base.speed = SPEED_1000; 145 cmd->base.duplex = DUPLEX_FULL; 146 cmd->base.port = PORT_MII; 147 cmd->base.autoneg = AUTONEG_ENABLE; 148 } else { 149 supported = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE; 150 ethtool_convert_legacy_u32_to_link_mode( 151 cmd->link_modes.supported, 152 supported); 153 ethtool_convert_legacy_u32_to_link_mode( 154 cmd->link_modes.advertising, 155 supported); 156 157 cmd->base.speed = SPEED_10000; 158 cmd->base.duplex = DUPLEX_FULL; 159 cmd->base.port = PORT_FIBRE; 160 cmd->base.autoneg = AUTONEG_DISABLE; 161 } 162 163 return 0; 164 } 165 166 static int xgene_set_link_ksettings(struct net_device *ndev, 167 const struct ethtool_link_ksettings *cmd) 168 { 169 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 170 struct phy_device *phydev = ndev->phydev; 171 172 if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { 173 if (!phydev) 174 return -ENODEV; 175 176 return phy_ethtool_ksettings_set(phydev, cmd); 177 } 178 179 if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { 180 if (pdata->mdio_driver) { 181 if (!phydev) 182 return -ENODEV; 183 184 return phy_ethtool_ksettings_set(phydev, cmd); 185 } 186 } 187 188 return -EINVAL; 189 } 190 191 static void xgene_get_strings(struct net_device *ndev, u32 stringset, u8 *data) 192 { 193 int i; 194 u8 *p = data; 195 196 if (stringset != ETH_SS_STATS) 197 return; 198 199 for (i = 0; i < XGENE_STATS_LEN; i++) { 200 memcpy(p, gstrings_stats[i].name, ETH_GSTRING_LEN); 201 p += ETH_GSTRING_LEN; 202 } 203 204 for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) { 205 memcpy(p, gstrings_extd_stats[i].name, ETH_GSTRING_LEN); 206 p += ETH_GSTRING_LEN; 207 } 208 } 209 210 static int xgene_get_sset_count(struct net_device *ndev, int sset) 211 { 212 if (sset != ETH_SS_STATS) 213 return -EINVAL; 214 215 return XGENE_STATS_LEN + XGENE_EXTD_STATS_LEN; 216 } 217 218 static void xgene_get_extd_stats(struct xgene_enet_pdata *pdata) 219 { 220 u32 rx_drop, tx_drop; 221 u32 tmp; 222 int i; 223 224 for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) { 225 tmp = xgene_enet_rd_stat(pdata, gstrings_extd_stats[i].addr); 226 if (gstrings_extd_stats[i].mask) 227 pdata->extd_stats[i] += tmp & 228 GENMASK(gstrings_extd_stats[i].mask - 1, 0); 229 } 230 231 pdata->mac_ops->get_drop_cnt(pdata, &rx_drop, &tx_drop); 232 pdata->extd_stats[RX_OVERRUN_IDX] += rx_drop; 233 pdata->extd_stats[TX_UNDERRUN_IDX] += tx_drop; 234 235 /* Errata 10GE_8 - Update Frame recovered from Errata 10GE_8/ENET_11 */ 236 pdata->extd_stats[FALSE_RFLR_IDX] = pdata->false_rflr; 237 } 238 239 int xgene_extd_stats_init(struct xgene_enet_pdata *pdata) 240 { 241 pdata->extd_stats = devm_kmalloc_array(&pdata->pdev->dev, 242 XGENE_EXTD_STATS_LEN, sizeof(u64), GFP_KERNEL); 243 if (!pdata->extd_stats) 244 return -ENOMEM; 245 246 xgene_get_extd_stats(pdata); 247 memset(pdata->extd_stats, 0, XGENE_EXTD_STATS_LEN * sizeof(u64)); 248 249 return 0; 250 } 251 252 static void xgene_get_ethtool_stats(struct net_device *ndev, 253 struct ethtool_stats *dummy, 254 u64 *data) 255 { 256 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 257 struct rtnl_link_stats64 stats; 258 int i; 259 260 dev_get_stats(ndev, &stats); 261 for (i = 0; i < XGENE_STATS_LEN; i++) 262 data[i] = *(u64 *)((char *)&stats + gstrings_stats[i].offset); 263 264 xgene_get_extd_stats(pdata); 265 for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) 266 data[i + XGENE_STATS_LEN] = pdata->extd_stats[i]; 267 } 268 269 static void xgene_get_pauseparam(struct net_device *ndev, 270 struct ethtool_pauseparam *pp) 271 { 272 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 273 274 pp->autoneg = pdata->pause_autoneg; 275 pp->tx_pause = pdata->tx_pause; 276 pp->rx_pause = pdata->rx_pause; 277 } 278 279 static int xgene_set_pauseparam(struct net_device *ndev, 280 struct ethtool_pauseparam *pp) 281 { 282 struct xgene_enet_pdata *pdata = netdev_priv(ndev); 283 struct phy_device *phydev = ndev->phydev; 284 u32 oldadv, newadv; 285 286 if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII || 287 pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { 288 if (!phydev) 289 return -EINVAL; 290 291 if (!(phydev->supported & SUPPORTED_Pause) || 292 (!(phydev->supported & SUPPORTED_Asym_Pause) && 293 pp->rx_pause != pp->tx_pause)) 294 return -EINVAL; 295 296 pdata->pause_autoneg = pp->autoneg; 297 pdata->tx_pause = pp->tx_pause; 298 pdata->rx_pause = pp->rx_pause; 299 300 oldadv = phydev->advertising; 301 newadv = oldadv & ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); 302 303 if (pp->rx_pause) 304 newadv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause; 305 306 if (pp->tx_pause) 307 newadv ^= ADVERTISED_Asym_Pause; 308 309 if (oldadv ^ newadv) { 310 phydev->advertising = newadv; 311 312 if (phydev->autoneg) 313 return phy_start_aneg(phydev); 314 315 if (!pp->autoneg) { 316 pdata->mac_ops->flowctl_tx(pdata, 317 pdata->tx_pause); 318 pdata->mac_ops->flowctl_rx(pdata, 319 pdata->rx_pause); 320 } 321 } 322 323 } else { 324 if (pp->autoneg) 325 return -EINVAL; 326 327 pdata->tx_pause = pp->tx_pause; 328 pdata->rx_pause = pp->rx_pause; 329 330 pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause); 331 pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause); 332 } 333 334 return 0; 335 } 336 337 static const struct ethtool_ops xgene_ethtool_ops = { 338 .get_drvinfo = xgene_get_drvinfo, 339 .get_link = ethtool_op_get_link, 340 .get_strings = xgene_get_strings, 341 .get_sset_count = xgene_get_sset_count, 342 .get_ethtool_stats = xgene_get_ethtool_stats, 343 .get_link_ksettings = xgene_get_link_ksettings, 344 .set_link_ksettings = xgene_set_link_ksettings, 345 .get_pauseparam = xgene_get_pauseparam, 346 .set_pauseparam = xgene_set_pauseparam 347 }; 348 349 void xgene_enet_set_ethtool_ops(struct net_device *ndev) 350 { 351 ndev->ethtool_ops = &xgene_ethtool_ops; 352 } 353