diff options
Diffstat (limited to 'drivers/net/usb/ch9200.c')
-rw-r--r-- | drivers/net/usb/ch9200.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/drivers/net/usb/ch9200.c b/drivers/net/usb/ch9200.c new file mode 100644 index 000000000000..5e151e6a3e09 --- /dev/null +++ b/drivers/net/usb/ch9200.c | |||
@@ -0,0 +1,432 @@ | |||
1 | /* | ||
2 | * USB 10M/100M ethernet adapter | ||
3 | * | ||
4 | * This file is licensed under the terms of the GNU General Public License | ||
5 | * version 2. This program is licensed "as is" without any warranty of any | ||
6 | * kind, whether express or implied | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/stddef.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/netdevice.h> | ||
16 | #include <linux/etherdevice.h> | ||
17 | #include <linux/ethtool.h> | ||
18 | #include <linux/mii.h> | ||
19 | #include <linux/usb.h> | ||
20 | #include <linux/crc32.h> | ||
21 | #include <linux/usb/usbnet.h> | ||
22 | #include <linux/slab.h> | ||
23 | |||
24 | #define CH9200_VID 0x1A86 | ||
25 | #define CH9200_PID_E092 0xE092 | ||
26 | |||
27 | #define CTRL_TIMEOUT_MS 1000 | ||
28 | |||
29 | #define CONTROL_TIMEOUT_MS 1000 | ||
30 | |||
31 | #define REQUEST_READ 0x0E | ||
32 | #define REQUEST_WRITE 0x0F | ||
33 | |||
34 | /* Address space: | ||
35 | * 00-63 : MII | ||
36 | * 64-128: MAC | ||
37 | * | ||
38 | * Note: all accesses must be 16-bit | ||
39 | */ | ||
40 | |||
41 | #define MAC_REG_CTRL 64 | ||
42 | #define MAC_REG_STATUS 66 | ||
43 | #define MAC_REG_INTERRUPT_MASK 68 | ||
44 | #define MAC_REG_PHY_COMMAND 70 | ||
45 | #define MAC_REG_PHY_DATA 72 | ||
46 | #define MAC_REG_STATION_L 74 | ||
47 | #define MAC_REG_STATION_M 76 | ||
48 | #define MAC_REG_STATION_H 78 | ||
49 | #define MAC_REG_HASH_L 80 | ||
50 | #define MAC_REG_HASH_M1 82 | ||
51 | #define MAC_REG_HASH_M2 84 | ||
52 | #define MAC_REG_HASH_H 86 | ||
53 | #define MAC_REG_THRESHOLD 88 | ||
54 | #define MAC_REG_FIFO_DEPTH 90 | ||
55 | #define MAC_REG_PAUSE 92 | ||
56 | #define MAC_REG_FLOW_CONTROL 94 | ||
57 | |||
58 | /* Control register bits | ||
59 | * | ||
60 | * Note: bits 13 and 15 are reserved | ||
61 | */ | ||
62 | #define LOOPBACK (0x01 << 14) | ||
63 | #define BASE100X (0x01 << 12) | ||
64 | #define MBPS_10 (0x01 << 11) | ||
65 | #define DUPLEX_MODE (0x01 << 10) | ||
66 | #define PAUSE_FRAME (0x01 << 9) | ||
67 | #define PROMISCUOUS (0x01 << 8) | ||
68 | #define MULTICAST (0x01 << 7) | ||
69 | #define BROADCAST (0x01 << 6) | ||
70 | #define HASH (0x01 << 5) | ||
71 | #define APPEND_PAD (0x01 << 4) | ||
72 | #define APPEND_CRC (0x01 << 3) | ||
73 | #define TRANSMITTER_ACTION (0x01 << 2) | ||
74 | #define RECEIVER_ACTION (0x01 << 1) | ||
75 | #define DMA_ACTION (0x01 << 0) | ||
76 | |||
77 | /* Status register bits | ||
78 | * | ||
79 | * Note: bits 7-15 are reserved | ||
80 | */ | ||
81 | #define ALIGNMENT (0x01 << 6) | ||
82 | #define FIFO_OVER_RUN (0x01 << 5) | ||
83 | #define FIFO_UNDER_RUN (0x01 << 4) | ||
84 | #define RX_ERROR (0x01 << 3) | ||
85 | #define RX_COMPLETE (0x01 << 2) | ||
86 | #define TX_ERROR (0x01 << 1) | ||
87 | #define TX_COMPLETE (0x01 << 0) | ||
88 | |||
89 | /* FIFO depth register bits | ||
90 | * | ||
91 | * Note: bits 6 and 14 are reserved | ||
92 | */ | ||
93 | |||
94 | #define ETH_TXBD (0x01 << 15) | ||
95 | #define ETN_TX_FIFO_DEPTH (0x01 << 8) | ||
96 | #define ETH_RXBD (0x01 << 7) | ||
97 | #define ETH_RX_FIFO_DEPTH (0x01 << 0) | ||
98 | |||
99 | static int control_read(struct usbnet *dev, | ||
100 | unsigned char request, unsigned short value, | ||
101 | unsigned short index, void *data, unsigned short size, | ||
102 | int timeout) | ||
103 | { | ||
104 | unsigned char *buf = NULL; | ||
105 | unsigned char request_type; | ||
106 | int err = 0; | ||
107 | |||
108 | if (request == REQUEST_READ) | ||
109 | request_type = (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER); | ||
110 | else | ||
111 | request_type = (USB_DIR_IN | USB_TYPE_VENDOR | | ||
112 | USB_RECIP_DEVICE); | ||
113 | |||
114 | netdev_dbg(dev->net, "Control_read() index=0x%02x size=%d\n", | ||
115 | index, size); | ||
116 | |||
117 | buf = kmalloc(size, GFP_KERNEL); | ||
118 | if (!buf) { | ||
119 | err = -ENOMEM; | ||
120 | goto err_out; | ||
121 | } | ||
122 | |||
123 | err = usb_control_msg(dev->udev, | ||
124 | usb_rcvctrlpipe(dev->udev, 0), | ||
125 | request, request_type, value, index, buf, size, | ||
126 | timeout); | ||
127 | if (err == size) | ||
128 | memcpy(data, buf, size); | ||
129 | else if (err >= 0) | ||
130 | err = -EINVAL; | ||
131 | kfree(buf); | ||
132 | |||
133 | return err; | ||
134 | |||
135 | err_out: | ||
136 | return err; | ||
137 | } | ||
138 | |||
139 | static int control_write(struct usbnet *dev, unsigned char request, | ||
140 | unsigned short value, unsigned short index, | ||
141 | void *data, unsigned short size, int timeout) | ||
142 | { | ||
143 | unsigned char *buf = NULL; | ||
144 | unsigned char request_type; | ||
145 | int err = 0; | ||
146 | |||
147 | if (request == REQUEST_WRITE) | ||
148 | request_type = (USB_DIR_OUT | USB_TYPE_VENDOR | | ||
149 | USB_RECIP_OTHER); | ||
150 | else | ||
151 | request_type = (USB_DIR_OUT | USB_TYPE_VENDOR | | ||
152 | USB_RECIP_DEVICE); | ||
153 | |||
154 | netdev_dbg(dev->net, "Control_write() index=0x%02x size=%d\n", | ||
155 | index, size); | ||
156 | |||
157 | if (data) { | ||
158 | buf = kmalloc(size, GFP_KERNEL); | ||
159 | if (!buf) { | ||
160 | err = -ENOMEM; | ||
161 | goto err_out; | ||
162 | } | ||
163 | memcpy(buf, data, size); | ||
164 | } | ||
165 | |||
166 | err = usb_control_msg(dev->udev, | ||
167 | usb_sndctrlpipe(dev->udev, 0), | ||
168 | request, request_type, value, index, buf, size, | ||
169 | timeout); | ||
170 | if (err >= 0 && err < size) | ||
171 | err = -EINVAL; | ||
172 | kfree(buf); | ||
173 | |||
174 | return 0; | ||
175 | |||
176 | err_out: | ||
177 | return err; | ||
178 | } | ||
179 | |||
180 | static int ch9200_mdio_read(struct net_device *netdev, int phy_id, int loc) | ||
181 | { | ||
182 | struct usbnet *dev = netdev_priv(netdev); | ||
183 | unsigned char buff[2]; | ||
184 | |||
185 | netdev_dbg(netdev, "ch9200_mdio_read phy_id:%02x loc:%02x\n", | ||
186 | phy_id, loc); | ||
187 | |||
188 | if (phy_id != 0) | ||
189 | return -ENODEV; | ||
190 | |||
191 | control_read(dev, REQUEST_READ, 0, loc * 2, buff, 0x02, | ||
192 | CONTROL_TIMEOUT_MS); | ||
193 | |||
194 | return (buff[0] | buff[1] << 8); | ||
195 | } | ||
196 | |||
197 | static void ch9200_mdio_write(struct net_device *netdev, | ||
198 | int phy_id, int loc, int val) | ||
199 | { | ||
200 | struct usbnet *dev = netdev_priv(netdev); | ||
201 | unsigned char buff[2]; | ||
202 | |||
203 | netdev_dbg(netdev, "ch9200_mdio_write() phy_id=%02x loc:%02x\n", | ||
204 | phy_id, loc); | ||
205 | |||
206 | if (phy_id != 0) | ||
207 | return; | ||
208 | |||
209 | buff[0] = (unsigned char)val; | ||
210 | buff[1] = (unsigned char)(val >> 8); | ||
211 | |||
212 | control_write(dev, REQUEST_WRITE, 0, loc * 2, buff, 0x02, | ||
213 | CONTROL_TIMEOUT_MS); | ||
214 | } | ||
215 | |||
216 | static int ch9200_link_reset(struct usbnet *dev) | ||
217 | { | ||
218 | struct ethtool_cmd ecmd; | ||
219 | |||
220 | mii_check_media(&dev->mii, 1, 1); | ||
221 | mii_ethtool_gset(&dev->mii, &ecmd); | ||
222 | |||
223 | netdev_dbg(dev->net, "link_reset() speed:%d duplex:%d\n", | ||
224 | ecmd.speed, ecmd.duplex); | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static void ch9200_status(struct usbnet *dev, struct urb *urb) | ||
230 | { | ||
231 | int link; | ||
232 | unsigned char *buf; | ||
233 | |||
234 | if (urb->actual_length < 16) | ||
235 | return; | ||
236 | |||
237 | buf = urb->transfer_buffer; | ||
238 | link = !!(buf[0] & 0x01); | ||
239 | |||
240 | if (link) { | ||
241 | netif_carrier_on(dev->net); | ||
242 | usbnet_defer_kevent(dev, EVENT_LINK_RESET); | ||
243 | } else { | ||
244 | netif_carrier_off(dev->net); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | static struct sk_buff *ch9200_tx_fixup(struct usbnet *dev, struct sk_buff *skb, | ||
249 | gfp_t flags) | ||
250 | { | ||
251 | int i = 0; | ||
252 | int len = 0; | ||
253 | int tx_overhead = 0; | ||
254 | |||
255 | tx_overhead = 0x40; | ||
256 | |||
257 | len = skb->len; | ||
258 | if (skb_headroom(skb) < tx_overhead) { | ||
259 | struct sk_buff *skb2; | ||
260 | |||
261 | skb2 = skb_copy_expand(skb, tx_overhead, 0, flags); | ||
262 | dev_kfree_skb_any(skb); | ||
263 | skb = skb2; | ||
264 | if (!skb) | ||
265 | return NULL; | ||
266 | } | ||
267 | |||
268 | __skb_push(skb, tx_overhead); | ||
269 | /* usbnet adds padding if length is a multiple of packet size | ||
270 | * if so, adjust length value in header | ||
271 | */ | ||
272 | if ((skb->len % dev->maxpacket) == 0) | ||
273 | len++; | ||
274 | |||
275 | skb->data[0] = len; | ||
276 | skb->data[1] = len >> 8; | ||
277 | skb->data[2] = 0x00; | ||
278 | skb->data[3] = 0x80; | ||
279 | |||
280 | for (i = 4; i < 48; i++) | ||
281 | skb->data[i] = 0x00; | ||
282 | |||
283 | skb->data[48] = len; | ||
284 | skb->data[49] = len >> 8; | ||
285 | skb->data[50] = 0x00; | ||
286 | skb->data[51] = 0x80; | ||
287 | |||
288 | for (i = 52; i < 64; i++) | ||
289 | skb->data[i] = 0x00; | ||
290 | |||
291 | return skb; | ||
292 | } | ||
293 | |||
294 | static int ch9200_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | ||
295 | { | ||
296 | int len = 0; | ||
297 | int rx_overhead = 0; | ||
298 | |||
299 | rx_overhead = 64; | ||
300 | |||
301 | if (unlikely(skb->len < rx_overhead)) { | ||
302 | dev_err(&dev->udev->dev, "unexpected tiny rx frame\n"); | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | len = (skb->data[skb->len - 16] | skb->data[skb->len - 15] << 8); | ||
307 | skb_trim(skb, len); | ||
308 | |||
309 | return 1; | ||
310 | } | ||
311 | |||
312 | static int get_mac_address(struct usbnet *dev, unsigned char *data) | ||
313 | { | ||
314 | int err = 0; | ||
315 | unsigned char mac_addr[0x06]; | ||
316 | int rd_mac_len = 0; | ||
317 | |||
318 | netdev_dbg(dev->net, "get_mac_address:\n\tusbnet VID:%0x PID:%0x\n", | ||
319 | dev->udev->descriptor.idVendor, | ||
320 | dev->udev->descriptor.idProduct); | ||
321 | |||
322 | memset(mac_addr, 0, sizeof(mac_addr)); | ||
323 | rd_mac_len = control_read(dev, REQUEST_READ, 0, | ||
324 | MAC_REG_STATION_L, mac_addr, 0x02, | ||
325 | CONTROL_TIMEOUT_MS); | ||
326 | rd_mac_len += control_read(dev, REQUEST_READ, 0, MAC_REG_STATION_M, | ||
327 | mac_addr + 2, 0x02, CONTROL_TIMEOUT_MS); | ||
328 | rd_mac_len += control_read(dev, REQUEST_READ, 0, MAC_REG_STATION_H, | ||
329 | mac_addr + 4, 0x02, CONTROL_TIMEOUT_MS); | ||
330 | if (rd_mac_len != ETH_ALEN) | ||
331 | err = -EINVAL; | ||
332 | |||
333 | data[0] = mac_addr[5]; | ||
334 | data[1] = mac_addr[4]; | ||
335 | data[2] = mac_addr[3]; | ||
336 | data[3] = mac_addr[2]; | ||
337 | data[4] = mac_addr[1]; | ||
338 | data[5] = mac_addr[0]; | ||
339 | |||
340 | return err; | ||
341 | } | ||
342 | |||
343 | static int ch9200_bind(struct usbnet *dev, struct usb_interface *intf) | ||
344 | { | ||
345 | int retval = 0; | ||
346 | unsigned char data[2]; | ||
347 | |||
348 | retval = usbnet_get_endpoints(dev, intf); | ||
349 | if (retval) | ||
350 | return retval; | ||
351 | |||
352 | dev->mii.dev = dev->net; | ||
353 | dev->mii.mdio_read = ch9200_mdio_read; | ||
354 | dev->mii.mdio_write = ch9200_mdio_write; | ||
355 | dev->mii.reg_num_mask = 0x1f; | ||
356 | |||
357 | dev->mii.phy_id_mask = 0x1f; | ||
358 | |||
359 | dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; | ||
360 | dev->rx_urb_size = 24 * 64 + 16; | ||
361 | mii_nway_restart(&dev->mii); | ||
362 | |||
363 | data[0] = 0x01; | ||
364 | data[1] = 0x0F; | ||
365 | retval = control_write(dev, REQUEST_WRITE, 0, MAC_REG_THRESHOLD, data, | ||
366 | 0x02, CONTROL_TIMEOUT_MS); | ||
367 | |||
368 | data[0] = 0xA0; | ||
369 | data[1] = 0x90; | ||
370 | retval = control_write(dev, REQUEST_WRITE, 0, MAC_REG_FIFO_DEPTH, data, | ||
371 | 0x02, CONTROL_TIMEOUT_MS); | ||
372 | |||
373 | data[0] = 0x30; | ||
374 | data[1] = 0x00; | ||
375 | retval = control_write(dev, REQUEST_WRITE, 0, MAC_REG_PAUSE, data, | ||
376 | 0x02, CONTROL_TIMEOUT_MS); | ||
377 | |||
378 | data[0] = 0x17; | ||
379 | data[1] = 0xD8; | ||
380 | retval = control_write(dev, REQUEST_WRITE, 0, MAC_REG_FLOW_CONTROL, | ||
381 | data, 0x02, CONTROL_TIMEOUT_MS); | ||
382 | |||
383 | /* Undocumented register */ | ||
384 | data[0] = 0x01; | ||
385 | data[1] = 0x00; | ||
386 | retval = control_write(dev, REQUEST_WRITE, 0, 254, data, 0x02, | ||
387 | CONTROL_TIMEOUT_MS); | ||
388 | |||
389 | data[0] = 0x5F; | ||
390 | data[1] = 0x0D; | ||
391 | retval = control_write(dev, REQUEST_WRITE, 0, MAC_REG_CTRL, data, 0x02, | ||
392 | CONTROL_TIMEOUT_MS); | ||
393 | |||
394 | retval = get_mac_address(dev, dev->net->dev_addr); | ||
395 | |||
396 | return retval; | ||
397 | } | ||
398 | |||
399 | static const struct driver_info ch9200_info = { | ||
400 | .description = "CH9200 USB to Network Adaptor", | ||
401 | .flags = FLAG_ETHER, | ||
402 | .bind = ch9200_bind, | ||
403 | .rx_fixup = ch9200_rx_fixup, | ||
404 | .tx_fixup = ch9200_tx_fixup, | ||
405 | .status = ch9200_status, | ||
406 | .link_reset = ch9200_link_reset, | ||
407 | .reset = ch9200_link_reset, | ||
408 | }; | ||
409 | |||
410 | static const struct usb_device_id ch9200_products[] = { | ||
411 | { | ||
412 | USB_DEVICE(0x1A86, 0xE092), | ||
413 | .driver_info = (unsigned long)&ch9200_info, | ||
414 | }, | ||
415 | {}, | ||
416 | }; | ||
417 | |||
418 | MODULE_DEVICE_TABLE(usb, ch9200_products); | ||
419 | |||
420 | static struct usb_driver ch9200_driver = { | ||
421 | .name = "ch9200", | ||
422 | .id_table = ch9200_products, | ||
423 | .probe = usbnet_probe, | ||
424 | .disconnect = usbnet_disconnect, | ||
425 | .suspend = usbnet_suspend, | ||
426 | .resume = usbnet_resume, | ||
427 | }; | ||
428 | |||
429 | module_usb_driver(ch9200_driver); | ||
430 | |||
431 | MODULE_DESCRIPTION("QinHeng CH9200 USB Network device"); | ||
432 | MODULE_LICENSE("GPL"); | ||