]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/net/atl1/atl1_ethtool.c
remove the obsolete xircom_tulip_cb driver
[linux-2.6-omap-h63xx.git] / drivers / net / atl1 / atl1_ethtool.c
1 /*
2  * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
3  * Copyright(c) 2006 Chris Snook <csnook@redhat.com>
4  * Copyright(c) 2006 Jay Cliburn <jcliburn@gmail.com>
5  *
6  * Derived from Intel e1000 driver
7  * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the Free
11  * Software Foundation; either version 2 of the License, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program; if not, write to the Free Software Foundation, Inc., 59
21  * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #include <linux/types.h>
25 #include <linux/pci.h>
26 #include <linux/ethtool.h>
27 #include <linux/netdevice.h>
28 #include <linux/mii.h>
29 #include <asm/uaccess.h>
30
31 #include "atl1.h"
32
33 struct atl1_stats {
34         char stat_string[ETH_GSTRING_LEN];
35         int sizeof_stat;
36         int stat_offset;
37 };
38
39 #define ATL1_STAT(m) sizeof(((struct atl1_adapter *)0)->m), \
40         offsetof(struct atl1_adapter, m)
41
42 static struct atl1_stats atl1_gstrings_stats[] = {
43         {"rx_packets", ATL1_STAT(soft_stats.rx_packets)},
44         {"tx_packets", ATL1_STAT(soft_stats.tx_packets)},
45         {"rx_bytes", ATL1_STAT(soft_stats.rx_bytes)},
46         {"tx_bytes", ATL1_STAT(soft_stats.tx_bytes)},
47         {"rx_errors", ATL1_STAT(soft_stats.rx_errors)},
48         {"tx_errors", ATL1_STAT(soft_stats.tx_errors)},
49         {"rx_dropped", ATL1_STAT(net_stats.rx_dropped)},
50         {"tx_dropped", ATL1_STAT(net_stats.tx_dropped)},
51         {"multicast", ATL1_STAT(soft_stats.multicast)},
52         {"collisions", ATL1_STAT(soft_stats.collisions)},
53         {"rx_length_errors", ATL1_STAT(soft_stats.rx_length_errors)},
54         {"rx_over_errors", ATL1_STAT(soft_stats.rx_missed_errors)},
55         {"rx_crc_errors", ATL1_STAT(soft_stats.rx_crc_errors)},
56         {"rx_frame_errors", ATL1_STAT(soft_stats.rx_frame_errors)},
57         {"rx_fifo_errors", ATL1_STAT(soft_stats.rx_fifo_errors)},
58         {"rx_missed_errors", ATL1_STAT(soft_stats.rx_missed_errors)},
59         {"tx_aborted_errors", ATL1_STAT(soft_stats.tx_aborted_errors)},
60         {"tx_carrier_errors", ATL1_STAT(soft_stats.tx_carrier_errors)},
61         {"tx_fifo_errors", ATL1_STAT(soft_stats.tx_fifo_errors)},
62         {"tx_window_errors", ATL1_STAT(soft_stats.tx_window_errors)},
63         {"tx_abort_exce_coll", ATL1_STAT(soft_stats.excecol)},
64         {"tx_abort_late_coll", ATL1_STAT(soft_stats.latecol)},
65         {"tx_deferred_ok", ATL1_STAT(soft_stats.deffer)},
66         {"tx_single_coll_ok", ATL1_STAT(soft_stats.scc)},
67         {"tx_multi_coll_ok", ATL1_STAT(soft_stats.mcc)},
68         {"tx_underun", ATL1_STAT(soft_stats.tx_underun)},
69         {"tx_trunc", ATL1_STAT(soft_stats.tx_trunc)},
70         {"tx_pause", ATL1_STAT(soft_stats.tx_pause)},
71         {"rx_pause", ATL1_STAT(soft_stats.rx_pause)},
72         {"rx_rrd_ov", ATL1_STAT(soft_stats.rx_rrd_ov)},
73         {"rx_trunc", ATL1_STAT(soft_stats.rx_trunc)}
74 };
75
76 static void atl1_get_ethtool_stats(struct net_device *netdev,
77                                 struct ethtool_stats *stats, u64 *data)
78 {
79         struct atl1_adapter *adapter = netdev_priv(netdev);
80         int i;
81         char *p;
82
83         for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
84                 p = (char *)adapter+atl1_gstrings_stats[i].stat_offset;
85                 data[i] = (atl1_gstrings_stats[i].sizeof_stat ==
86                         sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
87         }
88
89 }
90
91 static int atl1_get_sset_count(struct net_device *netdev, int sset)
92 {
93         switch (sset) {
94         case ETH_SS_STATS:
95                 return ARRAY_SIZE(atl1_gstrings_stats);
96         default:
97                 return -EOPNOTSUPP;
98         }
99 }
100
101 static int atl1_get_settings(struct net_device *netdev,
102                                 struct ethtool_cmd *ecmd)
103 {
104         struct atl1_adapter *adapter = netdev_priv(netdev);
105         struct atl1_hw *hw = &adapter->hw;
106
107         ecmd->supported = (SUPPORTED_10baseT_Half |
108                            SUPPORTED_10baseT_Full |
109                            SUPPORTED_100baseT_Half |
110                            SUPPORTED_100baseT_Full |
111                            SUPPORTED_1000baseT_Full |
112                            SUPPORTED_Autoneg | SUPPORTED_TP);
113         ecmd->advertising = ADVERTISED_TP;
114         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
115             hw->media_type == MEDIA_TYPE_1000M_FULL) {
116                 ecmd->advertising |= ADVERTISED_Autoneg;
117                 if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) {
118                         ecmd->advertising |= ADVERTISED_Autoneg;
119                         ecmd->advertising |=
120                             (ADVERTISED_10baseT_Half |
121                              ADVERTISED_10baseT_Full |
122                              ADVERTISED_100baseT_Half |
123                              ADVERTISED_100baseT_Full |
124                              ADVERTISED_1000baseT_Full);
125                 }
126                 else
127                         ecmd->advertising |= (ADVERTISED_1000baseT_Full);
128         }
129         ecmd->port = PORT_TP;
130         ecmd->phy_address = 0;
131         ecmd->transceiver = XCVR_INTERNAL;
132
133         if (netif_carrier_ok(adapter->netdev)) {
134                 u16 link_speed, link_duplex;
135                 atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex);
136                 ecmd->speed = link_speed;
137                 if (link_duplex == FULL_DUPLEX)
138                         ecmd->duplex = DUPLEX_FULL;
139                 else
140                         ecmd->duplex = DUPLEX_HALF;
141         } else {
142                 ecmd->speed = -1;
143                 ecmd->duplex = -1;
144         }
145         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
146             hw->media_type == MEDIA_TYPE_1000M_FULL)
147                 ecmd->autoneg = AUTONEG_ENABLE;
148         else
149                 ecmd->autoneg = AUTONEG_DISABLE;
150
151         return 0;
152 }
153
154 static int atl1_set_settings(struct net_device *netdev,
155                                 struct ethtool_cmd *ecmd)
156 {
157         struct atl1_adapter *adapter = netdev_priv(netdev);
158         struct atl1_hw *hw = &adapter->hw;
159         u16 phy_data;
160         int ret_val = 0;
161         u16 old_media_type = hw->media_type;
162
163         if (netif_running(adapter->netdev)) {
164                 dev_dbg(&adapter->pdev->dev, "ethtool shutting down adapter\n");
165                 atl1_down(adapter);
166         }
167
168         if (ecmd->autoneg == AUTONEG_ENABLE)
169                 hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
170         else {
171                 if (ecmd->speed == SPEED_1000) {
172                         if (ecmd->duplex != DUPLEX_FULL) {
173                                 dev_warn(&adapter->pdev->dev,
174                                         "can't force to 1000M half duplex\n");
175                                 ret_val = -EINVAL;
176                                 goto exit_sset;
177                         }
178                         hw->media_type = MEDIA_TYPE_1000M_FULL;
179                 } else if (ecmd->speed == SPEED_100) {
180                         if (ecmd->duplex == DUPLEX_FULL) {
181                                 hw->media_type = MEDIA_TYPE_100M_FULL;
182                         } else
183                                 hw->media_type = MEDIA_TYPE_100M_HALF;
184                 } else {
185                         if (ecmd->duplex == DUPLEX_FULL)
186                                 hw->media_type = MEDIA_TYPE_10M_FULL;
187                         else
188                                 hw->media_type = MEDIA_TYPE_10M_HALF;
189                 }
190         }
191         switch (hw->media_type) {
192         case MEDIA_TYPE_AUTO_SENSOR:
193                 ecmd->advertising =
194                     ADVERTISED_10baseT_Half |
195                     ADVERTISED_10baseT_Full |
196                     ADVERTISED_100baseT_Half |
197                     ADVERTISED_100baseT_Full |
198                     ADVERTISED_1000baseT_Full |
199                     ADVERTISED_Autoneg | ADVERTISED_TP;
200                 break;
201         case MEDIA_TYPE_1000M_FULL:
202                 ecmd->advertising =
203                     ADVERTISED_1000baseT_Full |
204                     ADVERTISED_Autoneg | ADVERTISED_TP;
205                 break;
206         default:
207                 ecmd->advertising = 0;
208                 break;
209         }
210         if (atl1_phy_setup_autoneg_adv(hw)) {
211                 ret_val = -EINVAL;
212                 dev_warn(&adapter->pdev->dev,
213                         "invalid ethtool speed/duplex setting\n");
214                 goto exit_sset;
215         }
216         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
217             hw->media_type == MEDIA_TYPE_1000M_FULL)
218                 phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
219         else {
220                 switch (hw->media_type) {
221                 case MEDIA_TYPE_100M_FULL:
222                         phy_data =
223                             MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
224                             MII_CR_RESET;
225                         break;
226                 case MEDIA_TYPE_100M_HALF:
227                         phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
228                         break;
229                 case MEDIA_TYPE_10M_FULL:
230                         phy_data =
231                             MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
232                         break;
233                 default:        /* MEDIA_TYPE_10M_HALF: */
234                         phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
235                         break;
236                 }
237         }
238         atl1_write_phy_reg(hw, MII_BMCR, phy_data);
239 exit_sset:
240         if (ret_val)
241                 hw->media_type = old_media_type;
242
243         if (netif_running(adapter->netdev)) {
244                 dev_dbg(&adapter->pdev->dev, "ethtool starting adapter\n");
245                 atl1_up(adapter);
246         } else if (!ret_val) {
247                 dev_dbg(&adapter->pdev->dev, "ethtool resetting adapter\n");
248                 atl1_reset(adapter);
249         }
250         return ret_val;
251 }
252
253 static void atl1_get_drvinfo(struct net_device *netdev,
254                                 struct ethtool_drvinfo *drvinfo)
255 {
256         struct atl1_adapter *adapter = netdev_priv(netdev);
257
258         strncpy(drvinfo->driver, atl1_driver_name, sizeof(drvinfo->driver));
259         strncpy(drvinfo->version, atl1_driver_version,
260                 sizeof(drvinfo->version));
261         strncpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
262         strncpy(drvinfo->bus_info, pci_name(adapter->pdev),
263                 sizeof(drvinfo->bus_info));
264         drvinfo->eedump_len = ATL1_EEDUMP_LEN;
265 }
266
267 static void atl1_get_wol(struct net_device *netdev,
268                             struct ethtool_wolinfo *wol)
269 {
270         struct atl1_adapter *adapter = netdev_priv(netdev);
271
272         wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC;
273         wol->wolopts = 0;
274         if (adapter->wol & ATL1_WUFC_EX)
275                 wol->wolopts |= WAKE_UCAST;
276         if (adapter->wol & ATL1_WUFC_MC)
277                 wol->wolopts |= WAKE_MCAST;
278         if (adapter->wol & ATL1_WUFC_BC)
279                 wol->wolopts |= WAKE_BCAST;
280         if (adapter->wol & ATL1_WUFC_MAG)
281                 wol->wolopts |= WAKE_MAGIC;
282         return;
283 }
284
285 static int atl1_set_wol(struct net_device *netdev,
286                         struct ethtool_wolinfo *wol)
287 {
288         struct atl1_adapter *adapter = netdev_priv(netdev);
289
290         if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE))
291                 return -EOPNOTSUPP;
292         adapter->wol = 0;
293         if (wol->wolopts & WAKE_UCAST)
294                 adapter->wol |= ATL1_WUFC_EX;
295         if (wol->wolopts & WAKE_MCAST)
296                 adapter->wol |= ATL1_WUFC_MC;
297         if (wol->wolopts & WAKE_BCAST)
298                 adapter->wol |= ATL1_WUFC_BC;
299         if (wol->wolopts & WAKE_MAGIC)
300                 adapter->wol |= ATL1_WUFC_MAG;
301         return 0;
302 }
303
304 static void atl1_get_ringparam(struct net_device *netdev,
305                             struct ethtool_ringparam *ring)
306 {
307         struct atl1_adapter *adapter = netdev_priv(netdev);
308         struct atl1_tpd_ring *txdr = &adapter->tpd_ring;
309         struct atl1_rfd_ring *rxdr = &adapter->rfd_ring;
310
311         ring->rx_max_pending = ATL1_MAX_RFD;
312         ring->tx_max_pending = ATL1_MAX_TPD;
313         ring->rx_mini_max_pending = 0;
314         ring->rx_jumbo_max_pending = 0;
315         ring->rx_pending = rxdr->count;
316         ring->tx_pending = txdr->count;
317         ring->rx_mini_pending = 0;
318         ring->rx_jumbo_pending = 0;
319 }
320
321 static int atl1_set_ringparam(struct net_device *netdev,
322                                 struct ethtool_ringparam *ring)
323 {
324         struct atl1_adapter *adapter = netdev_priv(netdev);
325         struct atl1_tpd_ring *tpdr = &adapter->tpd_ring;
326         struct atl1_rrd_ring *rrdr = &adapter->rrd_ring;
327         struct atl1_rfd_ring *rfdr = &adapter->rfd_ring;
328
329         struct atl1_tpd_ring tpd_old, tpd_new;
330         struct atl1_rfd_ring rfd_old, rfd_new;
331         struct atl1_rrd_ring rrd_old, rrd_new;
332         struct atl1_ring_header rhdr_old, rhdr_new;
333         int err;
334
335         tpd_old = adapter->tpd_ring;
336         rfd_old = adapter->rfd_ring;
337         rrd_old = adapter->rrd_ring;
338         rhdr_old = adapter->ring_header;
339
340         if (netif_running(adapter->netdev))
341                 atl1_down(adapter);
342
343         rfdr->count = (u16) max(ring->rx_pending, (u32) ATL1_MIN_RFD);
344         rfdr->count = rfdr->count > ATL1_MAX_RFD ? ATL1_MAX_RFD :
345                         rfdr->count;
346         rfdr->count = (rfdr->count + 3) & ~3;
347         rrdr->count = rfdr->count;
348
349         tpdr->count = (u16) max(ring->tx_pending, (u32) ATL1_MIN_TPD);
350         tpdr->count = tpdr->count > ATL1_MAX_TPD ? ATL1_MAX_TPD :
351                         tpdr->count;
352         tpdr->count = (tpdr->count + 3) & ~3;
353
354         if (netif_running(adapter->netdev)) {
355                 /* try to get new resources before deleting old */
356                 err = atl1_setup_ring_resources(adapter);
357                 if (err)
358                         goto err_setup_ring;
359
360                 /*
361                  * save the new, restore the old in order to free it,
362                  * then restore the new back again
363                  */
364
365                 rfd_new = adapter->rfd_ring;
366                 rrd_new = adapter->rrd_ring;
367                 tpd_new = adapter->tpd_ring;
368                 rhdr_new = adapter->ring_header;
369                 adapter->rfd_ring = rfd_old;
370                 adapter->rrd_ring = rrd_old;
371                 adapter->tpd_ring = tpd_old;
372                 adapter->ring_header = rhdr_old;
373                 atl1_free_ring_resources(adapter);
374                 adapter->rfd_ring = rfd_new;
375                 adapter->rrd_ring = rrd_new;
376                 adapter->tpd_ring = tpd_new;
377                 adapter->ring_header = rhdr_new;
378
379                 err = atl1_up(adapter);
380                 if (err)
381                         return err;
382         }
383         return 0;
384
385 err_setup_ring:
386         adapter->rfd_ring = rfd_old;
387         adapter->rrd_ring = rrd_old;
388         adapter->tpd_ring = tpd_old;
389         adapter->ring_header = rhdr_old;
390         atl1_up(adapter);
391         return err;
392 }
393
394 static void atl1_get_pauseparam(struct net_device *netdev,
395                              struct ethtool_pauseparam *epause)
396 {
397         struct atl1_adapter *adapter = netdev_priv(netdev);
398         struct atl1_hw *hw = &adapter->hw;
399
400         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
401             hw->media_type == MEDIA_TYPE_1000M_FULL) {
402                 epause->autoneg = AUTONEG_ENABLE;
403         } else {
404                 epause->autoneg = AUTONEG_DISABLE;
405         }
406         epause->rx_pause = 1;
407         epause->tx_pause = 1;
408 }
409
410 static int atl1_set_pauseparam(struct net_device *netdev,
411                              struct ethtool_pauseparam *epause)
412 {
413         struct atl1_adapter *adapter = netdev_priv(netdev);
414         struct atl1_hw *hw = &adapter->hw;
415
416         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
417             hw->media_type == MEDIA_TYPE_1000M_FULL) {
418                 epause->autoneg = AUTONEG_ENABLE;
419         } else {
420                 epause->autoneg = AUTONEG_DISABLE;
421         }
422
423         epause->rx_pause = 1;
424         epause->tx_pause = 1;
425
426         return 0;
427 }
428
429 static u32 atl1_get_rx_csum(struct net_device *netdev)
430 {
431         return 1;
432 }
433
434 static void atl1_get_strings(struct net_device *netdev, u32 stringset,
435                                 u8 *data)
436 {
437         u8 *p = data;
438         int i;
439
440         switch (stringset) {
441         case ETH_SS_STATS:
442                 for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
443                         memcpy(p, atl1_gstrings_stats[i].stat_string,
444                                 ETH_GSTRING_LEN);
445                         p += ETH_GSTRING_LEN;
446                 }
447                 break;
448         }
449 }
450
451 static int atl1_nway_reset(struct net_device *netdev)
452 {
453         struct atl1_adapter *adapter = netdev_priv(netdev);
454         struct atl1_hw *hw = &adapter->hw;
455
456         if (netif_running(netdev)) {
457                 u16 phy_data;
458                 atl1_down(adapter);
459
460                 if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
461                         hw->media_type == MEDIA_TYPE_1000M_FULL) {
462                         phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
463                 } else {
464                         switch (hw->media_type) {
465                         case MEDIA_TYPE_100M_FULL:
466                                 phy_data = MII_CR_FULL_DUPLEX |
467                                         MII_CR_SPEED_100 | MII_CR_RESET;
468                                 break;
469                         case MEDIA_TYPE_100M_HALF:
470                                 phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
471                                 break;
472                         case MEDIA_TYPE_10M_FULL:
473                                 phy_data = MII_CR_FULL_DUPLEX |
474                                         MII_CR_SPEED_10 | MII_CR_RESET;
475                                 break;
476                         default:  /* MEDIA_TYPE_10M_HALF */
477                                 phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
478                         }
479                 }
480                 atl1_write_phy_reg(hw, MII_BMCR, phy_data);
481                 atl1_up(adapter);
482         }
483         return 0;
484 }
485
486 const struct ethtool_ops atl1_ethtool_ops = {
487         .get_settings           = atl1_get_settings,
488         .set_settings           = atl1_set_settings,
489         .get_drvinfo            = atl1_get_drvinfo,
490         .get_wol                = atl1_get_wol,
491         .set_wol                = atl1_set_wol,
492         .get_ringparam          = atl1_get_ringparam,
493         .set_ringparam          = atl1_set_ringparam,
494         .get_pauseparam         = atl1_get_pauseparam,
495         .set_pauseparam         = atl1_set_pauseparam,
496         .get_rx_csum            = atl1_get_rx_csum,
497         .set_tx_csum            = ethtool_op_set_tx_hw_csum,
498         .get_link               = ethtool_op_get_link,
499         .set_sg                 = ethtool_op_set_sg,
500         .get_strings            = atl1_get_strings,
501         .nway_reset             = atl1_nway_reset,
502         .get_ethtool_stats      = atl1_get_ethtool_stats,
503         .get_sset_count         = atl1_get_sset_count,
504         .set_tso                = ethtool_op_set_tso,
505 };