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