]>
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> | |
27 | #include <linux/mii.h> | |
28 | #include <linux/phy.h> | |
29 | ||
30 | #include "stmmac.h" | |
aec7ff27 | 31 | #include "dwmac_dma.h" |
47dd7a54 GC |
32 | |
33 | #define REG_SPACE_SIZE 0x1054 | |
34 | #define MAC100_ETHTOOL_NAME "st_mac100" | |
35 | #define GMAC_ETHTOOL_NAME "st_gmac" | |
36 | ||
37 | struct stmmac_stats { | |
38 | char stat_string[ETH_GSTRING_LEN]; | |
39 | int sizeof_stat; | |
40 | int stat_offset; | |
41 | }; | |
42 | ||
43 | #define STMMAC_STAT(m) \ | |
44 | { #m, FIELD_SIZEOF(struct stmmac_extra_stats, m), \ | |
45 | offsetof(struct stmmac_priv, xstats.m)} | |
46 | ||
47 | static const struct stmmac_stats stmmac_gstrings_stats[] = { | |
48 | STMMAC_STAT(tx_underflow), | |
49 | STMMAC_STAT(tx_carrier), | |
50 | STMMAC_STAT(tx_losscarrier), | |
51 | STMMAC_STAT(tx_heartbeat), | |
52 | STMMAC_STAT(tx_deferred), | |
53 | STMMAC_STAT(tx_vlan), | |
54 | STMMAC_STAT(rx_vlan), | |
55 | STMMAC_STAT(tx_jabber), | |
56 | STMMAC_STAT(tx_frame_flushed), | |
57 | STMMAC_STAT(tx_payload_error), | |
58 | STMMAC_STAT(tx_ip_header_error), | |
59 | STMMAC_STAT(rx_desc), | |
60 | STMMAC_STAT(rx_partial), | |
61 | STMMAC_STAT(rx_runt), | |
62 | STMMAC_STAT(rx_toolong), | |
63 | STMMAC_STAT(rx_collision), | |
64 | STMMAC_STAT(rx_crc), | |
65 | STMMAC_STAT(rx_lenght), | |
66 | STMMAC_STAT(rx_mii), | |
67 | STMMAC_STAT(rx_multicast), | |
68 | STMMAC_STAT(rx_gmac_overflow), | |
69 | STMMAC_STAT(rx_watchdog), | |
70 | STMMAC_STAT(da_rx_filter_fail), | |
71 | STMMAC_STAT(sa_rx_filter_fail), | |
72 | STMMAC_STAT(rx_missed_cntr), | |
73 | STMMAC_STAT(rx_overflow_cntr), | |
74 | STMMAC_STAT(tx_undeflow_irq), | |
75 | STMMAC_STAT(tx_process_stopped_irq), | |
76 | STMMAC_STAT(tx_jabber_irq), | |
77 | STMMAC_STAT(rx_overflow_irq), | |
78 | STMMAC_STAT(rx_buf_unav_irq), | |
79 | STMMAC_STAT(rx_process_stopped_irq), | |
80 | STMMAC_STAT(rx_watchdog_irq), | |
81 | STMMAC_STAT(tx_early_irq), | |
82 | STMMAC_STAT(fatal_bus_error_irq), | |
83 | STMMAC_STAT(threshold), | |
84 | STMMAC_STAT(tx_pkt_n), | |
85 | STMMAC_STAT(rx_pkt_n), | |
86 | STMMAC_STAT(poll_n), | |
87 | STMMAC_STAT(sched_timer_n), | |
88 | STMMAC_STAT(normal_irq_n), | |
89 | }; | |
90 | #define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats) | |
91 | ||
92 | void stmmac_ethtool_getdrvinfo(struct net_device *dev, | |
93 | struct ethtool_drvinfo *info) | |
94 | { | |
95 | struct stmmac_priv *priv = netdev_priv(dev); | |
96 | ||
97 | if (!priv->is_gmac) | |
98 | strcpy(info->driver, MAC100_ETHTOOL_NAME); | |
99 | else | |
100 | strcpy(info->driver, GMAC_ETHTOOL_NAME); | |
101 | ||
102 | strcpy(info->version, DRV_MODULE_VERSION); | |
103 | info->fw_version[0] = '\0'; | |
104 | info->n_stats = STMMAC_STATS_LEN; | |
105 | return; | |
106 | } | |
107 | ||
108 | int stmmac_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) | |
109 | { | |
110 | struct stmmac_priv *priv = netdev_priv(dev); | |
111 | struct phy_device *phy = priv->phydev; | |
112 | int rc; | |
113 | if (phy == NULL) { | |
114 | pr_err("%s: %s: PHY is not registered\n", | |
115 | __func__, dev->name); | |
116 | return -ENODEV; | |
117 | } | |
118 | if (!netif_running(dev)) { | |
119 | pr_err("%s: interface is disabled: we cannot track " | |
120 | "link speed / duplex setting\n", dev->name); | |
121 | return -EBUSY; | |
122 | } | |
123 | cmd->transceiver = XCVR_INTERNAL; | |
124 | spin_lock_irq(&priv->lock); | |
125 | rc = phy_ethtool_gset(phy, cmd); | |
126 | spin_unlock_irq(&priv->lock); | |
127 | return rc; | |
128 | } | |
129 | ||
130 | int stmmac_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) | |
131 | { | |
132 | struct stmmac_priv *priv = netdev_priv(dev); | |
133 | struct phy_device *phy = priv->phydev; | |
134 | int rc; | |
135 | ||
136 | spin_lock(&priv->lock); | |
137 | rc = phy_ethtool_sset(phy, cmd); | |
138 | spin_unlock(&priv->lock); | |
139 | ||
140 | return rc; | |
141 | } | |
142 | ||
143 | u32 stmmac_ethtool_getmsglevel(struct net_device *dev) | |
144 | { | |
145 | struct stmmac_priv *priv = netdev_priv(dev); | |
146 | return priv->msg_enable; | |
147 | } | |
148 | ||
149 | void stmmac_ethtool_setmsglevel(struct net_device *dev, u32 level) | |
150 | { | |
151 | struct stmmac_priv *priv = netdev_priv(dev); | |
152 | priv->msg_enable = level; | |
153 | ||
154 | } | |
155 | ||
156 | int stmmac_check_if_running(struct net_device *dev) | |
157 | { | |
158 | if (!netif_running(dev)) | |
159 | return -EBUSY; | |
160 | return 0; | |
161 | } | |
162 | ||
163 | int stmmac_ethtool_get_regs_len(struct net_device *dev) | |
164 | { | |
165 | return REG_SPACE_SIZE; | |
166 | } | |
167 | ||
168 | void stmmac_ethtool_gregs(struct net_device *dev, | |
169 | struct ethtool_regs *regs, void *space) | |
170 | { | |
171 | int i; | |
172 | u32 *reg_space = (u32 *) space; | |
173 | ||
174 | struct stmmac_priv *priv = netdev_priv(dev); | |
175 | ||
176 | memset(reg_space, 0x0, REG_SPACE_SIZE); | |
177 | ||
178 | if (!priv->is_gmac) { | |
179 | /* MAC registers */ | |
180 | for (i = 0; i < 12; i++) | |
181 | reg_space[i] = readl(dev->base_addr + (i * 4)); | |
182 | /* DMA registers */ | |
183 | for (i = 0; i < 9; i++) | |
184 | reg_space[i + 12] = | |
185 | readl(dev->base_addr + (DMA_BUS_MODE + (i * 4))); | |
186 | reg_space[22] = readl(dev->base_addr + DMA_CUR_TX_BUF_ADDR); | |
187 | reg_space[23] = readl(dev->base_addr + DMA_CUR_RX_BUF_ADDR); | |
188 | } else { | |
189 | /* MAC registers */ | |
190 | for (i = 0; i < 55; i++) | |
191 | reg_space[i] = readl(dev->base_addr + (i * 4)); | |
192 | /* DMA registers */ | |
193 | for (i = 0; i < 22; i++) | |
194 | reg_space[i + 55] = | |
195 | readl(dev->base_addr + (DMA_BUS_MODE + (i * 4))); | |
196 | } | |
197 | ||
198 | return; | |
199 | } | |
200 | ||
201 | int stmmac_ethtool_set_tx_csum(struct net_device *netdev, u32 data) | |
202 | { | |
203 | if (data) | |
204 | netdev->features |= NETIF_F_HW_CSUM; | |
205 | else | |
206 | netdev->features &= ~NETIF_F_HW_CSUM; | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | u32 stmmac_ethtool_get_rx_csum(struct net_device *dev) | |
212 | { | |
213 | struct stmmac_priv *priv = netdev_priv(dev); | |
214 | ||
215 | return priv->rx_csum; | |
216 | } | |
217 | ||
218 | static void | |
219 | stmmac_get_pauseparam(struct net_device *netdev, | |
220 | struct ethtool_pauseparam *pause) | |
221 | { | |
222 | struct stmmac_priv *priv = netdev_priv(netdev); | |
223 | ||
224 | spin_lock(&priv->lock); | |
225 | ||
226 | pause->rx_pause = 0; | |
227 | pause->tx_pause = 0; | |
228 | pause->autoneg = priv->phydev->autoneg; | |
229 | ||
230 | if (priv->flow_ctrl & FLOW_RX) | |
231 | pause->rx_pause = 1; | |
232 | if (priv->flow_ctrl & FLOW_TX) | |
233 | pause->tx_pause = 1; | |
234 | ||
235 | spin_unlock(&priv->lock); | |
236 | return; | |
237 | } | |
238 | ||
239 | static int | |
240 | stmmac_set_pauseparam(struct net_device *netdev, | |
241 | struct ethtool_pauseparam *pause) | |
242 | { | |
243 | struct stmmac_priv *priv = netdev_priv(netdev); | |
244 | struct phy_device *phy = priv->phydev; | |
245 | int new_pause = FLOW_OFF; | |
246 | int ret = 0; | |
247 | ||
248 | spin_lock(&priv->lock); | |
249 | ||
250 | if (pause->rx_pause) | |
251 | new_pause |= FLOW_RX; | |
252 | if (pause->tx_pause) | |
253 | new_pause |= FLOW_TX; | |
254 | ||
255 | priv->flow_ctrl = new_pause; | |
256 | ||
257 | if (phy->autoneg) { | |
258 | if (netif_running(netdev)) { | |
259 | struct ethtool_cmd cmd; | |
260 | /* auto-negotiation automatically restarted */ | |
261 | cmd.cmd = ETHTOOL_NWAY_RST; | |
262 | cmd.supported = phy->supported; | |
263 | cmd.advertising = phy->advertising; | |
264 | cmd.autoneg = phy->autoneg; | |
265 | cmd.speed = phy->speed; | |
266 | cmd.duplex = phy->duplex; | |
267 | cmd.phy_address = phy->addr; | |
268 | ret = phy_ethtool_sset(phy, &cmd); | |
269 | } | |
270 | } else { | |
271 | unsigned long ioaddr = netdev->base_addr; | |
db98a0b0 GC |
272 | priv->hw->mac->flow_ctrl(ioaddr, phy->duplex, |
273 | priv->flow_ctrl, priv->pause); | |
47dd7a54 GC |
274 | } |
275 | spin_unlock(&priv->lock); | |
276 | return ret; | |
277 | } | |
278 | ||
279 | static void stmmac_get_ethtool_stats(struct net_device *dev, | |
280 | struct ethtool_stats *dummy, u64 *data) | |
281 | { | |
282 | struct stmmac_priv *priv = netdev_priv(dev); | |
283 | unsigned long ioaddr = dev->base_addr; | |
284 | int i; | |
285 | ||
286 | /* Update HW stats if supported */ | |
db98a0b0 GC |
287 | priv->hw->dma->dma_diagnostic_fr(&dev->stats, (void *) &priv->xstats, |
288 | ioaddr); | |
47dd7a54 GC |
289 | |
290 | for (i = 0; i < STMMAC_STATS_LEN; i++) { | |
291 | char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset; | |
292 | data[i] = (stmmac_gstrings_stats[i].sizeof_stat == | |
293 | sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p); | |
294 | } | |
295 | ||
296 | return; | |
297 | } | |
298 | ||
299 | static int stmmac_get_sset_count(struct net_device *netdev, int sset) | |
300 | { | |
301 | switch (sset) { | |
302 | case ETH_SS_STATS: | |
303 | return STMMAC_STATS_LEN; | |
304 | default: | |
305 | return -EOPNOTSUPP; | |
306 | } | |
307 | } | |
308 | ||
309 | static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data) | |
310 | { | |
311 | int i; | |
312 | u8 *p = data; | |
313 | ||
314 | switch (stringset) { | |
315 | case ETH_SS_STATS: | |
316 | for (i = 0; i < STMMAC_STATS_LEN; i++) { | |
317 | memcpy(p, stmmac_gstrings_stats[i].stat_string, | |
318 | ETH_GSTRING_LEN); | |
319 | p += ETH_GSTRING_LEN; | |
320 | } | |
321 | break; | |
322 | default: | |
323 | WARN_ON(1); | |
324 | break; | |
325 | } | |
326 | return; | |
327 | } | |
328 | ||
329 | /* Currently only support WOL through Magic packet. */ | |
330 | static void stmmac_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) | |
331 | { | |
332 | struct stmmac_priv *priv = netdev_priv(dev); | |
333 | ||
334 | spin_lock_irq(&priv->lock); | |
335 | if (priv->wolenabled == PMT_SUPPORTED) { | |
336 | wol->supported = WAKE_MAGIC; | |
337 | wol->wolopts = priv->wolopts; | |
338 | } | |
339 | spin_unlock_irq(&priv->lock); | |
340 | } | |
341 | ||
342 | static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) | |
343 | { | |
344 | struct stmmac_priv *priv = netdev_priv(dev); | |
345 | u32 support = WAKE_MAGIC; | |
346 | ||
347 | if (priv->wolenabled == PMT_NOT_SUPPORTED) | |
348 | return -EINVAL; | |
349 | ||
350 | if (wol->wolopts & ~support) | |
351 | return -EINVAL; | |
352 | ||
353 | if (wol->wolopts == 0) | |
354 | device_set_wakeup_enable(priv->device, 0); | |
355 | else | |
356 | device_set_wakeup_enable(priv->device, 1); | |
357 | ||
358 | spin_lock_irq(&priv->lock); | |
359 | priv->wolopts = wol->wolopts; | |
360 | spin_unlock_irq(&priv->lock); | |
361 | ||
362 | return 0; | |
363 | } | |
364 | ||
365 | static struct ethtool_ops stmmac_ethtool_ops = { | |
366 | .begin = stmmac_check_if_running, | |
367 | .get_drvinfo = stmmac_ethtool_getdrvinfo, | |
368 | .get_settings = stmmac_ethtool_getsettings, | |
369 | .set_settings = stmmac_ethtool_setsettings, | |
370 | .get_msglevel = stmmac_ethtool_getmsglevel, | |
371 | .set_msglevel = stmmac_ethtool_setmsglevel, | |
372 | .get_regs = stmmac_ethtool_gregs, | |
373 | .get_regs_len = stmmac_ethtool_get_regs_len, | |
374 | .get_link = ethtool_op_get_link, | |
375 | .get_rx_csum = stmmac_ethtool_get_rx_csum, | |
376 | .get_tx_csum = ethtool_op_get_tx_csum, | |
377 | .set_tx_csum = stmmac_ethtool_set_tx_csum, | |
378 | .get_sg = ethtool_op_get_sg, | |
379 | .set_sg = ethtool_op_set_sg, | |
380 | .get_pauseparam = stmmac_get_pauseparam, | |
381 | .set_pauseparam = stmmac_set_pauseparam, | |
382 | .get_ethtool_stats = stmmac_get_ethtool_stats, | |
383 | .get_strings = stmmac_get_strings, | |
384 | .get_wol = stmmac_get_wol, | |
385 | .set_wol = stmmac_set_wol, | |
386 | .get_sset_count = stmmac_get_sset_count, | |
387 | #ifdef NETIF_F_TSO | |
388 | .get_tso = ethtool_op_get_tso, | |
389 | .set_tso = ethtool_op_set_tso, | |
390 | #endif | |
391 | }; | |
392 | ||
393 | void stmmac_set_ethtool_ops(struct net_device *netdev) | |
394 | { | |
395 | SET_ETHTOOL_OPS(netdev, &stmmac_ethtool_ops); | |
396 | } |