]>
Commit | Line | Data |
---|---|---|
2e55cc72 DB |
1 | /* |
2 | * ASIX AX8817X based USB 2.0 Ethernet Devices | |
933a27d3 | 3 | * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com> |
2e55cc72 | 4 | * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> |
933a27d3 | 5 | * Copyright (C) 2006 James Painter <jamie.painter@iname.com> |
2e55cc72 DB |
6 | * Copyright (c) 2002-2003 TiVo Inc. |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
9cb00073 | 19 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
2e55cc72 DB |
20 | */ |
21 | ||
607740bc CR |
22 | #include "asix.h" |
23 | ||
24 | int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, | |
d9fe64e5 | 25 | u16 size, void *data, int in_pm) |
2e55cc72 | 26 | { |
0bc69efb | 27 | int ret; |
d9fe64e5 RF |
28 | int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); |
29 | ||
30 | BUG_ON(!dev); | |
31 | ||
32 | if (!in_pm) | |
33 | fn = usbnet_read_cmd; | |
34 | else | |
35 | fn = usbnet_read_cmd_nopm; | |
36 | ||
37 | ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
38 | value, index, data, size); | |
39 | ||
40 | if (unlikely(ret < 0)) | |
41 | netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n", | |
42 | index, ret); | |
0bc69efb | 43 | |
0bc69efb | 44 | return ret; |
2e55cc72 DB |
45 | } |
46 | ||
607740bc | 47 | int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, |
d9fe64e5 | 48 | u16 size, void *data, int in_pm) |
2e55cc72 | 49 | { |
d9fe64e5 RF |
50 | int ret; |
51 | int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); | |
52 | ||
53 | BUG_ON(!dev); | |
54 | ||
55 | if (!in_pm) | |
56 | fn = usbnet_write_cmd; | |
57 | else | |
58 | fn = usbnet_write_cmd_nopm; | |
59 | ||
60 | ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
61 | value, index, data, size); | |
62 | ||
63 | if (unlikely(ret < 0)) | |
64 | netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n", | |
65 | index, ret); | |
66 | ||
67 | return ret; | |
2e55cc72 DB |
68 | } |
69 | ||
607740bc CR |
70 | void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, |
71 | u16 size, void *data) | |
933a27d3 | 72 | { |
0bc69efb ML |
73 | usbnet_write_cmd_async(dev, cmd, |
74 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
75 | value, index, data, size); | |
933a27d3 DH |
76 | } |
77 | ||
960eb4ee DJ |
78 | static void reset_asix_rx_fixup_info(struct asix_rx_fixup_info *rx) |
79 | { | |
80 | /* Reset the variables that have a lifetime outside of | |
81 | * asix_rx_fixup_internal() so that future processing starts from a | |
82 | * known set of initial conditions. | |
83 | */ | |
84 | ||
85 | if (rx->ax_skb) { | |
86 | /* Discard any incomplete Ethernet frame in the netdev buffer */ | |
87 | kfree_skb(rx->ax_skb); | |
88 | rx->ax_skb = NULL; | |
89 | } | |
90 | ||
91 | /* Assume the Data header 32-bit word is at the start of the current | |
92 | * or next URB socket buffer so reset all the state variables. | |
93 | */ | |
94 | rx->remaining = 0; | |
95 | rx->split_head = false; | |
96 | rx->header = 0; | |
97 | } | |
98 | ||
8b5b6f54 LS |
99 | int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, |
100 | struct asix_rx_fixup_info *rx) | |
933a27d3 | 101 | { |
a9e0aca4 | 102 | int offset = 0; |
7b0378f5 | 103 | u16 size; |
933a27d3 | 104 | |
3f30b158 DJ |
105 | /* When an Ethernet frame spans multiple URB socket buffers, |
106 | * do a sanity test for the Data header synchronisation. | |
107 | * Attempt to detect the situation of the previous socket buffer having | |
108 | * been truncated or a socket buffer was missing. These situations | |
109 | * cause a discontinuity in the data stream and therefore need to avoid | |
110 | * appending bad data to the end of the current netdev socket buffer. | |
111 | * Also avoid unnecessarily discarding a good current netdev socket | |
112 | * buffer. | |
113 | */ | |
114 | if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) { | |
cd9e2e5d | 115 | offset = ((rx->remaining + 1) & 0xfffe); |
3f30b158 DJ |
116 | rx->header = get_unaligned_le32(skb->data + offset); |
117 | offset = 0; | |
118 | ||
119 | size = (u16)(rx->header & 0x7ff); | |
120 | if (size != ((~rx->header >> 16) & 0x7ff)) { | |
121 | netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n", | |
122 | rx->remaining); | |
960eb4ee | 123 | reset_asix_rx_fixup_info(rx); |
3f30b158 DJ |
124 | } |
125 | } | |
126 | ||
8b5b6f54 | 127 | while (offset + sizeof(u16) <= skb->len) { |
7b0378f5 | 128 | u16 copy_length; |
933a27d3 | 129 | |
7b0378f5 | 130 | if (!rx->remaining) { |
3bfc69ab DJ |
131 | if (skb->len - offset == sizeof(u16)) { |
132 | rx->header = get_unaligned_le16( | |
133 | skb->data + offset); | |
134 | rx->split_head = true; | |
135 | offset += sizeof(u16); | |
136 | break; | |
137 | } | |
138 | ||
139 | if (rx->split_head == true) { | |
140 | rx->header |= (get_unaligned_le16( | |
141 | skb->data + offset) << 16); | |
142 | rx->split_head = false; | |
143 | offset += sizeof(u16); | |
8b5b6f54 LS |
144 | } else { |
145 | rx->header = get_unaligned_le32(skb->data + | |
146 | offset); | |
147 | offset += sizeof(u32); | |
148 | } | |
bc466e67 | 149 | |
7b0378f5 DJ |
150 | /* take frame length from Data header 32-bit word */ |
151 | size = (u16)(rx->header & 0x7ff); | |
152 | if (size != ((~rx->header >> 16) & 0x7ff)) { | |
8b5b6f54 LS |
153 | netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", |
154 | rx->header, offset); | |
960eb4ee | 155 | reset_asix_rx_fixup_info(rx); |
8b5b6f54 LS |
156 | return 0; |
157 | } | |
9a5ccd8e | 158 | if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { |
b70183db | 159 | netdev_dbg(dev->net, "asix_rx_fixup() Bad RX Length %d\n", |
9a5ccd8e | 160 | size); |
960eb4ee | 161 | reset_asix_rx_fixup_info(rx); |
9a5ccd8e DJ |
162 | return 0; |
163 | } | |
164 | ||
6a570814 DJ |
165 | /* Sometimes may fail to get a netdev socket buffer but |
166 | * continue to process the URB socket buffer so that | |
167 | * synchronisation of the Ethernet frame Data header | |
168 | * word is maintained. | |
169 | */ | |
7b0378f5 | 170 | rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, size); |
3f78d1f2 | 171 | |
9a5ccd8e | 172 | rx->remaining = size; |
933a27d3 | 173 | } |
933a27d3 | 174 | |
7b0378f5 DJ |
175 | if (rx->remaining > skb->len - offset) { |
176 | copy_length = skb->len - offset; | |
177 | rx->remaining -= copy_length; | |
178 | } else { | |
179 | copy_length = rx->remaining; | |
180 | rx->remaining = 0; | |
8b5b6f54 | 181 | } |
933a27d3 | 182 | |
6a570814 | 183 | if (rx->ax_skb) { |
b952f4df | 184 | skb_put_data(rx->ax_skb, skb->data + offset, |
185 | copy_length); | |
22889dbb | 186 | if (!rx->remaining) { |
6a570814 | 187 | usbnet_skb_return(dev, rx->ax_skb); |
22889dbb DJ |
188 | rx->ax_skb = NULL; |
189 | } | |
6a570814 | 190 | } |
8b5b6f54 | 191 | |
7b0378f5 | 192 | offset += (copy_length + 1) & 0xfffe; |
933a27d3 DH |
193 | } |
194 | ||
a9e0aca4 | 195 | if (skb->len != offset) { |
8b5b6f54 LS |
196 | netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n", |
197 | skb->len, offset); | |
960eb4ee | 198 | reset_asix_rx_fixup_info(rx); |
933a27d3 DH |
199 | return 0; |
200 | } | |
8b5b6f54 | 201 | |
933a27d3 DH |
202 | return 1; |
203 | } | |
204 | ||
8b5b6f54 LS |
205 | int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb) |
206 | { | |
207 | struct asix_common_private *dp = dev->driver_priv; | |
208 | struct asix_rx_fixup_info *rx = &dp->rx_fixup_info; | |
209 | ||
210 | return asix_rx_fixup_internal(dev, skb, rx); | |
211 | } | |
212 | ||
d0c8f338 DJ |
213 | void asix_rx_fixup_common_free(struct asix_common_private *dp) |
214 | { | |
215 | struct asix_rx_fixup_info *rx; | |
216 | ||
217 | if (!dp) | |
218 | return; | |
219 | ||
220 | rx = &dp->rx_fixup_info; | |
221 | ||
222 | if (rx->ax_skb) { | |
223 | kfree_skb(rx->ax_skb); | |
224 | rx->ax_skb = NULL; | |
225 | } | |
226 | } | |
227 | ||
607740bc CR |
228 | struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, |
229 | gfp_t flags) | |
933a27d3 DH |
230 | { |
231 | int padlen; | |
232 | int headroom = skb_headroom(skb); | |
233 | int tailroom = skb_tailroom(skb); | |
234 | u32 packet_len; | |
235 | u32 padbytes = 0xffff0000; | |
236 | ||
2a580949 | 237 | padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; |
933a27d3 | 238 | |
95162d65 ED |
239 | /* We need to push 4 bytes in front of frame (packet_len) |
240 | * and maybe add 4 bytes after the end (if padlen is 4) | |
241 | * | |
242 | * Avoid skb_copy_expand() expensive call, using following rules : | |
243 | * - We are allowed to push 4 bytes in headroom if skb_header_cloned() | |
244 | * is false (and if we have 4 bytes of headroom) | |
245 | * - We are allowed to put 4 bytes at tail if skb_cloned() | |
246 | * is false (and if we have 4 bytes of tailroom) | |
247 | * | |
242c1a28 | 248 | * TCP packets for example are cloned, but __skb_header_release() |
95162d65 ED |
249 | * was called in tcp stack, allowing us to use headroom for our needs. |
250 | */ | |
251 | if (!skb_header_cloned(skb) && | |
252 | !(padlen && skb_cloned(skb)) && | |
253 | headroom + tailroom >= 4 + padlen) { | |
254 | /* following should not happen, but better be safe */ | |
255 | if (headroom < 4 || | |
256 | tailroom < padlen) { | |
933a27d3 | 257 | skb->data = memmove(skb->head + 4, skb->data, skb->len); |
27a884dc | 258 | skb_set_tail_pointer(skb, skb->len); |
933a27d3 DH |
259 | } |
260 | } else { | |
261 | struct sk_buff *skb2; | |
95162d65 | 262 | |
933a27d3 DH |
263 | skb2 = skb_copy_expand(skb, 4, padlen, flags); |
264 | dev_kfree_skb_any(skb); | |
265 | skb = skb2; | |
266 | if (!skb) | |
267 | return NULL; | |
268 | } | |
269 | ||
95162d65 | 270 | packet_len = ((skb->len ^ 0x0000ffff) << 16) + skb->len; |
933a27d3 | 271 | skb_push(skb, 4); |
57e4f041 | 272 | cpu_to_le32s(&packet_len); |
27d7ff46 | 273 | skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); |
933a27d3 | 274 | |
2a580949 | 275 | if (padlen) { |
57e4f041 | 276 | cpu_to_le32s(&padbytes); |
27a884dc | 277 | memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); |
933a27d3 DH |
278 | skb_put(skb, sizeof(padbytes)); |
279 | } | |
1e9e39f4 | 280 | |
7a1e890e | 281 | usbnet_set_skb_tx_stats(skb, 1, 0); |
933a27d3 DH |
282 | return skb; |
283 | } | |
284 | ||
d9fe64e5 | 285 | int asix_set_sw_mii(struct usbnet *dev, int in_pm) |
48b1be6a DH |
286 | { |
287 | int ret; | |
d9fe64e5 RF |
288 | ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL, in_pm); |
289 | ||
48b1be6a | 290 | if (ret < 0) |
60b86755 | 291 | netdev_err(dev->net, "Failed to enable software MII access\n"); |
48b1be6a DH |
292 | return ret; |
293 | } | |
294 | ||
d9fe64e5 | 295 | int asix_set_hw_mii(struct usbnet *dev, int in_pm) |
48b1be6a DH |
296 | { |
297 | int ret; | |
d9fe64e5 | 298 | ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL, in_pm); |
48b1be6a | 299 | if (ret < 0) |
60b86755 | 300 | netdev_err(dev->net, "Failed to enable hardware MII access\n"); |
48b1be6a DH |
301 | return ret; |
302 | } | |
303 | ||
16626b0c | 304 | int asix_read_phy_addr(struct usbnet *dev, int internal) |
48b1be6a | 305 | { |
16626b0c | 306 | int offset = (internal ? 1 : 0); |
51bf2976 | 307 | u8 buf[2]; |
d9fe64e5 | 308 | int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf, 0); |
48b1be6a | 309 | |
60b86755 | 310 | netdev_dbg(dev->net, "asix_get_phy_addr()\n"); |
933a27d3 | 311 | |
51bf2976 | 312 | if (ret < 0) { |
60b86755 | 313 | netdev_err(dev->net, "Error reading PHYID register: %02x\n", ret); |
51bf2976 | 314 | goto out; |
48b1be6a | 315 | } |
60b86755 JP |
316 | netdev_dbg(dev->net, "asix_get_phy_addr() returning 0x%04x\n", |
317 | *((__le16 *)buf)); | |
16626b0c | 318 | ret = buf[offset]; |
51bf2976 AV |
319 | |
320 | out: | |
48b1be6a DH |
321 | return ret; |
322 | } | |
323 | ||
16626b0c CR |
324 | int asix_get_phy_addr(struct usbnet *dev) |
325 | { | |
326 | /* return the address of the internal phy */ | |
327 | return asix_read_phy_addr(dev, 1); | |
328 | } | |
329 | ||
330 | ||
d9fe64e5 | 331 | int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm) |
48b1be6a DH |
332 | { |
333 | int ret; | |
334 | ||
d9fe64e5 | 335 | ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL, in_pm); |
48b1be6a | 336 | if (ret < 0) |
60b86755 | 337 | netdev_err(dev->net, "Failed to send software reset: %02x\n", ret); |
933a27d3 DH |
338 | |
339 | return ret; | |
340 | } | |
48b1be6a | 341 | |
d9fe64e5 | 342 | u16 asix_read_rx_ctl(struct usbnet *dev, int in_pm) |
933a27d3 | 343 | { |
51bf2976 | 344 | __le16 v; |
d9fe64e5 | 345 | int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v, in_pm); |
933a27d3 | 346 | |
51bf2976 | 347 | if (ret < 0) { |
60b86755 | 348 | netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret); |
51bf2976 | 349 | goto out; |
933a27d3 | 350 | } |
51bf2976 AV |
351 | ret = le16_to_cpu(v); |
352 | out: | |
48b1be6a DH |
353 | return ret; |
354 | } | |
355 | ||
d9fe64e5 | 356 | int asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm) |
48b1be6a DH |
357 | { |
358 | int ret; | |
359 | ||
60b86755 | 360 | netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode); |
d9fe64e5 | 361 | ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL, in_pm); |
48b1be6a | 362 | if (ret < 0) |
60b86755 JP |
363 | netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n", |
364 | mode, ret); | |
48b1be6a DH |
365 | |
366 | return ret; | |
367 | } | |
368 | ||
d9fe64e5 | 369 | u16 asix_read_medium_status(struct usbnet *dev, int in_pm) |
2e55cc72 | 370 | { |
51bf2976 | 371 | __le16 v; |
d9fe64e5 RF |
372 | int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, |
373 | 0, 0, 2, &v, in_pm); | |
2e55cc72 | 374 | |
51bf2976 | 375 | if (ret < 0) { |
60b86755 JP |
376 | netdev_err(dev->net, "Error reading Medium Status register: %02x\n", |
377 | ret); | |
83e1b918 | 378 | return ret; /* TODO: callers not checking for error ret */ |
2e55cc72 | 379 | } |
83e1b918 GG |
380 | |
381 | return le16_to_cpu(v); | |
382 | ||
2e55cc72 DB |
383 | } |
384 | ||
d9fe64e5 | 385 | int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm) |
2e55cc72 | 386 | { |
933a27d3 | 387 | int ret; |
2e55cc72 | 388 | |
60b86755 | 389 | netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode); |
d9fe64e5 RF |
390 | ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, |
391 | mode, 0, 0, NULL, in_pm); | |
933a27d3 | 392 | if (ret < 0) |
60b86755 JP |
393 | netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n", |
394 | mode, ret); | |
2e55cc72 | 395 | |
933a27d3 DH |
396 | return ret; |
397 | } | |
2e55cc72 | 398 | |
d9fe64e5 | 399 | int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm) |
933a27d3 DH |
400 | { |
401 | int ret; | |
2e55cc72 | 402 | |
60b86755 | 403 | netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value); |
d9fe64e5 | 404 | ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL, in_pm); |
933a27d3 | 405 | if (ret < 0) |
60b86755 JP |
406 | netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n", |
407 | value, ret); | |
2e55cc72 | 408 | |
933a27d3 DH |
409 | if (sleep) |
410 | msleep(sleep); | |
411 | ||
412 | return ret; | |
2e55cc72 DB |
413 | } |
414 | ||
933a27d3 DH |
415 | /* |
416 | * AX88772 & AX88178 have a 16-bit RX_CTL value | |
417 | */ | |
607740bc | 418 | void asix_set_multicast(struct net_device *net) |
2e55cc72 DB |
419 | { |
420 | struct usbnet *dev = netdev_priv(net); | |
48b1be6a | 421 | struct asix_data *data = (struct asix_data *)&dev->data; |
933a27d3 | 422 | u16 rx_ctl = AX_DEFAULT_RX_CTL; |
2e55cc72 DB |
423 | |
424 | if (net->flags & IFF_PROMISC) { | |
933a27d3 | 425 | rx_ctl |= AX_RX_CTL_PRO; |
8e95a202 | 426 | } else if (net->flags & IFF_ALLMULTI || |
4cd24eaf | 427 | netdev_mc_count(net) > AX_MAX_MCAST) { |
933a27d3 | 428 | rx_ctl |= AX_RX_CTL_AMALL; |
4cd24eaf | 429 | } else if (netdev_mc_empty(net)) { |
2e55cc72 DB |
430 | /* just broadcast and directed */ |
431 | } else { | |
432 | /* We use the 20 byte dev->data | |
433 | * for our 8 byte filter buffer | |
434 | * to avoid allocating memory that | |
435 | * is tricky to free later */ | |
22bedad3 | 436 | struct netdev_hw_addr *ha; |
2e55cc72 | 437 | u32 crc_bits; |
2e55cc72 DB |
438 | |
439 | memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); | |
440 | ||
441 | /* Build the multicast hash filter. */ | |
22bedad3 JP |
442 | netdev_for_each_mc_addr(ha, net) { |
443 | crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; | |
2e55cc72 DB |
444 | data->multi_filter[crc_bits >> 3] |= |
445 | 1 << (crc_bits & 7); | |
2e55cc72 DB |
446 | } |
447 | ||
48b1be6a | 448 | asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0, |
2e55cc72 DB |
449 | AX_MCAST_FILTER_SIZE, data->multi_filter); |
450 | ||
933a27d3 | 451 | rx_ctl |= AX_RX_CTL_AM; |
2e55cc72 DB |
452 | } |
453 | ||
48b1be6a | 454 | asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); |
2e55cc72 DB |
455 | } |
456 | ||
607740bc | 457 | int asix_mdio_read(struct net_device *netdev, int phy_id, int loc) |
2e55cc72 DB |
458 | { |
459 | struct usbnet *dev = netdev_priv(netdev); | |
51bf2976 | 460 | __le16 res; |
d9fe64e5 RF |
461 | u8 smsr; |
462 | int i = 0; | |
8a46f665 | 463 | int ret; |
2e55cc72 | 464 | |
a9fc6338 | 465 | mutex_lock(&dev->phy_mutex); |
d9fe64e5 | 466 | do { |
8a46f665 | 467 | ret = asix_set_sw_mii(dev, 0); |
610df1d2 | 468 | if (ret == -ENODEV || ret == -ETIMEDOUT) |
8a46f665 | 469 | break; |
d9fe64e5 | 470 | usleep_range(1000, 1100); |
8a46f665 RF |
471 | ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, |
472 | 0, 0, 1, &smsr, 0); | |
473 | } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV)); | |
610df1d2 | 474 | if (ret == -ENODEV || ret == -ETIMEDOUT) { |
8a46f665 RF |
475 | mutex_unlock(&dev->phy_mutex); |
476 | return ret; | |
477 | } | |
d9fe64e5 | 478 | |
48b1be6a | 479 | asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, |
d9fe64e5 RF |
480 | (__u16)loc, 2, &res, 0); |
481 | asix_set_hw_mii(dev, 0); | |
a9fc6338 | 482 | mutex_unlock(&dev->phy_mutex); |
2e55cc72 | 483 | |
60b86755 | 484 | netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", |
d9fe64e5 | 485 | phy_id, loc, le16_to_cpu(res)); |
2e55cc72 | 486 | |
51bf2976 | 487 | return le16_to_cpu(res); |
2e55cc72 DB |
488 | } |
489 | ||
607740bc | 490 | void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val) |
2e55cc72 DB |
491 | { |
492 | struct usbnet *dev = netdev_priv(netdev); | |
51bf2976 | 493 | __le16 res = cpu_to_le16(val); |
d9fe64e5 RF |
494 | u8 smsr; |
495 | int i = 0; | |
8a46f665 | 496 | int ret; |
2e55cc72 | 497 | |
60b86755 | 498 | netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", |
d9fe64e5 RF |
499 | phy_id, loc, val); |
500 | ||
501 | mutex_lock(&dev->phy_mutex); | |
502 | do { | |
8a46f665 RF |
503 | ret = asix_set_sw_mii(dev, 0); |
504 | if (ret == -ENODEV) | |
505 | break; | |
d9fe64e5 | 506 | usleep_range(1000, 1100); |
8a46f665 RF |
507 | ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, |
508 | 0, 0, 1, &smsr, 0); | |
509 | } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV)); | |
510 | if (ret == -ENODEV) { | |
511 | mutex_unlock(&dev->phy_mutex); | |
512 | return; | |
513 | } | |
d9fe64e5 RF |
514 | |
515 | asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, | |
516 | (__u16)loc, 2, &res, 0); | |
517 | asix_set_hw_mii(dev, 0); | |
518 | mutex_unlock(&dev->phy_mutex); | |
519 | } | |
520 | ||
521 | int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc) | |
522 | { | |
523 | struct usbnet *dev = netdev_priv(netdev); | |
524 | __le16 res; | |
525 | u8 smsr; | |
526 | int i = 0; | |
8a46f665 | 527 | int ret; |
d9fe64e5 RF |
528 | |
529 | mutex_lock(&dev->phy_mutex); | |
530 | do { | |
8a46f665 | 531 | ret = asix_set_sw_mii(dev, 1); |
610df1d2 | 532 | if (ret == -ENODEV || ret == -ETIMEDOUT) |
8a46f665 | 533 | break; |
d9fe64e5 | 534 | usleep_range(1000, 1100); |
8a46f665 RF |
535 | ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, |
536 | 0, 0, 1, &smsr, 1); | |
537 | } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV)); | |
610df1d2 | 538 | if (ret == -ENODEV || ret == -ETIMEDOUT) { |
8a46f665 RF |
539 | mutex_unlock(&dev->phy_mutex); |
540 | return ret; | |
541 | } | |
d9fe64e5 RF |
542 | |
543 | asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, | |
544 | (__u16)loc, 2, &res, 1); | |
545 | asix_set_hw_mii(dev, 1); | |
546 | mutex_unlock(&dev->phy_mutex); | |
547 | ||
548 | netdev_dbg(dev->net, "asix_mdio_read_nopm() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", | |
549 | phy_id, loc, le16_to_cpu(res)); | |
550 | ||
551 | return le16_to_cpu(res); | |
552 | } | |
553 | ||
554 | void | |
555 | asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, int val) | |
556 | { | |
557 | struct usbnet *dev = netdev_priv(netdev); | |
558 | __le16 res = cpu_to_le16(val); | |
559 | u8 smsr; | |
560 | int i = 0; | |
8a46f665 | 561 | int ret; |
d9fe64e5 RF |
562 | |
563 | netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", | |
564 | phy_id, loc, val); | |
565 | ||
a9fc6338 | 566 | mutex_lock(&dev->phy_mutex); |
d9fe64e5 | 567 | do { |
8a46f665 RF |
568 | ret = asix_set_sw_mii(dev, 1); |
569 | if (ret == -ENODEV) | |
570 | break; | |
d9fe64e5 | 571 | usleep_range(1000, 1100); |
8a46f665 RF |
572 | ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, |
573 | 0, 0, 1, &smsr, 1); | |
574 | } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV)); | |
575 | if (ret == -ENODEV) { | |
576 | mutex_unlock(&dev->phy_mutex); | |
577 | return; | |
578 | } | |
d9fe64e5 RF |
579 | |
580 | asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, | |
581 | (__u16)loc, 2, &res, 1); | |
582 | asix_set_hw_mii(dev, 1); | |
a9fc6338 | 583 | mutex_unlock(&dev->phy_mutex); |
2e55cc72 DB |
584 | } |
585 | ||
607740bc | 586 | void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) |
2e55cc72 DB |
587 | { |
588 | struct usbnet *dev = netdev_priv(net); | |
589 | u8 opt; | |
590 | ||
d9fe64e5 RF |
591 | if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, |
592 | 0, 0, 1, &opt, 0) < 0) { | |
2e55cc72 DB |
593 | wolinfo->supported = 0; |
594 | wolinfo->wolopts = 0; | |
595 | return; | |
596 | } | |
597 | wolinfo->supported = WAKE_PHY | WAKE_MAGIC; | |
598 | wolinfo->wolopts = 0; | |
f87ce5b2 | 599 | if (opt & AX_MONITOR_LINK) |
600 | wolinfo->wolopts |= WAKE_PHY; | |
601 | if (opt & AX_MONITOR_MAGIC) | |
602 | wolinfo->wolopts |= WAKE_MAGIC; | |
2e55cc72 DB |
603 | } |
604 | ||
607740bc | 605 | int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) |
2e55cc72 DB |
606 | { |
607 | struct usbnet *dev = netdev_priv(net); | |
608 | u8 opt = 0; | |
2e55cc72 DB |
609 | |
610 | if (wolinfo->wolopts & WAKE_PHY) | |
611 | opt |= AX_MONITOR_LINK; | |
612 | if (wolinfo->wolopts & WAKE_MAGIC) | |
613 | opt |= AX_MONITOR_MAGIC; | |
2e55cc72 | 614 | |
48b1be6a | 615 | if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, |
d9fe64e5 | 616 | opt, 0, 0, NULL, 0) < 0) |
2e55cc72 DB |
617 | return -EINVAL; |
618 | ||
619 | return 0; | |
620 | } | |
621 | ||
607740bc | 622 | int asix_get_eeprom_len(struct net_device *net) |
2e55cc72 | 623 | { |
ceb02c91 | 624 | return AX_EEPROM_LEN; |
2e55cc72 DB |
625 | } |
626 | ||
607740bc CR |
627 | int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, |
628 | u8 *data) | |
2e55cc72 DB |
629 | { |
630 | struct usbnet *dev = netdev_priv(net); | |
ceb02c91 CR |
631 | u16 *eeprom_buff; |
632 | int first_word, last_word; | |
2e55cc72 DB |
633 | int i; |
634 | ||
ceb02c91 | 635 | if (eeprom->len == 0) |
2e55cc72 DB |
636 | return -EINVAL; |
637 | ||
638 | eeprom->magic = AX_EEPROM_MAGIC; | |
639 | ||
ceb02c91 CR |
640 | first_word = eeprom->offset >> 1; |
641 | last_word = (eeprom->offset + eeprom->len - 1) >> 1; | |
642 | ||
6da2ec56 KC |
643 | eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16), |
644 | GFP_KERNEL); | |
ceb02c91 CR |
645 | if (!eeprom_buff) |
646 | return -ENOMEM; | |
647 | ||
2e55cc72 | 648 | /* ax8817x returns 2 bytes from eeprom on read */ |
ceb02c91 CR |
649 | for (i = first_word; i <= last_word; i++) { |
650 | if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2, | |
d9fe64e5 | 651 | &eeprom_buff[i - first_word], 0) < 0) { |
ceb02c91 CR |
652 | kfree(eeprom_buff); |
653 | return -EIO; | |
654 | } | |
2e55cc72 | 655 | } |
ceb02c91 CR |
656 | |
657 | memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len); | |
658 | kfree(eeprom_buff); | |
2e55cc72 DB |
659 | return 0; |
660 | } | |
661 | ||
cb7b24cd CR |
662 | int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, |
663 | u8 *data) | |
664 | { | |
665 | struct usbnet *dev = netdev_priv(net); | |
666 | u16 *eeprom_buff; | |
667 | int first_word, last_word; | |
668 | int i; | |
669 | int ret; | |
670 | ||
671 | netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n", | |
672 | eeprom->len, eeprom->offset, eeprom->magic); | |
673 | ||
674 | if (eeprom->len == 0) | |
675 | return -EINVAL; | |
676 | ||
677 | if (eeprom->magic != AX_EEPROM_MAGIC) | |
678 | return -EINVAL; | |
679 | ||
680 | first_word = eeprom->offset >> 1; | |
681 | last_word = (eeprom->offset + eeprom->len - 1) >> 1; | |
682 | ||
6da2ec56 KC |
683 | eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16), |
684 | GFP_KERNEL); | |
cb7b24cd CR |
685 | if (!eeprom_buff) |
686 | return -ENOMEM; | |
687 | ||
688 | /* align data to 16 bit boundaries, read the missing data from | |
689 | the EEPROM */ | |
690 | if (eeprom->offset & 1) { | |
691 | ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2, | |
d9fe64e5 | 692 | &eeprom_buff[0], 0); |
cb7b24cd CR |
693 | if (ret < 0) { |
694 | netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word); | |
695 | goto free; | |
696 | } | |
697 | } | |
698 | ||
699 | if ((eeprom->offset + eeprom->len) & 1) { | |
700 | ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2, | |
d9fe64e5 | 701 | &eeprom_buff[last_word - first_word], 0); |
cb7b24cd CR |
702 | if (ret < 0) { |
703 | netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word); | |
704 | goto free; | |
705 | } | |
706 | } | |
707 | ||
708 | memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len); | |
709 | ||
710 | /* write data to EEPROM */ | |
d9fe64e5 | 711 | ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL, 0); |
cb7b24cd CR |
712 | if (ret < 0) { |
713 | netdev_err(net, "Failed to enable EEPROM write\n"); | |
714 | goto free; | |
715 | } | |
716 | msleep(20); | |
717 | ||
718 | for (i = first_word; i <= last_word; i++) { | |
719 | netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n", | |
720 | i, eeprom_buff[i - first_word]); | |
721 | ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i, | |
d9fe64e5 | 722 | eeprom_buff[i - first_word], 0, NULL, 0); |
cb7b24cd CR |
723 | if (ret < 0) { |
724 | netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", | |
725 | i); | |
726 | goto free; | |
727 | } | |
728 | msleep(20); | |
729 | } | |
730 | ||
d9fe64e5 | 731 | ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL, 0); |
cb7b24cd CR |
732 | if (ret < 0) { |
733 | netdev_err(net, "Failed to disable EEPROM write\n"); | |
734 | goto free; | |
735 | } | |
736 | ||
737 | ret = 0; | |
738 | free: | |
739 | kfree(eeprom_buff); | |
740 | return ret; | |
741 | } | |
742 | ||
607740bc | 743 | void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) |
2e55cc72 DB |
744 | { |
745 | /* Inherit standard device info */ | |
746 | usbnet_get_drvinfo(net, info); | |
7826d43f JP |
747 | strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); |
748 | strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); | |
2e55cc72 DB |
749 | } |
750 | ||
607740bc | 751 | int asix_set_mac_address(struct net_device *net, void *p) |
7f29a3ba JK |
752 | { |
753 | struct usbnet *dev = netdev_priv(net); | |
754 | struct asix_data *data = (struct asix_data *)&dev->data; | |
755 | struct sockaddr *addr = p; | |
756 | ||
757 | if (netif_running(net)) | |
758 | return -EBUSY; | |
759 | if (!is_valid_ether_addr(addr->sa_data)) | |
760 | return -EADDRNOTAVAIL; | |
761 | ||
762 | memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); | |
763 | ||
764 | /* We use the 20 byte dev->data | |
765 | * for our 6 byte mac buffer | |
766 | * to avoid allocating memory that | |
767 | * is tricky to free later */ | |
768 | memcpy(data->mac_addr, addr->sa_data, ETH_ALEN); | |
769 | asix_write_cmd_async(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, | |
770 | data->mac_addr); | |
771 | ||
772 | return 0; | |
773 | } |