Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 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 | |
Philippe Reynes | 36a19b2 | 2016-09-11 17:54:04 +0200 | [diff] [blame] | 57 | static int xgene_get_link_ksettings(struct net_device *ndev, |
| 58 | struct ethtool_link_ksettings *cmd) |
Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 59 | { |
| 60 | struct xgene_enet_pdata *pdata = netdev_priv(ndev); |
Philippe Reynes | 971d3a4 | 2016-09-11 17:54:03 +0200 | [diff] [blame] | 61 | struct phy_device *phydev = ndev->phydev; |
Philippe Reynes | 36a19b2 | 2016-09-11 17:54:04 +0200 | [diff] [blame] | 62 | u32 supported; |
Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 63 | |
Iyappan Subramanian | 41aace6 | 2014-10-09 18:32:07 -0700 | [diff] [blame] | 64 | if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { |
| 65 | if (phydev == NULL) |
| 66 | return -ENODEV; |
Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 67 | |
Philippe Reynes | 36a19b2 | 2016-09-11 17:54:04 +0200 | [diff] [blame] | 68 | return phy_ethtool_ksettings_get(phydev, cmd); |
Iyappan Subramanian | 5e6a024 | 2014-10-13 17:05:35 -0700 | [diff] [blame] | 69 | } else if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { |
Iyappan Subramanian | 52d1fd9 | 2016-07-25 17:12:44 -0700 | [diff] [blame] | 70 | if (pdata->mdio_driver) { |
| 71 | if (!phydev) |
| 72 | return -ENODEV; |
| 73 | |
Philippe Reynes | 36a19b2 | 2016-09-11 17:54:04 +0200 | [diff] [blame] | 74 | return phy_ethtool_ksettings_get(phydev, cmd); |
Iyappan Subramanian | 52d1fd9 | 2016-07-25 17:12:44 -0700 | [diff] [blame] | 75 | } |
| 76 | |
Philippe Reynes | 36a19b2 | 2016-09-11 17:54:04 +0200 | [diff] [blame] | 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; |
Iyappan Subramanian | 5e6a024 | 2014-10-13 17:05:35 -0700 | [diff] [blame] | 90 | } else { |
Philippe Reynes | 36a19b2 | 2016-09-11 17:54:04 +0200 | [diff] [blame] | 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; |
Iyappan Subramanian | 41aace6 | 2014-10-09 18:32:07 -0700 | [diff] [blame] | 103 | } |
| 104 | |
Iyappan Subramanian | 41aace6 | 2014-10-09 18:32:07 -0700 | [diff] [blame] | 105 | return 0; |
Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 106 | } |
| 107 | |
Philippe Reynes | 36a19b2 | 2016-09-11 17:54:04 +0200 | [diff] [blame] | 108 | static int xgene_set_link_ksettings(struct net_device *ndev, |
| 109 | const struct ethtool_link_ksettings *cmd) |
Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 110 | { |
| 111 | struct xgene_enet_pdata *pdata = netdev_priv(ndev); |
Philippe Reynes | 971d3a4 | 2016-09-11 17:54:03 +0200 | [diff] [blame] | 112 | struct phy_device *phydev = ndev->phydev; |
Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 113 | |
Iyappan Subramanian | 41aace6 | 2014-10-09 18:32:07 -0700 | [diff] [blame] | 114 | if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { |
Iyappan Subramanian | 52d1fd9 | 2016-07-25 17:12:44 -0700 | [diff] [blame] | 115 | if (!phydev) |
Iyappan Subramanian | 41aace6 | 2014-10-09 18:32:07 -0700 | [diff] [blame] | 116 | return -ENODEV; |
Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 117 | |
Philippe Reynes | 36a19b2 | 2016-09-11 17:54:04 +0200 | [diff] [blame] | 118 | return phy_ethtool_ksettings_set(phydev, cmd); |
Iyappan Subramanian | 41aace6 | 2014-10-09 18:32:07 -0700 | [diff] [blame] | 119 | } |
| 120 | |
Iyappan Subramanian | 52d1fd9 | 2016-07-25 17:12:44 -0700 | [diff] [blame] | 121 | if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { |
| 122 | if (pdata->mdio_driver) { |
| 123 | if (!phydev) |
| 124 | return -ENODEV; |
| 125 | |
Philippe Reynes | 36a19b2 | 2016-09-11 17:54:04 +0200 | [diff] [blame] | 126 | return phy_ethtool_ksettings_set(phydev, cmd); |
Iyappan Subramanian | 52d1fd9 | 2016-07-25 17:12:44 -0700 | [diff] [blame] | 127 | } |
| 128 | } |
| 129 | |
Iyappan Subramanian | 41aace6 | 2014-10-09 18:32:07 -0700 | [diff] [blame] | 130 | return -EINVAL; |
Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 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 const struct ethtool_ops xgene_ethtool_ops = { |
| 167 | .get_drvinfo = xgene_get_drvinfo, |
Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 168 | .get_link = ethtool_op_get_link, |
| 169 | .get_strings = xgene_get_strings, |
| 170 | .get_sset_count = xgene_get_sset_count, |
Philippe Reynes | 36a19b2 | 2016-09-11 17:54:04 +0200 | [diff] [blame] | 171 | .get_ethtool_stats = xgene_get_ethtool_stats, |
| 172 | .get_link_ksettings = xgene_get_link_ksettings, |
| 173 | .set_link_ksettings = xgene_set_link_ksettings, |
Iyappan Subramanian | e6ad767 | 2014-08-07 15:14:28 -0700 | [diff] [blame] | 174 | }; |
| 175 | |
| 176 | void xgene_enet_set_ethtool_ops(struct net_device *ndev) |
| 177 | { |
| 178 | ndev->ethtool_ops = &xgene_ethtool_ops; |
| 179 | } |