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