aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/net/mcs7830.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2006-10-08 18:08:00 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-10-17 17:46:32 -0400
commit2a36d7083438ccb607055abae633f39495a99947 (patch)
tree622266ad628307fe8e9ceec355c910b8c70a53b1 /drivers/usb/net/mcs7830.c
parent27d39e2627dc7493f554bc0549d8c63953762478 (diff)
USB: driver for mcs7830 (aka DeLOCK) USB ethernet adapter
This driver adds support for the DeLOCK USB ethernet adapter and potentially others based on the MosChip MCS7830 chip. It is based on the usbnet and asix drivers as well as the original device driver provided by MosChip, which in turn was based on the usbnet driver. It has been tested successfully on an OHCI, but interestingly there seems to be a problem with the mcs7830 when connected to the ICH6/EHCI in my thinkpad: it keeps receiving lots of broken packets in the RX interrupt. The problem goes away when I'm using an active USB hub, so I assume it's not related to the device driver, but rather to the hardware. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/net/mcs7830.c')
-rw-r--r--drivers/usb/net/mcs7830.c525
1 files changed, 525 insertions, 0 deletions
diff --git a/drivers/usb/net/mcs7830.c b/drivers/usb/net/mcs7830.c
new file mode 100644
index 000000000000..0266090a1d7d
--- /dev/null
+++ b/drivers/usb/net/mcs7830.c
@@ -0,0 +1,525 @@
1/*
2 * MosChips MCS7830 based USB 2.0 Ethernet Devices
3 *
4 * based on usbnet.c, asix.c and the vendor provided mcs7830 driver
5 *
6 * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>
7 * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
8 * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
9 * Copyright (c) 2002-2003 TiVo Inc.
10 *
11 * 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
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#include <linux/crc32.h>
27#include <linux/etherdevice.h>
28#include <linux/ethtool.h>
29#include <linux/init.h>
30#include <linux/mii.h>
31#include <linux/module.h>
32#include <linux/netdevice.h>
33#include <linux/usb.h>
34
35#include "usbnet.h"
36
37/* requests */
38#define MCS7830_RD_BMREQ (USB_DIR_IN | USB_TYPE_VENDOR | \
39 USB_RECIP_DEVICE)
40#define MCS7830_WR_BMREQ (USB_DIR_OUT | USB_TYPE_VENDOR | \
41 USB_RECIP_DEVICE)
42#define MCS7830_RD_BREQ 0x0E
43#define MCS7830_WR_BREQ 0x0D
44
45#define MCS7830_CTRL_TIMEOUT 1000
46#define MCS7830_MAX_MCAST 64
47
48#define MCS7830_VENDOR_ID 0x9710
49#define MCS7830_PRODUCT_ID 0x7830
50
51#define MCS7830_MII_ADVERTISE (ADVERTISE_PAUSE_CAP | ADVERTISE_100FULL | \
52 ADVERTISE_100HALF | ADVERTISE_10FULL | \
53 ADVERTISE_10HALF | ADVERTISE_CSMA)
54
55/* HIF_REG_XX coressponding index value */
56enum {
57 HIF_REG_MULTICAST_HASH = 0x00,
58 HIF_REG_PACKET_GAP1 = 0x08,
59 HIF_REG_PACKET_GAP2 = 0x09,
60 HIF_REG_PHY_DATA = 0x0a,
61 HIF_REG_PHY_CMD1 = 0x0c,
62 HIF_REG_PHY_CMD1_READ = 0x40,
63 HIF_REG_PHY_CMD1_WRITE = 0x20,
64 HIF_REG_PHY_CMD1_PHYADDR = 0x01,
65 HIF_REG_PHY_CMD2 = 0x0d,
66 HIF_REG_PHY_CMD2_PEND_FLAG_BIT = 0x80,
67 HIF_REG_PHY_CMD2_READY_FLAG_BIT = 0x40,
68 HIF_REG_CONFIG = 0x0e,
69 HIF_REG_CONFIG_CFG = 0x80,
70 HIF_REG_CONFIG_SPEED100 = 0x40,
71 HIF_REG_CONFIG_FULLDUPLEX_ENABLE = 0x20,
72 HIF_REG_CONFIG_RXENABLE = 0x10,
73 HIF_REG_CONFIG_TXENABLE = 0x08,
74 HIF_REG_CONFIG_SLEEPMODE = 0x04,
75 HIF_REG_CONFIG_ALLMULTICAST = 0x02,
76 HIF_REG_CONFIG_PROMISCIOUS = 0x01,
77 HIF_REG_ETHERNET_ADDR = 0x0f,
78 HIF_REG_22 = 0x15,
79 HIF_REG_PAUSE_THRESHOLD = 0x16,
80 HIF_REG_PAUSE_THRESHOLD_DEFAULT = 0,
81};
82
83struct mcs7830_data {
84 u8 multi_filter[8];
85 u8 config;
86};
87
88static const char driver_name[] = "MOSCHIP usb-ethernet driver";
89
90static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data)
91{
92 struct usb_device *xdev = dev->udev;
93 int ret;
94
95 ret = usb_control_msg(xdev, usb_rcvctrlpipe(xdev, 0), MCS7830_RD_BREQ,
96 MCS7830_RD_BMREQ, 0x0000, index, data,
97 size, msecs_to_jiffies(MCS7830_CTRL_TIMEOUT));
98 return ret;
99}
100
101static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, void *data)
102{
103 struct usb_device *xdev = dev->udev;
104 int ret;
105
106 ret = usb_control_msg(xdev, usb_sndctrlpipe(xdev, 0), MCS7830_WR_BREQ,
107 MCS7830_WR_BMREQ, 0x0000, index, data,
108 size, msecs_to_jiffies(MCS7830_CTRL_TIMEOUT));
109 return ret;
110}
111
112static void mcs7830_async_cmd_callback(struct urb *urb)
113{
114 struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
115
116 if (urb->status < 0)
117 printk(KERN_DEBUG "mcs7830_async_cmd_callback() failed with %d",
118 urb->status);
119
120 kfree(req);
121 usb_free_urb(urb);
122}
123
124static void mcs7830_set_reg_async(struct usbnet *dev, u16 index, u16 size, void *data)
125{
126 struct usb_ctrlrequest *req;
127 int ret;
128 struct urb *urb;
129
130 urb = usb_alloc_urb(0, GFP_ATOMIC);
131 if (!urb) {
132 dev_dbg(&dev->udev->dev, "Error allocating URB "
133 "in write_cmd_async!");
134 return;
135 }
136
137 req = kmalloc(sizeof *req, GFP_ATOMIC);
138 if (!req) {
139 dev_err(&dev->udev->dev, "Failed to allocate memory for "
140 "control request");
141 goto out;
142 }
143 req->bRequestType = MCS7830_WR_BMREQ;
144 req->bRequest = MCS7830_WR_BREQ;
145 req->wValue = 0;
146 req->wIndex = cpu_to_le16(index);
147 req->wLength = cpu_to_le16(size);
148
149 usb_fill_control_urb(urb, dev->udev,
150 usb_sndctrlpipe(dev->udev, 0),
151 (void *)req, data, size,
152 mcs7830_async_cmd_callback, req);
153
154 ret = usb_submit_urb(urb, GFP_ATOMIC);
155 if (ret < 0) {
156 dev_err(&dev->udev->dev, "Error submitting the control "
157 "message: ret=%d", ret);
158 goto out;
159 }
160 return;
161out:
162 kfree(req);
163 usb_free_urb(urb);
164}
165
166static int mcs7830_get_address(struct usbnet *dev)
167{
168 int ret;
169 ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN,
170 dev->net->dev_addr);
171 if (ret < 0)
172 return ret;
173 return 0;
174}
175
176static int mcs7830_read_phy(struct usbnet *dev, u8 index)
177{
178 int ret;
179 int i;
180 __le16 val;
181
182 u8 cmd[2] = {
183 HIF_REG_PHY_CMD1_READ | HIF_REG_PHY_CMD1_PHYADDR,
184 HIF_REG_PHY_CMD2_PEND_FLAG_BIT | index,
185 };
186
187 /* write the MII command */
188 ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
189 if (ret < 0)
190 goto out;
191
192 /* wait for the data to become valid, should be within < 1ms */
193 for (i = 0; i < 10; i++) {
194 ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
195 if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT))
196 break;
197 ret = -EIO;
198 msleep(1);
199 }
200 if (ret < 0)
201 goto out;
202
203 /* read actual register contents */
204 ret = mcs7830_get_reg(dev, HIF_REG_PHY_DATA, 2, &val);
205 if (ret < 0)
206 goto out;
207 ret = le16_to_cpu(val);
208 dev_dbg(&dev->udev->dev, "read PHY reg %02x: %04x (%d tries)\n",
209 index, val, i);
210out:
211 return ret;
212}
213
214static int mcs7830_write_phy(struct usbnet *dev, u8 index, u16 val)
215{
216 int ret;
217 int i;
218 __le16 le_val;
219
220 u8 cmd[2] = {
221 HIF_REG_PHY_CMD1_WRITE | HIF_REG_PHY_CMD1_PHYADDR,
222 HIF_REG_PHY_CMD2_PEND_FLAG_BIT | (index & 0x1F),
223 };
224
225 /* write the new register contents */
226 le_val = cpu_to_le16(val);
227 ret = mcs7830_set_reg(dev, HIF_REG_PHY_DATA, 2, &le_val);
228 if (ret < 0)
229 goto out;
230
231 /* write the MII command */
232 ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
233 if (ret < 0)
234 goto out;
235
236 /* wait for the command to be accepted by the PHY */
237 for (i = 0; i < 10; i++) {
238 ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
239 if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT))
240 break;
241 ret = -EIO;
242 msleep(1);
243 }
244 if (ret < 0)
245 goto out;
246
247 ret = 0;
248 dev_dbg(&dev->udev->dev, "write PHY reg %02x: %04x (%d tries)\n",
249 index, val, i);
250out:
251 return ret;
252}
253
254/*
255 * This algorithm comes from the original mcs7830 version 1.4 driver,
256 * not sure if it is needed.
257 */
258static int mcs7830_set_autoneg(struct usbnet *dev, int ptrUserPhyMode)
259{
260 int ret;
261 /* Enable all media types */
262 ret = mcs7830_write_phy(dev, MII_ADVERTISE, MCS7830_MII_ADVERTISE);
263
264 /* First reset BMCR */
265 if (!ret)
266 ret = mcs7830_write_phy(dev, MII_BMCR, 0x0000);
267 /* Enable Auto Neg */
268 if (!ret)
269 ret = mcs7830_write_phy(dev, MII_BMCR, BMCR_ANENABLE);
270 /* Restart Auto Neg (Keep the Enable Auto Neg Bit Set) */
271 if (!ret)
272 ret = mcs7830_write_phy(dev, MII_BMCR,
273 BMCR_ANENABLE | BMCR_ANRESTART );
274 return ret < 0 ? : 0;
275}
276
277
278/*
279 * if we can read register 22, the chip revision is C or higher
280 */
281static int mcs7830_get_rev(struct usbnet *dev)
282{
283 u8 dummy[2];
284 int ret;
285 ret = mcs7830_get_reg(dev, HIF_REG_22, 2, dummy);
286 if (ret > 0)
287 return 2; /* Rev C or later */
288 return 1; /* earlier revision */
289}
290
291/*
292 * On rev. C we need to set the pause threshold
293 */
294static void mcs7830_rev_C_fixup(struct usbnet *dev)
295{
296 u8 pause_threshold = HIF_REG_PAUSE_THRESHOLD_DEFAULT;
297 int retry;
298
299 for (retry = 0; retry < 2; retry++) {
300 if (mcs7830_get_rev(dev) == 2) {
301 dev_info(&dev->udev->dev, "applying rev.C fixup\n");
302 mcs7830_set_reg(dev, HIF_REG_PAUSE_THRESHOLD,
303 1, &pause_threshold);
304 }
305 msleep(1);
306 }
307}
308
309static int mcs7830_init_dev(struct usbnet *dev)
310{
311 int ret;
312 int retry;
313
314 /* Read MAC address from EEPROM */
315 ret = -EINVAL;
316 for (retry = 0; retry < 5 && ret; retry++)
317 ret = mcs7830_get_address(dev);
318 if (ret) {
319 dev_warn(&dev->udev->dev, "Cannot read MAC address\n");
320 goto out;
321 }
322
323 /* Set up PHY */
324 ret = mcs7830_set_autoneg(dev, 0);
325 if (ret) {
326 dev_info(&dev->udev->dev, "Cannot set autoneg\n");
327 goto out;
328 }
329
330 mcs7830_rev_C_fixup(dev);
331 ret = 0;
332out:
333 return ret;
334}
335
336static int mcs7830_mdio_read(struct net_device *netdev, int phy_id,
337 int location)
338{
339 struct usbnet *dev = netdev->priv;
340 return mcs7830_read_phy(dev, location);
341}
342
343static void mcs7830_mdio_write(struct net_device *netdev, int phy_id,
344 int location, int val)
345{
346 struct usbnet *dev = netdev->priv;
347 mcs7830_write_phy(dev, location, val);
348}
349
350static int mcs7830_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
351{
352 struct usbnet *dev = netdev_priv(net);
353 return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
354}
355
356/* credits go to asix_set_multicast */
357static void mcs7830_set_multicast(struct net_device *net)
358{
359 struct usbnet *dev = netdev_priv(net);
360 struct mcs7830_data *data = (struct mcs7830_data *)&dev->data;
361
362 data->config = HIF_REG_CONFIG_TXENABLE;
363
364 /* this should not be needed, but it doesn't work otherwise */
365 data->config |= HIF_REG_CONFIG_ALLMULTICAST;
366
367 if (net->flags & IFF_PROMISC) {
368 data->config |= HIF_REG_CONFIG_PROMISCIOUS;
369 } else if (net->flags & IFF_ALLMULTI
370 || net->mc_count > MCS7830_MAX_MCAST) {
371 data->config |= HIF_REG_CONFIG_ALLMULTICAST;
372 } else if (net->mc_count == 0) {
373 /* just broadcast and directed */
374 } else {
375 /* We use the 20 byte dev->data
376 * for our 8 byte filter buffer
377 * to avoid allocating memory that
378 * is tricky to free later */
379 struct dev_mc_list *mc_list = net->mc_list;
380 u32 crc_bits;
381 int i;
382
383 memset(data->multi_filter, 0, sizeof data->multi_filter);
384
385 /* Build the multicast hash filter. */
386 for (i = 0; i < net->mc_count; i++) {
387 crc_bits = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26;
388 data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7);
389 mc_list = mc_list->next;
390 }
391
392 mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH,
393 sizeof data->multi_filter,
394 data->multi_filter);
395 }
396
397 mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config);
398}
399
400static int mcs7830_get_regs_len(struct net_device *net)
401{
402 struct usbnet *dev = netdev_priv(net);
403
404 switch (mcs7830_get_rev(dev)) {
405 case 1:
406 return 21;
407 case 2:
408 return 32;
409 }
410 return 0;
411}
412
413static void mcs7830_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *drvinfo)
414{
415 usbnet_get_drvinfo(net, drvinfo);
416 drvinfo->regdump_len = mcs7830_get_regs_len(net);
417}
418
419static void mcs7830_get_regs(struct net_device *net, struct ethtool_regs *regs, void *data)
420{
421 struct usbnet *dev = netdev_priv(net);
422
423 regs->version = mcs7830_get_rev(dev);
424 mcs7830_get_reg(dev, 0, regs->len, data);
425}
426
427static struct ethtool_ops mcs7830_ethtool_ops = {
428 .get_drvinfo = mcs7830_get_drvinfo,
429 .get_regs_len = mcs7830_get_regs_len,
430 .get_regs = mcs7830_get_regs,
431
432 /* common usbnet calls */
433 .get_msglevel = usbnet_get_msglevel,
434 .set_msglevel = usbnet_set_msglevel,
435};
436
437static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev)
438{
439 struct net_device *net = dev->net;
440 int ret;
441
442 ret = mcs7830_init_dev(dev);
443 if (ret)
444 goto out;
445
446 net->do_ioctl = mcs7830_ioctl;
447 net->ethtool_ops = &mcs7830_ethtool_ops;
448 net->set_multicast_list = mcs7830_set_multicast;
449 mcs7830_set_multicast(net);
450
451 /* reserve space for the status byte on rx */
452 dev->rx_urb_size = ETH_FRAME_LEN + 1;
453
454 dev->mii.mdio_read = mcs7830_mdio_read;
455 dev->mii.mdio_write = mcs7830_mdio_write;
456 dev->mii.dev = net;
457 dev->mii.phy_id_mask = 0x3f;
458 dev->mii.reg_num_mask = 0x1f;
459 dev->mii.phy_id = *((u8 *) net->dev_addr + 1);
460
461 ret = usbnet_get_endpoints(dev, udev);
462out:
463 return ret;
464}
465
466/* The chip always appends a status bytes that we need to strip */
467static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
468{
469 u8 status;
470
471 if (skb->len == 0) {
472 dev_err(&dev->udev->dev, "unexpected empty rx frame\n");
473 return 0;
474 }
475
476 skb_trim(skb, skb->len - 1);
477 status = skb->data[skb->len];
478
479 if (status != 0x20)
480 dev_dbg(&dev->udev->dev, "rx fixup status %x\n", status);
481
482 return skb->len > 0;
483}
484
485static const struct driver_info moschip_info = {
486 .description = "MOSCHIP 7830 usb-NET adapter",
487 .bind = mcs7830_bind,
488 .rx_fixup = mcs7830_rx_fixup,
489 .flags = FLAG_ETHER,
490 .in = 1,
491 .out = 2,
492};
493
494static const struct usb_device_id products[] = {
495 {
496 USB_DEVICE(MCS7830_VENDOR_ID, MCS7830_PRODUCT_ID),
497 .driver_info = (unsigned long) &moschip_info,
498 },
499 {},
500};
501MODULE_DEVICE_TABLE(usb, products);
502
503static struct usb_driver mcs7830_driver = {
504 .name = driver_name,
505 .id_table = products,
506 .probe = usbnet_probe,
507 .disconnect = usbnet_disconnect,
508 .suspend = usbnet_suspend,
509 .resume = usbnet_resume,
510};
511
512static int __init mcs7830_init(void)
513{
514 return usb_register(&mcs7830_driver);
515}
516module_init(mcs7830_init);
517
518static void __exit mcs7830_exit(void)
519{
520 usb_deregister(&mcs7830_driver);
521}
522module_exit(mcs7830_exit);
523
524MODULE_DESCRIPTION("USB to network adapter MCS7830)");
525MODULE_LICENSE("GPL");