Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[sfrench/cifs-2.6.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_stats_count(struct net_device *netdev)
92 {
93         return ARRAY_SIZE(atl1_gstrings_stats);
94 }
95
96 static int atl1_get_settings(struct net_device *netdev,
97                                 struct ethtool_cmd *ecmd)
98 {
99         struct atl1_adapter *adapter = netdev_priv(netdev);
100         struct atl1_hw *hw = &adapter->hw;
101
102         ecmd->supported = (SUPPORTED_10baseT_Half |
103                            SUPPORTED_10baseT_Full |
104                            SUPPORTED_100baseT_Half |
105                            SUPPORTED_100baseT_Full |
106                            SUPPORTED_1000baseT_Full |
107                            SUPPORTED_Autoneg | SUPPORTED_TP);
108         ecmd->advertising = ADVERTISED_TP;
109         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
110             hw->media_type == MEDIA_TYPE_1000M_FULL) {
111                 ecmd->advertising |= ADVERTISED_Autoneg;
112                 if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) {
113                         ecmd->advertising |= ADVERTISED_Autoneg;
114                         ecmd->advertising |=
115                             (ADVERTISED_10baseT_Half |
116                              ADVERTISED_10baseT_Full |
117                              ADVERTISED_100baseT_Half |
118                              ADVERTISED_100baseT_Full |
119                              ADVERTISED_1000baseT_Full);
120                 }
121                 else
122                         ecmd->advertising |= (ADVERTISED_1000baseT_Full);
123         }
124         ecmd->port = PORT_TP;
125         ecmd->phy_address = 0;
126         ecmd->transceiver = XCVR_INTERNAL;
127
128         if (netif_carrier_ok(adapter->netdev)) {
129                 u16 link_speed, link_duplex;
130                 atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex);
131                 ecmd->speed = link_speed;
132                 if (link_duplex == FULL_DUPLEX)
133                         ecmd->duplex = DUPLEX_FULL;
134                 else
135                         ecmd->duplex = DUPLEX_HALF;
136         } else {
137                 ecmd->speed = -1;
138                 ecmd->duplex = -1;
139         }
140         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
141             hw->media_type == MEDIA_TYPE_1000M_FULL)
142                 ecmd->autoneg = AUTONEG_ENABLE;
143         else
144                 ecmd->autoneg = AUTONEG_DISABLE;
145
146         return 0;
147 }
148
149 static int atl1_set_settings(struct net_device *netdev,
150                                 struct ethtool_cmd *ecmd)
151 {
152         struct atl1_adapter *adapter = netdev_priv(netdev);
153         struct atl1_hw *hw = &adapter->hw;
154         u16 phy_data;
155         int ret_val = 0;
156         u16 old_media_type = hw->media_type;
157
158         if (netif_running(adapter->netdev)) {
159                 dev_dbg(&adapter->pdev->dev, "ethtool shutting down adapter\n");
160                 atl1_down(adapter);
161         }
162
163         if (ecmd->autoneg == AUTONEG_ENABLE)
164                 hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
165         else {
166                 if (ecmd->speed == SPEED_1000) {
167                         if (ecmd->duplex != DUPLEX_FULL) {
168                                 dev_warn(&adapter->pdev->dev,
169                                         "can't force to 1000M half duplex\n");
170                                 ret_val = -EINVAL;
171                                 goto exit_sset;
172                         }
173                         hw->media_type = MEDIA_TYPE_1000M_FULL;
174                 } else if (ecmd->speed == SPEED_100) {
175                         if (ecmd->duplex == DUPLEX_FULL) {
176                                 hw->media_type = MEDIA_TYPE_100M_FULL;
177                         } else
178                                 hw->media_type = MEDIA_TYPE_100M_HALF;
179                 } else {
180                         if (ecmd->duplex == DUPLEX_FULL)
181                                 hw->media_type = MEDIA_TYPE_10M_FULL;
182                         else
183                                 hw->media_type = MEDIA_TYPE_10M_HALF;
184                 }
185         }
186         switch (hw->media_type) {
187         case MEDIA_TYPE_AUTO_SENSOR:
188                 ecmd->advertising =
189                     ADVERTISED_10baseT_Half |
190                     ADVERTISED_10baseT_Full |
191                     ADVERTISED_100baseT_Half |
192                     ADVERTISED_100baseT_Full |
193                     ADVERTISED_1000baseT_Full |
194                     ADVERTISED_Autoneg | ADVERTISED_TP;
195                 break;
196         case MEDIA_TYPE_1000M_FULL:
197                 ecmd->advertising =
198                     ADVERTISED_1000baseT_Full |
199                     ADVERTISED_Autoneg | ADVERTISED_TP;
200                 break;
201         default:
202                 ecmd->advertising = 0;
203                 break;
204         }
205         if (atl1_phy_setup_autoneg_adv(hw)) {
206                 ret_val = -EINVAL;
207                 dev_warn(&adapter->pdev->dev,
208                         "invalid ethtool speed/duplex setting\n");
209                 goto exit_sset;
210         }
211         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
212             hw->media_type == MEDIA_TYPE_1000M_FULL)
213                 phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
214         else {
215                 switch (hw->media_type) {
216                 case MEDIA_TYPE_100M_FULL:
217                         phy_data =
218                             MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
219                             MII_CR_RESET;
220                         break;
221                 case MEDIA_TYPE_100M_HALF:
222                         phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
223                         break;
224                 case MEDIA_TYPE_10M_FULL:
225                         phy_data =
226                             MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
227                         break;
228                 default:        /* MEDIA_TYPE_10M_HALF: */
229                         phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
230                         break;
231                 }
232         }
233         atl1_write_phy_reg(hw, MII_BMCR, phy_data);
234 exit_sset:
235         if (ret_val)
236                 hw->media_type = old_media_type;
237
238         if (netif_running(adapter->netdev)) {
239                 dev_dbg(&adapter->pdev->dev, "ethtool starting adapter\n");
240                 atl1_up(adapter);
241         } else if (!ret_val) {
242                 dev_dbg(&adapter->pdev->dev, "ethtool resetting adapter\n");
243                 atl1_reset(adapter);
244         }
245         return ret_val;
246 }
247
248 static void atl1_get_drvinfo(struct net_device *netdev,
249                                 struct ethtool_drvinfo *drvinfo)
250 {
251         struct atl1_adapter *adapter = netdev_priv(netdev);
252
253         strncpy(drvinfo->driver, atl1_driver_name, sizeof(drvinfo->driver));
254         strncpy(drvinfo->version, atl1_driver_version,
255                 sizeof(drvinfo->version));
256         strncpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
257         strncpy(drvinfo->bus_info, pci_name(adapter->pdev),
258                 sizeof(drvinfo->bus_info));
259         drvinfo->eedump_len = ATL1_EEDUMP_LEN;
260 }
261
262 static void atl1_get_wol(struct net_device *netdev,
263                             struct ethtool_wolinfo *wol)
264 {
265         struct atl1_adapter *adapter = netdev_priv(netdev);
266
267         wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC;
268         wol->wolopts = 0;
269         if (adapter->wol & ATL1_WUFC_EX)
270                 wol->wolopts |= WAKE_UCAST;
271         if (adapter->wol & ATL1_WUFC_MC)
272                 wol->wolopts |= WAKE_MCAST;
273         if (adapter->wol & ATL1_WUFC_BC)
274                 wol->wolopts |= WAKE_BCAST;
275         if (adapter->wol & ATL1_WUFC_MAG)
276                 wol->wolopts |= WAKE_MAGIC;
277         return;
278 }
279
280 static int atl1_set_wol(struct net_device *netdev,
281                         struct ethtool_wolinfo *wol)
282 {
283         struct atl1_adapter *adapter = netdev_priv(netdev);
284
285         if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE))
286                 return -EOPNOTSUPP;
287         adapter->wol = 0;
288         if (wol->wolopts & WAKE_UCAST)
289                 adapter->wol |= ATL1_WUFC_EX;
290         if (wol->wolopts & WAKE_MCAST)
291                 adapter->wol |= ATL1_WUFC_MC;
292         if (wol->wolopts & WAKE_BCAST)
293                 adapter->wol |= ATL1_WUFC_BC;
294         if (wol->wolopts & WAKE_MAGIC)
295                 adapter->wol |= ATL1_WUFC_MAG;
296         return 0;
297 }
298
299 static void atl1_get_ringparam(struct net_device *netdev,
300                             struct ethtool_ringparam *ring)
301 {
302         struct atl1_adapter *adapter = netdev_priv(netdev);
303         struct atl1_tpd_ring *txdr = &adapter->tpd_ring;
304         struct atl1_rfd_ring *rxdr = &adapter->rfd_ring;
305
306         ring->rx_max_pending = ATL1_MAX_RFD;
307         ring->tx_max_pending = ATL1_MAX_TPD;
308         ring->rx_mini_max_pending = 0;
309         ring->rx_jumbo_max_pending = 0;
310         ring->rx_pending = rxdr->count;
311         ring->tx_pending = txdr->count;
312         ring->rx_mini_pending = 0;
313         ring->rx_jumbo_pending = 0;
314 }
315
316 static int atl1_set_ringparam(struct net_device *netdev,
317                                 struct ethtool_ringparam *ring)
318 {
319         struct atl1_adapter *adapter = netdev_priv(netdev);
320         struct atl1_tpd_ring *tpdr = &adapter->tpd_ring;
321         struct atl1_rrd_ring *rrdr = &adapter->rrd_ring;
322         struct atl1_rfd_ring *rfdr = &adapter->rfd_ring;
323
324         struct atl1_tpd_ring tpd_old, tpd_new;
325         struct atl1_rfd_ring rfd_old, rfd_new;
326         struct atl1_rrd_ring rrd_old, rrd_new;
327         struct atl1_ring_header rhdr_old, rhdr_new;
328         int err;
329
330         tpd_old = adapter->tpd_ring;
331         rfd_old = adapter->rfd_ring;
332         rrd_old = adapter->rrd_ring;
333         rhdr_old = adapter->ring_header;
334
335         if (netif_running(adapter->netdev))
336                 atl1_down(adapter);
337
338         rfdr->count = (u16) max(ring->rx_pending, (u32) ATL1_MIN_RFD);
339         rfdr->count = rfdr->count > ATL1_MAX_RFD ? ATL1_MAX_RFD :
340                         rfdr->count;
341         rfdr->count = (rfdr->count + 3) & ~3;
342         rrdr->count = rfdr->count;
343
344         tpdr->count = (u16) max(ring->tx_pending, (u32) ATL1_MIN_TPD);
345         tpdr->count = tpdr->count > ATL1_MAX_TPD ? ATL1_MAX_TPD :
346                         tpdr->count;
347         tpdr->count = (tpdr->count + 3) & ~3;
348
349         if (netif_running(adapter->netdev)) {
350                 /* try to get new resources before deleting old */
351                 err = atl1_setup_ring_resources(adapter);
352                 if (err)
353                         goto err_setup_ring;
354
355                 /*
356                  * save the new, restore the old in order to free it,
357                  * then restore the new back again
358                  */
359
360                 rfd_new = adapter->rfd_ring;
361                 rrd_new = adapter->rrd_ring;
362                 tpd_new = adapter->tpd_ring;
363                 rhdr_new = adapter->ring_header;
364                 adapter->rfd_ring = rfd_old;
365                 adapter->rrd_ring = rrd_old;
366                 adapter->tpd_ring = tpd_old;
367                 adapter->ring_header = rhdr_old;
368                 atl1_free_ring_resources(adapter);
369                 adapter->rfd_ring = rfd_new;
370                 adapter->rrd_ring = rrd_new;
371                 adapter->tpd_ring = tpd_new;
372                 adapter->ring_header = rhdr_new;
373
374                 err = atl1_up(adapter);
375                 if (err)
376                         return err;
377         }
378         return 0;
379
380 err_setup_ring:
381         adapter->rfd_ring = rfd_old;
382         adapter->rrd_ring = rrd_old;
383         adapter->tpd_ring = tpd_old;
384         adapter->ring_header = rhdr_old;
385         atl1_up(adapter);
386         return err;
387 }
388
389 static void atl1_get_pauseparam(struct net_device *netdev,
390                              struct ethtool_pauseparam *epause)
391 {
392         struct atl1_adapter *adapter = netdev_priv(netdev);
393         struct atl1_hw *hw = &adapter->hw;
394
395         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
396             hw->media_type == MEDIA_TYPE_1000M_FULL) {
397                 epause->autoneg = AUTONEG_ENABLE;
398         } else {
399                 epause->autoneg = AUTONEG_DISABLE;
400         }
401         epause->rx_pause = 1;
402         epause->tx_pause = 1;
403 }
404
405 static int atl1_set_pauseparam(struct net_device *netdev,
406                              struct ethtool_pauseparam *epause)
407 {
408         struct atl1_adapter *adapter = netdev_priv(netdev);
409         struct atl1_hw *hw = &adapter->hw;
410
411         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
412             hw->media_type == MEDIA_TYPE_1000M_FULL) {
413                 epause->autoneg = AUTONEG_ENABLE;
414         } else {
415                 epause->autoneg = AUTONEG_DISABLE;
416         }
417
418         epause->rx_pause = 1;
419         epause->tx_pause = 1;
420
421         return 0;
422 }
423
424 static u32 atl1_get_rx_csum(struct net_device *netdev)
425 {
426         return 1;
427 }
428
429 static void atl1_get_strings(struct net_device *netdev, u32 stringset,
430                                 u8 *data)
431 {
432         u8 *p = data;
433         int i;
434
435         switch (stringset) {
436         case ETH_SS_STATS:
437                 for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
438                         memcpy(p, atl1_gstrings_stats[i].stat_string,
439                                 ETH_GSTRING_LEN);
440                         p += ETH_GSTRING_LEN;
441                 }
442                 break;
443         }
444 }
445
446 static int atl1_nway_reset(struct net_device *netdev)
447 {
448         struct atl1_adapter *adapter = netdev_priv(netdev);
449         struct atl1_hw *hw = &adapter->hw;
450
451         if (netif_running(netdev)) {
452                 u16 phy_data;
453                 atl1_down(adapter);
454
455                 if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
456                         hw->media_type == MEDIA_TYPE_1000M_FULL) {
457                         phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
458                 } else {
459                         switch (hw->media_type) {
460                         case MEDIA_TYPE_100M_FULL:
461                                 phy_data = MII_CR_FULL_DUPLEX |
462                                         MII_CR_SPEED_100 | MII_CR_RESET;
463                                 break;
464                         case MEDIA_TYPE_100M_HALF:
465                                 phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
466                                 break;
467                         case MEDIA_TYPE_10M_FULL:
468                                 phy_data = MII_CR_FULL_DUPLEX |
469                                         MII_CR_SPEED_10 | MII_CR_RESET;
470                                 break;
471                         default:  /* MEDIA_TYPE_10M_HALF */
472                                 phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
473                         }
474                 }
475                 atl1_write_phy_reg(hw, MII_BMCR, phy_data);
476                 atl1_up(adapter);
477         }
478         return 0;
479 }
480
481 const struct ethtool_ops atl1_ethtool_ops = {
482         .get_settings           = atl1_get_settings,
483         .set_settings           = atl1_set_settings,
484         .get_drvinfo            = atl1_get_drvinfo,
485         .get_wol                = atl1_get_wol,
486         .set_wol                = atl1_set_wol,
487         .get_ringparam          = atl1_get_ringparam,
488         .set_ringparam          = atl1_set_ringparam,
489         .get_pauseparam         = atl1_get_pauseparam,
490         .set_pauseparam         = atl1_set_pauseparam,
491         .get_rx_csum            = atl1_get_rx_csum,
492         .get_tx_csum            = ethtool_op_get_tx_csum,
493         .set_tx_csum            = ethtool_op_set_tx_hw_csum,
494         .get_link               = ethtool_op_get_link,
495         .get_sg                 = ethtool_op_get_sg,
496         .set_sg                 = ethtool_op_set_sg,
497         .get_strings            = atl1_get_strings,
498         .nway_reset             = atl1_nway_reset,
499         .get_ethtool_stats      = atl1_get_ethtool_stats,
500         .get_stats_count        = atl1_get_stats_count,
501         .get_tso                = ethtool_op_get_tso,
502         .set_tso                = ethtool_op_set_tso,
503 };