18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ASIX AX8817X based USB 2.0 Ethernet Devices 48c2ecf20Sopenharmony_ci * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com> 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> 68c2ecf20Sopenharmony_ci * Copyright (C) 2006 James Painter <jamie.painter@iname.com> 78c2ecf20Sopenharmony_ci * Copyright (c) 2002-2003 TiVo Inc. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "asix.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ciint asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, 138c2ecf20Sopenharmony_ci u16 size, void *data, int in_pm) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci int ret; 168c2ecf20Sopenharmony_ci int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci BUG_ON(!dev); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci if (!in_pm) 218c2ecf20Sopenharmony_ci fn = usbnet_read_cmd; 228c2ecf20Sopenharmony_ci else 238c2ecf20Sopenharmony_ci fn = usbnet_read_cmd_nopm; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 268c2ecf20Sopenharmony_ci value, index, data, size); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 298c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n", 308c2ecf20Sopenharmony_ci index, ret); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci return ret; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciint asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, 368c2ecf20Sopenharmony_ci u16 size, void *data, int in_pm) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci int ret; 398c2ecf20Sopenharmony_ci int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci BUG_ON(!dev); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (!in_pm) 448c2ecf20Sopenharmony_ci fn = usbnet_write_cmd; 458c2ecf20Sopenharmony_ci else 468c2ecf20Sopenharmony_ci fn = usbnet_write_cmd_nopm; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 498c2ecf20Sopenharmony_ci value, index, data, size); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 528c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n", 538c2ecf20Sopenharmony_ci index, ret); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return ret; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_civoid asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, 598c2ecf20Sopenharmony_ci u16 size, void *data) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci usbnet_write_cmd_async(dev, cmd, 628c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 638c2ecf20Sopenharmony_ci value, index, data, size); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void reset_asix_rx_fixup_info(struct asix_rx_fixup_info *rx) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci /* Reset the variables that have a lifetime outside of 698c2ecf20Sopenharmony_ci * asix_rx_fixup_internal() so that future processing starts from a 708c2ecf20Sopenharmony_ci * known set of initial conditions. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (rx->ax_skb) { 748c2ecf20Sopenharmony_ci /* Discard any incomplete Ethernet frame in the netdev buffer */ 758c2ecf20Sopenharmony_ci kfree_skb(rx->ax_skb); 768c2ecf20Sopenharmony_ci rx->ax_skb = NULL; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Assume the Data header 32-bit word is at the start of the current 808c2ecf20Sopenharmony_ci * or next URB socket buffer so reset all the state variables. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci rx->remaining = 0; 838c2ecf20Sopenharmony_ci rx->split_head = false; 848c2ecf20Sopenharmony_ci rx->header = 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciint asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, 888c2ecf20Sopenharmony_ci struct asix_rx_fixup_info *rx) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci int offset = 0; 918c2ecf20Sopenharmony_ci u16 size; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* When an Ethernet frame spans multiple URB socket buffers, 948c2ecf20Sopenharmony_ci * do a sanity test for the Data header synchronisation. 958c2ecf20Sopenharmony_ci * Attempt to detect the situation of the previous socket buffer having 968c2ecf20Sopenharmony_ci * been truncated or a socket buffer was missing. These situations 978c2ecf20Sopenharmony_ci * cause a discontinuity in the data stream and therefore need to avoid 988c2ecf20Sopenharmony_ci * appending bad data to the end of the current netdev socket buffer. 998c2ecf20Sopenharmony_ci * Also avoid unnecessarily discarding a good current netdev socket 1008c2ecf20Sopenharmony_ci * buffer. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) { 1038c2ecf20Sopenharmony_ci offset = ((rx->remaining + 1) & 0xfffe); 1048c2ecf20Sopenharmony_ci rx->header = get_unaligned_le32(skb->data + offset); 1058c2ecf20Sopenharmony_ci offset = 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci size = (u16)(rx->header & 0x7ff); 1088c2ecf20Sopenharmony_ci if (size != ((~rx->header >> 16) & 0x7ff)) { 1098c2ecf20Sopenharmony_ci netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n", 1108c2ecf20Sopenharmony_ci rx->remaining); 1118c2ecf20Sopenharmony_ci reset_asix_rx_fixup_info(rx); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci while (offset + sizeof(u16) <= skb->len) { 1168c2ecf20Sopenharmony_ci u16 copy_length; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (!rx->remaining) { 1198c2ecf20Sopenharmony_ci if (skb->len - offset == sizeof(u16)) { 1208c2ecf20Sopenharmony_ci rx->header = get_unaligned_le16( 1218c2ecf20Sopenharmony_ci skb->data + offset); 1228c2ecf20Sopenharmony_ci rx->split_head = true; 1238c2ecf20Sopenharmony_ci offset += sizeof(u16); 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (rx->split_head == true) { 1288c2ecf20Sopenharmony_ci rx->header |= (get_unaligned_le16( 1298c2ecf20Sopenharmony_ci skb->data + offset) << 16); 1308c2ecf20Sopenharmony_ci rx->split_head = false; 1318c2ecf20Sopenharmony_ci offset += sizeof(u16); 1328c2ecf20Sopenharmony_ci } else { 1338c2ecf20Sopenharmony_ci rx->header = get_unaligned_le32(skb->data + 1348c2ecf20Sopenharmony_ci offset); 1358c2ecf20Sopenharmony_ci offset += sizeof(u32); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* take frame length from Data header 32-bit word */ 1398c2ecf20Sopenharmony_ci size = (u16)(rx->header & 0x7ff); 1408c2ecf20Sopenharmony_ci if (size != ((~rx->header >> 16) & 0x7ff)) { 1418c2ecf20Sopenharmony_ci netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", 1428c2ecf20Sopenharmony_ci rx->header, offset); 1438c2ecf20Sopenharmony_ci reset_asix_rx_fixup_info(rx); 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { 1478c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "asix_rx_fixup() Bad RX Length %d\n", 1488c2ecf20Sopenharmony_ci size); 1498c2ecf20Sopenharmony_ci reset_asix_rx_fixup_info(rx); 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Sometimes may fail to get a netdev socket buffer but 1548c2ecf20Sopenharmony_ci * continue to process the URB socket buffer so that 1558c2ecf20Sopenharmony_ci * synchronisation of the Ethernet frame Data header 1568c2ecf20Sopenharmony_ci * word is maintained. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, size); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci rx->remaining = size; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (rx->remaining > skb->len - offset) { 1648c2ecf20Sopenharmony_ci copy_length = skb->len - offset; 1658c2ecf20Sopenharmony_ci rx->remaining -= copy_length; 1668c2ecf20Sopenharmony_ci } else { 1678c2ecf20Sopenharmony_ci copy_length = rx->remaining; 1688c2ecf20Sopenharmony_ci rx->remaining = 0; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (rx->ax_skb) { 1728c2ecf20Sopenharmony_ci skb_put_data(rx->ax_skb, skb->data + offset, 1738c2ecf20Sopenharmony_ci copy_length); 1748c2ecf20Sopenharmony_ci if (!rx->remaining) { 1758c2ecf20Sopenharmony_ci usbnet_skb_return(dev, rx->ax_skb); 1768c2ecf20Sopenharmony_ci rx->ax_skb = NULL; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci offset += (copy_length + 1) & 0xfffe; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (skb->len != offset) { 1848c2ecf20Sopenharmony_ci netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n", 1858c2ecf20Sopenharmony_ci skb->len, offset); 1868c2ecf20Sopenharmony_ci reset_asix_rx_fixup_info(rx); 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return 1; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciint asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct asix_common_private *dp = dev->driver_priv; 1968c2ecf20Sopenharmony_ci struct asix_rx_fixup_info *rx = &dp->rx_fixup_info; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return asix_rx_fixup_internal(dev, skb, rx); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_civoid asix_rx_fixup_common_free(struct asix_common_private *dp) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct asix_rx_fixup_info *rx; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (!dp) 2068c2ecf20Sopenharmony_ci return; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci rx = &dp->rx_fixup_info; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (rx->ax_skb) { 2118c2ecf20Sopenharmony_ci kfree_skb(rx->ax_skb); 2128c2ecf20Sopenharmony_ci rx->ax_skb = NULL; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistruct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 2178c2ecf20Sopenharmony_ci gfp_t flags) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci int padlen; 2208c2ecf20Sopenharmony_ci int headroom = skb_headroom(skb); 2218c2ecf20Sopenharmony_ci int tailroom = skb_tailroom(skb); 2228c2ecf20Sopenharmony_ci u32 packet_len; 2238c2ecf20Sopenharmony_ci u32 padbytes = 0xffff0000; 2248c2ecf20Sopenharmony_ci void *ptr; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* We need to push 4 bytes in front of frame (packet_len) 2298c2ecf20Sopenharmony_ci * and maybe add 4 bytes after the end (if padlen is 4) 2308c2ecf20Sopenharmony_ci * 2318c2ecf20Sopenharmony_ci * Avoid skb_copy_expand() expensive call, using following rules : 2328c2ecf20Sopenharmony_ci * - We are allowed to push 4 bytes in headroom if skb_header_cloned() 2338c2ecf20Sopenharmony_ci * is false (and if we have 4 bytes of headroom) 2348c2ecf20Sopenharmony_ci * - We are allowed to put 4 bytes at tail if skb_cloned() 2358c2ecf20Sopenharmony_ci * is false (and if we have 4 bytes of tailroom) 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci * TCP packets for example are cloned, but __skb_header_release() 2388c2ecf20Sopenharmony_ci * was called in tcp stack, allowing us to use headroom for our needs. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci if (!skb_header_cloned(skb) && 2418c2ecf20Sopenharmony_ci !(padlen && skb_cloned(skb)) && 2428c2ecf20Sopenharmony_ci headroom + tailroom >= 4 + padlen) { 2438c2ecf20Sopenharmony_ci /* following should not happen, but better be safe */ 2448c2ecf20Sopenharmony_ci if (headroom < 4 || 2458c2ecf20Sopenharmony_ci tailroom < padlen) { 2468c2ecf20Sopenharmony_ci skb->data = memmove(skb->head + 4, skb->data, skb->len); 2478c2ecf20Sopenharmony_ci skb_set_tail_pointer(skb, skb->len); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } else { 2508c2ecf20Sopenharmony_ci struct sk_buff *skb2; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci skb2 = skb_copy_expand(skb, 4, padlen, flags); 2538c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2548c2ecf20Sopenharmony_ci skb = skb2; 2558c2ecf20Sopenharmony_ci if (!skb) 2568c2ecf20Sopenharmony_ci return NULL; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci packet_len = ((skb->len ^ 0x0000ffff) << 16) + skb->len; 2608c2ecf20Sopenharmony_ci ptr = skb_push(skb, 4); 2618c2ecf20Sopenharmony_ci put_unaligned_le32(packet_len, ptr); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (padlen) { 2648c2ecf20Sopenharmony_ci put_unaligned_le32(padbytes, skb_tail_pointer(skb)); 2658c2ecf20Sopenharmony_ci skb_put(skb, sizeof(padbytes)); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci usbnet_set_skb_tx_stats(skb, 1, 0); 2698c2ecf20Sopenharmony_ci return skb; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ciint asix_set_sw_mii(struct usbnet *dev, int in_pm) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci int ret; 2758c2ecf20Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL, in_pm); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (ret < 0) 2788c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to enable software MII access\n"); 2798c2ecf20Sopenharmony_ci return ret; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ciint asix_set_hw_mii(struct usbnet *dev, int in_pm) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci int ret; 2858c2ecf20Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL, in_pm); 2868c2ecf20Sopenharmony_ci if (ret < 0) 2878c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to enable hardware MII access\n"); 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ciint asix_read_phy_addr(struct usbnet *dev, int internal) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci int offset = (internal ? 1 : 0); 2948c2ecf20Sopenharmony_ci u8 buf[2]; 2958c2ecf20Sopenharmony_ci int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf, 0); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "asix_get_phy_addr()\n"); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (ret < 2) { 3008c2ecf20Sopenharmony_ci netdev_err(dev->net, "Error reading PHYID register: %02x\n", ret); 3018c2ecf20Sopenharmony_ci goto out; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "asix_get_phy_addr() returning 0x%04x\n", 3048c2ecf20Sopenharmony_ci *((__le16 *)buf)); 3058c2ecf20Sopenharmony_ci ret = buf[offset]; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ciout: 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ciint asix_get_phy_addr(struct usbnet *dev) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci /* return the address of the internal phy */ 3148c2ecf20Sopenharmony_ci return asix_read_phy_addr(dev, 1); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ciint asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int ret; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL, in_pm); 3238c2ecf20Sopenharmony_ci if (ret < 0) 3248c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to send software reset: %02x\n", ret); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return ret; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ciu16 asix_read_rx_ctl(struct usbnet *dev, int in_pm) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci __le16 v; 3328c2ecf20Sopenharmony_ci int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v, in_pm); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (ret < 0) { 3358c2ecf20Sopenharmony_ci netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret); 3368c2ecf20Sopenharmony_ci goto out; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci ret = le16_to_cpu(v); 3398c2ecf20Sopenharmony_ciout: 3408c2ecf20Sopenharmony_ci return ret; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ciint asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci int ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode); 3488c2ecf20Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL, in_pm); 3498c2ecf20Sopenharmony_ci if (ret < 0) 3508c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n", 3518c2ecf20Sopenharmony_ci mode, ret); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ciu16 asix_read_medium_status(struct usbnet *dev, int in_pm) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci __le16 v; 3598c2ecf20Sopenharmony_ci int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, 3608c2ecf20Sopenharmony_ci 0, 0, 2, &v, in_pm); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (ret < 0) { 3638c2ecf20Sopenharmony_ci netdev_err(dev->net, "Error reading Medium Status register: %02x\n", 3648c2ecf20Sopenharmony_ci ret); 3658c2ecf20Sopenharmony_ci return ret; /* TODO: callers not checking for error ret */ 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return le16_to_cpu(v); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ciint asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci int ret; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode); 3778c2ecf20Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, 3788c2ecf20Sopenharmony_ci mode, 0, 0, NULL, in_pm); 3798c2ecf20Sopenharmony_ci if (ret < 0) 3808c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n", 3818c2ecf20Sopenharmony_ci mode, ret); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return ret; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciint asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci int ret; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value); 3918c2ecf20Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL, in_pm); 3928c2ecf20Sopenharmony_ci if (ret < 0) 3938c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n", 3948c2ecf20Sopenharmony_ci value, ret); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (sleep) 3978c2ecf20Sopenharmony_ci msleep(sleep); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return ret; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/* 4038c2ecf20Sopenharmony_ci * AX88772 & AX88178 have a 16-bit RX_CTL value 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_civoid asix_set_multicast(struct net_device *net) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 4088c2ecf20Sopenharmony_ci struct asix_data *data = (struct asix_data *)&dev->data; 4098c2ecf20Sopenharmony_ci u16 rx_ctl = AX_DEFAULT_RX_CTL; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (net->flags & IFF_PROMISC) { 4128c2ecf20Sopenharmony_ci rx_ctl |= AX_RX_CTL_PRO; 4138c2ecf20Sopenharmony_ci } else if (net->flags & IFF_ALLMULTI || 4148c2ecf20Sopenharmony_ci netdev_mc_count(net) > AX_MAX_MCAST) { 4158c2ecf20Sopenharmony_ci rx_ctl |= AX_RX_CTL_AMALL; 4168c2ecf20Sopenharmony_ci } else if (netdev_mc_empty(net)) { 4178c2ecf20Sopenharmony_ci /* just broadcast and directed */ 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci /* We use the 20 byte dev->data 4208c2ecf20Sopenharmony_ci * for our 8 byte filter buffer 4218c2ecf20Sopenharmony_ci * to avoid allocating memory that 4228c2ecf20Sopenharmony_ci * is tricky to free later */ 4238c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 4248c2ecf20Sopenharmony_ci u32 crc_bits; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* Build the multicast hash filter. */ 4298c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, net) { 4308c2ecf20Sopenharmony_ci crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; 4318c2ecf20Sopenharmony_ci data->multi_filter[crc_bits >> 3] |= 4328c2ecf20Sopenharmony_ci 1 << (crc_bits & 7); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0, 4368c2ecf20Sopenharmony_ci AX_MCAST_FILTER_SIZE, data->multi_filter); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci rx_ctl |= AX_RX_CTL_AM; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ciint asix_mdio_read(struct net_device *netdev, int phy_id, int loc) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 4478c2ecf20Sopenharmony_ci __le16 res; 4488c2ecf20Sopenharmony_ci u8 smsr; 4498c2ecf20Sopenharmony_ci int i = 0; 4508c2ecf20Sopenharmony_ci int ret; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 4538c2ecf20Sopenharmony_ci do { 4548c2ecf20Sopenharmony_ci ret = asix_set_sw_mii(dev, 0); 4558c2ecf20Sopenharmony_ci if (ret == -ENODEV || ret == -ETIMEDOUT) 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 4588c2ecf20Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 4598c2ecf20Sopenharmony_ci 0, 0, 1, &smsr, 0); 4608c2ecf20Sopenharmony_ci } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV)); 4618c2ecf20Sopenharmony_ci if (ret == -ENODEV || ret == -ETIMEDOUT) { 4628c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 4638c2ecf20Sopenharmony_ci return ret; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, 4678c2ecf20Sopenharmony_ci (__u16)loc, 2, &res, 0); 4688c2ecf20Sopenharmony_ci asix_set_hw_mii(dev, 0); 4698c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", 4728c2ecf20Sopenharmony_ci phy_id, loc, le16_to_cpu(res)); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return le16_to_cpu(res); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_civoid asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 4808c2ecf20Sopenharmony_ci __le16 res = cpu_to_le16(val); 4818c2ecf20Sopenharmony_ci u8 smsr; 4828c2ecf20Sopenharmony_ci int i = 0; 4838c2ecf20Sopenharmony_ci int ret; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", 4868c2ecf20Sopenharmony_ci phy_id, loc, val); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 4898c2ecf20Sopenharmony_ci do { 4908c2ecf20Sopenharmony_ci ret = asix_set_sw_mii(dev, 0); 4918c2ecf20Sopenharmony_ci if (ret == -ENODEV) 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 4948c2ecf20Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 4958c2ecf20Sopenharmony_ci 0, 0, 1, &smsr, 0); 4968c2ecf20Sopenharmony_ci } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV)); 4978c2ecf20Sopenharmony_ci if (ret == -ENODEV) { 4988c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 4998c2ecf20Sopenharmony_ci return; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, 5038c2ecf20Sopenharmony_ci (__u16)loc, 2, &res, 0); 5048c2ecf20Sopenharmony_ci asix_set_hw_mii(dev, 0); 5058c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ciint asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 5118c2ecf20Sopenharmony_ci __le16 res; 5128c2ecf20Sopenharmony_ci u8 smsr; 5138c2ecf20Sopenharmony_ci int i = 0; 5148c2ecf20Sopenharmony_ci int ret; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 5178c2ecf20Sopenharmony_ci do { 5188c2ecf20Sopenharmony_ci ret = asix_set_sw_mii(dev, 1); 5198c2ecf20Sopenharmony_ci if (ret == -ENODEV || ret == -ETIMEDOUT) 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 5228c2ecf20Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 5238c2ecf20Sopenharmony_ci 0, 0, 1, &smsr, 1); 5248c2ecf20Sopenharmony_ci } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV)); 5258c2ecf20Sopenharmony_ci if (ret == -ENODEV || ret == -ETIMEDOUT) { 5268c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 5278c2ecf20Sopenharmony_ci return ret; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, 5318c2ecf20Sopenharmony_ci (__u16)loc, 2, &res, 1); 5328c2ecf20Sopenharmony_ci asix_set_hw_mii(dev, 1); 5338c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "asix_mdio_read_nopm() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", 5368c2ecf20Sopenharmony_ci phy_id, loc, le16_to_cpu(res)); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return le16_to_cpu(res); 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_civoid 5428c2ecf20Sopenharmony_ciasix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, int val) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 5458c2ecf20Sopenharmony_ci __le16 res = cpu_to_le16(val); 5468c2ecf20Sopenharmony_ci u8 smsr; 5478c2ecf20Sopenharmony_ci int i = 0; 5488c2ecf20Sopenharmony_ci int ret; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", 5518c2ecf20Sopenharmony_ci phy_id, loc, val); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 5548c2ecf20Sopenharmony_ci do { 5558c2ecf20Sopenharmony_ci ret = asix_set_sw_mii(dev, 1); 5568c2ecf20Sopenharmony_ci if (ret == -ENODEV) 5578c2ecf20Sopenharmony_ci break; 5588c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 5598c2ecf20Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 5608c2ecf20Sopenharmony_ci 0, 0, 1, &smsr, 1); 5618c2ecf20Sopenharmony_ci } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV)); 5628c2ecf20Sopenharmony_ci if (ret == -ENODEV) { 5638c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 5648c2ecf20Sopenharmony_ci return; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, 5688c2ecf20Sopenharmony_ci (__u16)loc, 2, &res, 1); 5698c2ecf20Sopenharmony_ci asix_set_hw_mii(dev, 1); 5708c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_civoid asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 5768c2ecf20Sopenharmony_ci u8 opt; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 5798c2ecf20Sopenharmony_ci 0, 0, 1, &opt, 0) < 0) { 5808c2ecf20Sopenharmony_ci wolinfo->supported = 0; 5818c2ecf20Sopenharmony_ci wolinfo->wolopts = 0; 5828c2ecf20Sopenharmony_ci return; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci wolinfo->supported = WAKE_PHY | WAKE_MAGIC; 5858c2ecf20Sopenharmony_ci wolinfo->wolopts = 0; 5868c2ecf20Sopenharmony_ci if (opt & AX_MONITOR_LINK) 5878c2ecf20Sopenharmony_ci wolinfo->wolopts |= WAKE_PHY; 5888c2ecf20Sopenharmony_ci if (opt & AX_MONITOR_MAGIC) 5898c2ecf20Sopenharmony_ci wolinfo->wolopts |= WAKE_MAGIC; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ciint asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 5958c2ecf20Sopenharmony_ci u8 opt = 0; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (wolinfo->wolopts & ~(WAKE_PHY | WAKE_MAGIC)) 5988c2ecf20Sopenharmony_ci return -EINVAL; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (wolinfo->wolopts & WAKE_PHY) 6018c2ecf20Sopenharmony_ci opt |= AX_MONITOR_LINK; 6028c2ecf20Sopenharmony_ci if (wolinfo->wolopts & WAKE_MAGIC) 6038c2ecf20Sopenharmony_ci opt |= AX_MONITOR_MAGIC; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, 6068c2ecf20Sopenharmony_ci opt, 0, 0, NULL, 0) < 0) 6078c2ecf20Sopenharmony_ci return -EINVAL; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ciint asix_get_eeprom_len(struct net_device *net) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci return AX_EEPROM_LEN; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ciint asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, 6188c2ecf20Sopenharmony_ci u8 *data) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 6218c2ecf20Sopenharmony_ci u16 *eeprom_buff; 6228c2ecf20Sopenharmony_ci int first_word, last_word; 6238c2ecf20Sopenharmony_ci int i; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (eeprom->len == 0) 6268c2ecf20Sopenharmony_ci return -EINVAL; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci eeprom->magic = AX_EEPROM_MAGIC; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci first_word = eeprom->offset >> 1; 6318c2ecf20Sopenharmony_ci last_word = (eeprom->offset + eeprom->len - 1) >> 1; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16), 6348c2ecf20Sopenharmony_ci GFP_KERNEL); 6358c2ecf20Sopenharmony_ci if (!eeprom_buff) 6368c2ecf20Sopenharmony_ci return -ENOMEM; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci /* ax8817x returns 2 bytes from eeprom on read */ 6398c2ecf20Sopenharmony_ci for (i = first_word; i <= last_word; i++) { 6408c2ecf20Sopenharmony_ci if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2, 6418c2ecf20Sopenharmony_ci &eeprom_buff[i - first_word], 0) < 0) { 6428c2ecf20Sopenharmony_ci kfree(eeprom_buff); 6438c2ecf20Sopenharmony_ci return -EIO; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len); 6488c2ecf20Sopenharmony_ci kfree(eeprom_buff); 6498c2ecf20Sopenharmony_ci return 0; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ciint asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, 6538c2ecf20Sopenharmony_ci u8 *data) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 6568c2ecf20Sopenharmony_ci u16 *eeprom_buff; 6578c2ecf20Sopenharmony_ci int first_word, last_word; 6588c2ecf20Sopenharmony_ci int i; 6598c2ecf20Sopenharmony_ci int ret; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n", 6628c2ecf20Sopenharmony_ci eeprom->len, eeprom->offset, eeprom->magic); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (eeprom->len == 0) 6658c2ecf20Sopenharmony_ci return -EINVAL; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (eeprom->magic != AX_EEPROM_MAGIC) 6688c2ecf20Sopenharmony_ci return -EINVAL; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci first_word = eeprom->offset >> 1; 6718c2ecf20Sopenharmony_ci last_word = (eeprom->offset + eeprom->len - 1) >> 1; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16), 6748c2ecf20Sopenharmony_ci GFP_KERNEL); 6758c2ecf20Sopenharmony_ci if (!eeprom_buff) 6768c2ecf20Sopenharmony_ci return -ENOMEM; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* align data to 16 bit boundaries, read the missing data from 6798c2ecf20Sopenharmony_ci the EEPROM */ 6808c2ecf20Sopenharmony_ci if (eeprom->offset & 1) { 6818c2ecf20Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2, 6828c2ecf20Sopenharmony_ci &eeprom_buff[0], 0); 6838c2ecf20Sopenharmony_ci if (ret < 0) { 6848c2ecf20Sopenharmony_ci netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word); 6858c2ecf20Sopenharmony_ci goto free; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if ((eeprom->offset + eeprom->len) & 1) { 6908c2ecf20Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2, 6918c2ecf20Sopenharmony_ci &eeprom_buff[last_word - first_word], 0); 6928c2ecf20Sopenharmony_ci if (ret < 0) { 6938c2ecf20Sopenharmony_ci netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word); 6948c2ecf20Sopenharmony_ci goto free; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* write data to EEPROM */ 7018c2ecf20Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL, 0); 7028c2ecf20Sopenharmony_ci if (ret < 0) { 7038c2ecf20Sopenharmony_ci netdev_err(net, "Failed to enable EEPROM write\n"); 7048c2ecf20Sopenharmony_ci goto free; 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci msleep(20); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci for (i = first_word; i <= last_word; i++) { 7098c2ecf20Sopenharmony_ci netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n", 7108c2ecf20Sopenharmony_ci i, eeprom_buff[i - first_word]); 7118c2ecf20Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i, 7128c2ecf20Sopenharmony_ci eeprom_buff[i - first_word], 0, NULL, 0); 7138c2ecf20Sopenharmony_ci if (ret < 0) { 7148c2ecf20Sopenharmony_ci netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", 7158c2ecf20Sopenharmony_ci i); 7168c2ecf20Sopenharmony_ci goto free; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci msleep(20); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL, 0); 7228c2ecf20Sopenharmony_ci if (ret < 0) { 7238c2ecf20Sopenharmony_ci netdev_err(net, "Failed to disable EEPROM write\n"); 7248c2ecf20Sopenharmony_ci goto free; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci ret = 0; 7288c2ecf20Sopenharmony_cifree: 7298c2ecf20Sopenharmony_ci kfree(eeprom_buff); 7308c2ecf20Sopenharmony_ci return ret; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_civoid asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci /* Inherit standard device info */ 7368c2ecf20Sopenharmony_ci usbnet_get_drvinfo(net, info); 7378c2ecf20Sopenharmony_ci strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); 7388c2ecf20Sopenharmony_ci strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ciint asix_set_mac_address(struct net_device *net, void *p) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 7448c2ecf20Sopenharmony_ci struct asix_data *data = (struct asix_data *)&dev->data; 7458c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (netif_running(net)) 7488c2ecf20Sopenharmony_ci return -EBUSY; 7498c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 7508c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* We use the 20 byte dev->data 7558c2ecf20Sopenharmony_ci * for our 6 byte mac buffer 7568c2ecf20Sopenharmony_ci * to avoid allocating memory that 7578c2ecf20Sopenharmony_ci * is tricky to free later */ 7588c2ecf20Sopenharmony_ci memcpy(data->mac_addr, addr->sa_data, ETH_ALEN); 7598c2ecf20Sopenharmony_ci asix_write_cmd_async(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, 7608c2ecf20Sopenharmony_ci data->mac_addr); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci return 0; 7638c2ecf20Sopenharmony_ci} 764