Blame view

drivers/net/usb/mcs7830.c 16.8 KB
2a36d7083   Arnd Bergmann   USB: driver for m...
1
  /*
bbccc16c8   Andreas Mohr   net: Add USB PID ...
2
   * MOSCHIP MCS7830 based (7730/7830/7832) USB 2.0 Ethernet Devices
2a36d7083   Arnd Bergmann   USB: driver for m...
3
4
5
   *
   * based on usbnet.c, asix.c and the vendor provided mcs7830 driver
   *
76802851b   Andreas Mohr   MCS7830 USB-Ether...
6
   * Copyright (C) 2010 Andreas Mohr <andi@lisas.de>
2a36d7083   Arnd Bergmann   USB: driver for m...
7
8
9
10
11
   * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>
   * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
   * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
   * Copyright (c) 2002-2003 TiVo Inc.
   *
76802851b   Andreas Mohr   MCS7830 USB-Ether...
12
13
   * Definitions gathered from MOSCHIP, Data Sheet_7830DA.pdf (thanks!).
   *
bbccc16c8   Andreas Mohr   net: Add USB PID ...
14
15
16
   * 2010-12-19: add 7832 USB PID ("functionality same as MCS7830"),
   *             per active notification by manufacturer
   *
76802851b   Andreas Mohr   MCS7830 USB-Ether...
17
   * TODO:
76802851b   Andreas Mohr   MCS7830 USB-Ether...
18
19
20
21
22
23
24
25
26
27
   * - support HIF_REG_CONFIG_SLEEPMODE/HIF_REG_CONFIG_TXENABLE (via autopm?)
   * - implement ethtool_ops get_pauseparam/set_pauseparam
   *   via HIF_REG_PAUSE_THRESHOLD (>= revision C only!)
   * - implement get_eeprom/[set_eeprom]
   * - switch PHY on/off on ifup/ifdown (perhaps in usbnet.c, via MII)
   * - mcs7830_get_regs() handling is weird: for rev 2 we return 32 regs,
   *   can access only ~ 24, remaining user buffer is uninitialized garbage
   * - anything else?
   *
   *
2a36d7083   Arnd Bergmann   USB: driver for m...
28
29
30
31
32
33
34
35
36
37
38
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
9cb00073d   Jeff Kirsher   usb: Fix FSF addr...
39
   * along with this program; if not, see <http://www.gnu.org/licenses/>.
2a36d7083   Arnd Bergmann   USB: driver for m...
40
41
42
43
44
   */
  
  #include <linux/crc32.h>
  #include <linux/etherdevice.h>
  #include <linux/ethtool.h>
2a36d7083   Arnd Bergmann   USB: driver for m...
45
46
47
  #include <linux/mii.h>
  #include <linux/module.h>
  #include <linux/netdevice.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
48
  #include <linux/slab.h>
2a36d7083   Arnd Bergmann   USB: driver for m...
49
  #include <linux/usb.h>
3692e94f1   Jussi Kivilinna   Move usbnet.h and...
50
  #include <linux/usb/usbnet.h>
2a36d7083   Arnd Bergmann   USB: driver for m...
51
52
53
54
55
56
57
58
59
60
61
62
63
  
  /* requests */
  #define MCS7830_RD_BMREQ	(USB_DIR_IN  | USB_TYPE_VENDOR | \
  				 USB_RECIP_DEVICE)
  #define MCS7830_WR_BMREQ	(USB_DIR_OUT | USB_TYPE_VENDOR | \
  				 USB_RECIP_DEVICE)
  #define MCS7830_RD_BREQ		0x0E
  #define MCS7830_WR_BREQ		0x0D
  
  #define MCS7830_CTRL_TIMEOUT	1000
  #define MCS7830_MAX_MCAST	64
  
  #define MCS7830_VENDOR_ID	0x9710
bbccc16c8   Andreas Mohr   net: Add USB PID ...
64
  #define MCS7832_PRODUCT_ID	0x7832
2a36d7083   Arnd Bergmann   USB: driver for m...
65
  #define MCS7830_PRODUCT_ID	0x7830
8382cc1c2   Arnd Bergmann   net/usb/mcs7830: ...
66
67
68
69
  #define MCS7730_PRODUCT_ID	0x7730
  
  #define SITECOM_VENDOR_ID	0x0DF6
  #define LN_030_PRODUCT_ID	0x0021
