]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/net/atl1/atl1_ethtool.c
Add Attansic L1 ethernet 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_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                 printk(KERN_DEBUG "%s: ethtool shutting down adapter\n",
160                         atl1_driver_name);
161                 atl1_down(adapter);
162         }
163
164         if (ecmd->autoneg == AUTONEG_ENABLE)
165                 hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
166         else {
167                 if (ecmd->speed == SPEED_1000) {
168                         if (ecmd->duplex != DUPLEX_FULL) {
169                                 printk(KERN_WARNING
170                                        "%s: can't force to 1000M half duplex\n",
171                                         atl1_driver_name);
172                                 ret_val = -EINVAL;
173                                 goto exit_sset;
174                         }
175                         hw->media_type = MEDIA_TYPE_1000M_FULL;
176                 } else if (ecmd->speed == SPEED_100) {
177                         if (ecmd->duplex == DUPLEX_FULL) {
178                                 hw->media_type = MEDIA_TYPE_100M_FULL;
179                         } else
180                                 hw->media_type = MEDIA_TYPE_100M_HALF;
181                 } else {
182                         if (ecmd->duplex == DUPLEX_FULL)
183                                 hw->media_type = MEDIA_TYPE_10M_FULL;
184                         else
185                                 hw->media_type = MEDIA_TYPE_10M_HALF;
186                 }
187         }
188         switch (hw->media_type) {
189         case MEDIA_TYPE_AUTO_SENSOR:
190                 ecmd->advertising =
191                     ADVERTISED_10baseT_Half |
192                     ADVERTISED_10baseT_Full |
193                     ADVERTISED_100baseT_Half |
194                     ADVERTISED_100baseT_Full |
195                     ADVERTISED_1000baseT_Full |
196                     ADVERTISED_Autoneg | ADVERTISED_TP;
197                 break;
198         case MEDIA_TYPE_1000M_FULL:
199                 ecmd->advertising =
200                     ADVERTISED_1000baseT_Full |
201                     ADVERTISED_Autoneg | ADVERTISED_TP;
202                 break;
203         default:
204                 ecmd->advertising = 0;
205                 break;
206         }
207         if (atl1_phy_setup_autoneg_adv(hw)) {
208                 ret_val = -EINVAL;
209                 printk(KERN_WARNING
210                         "%s: invalid ethtool speed/duplex setting\n",
211                         atl1_driver_name);
212                 goto exit_sset;
213         }
214         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
215             hw->media_type == MEDIA_TYPE_1000M_FULL)
216                 phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
217         else {
218                 switch (hw->media_type) {
219                 case MEDIA_TYPE_100M_FULL:
220                         phy_data =
221                             MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
222                             MII_CR_RESET;
223                         break;
224                 case MEDIA_TYPE_100M_HALF:
225                         phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
226                         break;
227                 case MEDIA_TYPE_10M_FULL:
228                         phy_data =
229                             MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
230                         break;
231                 default:        /* MEDIA_TYPE_10M_HALF: */
232                         phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
233                         break;
234                 }
235         }
236         atl1_write_phy_reg(hw, MII_BMCR, phy_data);
237 exit_sset:
238         if (ret_val)
239                 hw->media_type = old_media_type;
240
241         if (netif_running(adapter->netdev)) {
242                 printk(KERN_DEBUG "%s: ethtool starting adapter\n",
243                         atl1_driver_name);
244                 atl1_up(adapter);
245         } else if (!ret_val) {
246                 printk(KERN_DEBUG "%s: ethtool resetting adapter\n",
247                         atl1_driver_name);
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         .get_tx_csum            = ethtool_op_get_tx_csum,
498         .set_tx_csum            = ethtool_op_set_tx_hw_csum,
499         .get_link               = ethtool_op_get_link,
500         .get_sg                 = ethtool_op_get_sg,
501         .set_sg                 = ethtool_op_set_sg,
502         .get_strings            = atl1_get_strings,
503         .nway_reset             = atl1_nway_reset,
504         .get_ethtool_stats      = atl1_get_ethtool_stats,
505         .get_stats_count        = atl1_get_stats_count,
506         .get_tso                = ethtool_op_get_tso,
507         .set_tso                = ethtool_op_set_tso,
508 };