Commit | Line | Data |
---|---|---|
47dd7a54 GC |
1 | /******************************************************************************* |
2 | STMMAC Ethtool support | |
3 | ||
4 | Copyright (C) 2007-2009 STMicroelectronics Ltd | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify it | |
7 | under the terms and conditions of the GNU General Public License, | |
8 | version 2, as published by the Free Software Foundation. | |
9 | ||
10 | This program is distributed in the hope it will be useful, but WITHOUT | |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License along with | |
16 | this program; if not, write to the Free Software Foundation, Inc., | |
17 | 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
18 | ||
19 | The full GNU General Public License is included in this distribution in | |
20 | the file called "COPYING". | |
21 | ||
22 | Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> | |
23 | *******************************************************************************/ | |
24 | ||
25 | #include <linux/etherdevice.h> | |
26 | #include <linux/ethtool.h> | |
a6b7a407 | 27 | #include <linux/interrupt.h> |
47dd7a54 GC |
28 | #include <linux/mii.h> |
29 | #include <linux/phy.h> | |
b7f080cf | 30 | #include <asm/io.h> |
47dd7a54 GC |
31 | |
32 | #include "stmmac.h" | |
aec7ff27 | 33 | #include "dwmac_dma.h" |
47dd7a54 GC |
34 | |
35 | #define REG_SPACE_SIZE 0x1054 | |
36 | #define MAC100_ETHTOOL_NAME "st_mac100" | |
37 | #define GMAC_ETHTOOL_NAME "st_gmac" | |
38 | ||
39 | struct stmmac_stats { | |
40 | char stat_string[ETH_GSTRING_LEN]; | |
41 | int sizeof_stat; | |
42 | int stat_offset; | |
43 | }; | |
44 | ||
45 | #define STMMAC_STAT(m) \ | |
46 | { #m, FIELD_SIZEOF(struct stmmac_extra_stats, m), \ | |
47 | offsetof(struct stmmac_priv, xstats.m)} | |
48 | ||
1c901a46 | 49 | static const struct stmmac_stats stmmac_gstrings_stats[] = { |
1cc5a735 | 50 | /* Transmit errors */ |
47dd7a54 GC |
51 | STMMAC_STAT(tx_underflow), |
52 | STMMAC_STAT(tx_carrier), | |
53 | STMMAC_STAT(tx_losscarrier), | |
3c20f72f | 54 | STMMAC_STAT(vlan_tag), |
47dd7a54 GC |
55 | STMMAC_STAT(tx_deferred), |
56 | STMMAC_STAT(tx_vlan), | |
47dd7a54 GC |
57 | STMMAC_STAT(tx_jabber), |
58 | STMMAC_STAT(tx_frame_flushed), | |
59 | STMMAC_STAT(tx_payload_error), | |
60 | STMMAC_STAT(tx_ip_header_error), | |
1cc5a735 | 61 | /* Receive errors */ |
47dd7a54 | 62 | STMMAC_STAT(rx_desc), |
3c20f72f GC |
63 | STMMAC_STAT(sa_filter_fail), |
64 | STMMAC_STAT(overflow_error), | |
65 | STMMAC_STAT(ipc_csum_error), | |
47dd7a54 GC |
66 | STMMAC_STAT(rx_collision), |
67 | STMMAC_STAT(rx_crc), | |
1cc5a735 | 68 | STMMAC_STAT(dribbling_bit), |
1b924032 | 69 | STMMAC_STAT(rx_length), |
47dd7a54 GC |
70 | STMMAC_STAT(rx_mii), |
71 | STMMAC_STAT(rx_multicast), | |
72 | STMMAC_STAT(rx_gmac_overflow), | |
73 | STMMAC_STAT(rx_watchdog), | |
74 | STMMAC_STAT(da_rx_filter_fail), | |
75 | STMMAC_STAT(sa_rx_filter_fail), | |
76 | STMMAC_STAT(rx_missed_cntr), | |
77 | STMMAC_STAT(rx_overflow_cntr), | |
1cc5a735 | 78 | STMMAC_STAT(rx_vlan), |
62a2ab93 | 79 | /* Tx/Rx IRQ error info */ |
47dd7a54 GC |
80 | STMMAC_STAT(tx_undeflow_irq), |
81 | STMMAC_STAT(tx_process_stopped_irq), | |
82 | STMMAC_STAT(tx_jabber_irq), | |
83 | STMMAC_STAT(rx_overflow_irq), | |
84 | STMMAC_STAT(rx_buf_unav_irq), | |
85 | STMMAC_STAT(rx_process_stopped_irq), | |
86 | STMMAC_STAT(rx_watchdog_irq), | |
87 | STMMAC_STAT(tx_early_irq), | |
88 | STMMAC_STAT(fatal_bus_error_irq), | |
62a2ab93 GC |
89 | /* Tx/Rx IRQ Events */ |
90 | STMMAC_STAT(rx_early_irq), | |
47dd7a54 GC |
91 | STMMAC_STAT(threshold), |
92 | STMMAC_STAT(tx_pkt_n), | |
93 | STMMAC_STAT(rx_pkt_n), | |
d765955d | 94 | STMMAC_STAT(normal_irq_n), |
9125cdd1 GC |
95 | STMMAC_STAT(rx_normal_irq_n), |
96 | STMMAC_STAT(napi_poll), | |
97 | STMMAC_STAT(tx_normal_irq_n), | |
98 | STMMAC_STAT(tx_clean), | |
99 | STMMAC_STAT(tx_reset_ic_bit), | |
62a2ab93 GC |
100 | STMMAC_STAT(irq_receive_pmt_irq_n), |
101 | /* MMC info */ | |
d765955d GC |
102 | STMMAC_STAT(mmc_tx_irq_n), |
103 | STMMAC_STAT(mmc_rx_irq_n), | |
104 | STMMAC_STAT(mmc_rx_csum_offload_irq_n), | |
62a2ab93 | 105 | /* EEE */ |
d765955d GC |
106 | STMMAC_STAT(irq_tx_path_in_lpi_mode_n), |
107 | STMMAC_STAT(irq_tx_path_exit_lpi_mode_n), | |
108 | STMMAC_STAT(irq_rx_path_in_lpi_mode_n), | |
109 | STMMAC_STAT(irq_rx_path_exit_lpi_mode_n), | |
110 | STMMAC_STAT(phy_eee_wakeup_error_n), | |
47dd7a54 GC |
111 | }; |
112 | #define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats) | |
113 | ||
1c901a46 GC |
114 | /* HW MAC Management counters (if supported) */ |
115 | #define STMMAC_MMC_STAT(m) \ | |
116 | { #m, FIELD_SIZEOF(struct stmmac_counters, m), \ | |
117 | offsetof(struct stmmac_priv, mmc.m)} | |
118 | ||
38fe7a93 | 119 | static const struct stmmac_stats stmmac_mmc[] = { |
1c901a46 GC |
120 | STMMAC_MMC_STAT(mmc_tx_octetcount_gb), |
121 | STMMAC_MMC_STAT(mmc_tx_framecount_gb), | |
122 | STMMAC_MMC_STAT(mmc_tx_broadcastframe_g), | |
123 | STMMAC_MMC_STAT(mmc_tx_multicastframe_g), | |
124 | STMMAC_MMC_STAT(mmc_tx_64_octets_gb), | |
125 | STMMAC_MMC_STAT(mmc_tx_65_to_127_octets_gb), | |
126 | STMMAC_MMC_STAT(mmc_tx_128_to_255_octets_gb), | |
127 | STMMAC_MMC_STAT(mmc_tx_256_to_511_octets_gb), | |
128 | STMMAC_MMC_STAT(mmc_tx_512_to_1023_octets_gb), | |
129 | STMMAC_MMC_STAT(mmc_tx_1024_to_max_octets_gb), | |
130 | STMMAC_MMC_STAT(mmc_tx_unicast_gb), | |
131 | STMMAC_MMC_STAT(mmc_tx_multicast_gb), | |
132 | STMMAC_MMC_STAT(mmc_tx_broadcast_gb), | |
133 | STMMAC_MMC_STAT(mmc_tx_underflow_error), | |
134 | STMMAC_MMC_STAT(mmc_tx_singlecol_g), | |
135 | STMMAC_MMC_STAT(mmc_tx_multicol_g), | |
136 | STMMAC_MMC_STAT(mmc_tx_deferred), | |
137 | STMMAC_MMC_STAT(mmc_tx_latecol), | |
138 | STMMAC_MMC_STAT(mmc_tx_exesscol), | |
139 | STMMAC_MMC_STAT(mmc_tx_carrier_error), | |
140 | STMMAC_MMC_STAT(mmc_tx_octetcount_g), | |
141 | STMMAC_MMC_STAT(mmc_tx_framecount_g), | |
142 | STMMAC_MMC_STAT(mmc_tx_excessdef), | |
143 | STMMAC_MMC_STAT(mmc_tx_pause_frame), | |
144 | STMMAC_MMC_STAT(mmc_tx_vlan_frame_g), | |
145 | STMMAC_MMC_STAT(mmc_rx_framecount_gb), | |
146 | STMMAC_MMC_STAT(mmc_rx_octetcount_gb), | |
147 | STMMAC_MMC_STAT(mmc_rx_octetcount_g), | |
148 | STMMAC_MMC_STAT(mmc_rx_broadcastframe_g), | |
149 | STMMAC_MMC_STAT(mmc_rx_multicastframe_g), | |
150 | STMMAC_MMC_STAT(mmc_rx_crc_errror), | |
151 | STMMAC_MMC_STAT(mmc_rx_align_error), | |
152 | STMMAC_MMC_STAT(mmc_rx_run_error), | |
153 | STMMAC_MMC_STAT(mmc_rx_jabber_error), | |
154 | STMMAC_MMC_STAT(mmc_rx_undersize_g), | |
155 | STMMAC_MMC_STAT(mmc_rx_oversize_g), | |
156 | STMMAC_MMC_STAT(mmc_rx_64_octets_gb), | |
157 | STMMAC_MMC_STAT(mmc_rx_65_to_127_octets_gb), | |
158 | STMMAC_MMC_STAT(mmc_rx_128_to_255_octets_gb), | |
159 | STMMAC_MMC_STAT(mmc_rx_256_to_511_octets_gb), | |
160 | STMMAC_MMC_STAT(mmc_rx_512_to_1023_octets_gb), | |
161 | STMMAC_MMC_STAT(mmc_rx_1024_to_max_octets_gb), | |
162 | STMMAC_MMC_STAT(mmc_rx_unicast_g), | |
163 | STMMAC_MMC_STAT(mmc_rx_length_error), | |
164 | STMMAC_MMC_STAT(mmc_rx_autofrangetype), | |
165 | STMMAC_MMC_STAT(mmc_rx_pause_frames), | |
166 | STMMAC_MMC_STAT(mmc_rx_fifo_overflow), | |
167 | STMMAC_MMC_STAT(mmc_rx_vlan_frames_gb), | |
168 | STMMAC_MMC_STAT(mmc_rx_watchdog_error), | |
169 | STMMAC_MMC_STAT(mmc_rx_ipc_intr_mask), | |
170 | STMMAC_MMC_STAT(mmc_rx_ipc_intr), | |
171 | STMMAC_MMC_STAT(mmc_rx_ipv4_gd), | |
172 | STMMAC_MMC_STAT(mmc_rx_ipv4_hderr), | |
173 | STMMAC_MMC_STAT(mmc_rx_ipv4_nopay), | |
174 | STMMAC_MMC_STAT(mmc_rx_ipv4_frag), | |
175 | STMMAC_MMC_STAT(mmc_rx_ipv4_udsbl), | |
176 | STMMAC_MMC_STAT(mmc_rx_ipv4_gd_octets), | |
177 | STMMAC_MMC_STAT(mmc_rx_ipv4_hderr_octets), | |
178 | STMMAC_MMC_STAT(mmc_rx_ipv4_nopay_octets), | |
179 | STMMAC_MMC_STAT(mmc_rx_ipv4_frag_octets), | |
180 | STMMAC_MMC_STAT(mmc_rx_ipv4_udsbl_octets), | |
181 | STMMAC_MMC_STAT(mmc_rx_ipv6_gd_octets), | |
182 | STMMAC_MMC_STAT(mmc_rx_ipv6_hderr_octets), | |
183 | STMMAC_MMC_STAT(mmc_rx_ipv6_nopay_octets), | |
184 | STMMAC_MMC_STAT(mmc_rx_ipv6_gd), | |
185 | STMMAC_MMC_STAT(mmc_rx_ipv6_hderr), | |
186 | STMMAC_MMC_STAT(mmc_rx_ipv6_nopay), | |
187 | STMMAC_MMC_STAT(mmc_rx_udp_gd), | |
188 | STMMAC_MMC_STAT(mmc_rx_udp_err), | |
189 | STMMAC_MMC_STAT(mmc_rx_tcp_gd), | |
190 | STMMAC_MMC_STAT(mmc_rx_tcp_err), | |
191 | STMMAC_MMC_STAT(mmc_rx_icmp_gd), | |
192 | STMMAC_MMC_STAT(mmc_rx_icmp_err), | |
193 | STMMAC_MMC_STAT(mmc_rx_udp_gd_octets), | |
194 | STMMAC_MMC_STAT(mmc_rx_udp_err_octets), | |
195 | STMMAC_MMC_STAT(mmc_rx_tcp_gd_octets), | |
196 | STMMAC_MMC_STAT(mmc_rx_tcp_err_octets), | |
197 | STMMAC_MMC_STAT(mmc_rx_icmp_gd_octets), | |
198 | STMMAC_MMC_STAT(mmc_rx_icmp_err_octets), | |
199 | }; | |
38fe7a93 | 200 | #define STMMAC_MMC_STATS_LEN ARRAY_SIZE(stmmac_mmc) |
1c901a46 | 201 | |
8ee17ae6 | 202 | static void stmmac_ethtool_getdrvinfo(struct net_device *dev, |
203 | struct ethtool_drvinfo *info) | |
47dd7a54 GC |
204 | { |
205 | struct stmmac_priv *priv = netdev_priv(dev); | |
206 | ||
1c901a46 | 207 | if (priv->plat->has_gmac) |
33a5ba14 | 208 | strlcpy(info->driver, GMAC_ETHTOOL_NAME, sizeof(info->driver)); |
1c901a46 | 209 | else |
33a5ba14 RJ |
210 | strlcpy(info->driver, MAC100_ETHTOOL_NAME, |
211 | sizeof(info->driver)); | |
47dd7a54 | 212 | |
7826d43f | 213 | strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); |
47dd7a54 GC |
214 | } |
215 | ||
8ee17ae6 | 216 | static int stmmac_ethtool_getsettings(struct net_device *dev, |
217 | struct ethtool_cmd *cmd) | |
47dd7a54 GC |
218 | { |
219 | struct stmmac_priv *priv = netdev_priv(dev); | |
220 | struct phy_device *phy = priv->phydev; | |
221 | int rc; | |
222 | if (phy == NULL) { | |
223 | pr_err("%s: %s: PHY is not registered\n", | |
224 | __func__, dev->name); | |
225 | return -ENODEV; | |
226 | } | |
227 | if (!netif_running(dev)) { | |
228 | pr_err("%s: interface is disabled: we cannot track " | |
229 | "link speed / duplex setting\n", dev->name); | |
230 | return -EBUSY; | |
231 | } | |
232 | cmd->transceiver = XCVR_INTERNAL; | |
233 | spin_lock_irq(&priv->lock); | |
234 | rc = phy_ethtool_gset(phy, cmd); | |
235 | spin_unlock_irq(&priv->lock); | |
236 | return rc; | |
237 | } | |
238 | ||
8ee17ae6 | 239 | static int stmmac_ethtool_setsettings(struct net_device *dev, |
240 | struct ethtool_cmd *cmd) | |
47dd7a54 GC |
241 | { |
242 | struct stmmac_priv *priv = netdev_priv(dev); | |
243 | struct phy_device *phy = priv->phydev; | |
244 | int rc; | |
245 | ||
246 | spin_lock(&priv->lock); | |
247 | rc = phy_ethtool_sset(phy, cmd); | |
248 | spin_unlock(&priv->lock); | |
249 | ||
250 | return rc; | |
251 | } | |
252 | ||
8ee17ae6 | 253 | static u32 stmmac_ethtool_getmsglevel(struct net_device *dev) |
47dd7a54 GC |
254 | { |
255 | struct stmmac_priv *priv = netdev_priv(dev); | |
256 | return priv->msg_enable; | |
257 | } | |
258 | ||
8ee17ae6 | 259 | static void stmmac_ethtool_setmsglevel(struct net_device *dev, u32 level) |
47dd7a54 GC |
260 | { |
261 | struct stmmac_priv *priv = netdev_priv(dev); | |
262 | priv->msg_enable = level; | |
263 | ||
264 | } | |
265 | ||
8ee17ae6 | 266 | static int stmmac_check_if_running(struct net_device *dev) |
47dd7a54 GC |
267 | { |
268 | if (!netif_running(dev)) | |
269 | return -EBUSY; | |
270 | return 0; | |
271 | } | |
272 | ||
8ee17ae6 | 273 | static int stmmac_ethtool_get_regs_len(struct net_device *dev) |
47dd7a54 GC |
274 | { |
275 | return REG_SPACE_SIZE; | |
276 | } | |
277 | ||
8ee17ae6 | 278 | static void stmmac_ethtool_gregs(struct net_device *dev, |
47dd7a54 GC |
279 | struct ethtool_regs *regs, void *space) |
280 | { | |
281 | int i; | |
282 | u32 *reg_space = (u32 *) space; | |
283 | ||
284 | struct stmmac_priv *priv = netdev_priv(dev); | |
285 | ||
286 | memset(reg_space, 0x0, REG_SPACE_SIZE); | |
287 | ||
9dfeb4d9 | 288 | if (!priv->plat->has_gmac) { |
47dd7a54 GC |
289 | /* MAC registers */ |
290 | for (i = 0; i < 12; i++) | |
ad01b7d4 | 291 | reg_space[i] = readl(priv->ioaddr + (i * 4)); |
47dd7a54 GC |
292 | /* DMA registers */ |
293 | for (i = 0; i < 9; i++) | |
294 | reg_space[i + 12] = | |
ad01b7d4 GC |
295 | readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4))); |
296 | reg_space[22] = readl(priv->ioaddr + DMA_CUR_TX_BUF_ADDR); | |
297 | reg_space[23] = readl(priv->ioaddr + DMA_CUR_RX_BUF_ADDR); | |
47dd7a54 GC |
298 | } else { |
299 | /* MAC registers */ | |
300 | for (i = 0; i < 55; i++) | |
ad01b7d4 | 301 | reg_space[i] = readl(priv->ioaddr + (i * 4)); |
47dd7a54 GC |
302 | /* DMA registers */ |
303 | for (i = 0; i < 22; i++) | |
304 | reg_space[i + 55] = | |
ad01b7d4 | 305 | readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4))); |
47dd7a54 | 306 | } |
47dd7a54 GC |
307 | } |
308 | ||
47dd7a54 GC |
309 | static void |
310 | stmmac_get_pauseparam(struct net_device *netdev, | |
311 | struct ethtool_pauseparam *pause) | |
312 | { | |
313 | struct stmmac_priv *priv = netdev_priv(netdev); | |
314 | ||
315 | spin_lock(&priv->lock); | |
316 | ||
317 | pause->rx_pause = 0; | |
318 | pause->tx_pause = 0; | |
319 | pause->autoneg = priv->phydev->autoneg; | |
320 | ||
321 | if (priv->flow_ctrl & FLOW_RX) | |
322 | pause->rx_pause = 1; | |
323 | if (priv->flow_ctrl & FLOW_TX) | |
324 | pause->tx_pause = 1; | |
325 | ||
326 | spin_unlock(&priv->lock); | |
47dd7a54 GC |
327 | } |
328 | ||
329 | static int | |
330 | stmmac_set_pauseparam(struct net_device *netdev, | |
331 | struct ethtool_pauseparam *pause) | |
332 | { | |
333 | struct stmmac_priv *priv = netdev_priv(netdev); | |
334 | struct phy_device *phy = priv->phydev; | |
335 | int new_pause = FLOW_OFF; | |
336 | int ret = 0; | |
337 | ||
338 | spin_lock(&priv->lock); | |
339 | ||
340 | if (pause->rx_pause) | |
341 | new_pause |= FLOW_RX; | |
342 | if (pause->tx_pause) | |
343 | new_pause |= FLOW_TX; | |
344 | ||
345 | priv->flow_ctrl = new_pause; | |
64c7f304 | 346 | phy->autoneg = pause->autoneg; |
47dd7a54 GC |
347 | |
348 | if (phy->autoneg) { | |
1334cb60 DD |
349 | if (netif_running(netdev)) |
350 | ret = phy_start_aneg(phy); | |
ad01b7d4 GC |
351 | } else |
352 | priv->hw->mac->flow_ctrl(priv->ioaddr, phy->duplex, | |
db98a0b0 | 353 | priv->flow_ctrl, priv->pause); |
47dd7a54 GC |
354 | spin_unlock(&priv->lock); |
355 | return ret; | |
356 | } | |
357 | ||
358 | static void stmmac_get_ethtool_stats(struct net_device *dev, | |
359 | struct ethtool_stats *dummy, u64 *data) | |
360 | { | |
361 | struct stmmac_priv *priv = netdev_priv(dev); | |
1c901a46 | 362 | int i, j = 0; |
47dd7a54 | 363 | |
1c901a46 GC |
364 | /* Update the DMA HW counters for dwmac10/100 */ |
365 | if (!priv->plat->has_gmac) | |
366 | priv->hw->dma->dma_diagnostic_fr(&dev->stats, | |
367 | (void *) &priv->xstats, | |
368 | priv->ioaddr); | |
369 | else { | |
370 | /* If supported, for new GMAC chips expose the MMC counters */ | |
38fe7a93 GC |
371 | if (priv->dma_cap.rmon) { |
372 | dwmac_mmc_read(priv->ioaddr, &priv->mmc); | |
1c901a46 | 373 | |
38fe7a93 GC |
374 | for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) { |
375 | char *p; | |
376 | p = (char *)priv + stmmac_mmc[i].stat_offset; | |
1c901a46 | 377 | |
38fe7a93 GC |
378 | data[j++] = (stmmac_mmc[i].sizeof_stat == |
379 | sizeof(u64)) ? (*(u64 *)p) : | |
380 | (*(u32 *)p); | |
381 | } | |
1c901a46 | 382 | } |
d765955d GC |
383 | if (priv->eee_enabled) { |
384 | int val = phy_get_eee_err(priv->phydev); | |
385 | if (val) | |
386 | priv->xstats.phy_eee_wakeup_error_n = val; | |
387 | } | |
1c901a46 | 388 | } |
47dd7a54 GC |
389 | for (i = 0; i < STMMAC_STATS_LEN; i++) { |
390 | char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset; | |
1c901a46 GC |
391 | data[j++] = (stmmac_gstrings_stats[i].sizeof_stat == |
392 | sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p); | |
47dd7a54 | 393 | } |
47dd7a54 GC |
394 | } |
395 | ||
396 | static int stmmac_get_sset_count(struct net_device *netdev, int sset) | |
397 | { | |
1c901a46 GC |
398 | struct stmmac_priv *priv = netdev_priv(netdev); |
399 | int len; | |
400 | ||
47dd7a54 GC |
401 | switch (sset) { |
402 | case ETH_SS_STATS: | |
1c901a46 GC |
403 | len = STMMAC_STATS_LEN; |
404 | ||
38fe7a93 | 405 | if (priv->dma_cap.rmon) |
1c901a46 GC |
406 | len += STMMAC_MMC_STATS_LEN; |
407 | ||
408 | return len; | |
47dd7a54 GC |
409 | default: |
410 | return -EOPNOTSUPP; | |
411 | } | |
412 | } | |
413 | ||
414 | static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data) | |
415 | { | |
416 | int i; | |
417 | u8 *p = data; | |
1c901a46 | 418 | struct stmmac_priv *priv = netdev_priv(dev); |
47dd7a54 GC |
419 | |
420 | switch (stringset) { | |
421 | case ETH_SS_STATS: | |
38fe7a93 | 422 | if (priv->dma_cap.rmon) |
1c901a46 | 423 | for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) { |
38fe7a93 | 424 | memcpy(p, stmmac_mmc[i].stat_string, |
1c901a46 GC |
425 | ETH_GSTRING_LEN); |
426 | p += ETH_GSTRING_LEN; | |
427 | } | |
47dd7a54 GC |
428 | for (i = 0; i < STMMAC_STATS_LEN; i++) { |
429 | memcpy(p, stmmac_gstrings_stats[i].stat_string, | |
430 | ETH_GSTRING_LEN); | |
431 | p += ETH_GSTRING_LEN; | |
432 | } | |
433 | break; | |
434 | default: | |
435 | WARN_ON(1); | |
436 | break; | |
437 | } | |
47dd7a54 GC |
438 | } |
439 | ||
440 | /* Currently only support WOL through Magic packet. */ | |
441 | static void stmmac_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) | |
442 | { | |
443 | struct stmmac_priv *priv = netdev_priv(dev); | |
444 | ||
445 | spin_lock_irq(&priv->lock); | |
543876c9 | 446 | if (device_can_wakeup(priv->device)) { |
74ae2fd7 | 447 | wol->supported = WAKE_MAGIC | WAKE_UCAST; |
47dd7a54 GC |
448 | wol->wolopts = priv->wolopts; |
449 | } | |
450 | spin_unlock_irq(&priv->lock); | |
451 | } | |
452 | ||
453 | static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) | |
454 | { | |
455 | struct stmmac_priv *priv = netdev_priv(dev); | |
74ae2fd7 | 456 | u32 support = WAKE_MAGIC | WAKE_UCAST; |
47dd7a54 | 457 | |
19e30c14 GC |
458 | /* By default almost all GMAC devices support the WoL via |
459 | * magic frame but we can disable it if the HW capability | |
460 | * register shows no support for pmt_magic_frame. */ | |
461 | if ((priv->hw_cap_support) && (!priv->dma_cap.pmt_magic_frame)) | |
462 | wol->wolopts &= ~WAKE_MAGIC; | |
463 | ||
543876c9 | 464 | if (!device_can_wakeup(priv->device)) |
47dd7a54 GC |
465 | return -EINVAL; |
466 | ||
467 | if (wol->wolopts & ~support) | |
468 | return -EINVAL; | |
469 | ||
543876c9 GC |
470 | if (wol->wolopts) { |
471 | pr_info("stmmac: wakeup enable\n"); | |
47dd7a54 | 472 | device_set_wakeup_enable(priv->device, 1); |
3172d3af | 473 | enable_irq_wake(priv->wol_irq); |
543876c9 GC |
474 | } else { |
475 | device_set_wakeup_enable(priv->device, 0); | |
3172d3af | 476 | disable_irq_wake(priv->wol_irq); |
543876c9 | 477 | } |
47dd7a54 GC |
478 | |
479 | spin_lock_irq(&priv->lock); | |
480 | priv->wolopts = wol->wolopts; | |
481 | spin_unlock_irq(&priv->lock); | |
482 | ||
483 | return 0; | |
484 | } | |
485 | ||
d765955d GC |
486 | static int stmmac_ethtool_op_get_eee(struct net_device *dev, |
487 | struct ethtool_eee *edata) | |
488 | { | |
489 | struct stmmac_priv *priv = netdev_priv(dev); | |
490 | ||
491 | if (!priv->dma_cap.eee) | |
492 | return -EOPNOTSUPP; | |
493 | ||
494 | edata->eee_enabled = priv->eee_enabled; | |
495 | edata->eee_active = priv->eee_active; | |
496 | edata->tx_lpi_timer = priv->tx_lpi_timer; | |
497 | ||
498 | return phy_ethtool_get_eee(priv->phydev, edata); | |
499 | } | |
500 | ||
501 | static int stmmac_ethtool_op_set_eee(struct net_device *dev, | |
502 | struct ethtool_eee *edata) | |
503 | { | |
504 | struct stmmac_priv *priv = netdev_priv(dev); | |
505 | ||
506 | priv->eee_enabled = edata->eee_enabled; | |
507 | ||
508 | if (!priv->eee_enabled) | |
509 | stmmac_disable_eee_mode(priv); | |
510 | else { | |
511 | /* We are asking for enabling the EEE but it is safe | |
512 | * to verify all by invoking the eee_init function. | |
513 | * In case of failure it will return an error. | |
514 | */ | |
515 | priv->eee_enabled = stmmac_eee_init(priv); | |
516 | if (!priv->eee_enabled) | |
517 | return -EOPNOTSUPP; | |
518 | ||
519 | /* Do not change tx_lpi_timer in case of failure */ | |
520 | priv->tx_lpi_timer = edata->tx_lpi_timer; | |
521 | } | |
522 | ||
523 | return phy_ethtool_set_eee(priv->phydev, edata); | |
524 | } | |
525 | ||
48f44da4 GC |
526 | static u32 stmmac_usec2riwt(u32 usec, struct stmmac_priv *priv) |
527 | { | |
528 | unsigned long clk = clk_get_rate(priv->stmmac_clk); | |
529 | ||
530 | if (!clk) | |
531 | return 0; | |
532 | ||
533 | return (usec * (clk / 1000000)) / 256; | |
534 | } | |
535 | ||
536 | static u32 stmmac_riwt2usec(u32 riwt, struct stmmac_priv *priv) | |
537 | { | |
538 | unsigned long clk = clk_get_rate(priv->stmmac_clk); | |
539 | ||
540 | if (!clk) | |
541 | return 0; | |
542 | ||
543 | return (riwt * 256) / (clk / 1000000); | |
544 | } | |
545 | ||
546 | static int stmmac_get_coalesce(struct net_device *dev, | |
547 | struct ethtool_coalesce *ec) | |
548 | { | |
549 | struct stmmac_priv *priv = netdev_priv(dev); | |
550 | ||
551 | ec->tx_coalesce_usecs = priv->tx_coal_timer; | |
552 | ec->tx_max_coalesced_frames = priv->tx_coal_frames; | |
553 | ||
554 | if (priv->use_riwt) | |
555 | ec->rx_coalesce_usecs = stmmac_riwt2usec(priv->rx_riwt, priv); | |
556 | ||
557 | return 0; | |
558 | } | |
559 | ||
560 | static int stmmac_set_coalesce(struct net_device *dev, | |
561 | struct ethtool_coalesce *ec) | |
562 | { | |
563 | struct stmmac_priv *priv = netdev_priv(dev); | |
564 | unsigned int rx_riwt; | |
565 | ||
566 | /* Check not supported parameters */ | |
567 | if ((ec->rx_max_coalesced_frames) || (ec->rx_coalesce_usecs_irq) || | |
568 | (ec->rx_max_coalesced_frames_irq) || (ec->tx_coalesce_usecs_irq) || | |
569 | (ec->use_adaptive_rx_coalesce) || (ec->use_adaptive_tx_coalesce) || | |
570 | (ec->pkt_rate_low) || (ec->rx_coalesce_usecs_low) || | |
571 | (ec->rx_max_coalesced_frames_low) || (ec->tx_coalesce_usecs_high) || | |
572 | (ec->tx_max_coalesced_frames_low) || (ec->pkt_rate_high) || | |
573 | (ec->tx_coalesce_usecs_low) || (ec->rx_coalesce_usecs_high) || | |
574 | (ec->rx_max_coalesced_frames_high) || | |
575 | (ec->tx_max_coalesced_frames_irq) || | |
576 | (ec->stats_block_coalesce_usecs) || | |
577 | (ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval)) | |
578 | return -EOPNOTSUPP; | |
579 | ||
580 | if (ec->rx_coalesce_usecs == 0) | |
581 | return -EINVAL; | |
582 | ||
583 | if ((ec->tx_coalesce_usecs == 0) && | |
584 | (ec->tx_max_coalesced_frames == 0)) | |
585 | return -EINVAL; | |
586 | ||
587 | if ((ec->tx_coalesce_usecs > STMMAC_COAL_TX_TIMER) || | |
588 | (ec->tx_max_coalesced_frames > STMMAC_TX_MAX_FRAMES)) | |
589 | return -EINVAL; | |
590 | ||
591 | rx_riwt = stmmac_usec2riwt(ec->rx_coalesce_usecs, priv); | |
592 | ||
593 | if ((rx_riwt > MAX_DMA_RIWT) || (rx_riwt < MIN_DMA_RIWT)) | |
594 | return -EINVAL; | |
595 | else if (!priv->use_riwt) | |
596 | return -EOPNOTSUPP; | |
597 | ||
598 | /* Only copy relevant parameters, ignore all others. */ | |
599 | priv->tx_coal_frames = ec->tx_max_coalesced_frames; | |
600 | priv->tx_coal_timer = ec->tx_coalesce_usecs; | |
601 | priv->rx_riwt = rx_riwt; | |
602 | priv->hw->dma->rx_watchdog(priv->ioaddr, priv->rx_riwt); | |
603 | ||
604 | return 0; | |
605 | } | |
606 | ||
9b07be4b | 607 | static const struct ethtool_ops stmmac_ethtool_ops = { |
47dd7a54 GC |
608 | .begin = stmmac_check_if_running, |
609 | .get_drvinfo = stmmac_ethtool_getdrvinfo, | |
610 | .get_settings = stmmac_ethtool_getsettings, | |
611 | .set_settings = stmmac_ethtool_setsettings, | |
612 | .get_msglevel = stmmac_ethtool_getmsglevel, | |
613 | .set_msglevel = stmmac_ethtool_setmsglevel, | |
614 | .get_regs = stmmac_ethtool_gregs, | |
615 | .get_regs_len = stmmac_ethtool_get_regs_len, | |
616 | .get_link = ethtool_op_get_link, | |
47dd7a54 GC |
617 | .get_pauseparam = stmmac_get_pauseparam, |
618 | .set_pauseparam = stmmac_set_pauseparam, | |
619 | .get_ethtool_stats = stmmac_get_ethtool_stats, | |
620 | .get_strings = stmmac_get_strings, | |
621 | .get_wol = stmmac_get_wol, | |
622 | .set_wol = stmmac_set_wol, | |
d765955d GC |
623 | .get_eee = stmmac_ethtool_op_get_eee, |
624 | .set_eee = stmmac_ethtool_op_set_eee, | |
47dd7a54 | 625 | .get_sset_count = stmmac_get_sset_count, |
912b3b3d | 626 | .get_ts_info = ethtool_op_get_ts_info, |
48f44da4 GC |
627 | .get_coalesce = stmmac_get_coalesce, |
628 | .set_coalesce = stmmac_set_coalesce, | |
47dd7a54 GC |
629 | }; |
630 | ||
631 | void stmmac_set_ethtool_ops(struct net_device *netdev) | |
632 | { | |
633 | SET_ETHTOOL_OPS(netdev, &stmmac_ethtool_ops); | |
634 | } |