2a36d7083   Arnd Bergmann   USB: driver for m...
70
71
72
73
  
  #define MCS7830_MII_ADVERTISE	(ADVERTISE_PAUSE_CAP | ADVERTISE_100FULL | \
  				 ADVERTISE_100HALF | ADVERTISE_10FULL | \
  				 ADVERTISE_10HALF | ADVERTISE_CSMA)
00e499131   Andreas Mohr   MCS7830 USB-Ether...
74
  /* HIF_REG_XX corresponding index value */
2a36d7083   Arnd Bergmann   USB: driver for m...
75
76
77
78
79
80
81
82
83
84
85
86
87
  enum {
  	HIF_REG_MULTICAST_HASH			= 0x00,
  	HIF_REG_PACKET_GAP1			= 0x08,
  	HIF_REG_PACKET_GAP2			= 0x09,
  	HIF_REG_PHY_DATA			= 0x0a,
  	HIF_REG_PHY_CMD1			= 0x0c,
  	   HIF_REG_PHY_CMD1_READ		= 0x40,
  	   HIF_REG_PHY_CMD1_WRITE		= 0x20,
  	   HIF_REG_PHY_CMD1_PHYADDR		= 0x01,
  	HIF_REG_PHY_CMD2			= 0x0d,
  	   HIF_REG_PHY_CMD2_PEND_FLAG_BIT	= 0x80,
  	   HIF_REG_PHY_CMD2_READY_FLAG_BIT	= 0x40,
  	HIF_REG_CONFIG				= 0x0e,
c774651a5   Andreas Mohr   MCS7830 USB-Ether...
88
  	/* hmm, spec sez: "R/W", "Except bit 3" (likely TXENABLE). */
2a36d7083   Arnd Bergmann   USB: driver for m...
89
90
91
92
93
94
95
  	   HIF_REG_CONFIG_CFG			= 0x80,
  	   HIF_REG_CONFIG_SPEED100		= 0x40,
  	   HIF_REG_CONFIG_FULLDUPLEX_ENABLE	= 0x20,
  	   HIF_REG_CONFIG_RXENABLE		= 0x10,
  	   HIF_REG_CONFIG_TXENABLE		= 0x08,
  	   HIF_REG_CONFIG_SLEEPMODE		= 0x04,
  	   HIF_REG_CONFIG_ALLMULTICAST		= 0x02,
00e499131   Andreas Mohr   MCS7830 USB-Ether...
96
  	   HIF_REG_CONFIG_PROMISCUOUS		= 0x01,
2a36d7083   Arnd Bergmann   USB: driver for m...
97
  	HIF_REG_ETHERNET_ADDR			= 0x0f,
c774651a5   Andreas Mohr   MCS7830 USB-Ether...
98
  	HIF_REG_FRAME_DROP_COUNTER		= 0x15, /* 0..ff; reset: 0 */
2a36d7083   Arnd Bergmann   USB: driver for m...
99
100
101
  	HIF_REG_PAUSE_THRESHOLD			= 0x16,
  	   HIF_REG_PAUSE_THRESHOLD_DEFAULT	= 0,
  };
76802851b   Andreas Mohr   MCS7830 USB-Ether...
102
103
104
105
106
107
108
109
110
111
  /* Trailing status byte in Ethernet Rx frame */
  enum {
  	MCS7830_RX_SHORT_FRAME		= 0x01, /* < 64 bytes */
  	MCS7830_RX_LENGTH_ERROR		= 0x02, /* framelen != Ethernet length field */
  	MCS7830_RX_ALIGNMENT_ERROR	= 0x04, /* non-even number of nibbles */
  	MCS7830_RX_CRC_ERROR		= 0x08,
  	MCS7830_RX_LARGE_FRAME		= 0x10, /* > 1518 bytes */
  	MCS7830_RX_FRAME_CORRECT	= 0x20, /* frame is correct */
  	/* [7:6] reserved */
  };
2a36d7083   Arnd Bergmann   USB: driver for m...
112
113
114
115
116
117
118
119
120
  struct mcs7830_data {
  	u8 multi_filter[8];
  	u8 config;
  };
  
  static const char driver_name[] = "MOSCHIP usb-ethernet driver";
  
  static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data)
  {
9c06a2b58   Ming Lei   usbnet: mcs7830: ...
121
122
  	return usbnet_read_cmd(dev, MCS7830_RD_BREQ, MCS7830_RD_BMREQ,
  				0x0000, index, data, size);
2a36d7083   Arnd Bergmann   USB: driver for m...
123
  }
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
124
  static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, const void *data)
