Jie Deng | d4d49bc | 2017-04-12 13:10:06 +0800 | [diff] [blame] | 1 | /* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver |
| 2 | * |
| 3 | * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) |
| 4 | * |
| 5 | * This program is dual-licensed; you may select either version 2 of |
| 6 | * the GNU General Public License ("GPL") or BSD license ("BSD"). |
| 7 | * |
| 8 | * This Synopsys DWC XLGMAC software driver and associated documentation |
| 9 | * (hereinafter the "Software") is an unsupported proprietary work of |
| 10 | * Synopsys, Inc. unless otherwise expressly agreed to in writing between |
| 11 | * Synopsys and you. The Software IS NOT an item of Licensed Software or a |
| 12 | * Licensed Product under any End User Software License Agreement or |
| 13 | * Agreement for Licensed Products with Synopsys or any supplement thereto. |
| 14 | * Synopsys is a registered trademark of Synopsys, Inc. Other names included |
| 15 | * in the SOFTWARE may be the trademarks of their respective owners. |
| 16 | */ |
| 17 | |
| 18 | #include <linux/ethtool.h> |
| 19 | #include <linux/kernel.h> |
| 20 | #include <linux/netdevice.h> |
| 21 | |
| 22 | #include "dwc-xlgmac.h" |
| 23 | #include "dwc-xlgmac-reg.h" |
| 24 | |
| 25 | struct xlgmac_stats_desc { |
| 26 | char stat_string[ETH_GSTRING_LEN]; |
| 27 | int stat_offset; |
| 28 | }; |
| 29 | |
| 30 | #define XLGMAC_STAT(str, var) \ |
| 31 | { \ |
| 32 | str, \ |
| 33 | offsetof(struct xlgmac_pdata, stats.var), \ |
| 34 | } |
| 35 | |
| 36 | static const struct xlgmac_stats_desc xlgmac_gstring_stats[] = { |
| 37 | /* MMC TX counters */ |
| 38 | XLGMAC_STAT("tx_bytes", txoctetcount_gb), |
| 39 | XLGMAC_STAT("tx_bytes_good", txoctetcount_g), |
| 40 | XLGMAC_STAT("tx_packets", txframecount_gb), |
| 41 | XLGMAC_STAT("tx_packets_good", txframecount_g), |
| 42 | XLGMAC_STAT("tx_unicast_packets", txunicastframes_gb), |
| 43 | XLGMAC_STAT("tx_broadcast_packets", txbroadcastframes_gb), |
| 44 | XLGMAC_STAT("tx_broadcast_packets_good", txbroadcastframes_g), |
| 45 | XLGMAC_STAT("tx_multicast_packets", txmulticastframes_gb), |
| 46 | XLGMAC_STAT("tx_multicast_packets_good", txmulticastframes_g), |
| 47 | XLGMAC_STAT("tx_vlan_packets_good", txvlanframes_g), |
| 48 | XLGMAC_STAT("tx_64_byte_packets", tx64octets_gb), |
| 49 | XLGMAC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb), |
| 50 | XLGMAC_STAT("tx_128_to_255_byte_packets", tx128to255octets_gb), |
| 51 | XLGMAC_STAT("tx_256_to_511_byte_packets", tx256to511octets_gb), |
| 52 | XLGMAC_STAT("tx_512_to_1023_byte_packets", tx512to1023octets_gb), |
| 53 | XLGMAC_STAT("tx_1024_to_max_byte_packets", tx1024tomaxoctets_gb), |
| 54 | XLGMAC_STAT("tx_underflow_errors", txunderflowerror), |
| 55 | XLGMAC_STAT("tx_pause_frames", txpauseframes), |
| 56 | |
| 57 | /* MMC RX counters */ |
| 58 | XLGMAC_STAT("rx_bytes", rxoctetcount_gb), |
| 59 | XLGMAC_STAT("rx_bytes_good", rxoctetcount_g), |
| 60 | XLGMAC_STAT("rx_packets", rxframecount_gb), |
| 61 | XLGMAC_STAT("rx_unicast_packets_good", rxunicastframes_g), |
| 62 | XLGMAC_STAT("rx_broadcast_packets_good", rxbroadcastframes_g), |
| 63 | XLGMAC_STAT("rx_multicast_packets_good", rxmulticastframes_g), |
| 64 | XLGMAC_STAT("rx_vlan_packets", rxvlanframes_gb), |
| 65 | XLGMAC_STAT("rx_64_byte_packets", rx64octets_gb), |
| 66 | XLGMAC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb), |
| 67 | XLGMAC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb), |
| 68 | XLGMAC_STAT("rx_256_to_511_byte_packets", rx256to511octets_gb), |
| 69 | XLGMAC_STAT("rx_512_to_1023_byte_packets", rx512to1023octets_gb), |
| 70 | XLGMAC_STAT("rx_1024_to_max_byte_packets", rx1024tomaxoctets_gb), |
| 71 | XLGMAC_STAT("rx_undersize_packets_good", rxundersize_g), |
| 72 | XLGMAC_STAT("rx_oversize_packets_good", rxoversize_g), |
| 73 | XLGMAC_STAT("rx_crc_errors", rxcrcerror), |
| 74 | XLGMAC_STAT("rx_crc_errors_small_packets", rxrunterror), |
| 75 | XLGMAC_STAT("rx_crc_errors_giant_packets", rxjabbererror), |
| 76 | XLGMAC_STAT("rx_length_errors", rxlengtherror), |
| 77 | XLGMAC_STAT("rx_out_of_range_errors", rxoutofrangetype), |
| 78 | XLGMAC_STAT("rx_fifo_overflow_errors", rxfifooverflow), |
| 79 | XLGMAC_STAT("rx_watchdog_errors", rxwatchdogerror), |
| 80 | XLGMAC_STAT("rx_pause_frames", rxpauseframes), |
| 81 | |
| 82 | /* Extra counters */ |
| 83 | XLGMAC_STAT("tx_tso_packets", tx_tso_packets), |
| 84 | XLGMAC_STAT("rx_split_header_packets", rx_split_header_packets), |
| 85 | XLGMAC_STAT("tx_process_stopped", tx_process_stopped), |
| 86 | XLGMAC_STAT("rx_process_stopped", rx_process_stopped), |
| 87 | XLGMAC_STAT("tx_buffer_unavailable", tx_buffer_unavailable), |
| 88 | XLGMAC_STAT("rx_buffer_unavailable", rx_buffer_unavailable), |
| 89 | XLGMAC_STAT("fatal_bus_error", fatal_bus_error), |
| 90 | XLGMAC_STAT("tx_vlan_packets", tx_vlan_packets), |
| 91 | XLGMAC_STAT("rx_vlan_packets", rx_vlan_packets), |
| 92 | XLGMAC_STAT("napi_poll_isr", napi_poll_isr), |
| 93 | XLGMAC_STAT("napi_poll_txtimer", napi_poll_txtimer), |
| 94 | }; |
| 95 | |
| 96 | #define XLGMAC_STATS_COUNT ARRAY_SIZE(xlgmac_gstring_stats) |
| 97 | |
| 98 | static void xlgmac_ethtool_get_drvinfo(struct net_device *netdev, |
| 99 | struct ethtool_drvinfo *drvinfo) |
| 100 | { |
| 101 | struct xlgmac_pdata *pdata = netdev_priv(netdev); |
| 102 | u32 ver = pdata->hw_feat.version; |
| 103 | u32 snpsver, devid, userver; |
| 104 | |
| 105 | strlcpy(drvinfo->driver, pdata->drv_name, sizeof(drvinfo->driver)); |
| 106 | strlcpy(drvinfo->version, pdata->drv_ver, sizeof(drvinfo->version)); |
| 107 | strlcpy(drvinfo->bus_info, dev_name(pdata->dev), |
| 108 | sizeof(drvinfo->bus_info)); |
| 109 | /* S|SNPSVER: Synopsys-defined Version |
| 110 | * D|DEVID: Indicates the Device family |
| 111 | * U|USERVER: User-defined Version |
| 112 | */ |
| 113 | snpsver = XLGMAC_GET_REG_BITS(ver, MAC_VR_SNPSVER_POS, |
| 114 | MAC_VR_SNPSVER_LEN); |
| 115 | devid = XLGMAC_GET_REG_BITS(ver, MAC_VR_DEVID_POS, |
| 116 | MAC_VR_DEVID_LEN); |
| 117 | userver = XLGMAC_GET_REG_BITS(ver, MAC_VR_USERVER_POS, |
| 118 | MAC_VR_USERVER_LEN); |
| 119 | snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), |
| 120 | "S.D.U: %x.%x.%x", snpsver, devid, userver); |
| 121 | } |
| 122 | |
| 123 | static u32 xlgmac_ethtool_get_msglevel(struct net_device *netdev) |
| 124 | { |
| 125 | struct xlgmac_pdata *pdata = netdev_priv(netdev); |
| 126 | |
| 127 | return pdata->msg_enable; |
| 128 | } |
| 129 | |
| 130 | static void xlgmac_ethtool_set_msglevel(struct net_device *netdev, |
| 131 | u32 msglevel) |
| 132 | { |
| 133 | struct xlgmac_pdata *pdata = netdev_priv(netdev); |
| 134 | |
| 135 | pdata->msg_enable = msglevel; |
| 136 | } |
| 137 | |
| 138 | static void xlgmac_ethtool_get_channels(struct net_device *netdev, |
| 139 | struct ethtool_channels *channel) |
| 140 | { |
| 141 | struct xlgmac_pdata *pdata = netdev_priv(netdev); |
| 142 | |
| 143 | channel->max_rx = XLGMAC_MAX_DMA_CHANNELS; |
| 144 | channel->max_tx = XLGMAC_MAX_DMA_CHANNELS; |
| 145 | channel->rx_count = pdata->rx_q_count; |
| 146 | channel->tx_count = pdata->tx_q_count; |
| 147 | } |
| 148 | |
| 149 | static int xlgmac_ethtool_get_coalesce(struct net_device *netdev, |
| 150 | struct ethtool_coalesce *ec) |
| 151 | { |
| 152 | struct xlgmac_pdata *pdata = netdev_priv(netdev); |
| 153 | |
| 154 | memset(ec, 0, sizeof(struct ethtool_coalesce)); |
| 155 | ec->rx_coalesce_usecs = pdata->rx_usecs; |
| 156 | ec->rx_max_coalesced_frames = pdata->rx_frames; |
| 157 | ec->tx_max_coalesced_frames = pdata->tx_frames; |
| 158 | |
| 159 | return 0; |
| 160 | } |
| 161 | |
| 162 | static int xlgmac_ethtool_set_coalesce(struct net_device *netdev, |
| 163 | struct ethtool_coalesce *ec) |
| 164 | { |
| 165 | struct xlgmac_pdata *pdata = netdev_priv(netdev); |
| 166 | struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; |
| 167 | unsigned int rx_frames, rx_riwt, rx_usecs; |
| 168 | unsigned int tx_frames; |
| 169 | |
| 170 | /* Check for not supported parameters */ |
| 171 | if ((ec->rx_coalesce_usecs_irq) || (ec->rx_max_coalesced_frames_irq) || |
| 172 | (ec->tx_coalesce_usecs) || (ec->tx_coalesce_usecs_high) || |
| 173 | (ec->tx_max_coalesced_frames_irq) || (ec->tx_coalesce_usecs_irq) || |
| 174 | (ec->stats_block_coalesce_usecs) || (ec->pkt_rate_low) || |
| 175 | (ec->use_adaptive_rx_coalesce) || (ec->use_adaptive_tx_coalesce) || |
| 176 | (ec->rx_max_coalesced_frames_low) || (ec->rx_coalesce_usecs_low) || |
| 177 | (ec->tx_coalesce_usecs_low) || (ec->tx_max_coalesced_frames_low) || |
| 178 | (ec->pkt_rate_high) || (ec->rx_coalesce_usecs_high) || |
| 179 | (ec->rx_max_coalesced_frames_high) || |
| 180 | (ec->tx_max_coalesced_frames_high) || |
| 181 | (ec->rate_sample_interval)) |
| 182 | return -EOPNOTSUPP; |
| 183 | |
| 184 | rx_usecs = ec->rx_coalesce_usecs; |
| 185 | rx_riwt = hw_ops->usec_to_riwt(pdata, rx_usecs); |
| 186 | rx_frames = ec->rx_max_coalesced_frames; |
| 187 | tx_frames = ec->tx_max_coalesced_frames; |
| 188 | |
| 189 | if ((rx_riwt > XLGMAC_MAX_DMA_RIWT) || |
| 190 | (rx_riwt < XLGMAC_MIN_DMA_RIWT) || |
| 191 | (rx_frames > pdata->rx_desc_count)) |
| 192 | return -EINVAL; |
| 193 | |
| 194 | if (tx_frames > pdata->tx_desc_count) |
| 195 | return -EINVAL; |
| 196 | |
| 197 | pdata->rx_riwt = rx_riwt; |
| 198 | pdata->rx_usecs = rx_usecs; |
| 199 | pdata->rx_frames = rx_frames; |
| 200 | hw_ops->config_rx_coalesce(pdata); |
| 201 | |
| 202 | pdata->tx_frames = tx_frames; |
| 203 | hw_ops->config_tx_coalesce(pdata); |
| 204 | |
| 205 | return 0; |
| 206 | } |
| 207 | |
| 208 | static void xlgmac_ethtool_get_strings(struct net_device *netdev, |
| 209 | u32 stringset, u8 *data) |
| 210 | { |
| 211 | int i; |
| 212 | |
| 213 | switch (stringset) { |
| 214 | case ETH_SS_STATS: |
| 215 | for (i = 0; i < XLGMAC_STATS_COUNT; i++) { |
| 216 | memcpy(data, xlgmac_gstring_stats[i].stat_string, |
| 217 | ETH_GSTRING_LEN); |
| 218 | data += ETH_GSTRING_LEN; |
| 219 | } |
| 220 | break; |
| 221 | default: |
| 222 | WARN_ON(1); |
| 223 | break; |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | static int xlgmac_ethtool_get_sset_count(struct net_device *netdev, |
| 228 | int stringset) |
| 229 | { |
| 230 | int ret; |
| 231 | |
| 232 | switch (stringset) { |
| 233 | case ETH_SS_STATS: |
| 234 | ret = XLGMAC_STATS_COUNT; |
| 235 | break; |
| 236 | |
| 237 | default: |
| 238 | ret = -EOPNOTSUPP; |
| 239 | } |
| 240 | |
| 241 | return ret; |
| 242 | } |
| 243 | |
| 244 | static void xlgmac_ethtool_get_ethtool_stats(struct net_device *netdev, |
| 245 | struct ethtool_stats *stats, |
| 246 | u64 *data) |
| 247 | { |
| 248 | struct xlgmac_pdata *pdata = netdev_priv(netdev); |
| 249 | u8 *stat; |
| 250 | int i; |
| 251 | |
| 252 | pdata->hw_ops.read_mmc_stats(pdata); |
| 253 | for (i = 0; i < XLGMAC_STATS_COUNT; i++) { |
| 254 | stat = (u8 *)pdata + xlgmac_gstring_stats[i].stat_offset; |
| 255 | *data++ = *(u64 *)stat; |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | static const struct ethtool_ops xlgmac_ethtool_ops = { |
| 260 | .get_drvinfo = xlgmac_ethtool_get_drvinfo, |
| 261 | .get_link = ethtool_op_get_link, |
| 262 | .get_msglevel = xlgmac_ethtool_get_msglevel, |
| 263 | .set_msglevel = xlgmac_ethtool_set_msglevel, |
| 264 | .get_channels = xlgmac_ethtool_get_channels, |
| 265 | .get_coalesce = xlgmac_ethtool_get_coalesce, |
| 266 | .set_coalesce = xlgmac_ethtool_set_coalesce, |
| 267 | .get_strings = xlgmac_ethtool_get_strings, |
| 268 | .get_sset_count = xlgmac_ethtool_get_sset_count, |
| 269 | .get_ethtool_stats = xlgmac_ethtool_get_ethtool_stats, |
| 270 | }; |
| 271 | |
| 272 | const struct ethtool_ops *xlgmac_get_ethtool_ops(void) |
| 273 | { |
| 274 | return &xlgmac_ethtool_ops; |
| 275 | } |