aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb/mcs7830.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/usb/mcs7830.c')
-rw-r--r--drivers/net/usb/mcs7830.c259
1 files changed, 178 insertions, 81 deletions
diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c
index 10873d96b2da..9f24e3f871e1 100644
--- a/drivers/net/usb/mcs7830.c
+++ b/drivers/net/usb/mcs7830.c
@@ -1,13 +1,27 @@
1/* 1/*
2 * MosChips MCS7830 based USB 2.0 Ethernet Devices 2 * MOSCHIP MCS7830 based USB 2.0 Ethernet Devices
3 * 3 *
4 * based on usbnet.c, asix.c and the vendor provided mcs7830 driver 4 * based on usbnet.c, asix.c and the vendor provided mcs7830 driver
5 * 5 *
6 * Copyright (C) 2010 Andreas Mohr <andi@lisas.de>
6 * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de> 7 * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>
7 * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com> 8 * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
8 * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> 9 * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
9 * Copyright (c) 2002-2003 TiVo Inc. 10 * Copyright (c) 2002-2003 TiVo Inc.
10 * 11 *
12 * Definitions gathered from MOSCHIP, Data Sheet_7830DA.pdf (thanks!).
13 *
14 * TODO:
15 * - support HIF_REG_CONFIG_SLEEPMODE/HIF_REG_CONFIG_TXENABLE (via autopm?)
16 * - implement ethtool_ops get_pauseparam/set_pauseparam
17 * via HIF_REG_PAUSE_THRESHOLD (>= revision C only!)
18 * - implement get_eeprom/[set_eeprom]
19 * - switch PHY on/off on ifup/ifdown (perhaps in usbnet.c, via MII)
20 * - mcs7830_get_regs() handling is weird: for rev 2 we return 32 regs,
21 * can access only ~ 24, remaining user buffer is uninitialized garbage
22 * - anything else?
23 *
24 *
11 * This program is free software; you can redistribute it and/or modify 25 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by 26 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or 27 * the Free Software Foundation; either version 2 of the License, or
@@ -30,6 +44,7 @@
30#include <linux/mii.h> 44#include <linux/mii.h>
31#include <linux/module.h> 45#include <linux/module.h>
32#include <linux/netdevice.h> 46#include <linux/netdevice.h>
47#include <linux/slab.h>
33#include <linux/usb.h> 48#include <linux/usb.h>
34#include <linux/usb/usbnet.h> 49#include <linux/usb/usbnet.h>
35 50
@@ -55,7 +70,7 @@
55 ADVERTISE_100HALF | ADVERTISE_10FULL | \ 70 ADVERTISE_100HALF | ADVERTISE_10FULL | \
56 ADVERTISE_10HALF | ADVERTISE_CSMA) 71 ADVERTISE_10HALF | ADVERTISE_CSMA)
57 72
58/* HIF_REG_XX coressponding index value */ 73/* HIF_REG_XX corresponding index value */
59enum { 74enum {
60 HIF_REG_MULTICAST_HASH = 0x00, 75 HIF_REG_MULTICAST_HASH = 0x00,
61 HIF_REG_PACKET_GAP1 = 0x08, 76 HIF_REG_PACKET_GAP1 = 0x08,
@@ -69,6 +84,7 @@ enum {
69 HIF_REG_PHY_CMD2_PEND_FLAG_BIT = 0x80, 84 HIF_REG_PHY_CMD2_PEND_FLAG_BIT = 0x80,
70 HIF_REG_PHY_CMD2_READY_FLAG_BIT = 0x40, 85 HIF_REG_PHY_CMD2_READY_FLAG_BIT = 0x40,
71 HIF_REG_CONFIG = 0x0e, 86 HIF_REG_CONFIG = 0x0e,
87 /* hmm, spec sez: "R/W", "Except bit 3" (likely TXENABLE). */
72 HIF_REG_CONFIG_CFG = 0x80, 88 HIF_REG_CONFIG_CFG = 0x80,
73 HIF_REG_CONFIG_SPEED100 = 0x40, 89 HIF_REG_CONFIG_SPEED100 = 0x40,
74 HIF_REG_CONFIG_FULLDUPLEX_ENABLE = 0x20, 90 HIF_REG_CONFIG_FULLDUPLEX_ENABLE = 0x20,
@@ -76,13 +92,24 @@ enum {
76 HIF_REG_CONFIG_TXENABLE = 0x08, 92 HIF_REG_CONFIG_TXENABLE = 0x08,
77 HIF_REG_CONFIG_SLEEPMODE = 0x04, 93 HIF_REG_CONFIG_SLEEPMODE = 0x04,
78 HIF_REG_CONFIG_ALLMULTICAST = 0x02, 94 HIF_REG_CONFIG_ALLMULTICAST = 0x02,
79 HIF_REG_CONFIG_PROMISCIOUS = 0x01, 95 HIF_REG_CONFIG_PROMISCUOUS = 0x01,
80 HIF_REG_ETHERNET_ADDR = 0x0f, 96 HIF_REG_ETHERNET_ADDR = 0x0f,
81 HIF_REG_22 = 0x15, 97 HIF_REG_FRAME_DROP_COUNTER = 0x15, /* 0..ff; reset: 0 */
82 HIF_REG_PAUSE_THRESHOLD = 0x16, 98 HIF_REG_PAUSE_THRESHOLD = 0x16,
83 HIF_REG_PAUSE_THRESHOLD_DEFAULT = 0, 99 HIF_REG_PAUSE_THRESHOLD_DEFAULT = 0,
84}; 100};
85 101
102/* Trailing status byte in Ethernet Rx frame */
103enum {
104 MCS7830_RX_SHORT_FRAME = 0x01, /* < 64 bytes */
105 MCS7830_RX_LENGTH_ERROR = 0x02, /* framelen != Ethernet length field */
106 MCS7830_RX_ALIGNMENT_ERROR = 0x04, /* non-even number of nibbles */
107 MCS7830_RX_CRC_ERROR = 0x08,
108 MCS7830_RX_LARGE_FRAME = 0x10, /* > 1518 bytes */
109 MCS7830_RX_FRAME_CORRECT = 0x20, /* frame is correct */
110 /* [7:6] reserved */
111};
112
86struct mcs7830_data { 113struct mcs7830_data {
87 u8 multi_filter[8]; 114 u8 multi_filter[8];
88 u8 config; 115 u8 config;
@@ -109,7 +136,7 @@ static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data)
109 return ret; 136 return ret;
110} 137}
111 138
112static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, void *data) 139static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, const void *data)
113{ 140{
114 struct usb_device *xdev = dev->udev; 141 struct usb_device *xdev = dev->udev;
115 int ret; 142 int ret;
@@ -183,13 +210,43 @@ out:
183 usb_free_urb(urb); 210 usb_free_urb(urb);
184} 211}
185 212
186static int mcs7830_get_address(struct usbnet *dev) 213static int mcs7830_hif_get_mac_address(struct usbnet *dev, unsigned char *addr)
214{
215 int ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr);
216 if (ret < 0)
217 return ret;
218 return 0;
219}
220
221static int mcs7830_hif_set_mac_address(struct usbnet *dev, unsigned char *addr)
222{
223 int ret = mcs7830_set_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr);
224
225 if (ret < 0)
226 return ret;
227 return 0;
228}
229
230static int mcs7830_set_mac_address(struct net_device *netdev, void *p)
187{ 231{
188 int ret; 232 int ret;
189 ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, 233 struct usbnet *dev = netdev_priv(netdev);
190 dev->net->dev_addr); 234 struct sockaddr *addr = p;
235
236 if (netif_running(netdev))
237 return -EBUSY;
238
239 if (!is_valid_ether_addr(addr->sa_data))
240 return -EINVAL;
241
242 ret = mcs7830_hif_set_mac_address(dev, addr->sa_data);
243
191 if (ret < 0) 244 if (ret < 0)
192 return ret; 245 return ret;
246
247 /* it worked --> adopt it on netdev side */
248 memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
249
193 return 0; 250 return 0;
194} 251}
195 252
@@ -307,7 +364,7 @@ static int mcs7830_get_rev(struct usbnet *dev)
307{ 364{
308 u8 dummy[2]; 365 u8 dummy[2];
309 int ret; 366 int ret;
310 ret = mcs7830_get_reg(dev, HIF_REG_22, 2, dummy); 367 ret = mcs7830_get_reg(dev, HIF_REG_FRAME_DROP_COUNTER, 2, dummy);
311 if (ret > 0) 368 if (ret > 0)
312 return 2; /* Rev C or later */ 369 return 2; /* Rev C or later */
313 return 1; /* earlier revision */ 370 return 1; /* earlier revision */
@@ -331,33 +388,6 @@ static void mcs7830_rev_C_fixup(struct usbnet *dev)
331 } 388 }
332} 389}
333 390
334static int mcs7830_init_dev(struct usbnet *dev)
335{
336 int ret;
337 int retry;
338
339 /* Read MAC address from EEPROM */
340 ret = -EINVAL;
341 for (retry = 0; retry < 5 && ret; retry++)
342 ret = mcs7830_get_address(dev);
343 if (ret) {
344 dev_warn(&dev->udev->dev, "Cannot read MAC address\n");
345 goto out;
346 }
347
348 /* Set up PHY */
349 ret = mcs7830_set_autoneg(dev, 0);
350 if (ret) {
351 dev_info(&dev->udev->dev, "Cannot set autoneg\n");
352 goto out;
353 }
354
355 mcs7830_rev_C_fixup(dev);
356 ret = 0;
357out:
358 return ret;
359}
360
361static int mcs7830_mdio_read(struct net_device *netdev, int phy_id, 391static int mcs7830_mdio_read(struct net_device *netdev, int phy_id,
362 int location) 392 int location)
363{ 393{
@@ -378,11 +408,33 @@ static int mcs7830_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
378 return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); 408 return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
379} 409}
380 410
381/* credits go to asix_set_multicast */ 411static inline struct mcs7830_data *mcs7830_get_data(struct usbnet *dev)
382static void mcs7830_set_multicast(struct net_device *net) 412{
413 return (struct mcs7830_data *)&dev->data;
414}
415
416static void mcs7830_hif_update_multicast_hash(struct usbnet *dev)
417{
418 struct mcs7830_data *data = mcs7830_get_data(dev);
419 mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH,
420 sizeof data->multi_filter,
421 data->multi_filter);
422}
423
424static void mcs7830_hif_update_config(struct usbnet *dev)
425{
426 /* implementation specific to data->config
427 (argument needs to be heap-based anyway - USB DMA!) */
428 struct mcs7830_data *data = mcs7830_get_data(dev);
429 mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config);
430}
431
432static void mcs7830_data_set_multicast(struct net_device *net)
383{ 433{
384 struct usbnet *dev = netdev_priv(net); 434 struct usbnet *dev = netdev_priv(net);
385 struct mcs7830_data *data = (struct mcs7830_data *)&dev->data; 435 struct mcs7830_data *data = mcs7830_get_data(dev);
436
437 memset(data->multi_filter, 0, sizeof data->multi_filter);
386 438
387 data->config = HIF_REG_CONFIG_TXENABLE; 439 data->config = HIF_REG_CONFIG_TXENABLE;
388 440
@@ -390,36 +442,64 @@ static void mcs7830_set_multicast(struct net_device *net)
390 data->config |= HIF_REG_CONFIG_ALLMULTICAST; 442 data->config |= HIF_REG_CONFIG_ALLMULTICAST;
391 443
392 if (net->flags & IFF_PROMISC) { 444 if (net->flags & IFF_PROMISC) {
393 data->config |= HIF_REG_CONFIG_PROMISCIOUS; 445 data->config |= HIF_REG_CONFIG_PROMISCUOUS;
394 } else if (net->flags & IFF_ALLMULTI 446 } else if (net->flags & IFF_ALLMULTI ||
395 || net->mc_count > MCS7830_MAX_MCAST) { 447 netdev_mc_count(net) > MCS7830_MAX_MCAST) {
396 data->config |= HIF_REG_CONFIG_ALLMULTICAST; 448 data->config |= HIF_REG_CONFIG_ALLMULTICAST;
397 } else if (net->mc_count == 0) { 449 } else if (netdev_mc_empty(net)) {
398 /* just broadcast and directed */ 450 /* just broadcast and directed */
399 } else { 451 } else {
400 /* We use the 20 byte dev->data 452 /* We use the 20 byte dev->data
401 * for our 8 byte filter buffer 453 * for our 8 byte filter buffer
402 * to avoid allocating memory that 454 * to avoid allocating memory that
403 * is tricky to free later */ 455 * is tricky to free later */
404 struct dev_mc_list *mc_list = net->mc_list; 456 struct dev_mc_list *mc_list;
405 u32 crc_bits; 457 u32 crc_bits;
406 int i;
407
408 memset(data->multi_filter, 0, sizeof data->multi_filter);
409 458
410 /* Build the multicast hash filter. */ 459 /* Build the multicast hash filter. */
411 for (i = 0; i < net->mc_count; i++) { 460 netdev_for_each_mc_addr(mc_list, net) {
412 crc_bits = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26; 461 crc_bits = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26;
413 data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7); 462 data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7);
414 mc_list = mc_list->next;
415 } 463 }
464 }
465}
416 466
417 mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH, 467static int mcs7830_apply_base_config(struct usbnet *dev)
418 sizeof data->multi_filter, 468{
419 data->multi_filter); 469 int ret;
470
471 /* re-configure known MAC (suspend case etc.) */
472 ret = mcs7830_hif_set_mac_address(dev, dev->net->dev_addr);
473 if (ret) {
474 dev_info(&dev->udev->dev, "Cannot set MAC address\n");
475 goto out;
420 } 476 }
421 477
422 mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config); 478 /* Set up PHY */
479 ret = mcs7830_set_autoneg(dev, 0);
480 if (ret) {
481 dev_info(&dev->udev->dev, "Cannot set autoneg\n");
482 goto out;
483 }
484
485 mcs7830_hif_update_multicast_hash(dev);
486 mcs7830_hif_update_config(dev);
487
488 mcs7830_rev_C_fixup(dev);
489 ret = 0;
490out:
491 return ret;
492}
493
494/* credits go to asix_set_multicast */
495static void mcs7830_set_multicast(struct net_device *net)
496{
497 struct usbnet *dev = netdev_priv(net);
498
499 mcs7830_data_set_multicast(net);
500
501 mcs7830_hif_update_multicast_hash(dev);
502 mcs7830_hif_update_config(dev);
423} 503}
424 504
425static int mcs7830_get_regs_len(struct net_device *net) 505static int mcs7830_get_regs_len(struct net_device *net)
@@ -463,29 +543,6 @@ static const struct ethtool_ops mcs7830_ethtool_ops = {
463 .nway_reset = usbnet_nway_reset, 543 .nway_reset = usbnet_nway_reset,
464}; 544};
465 545
466static int mcs7830_set_mac_address(struct net_device *netdev, void *p)
467{
468 int ret;
469 struct usbnet *dev = netdev_priv(netdev);
470 struct sockaddr *addr = p;
471
472 if (netif_running(netdev))
473 return -EBUSY;
474
475 if (!is_valid_ether_addr(addr->sa_data))
476 return -EINVAL;
477
478 memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
479
480 ret = mcs7830_set_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN,
481 netdev->dev_addr);
482
483 if (ret < 0)
484 return ret;
485
486 return 0;
487}
488
489static const struct net_device_ops mcs7830_netdev_ops = { 546static const struct net_device_ops mcs7830_netdev_ops = {
490 .ndo_open = usbnet_open, 547 .ndo_open = usbnet_open,
491 .ndo_stop = usbnet_stop, 548 .ndo_stop = usbnet_stop,
@@ -495,21 +552,32 @@ static const struct net_device_ops mcs7830_netdev_ops = {
495 .ndo_validate_addr = eth_validate_addr, 552 .ndo_validate_addr = eth_validate_addr,
496 .ndo_do_ioctl = mcs7830_ioctl, 553 .ndo_do_ioctl = mcs7830_ioctl,
497 .ndo_set_multicast_list = mcs7830_set_multicast, 554 .ndo_set_multicast_list = mcs7830_set_multicast,
498 .ndo_set_mac_address = mcs7830_set_mac_address, 555 .ndo_set_mac_address = mcs7830_set_mac_address,
499}; 556};
500 557
501static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev) 558static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev)
502{ 559{
503 struct net_device *net = dev->net; 560 struct net_device *net = dev->net;
504 int ret; 561 int ret;
562 int retry;
505 563
506 ret = mcs7830_init_dev(dev); 564 /* Initial startup: Gather MAC address setting from EEPROM */
565 ret = -EINVAL;
566 for (retry = 0; retry < 5 && ret; retry++)
567 ret = mcs7830_hif_get_mac_address(dev, net->dev_addr);
568 if (ret) {
569 dev_warn(&dev->udev->dev, "Cannot read MAC address\n");
570 goto out;
571 }
572
573 mcs7830_data_set_multicast(net);
574
575 ret = mcs7830_apply_base_config(dev);
507 if (ret) 576 if (ret)
508 goto out; 577 goto out;
509 578
510 net->ethtool_ops = &mcs7830_ethtool_ops; 579 net->ethtool_ops = &mcs7830_ethtool_ops;
511 net->netdev_ops = &mcs7830_netdev_ops; 580 net->netdev_ops = &mcs7830_netdev_ops;
512 mcs7830_set_multicast(net);
513 581
514 /* reserve space for the status byte on rx */ 582 /* reserve space for the status byte on rx */
515 dev->rx_urb_size = ETH_FRAME_LEN + 1; 583 dev->rx_urb_size = ETH_FRAME_LEN + 1;
@@ -526,7 +594,7 @@ out:
526 return ret; 594 return ret;
527} 595}
528 596
529/* The chip always appends a status bytes that we need to strip */ 597/* The chip always appends a status byte that we need to strip */
530static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 598static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
531{ 599{
532 u8 status; 600 u8 status;
@@ -539,9 +607,23 @@ static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
539 skb_trim(skb, skb->len - 1); 607 skb_trim(skb, skb->len - 1);
540 status = skb->data[skb->len]; 608 status = skb->data[skb->len];
541 609
542 if (status != 0x20) 610 if (status != MCS7830_RX_FRAME_CORRECT) {
543 dev_dbg(&dev->udev->dev, "rx fixup status %x\n", status); 611 dev_dbg(&dev->udev->dev, "rx fixup status %x\n", status);
544 612
613 /* hmm, perhaps usbnet.c already sees a globally visible
614 frame error and increments rx_errors on its own already? */
615 dev->net->stats.rx_errors++;
616
617 if (status & (MCS7830_RX_SHORT_FRAME
618 |MCS7830_RX_LENGTH_ERROR
619 |MCS7830_RX_LARGE_FRAME))
620 dev->net->stats.rx_length_errors++;
621 if (status & MCS7830_RX_ALIGNMENT_ERROR)
622 dev->net->stats.rx_frame_errors++;
623 if (status & MCS7830_RX_CRC_ERROR)
624 dev->net->stats.rx_crc_errors++;
625 }
626
545 return skb->len > 0; 627 return skb->len > 0;
546} 628}
547 629
@@ -580,6 +662,20 @@ static const struct usb_device_id products[] = {
580}; 662};
581MODULE_DEVICE_TABLE(usb, products); 663MODULE_DEVICE_TABLE(usb, products);
582 664
665static int mcs7830_reset_resume (struct usb_interface *intf)
666{
667 /* YES, this function is successful enough that ethtool -d
668 does show same output pre-/post-suspend */
669
670 struct usbnet *dev = usb_get_intfdata(intf);
671
672 mcs7830_apply_base_config(dev);
673
674 usbnet_resume(intf);
675
676 return 0;
677}
678
583static struct usb_driver mcs7830_driver = { 679static struct usb_driver mcs7830_driver = {
584 .name = driver_name, 680 .name = driver_name,
585 .id_table = products, 681 .id_table = products,
@@ -587,6 +683,7 @@ static struct usb_driver mcs7830_driver = {
587 .disconnect = usbnet_disconnect, 683 .disconnect = usbnet_disconnect,
588 .suspend = usbnet_suspend, 684 .suspend = usbnet_suspend,
589 .resume = usbnet_resume, 685 .resume = usbnet_resume,
686 .reset_resume = mcs7830_reset_resume,
590}; 687};
591 688
592static int __init mcs7830_init(void) 689static int __init mcs7830_init(void)