2a36d7083   Arnd Bergmann   USB: driver for m...
125
  {
9c06a2b58   Ming Lei   usbnet: mcs7830: ...
126
127
  	return usbnet_write_cmd(dev, MCS7830_WR_BREQ, MCS7830_WR_BMREQ,
  				0x0000, index, data, size);
2a36d7083   Arnd Bergmann   USB: driver for m...
128
129
130
131
  }
  
  static void mcs7830_set_reg_async(struct usbnet *dev, u16 index, u16 size, void *data)
  {
9c06a2b58   Ming Lei   usbnet: mcs7830: ...
132
133
  	usbnet_write_cmd_async(dev, MCS7830_WR_BREQ, MCS7830_WR_BMREQ,
  				0x0000, index, data, size);
2a36d7083   Arnd Bergmann   USB: driver for m...
134
  }
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  static int mcs7830_hif_get_mac_address(struct usbnet *dev, unsigned char *addr)
  {
  	int ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr);
  	if (ret < 0)
  		return ret;
  	return 0;
  }
  
  static int mcs7830_hif_set_mac_address(struct usbnet *dev, unsigned char *addr)
  {
  	int ret = mcs7830_set_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr);
  
  	if (ret < 0)
  		return ret;
  	return 0;
  }
  
  static int mcs7830_set_mac_address(struct net_device *netdev, void *p)
2a36d7083   Arnd Bergmann   USB: driver for m...
153
154
  {
  	int ret;
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
155
156
157
158
159
160
161
  	struct usbnet *dev = netdev_priv(netdev);
  	struct sockaddr *addr = p;
  
  	if (netif_running(netdev))
  		return -EBUSY;
  
  	if (!is_valid_ether_addr(addr->sa_data))
15b8f4cf8   Danny Kukawka   mcs7830: unify re...
162
  		return -EADDRNOTAVAIL;
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
163
164
  
  	ret = mcs7830_hif_set_mac_address(dev, addr->sa_data);
2a36d7083   Arnd Bergmann   USB: driver for m...
165
166
  	if (ret < 0)
  		return ret;
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
167
168
169
  
  	/* it worked --> adopt it on netdev side */
  	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
2a36d7083   Arnd Bergmann   USB: driver for m...
170
171
172
173
174
175
176
177
178
179
180
181
182
  	return 0;
  }
  
  static int mcs7830_read_phy(struct usbnet *dev, u8 index)
  {
  	int ret;
  	int i;
  	__le16 val;
  
  	u8 cmd[2] = {
  		HIF_REG_PHY_CMD1_READ | HIF_REG_PHY_CMD1_PHYADDR,
  		HIF_REG_PHY_CMD2_PEND_FLAG_BIT | index,
  	};
a9fc6338b   Arnd Bergmann   usbnet: add a mut...
183
  	mutex_lock(&dev->phy_mutex);
2a36d7083   Arnd Bergmann   USB: driver for m...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  	/* write the MII command */
  	ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
  	if (ret < 0)
  		goto out;
  
  	/* wait for the data to become valid, should be within < 1ms */
  	for (i = 0; i < 10; i++) {
  		ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
  		if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT))
  			break;
  		ret = -EIO;
  		msleep(1);
  	}
  	if (ret < 0)
  		goto out;
  
  	/* read actual register contents */
  	ret = mcs7830_get_reg(dev, HIF_REG_PHY_DATA, 2, &val);
  	if (ret < 0)
  		goto out;
  	ret = le16_to_cpu(val);
  	dev_dbg(&dev->udev->dev, "read PHY reg %02x: %04x (%d tries)
  ",
  		index, val, i);
  out:
a9fc6338b   Arnd Bergmann   usbnet: add a mut...
209
  	mutex_unlock(&dev->phy_mutex);
