xref: /openbmc/u-boot/drivers/usb/eth/mcs7830.c (revision baefb63a)
1 /*
2  * Copyright (c) 2013 Gerhard Sittig <gsi@denx.de>
3  * based on the U-Boot Asix driver as well as information
4  * from the Linux Moschip driver
5  *
6  * SPDX-License-Identifier:	GPL-2.0+
7  */
8 
9 /*
10  * MOSCHIP MCS7830 based (7730/7830/7832) USB 2.0 Ethernet Devices
11  */
12 
13 #include <common.h>
14 #include <dm.h>
15 #include <errno.h>
16 #include <linux/mii.h>
17 #include <malloc.h>
18 #include <memalign.h>
19 #include <usb.h>
20 
21 #include "usb_ether.h"
22 
23 #define MCS7830_BASE_NAME	"mcs"
24 
25 #define USBCALL_TIMEOUT		1000
26 #define LINKSTATUS_TIMEOUT	5000	/* link status, connect timeout */
27 #define LINKSTATUS_TIMEOUT_RES	50	/* link status, resolution in msec */
28 
29 #define MCS7830_RX_URB_SIZE	2048
30 
31 /* command opcodes */
32 #define MCS7830_WR_BREQ		0x0d
33 #define MCS7830_RD_BREQ		0x0e
34 
35 /* register layout, numerical offset specs for USB API calls */
36 struct mcs7830_regs {
37 	uint8_t multicast_hashes[8];
38 	uint8_t packet_gap[2];
39 	uint8_t phy_data[2];
40 	uint8_t phy_command[2];
41 	uint8_t configuration;
42 	uint8_t ether_address[6];
43 	uint8_t frame_drop_count;
44 	uint8_t pause_threshold;
45 };
46 #define REG_MULTICAST_HASH	offsetof(struct mcs7830_regs, multicast_hashes)
47 #define REG_PHY_DATA		offsetof(struct mcs7830_regs, phy_data)
48 #define REG_PHY_CMD		offsetof(struct mcs7830_regs, phy_command)
49 #define REG_CONFIG		offsetof(struct mcs7830_regs, configuration)
50 #define REG_ETHER_ADDR		offsetof(struct mcs7830_regs, ether_address)
51 #define REG_FRAME_DROP_COUNTER	offsetof(struct mcs7830_regs, frame_drop_count)
52 #define REG_PAUSE_THRESHOLD	offsetof(struct mcs7830_regs, pause_threshold)
53 
54 /* bit masks and default values for the above registers */
55 #define PHY_CMD1_READ		0x40
56 #define PHY_CMD1_WRITE		0x20
57 #define PHY_CMD1_PHYADDR	0x01
58 
59 #define PHY_CMD2_PEND		0x80
60 #define PHY_CMD2_READY		0x40
61 
62 #define CONF_CFG		0x80
63 #define CONF_SPEED100		0x40
64 #define CONF_FDX_ENABLE		0x20
65 #define CONF_RXENABLE		0x10
66 #define CONF_TXENABLE		0x08
67 #define CONF_SLEEPMODE		0x04
68 #define CONF_ALLMULTICAST	0x02
69 #define CONF_PROMISCUOUS	0x01
70 
71 #define PAUSE_THRESHOLD_DEFAULT	0
72 
73 /* bit masks for the status byte which follows received ethernet frames */
74 #define STAT_RX_FRAME_CORRECT	0x20
75 #define STAT_RX_LARGE_FRAME	0x10
76 #define STAT_RX_CRC_ERROR	0x08
77 #define STAT_RX_ALIGNMENT_ERROR	0x04
78 #define STAT_RX_LENGTH_ERROR	0x02
79 #define STAT_RX_SHORT_FRAME	0x01
80 
81 /*
82  * struct mcs7830_private - private driver data for an individual adapter
83  * @config:	shadow for the network adapter's configuration register
84  * @mchash:	shadow for the network adapter's multicast hash registers
85  */
86 struct mcs7830_private {
87 #ifdef CONFIG_DM_ETH
88 	uint8_t rx_buf[MCS7830_RX_URB_SIZE];
89 	struct ueth_data ueth;
90 #endif
91 	uint8_t config;
92 	uint8_t mchash[8];
93 };
94 
95 /*
96  * mcs7830_read_reg() - read a register of the network adapter
97  * @udev:	network device to read from
98  * @idx:	index of the register to start reading from
99  * @size:	number of bytes to read
100  * @data:	buffer to read into
101  * Return: zero upon success, negative upon error
102  */
103 static int mcs7830_read_reg(struct usb_device *udev, uint8_t idx,
104 			    uint16_t size, void *data)
105 {
106 	int len;
107 	ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, size);
108 
109 	debug("%s() idx=0x%04X sz=%d\n", __func__, idx, size);
110 
111 	len = usb_control_msg(udev,
112 			      usb_rcvctrlpipe(udev, 0),
113 			      MCS7830_RD_BREQ,
114 			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
115 			      0, idx, buf, size,
116 			      USBCALL_TIMEOUT);
117 	if (len != size) {
118 		debug("%s() len=%d != sz=%d\n", __func__, len, size);
119 		return -EIO;
120 	}
121 	memcpy(data, buf, size);
122 	return 0;
123 }
124 
125 /*
126  * mcs7830_write_reg() - write a register of the network adapter
127  * @udev:	network device to write to
128  * @idx:	index of the register to start writing to
129  * @size:	number of bytes to write
130  * @data:	buffer holding the data to write
131  * Return: zero upon success, negative upon error
132  */
133 static int mcs7830_write_reg(struct usb_device *udev, uint8_t idx,
134 			     uint16_t size, void *data)
135 {
136 	int len;
137 	ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, size);
138 
139 	debug("%s() idx=0x%04X sz=%d\n", __func__, idx, size);
140 
141 	memcpy(buf, data, size);
142 	len = usb_control_msg(udev,
143 			      usb_sndctrlpipe(udev, 0),
144 			      MCS7830_WR_BREQ,
145 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
146 			      0, idx, buf, size,
147 			      USBCALL_TIMEOUT);
148 	if (len != size) {
149 		debug("%s() len=%d != sz=%d\n", __func__, len, size);
150 		return -EIO;
151 	}
152 	return 0;
153 }
154 
155 /*
156  * mcs7830_phy_emit_wait() - emit PHY read/write access, wait for its execution
157  * @udev:	network device to talk to
158  * @rwflag:	PHY_CMD1_READ or PHY_CMD1_WRITE opcode
159  * @index:	number of the PHY register to read or write
160  * Return: zero upon success, negative upon error
161  */
162 static int mcs7830_phy_emit_wait(struct usb_device *udev,
163 				 uint8_t rwflag, uint8_t index)
164 {
165 	int rc;
166 	int retry;
167 	uint8_t cmd[2];
168 
169 	/* send the PHY read/write request */
170 	cmd[0] = rwflag | PHY_CMD1_PHYADDR;
171 	cmd[1] = PHY_CMD2_PEND | (index & 0x1f);
172 	rc = mcs7830_write_reg(udev, REG_PHY_CMD, sizeof(cmd), cmd);
173 	if (rc < 0)
174 		return rc;
175 
176 	/* wait for the response to become available (usually < 1ms) */
177 	retry = 10;
178 	do {
179 		rc = mcs7830_read_reg(udev, REG_PHY_CMD, sizeof(cmd), cmd);
180 		if (rc < 0)
181 			return rc;
182 		if (cmd[1] & PHY_CMD2_READY)
183 			return 0;
184 		if (!retry--)
185 			return -ETIMEDOUT;
186 		mdelay(1);
187 	} while (1);
188 	/* UNREACH */
189 }
190 
191 /*
192  * mcs7830_read_phy() - read a PHY register of the network adapter
193  * @udev:	network device to read from
194  * @index:	index of the PHY register to read from
195  * Return: non-negative 16bit register content, negative upon error
196  */
197 static int mcs7830_read_phy(struct usb_device *udev, uint8_t index)
198 {
199 	int rc;
200 	uint16_t val;
201 
202 	/* issue the PHY read request and wait for its execution */
203 	rc = mcs7830_phy_emit_wait(udev, PHY_CMD1_READ, index);
204 	if (rc < 0)
205 		return rc;
206 
207 	/* fetch the PHY data which was read */
208 	rc = mcs7830_read_reg(udev, REG_PHY_DATA, sizeof(val), &val);
209 	if (rc < 0)
210 		return rc;
211 	rc = le16_to_cpu(val);
212 	debug("%s(%d) => 0x%04X\n", __func__, index, rc);
213 	return rc;
214 }
215 
216 /*
217  * mcs7830_write_phy() - write a PHY register of the network adapter
218  * @udev:	network device to write to
219  * @index:	index of the PHY register to write to
220  * @val:	value to write to the PHY register
221  * Return: zero upon success, negative upon error
222  */
223 static int mcs7830_write_phy(struct usb_device *udev, uint8_t index,
224 			     uint16_t val)
225 {
226 	int rc;
227 
228 	debug("%s(%d, 0x%04X)\n", __func__, index, val);
229 
230 	/* setup the PHY data which is to get written */
231 	val = cpu_to_le16(val);
232 	rc = mcs7830_write_reg(udev, REG_PHY_DATA, sizeof(val), &val);
233 	if (rc < 0)
234 		return rc;
235 
236 	/* issue the PHY write request and wait for its execution */
237 	rc = mcs7830_phy_emit_wait(udev, PHY_CMD1_WRITE, index);
238 	if (rc < 0)
239 		return rc;
240 
241 	return 0;
242 }
243 
244 /*
245  * mcs7830_write_config() - write to the network adapter's config register
246  * @udev:	network device to write to
247  * @priv:	private data
248  * Return: zero upon success, negative upon error
249  *
250  * the data which gets written is taken from the shadow config register
251  * within the device driver's private data
252  */
253 static int mcs7830_write_config(struct usb_device *udev,
254 				struct mcs7830_private *priv)
255 {
256 	int rc;
257 
258 	debug("%s()\n", __func__);
259 
260 	rc = mcs7830_write_reg(udev, REG_CONFIG,
261 			       sizeof(priv->config), &priv->config);
262 	if (rc < 0) {
263 		debug("writing config to adapter failed\n");
264 		return rc;
265 	}
266 
267 	return 0;
268 }
269 
270 /*
271  * mcs7830_write_mchash() - write the network adapter's multicast filter
272  * @udev:	network device to write to
273  * @priv:	private data
274  * Return: zero upon success, negative upon error
275  *
276  * the data which gets written is taken from the shadow multicast hashes
277  * within the device driver's private data
278  */
279 static int mcs7830_write_mchash(struct usb_device *udev,
280 				struct mcs7830_private *priv)
281 {
282 	int rc;
283 
284 	debug("%s()\n", __func__);
285 
286 	rc = mcs7830_write_reg(udev, REG_MULTICAST_HASH,
287 			       sizeof(priv->mchash), &priv->mchash);
288 	if (rc < 0) {
289 		debug("writing multicast hash to adapter failed\n");
290 		return rc;
291 	}
292 
293 	return 0;
294 }
295 
296 /*
297  * mcs7830_set_autoneg() - setup and trigger ethernet link autonegotiation
298  * @udev:	network device to run link negotiation on
299  * Return: zero upon success, negative upon error
300  *
301  * the routine advertises available media and starts autonegotiation
302  */
303 static int mcs7830_set_autoneg(struct usb_device *udev)
304 {
305 	int adv, flg;
306 	int rc;
307 
308 	debug("%s()\n", __func__);
309 
310 	/*
311 	 * algorithm taken from the Linux driver, which took it from
312 	 * "the original mcs7830 version 1.4 driver":
313 	 *
314 	 * enable all media, reset BMCR, enable auto neg, restart
315 	 * auto neg while keeping the enable auto neg flag set
316 	 */
317 
318 	adv = ADVERTISE_PAUSE_CAP | ADVERTISE_ALL | ADVERTISE_CSMA;
319 	rc = mcs7830_write_phy(udev, MII_ADVERTISE, adv);
320 
321 	flg = 0;
322 	if (!rc)
323 		rc = mcs7830_write_phy(udev, MII_BMCR, flg);
324 
325 	flg |= BMCR_ANENABLE;
326 	if (!rc)
327 		rc = mcs7830_write_phy(udev, MII_BMCR, flg);
328 
329 	flg |= BMCR_ANRESTART;
330 	if (!rc)
331 		rc = mcs7830_write_phy(udev, MII_BMCR, flg);
332 
333 	return rc;
334 }
335 
336 /*
337  * mcs7830_get_rev() - identify a network adapter's chip revision
338  * @udev:	network device to identify
339  * Return: non-negative number, reflecting the revision number
340  *
341  * currently, only "rev C and higher" and "below rev C" are needed, so
342  * the return value is #1 for "below rev C", and #2 for "rev C and above"
343  */
344 static int mcs7830_get_rev(struct usb_device *udev)
345 {
346 	uint8_t buf[2];
347 	int rc;
348 	int rev;
349 
350 	/* register 22 is readable in rev C and higher */
351 	rc = mcs7830_read_reg(udev, REG_FRAME_DROP_COUNTER, sizeof(buf), buf);
352 	if (rc < 0)
353 		rev = 1;
354 	else
355 		rev = 2;
356 	debug("%s() rc=%d, rev=%d\n", __func__, rc, rev);
357 	return rev;
358 }
359 
360 /*
361  * mcs7830_apply_fixup() - identify an adapter and potentially apply fixups
362  * @udev:	network device to identify and apply fixups to
363  * Return: zero upon success (no errors emitted from here)
364  *
365  * this routine identifies the network adapter's chip revision, and applies
366  * fixups for known issues
367  */
368 static int mcs7830_apply_fixup(struct usb_device *udev)
369 {
370 	int rev;
371 	int i;
372 	uint8_t thr;
373 
374 	rev = mcs7830_get_rev(udev);
375 	debug("%s() rev=%d\n", __func__, rev);
376 
377 	/*
378 	 * rev C requires setting the pause threshold (the Linux driver
379 	 * is inconsistent, the implementation does it for "rev C
380 	 * exactly", the introductory comment says "rev C and above")
381 	 */
382 	if (rev == 2) {
383 		debug("%s: applying rev C fixup\n", __func__);
384 		thr = PAUSE_THRESHOLD_DEFAULT;
385 		for (i = 0; i < 2; i++) {
386 			(void)mcs7830_write_reg(udev, REG_PAUSE_THRESHOLD,
387 						sizeof(thr), &thr);
388 			mdelay(1);
389 		}
390 	}
391 
392 	return 0;
393 }
394 
395 /*
396  * mcs7830_basic_reset() - bring the network adapter into a known first state
397  * @eth:	network device to act upon
398  * Return: zero upon success, negative upon error
399  *
400  * this routine initializes the network adapter such that subsequent invocations
401  * of the interface callbacks can exchange ethernet frames; link negotiation is
402  * triggered from here already and continues in background
403  */
404 static int mcs7830_basic_reset(struct usb_device *udev,
405 			       struct mcs7830_private *priv)
406 {
407 	int rc;
408 
409 	debug("%s()\n", __func__);
410 
411 	/*
412 	 * comment from the respective Linux driver, which
413 	 * unconditionally sets the ALLMULTICAST flag as well:
414 	 * should not be needed, but does not work otherwise
415 	 */
416 	priv->config = CONF_TXENABLE;
417 	priv->config |= CONF_ALLMULTICAST;
418 
419 	rc = mcs7830_set_autoneg(udev);
420 	if (rc < 0) {
421 		pr_err("setting autoneg failed\n");
422 		return rc;
423 	}
424 
425 	rc = mcs7830_write_mchash(udev, priv);
426 	if (rc < 0) {
427 		pr_err("failed to set multicast hash\n");
428 		return rc;
429 	}
430 
431 	rc = mcs7830_write_config(udev, priv);
432 	if (rc < 0) {
433 		pr_err("failed to set configuration\n");
434 		return rc;
435 	}
436 
437 	rc = mcs7830_apply_fixup(udev);
438 	if (rc < 0) {
439 		pr_err("fixup application failed\n");
440 		return rc;
441 	}
442 
443 	return 0;
444 }
445 
446 /*
447  * mcs7830_read_mac() - read an ethernet adapter's MAC address
448  * @udev:	network device to read from
449  * @enetaddr:	place to put ethernet MAC address
450  * Return: zero upon success, negative upon error
451  *
452  * this routine fetches the MAC address stored within the ethernet adapter,
453  * and stores it in the ethernet interface's data structure
454  */
455 static int mcs7830_read_mac(struct usb_device *udev, unsigned char enetaddr[])
456 {
457 	int rc;
458 	uint8_t buf[ETH_ALEN];
459 
460 	debug("%s()\n", __func__);
461 
462 	rc = mcs7830_read_reg(udev, REG_ETHER_ADDR, ETH_ALEN, buf);
463 	if (rc < 0) {
464 		debug("reading MAC from adapter failed\n");
465 		return rc;
466 	}
467 
468 	memcpy(enetaddr, buf, ETH_ALEN);
469 	return 0;
470 }
471 
472 static int mcs7830_write_mac_common(struct usb_device *udev,
473 				    unsigned char enetaddr[])
474 {
475 	int rc;
476 
477 	debug("%s()\n", __func__);
478 
479 	rc = mcs7830_write_reg(udev, REG_ETHER_ADDR, ETH_ALEN, enetaddr);
480 	if (rc < 0) {
481 		debug("writing MAC to adapter failed\n");
482 		return rc;
483 	}
484 	return 0;
485 }
486 
487 static int mcs7830_init_common(struct usb_device *udev)
488 {
489 	int timeout;
490 	int have_link;
491 
492 	debug("%s()\n", __func__);
493 
494 	timeout = 0;
495 	do {
496 		have_link = mcs7830_read_phy(udev, MII_BMSR) & BMSR_LSTATUS;
497 		if (have_link)
498 			break;
499 		udelay(LINKSTATUS_TIMEOUT_RES * 1000);
500 		timeout += LINKSTATUS_TIMEOUT_RES;
501 	} while (timeout < LINKSTATUS_TIMEOUT);
502 	if (!have_link) {
503 		debug("ethernet link is down\n");
504 		return -ETIMEDOUT;
505 	}
506 	return 0;
507 }
508 
509 static int mcs7830_send_common(struct ueth_data *ueth, void *packet,
510 			       int length)
511 {
512 	struct usb_device *udev = ueth->pusb_dev;
513 	int rc;
514 	int gotlen;
515 	/* there is a status byte after the ethernet frame */
516 	ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, PKTSIZE + sizeof(uint8_t));
517 
518 	memcpy(buf, packet, length);
519 	rc = usb_bulk_msg(udev,
520 			  usb_sndbulkpipe(udev, ueth->ep_out),
521 			  &buf[0], length, &gotlen,
522 			  USBCALL_TIMEOUT);
523 	debug("%s() TX want len %d, got len %d, rc %d\n",
524 	      __func__, length, gotlen, rc);
525 	return rc;
526 }
527 
528 static int mcs7830_recv_common(struct ueth_data *ueth, uint8_t *buf)
529 {
530 	int rc, wantlen, gotlen;
531 	uint8_t sts;
532 
533 	debug("%s()\n", __func__);
534 
535 	/* fetch input data from the adapter */
536 	wantlen = MCS7830_RX_URB_SIZE;
537 	rc = usb_bulk_msg(ueth->pusb_dev,
538 			  usb_rcvbulkpipe(ueth->pusb_dev, ueth->ep_in),
539 			  &buf[0], wantlen, &gotlen,
540 			  USBCALL_TIMEOUT);
541 	debug("%s() RX want len %d, got len %d, rc %d\n",
542 	      __func__, wantlen, gotlen, rc);
543 	if (rc != 0) {
544 		pr_err("RX: failed to receive\n");
545 		return rc;
546 	}
547 	if (gotlen > wantlen) {
548 		pr_err("RX: got too many bytes (%d)\n", gotlen);
549 		return -EIO;
550 	}
551 
552 	/*
553 	 * the bulk message that we received from USB contains exactly
554 	 * one ethernet frame and a trailing status byte
555 	 */
556 	if (gotlen < sizeof(sts))
557 		return -EIO;
558 	gotlen -= sizeof(sts);
559 	sts = buf[gotlen];
560 
561 	if (sts == STAT_RX_FRAME_CORRECT) {
562 		debug("%s() got a frame, len=%d\n", __func__, gotlen);
563 		return gotlen;
564 	}
565 
566 	debug("RX: frame error (sts 0x%02X, %s %s %s %s %s)\n",
567 	      sts,
568 	      (sts & STAT_RX_LARGE_FRAME) ? "large" : "-",
569 	      (sts & STAT_RX_LENGTH_ERROR) ?  "length" : "-",
570 	      (sts & STAT_RX_SHORT_FRAME) ? "short" : "-",
571 	      (sts & STAT_RX_CRC_ERROR) ? "crc" : "-",
572 	      (sts & STAT_RX_ALIGNMENT_ERROR) ?  "align" : "-");
573 	return -EIO;
574 }
575 
576 #ifndef CONFIG_DM_ETH
577 /*
578  * mcs7830_init() - network interface's init callback
579  * @udev:	network device to initialize
580  * @bd:		board information
581  * Return: zero upon success, negative upon error
582  *
583  * after initial setup during probe() and get_info(), this init() callback
584  * ensures that the link is up and subsequent send() and recv() calls can
585  * exchange ethernet frames
586  */
587 static int mcs7830_init(struct eth_device *eth, bd_t *bd)
588 {
589 	struct ueth_data *dev = eth->priv;
590 
591 	return mcs7830_init_common(dev->pusb_dev);
592 }
593 
594 /*
595  * mcs7830_send() - network interface's send callback
596  * @eth:	network device to send the frame from
597  * @packet:	ethernet frame content
598  * @length:	ethernet frame length
599  * Return: zero upon success, negative upon error
600  *
601  * this routine send an ethernet frame out of the network interface
602  */
603 static int mcs7830_send(struct eth_device *eth, void *packet, int length)
604 {
605 	struct ueth_data *dev = eth->priv;
606 
607 	return mcs7830_send_common(dev, packet, length);
608 }
609 
610 /*
611  * mcs7830_recv() - network interface's recv callback
612  * @eth:	network device to receive frames from
613  * Return: zero upon success, negative upon error
614  *
615  * this routine checks for available ethernet frames that the network
616  * interface might have received, and notifies the network stack
617  */
618 static int mcs7830_recv(struct eth_device *eth)
619 {
620 	ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, MCS7830_RX_URB_SIZE);
621 	struct ueth_data *ueth = eth->priv;
622 	int len;
623 
624 	len = mcs7830_recv_common(ueth, buf);
625 	if (len >= 0) {
626 		net_process_received_packet(buf, len);
627 		return 0;
628 	}
629 
630 	return len;
631 }
632 
633 /*
634  * mcs7830_halt() - network interface's halt callback
635  * @eth:	network device to cease operation of
636  * Return: none
637  *
638  * this routine is supposed to undo the effect of previous initialization and
639  * ethernet frames exchange; in this implementation it's a NOP
640  */
641 static void mcs7830_halt(struct eth_device *eth)
642 {
643 	debug("%s()\n", __func__);
644 }
645 
646 /*
647  * mcs7830_write_mac() - write an ethernet adapter's MAC address
648  * @eth:	network device to write to
649  * Return: zero upon success, negative upon error
650  *
651  * this routine takes the MAC address from the ethernet interface's data
652  * structure, and writes it into the ethernet adapter such that subsequent
653  * exchange of ethernet frames uses this address
654  */
655 static int mcs7830_write_mac(struct eth_device *eth)
656 {
657 	struct ueth_data *ueth = eth->priv;
658 
659 	return mcs7830_write_mac_common(ueth->pusb_dev, eth->enetaddr);
660 }
661 
662 /*
663  * mcs7830_iface_idx - index of detected network interfaces
664  *
665  * this counter keeps track of identified supported interfaces,
666  * to assign unique names as more interfaces are found
667  */
668 static int mcs7830_iface_idx;
669 
670 /*
671  * mcs7830_eth_before_probe() - network driver's before_probe callback
672  * Return: none
673  *
674  * this routine initializes driver's internal data in preparation of
675  * subsequent probe callbacks
676  */
677 void mcs7830_eth_before_probe(void)
678 {
679 	mcs7830_iface_idx = 0;
680 }
681 
682 /*
683  * struct mcs7830_dongle - description of a supported Moschip ethernet dongle
684  * @vendor:	16bit USB vendor identification
685  * @product:	16bit USB product identification
686  *
687  * this structure describes a supported USB ethernet dongle by means of the
688  * vendor and product codes found during USB enumeration; no flags are held
689  * here since all supported dongles have identical behaviour, and required
690  * fixups get determined at runtime, such that no manual configuration is
691  * needed
692  */
693 struct mcs7830_dongle {
694 	uint16_t vendor;
695 	uint16_t product;
696 };
697 
698 /*
699  * mcs7830_dongles - the list of supported Moschip based USB ethernet dongles
700  */
701 static const struct mcs7830_dongle mcs7830_dongles[] = {
702 	{ 0x9710, 0x7832, },	/* Moschip 7832 */
703 	{ 0x9710, 0x7830, },	/* Moschip 7830 */
704 	{ 0x9710, 0x7730, },	/* Moschip 7730 */
705 	{ 0x0df6, 0x0021, },	/* Sitecom LN 30 */
706 };
707 
708 /*
709  * mcs7830_eth_probe() - network driver's probe callback
710  * @dev:	detected USB device to check
711  * @ifnum:	detected USB interface to check
712  * @ss:		USB ethernet data structure to fill in upon match
713  * Return: #1 upon match, #0 upon mismatch or error
714  *
715  * this routine checks whether the found USB device is supported by
716  * this ethernet driver, and upon match fills in the USB ethernet
717  * data structure which later is passed to the get_info callback
718  */
719 int mcs7830_eth_probe(struct usb_device *dev, unsigned int ifnum,
720 		      struct ueth_data *ss)
721 {
722 	struct usb_interface *iface;
723 	struct usb_interface_descriptor *iface_desc;
724 	int i;
725 	struct mcs7830_private *priv;
726 	int ep_in_found, ep_out_found, ep_intr_found;
727 
728 	debug("%s()\n", __func__);
729 
730 	/* iterate the list of supported dongles */
731 	iface = &dev->config.if_desc[ifnum];
732 	iface_desc = &iface->desc;
733 	for (i = 0; i < ARRAY_SIZE(mcs7830_dongles); i++) {
734 		if (dev->descriptor.idVendor == mcs7830_dongles[i].vendor &&
735 		    dev->descriptor.idProduct == mcs7830_dongles[i].product)
736 			break;
737 	}
738 	if (i == ARRAY_SIZE(mcs7830_dongles))
739 		return 0;
740 	debug("detected USB ethernet device: %04X:%04X\n",
741 	      dev->descriptor.idVendor, dev->descriptor.idProduct);
742 
743 	/* fill in driver private data */
744 	priv = calloc(1, sizeof(*priv));
745 	if (!priv)
746 		return 0;
747 
748 	/* fill in the ueth_data structure, attach private data */
749 	memset(ss, 0, sizeof(*ss));
750 	ss->ifnum = ifnum;
751 	ss->pusb_dev = dev;
752 	ss->subclass = iface_desc->bInterfaceSubClass;
753 	ss->protocol = iface_desc->bInterfaceProtocol;
754 	ss->dev_priv = priv;
755 
756 	/*
757 	 * a minimum of three endpoints is expected: in (bulk),
758 	 * out (bulk), and interrupt; ignore all others
759 	 */
760 	ep_in_found = ep_out_found = ep_intr_found = 0;
761 	for (i = 0; i < iface_desc->bNumEndpoints; i++) {
762 		uint8_t eptype, epaddr;
763 		bool is_input;
764 
765 		eptype = iface->ep_desc[i].bmAttributes;
766 		eptype &= USB_ENDPOINT_XFERTYPE_MASK;
767 
768 		epaddr = iface->ep_desc[i].bEndpointAddress;
769 		is_input = epaddr & USB_DIR_IN;
770 		epaddr &= USB_ENDPOINT_NUMBER_MASK;
771 
772 		if (eptype == USB_ENDPOINT_XFER_BULK) {
773 			if (is_input && !ep_in_found) {
774 				ss->ep_in = epaddr;
775 				ep_in_found++;
776 			}
777 			if (!is_input && !ep_out_found) {
778 				ss->ep_out = epaddr;
779 				ep_out_found++;
780 			}
781 		}
782 
783 		if (eptype == USB_ENDPOINT_XFER_INT) {
784 			if (is_input && !ep_intr_found) {
785 				ss->ep_int = epaddr;
786 				ss->irqinterval = iface->ep_desc[i].bInterval;
787 				ep_intr_found++;
788 			}
789 		}
790 	}
791 	debug("endpoints: in %d, out %d, intr %d\n",
792 	      ss->ep_in, ss->ep_out, ss->ep_int);
793 
794 	/* apply basic sanity checks */
795 	if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
796 	    !ss->ep_in || !ss->ep_out || !ss->ep_int) {
797 		debug("device probe incomplete\n");
798 		return 0;
799 	}
800 
801 	dev->privptr = ss;
802 	return 1;
803 }
804 
805 /*
806  * mcs7830_eth_get_info() - network driver's get_info callback
807  * @dev:	detected USB device
808  * @ss:		USB ethernet data structure filled in at probe()
809  * @eth:	ethernet interface data structure to fill in
810  * Return: #1 upon success, #0 upon error
811  *
812  * this routine registers the mandatory init(), send(), recv(), and
813  * halt() callbacks with the ethernet interface, can register the
814  * optional write_hwaddr() callback with the ethernet interface,
815  * and initiates configuration of the interface such that subsequent
816  * calls to those callbacks results in network communication
817  */
818 int mcs7830_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
819 			 struct eth_device *eth)
820 {
821 	debug("%s()\n", __func__);
822 	if (!eth) {
823 		debug("%s: missing parameter.\n", __func__);
824 		return 0;
825 	}
826 
827 	snprintf(eth->name, sizeof(eth->name), "%s%d",
828 		 MCS7830_BASE_NAME, mcs7830_iface_idx++);
829 	eth->init = mcs7830_init;
830 	eth->send = mcs7830_send;
831 	eth->recv = mcs7830_recv;
832 	eth->halt = mcs7830_halt;
833 	eth->write_hwaddr = mcs7830_write_mac;
834 	eth->priv = ss;
835 
836 	if (mcs7830_basic_reset(ss->pusb_dev, ss->dev_priv))
837 		return 0;
838 
839 	if (mcs7830_read_mac(ss->pusb_dev, eth->enetaddr))
840 		return 0;
841 	debug("MAC %pM\n", eth->enetaddr);
842 
843 	return 1;
844 }
845 #endif
846 
847 
848 #ifdef CONFIG_DM_ETH
849 static int mcs7830_eth_start(struct udevice *dev)
850 {
851 	struct usb_device *udev = dev_get_parent_priv(dev);
852 
853 	return mcs7830_init_common(udev);
854 }
855 
856 void mcs7830_eth_stop(struct udevice *dev)
857 {
858 	debug("** %s()\n", __func__);
859 }
860 
861 int mcs7830_eth_send(struct udevice *dev, void *packet, int length)
862 {
863 	struct mcs7830_private *priv = dev_get_priv(dev);
864 	struct ueth_data *ueth = &priv->ueth;
865 
866 	return mcs7830_send_common(ueth, packet, length);
867 }
868 
869 int mcs7830_eth_recv(struct udevice *dev, int flags, uchar **packetp)
870 {
871 	struct mcs7830_private *priv = dev_get_priv(dev);
872 	struct ueth_data *ueth = &priv->ueth;
873 	int len;
874 
875 	len = mcs7830_recv_common(ueth, priv->rx_buf);
876 	*packetp = priv->rx_buf;
877 
878 	return len;
879 }
880 
881 static int mcs7830_free_pkt(struct udevice *dev, uchar *packet, int packet_len)
882 {
883 	struct mcs7830_private *priv = dev_get_priv(dev);
884 
885 	packet_len = ALIGN(packet_len, 4);
886 	usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len);
887 
888 	return 0;
889 }
890 
891 int mcs7830_write_hwaddr(struct udevice *dev)
892 {
893 	struct usb_device *udev = dev_get_parent_priv(dev);
894 	struct eth_pdata *pdata = dev_get_platdata(dev);
895 
896 	return mcs7830_write_mac_common(udev, pdata->enetaddr);
897 }
898 
899 static int mcs7830_eth_probe(struct udevice *dev)
900 {
901 	struct usb_device *udev = dev_get_parent_priv(dev);
902 	struct mcs7830_private *priv = dev_get_priv(dev);
903 	struct eth_pdata *pdata = dev_get_platdata(dev);
904 	struct ueth_data *ueth = &priv->ueth;
905 
906 	if (mcs7830_basic_reset(udev, priv))
907 		return 0;
908 
909 	if (mcs7830_read_mac(udev, pdata->enetaddr))
910 		return 0;
911 
912 	return usb_ether_register(dev, ueth, MCS7830_RX_URB_SIZE);
913 }
914 
915 static const struct eth_ops mcs7830_eth_ops = {
916 	.start	= mcs7830_eth_start,
917 	.send	= mcs7830_eth_send,
918 	.recv	= mcs7830_eth_recv,
919 	.free_pkt = mcs7830_free_pkt,
920 	.stop	= mcs7830_eth_stop,
921 	.write_hwaddr = mcs7830_write_hwaddr,
922 };
923 
924 U_BOOT_DRIVER(mcs7830_eth) = {
925 	.name	= "mcs7830_eth",
926 	.id	= UCLASS_ETH,
927 	.probe = mcs7830_eth_probe,
928 	.ops	= &mcs7830_eth_ops,
929 	.priv_auto_alloc_size = sizeof(struct mcs7830_private),
930 	.platdata_auto_alloc_size = sizeof(struct eth_pdata),
931 	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
932 };
933 
934 static const struct usb_device_id mcs7830_eth_id_table[] = {
935 	{ USB_DEVICE(0x9710, 0x7832) },		/* Moschip 7832 */
936 	{ USB_DEVICE(0x9710, 0x7830), },	/* Moschip 7830 */
937 	{ USB_DEVICE(0x9710, 0x7730), },	/* Moschip 7730 */
938 	{ USB_DEVICE(0x0df6, 0x0021), },	/* Sitecom LN 30 */
939 	{ }		/* Terminating entry */
940 };
941 
942 U_BOOT_USB_DEVICE(mcs7830_eth, mcs7830_eth_id_table);
943 #endif
944