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