2a36d7083   Arnd Bergmann   USB: driver for m...
210
211
212
213
214
215
216
217
218
219
220
221
222
  	return ret;
  }
  
  static int mcs7830_write_phy(struct usbnet *dev, u8 index, u16 val)
  {
  	int ret;
  	int i;
  	__le16 le_val;
  
  	u8 cmd[2] = {
  		HIF_REG_PHY_CMD1_WRITE | HIF_REG_PHY_CMD1_PHYADDR,
  		HIF_REG_PHY_CMD2_PEND_FLAG_BIT | (index & 0x1F),
  	};
a9fc6338b   Arnd Bergmann   usbnet: add a mut...
223
  	mutex_lock(&dev->phy_mutex);
2a36d7083   Arnd Bergmann   USB: driver for m...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  	/* write the new register contents */
  	le_val = cpu_to_le16(val);
  	ret = mcs7830_set_reg(dev, HIF_REG_PHY_DATA, 2, &le_val);
  	if (ret < 0)
  		goto out;
  
  	/* write the MII command */
  	ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
  	if (ret < 0)
  		goto out;
  
  	/* wait for the command to be accepted by the PHY */
  	for (i = 0; i < 10; i++) {
  		ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
  		if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT))
  			break;
  		ret = -EIO;
  		msleep(1);
  	}
  	if (ret < 0)
  		goto out;
  
  	ret = 0;
  	dev_dbg(&dev->udev->dev, "write PHY reg %02x: %04x (%d tries)
  ",
  		index, val, i);
  out:
a9fc6338b   Arnd Bergmann   usbnet: add a mut...
251
  	mutex_unlock(&dev->phy_mutex);
2a36d7083   Arnd Bergmann   USB: driver for m...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  	return ret;
  }
  
  /*
   * This algorithm comes from the original mcs7830 version 1.4 driver,
   * not sure if it is needed.
   */
  static int mcs7830_set_autoneg(struct usbnet *dev, int ptrUserPhyMode)
  {
  	int ret;
  	/* Enable all media types */
  	ret = mcs7830_write_phy(dev, MII_ADVERTISE, MCS7830_MII_ADVERTISE);
  
  	/* First reset BMCR */
  	if (!ret)
  		ret = mcs7830_write_phy(dev, MII_BMCR, 0x0000);
  	/* Enable Auto Neg */
  	if (!ret)
  		ret = mcs7830_write_phy(dev, MII_BMCR, BMCR_ANENABLE);
  	/* Restart Auto Neg (Keep the Enable Auto Neg Bit Set) */
  	if (!ret)
  		ret = mcs7830_write_phy(dev, MII_BMCR,
  				BMCR_ANENABLE | BMCR_ANRESTART	);
0e214ad81   Dan Carpenter   USB: mcs7830: ret...
275
  	return ret;
2a36d7083   Arnd Bergmann   USB: driver for m...
276
277
278
279
280
281
282
283
284
285
  }
  
  
  /*
   * if we can read register 22, the chip revision is C or higher
   */
  static int mcs7830_get_rev(struct usbnet *dev)
  {
  	u8 dummy[2];
  	int ret;
c774651a5   Andreas Mohr   MCS7830 USB-Ether...
286
  	ret = mcs7830_get_reg(dev, HIF_REG_FRAME_DROP_COUNTER, 2, dummy);
2a36d7083   Arnd Bergmann   USB: driver for m...
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  	if (ret > 0)
  		return 2; /* Rev C or later */
  	return 1; /* earlier revision */
  }
  
  /*
   * On rev. C we need to set the pause threshold
   */
  static void mcs7830_rev_C_fixup(struct usbnet *dev)
  {
  	u8 pause_threshold = HIF_REG_PAUSE_THRESHOLD_DEFAULT;
  	int retry;
  
  	for (retry = 0; retry < 2; retry++) {
  		if (mcs7830_get_rev(dev) == 2) {
  			dev_info(&dev->udev->dev, "applying rev.C fixup
  ");
  			mcs7830_set_reg(dev, HIF_REG_PAUSE_THRESHOLD,
  					1, &pause_threshold);
  		}
  		msleep(1);
  	}
  }
2a36d7083   Arnd Bergmann   USB: driver for m...
310
311
312
  static int mcs7830_mdio_read(struct net_device *netdev, int phy_id,
  			     int location)
  {
8f15ea42b   Wang Chen   netdevice: safe c...
313
  	struct usbnet *dev = netdev_priv(netdev);
2a36d7083   Arnd Bergmann   USB: driver for m...
314
315
316
317
318
319
  	return mcs7830_read_phy(dev, location);
  }
  
  static void mcs7830_mdio_write(struct net_device *netdev, int phy_id,
  				int location, int val)
  {
8f15ea42b   Wang Chen   netdevice: safe c...
320
  	struct usbnet *dev = netdev_priv(netdev);
2a36d7083   Arnd Bergmann   USB: driver for m...
321
322
323
324
325
326
327
328
  	mcs7830_write_phy(dev, location, val);
  }
  
  static int mcs7830_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
  {
  	struct usbnet *dev = netdev_priv(net);
  	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
  }
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  static inline struct mcs7830_data *mcs7830_get_data(struct usbnet *dev)
  {
  	return (struct mcs7830_data *)&dev->data;
  }
  
  static void mcs7830_hif_update_multicast_hash(struct usbnet *dev)
  {
  	struct mcs7830_data *data = mcs7830_get_data(dev);
  	mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH,
  				sizeof data->multi_filter,
  				data->multi_filter);
  }
  
  static void mcs7830_hif_update_config(struct usbnet *dev)
  {
  	/* implementation specific to data->config
             (argument needs to be heap-based anyway - USB DMA!) */
  	struct mcs7830_data *data = mcs7830_get_data(dev);
  	mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config);
  }
  
  static void mcs7830_data_set_multicast(struct net_device *net)
