]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
9b70e007 SG |
2 | /* |
3 | * Copyright (c) 2011 The Chromium OS Authors. | |
9b70e007 | 4 | * |
14727120 | 5 | * Patched for AX88772B by Antmicro Ltd <www.antmicro.com> |
9b70e007 SG |
6 | */ |
7 | ||
8 | #include <common.h> | |
fbc4b8af | 9 | #include <dm.h> |
90526e9f | 10 | #include <net.h> |
9b70e007 | 11 | #include <usb.h> |
fbc4b8af | 12 | #include <malloc.h> |
cf92e05c | 13 | #include <memalign.h> |
9b70e007 SG |
14 | #include <linux/mii.h> |
15 | #include "usb_ether.h" | |
9b70e007 SG |
16 | |
17 | /* ASIX AX8817X based USB 2.0 Ethernet Devices */ | |
18 | ||
19 | #define AX_CMD_SET_SW_MII 0x06 | |
20 | #define AX_CMD_READ_MII_REG 0x07 | |
21 | #define AX_CMD_WRITE_MII_REG 0x08 | |
22 | #define AX_CMD_SET_HW_MII 0x0a | |
02c8d8cc | 23 | #define AX_CMD_READ_EEPROM 0x0b |
9b70e007 SG |
24 | #define AX_CMD_READ_RX_CTL 0x0f |
25 | #define AX_CMD_WRITE_RX_CTL 0x10 | |
26 | #define AX_CMD_WRITE_IPG0 0x12 | |
27 | #define AX_CMD_READ_NODE_ID 0x13 | |
58f8fab8 | 28 | #define AX_CMD_WRITE_NODE_ID 0x14 |
9b70e007 SG |
29 | #define AX_CMD_READ_PHY_ID 0x19 |
30 | #define AX_CMD_WRITE_MEDIUM_MODE 0x1b | |
31 | #define AX_CMD_WRITE_GPIOS 0x1f | |
32 | #define AX_CMD_SW_RESET 0x20 | |
33 | #define AX_CMD_SW_PHY_SELECT 0x22 | |
34 | ||
35 | #define AX_SWRESET_CLEAR 0x00 | |
36 | #define AX_SWRESET_PRTE 0x04 | |
37 | #define AX_SWRESET_PRL 0x08 | |
38 | #define AX_SWRESET_IPRL 0x20 | |
39 | #define AX_SWRESET_IPPD 0x40 | |
40 | ||
41 | #define AX88772_IPG0_DEFAULT 0x15 | |
42 | #define AX88772_IPG1_DEFAULT 0x0c | |
43 | #define AX88772_IPG2_DEFAULT 0x12 | |
44 | ||
45 | /* AX88772 & AX88178 Medium Mode Register */ | |
46 | #define AX_MEDIUM_PF 0x0080 | |
47 | #define AX_MEDIUM_JFE 0x0040 | |
48 | #define AX_MEDIUM_TFC 0x0020 | |
49 | #define AX_MEDIUM_RFC 0x0010 | |
50 | #define AX_MEDIUM_ENCK 0x0008 | |
51 | #define AX_MEDIUM_AC 0x0004 | |
52 | #define AX_MEDIUM_FD 0x0002 | |
53 | #define AX_MEDIUM_GM 0x0001 | |
54 | #define AX_MEDIUM_SM 0x1000 | |
55 | #define AX_MEDIUM_SBP 0x0800 | |
56 | #define AX_MEDIUM_PS 0x0200 | |
57 | #define AX_MEDIUM_RE 0x0100 | |
58 | ||
59 | #define AX88178_MEDIUM_DEFAULT \ | |
60 | (AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \ | |
61 | AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \ | |
62 | AX_MEDIUM_RE) | |
63 | ||
64 | #define AX88772_MEDIUM_DEFAULT \ | |
65 | (AX_MEDIUM_FD | AX_MEDIUM_RFC | \ | |
66 | AX_MEDIUM_TFC | AX_MEDIUM_PS | \ | |
67 | AX_MEDIUM_AC | AX_MEDIUM_RE) | |
68 | ||
69 | /* AX88772 & AX88178 RX_CTL values */ | |
cea6c8ce AB |
70 | #define AX_RX_CTL_SO 0x0080 |
71 | #define AX_RX_CTL_AB 0x0008 | |
9b70e007 SG |
72 | |
73 | #define AX_DEFAULT_RX_CTL \ | |
74 | (AX_RX_CTL_SO | AX_RX_CTL_AB) | |
75 | ||
76 | /* GPIO 2 toggles */ | |
77 | #define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */ | |
78 | #define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */ | |
79 | #define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */ | |
80 | ||
81 | /* local defines */ | |
82 | #define ASIX_BASE_NAME "asx" | |
83 | #define USB_CTRL_SET_TIMEOUT 5000 | |
84 | #define USB_CTRL_GET_TIMEOUT 5000 | |
85 | #define USB_BULK_SEND_TIMEOUT 5000 | |
86 | #define USB_BULK_RECV_TIMEOUT 5000 | |
87 | ||
88 | #define AX_RX_URB_SIZE 2048 | |
89 | #define PHY_CONNECT_TIMEOUT 5000 | |
90 | ||
58f8fab8 LS |
91 | /* asix_flags defines */ |
92 | #define FLAG_NONE 0 | |
93 | #define FLAG_TYPE_AX88172 (1U << 0) | |
94 | #define FLAG_TYPE_AX88772 (1U << 1) | |
1dff9d0f LS |
95 | #define FLAG_TYPE_AX88772B (1U << 2) |
96 | #define FLAG_EEPROM_MAC (1U << 3) /* initial mac address in eeprom */ | |
58f8fab8 | 97 | |
9b70e007 | 98 | |
58f8fab8 LS |
99 | /* driver private */ |
100 | struct asix_private { | |
101 | int flags; | |
fbc4b8af SG |
102 | #ifdef CONFIG_DM_ETH |
103 | struct ueth_data ueth; | |
104 | #endif | |
58f8fab8 LS |
105 | }; |
106 | ||
fbc4b8af SG |
107 | #ifndef CONFIG_DM_ETH |
108 | /* local vars */ | |
109 | static int curr_eth_dev; /* index for name of next device detected */ | |
110 | #endif | |
111 | ||
9b70e007 SG |
112 | /* |
113 | * Asix infrastructure commands | |
114 | */ | |
115 | static int asix_write_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, | |
116 | u16 size, void *data) | |
117 | { | |
118 | int len; | |
119 | ||
120 | debug("asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x " | |
121 | "size=%d\n", cmd, value, index, size); | |
122 | ||
123 | len = usb_control_msg( | |
124 | dev->pusb_dev, | |
125 | usb_sndctrlpipe(dev->pusb_dev, 0), | |
126 | cmd, | |
127 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
128 | value, | |
129 | index, | |
130 | data, | |
131 | size, | |
132 | USB_CTRL_SET_TIMEOUT); | |
133 | ||
134 | return len == size ? 0 : -1; | |
135 | } | |
136 | ||
137 | static int asix_read_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, | |
138 | u16 size, void *data) | |
139 | { | |
140 | int len; | |
141 | ||
142 | debug("asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", | |
143 | cmd, value, index, size); | |
144 | ||
145 | len = usb_control_msg( | |
146 | dev->pusb_dev, | |
147 | usb_rcvctrlpipe(dev->pusb_dev, 0), | |
148 | cmd, | |
149 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
150 | value, | |
151 | index, | |
152 | data, | |
153 | size, | |
154 | USB_CTRL_GET_TIMEOUT); | |
155 | return len == size ? 0 : -1; | |
156 | } | |
157 | ||
158 | static inline int asix_set_sw_mii(struct ueth_data *dev) | |
159 | { | |
160 | int ret; | |
161 | ||
162 | ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); | |
163 | if (ret < 0) | |
164 | debug("Failed to enable software MII access\n"); | |
165 | return ret; | |
166 | } | |
167 | ||
168 | static inline int asix_set_hw_mii(struct ueth_data *dev) | |
169 | { | |
170 | int ret; | |
171 | ||
172 | ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); | |
173 | if (ret < 0) | |
174 | debug("Failed to enable hardware MII access\n"); | |
175 | return ret; | |
176 | } | |
177 | ||
178 | static int asix_mdio_read(struct ueth_data *dev, int phy_id, int loc) | |
179 | { | |
c59ab092 | 180 | ALLOC_CACHE_ALIGN_BUFFER(__le16, res, 1); |
9b70e007 SG |
181 | |
182 | asix_set_sw_mii(dev); | |
c59ab092 | 183 | asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, res); |
9b70e007 SG |
184 | asix_set_hw_mii(dev); |
185 | ||
186 | debug("asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", | |
c59ab092 | 187 | phy_id, loc, le16_to_cpu(*res)); |
9b70e007 | 188 | |
c59ab092 | 189 | return le16_to_cpu(*res); |
9b70e007 SG |
190 | } |
191 | ||
192 | static void | |
193 | asix_mdio_write(struct ueth_data *dev, int phy_id, int loc, int val) | |
194 | { | |
c59ab092 MV |
195 | ALLOC_CACHE_ALIGN_BUFFER(__le16, res, 1); |
196 | *res = cpu_to_le16(val); | |
9b70e007 SG |
197 | |
198 | debug("asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", | |
199 | phy_id, loc, val); | |
200 | asix_set_sw_mii(dev); | |
c59ab092 | 201 | asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, res); |
9b70e007 SG |
202 | asix_set_hw_mii(dev); |
203 | } | |
204 | ||
205 | /* | |
206 | * Asix "high level" commands | |
207 | */ | |
208 | static int asix_sw_reset(struct ueth_data *dev, u8 flags) | |
209 | { | |
210 | int ret; | |
211 | ||
212 | ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL); | |
213 | if (ret < 0) | |
214 | debug("Failed to send software reset: %02x\n", ret); | |
215 | else | |
216 | udelay(150 * 1000); | |
217 | ||
218 | return ret; | |
219 | } | |
220 | ||
221 | static inline int asix_get_phy_addr(struct ueth_data *dev) | |
222 | { | |
c59ab092 MV |
223 | ALLOC_CACHE_ALIGN_BUFFER(u8, buf, 2); |
224 | ||
9b70e007 SG |
225 | int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf); |
226 | ||
227 | debug("asix_get_phy_addr()\n"); | |
228 | ||
229 | if (ret < 0) { | |
230 | debug("Error reading PHYID register: %02x\n", ret); | |
231 | goto out; | |
232 | } | |
0ed1eb6f | 233 | debug("asix_get_phy_addr() returning 0x%02x%02x\n", buf[0], buf[1]); |
9b70e007 SG |
234 | ret = buf[1]; |
235 | ||
236 | out: | |
237 | return ret; | |
238 | } | |
239 | ||
240 | static int asix_write_medium_mode(struct ueth_data *dev, u16 mode) | |
241 | { | |
242 | int ret; | |
243 | ||
244 | debug("asix_write_medium_mode() - mode = 0x%04x\n", mode); | |
245 | ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, | |
246 | 0, 0, NULL); | |
247 | if (ret < 0) { | |
248 | debug("Failed to write Medium Mode mode to 0x%04x: %02x\n", | |
249 | mode, ret); | |
250 | } | |
251 | return ret; | |
252 | } | |
253 | ||
254 | static u16 asix_read_rx_ctl(struct ueth_data *dev) | |
255 | { | |
c59ab092 MV |
256 | ALLOC_CACHE_ALIGN_BUFFER(__le16, v, 1); |
257 | ||
258 | int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, v); | |
9b70e007 SG |
259 | |
260 | if (ret < 0) | |
261 | debug("Error reading RX_CTL register: %02x\n", ret); | |
262 | else | |
c59ab092 | 263 | ret = le16_to_cpu(*v); |
9b70e007 SG |
264 | return ret; |
265 | } | |
266 | ||
267 | static int asix_write_rx_ctl(struct ueth_data *dev, u16 mode) | |
268 | { | |
269 | int ret; | |
270 | ||
271 | debug("asix_write_rx_ctl() - mode = 0x%04x\n", mode); | |
272 | ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); | |
273 | if (ret < 0) { | |
274 | debug("Failed to write RX_CTL mode to 0x%04x: %02x\n", | |
275 | mode, ret); | |
276 | } | |
277 | return ret; | |
278 | } | |
279 | ||
280 | static int asix_write_gpio(struct ueth_data *dev, u16 value, int sleep) | |
281 | { | |
282 | int ret; | |
283 | ||
284 | debug("asix_write_gpio() - value = 0x%04x\n", value); | |
285 | ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL); | |
286 | if (ret < 0) { | |
287 | debug("Failed to write GPIO value 0x%04x: %02x\n", | |
288 | value, ret); | |
289 | } | |
290 | if (sleep) | |
291 | udelay(sleep * 1000); | |
292 | ||
293 | return ret; | |
294 | } | |
295 | ||
fbc4b8af | 296 | static int asix_write_hwaddr_common(struct ueth_data *dev, uint8_t *enetaddr) |
58f8fab8 | 297 | { |
58f8fab8 LS |
298 | int ret; |
299 | ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN); | |
300 | ||
fbc4b8af | 301 | memcpy(buf, enetaddr, ETH_ALEN); |
58f8fab8 LS |
302 | |
303 | ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, buf); | |
304 | if (ret < 0) | |
305 | debug("Failed to set MAC address: %02x\n", ret); | |
306 | ||
307 | return ret; | |
308 | } | |
309 | ||
9b70e007 SG |
310 | /* |
311 | * mii commands | |
312 | */ | |
313 | ||
314 | /* | |
315 | * mii_nway_restart - restart NWay (autonegotiation) for this interface | |
316 | * | |
317 | * Returns 0 on success, negative on error. | |
318 | */ | |
319 | static int mii_nway_restart(struct ueth_data *dev) | |
320 | { | |
321 | int bmcr; | |
322 | int r = -1; | |
323 | ||
324 | /* if autoneg is off, it's an error */ | |
325 | bmcr = asix_mdio_read(dev, dev->phy_id, MII_BMCR); | |
326 | ||
327 | if (bmcr & BMCR_ANENABLE) { | |
328 | bmcr |= BMCR_ANRESTART; | |
329 | asix_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr); | |
330 | r = 0; | |
331 | } | |
332 | ||
333 | return r; | |
334 | } | |
335 | ||
fbc4b8af SG |
336 | static int asix_read_mac_common(struct ueth_data *dev, |
337 | struct asix_private *priv, uint8_t *enetaddr) | |
02c8d8cc | 338 | { |
02c8d8cc | 339 | ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN); |
fbc4b8af | 340 | int i; |
02c8d8cc LS |
341 | |
342 | if (priv->flags & FLAG_EEPROM_MAC) { | |
343 | for (i = 0; i < (ETH_ALEN >> 1); i++) { | |
344 | if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, | |
345 | 0x04 + i, 0, 2, buf) < 0) { | |
346 | debug("Failed to read SROM address 04h.\n"); | |
347 | return -1; | |
348 | } | |
fbc4b8af | 349 | memcpy(enetaddr + i * 2, buf, 2); |
02c8d8cc LS |
350 | } |
351 | } else { | |
352 | if (asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf) | |
353 | < 0) { | |
354 | debug("Failed to read MAC address.\n"); | |
355 | return -1; | |
356 | } | |
fbc4b8af | 357 | memcpy(enetaddr, buf, ETH_ALEN); |
02c8d8cc LS |
358 | } |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
5fae53d0 | 363 | static int asix_basic_reset(struct ueth_data *dev) |
9b70e007 SG |
364 | { |
365 | int embd_phy; | |
9b70e007 | 366 | u16 rx_ctl; |
9b70e007 SG |
367 | |
368 | if (asix_write_gpio(dev, | |
369 | AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5) < 0) | |
5fae53d0 | 370 | return -1; |
9b70e007 SG |
371 | |
372 | /* 0x10 is the phy id of the embedded 10/100 ethernet phy */ | |
373 | embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); | |
374 | if (asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, | |
375 | embd_phy, 0, 0, NULL) < 0) { | |
376 | debug("Select PHY #1 failed\n"); | |
5fae53d0 | 377 | return -1; |
9b70e007 SG |
378 | } |
379 | ||
380 | if (asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL) < 0) | |
5fae53d0 | 381 | return -1; |
9b70e007 SG |
382 | |
383 | if (asix_sw_reset(dev, AX_SWRESET_CLEAR) < 0) | |
5fae53d0 | 384 | return -1; |
9b70e007 SG |
385 | |
386 | if (embd_phy) { | |
387 | if (asix_sw_reset(dev, AX_SWRESET_IPRL) < 0) | |
5fae53d0 | 388 | return -1; |
9b70e007 SG |
389 | } else { |
390 | if (asix_sw_reset(dev, AX_SWRESET_PRTE) < 0) | |
5fae53d0 | 391 | return -1; |
9b70e007 SG |
392 | } |
393 | ||
394 | rx_ctl = asix_read_rx_ctl(dev); | |
395 | debug("RX_CTL is 0x%04x after software reset\n", rx_ctl); | |
396 | if (asix_write_rx_ctl(dev, 0x0000) < 0) | |
5fae53d0 | 397 | return -1; |
9b70e007 SG |
398 | |
399 | rx_ctl = asix_read_rx_ctl(dev); | |
400 | debug("RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); | |
401 | ||
9b70e007 SG |
402 | dev->phy_id = asix_get_phy_addr(dev); |
403 | if (dev->phy_id < 0) | |
404 | debug("Failed to read phy id\n"); | |
405 | ||
9b70e007 SG |
406 | asix_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET); |
407 | asix_mdio_write(dev, dev->phy_id, MII_ADVERTISE, | |
408 | ADVERTISE_ALL | ADVERTISE_CSMA); | |
409 | mii_nway_restart(dev); | |
410 | ||
411 | if (asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT) < 0) | |
4edcf0a3 | 412 | return -1; |
9b70e007 SG |
413 | |
414 | if (asix_write_cmd(dev, AX_CMD_WRITE_IPG0, | |
415 | AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, | |
416 | AX88772_IPG2_DEFAULT, 0, NULL) < 0) { | |
417 | debug("Write IPG,IPG1,IPG2 failed\n"); | |
4edcf0a3 | 418 | return -1; |
9b70e007 SG |
419 | } |
420 | ||
4edcf0a3 JW |
421 | return 0; |
422 | } | |
423 | ||
14727120 | 424 | static int asix_init_common(struct ueth_data *dev, uint8_t *enetaddr) |
4edcf0a3 | 425 | { |
4edcf0a3 JW |
426 | int timeout = 0; |
427 | #define TIMEOUT_RESOLUTION 50 /* ms */ | |
428 | int link_detected; | |
429 | ||
430 | debug("** %s()\n", __func__); | |
431 | ||
cea6c8ce | 432 | if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0) |
14727120 MZ |
433 | goto out_err; |
434 | ||
435 | if (asix_write_hwaddr_common(dev, enetaddr) < 0) | |
9b70e007 SG |
436 | goto out_err; |
437 | ||
438 | do { | |
439 | link_detected = asix_mdio_read(dev, dev->phy_id, MII_BMSR) & | |
440 | BMSR_LSTATUS; | |
441 | if (!link_detected) { | |
442 | if (timeout == 0) | |
443 | printf("Waiting for Ethernet connection... "); | |
444 | udelay(TIMEOUT_RESOLUTION * 1000); | |
445 | timeout += TIMEOUT_RESOLUTION; | |
446 | } | |
447 | } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT); | |
448 | if (link_detected) { | |
449 | if (timeout != 0) | |
450 | printf("done.\n"); | |
451 | } else { | |
452 | printf("unable to connect.\n"); | |
453 | goto out_err; | |
454 | } | |
455 | ||
14727120 MZ |
456 | /* |
457 | * Wait some more to avoid timeout on first transfer | |
458 | * (e.g. EHCI timed out on TD - token=0x8008d80) | |
459 | */ | |
460 | mdelay(25); | |
461 | ||
9b70e007 SG |
462 | return 0; |
463 | out_err: | |
464 | return -1; | |
465 | } | |
466 | ||
fbc4b8af | 467 | static int asix_send_common(struct ueth_data *dev, void *packet, int length) |
9b70e007 | 468 | { |
9b70e007 SG |
469 | int err; |
470 | u32 packet_len; | |
471 | int actual_len; | |
c59ab092 MV |
472 | ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg, |
473 | PKTSIZE + sizeof(packet_len)); | |
9b70e007 SG |
474 | |
475 | debug("** %s(), len %d\n", __func__, length); | |
476 | ||
477 | packet_len = (((length) ^ 0x0000ffff) << 16) + (length); | |
478 | cpu_to_le32s(&packet_len); | |
479 | ||
480 | memcpy(msg, &packet_len, sizeof(packet_len)); | |
481 | memcpy(msg + sizeof(packet_len), (void *)packet, length); | |
9b70e007 SG |
482 | |
483 | err = usb_bulk_msg(dev->pusb_dev, | |
484 | usb_sndbulkpipe(dev->pusb_dev, dev->ep_out), | |
485 | (void *)msg, | |
486 | length + sizeof(packet_len), | |
487 | &actual_len, | |
488 | USB_BULK_SEND_TIMEOUT); | |
4c5998b7 | 489 | debug("Tx: len = %zu, actual = %u, err = %d\n", |
9b70e007 SG |
490 | length + sizeof(packet_len), actual_len, err); |
491 | ||
492 | return err; | |
493 | } | |
494 | ||
fbc4b8af SG |
495 | #ifndef CONFIG_DM_ETH |
496 | /* | |
497 | * Asix callbacks | |
498 | */ | |
499 | static int asix_init(struct eth_device *eth, bd_t *bd) | |
500 | { | |
501 | struct ueth_data *dev = (struct ueth_data *)eth->priv; | |
502 | ||
14727120 | 503 | return asix_init_common(dev, eth->enetaddr); |
fbc4b8af SG |
504 | } |
505 | ||
506 | static int asix_send(struct eth_device *eth, void *packet, int length) | |
507 | { | |
508 | struct ueth_data *dev = (struct ueth_data *)eth->priv; | |
509 | ||
510 | return asix_send_common(dev, packet, length); | |
511 | } | |
512 | ||
9b70e007 SG |
513 | static int asix_recv(struct eth_device *eth) |
514 | { | |
515 | struct ueth_data *dev = (struct ueth_data *)eth->priv; | |
c59ab092 | 516 | ALLOC_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, AX_RX_URB_SIZE); |
9b70e007 SG |
517 | unsigned char *buf_ptr; |
518 | int err; | |
519 | int actual_len; | |
520 | u32 packet_len; | |
521 | ||
522 | debug("** %s()\n", __func__); | |
523 | ||
524 | err = usb_bulk_msg(dev->pusb_dev, | |
525 | usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), | |
526 | (void *)recv_buf, | |
527 | AX_RX_URB_SIZE, | |
528 | &actual_len, | |
529 | USB_BULK_RECV_TIMEOUT); | |
530 | debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE, | |
531 | actual_len, err); | |
532 | if (err != 0) { | |
533 | debug("Rx: failed to receive\n"); | |
534 | return -1; | |
535 | } | |
536 | if (actual_len > AX_RX_URB_SIZE) { | |
537 | debug("Rx: received too many bytes %d\n", actual_len); | |
538 | return -1; | |
539 | } | |
540 | ||
541 | buf_ptr = recv_buf; | |
542 | while (actual_len > 0) { | |
543 | /* | |
544 | * 1st 4 bytes contain the length of the actual data as two | |
545 | * complementary 16-bit words. Extract the length of the data. | |
546 | */ | |
547 | if (actual_len < sizeof(packet_len)) { | |
548 | debug("Rx: incomplete packet length\n"); | |
549 | return -1; | |
550 | } | |
551 | memcpy(&packet_len, buf_ptr, sizeof(packet_len)); | |
552 | le32_to_cpus(&packet_len); | |
1dff9d0f | 553 | if (((~packet_len >> 16) & 0x7ff) != (packet_len & 0x7ff)) { |
9b70e007 | 554 | debug("Rx: malformed packet length: %#x (%#x:%#x)\n", |
1dff9d0f LS |
555 | packet_len, (~packet_len >> 16) & 0x7ff, |
556 | packet_len & 0x7ff); | |
9b70e007 SG |
557 | return -1; |
558 | } | |
1dff9d0f | 559 | packet_len = packet_len & 0x7ff; |
9b70e007 SG |
560 | if (packet_len > actual_len - sizeof(packet_len)) { |
561 | debug("Rx: too large packet: %d\n", packet_len); | |
562 | return -1; | |
563 | } | |
564 | ||
565 | /* Notify net stack */ | |
1fd92db8 JH |
566 | net_process_received_packet(buf_ptr + sizeof(packet_len), |
567 | packet_len); | |
9b70e007 SG |
568 | |
569 | /* Adjust for next iteration. Packets are padded to 16-bits */ | |
570 | if (packet_len & 1) | |
571 | packet_len++; | |
572 | actual_len -= sizeof(packet_len) + packet_len; | |
573 | buf_ptr += sizeof(packet_len) + packet_len; | |
574 | } | |
575 | ||
576 | return err; | |
577 | } | |
578 | ||
579 | static void asix_halt(struct eth_device *eth) | |
580 | { | |
581 | debug("** %s()\n", __func__); | |
582 | } | |
583 | ||
fbc4b8af SG |
584 | static int asix_write_hwaddr(struct eth_device *eth) |
585 | { | |
586 | struct ueth_data *dev = (struct ueth_data *)eth->priv; | |
587 | ||
588 | return asix_write_hwaddr_common(dev, eth->enetaddr); | |
589 | } | |
590 | ||
9b70e007 SG |
591 | /* |
592 | * Asix probing functions | |
593 | */ | |
594 | void asix_eth_before_probe(void) | |
595 | { | |
596 | curr_eth_dev = 0; | |
597 | } | |
598 | ||
599 | struct asix_dongle { | |
600 | unsigned short vendor; | |
601 | unsigned short product; | |
58f8fab8 | 602 | int flags; |
9b70e007 SG |
603 | }; |
604 | ||
51afc2c6 | 605 | static const struct asix_dongle asix_dongles[] = { |
58f8fab8 LS |
606 | { 0x05ac, 0x1402, FLAG_TYPE_AX88772 }, /* Apple USB Ethernet Adapter */ |
607 | { 0x07d1, 0x3c05, FLAG_TYPE_AX88772 }, /* D-Link DUB-E100 H/W Ver B1 */ | |
befd3872 | 608 | { 0x2001, 0x1a02, FLAG_TYPE_AX88772 }, /* D-Link DUB-E100 H/W Ver C1 */ |
58f8fab8 LS |
609 | /* Cables-to-Go USB Ethernet Adapter */ |
610 | { 0x0b95, 0x772a, FLAG_TYPE_AX88772 }, | |
611 | { 0x0b95, 0x7720, FLAG_TYPE_AX88772 }, /* Trendnet TU2-ET100 V3.0R */ | |
612 | { 0x0b95, 0x1720, FLAG_TYPE_AX88172 }, /* SMC */ | |
613 | { 0x0db0, 0xa877, FLAG_TYPE_AX88772 }, /* MSI - ASIX 88772a */ | |
614 | { 0x13b1, 0x0018, FLAG_TYPE_AX88172 }, /* Linksys 200M v2.1 */ | |
615 | { 0x1557, 0x7720, FLAG_TYPE_AX88772 }, /* 0Q0 cable ethernet */ | |
616 | /* DLink DUB-E100 H/W Ver B1 Alternate */ | |
617 | { 0x2001, 0x3c05, FLAG_TYPE_AX88772 }, | |
1dff9d0f LS |
618 | /* ASIX 88772B */ |
619 | { 0x0b95, 0x772b, FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC }, | |
a7f24722 | 620 | { 0x0b95, 0x7e2b, FLAG_TYPE_AX88772B }, |
58f8fab8 | 621 | { 0x0000, 0x0000, FLAG_NONE } /* END - Do not remove */ |
9b70e007 SG |
622 | }; |
623 | ||
624 | /* Probe to see if a new device is actually an asix device */ | |
625 | int asix_eth_probe(struct usb_device *dev, unsigned int ifnum, | |
626 | struct ueth_data *ss) | |
627 | { | |
628 | struct usb_interface *iface; | |
629 | struct usb_interface_descriptor *iface_desc; | |
1dff9d0f | 630 | int ep_in_found = 0, ep_out_found = 0; |
9b70e007 SG |
631 | int i; |
632 | ||
633 | /* let's examine the device now */ | |
634 | iface = &dev->config.if_desc[ifnum]; | |
635 | iface_desc = &dev->config.if_desc[ifnum].desc; | |
636 | ||
637 | for (i = 0; asix_dongles[i].vendor != 0; i++) { | |
638 | if (dev->descriptor.idVendor == asix_dongles[i].vendor && | |
639 | dev->descriptor.idProduct == asix_dongles[i].product) | |
640 | /* Found a supported dongle */ | |
641 | break; | |
642 | } | |
643 | ||
644 | if (asix_dongles[i].vendor == 0) | |
645 | return 0; | |
646 | ||
647 | memset(ss, 0, sizeof(struct ueth_data)); | |
648 | ||
649 | /* At this point, we know we've got a live one */ | |
650 | debug("\n\nUSB Ethernet device detected: %#04x:%#04x\n", | |
651 | dev->descriptor.idVendor, dev->descriptor.idProduct); | |
652 | ||
653 | /* Initialize the ueth_data structure with some useful info */ | |
654 | ss->ifnum = ifnum; | |
655 | ss->pusb_dev = dev; | |
656 | ss->subclass = iface_desc->bInterfaceSubClass; | |
657 | ss->protocol = iface_desc->bInterfaceProtocol; | |
658 | ||
58f8fab8 LS |
659 | /* alloc driver private */ |
660 | ss->dev_priv = calloc(1, sizeof(struct asix_private)); | |
661 | if (!ss->dev_priv) | |
662 | return 0; | |
663 | ||
664 | ((struct asix_private *)ss->dev_priv)->flags = asix_dongles[i].flags; | |
665 | ||
9b70e007 SG |
666 | /* |
667 | * We are expecting a minimum of 3 endpoints - in, out (bulk), and | |
668 | * int. We will ignore any others. | |
669 | */ | |
670 | for (i = 0; i < iface_desc->bNumEndpoints; i++) { | |
671 | /* is it an BULK endpoint? */ | |
672 | if ((iface->ep_desc[i].bmAttributes & | |
673 | USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { | |
1dff9d0f LS |
674 | u8 ep_addr = iface->ep_desc[i].bEndpointAddress; |
675 | if (ep_addr & USB_DIR_IN) { | |
676 | if (!ep_in_found) { | |
677 | ss->ep_in = ep_addr & | |
678 | USB_ENDPOINT_NUMBER_MASK; | |
679 | ep_in_found = 1; | |
680 | } | |
681 | } else { | |
682 | if (!ep_out_found) { | |
683 | ss->ep_out = ep_addr & | |
684 | USB_ENDPOINT_NUMBER_MASK; | |
685 | ep_out_found = 1; | |
686 | } | |
687 | } | |
9b70e007 SG |
688 | } |
689 | ||
690 | /* is it an interrupt endpoint? */ | |
691 | if ((iface->ep_desc[i].bmAttributes & | |
692 | USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { | |
693 | ss->ep_int = iface->ep_desc[i].bEndpointAddress & | |
694 | USB_ENDPOINT_NUMBER_MASK; | |
695 | ss->irqinterval = iface->ep_desc[i].bInterval; | |
696 | } | |
697 | } | |
698 | debug("Endpoints In %d Out %d Int %d\n", | |
699 | ss->ep_in, ss->ep_out, ss->ep_int); | |
700 | ||
701 | /* Do some basic sanity checks, and bail if we find a problem */ | |
702 | if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) || | |
703 | !ss->ep_in || !ss->ep_out || !ss->ep_int) { | |
704 | debug("Problems with device\n"); | |
705 | return 0; | |
706 | } | |
707 | dev->privptr = (void *)ss; | |
708 | return 1; | |
709 | } | |
710 | ||
711 | int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss, | |
712 | struct eth_device *eth) | |
713 | { | |
58f8fab8 LS |
714 | struct asix_private *priv = (struct asix_private *)ss->dev_priv; |
715 | ||
9b70e007 SG |
716 | if (!eth) { |
717 | debug("%s: missing parameter.\n", __func__); | |
718 | return 0; | |
719 | } | |
720 | sprintf(eth->name, "%s%d", ASIX_BASE_NAME, curr_eth_dev++); | |
721 | eth->init = asix_init; | |
722 | eth->send = asix_send; | |
723 | eth->recv = asix_recv; | |
724 | eth->halt = asix_halt; | |
58f8fab8 LS |
725 | if (!(priv->flags & FLAG_TYPE_AX88172)) |
726 | eth->write_hwaddr = asix_write_hwaddr; | |
9b70e007 SG |
727 | eth->priv = ss; |
728 | ||
5fae53d0 LS |
729 | if (asix_basic_reset(ss)) |
730 | return 0; | |
731 | ||
02c8d8cc | 732 | /* Get the MAC address */ |
fbc4b8af | 733 | if (asix_read_mac_common(ss, priv, eth->enetaddr)) |
02c8d8cc LS |
734 | return 0; |
735 | debug("MAC %pM\n", eth->enetaddr); | |
736 | ||
9b70e007 SG |
737 | return 1; |
738 | } | |
fbc4b8af SG |
739 | #endif |
740 | ||
741 | #ifdef CONFIG_DM_ETH | |
742 | static int asix_eth_start(struct udevice *dev) | |
743 | { | |
14727120 | 744 | struct eth_pdata *pdata = dev_get_platdata(dev); |
fbc4b8af SG |
745 | struct asix_private *priv = dev_get_priv(dev); |
746 | ||
14727120 | 747 | return asix_init_common(&priv->ueth, pdata->enetaddr); |
fbc4b8af SG |
748 | } |
749 | ||
750 | void asix_eth_stop(struct udevice *dev) | |
751 | { | |
752 | debug("** %s()\n", __func__); | |
753 | } | |
754 | ||
755 | int asix_eth_send(struct udevice *dev, void *packet, int length) | |
756 | { | |
757 | struct asix_private *priv = dev_get_priv(dev); | |
758 | ||
759 | return asix_send_common(&priv->ueth, packet, length); | |
760 | } | |
761 | ||
762 | int asix_eth_recv(struct udevice *dev, int flags, uchar **packetp) | |
763 | { | |
764 | struct asix_private *priv = dev_get_priv(dev); | |
765 | struct ueth_data *ueth = &priv->ueth; | |
766 | uint8_t *ptr; | |
767 | int ret, len; | |
768 | u32 packet_len; | |
769 | ||
770 | len = usb_ether_get_rx_bytes(ueth, &ptr); | |
771 | debug("%s: first try, len=%d\n", __func__, len); | |
772 | if (!len) { | |
773 | if (!(flags & ETH_RECV_CHECK_DEVICE)) | |
774 | return -EAGAIN; | |
775 | ret = usb_ether_receive(ueth, AX_RX_URB_SIZE); | |
776 | if (ret == -EAGAIN) | |
777 | return ret; | |
778 | ||
779 | len = usb_ether_get_rx_bytes(ueth, &ptr); | |
780 | debug("%s: second try, len=%d\n", __func__, len); | |
781 | } | |
782 | ||
783 | /* | |
784 | * 1st 4 bytes contain the length of the actual data as two | |
785 | * complementary 16-bit words. Extract the length of the data. | |
786 | */ | |
787 | if (len < sizeof(packet_len)) { | |
788 | debug("Rx: incomplete packet length\n"); | |
789 | goto err; | |
790 | } | |
791 | memcpy(&packet_len, ptr, sizeof(packet_len)); | |
792 | le32_to_cpus(&packet_len); | |
793 | if (((~packet_len >> 16) & 0x7ff) != (packet_len & 0x7ff)) { | |
794 | debug("Rx: malformed packet length: %#x (%#x:%#x)\n", | |
795 | packet_len, (~packet_len >> 16) & 0x7ff, | |
796 | packet_len & 0x7ff); | |
797 | goto err; | |
798 | } | |
799 | packet_len = packet_len & 0x7ff; | |
800 | if (packet_len > len - sizeof(packet_len)) { | |
801 | debug("Rx: too large packet: %d\n", packet_len); | |
802 | goto err; | |
803 | } | |
804 | ||
805 | *packetp = ptr + sizeof(packet_len); | |
806 | return packet_len; | |
807 | ||
808 | err: | |
809 | usb_ether_advance_rxbuf(ueth, -1); | |
810 | return -EINVAL; | |
811 | } | |
812 | ||
813 | static int asix_free_pkt(struct udevice *dev, uchar *packet, int packet_len) | |
814 | { | |
815 | struct asix_private *priv = dev_get_priv(dev); | |
816 | ||
817 | if (packet_len & 1) | |
818 | packet_len++; | |
819 | usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len); | |
820 | ||
821 | return 0; | |
822 | } | |
823 | ||
824 | int asix_write_hwaddr(struct udevice *dev) | |
825 | { | |
826 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
827 | struct asix_private *priv = dev_get_priv(dev); | |
828 | ||
829 | if (priv->flags & FLAG_TYPE_AX88172) | |
830 | return -ENOSYS; | |
831 | ||
832 | return asix_write_hwaddr_common(&priv->ueth, pdata->enetaddr); | |
833 | } | |
834 | ||
835 | static int asix_eth_probe(struct udevice *dev) | |
836 | { | |
837 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
838 | struct asix_private *priv = dev_get_priv(dev); | |
839 | struct ueth_data *ss = &priv->ueth; | |
840 | int ret; | |
841 | ||
842 | priv->flags = dev->driver_data; | |
843 | ret = usb_ether_register(dev, ss, AX_RX_URB_SIZE); | |
844 | if (ret) | |
845 | return ret; | |
846 | ||
847 | ret = asix_basic_reset(ss); | |
848 | if (ret) | |
849 | goto err; | |
850 | ||
851 | /* Get the MAC address */ | |
852 | ret = asix_read_mac_common(ss, priv, pdata->enetaddr); | |
853 | if (ret) | |
854 | goto err; | |
855 | debug("MAC %pM\n", pdata->enetaddr); | |
856 | ||
857 | return 0; | |
858 | ||
859 | err: | |
860 | return usb_ether_deregister(ss); | |
861 | } | |
862 | ||
863 | static const struct eth_ops asix_eth_ops = { | |
864 | .start = asix_eth_start, | |
865 | .send = asix_eth_send, | |
866 | .recv = asix_eth_recv, | |
867 | .free_pkt = asix_free_pkt, | |
868 | .stop = asix_eth_stop, | |
869 | .write_hwaddr = asix_write_hwaddr, | |
870 | }; | |
871 | ||
872 | U_BOOT_DRIVER(asix_eth) = { | |
873 | .name = "asix_eth", | |
874 | .id = UCLASS_ETH, | |
875 | .probe = asix_eth_probe, | |
876 | .ops = &asix_eth_ops, | |
877 | .priv_auto_alloc_size = sizeof(struct asix_private), | |
878 | .platdata_auto_alloc_size = sizeof(struct eth_pdata), | |
879 | }; | |
880 | ||
881 | static const struct usb_device_id asix_eth_id_table[] = { | |
882 | /* Apple USB Ethernet Adapter */ | |
883 | { USB_DEVICE(0x05ac, 0x1402), .driver_info = FLAG_TYPE_AX88772 }, | |
884 | /* D-Link DUB-E100 H/W Ver B1 */ | |
885 | { USB_DEVICE(0x07d1, 0x3c05), .driver_info = FLAG_TYPE_AX88772 }, | |
886 | /* D-Link DUB-E100 H/W Ver C1 */ | |
887 | { USB_DEVICE(0x2001, 0x1a02), .driver_info = FLAG_TYPE_AX88772 }, | |
888 | /* Cables-to-Go USB Ethernet Adapter */ | |
889 | { USB_DEVICE(0x0b95, 0x772a), .driver_info = FLAG_TYPE_AX88772 }, | |
890 | /* Trendnet TU2-ET100 V3.0R */ | |
891 | { USB_DEVICE(0x0b95, 0x7720), .driver_info = FLAG_TYPE_AX88772 }, | |
892 | /* SMC */ | |
893 | { USB_DEVICE(0x0b95, 0x1720), .driver_info = FLAG_TYPE_AX88172 }, | |
894 | /* MSI - ASIX 88772a */ | |
895 | { USB_DEVICE(0x0db0, 0xa877), .driver_info = FLAG_TYPE_AX88772 }, | |
896 | /* Linksys 200M v2.1 */ | |
897 | { USB_DEVICE(0x13b1, 0x0018), .driver_info = FLAG_TYPE_AX88172 }, | |
898 | /* 0Q0 cable ethernet */ | |
899 | { USB_DEVICE(0x1557, 0x7720), .driver_info = FLAG_TYPE_AX88772 }, | |
900 | /* DLink DUB-E100 H/W Ver B1 Alternate */ | |
901 | { USB_DEVICE(0x2001, 0x3c05), .driver_info = FLAG_TYPE_AX88772 }, | |
902 | /* ASIX 88772B */ | |
903 | { USB_DEVICE(0x0b95, 0x772b), | |
904 | .driver_info = FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC }, | |
905 | { USB_DEVICE(0x0b95, 0x7e2b), .driver_info = FLAG_TYPE_AX88772B }, | |
906 | { } /* Terminating entry */ | |
907 | }; | |
908 | ||
909 | U_BOOT_USB_DEVICE(asix_eth, asix_eth_id_table); | |
910 | #endif |