162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ASIX AX8817X based USB 2.0 Ethernet Devices 462306a36Sopenharmony_ci * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com> 562306a36Sopenharmony_ci * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> 662306a36Sopenharmony_ci * Copyright (C) 2006 James Painter <jamie.painter@iname.com> 762306a36Sopenharmony_ci * Copyright (c) 2002-2003 TiVo Inc. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "asix.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define AX_HOST_EN_RETRIES 30 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ciint __must_check asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, 1562306a36Sopenharmony_ci u16 size, void *data, int in_pm) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci int ret; 1862306a36Sopenharmony_ci int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci BUG_ON(!dev); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci if (!in_pm) 2362306a36Sopenharmony_ci fn = usbnet_read_cmd; 2462306a36Sopenharmony_ci else 2562306a36Sopenharmony_ci fn = usbnet_read_cmd_nopm; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 2862306a36Sopenharmony_ci value, index, data, size); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (unlikely(ret < size)) { 3162306a36Sopenharmony_ci ret = ret < 0 ? ret : -ENODATA; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n", 3462306a36Sopenharmony_ci index, ret); 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return ret; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ciint asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, 4162306a36Sopenharmony_ci u16 size, void *data, int in_pm) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci int ret; 4462306a36Sopenharmony_ci int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci BUG_ON(!dev); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (!in_pm) 4962306a36Sopenharmony_ci fn = usbnet_write_cmd; 5062306a36Sopenharmony_ci else 5162306a36Sopenharmony_ci fn = usbnet_write_cmd_nopm; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 5462306a36Sopenharmony_ci value, index, data, size); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (unlikely(ret < 0)) 5762306a36Sopenharmony_ci netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n", 5862306a36Sopenharmony_ci index, ret); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return ret; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_civoid asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, 6462306a36Sopenharmony_ci u16 size, void *data) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci usbnet_write_cmd_async(dev, cmd, 6762306a36Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 6862306a36Sopenharmony_ci value, index, data, size); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int asix_set_sw_mii(struct usbnet *dev, int in_pm) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci int ret; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL, in_pm); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (ret < 0) 7862306a36Sopenharmony_ci netdev_err(dev->net, "Failed to enable software MII access\n"); 7962306a36Sopenharmony_ci return ret; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int asix_set_hw_mii(struct usbnet *dev, int in_pm) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci int ret; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL, in_pm); 8762306a36Sopenharmony_ci if (ret < 0) 8862306a36Sopenharmony_ci netdev_err(dev->net, "Failed to enable hardware MII access\n"); 8962306a36Sopenharmony_ci return ret; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int asix_check_host_enable(struct usbnet *dev, int in_pm) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci int i, ret; 9562306a36Sopenharmony_ci u8 smsr; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci for (i = 0; i < AX_HOST_EN_RETRIES; ++i) { 9862306a36Sopenharmony_ci ret = asix_set_sw_mii(dev, in_pm); 9962306a36Sopenharmony_ci if (ret == -ENODEV || ret == -ETIMEDOUT) 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci usleep_range(1000, 1100); 10262306a36Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 10362306a36Sopenharmony_ci 0, 0, 1, &smsr, in_pm); 10462306a36Sopenharmony_ci if (ret == -ENODEV) 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci else if (ret < 0) 10762306a36Sopenharmony_ci continue; 10862306a36Sopenharmony_ci else if (smsr & AX_HOST_EN) 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return i >= AX_HOST_EN_RETRIES ? -ETIMEDOUT : ret; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void reset_asix_rx_fixup_info(struct asix_rx_fixup_info *rx) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci /* Reset the variables that have a lifetime outside of 11862306a36Sopenharmony_ci * asix_rx_fixup_internal() so that future processing starts from a 11962306a36Sopenharmony_ci * known set of initial conditions. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (rx->ax_skb) { 12362306a36Sopenharmony_ci /* Discard any incomplete Ethernet frame in the netdev buffer */ 12462306a36Sopenharmony_ci kfree_skb(rx->ax_skb); 12562306a36Sopenharmony_ci rx->ax_skb = NULL; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Assume the Data header 32-bit word is at the start of the current 12962306a36Sopenharmony_ci * or next URB socket buffer so reset all the state variables. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci rx->remaining = 0; 13262306a36Sopenharmony_ci rx->split_head = false; 13362306a36Sopenharmony_ci rx->header = 0; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciint asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, 13762306a36Sopenharmony_ci struct asix_rx_fixup_info *rx) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci int offset = 0; 14062306a36Sopenharmony_ci u16 size; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* When an Ethernet frame spans multiple URB socket buffers, 14362306a36Sopenharmony_ci * do a sanity test for the Data header synchronisation. 14462306a36Sopenharmony_ci * Attempt to detect the situation of the previous socket buffer having 14562306a36Sopenharmony_ci * been truncated or a socket buffer was missing. These situations 14662306a36Sopenharmony_ci * cause a discontinuity in the data stream and therefore need to avoid 14762306a36Sopenharmony_ci * appending bad data to the end of the current netdev socket buffer. 14862306a36Sopenharmony_ci * Also avoid unnecessarily discarding a good current netdev socket 14962306a36Sopenharmony_ci * buffer. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) { 15262306a36Sopenharmony_ci offset = ((rx->remaining + 1) & 0xfffe); 15362306a36Sopenharmony_ci rx->header = get_unaligned_le32(skb->data + offset); 15462306a36Sopenharmony_ci offset = 0; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci size = (u16)(rx->header & 0x7ff); 15762306a36Sopenharmony_ci if (size != ((~rx->header >> 16) & 0x7ff)) { 15862306a36Sopenharmony_ci netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n", 15962306a36Sopenharmony_ci rx->remaining); 16062306a36Sopenharmony_ci reset_asix_rx_fixup_info(rx); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci while (offset + sizeof(u16) <= skb->len) { 16562306a36Sopenharmony_ci u16 copy_length; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!rx->remaining) { 16862306a36Sopenharmony_ci if (skb->len - offset == sizeof(u16)) { 16962306a36Sopenharmony_ci rx->header = get_unaligned_le16( 17062306a36Sopenharmony_ci skb->data + offset); 17162306a36Sopenharmony_ci rx->split_head = true; 17262306a36Sopenharmony_ci offset += sizeof(u16); 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (rx->split_head == true) { 17762306a36Sopenharmony_ci rx->header |= (get_unaligned_le16( 17862306a36Sopenharmony_ci skb->data + offset) << 16); 17962306a36Sopenharmony_ci rx->split_head = false; 18062306a36Sopenharmony_ci offset += sizeof(u16); 18162306a36Sopenharmony_ci } else { 18262306a36Sopenharmony_ci rx->header = get_unaligned_le32(skb->data + 18362306a36Sopenharmony_ci offset); 18462306a36Sopenharmony_ci offset += sizeof(u32); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* take frame length from Data header 32-bit word */ 18862306a36Sopenharmony_ci size = (u16)(rx->header & 0x7ff); 18962306a36Sopenharmony_ci if (size != ((~rx->header >> 16) & 0x7ff)) { 19062306a36Sopenharmony_ci netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", 19162306a36Sopenharmony_ci rx->header, offset); 19262306a36Sopenharmony_ci reset_asix_rx_fixup_info(rx); 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { 19662306a36Sopenharmony_ci netdev_dbg(dev->net, "asix_rx_fixup() Bad RX Length %d\n", 19762306a36Sopenharmony_ci size); 19862306a36Sopenharmony_ci reset_asix_rx_fixup_info(rx); 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Sometimes may fail to get a netdev socket buffer but 20362306a36Sopenharmony_ci * continue to process the URB socket buffer so that 20462306a36Sopenharmony_ci * synchronisation of the Ethernet frame Data header 20562306a36Sopenharmony_ci * word is maintained. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, size); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci rx->remaining = size; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (rx->remaining > skb->len - offset) { 21362306a36Sopenharmony_ci copy_length = skb->len - offset; 21462306a36Sopenharmony_ci rx->remaining -= copy_length; 21562306a36Sopenharmony_ci } else { 21662306a36Sopenharmony_ci copy_length = rx->remaining; 21762306a36Sopenharmony_ci rx->remaining = 0; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (rx->ax_skb) { 22162306a36Sopenharmony_ci skb_put_data(rx->ax_skb, skb->data + offset, 22262306a36Sopenharmony_ci copy_length); 22362306a36Sopenharmony_ci if (!rx->remaining) { 22462306a36Sopenharmony_ci usbnet_skb_return(dev, rx->ax_skb); 22562306a36Sopenharmony_ci rx->ax_skb = NULL; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci offset += (copy_length + 1) & 0xfffe; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (skb->len != offset) { 23362306a36Sopenharmony_ci netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n", 23462306a36Sopenharmony_ci skb->len, offset); 23562306a36Sopenharmony_ci reset_asix_rx_fixup_info(rx); 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 1; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciint asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct asix_common_private *dp = dev->driver_priv; 24562306a36Sopenharmony_ci struct asix_rx_fixup_info *rx = &dp->rx_fixup_info; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return asix_rx_fixup_internal(dev, skb, rx); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_civoid asix_rx_fixup_common_free(struct asix_common_private *dp) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct asix_rx_fixup_info *rx; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (!dp) 25562306a36Sopenharmony_ci return; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci rx = &dp->rx_fixup_info; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (rx->ax_skb) { 26062306a36Sopenharmony_ci kfree_skb(rx->ax_skb); 26162306a36Sopenharmony_ci rx->ax_skb = NULL; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistruct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 26662306a36Sopenharmony_ci gfp_t flags) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci int padlen; 26962306a36Sopenharmony_ci int headroom = skb_headroom(skb); 27062306a36Sopenharmony_ci int tailroom = skb_tailroom(skb); 27162306a36Sopenharmony_ci u32 packet_len; 27262306a36Sopenharmony_ci u32 padbytes = 0xffff0000; 27362306a36Sopenharmony_ci void *ptr; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* We need to push 4 bytes in front of frame (packet_len) 27862306a36Sopenharmony_ci * and maybe add 4 bytes after the end (if padlen is 4) 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * Avoid skb_copy_expand() expensive call, using following rules : 28162306a36Sopenharmony_ci * - We are allowed to push 4 bytes in headroom if skb_header_cloned() 28262306a36Sopenharmony_ci * is false (and if we have 4 bytes of headroom) 28362306a36Sopenharmony_ci * - We are allowed to put 4 bytes at tail if skb_cloned() 28462306a36Sopenharmony_ci * is false (and if we have 4 bytes of tailroom) 28562306a36Sopenharmony_ci * 28662306a36Sopenharmony_ci * TCP packets for example are cloned, but __skb_header_release() 28762306a36Sopenharmony_ci * was called in tcp stack, allowing us to use headroom for our needs. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci if (!skb_header_cloned(skb) && 29062306a36Sopenharmony_ci !(padlen && skb_cloned(skb)) && 29162306a36Sopenharmony_ci headroom + tailroom >= 4 + padlen) { 29262306a36Sopenharmony_ci /* following should not happen, but better be safe */ 29362306a36Sopenharmony_ci if (headroom < 4 || 29462306a36Sopenharmony_ci tailroom < padlen) { 29562306a36Sopenharmony_ci skb->data = memmove(skb->head + 4, skb->data, skb->len); 29662306a36Sopenharmony_ci skb_set_tail_pointer(skb, skb->len); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci } else { 29962306a36Sopenharmony_ci struct sk_buff *skb2; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci skb2 = skb_copy_expand(skb, 4, padlen, flags); 30262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 30362306a36Sopenharmony_ci skb = skb2; 30462306a36Sopenharmony_ci if (!skb) 30562306a36Sopenharmony_ci return NULL; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci packet_len = ((skb->len ^ 0x0000ffff) << 16) + skb->len; 30962306a36Sopenharmony_ci ptr = skb_push(skb, 4); 31062306a36Sopenharmony_ci put_unaligned_le32(packet_len, ptr); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (padlen) { 31362306a36Sopenharmony_ci put_unaligned_le32(padbytes, skb_tail_pointer(skb)); 31462306a36Sopenharmony_ci skb_put(skb, sizeof(padbytes)); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci usbnet_set_skb_tx_stats(skb, 1, 0); 31862306a36Sopenharmony_ci return skb; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciint asix_read_phy_addr(struct usbnet *dev, bool internal) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int ret, offset; 32462306a36Sopenharmony_ci u8 buf[2]; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf, 0); 32762306a36Sopenharmony_ci if (ret < 0) 32862306a36Sopenharmony_ci goto error; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (ret < 2) { 33162306a36Sopenharmony_ci ret = -EIO; 33262306a36Sopenharmony_ci goto error; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci offset = (internal ? 1 : 0); 33662306a36Sopenharmony_ci ret = buf[offset]; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci netdev_dbg(dev->net, "%s PHY address 0x%x\n", 33962306a36Sopenharmony_ci internal ? "internal" : "external", ret); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return ret; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cierror: 34462306a36Sopenharmony_ci netdev_err(dev->net, "Error reading PHY_ID register: %02x\n", ret); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ciint asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci int ret; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL, in_pm); 35462306a36Sopenharmony_ci if (ret < 0) 35562306a36Sopenharmony_ci netdev_err(dev->net, "Failed to send software reset: %02x\n", ret); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return ret; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ciu16 asix_read_rx_ctl(struct usbnet *dev, int in_pm) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci __le16 v; 36362306a36Sopenharmony_ci int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v, in_pm); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (ret < 0) { 36662306a36Sopenharmony_ci netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret); 36762306a36Sopenharmony_ci goto out; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci ret = le16_to_cpu(v); 37062306a36Sopenharmony_ciout: 37162306a36Sopenharmony_ci return ret; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ciint asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci int ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode); 37962306a36Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL, in_pm); 38062306a36Sopenharmony_ci if (ret < 0) 38162306a36Sopenharmony_ci netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n", 38262306a36Sopenharmony_ci mode, ret); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return ret; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ciu16 asix_read_medium_status(struct usbnet *dev, int in_pm) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci __le16 v; 39062306a36Sopenharmony_ci int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, 39162306a36Sopenharmony_ci 0, 0, 2, &v, in_pm); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (ret < 0) { 39462306a36Sopenharmony_ci netdev_err(dev->net, "Error reading Medium Status register: %02x\n", 39562306a36Sopenharmony_ci ret); 39662306a36Sopenharmony_ci return ret; /* TODO: callers not checking for error ret */ 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return le16_to_cpu(v); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ciint asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci int ret; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode); 40862306a36Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, 40962306a36Sopenharmony_ci mode, 0, 0, NULL, in_pm); 41062306a36Sopenharmony_ci if (ret < 0) 41162306a36Sopenharmony_ci netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n", 41262306a36Sopenharmony_ci mode, ret); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return ret; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* set MAC link settings according to information from phylib */ 41862306a36Sopenharmony_civoid asix_adjust_link(struct net_device *netdev) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct phy_device *phydev = netdev->phydev; 42162306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 42262306a36Sopenharmony_ci u16 mode = 0; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (phydev->link) { 42562306a36Sopenharmony_ci mode = AX88772_MEDIUM_DEFAULT; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (phydev->duplex == DUPLEX_HALF) 42862306a36Sopenharmony_ci mode &= ~AX_MEDIUM_FD; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (phydev->speed != SPEED_100) 43162306a36Sopenharmony_ci mode &= ~AX_MEDIUM_PS; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci asix_write_medium_mode(dev, mode, 0); 43562306a36Sopenharmony_ci phy_print_status(phydev); 43662306a36Sopenharmony_ci usbnet_link_change(dev, phydev->link, 0); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ciint asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci int ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value); 44462306a36Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL, in_pm); 44562306a36Sopenharmony_ci if (ret < 0) 44662306a36Sopenharmony_ci netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n", 44762306a36Sopenharmony_ci value, ret); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (sleep) 45062306a36Sopenharmony_ci msleep(sleep); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/* 45662306a36Sopenharmony_ci * AX88772 & AX88178 have a 16-bit RX_CTL value 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_civoid asix_set_multicast(struct net_device *net) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 46162306a36Sopenharmony_ci struct asix_data *data = (struct asix_data *)&dev->data; 46262306a36Sopenharmony_ci u16 rx_ctl = AX_DEFAULT_RX_CTL; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (net->flags & IFF_PROMISC) { 46562306a36Sopenharmony_ci rx_ctl |= AX_RX_CTL_PRO; 46662306a36Sopenharmony_ci } else if (net->flags & IFF_ALLMULTI || 46762306a36Sopenharmony_ci netdev_mc_count(net) > AX_MAX_MCAST) { 46862306a36Sopenharmony_ci rx_ctl |= AX_RX_CTL_AMALL; 46962306a36Sopenharmony_ci } else if (netdev_mc_empty(net)) { 47062306a36Sopenharmony_ci /* just broadcast and directed */ 47162306a36Sopenharmony_ci } else { 47262306a36Sopenharmony_ci /* We use the 20 byte dev->data 47362306a36Sopenharmony_ci * for our 8 byte filter buffer 47462306a36Sopenharmony_ci * to avoid allocating memory that 47562306a36Sopenharmony_ci * is tricky to free later */ 47662306a36Sopenharmony_ci struct netdev_hw_addr *ha; 47762306a36Sopenharmony_ci u32 crc_bits; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* Build the multicast hash filter. */ 48262306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, net) { 48362306a36Sopenharmony_ci crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; 48462306a36Sopenharmony_ci data->multi_filter[crc_bits >> 3] |= 48562306a36Sopenharmony_ci 1 << (crc_bits & 7); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0, 48962306a36Sopenharmony_ci AX_MCAST_FILTER_SIZE, data->multi_filter); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci rx_ctl |= AX_RX_CTL_AM; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic int __asix_mdio_read(struct net_device *netdev, int phy_id, int loc, 49862306a36Sopenharmony_ci bool in_pm) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 50162306a36Sopenharmony_ci __le16 res; 50262306a36Sopenharmony_ci int ret; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci mutex_lock(&dev->phy_mutex); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = asix_check_host_enable(dev, in_pm); 50762306a36Sopenharmony_ci if (ret == -ENODEV || ret == -ETIMEDOUT) { 50862306a36Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 50962306a36Sopenharmony_ci return ret; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, 51362306a36Sopenharmony_ci &res, in_pm); 51462306a36Sopenharmony_ci if (ret < 0) 51562306a36Sopenharmony_ci goto out; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ret = asix_set_hw_mii(dev, in_pm); 51862306a36Sopenharmony_ciout: 51962306a36Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", 52262306a36Sopenharmony_ci phy_id, loc, le16_to_cpu(res)); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return ret < 0 ? ret : le16_to_cpu(res); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ciint asix_mdio_read(struct net_device *netdev, int phy_id, int loc) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci return __asix_mdio_read(netdev, phy_id, loc, false); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int __asix_mdio_write(struct net_device *netdev, int phy_id, int loc, 53362306a36Sopenharmony_ci int val, bool in_pm) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 53662306a36Sopenharmony_ci __le16 res = cpu_to_le16(val); 53762306a36Sopenharmony_ci int ret; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", 54062306a36Sopenharmony_ci phy_id, loc, val); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci mutex_lock(&dev->phy_mutex); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = asix_check_host_enable(dev, in_pm); 54562306a36Sopenharmony_ci if (ret == -ENODEV) 54662306a36Sopenharmony_ci goto out; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, 54962306a36Sopenharmony_ci &res, in_pm); 55062306a36Sopenharmony_ci if (ret < 0) 55162306a36Sopenharmony_ci goto out; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci ret = asix_set_hw_mii(dev, in_pm); 55462306a36Sopenharmony_ciout: 55562306a36Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return ret < 0 ? ret : 0; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_civoid asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci __asix_mdio_write(netdev, phy_id, loc, val, false); 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/* MDIO read and write wrappers for phylib */ 56662306a36Sopenharmony_ciint asix_mdio_bus_read(struct mii_bus *bus, int phy_id, int regnum) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct usbnet *priv = bus->priv; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return __asix_mdio_read(priv->net, phy_id, regnum, false); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ciint asix_mdio_bus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct usbnet *priv = bus->priv; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return __asix_mdio_write(priv->net, phy_id, regnum, val, false); 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ciint asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci return __asix_mdio_read(netdev, phy_id, loc, true); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_civoid 58662306a36Sopenharmony_ciasix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, int val) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci __asix_mdio_write(netdev, phy_id, loc, val, true); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_civoid asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 59462306a36Sopenharmony_ci u8 opt; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 59762306a36Sopenharmony_ci 0, 0, 1, &opt, 0) < 0) { 59862306a36Sopenharmony_ci wolinfo->supported = 0; 59962306a36Sopenharmony_ci wolinfo->wolopts = 0; 60062306a36Sopenharmony_ci return; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci wolinfo->supported = WAKE_PHY | WAKE_MAGIC; 60362306a36Sopenharmony_ci wolinfo->wolopts = 0; 60462306a36Sopenharmony_ci if (opt & AX_MONITOR_LINK) 60562306a36Sopenharmony_ci wolinfo->wolopts |= WAKE_PHY; 60662306a36Sopenharmony_ci if (opt & AX_MONITOR_MAGIC) 60762306a36Sopenharmony_ci wolinfo->wolopts |= WAKE_MAGIC; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ciint asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 61362306a36Sopenharmony_ci u8 opt = 0; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (wolinfo->wolopts & ~(WAKE_PHY | WAKE_MAGIC)) 61662306a36Sopenharmony_ci return -EINVAL; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (wolinfo->wolopts & WAKE_PHY) 61962306a36Sopenharmony_ci opt |= AX_MONITOR_LINK; 62062306a36Sopenharmony_ci if (wolinfo->wolopts & WAKE_MAGIC) 62162306a36Sopenharmony_ci opt |= AX_MONITOR_MAGIC; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, 62462306a36Sopenharmony_ci opt, 0, 0, NULL, 0) < 0) 62562306a36Sopenharmony_ci return -EINVAL; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ciint asix_get_eeprom_len(struct net_device *net) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci return AX_EEPROM_LEN; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ciint asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, 63662306a36Sopenharmony_ci u8 *data) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 63962306a36Sopenharmony_ci u16 *eeprom_buff; 64062306a36Sopenharmony_ci int first_word, last_word; 64162306a36Sopenharmony_ci int i; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (eeprom->len == 0) 64462306a36Sopenharmony_ci return -EINVAL; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci eeprom->magic = AX_EEPROM_MAGIC; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci first_word = eeprom->offset >> 1; 64962306a36Sopenharmony_ci last_word = (eeprom->offset + eeprom->len - 1) >> 1; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16), 65262306a36Sopenharmony_ci GFP_KERNEL); 65362306a36Sopenharmony_ci if (!eeprom_buff) 65462306a36Sopenharmony_ci return -ENOMEM; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* ax8817x returns 2 bytes from eeprom on read */ 65762306a36Sopenharmony_ci for (i = first_word; i <= last_word; i++) { 65862306a36Sopenharmony_ci if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2, 65962306a36Sopenharmony_ci &eeprom_buff[i - first_word], 0) < 0) { 66062306a36Sopenharmony_ci kfree(eeprom_buff); 66162306a36Sopenharmony_ci return -EIO; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len); 66662306a36Sopenharmony_ci kfree(eeprom_buff); 66762306a36Sopenharmony_ci return 0; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ciint asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, 67162306a36Sopenharmony_ci u8 *data) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 67462306a36Sopenharmony_ci u16 *eeprom_buff; 67562306a36Sopenharmony_ci int first_word, last_word; 67662306a36Sopenharmony_ci int i; 67762306a36Sopenharmony_ci int ret; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n", 68062306a36Sopenharmony_ci eeprom->len, eeprom->offset, eeprom->magic); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (eeprom->len == 0) 68362306a36Sopenharmony_ci return -EINVAL; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (eeprom->magic != AX_EEPROM_MAGIC) 68662306a36Sopenharmony_ci return -EINVAL; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci first_word = eeprom->offset >> 1; 68962306a36Sopenharmony_ci last_word = (eeprom->offset + eeprom->len - 1) >> 1; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16), 69262306a36Sopenharmony_ci GFP_KERNEL); 69362306a36Sopenharmony_ci if (!eeprom_buff) 69462306a36Sopenharmony_ci return -ENOMEM; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* align data to 16 bit boundaries, read the missing data from 69762306a36Sopenharmony_ci the EEPROM */ 69862306a36Sopenharmony_ci if (eeprom->offset & 1) { 69962306a36Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2, 70062306a36Sopenharmony_ci &eeprom_buff[0], 0); 70162306a36Sopenharmony_ci if (ret < 0) { 70262306a36Sopenharmony_ci netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word); 70362306a36Sopenharmony_ci goto free; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if ((eeprom->offset + eeprom->len) & 1) { 70862306a36Sopenharmony_ci ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2, 70962306a36Sopenharmony_ci &eeprom_buff[last_word - first_word], 0); 71062306a36Sopenharmony_ci if (ret < 0) { 71162306a36Sopenharmony_ci netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word); 71262306a36Sopenharmony_ci goto free; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* write data to EEPROM */ 71962306a36Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL, 0); 72062306a36Sopenharmony_ci if (ret < 0) { 72162306a36Sopenharmony_ci netdev_err(net, "Failed to enable EEPROM write\n"); 72262306a36Sopenharmony_ci goto free; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci msleep(20); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci for (i = first_word; i <= last_word; i++) { 72762306a36Sopenharmony_ci netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n", 72862306a36Sopenharmony_ci i, eeprom_buff[i - first_word]); 72962306a36Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i, 73062306a36Sopenharmony_ci eeprom_buff[i - first_word], 0, NULL, 0); 73162306a36Sopenharmony_ci if (ret < 0) { 73262306a36Sopenharmony_ci netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", 73362306a36Sopenharmony_ci i); 73462306a36Sopenharmony_ci goto free; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci msleep(20); 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL, 0); 74062306a36Sopenharmony_ci if (ret < 0) { 74162306a36Sopenharmony_ci netdev_err(net, "Failed to disable EEPROM write\n"); 74262306a36Sopenharmony_ci goto free; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci ret = 0; 74662306a36Sopenharmony_cifree: 74762306a36Sopenharmony_ci kfree(eeprom_buff); 74862306a36Sopenharmony_ci return ret; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_civoid asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci /* Inherit standard device info */ 75462306a36Sopenharmony_ci usbnet_get_drvinfo(net, info); 75562306a36Sopenharmony_ci strscpy(info->driver, DRIVER_NAME, sizeof(info->driver)); 75662306a36Sopenharmony_ci strscpy(info->version, DRIVER_VERSION, sizeof(info->version)); 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ciint asix_set_mac_address(struct net_device *net, void *p) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 76262306a36Sopenharmony_ci struct asix_data *data = (struct asix_data *)&dev->data; 76362306a36Sopenharmony_ci struct sockaddr *addr = p; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (netif_running(net)) 76662306a36Sopenharmony_ci return -EBUSY; 76762306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 76862306a36Sopenharmony_ci return -EADDRNOTAVAIL; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci eth_hw_addr_set(net, addr->sa_data); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* We use the 20 byte dev->data 77362306a36Sopenharmony_ci * for our 6 byte mac buffer 77462306a36Sopenharmony_ci * to avoid allocating memory that 77562306a36Sopenharmony_ci * is tricky to free later */ 77662306a36Sopenharmony_ci memcpy(data->mac_addr, addr->sa_data, ETH_ALEN); 77762306a36Sopenharmony_ci asix_write_cmd_async(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, 77862306a36Sopenharmony_ci data->mac_addr); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci return 0; 78162306a36Sopenharmony_ci} 782