2a36d7083   Arnd Bergmann   USB: driver for m...
351
352
  {
  	struct usbnet *dev = netdev_priv(net);
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
353
354
355
  	struct mcs7830_data *data = mcs7830_get_data(dev);
  
  	memset(data->multi_filter, 0, sizeof data->multi_filter);
2a36d7083   Arnd Bergmann   USB: driver for m...
356
357
358
359
360
361
362
  
  	data->config = HIF_REG_CONFIG_TXENABLE;
  
  	/* this should not be needed, but it doesn't work otherwise */
  	data->config |= HIF_REG_CONFIG_ALLMULTICAST;
  
  	if (net->flags & IFF_PROMISC) {
00e499131   Andreas Mohr   MCS7830 USB-Ether...
363
  		data->config |= HIF_REG_CONFIG_PROMISCUOUS;
8e95a2026   Joe Perches   drivers/net: Move...
364
  	} else if (net->flags & IFF_ALLMULTI ||
4cd24eaf0   Jiri Pirko   net: use netdev_m...
365
  		   netdev_mc_count(net) > MCS7830_MAX_MCAST) {
2a36d7083   Arnd Bergmann   USB: driver for m...
366
  		data->config |= HIF_REG_CONFIG_ALLMULTICAST;
4cd24eaf0   Jiri Pirko   net: use netdev_m...
367
  	} else if (netdev_mc_empty(net)) {
2a36d7083   Arnd Bergmann   USB: driver for m...
368
369
370
371
372
373
  		/* just broadcast and directed */
  	} else {
  		/* We use the 20 byte dev->data
  		 * for our 8 byte filter buffer
  		 * to avoid allocating memory that
  		 * is tricky to free later */
22bedad3c   Jiri Pirko   net: convert mult...
374
  		struct netdev_hw_addr *ha;
2a36d7083   Arnd Bergmann   USB: driver for m...
375
  		u32 crc_bits;
2a36d7083   Arnd Bergmann   USB: driver for m...
376

2a36d7083   Arnd Bergmann   USB: driver for m...
377
  		/* Build the multicast hash filter. */
22bedad3c   Jiri Pirko   net: convert mult...
378
379
  		netdev_for_each_mc_addr(ha, net) {
  			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
2a36d7083   Arnd Bergmann   USB: driver for m...
380
  			data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7);
2a36d7083   Arnd Bergmann   USB: driver for m...
381
  		}
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
382
383
  	}
  }
2a36d7083   Arnd Bergmann   USB: driver for m...
384

ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
385
386
387
388
389
390
391
392
393
394
  static int mcs7830_apply_base_config(struct usbnet *dev)
  {
  	int ret;
  
  	/* re-configure known MAC (suspend case etc.) */
  	ret = mcs7830_hif_set_mac_address(dev, dev->net->dev_addr);
  	if (ret) {
  		dev_info(&dev->udev->dev, "Cannot set MAC address
  ");
  		goto out;
2a36d7083   Arnd Bergmann   USB: driver for m...
395
  	}
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
  	/* Set up PHY */
  	ret = mcs7830_set_autoneg(dev, 0);
  	if (ret) {
  		dev_info(&dev->udev->dev, "Cannot set autoneg
  ");
  		goto out;
  	}
  
  	mcs7830_hif_update_multicast_hash(dev);
  	mcs7830_hif_update_config(dev);
  
  	mcs7830_rev_C_fixup(dev);
  	ret = 0;
  out:
  	return ret;
  }
  
  /* credits go to asix_set_multicast */
  static void mcs7830_set_multicast(struct net_device *net)
  {
  	struct usbnet *dev = netdev_priv(net);
  
  	mcs7830_data_set_multicast(net);
  
  	mcs7830_hif_update_multicast_hash(dev);
  	mcs7830_hif_update_config(dev);
2a36d7083   Arnd Bergmann   USB: driver for m...
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
  }
  
  static int mcs7830_get_regs_len(struct net_device *net)
  {
  	struct usbnet *dev = netdev_priv(net);
  
  	switch (mcs7830_get_rev(dev)) {
  	case 1:
  		return 21;
  	case 2:
  		return 32;
  	}
  	return 0;
  }
  
  static void mcs7830_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *drvinfo)
  {
  	usbnet_get_drvinfo(net, drvinfo);
  	drvinfo->regdump_len = mcs7830_get_regs_len(net);
  }
  
  static void mcs7830_get_regs(struct net_device *net, struct ethtool_regs *regs, void *data)
  {
  	struct usbnet *dev = netdev_priv(net);
  
  	regs->version = mcs7830_get_rev(dev);
  	mcs7830_get_reg(dev, 0, regs->len, data);
  }
0fc0b732e   Stephen Hemminger   netdev: drivers s...
450
  static const struct ethtool_ops mcs7830_ethtool_ops = {
2a36d7083   Arnd Bergmann   USB: driver for m...
451
452
453
454
455
  	.get_drvinfo		= mcs7830_get_drvinfo,
  	.get_regs_len		= mcs7830_get_regs_len,
  	.get_regs		= mcs7830_get_regs,
  
  	/* common usbnet calls */
c41286fd4   Arnd Bergmann   usbnet: improve g...
456
  	.get_link		= usbnet_get_link,
2a36d7083   Arnd Bergmann   USB: driver for m...
457
458
  	.get_msglevel		= usbnet_get_msglevel,
  	.set_msglevel		= usbnet_set_msglevel,
c41286fd4   Arnd Bergmann   usbnet: improve g...
459
460
461
  	.get_settings		= usbnet_get_settings,
  	.set_settings		= usbnet_set_settings,
  	.nway_reset		= usbnet_nway_reset,
2a36d7083   Arnd Bergmann   USB: driver for m...
462
  };
e12c4f832   Stephen Hemminger   usbnet: convert m...
463
464
465
466
467
468
469
470
  static const struct net_device_ops mcs7830_netdev_ops = {
  	.ndo_open		= usbnet_open,
  	.ndo_stop		= usbnet_stop,
  	.ndo_start_xmit		= usbnet_start_xmit,
  	.ndo_tx_timeout		= usbnet_tx_timeout,
  	.ndo_change_mtu		= usbnet_change_mtu,
  	.ndo_validate_addr	= eth_validate_addr,
  	.ndo_do_ioctl 		= mcs7830_ioctl,
afc4b13df   Jiri Pirko   net: remove use o...
471
  	.ndo_set_rx_mode	= mcs7830_set_multicast,
00e499131   Andreas Mohr   MCS7830 USB-Ether...
472
  	.ndo_set_mac_address	= mcs7830_set_mac_address,
e12c4f832   Stephen Hemminger   usbnet: convert m...
473
  };
2a36d7083   Arnd Bergmann   USB: driver for m...
474
475
476
477
  static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev)
  {
  	struct net_device *net = dev->net;
  	int ret;
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
478
  	int retry;
2a36d7083   Arnd Bergmann   USB: driver for m...
479

ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
480
481
482
483
484
485
486
487
488
489
490
491
492
  	/* Initial startup: Gather MAC address setting from EEPROM */
  	ret = -EINVAL;
  	for (retry = 0; retry < 5 && ret; retry++)
  		ret = mcs7830_hif_get_mac_address(dev, net->dev_addr);
  	if (ret) {
  		dev_warn(&dev->udev->dev, "Cannot read MAC address
  ");
  		goto out;
  	}
  
  	mcs7830_data_set_multicast(net);
  
  	ret = mcs7830_apply_base_config(dev);
2a36d7083   Arnd Bergmann   USB: driver for m...
493
494
  	if (ret)
  		goto out;
2a36d7083   Arnd Bergmann   USB: driver for m...
495
  	net->ethtool_ops = &mcs7830_ethtool_ops;
e12c4f832   Stephen Hemminger   usbnet: convert m...
496
  	net->netdev_ops = &mcs7830_netdev_ops;
2a36d7083   Arnd Bergmann   USB: driver for m...
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  
  	/* reserve space for the status byte on rx */
  	dev->rx_urb_size = ETH_FRAME_LEN + 1;
  
  	dev->mii.mdio_read = mcs7830_mdio_read;
  	dev->mii.mdio_write = mcs7830_mdio_write;
  	dev->mii.dev = net;
  	dev->mii.phy_id_mask = 0x3f;
  	dev->mii.reg_num_mask = 0x1f;
  	dev->mii.phy_id = *((u8 *) net->dev_addr + 1);
  
  	ret = usbnet_get_endpoints(dev, udev);
  out:
  	return ret;
  }
00e499131   Andreas Mohr   MCS7830 USB-Ether...
512
  /* The chip always appends a status byte that we need to strip */
2a36d7083   Arnd Bergmann   USB: driver for m...
513
514
515
  static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
  {
  	u8 status;
eb85569fe   Emil Goode   usbnet: remove ge...
516
517
518
519
  	/* This check is no longer done by usbnet */
  	if (skb->len < dev->net->hard_header_len) {
  		dev_err(&dev->udev->dev, "unexpected tiny rx frame
  ");
2a36d7083   Arnd Bergmann   USB: driver for m...
520
521
522
523
524
  		return 0;
  	}
  
  	skb_trim(skb, skb->len - 1);
  	status = skb->data[skb->len];
76802851b   Andreas Mohr   MCS7830 USB-Ether...
525
  	if (status != MCS7830_RX_FRAME_CORRECT) {
2a36d7083   Arnd Bergmann   USB: driver for m...
526
527
  		dev_dbg(&dev->udev->dev, "rx fixup status %x
  ", status);
76802851b   Andreas Mohr   MCS7830 USB-Ether...
528
529
530
531
532
533
534
535
536
537
538
539
540
  		/* hmm, perhaps usbnet.c already sees a globally visible
  		   frame error and increments rx_errors on its own already? */
  		dev->net->stats.rx_errors++;
  
  		if (status &	(MCS7830_RX_SHORT_FRAME
  				|MCS7830_RX_LENGTH_ERROR
  				|MCS7830_RX_LARGE_FRAME))
  			dev->net->stats.rx_length_errors++;
  		if (status & MCS7830_RX_ALIGNMENT_ERROR)
  			dev->net->stats.rx_frame_errors++;
  		if (status & MCS7830_RX_CRC_ERROR)
  			dev->net->stats.rx_crc_errors++;
  	}
2a36d7083   Arnd Bergmann   USB: driver for m...
541
542
  	return skb->len > 0;
  }
b1ff4f96f   Ondrej Zary   mcs7830: Implemen...
543
544
545
  static void mcs7830_status(struct usbnet *dev, struct urb *urb)
  {
  	u8 *buf = urb->transfer_buffer;
dabdaf0ca   Ondrej Zary   mcs7830: Fix link...
546
  	bool link, link_changed;
b1ff4f96f   Ondrej Zary   mcs7830: Implemen...
547
548
549
  
  	if (urb->actual_length < 16)
  		return;
8d88bbffc   Octavian Purdila   usbnet: mcs7830: ...
550
  	link = !(buf[1] == 0x20);
dabdaf0ca   Ondrej Zary   mcs7830: Fix link...
551
552
  	link_changed = netif_carrier_ok(dev->net) != link;
  	if (link_changed) {
8d88bbffc   Octavian Purdila   usbnet: mcs7830: ...
553
554
555
556
  		usbnet_link_change(dev, link, 0);
  		netdev_dbg(dev->net, "Link Status is: %d
  ", link);
  	}
b1ff4f96f   Ondrej Zary   mcs7830: Implemen...
557
  }
2a36d7083   Arnd Bergmann   USB: driver for m...
558
  static const struct driver_info moschip_info = {
bbccc16c8   Andreas Mohr   net: Add USB PID ...
559
  	.description	= "MOSCHIP 7830/7832/7730 usb-NET adapter",
8382cc1c2   Arnd Bergmann   net/usb/mcs7830: ...
560
561
  	.bind		= mcs7830_bind,
  	.rx_fixup	= mcs7830_rx_fixup,
b1ff4f96f   Ondrej Zary   mcs7830: Implemen...
562
563
  	.flags		= FLAG_ETHER | FLAG_LINK_INTR,
  	.status		= mcs7830_status,
8382cc1c2   Arnd Bergmann   net/usb/mcs7830: ...
564
565
566
567
568
569
  	.in		= 1,
  	.out		= 2,
  };
  
  static const struct driver_info sitecom_info = {
  	.description    = "Sitecom LN-30 usb-NET adapter",
2a36d7083   Arnd Bergmann   USB: driver for m...
570
571
  	.bind		= mcs7830_bind,
  	.rx_fixup	= mcs7830_rx_fixup,
b1ff4f96f   Ondrej Zary   mcs7830: Implemen...
572
573
  	.flags		= FLAG_ETHER | FLAG_LINK_INTR,
  	.status		= mcs7830_status,
2a36d7083   Arnd Bergmann   USB: driver for m...
574
575
576
577
578
579
  	.in		= 1,
  	.out		= 2,
  };
  
  static const struct usb_device_id products[] = {
  	{
bbccc16c8   Andreas Mohr   net: Add USB PID ...
580
581
582
583
  		USB_DEVICE(MCS7830_VENDOR_ID, MCS7832_PRODUCT_ID),
  		.driver_info = (unsigned long) &moschip_info,
  	},
  	{
2a36d7083   Arnd Bergmann   USB: driver for m...
584
585
586
  		USB_DEVICE(MCS7830_VENDOR_ID, MCS7830_PRODUCT_ID),
  		.driver_info = (unsigned long) &moschip_info,
  	},
8382cc1c2   Arnd Bergmann   net/usb/mcs7830: ...
587
588
589
590
591
592
593
594
  	{
  		USB_DEVICE(MCS7830_VENDOR_ID, MCS7730_PRODUCT_ID),
  		.driver_info = (unsigned long) &moschip_info,
  	},
  	{
  		USB_DEVICE(SITECOM_VENDOR_ID, LN_030_PRODUCT_ID),
  		.driver_info = (unsigned long) &sitecom_info,
  	},
2a36d7083   Arnd Bergmann   USB: driver for m...
595
596
597
  	{},
  };
  MODULE_DEVICE_TABLE(usb, products);
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
598
599
600
601
602
603
604
605
606
607
608
609
610
  static int mcs7830_reset_resume (struct usb_interface *intf)
  {
   	/* YES, this function is successful enough that ethtool -d
             does show same output pre-/post-suspend */
  
  	struct usbnet		*dev = usb_get_intfdata(intf);
  
  	mcs7830_apply_base_config(dev);
  
  	usbnet_resume(intf);
  
  	return 0;
  }
2a36d7083   Arnd Bergmann   USB: driver for m...
611
612
613
614
615
616
617
  static struct usb_driver mcs7830_driver = {
  	.name = driver_name,
  	.id_table = products,
  	.probe = usbnet_probe,
  	.disconnect = usbnet_disconnect,
  	.suspend = usbnet_suspend,
  	.resume = usbnet_resume,
ace2a4d0f   Andreas Mohr   MCS7830 USB-Ether...
618
  	.reset_resume = mcs7830_reset_resume,
e1f12eb6b   Sarah Sharp   USB: Disable hub-...
619
  	.disable_hub_initiated_lpm = 1,
2a36d7083   Arnd Bergmann   USB: driver for m...
620
  };
d632eb1bf   Greg Kroah-Hartman   USB: convert driv...
621
  module_usb_driver(mcs7830_driver);
2a36d7083   Arnd Bergmann   USB: driver for m...
622
623
624
  
  MODULE_DESCRIPTION("USB to network adapter MCS7830)");
  MODULE_LICENSE("GPL");