18c2ecf20Sopenharmony_ci/* CoreChip-sz SR9800 one chip USB 2.0 Ethernet Devices 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * Author : Liu Junliang <liujunliang_ljl@163.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on asix_common.c, asix_devices.c 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License 88c2ecf20Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied.* 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/kmod.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 188c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 198c2ecf20Sopenharmony_ci#include <linux/mii.h> 208c2ecf20Sopenharmony_ci#include <linux/usb.h> 218c2ecf20Sopenharmony_ci#include <linux/crc32.h> 228c2ecf20Sopenharmony_ci#include <linux/usb/usbnet.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "sr9800.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int sr_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, 298c2ecf20Sopenharmony_ci u16 size, void *data) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci int err; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci err = usbnet_read_cmd(dev, cmd, SR_REQ_RD_REG, value, index, 348c2ecf20Sopenharmony_ci data, size); 358c2ecf20Sopenharmony_ci if ((err != size) && (err >= 0)) 368c2ecf20Sopenharmony_ci err = -EINVAL; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return err; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int sr_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, 428c2ecf20Sopenharmony_ci u16 size, void *data) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci int err; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci err = usbnet_write_cmd(dev, cmd, SR_REQ_WR_REG, value, index, 478c2ecf20Sopenharmony_ci data, size); 488c2ecf20Sopenharmony_ci if ((err != size) && (err >= 0)) 498c2ecf20Sopenharmony_ci err = -EINVAL; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return err; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void 558c2ecf20Sopenharmony_cisr_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, 568c2ecf20Sopenharmony_ci u16 size, void *data) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci usbnet_write_cmd_async(dev, cmd, SR_REQ_WR_REG, value, index, data, 598c2ecf20Sopenharmony_ci size); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int sr_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci int offset = 0; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* This check is no longer done by usbnet */ 678c2ecf20Sopenharmony_ci if (skb->len < dev->net->hard_header_len) 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci while (offset + sizeof(u32) < skb->len) { 718c2ecf20Sopenharmony_ci struct sk_buff *sr_skb; 728c2ecf20Sopenharmony_ci u16 size; 738c2ecf20Sopenharmony_ci u32 header = get_unaligned_le32(skb->data + offset); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci offset += sizeof(u32); 768c2ecf20Sopenharmony_ci /* get the packet length */ 778c2ecf20Sopenharmony_ci size = (u16) (header & 0x7ff); 788c2ecf20Sopenharmony_ci if (size != ((~header >> 16) & 0x07ff)) { 798c2ecf20Sopenharmony_ci netdev_err(dev->net, "%s : Bad Header Length\n", 808c2ecf20Sopenharmony_ci __func__); 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if ((size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) || 858c2ecf20Sopenharmony_ci (size + offset > skb->len)) { 868c2ecf20Sopenharmony_ci netdev_err(dev->net, "%s : Bad RX Length %d\n", 878c2ecf20Sopenharmony_ci __func__, size); 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci sr_skb = netdev_alloc_skb_ip_align(dev->net, size); 918c2ecf20Sopenharmony_ci if (!sr_skb) 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci skb_put(sr_skb, size); 958c2ecf20Sopenharmony_ci memcpy(sr_skb->data, skb->data + offset, size); 968c2ecf20Sopenharmony_ci usbnet_skb_return(dev, sr_skb); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci offset += (size + 1) & 0xfffe; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (skb->len != offset) { 1028c2ecf20Sopenharmony_ci netdev_err(dev->net, "%s : Bad SKB Length %d\n", __func__, 1038c2ecf20Sopenharmony_ci skb->len); 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return 1; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic struct sk_buff *sr_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 1118c2ecf20Sopenharmony_ci gfp_t flags) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci int headroom = skb_headroom(skb); 1148c2ecf20Sopenharmony_ci int tailroom = skb_tailroom(skb); 1158c2ecf20Sopenharmony_ci u32 padbytes = 0xffff0000; 1168c2ecf20Sopenharmony_ci u32 packet_len; 1178c2ecf20Sopenharmony_ci int padlen; 1188c2ecf20Sopenharmony_ci void *ptr; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci padlen = ((skb->len + 4) % (dev->maxpacket - 1)) ? 0 : 4; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if ((!skb_cloned(skb)) && ((headroom + tailroom) >= (4 + padlen))) { 1238c2ecf20Sopenharmony_ci if ((headroom < 4) || (tailroom < padlen)) { 1248c2ecf20Sopenharmony_ci skb->data = memmove(skb->head + 4, skb->data, 1258c2ecf20Sopenharmony_ci skb->len); 1268c2ecf20Sopenharmony_ci skb_set_tail_pointer(skb, skb->len); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci } else { 1298c2ecf20Sopenharmony_ci struct sk_buff *skb2; 1308c2ecf20Sopenharmony_ci skb2 = skb_copy_expand(skb, 4, padlen, flags); 1318c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1328c2ecf20Sopenharmony_ci skb = skb2; 1338c2ecf20Sopenharmony_ci if (!skb) 1348c2ecf20Sopenharmony_ci return NULL; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ptr = skb_push(skb, 4); 1388c2ecf20Sopenharmony_ci packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4); 1398c2ecf20Sopenharmony_ci put_unaligned_le32(packet_len, ptr); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (padlen) { 1428c2ecf20Sopenharmony_ci put_unaligned_le32(padbytes, skb_tail_pointer(skb)); 1438c2ecf20Sopenharmony_ci skb_put(skb, sizeof(padbytes)); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci usbnet_set_skb_tx_stats(skb, 1, 0); 1478c2ecf20Sopenharmony_ci return skb; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void sr_status(struct usbnet *dev, struct urb *urb) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct sr9800_int_data *event; 1538c2ecf20Sopenharmony_ci int link; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (urb->actual_length < 8) 1568c2ecf20Sopenharmony_ci return; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci event = urb->transfer_buffer; 1598c2ecf20Sopenharmony_ci link = event->link & 0x01; 1608c2ecf20Sopenharmony_ci if (netif_carrier_ok(dev->net) != link) { 1618c2ecf20Sopenharmony_ci usbnet_link_change(dev, link, 1); 1628c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "Link Status is: %d\n", link); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic inline int sr_set_sw_mii(struct usbnet *dev) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int ret; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); 1738c2ecf20Sopenharmony_ci if (ret < 0) 1748c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to enable software MII access\n"); 1758c2ecf20Sopenharmony_ci return ret; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic inline int sr_set_hw_mii(struct usbnet *dev) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci int ret; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); 1838c2ecf20Sopenharmony_ci if (ret < 0) 1848c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to enable hardware MII access\n"); 1858c2ecf20Sopenharmony_ci return ret; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic inline int sr_get_phy_addr(struct usbnet *dev) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci u8 buf[2]; 1918c2ecf20Sopenharmony_ci int ret; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ret = sr_read_cmd(dev, SR_CMD_READ_PHY_ID, 0, 0, 2, buf); 1948c2ecf20Sopenharmony_ci if (ret < 0) { 1958c2ecf20Sopenharmony_ci netdev_err(dev->net, "%s : Error reading PHYID register:%02x\n", 1968c2ecf20Sopenharmony_ci __func__, ret); 1978c2ecf20Sopenharmony_ci goto out; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%s : returning 0x%04x\n", __func__, 2008c2ecf20Sopenharmony_ci *((__le16 *)buf)); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = buf[1]; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ciout: 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int sr_sw_reset(struct usbnet *dev, u8 flags) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci int ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_SW_RESET, flags, 0, 0, NULL); 2138c2ecf20Sopenharmony_ci if (ret < 0) 2148c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to send software reset:%02x\n", 2158c2ecf20Sopenharmony_ci ret); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return ret; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic u16 sr_read_rx_ctl(struct usbnet *dev) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci __le16 v; 2238c2ecf20Sopenharmony_ci int ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = sr_read_cmd(dev, SR_CMD_READ_RX_CTL, 0, 0, 2, &v); 2268c2ecf20Sopenharmony_ci if (ret < 0) { 2278c2ecf20Sopenharmony_ci netdev_err(dev->net, "Error reading RX_CTL register:%02x\n", 2288c2ecf20Sopenharmony_ci ret); 2298c2ecf20Sopenharmony_ci goto out; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ret = le16_to_cpu(v); 2338c2ecf20Sopenharmony_ciout: 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int sr_write_rx_ctl(struct usbnet *dev, u16 mode) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci int ret; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%s : mode = 0x%04x\n", __func__, mode); 2428c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); 2438c2ecf20Sopenharmony_ci if (ret < 0) 2448c2ecf20Sopenharmony_ci netdev_err(dev->net, 2458c2ecf20Sopenharmony_ci "Failed to write RX_CTL mode to 0x%04x:%02x\n", 2468c2ecf20Sopenharmony_ci mode, ret); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return ret; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic u16 sr_read_medium_status(struct usbnet *dev) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci __le16 v; 2548c2ecf20Sopenharmony_ci int ret; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ret = sr_read_cmd(dev, SR_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v); 2578c2ecf20Sopenharmony_ci if (ret < 0) { 2588c2ecf20Sopenharmony_ci netdev_err(dev->net, 2598c2ecf20Sopenharmony_ci "Error reading Medium Status register:%02x\n", ret); 2608c2ecf20Sopenharmony_ci return ret; /* TODO: callers not checking for error ret */ 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return le16_to_cpu(v); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int sr_write_medium_mode(struct usbnet *dev, u16 mode) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci int ret; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%s : mode = 0x%04x\n", __func__, mode); 2718c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); 2728c2ecf20Sopenharmony_ci if (ret < 0) 2738c2ecf20Sopenharmony_ci netdev_err(dev->net, 2748c2ecf20Sopenharmony_ci "Failed to write Medium Mode mode to 0x%04x:%02x\n", 2758c2ecf20Sopenharmony_ci mode, ret); 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int sr_write_gpio(struct usbnet *dev, u16 value, int sleep) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci int ret; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%s : value = 0x%04x\n", __func__, value); 2848c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_WRITE_GPIOS, value, 0, 0, NULL); 2858c2ecf20Sopenharmony_ci if (ret < 0) 2868c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to write GPIO value 0x%04x:%02x\n", 2878c2ecf20Sopenharmony_ci value, ret); 2888c2ecf20Sopenharmony_ci if (sleep) 2898c2ecf20Sopenharmony_ci msleep(sleep); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return ret; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* SR9800 have a 16-bit RX_CTL value */ 2958c2ecf20Sopenharmony_cistatic void sr_set_multicast(struct net_device *net) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 2988c2ecf20Sopenharmony_ci struct sr_data *data = (struct sr_data *)&dev->data; 2998c2ecf20Sopenharmony_ci u16 rx_ctl = SR_DEFAULT_RX_CTL; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (net->flags & IFF_PROMISC) { 3028c2ecf20Sopenharmony_ci rx_ctl |= SR_RX_CTL_PRO; 3038c2ecf20Sopenharmony_ci } else if (net->flags & IFF_ALLMULTI || 3048c2ecf20Sopenharmony_ci netdev_mc_count(net) > SR_MAX_MCAST) { 3058c2ecf20Sopenharmony_ci rx_ctl |= SR_RX_CTL_AMALL; 3068c2ecf20Sopenharmony_ci } else if (netdev_mc_empty(net)) { 3078c2ecf20Sopenharmony_ci /* just broadcast and directed */ 3088c2ecf20Sopenharmony_ci } else { 3098c2ecf20Sopenharmony_ci /* We use the 20 byte dev->data 3108c2ecf20Sopenharmony_ci * for our 8 byte filter buffer 3118c2ecf20Sopenharmony_ci * to avoid allocating memory that 3128c2ecf20Sopenharmony_ci * is tricky to free later 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 3158c2ecf20Sopenharmony_ci u32 crc_bits; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci memset(data->multi_filter, 0, SR_MCAST_FILTER_SIZE); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Build the multicast hash filter. */ 3208c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, net) { 3218c2ecf20Sopenharmony_ci crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; 3228c2ecf20Sopenharmony_ci data->multi_filter[crc_bits >> 3] |= 3238c2ecf20Sopenharmony_ci 1 << (crc_bits & 7); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci sr_write_cmd_async(dev, SR_CMD_WRITE_MULTI_FILTER, 0, 0, 3278c2ecf20Sopenharmony_ci SR_MCAST_FILTER_SIZE, data->multi_filter); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci rx_ctl |= SR_RX_CTL_AM; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci sr_write_cmd_async(dev, SR_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int sr_mdio_read(struct net_device *net, int phy_id, int loc) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 3388c2ecf20Sopenharmony_ci __le16 res = 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 3418c2ecf20Sopenharmony_ci sr_set_sw_mii(dev); 3428c2ecf20Sopenharmony_ci sr_read_cmd(dev, SR_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, &res); 3438c2ecf20Sopenharmony_ci sr_set_hw_mii(dev); 3448c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci netdev_dbg(dev->net, 3478c2ecf20Sopenharmony_ci "%s : phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", __func__, 3488c2ecf20Sopenharmony_ci phy_id, loc, le16_to_cpu(res)); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return le16_to_cpu(res); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void 3548c2ecf20Sopenharmony_cisr_mdio_write(struct net_device *net, int phy_id, int loc, int val) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 3578c2ecf20Sopenharmony_ci __le16 res = cpu_to_le16(val); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci netdev_dbg(dev->net, 3608c2ecf20Sopenharmony_ci "%s : phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", __func__, 3618c2ecf20Sopenharmony_ci phy_id, loc, val); 3628c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 3638c2ecf20Sopenharmony_ci sr_set_sw_mii(dev); 3648c2ecf20Sopenharmony_ci sr_write_cmd(dev, SR_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); 3658c2ecf20Sopenharmony_ci sr_set_hw_mii(dev); 3668c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */ 3708c2ecf20Sopenharmony_cistatic u32 sr_get_phyid(struct usbnet *dev) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci int phy_reg; 3738c2ecf20Sopenharmony_ci u32 phy_id; 3748c2ecf20Sopenharmony_ci int i; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* Poll for the rare case the FW or phy isn't ready yet. */ 3778c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 3788c2ecf20Sopenharmony_ci phy_reg = sr_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1); 3798c2ecf20Sopenharmony_ci if (phy_reg != 0 && phy_reg != 0xFFFF) 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci mdelay(1); 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (phy_reg <= 0 || phy_reg == 0xFFFF) 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci phy_id = (phy_reg & 0xffff) << 16; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci phy_reg = sr_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2); 3908c2ecf20Sopenharmony_ci if (phy_reg < 0) 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci phy_id |= (phy_reg & 0xffff); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return phy_id; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void 3998c2ecf20Sopenharmony_cisr_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 4028c2ecf20Sopenharmony_ci u8 opt; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (sr_read_cmd(dev, SR_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) { 4058c2ecf20Sopenharmony_ci wolinfo->supported = 0; 4068c2ecf20Sopenharmony_ci wolinfo->wolopts = 0; 4078c2ecf20Sopenharmony_ci return; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci wolinfo->supported = WAKE_PHY | WAKE_MAGIC; 4108c2ecf20Sopenharmony_ci wolinfo->wolopts = 0; 4118c2ecf20Sopenharmony_ci if (opt & SR_MONITOR_LINK) 4128c2ecf20Sopenharmony_ci wolinfo->wolopts |= WAKE_PHY; 4138c2ecf20Sopenharmony_ci if (opt & SR_MONITOR_MAGIC) 4148c2ecf20Sopenharmony_ci wolinfo->wolopts |= WAKE_MAGIC; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int 4188c2ecf20Sopenharmony_cisr_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 4218c2ecf20Sopenharmony_ci u8 opt = 0; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (wolinfo->wolopts & ~(WAKE_PHY | WAKE_MAGIC)) 4248c2ecf20Sopenharmony_ci return -EINVAL; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (wolinfo->wolopts & WAKE_PHY) 4278c2ecf20Sopenharmony_ci opt |= SR_MONITOR_LINK; 4288c2ecf20Sopenharmony_ci if (wolinfo->wolopts & WAKE_MAGIC) 4298c2ecf20Sopenharmony_ci opt |= SR_MONITOR_MAGIC; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (sr_write_cmd(dev, SR_CMD_WRITE_MONITOR_MODE, 4328c2ecf20Sopenharmony_ci opt, 0, 0, NULL) < 0) 4338c2ecf20Sopenharmony_ci return -EINVAL; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int sr_get_eeprom_len(struct net_device *net) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 4418c2ecf20Sopenharmony_ci struct sr_data *data = (struct sr_data *)&dev->data; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return data->eeprom_len; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int sr_get_eeprom(struct net_device *net, 4478c2ecf20Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 4508c2ecf20Sopenharmony_ci __le16 *ebuf = (__le16 *)data; 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci int i; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* Crude hack to ensure that we don't overwrite memory 4558c2ecf20Sopenharmony_ci * if an odd length is supplied 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ci if (eeprom->len % 2) 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci eeprom->magic = SR_EEPROM_MAGIC; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* sr9800 returns 2 bytes from eeprom on read */ 4638c2ecf20Sopenharmony_ci for (i = 0; i < eeprom->len / 2; i++) { 4648c2ecf20Sopenharmony_ci ret = sr_read_cmd(dev, SR_CMD_READ_EEPROM, eeprom->offset + i, 4658c2ecf20Sopenharmony_ci 0, 2, &ebuf[i]); 4668c2ecf20Sopenharmony_ci if (ret < 0) 4678c2ecf20Sopenharmony_ci return -EINVAL; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void sr_get_drvinfo(struct net_device *net, 4738c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci /* Inherit standard device info */ 4768c2ecf20Sopenharmony_ci usbnet_get_drvinfo(net, info); 4778c2ecf20Sopenharmony_ci strncpy(info->driver, DRIVER_NAME, sizeof(info->driver)); 4788c2ecf20Sopenharmony_ci strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic u32 sr_get_link(struct net_device *net) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return mii_link_ok(&dev->mii); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic int sr_ioctl(struct net_device *net, struct ifreq *rq, int cmd) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int sr_set_mac_address(struct net_device *net, void *p) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 4988c2ecf20Sopenharmony_ci struct sr_data *data = (struct sr_data *)&dev->data; 4998c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (netif_running(net)) 5028c2ecf20Sopenharmony_ci return -EBUSY; 5038c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 5048c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* We use the 20 byte dev->data 5098c2ecf20Sopenharmony_ci * for our 6 byte mac buffer 5108c2ecf20Sopenharmony_ci * to avoid allocating memory that 5118c2ecf20Sopenharmony_ci * is tricky to free later 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_ci memcpy(data->mac_addr, addr->sa_data, ETH_ALEN); 5148c2ecf20Sopenharmony_ci sr_write_cmd_async(dev, SR_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, 5158c2ecf20Sopenharmony_ci data->mac_addr); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic const struct ethtool_ops sr9800_ethtool_ops = { 5218c2ecf20Sopenharmony_ci .get_drvinfo = sr_get_drvinfo, 5228c2ecf20Sopenharmony_ci .get_link = sr_get_link, 5238c2ecf20Sopenharmony_ci .get_msglevel = usbnet_get_msglevel, 5248c2ecf20Sopenharmony_ci .set_msglevel = usbnet_set_msglevel, 5258c2ecf20Sopenharmony_ci .get_wol = sr_get_wol, 5268c2ecf20Sopenharmony_ci .set_wol = sr_set_wol, 5278c2ecf20Sopenharmony_ci .get_eeprom_len = sr_get_eeprom_len, 5288c2ecf20Sopenharmony_ci .get_eeprom = sr_get_eeprom, 5298c2ecf20Sopenharmony_ci .nway_reset = usbnet_nway_reset, 5308c2ecf20Sopenharmony_ci .get_link_ksettings = usbnet_get_link_ksettings, 5318c2ecf20Sopenharmony_ci .set_link_ksettings = usbnet_set_link_ksettings, 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int sr9800_link_reset(struct usbnet *dev) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; 5378c2ecf20Sopenharmony_ci u16 mode; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci mii_check_media(&dev->mii, 1, 1); 5408c2ecf20Sopenharmony_ci mii_ethtool_gset(&dev->mii, &ecmd); 5418c2ecf20Sopenharmony_ci mode = SR9800_MEDIUM_DEFAULT; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (ethtool_cmd_speed(&ecmd) != SPEED_100) 5448c2ecf20Sopenharmony_ci mode &= ~SR_MEDIUM_PS; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (ecmd.duplex != DUPLEX_FULL) 5478c2ecf20Sopenharmony_ci mode &= ~SR_MEDIUM_FD; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%s : speed: %u duplex: %d mode: 0x%04x\n", 5508c2ecf20Sopenharmony_ci __func__, ethtool_cmd_speed(&ecmd), ecmd.duplex, mode); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci sr_write_medium_mode(dev, mode); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic int sr9800_set_default_mode(struct usbnet *dev) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci u16 rx_ctl; 5618c2ecf20Sopenharmony_ci int ret; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci sr_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); 5648c2ecf20Sopenharmony_ci sr_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, 5658c2ecf20Sopenharmony_ci ADVERTISE_ALL | ADVERTISE_CSMA); 5668c2ecf20Sopenharmony_ci mii_nway_restart(&dev->mii); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci ret = sr_write_medium_mode(dev, SR9800_MEDIUM_DEFAULT); 5698c2ecf20Sopenharmony_ci if (ret < 0) 5708c2ecf20Sopenharmony_ci goto out; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_WRITE_IPG012, 5738c2ecf20Sopenharmony_ci SR9800_IPG0_DEFAULT | SR9800_IPG1_DEFAULT, 5748c2ecf20Sopenharmony_ci SR9800_IPG2_DEFAULT, 0, NULL); 5758c2ecf20Sopenharmony_ci if (ret < 0) { 5768c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret); 5778c2ecf20Sopenharmony_ci goto out; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* Set RX_CTL to default values with 2k buffer, and enable cactus */ 5818c2ecf20Sopenharmony_ci ret = sr_write_rx_ctl(dev, SR_DEFAULT_RX_CTL); 5828c2ecf20Sopenharmony_ci if (ret < 0) 5838c2ecf20Sopenharmony_ci goto out; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci rx_ctl = sr_read_rx_ctl(dev); 5868c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n", 5878c2ecf20Sopenharmony_ci rx_ctl); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci rx_ctl = sr_read_medium_status(dev); 5908c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "Medium Status:0x%04x after all initializations\n", 5918c2ecf20Sopenharmony_ci rx_ctl); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ciout: 5958c2ecf20Sopenharmony_ci return ret; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int sr9800_reset(struct usbnet *dev) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct sr_data *data = (struct sr_data *)&dev->data; 6018c2ecf20Sopenharmony_ci int ret, embd_phy; 6028c2ecf20Sopenharmony_ci u16 rx_ctl; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci ret = sr_write_gpio(dev, 6058c2ecf20Sopenharmony_ci SR_GPIO_RSE | SR_GPIO_GPO_2 | SR_GPIO_GPO2EN, 5); 6068c2ecf20Sopenharmony_ci if (ret < 0) 6078c2ecf20Sopenharmony_ci goto out; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci embd_phy = ((sr_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); 6128c2ecf20Sopenharmony_ci if (ret < 0) { 6138c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); 6148c2ecf20Sopenharmony_ci goto out; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci ret = sr_sw_reset(dev, SR_SWRESET_IPPD | SR_SWRESET_PRL); 6188c2ecf20Sopenharmony_ci if (ret < 0) 6198c2ecf20Sopenharmony_ci goto out; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci msleep(150); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci ret = sr_sw_reset(dev, SR_SWRESET_CLEAR); 6248c2ecf20Sopenharmony_ci if (ret < 0) 6258c2ecf20Sopenharmony_ci goto out; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci msleep(150); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (embd_phy) { 6308c2ecf20Sopenharmony_ci ret = sr_sw_reset(dev, SR_SWRESET_IPRL); 6318c2ecf20Sopenharmony_ci if (ret < 0) 6328c2ecf20Sopenharmony_ci goto out; 6338c2ecf20Sopenharmony_ci } else { 6348c2ecf20Sopenharmony_ci ret = sr_sw_reset(dev, SR_SWRESET_PRTE); 6358c2ecf20Sopenharmony_ci if (ret < 0) 6368c2ecf20Sopenharmony_ci goto out; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci msleep(150); 6408c2ecf20Sopenharmony_ci rx_ctl = sr_read_rx_ctl(dev); 6418c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); 6428c2ecf20Sopenharmony_ci ret = sr_write_rx_ctl(dev, 0x0000); 6438c2ecf20Sopenharmony_ci if (ret < 0) 6448c2ecf20Sopenharmony_ci goto out; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci rx_ctl = sr_read_rx_ctl(dev); 6478c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci ret = sr_sw_reset(dev, SR_SWRESET_PRL); 6508c2ecf20Sopenharmony_ci if (ret < 0) 6518c2ecf20Sopenharmony_ci goto out; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci msleep(150); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci ret = sr_sw_reset(dev, SR_SWRESET_IPRL | SR_SWRESET_PRL); 6568c2ecf20Sopenharmony_ci if (ret < 0) 6578c2ecf20Sopenharmony_ci goto out; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci msleep(150); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci ret = sr9800_set_default_mode(dev); 6628c2ecf20Sopenharmony_ci if (ret < 0) 6638c2ecf20Sopenharmony_ci goto out; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* Rewrite MAC address */ 6668c2ecf20Sopenharmony_ci memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); 6678c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, 6688c2ecf20Sopenharmony_ci data->mac_addr); 6698c2ecf20Sopenharmony_ci if (ret < 0) 6708c2ecf20Sopenharmony_ci goto out; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return 0; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ciout: 6758c2ecf20Sopenharmony_ci return ret; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic const struct net_device_ops sr9800_netdev_ops = { 6798c2ecf20Sopenharmony_ci .ndo_open = usbnet_open, 6808c2ecf20Sopenharmony_ci .ndo_stop = usbnet_stop, 6818c2ecf20Sopenharmony_ci .ndo_start_xmit = usbnet_start_xmit, 6828c2ecf20Sopenharmony_ci .ndo_tx_timeout = usbnet_tx_timeout, 6838c2ecf20Sopenharmony_ci .ndo_change_mtu = usbnet_change_mtu, 6848c2ecf20Sopenharmony_ci .ndo_get_stats64 = usbnet_get_stats64, 6858c2ecf20Sopenharmony_ci .ndo_set_mac_address = sr_set_mac_address, 6868c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 6878c2ecf20Sopenharmony_ci .ndo_do_ioctl = sr_ioctl, 6888c2ecf20Sopenharmony_ci .ndo_set_rx_mode = sr_set_multicast, 6898c2ecf20Sopenharmony_ci}; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic int sr9800_phy_powerup(struct usbnet *dev) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci int ret; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci /* set the embedded Ethernet PHY in power-down state */ 6968c2ecf20Sopenharmony_ci ret = sr_sw_reset(dev, SR_SWRESET_IPPD | SR_SWRESET_IPRL); 6978c2ecf20Sopenharmony_ci if (ret < 0) { 6988c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to power down PHY : %d\n", ret); 6998c2ecf20Sopenharmony_ci return ret; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci msleep(20); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* set the embedded Ethernet PHY in power-up state */ 7048c2ecf20Sopenharmony_ci ret = sr_sw_reset(dev, SR_SWRESET_IPRL); 7058c2ecf20Sopenharmony_ci if (ret < 0) { 7068c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to reset PHY: %d\n", ret); 7078c2ecf20Sopenharmony_ci return ret; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci msleep(600); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* set the embedded Ethernet PHY in reset state */ 7128c2ecf20Sopenharmony_ci ret = sr_sw_reset(dev, SR_SWRESET_CLEAR); 7138c2ecf20Sopenharmony_ci if (ret < 0) { 7148c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to power up PHY: %d\n", ret); 7158c2ecf20Sopenharmony_ci return ret; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci msleep(20); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* set the embedded Ethernet PHY in power-up state */ 7208c2ecf20Sopenharmony_ci ret = sr_sw_reset(dev, SR_SWRESET_IPRL); 7218c2ecf20Sopenharmony_ci if (ret < 0) { 7228c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to reset PHY: %d\n", ret); 7238c2ecf20Sopenharmony_ci return ret; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic int sr9800_bind(struct usbnet *dev, struct usb_interface *intf) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci struct sr_data *data = (struct sr_data *)&dev->data; 7328c2ecf20Sopenharmony_ci u16 led01_mux, led23_mux; 7338c2ecf20Sopenharmony_ci int ret, embd_phy; 7348c2ecf20Sopenharmony_ci u32 phyid; 7358c2ecf20Sopenharmony_ci u16 rx_ctl; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci data->eeprom_len = SR9800_EEPROM_LEN; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci usbnet_get_endpoints(dev, intf); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* LED Setting Rule : 7428c2ecf20Sopenharmony_ci * AABB:CCDD 7438c2ecf20Sopenharmony_ci * AA : MFA0(LED0) 7448c2ecf20Sopenharmony_ci * BB : MFA1(LED1) 7458c2ecf20Sopenharmony_ci * CC : MFA2(LED2), Reserved for SR9800 7468c2ecf20Sopenharmony_ci * DD : MFA3(LED3), Reserved for SR9800 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_ci led01_mux = (SR_LED_MUX_LINK_ACTIVE << 8) | SR_LED_MUX_LINK; 7498c2ecf20Sopenharmony_ci led23_mux = (SR_LED_MUX_LINK_ACTIVE << 8) | SR_LED_MUX_TX_ACTIVE; 7508c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_LED_MUX, led01_mux, led23_mux, 0, NULL); 7518c2ecf20Sopenharmony_ci if (ret < 0) { 7528c2ecf20Sopenharmony_ci netdev_err(dev->net, "set LINK LED failed : %d\n", ret); 7538c2ecf20Sopenharmony_ci goto out; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Get the MAC address */ 7578c2ecf20Sopenharmony_ci ret = sr_read_cmd(dev, SR_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, 7588c2ecf20Sopenharmony_ci dev->net->dev_addr); 7598c2ecf20Sopenharmony_ci if (ret < 0) { 7608c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret); 7618c2ecf20Sopenharmony_ci return ret; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "mac addr : %pM\n", dev->net->dev_addr); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* Initialize MII structure */ 7668c2ecf20Sopenharmony_ci dev->mii.dev = dev->net; 7678c2ecf20Sopenharmony_ci dev->mii.mdio_read = sr_mdio_read; 7688c2ecf20Sopenharmony_ci dev->mii.mdio_write = sr_mdio_write; 7698c2ecf20Sopenharmony_ci dev->mii.phy_id_mask = 0x1f; 7708c2ecf20Sopenharmony_ci dev->mii.reg_num_mask = 0x1f; 7718c2ecf20Sopenharmony_ci dev->mii.phy_id = sr_get_phy_addr(dev); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci dev->net->netdev_ops = &sr9800_netdev_ops; 7748c2ecf20Sopenharmony_ci dev->net->ethtool_ops = &sr9800_ethtool_ops; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); 7778c2ecf20Sopenharmony_ci /* Reset the PHY to normal operation mode */ 7788c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); 7798c2ecf20Sopenharmony_ci if (ret < 0) { 7808c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); 7818c2ecf20Sopenharmony_ci return ret; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* Init PHY routine */ 7858c2ecf20Sopenharmony_ci ret = sr9800_phy_powerup(dev); 7868c2ecf20Sopenharmony_ci if (ret < 0) 7878c2ecf20Sopenharmony_ci goto out; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci rx_ctl = sr_read_rx_ctl(dev); 7908c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); 7918c2ecf20Sopenharmony_ci ret = sr_write_rx_ctl(dev, 0x0000); 7928c2ecf20Sopenharmony_ci if (ret < 0) 7938c2ecf20Sopenharmony_ci goto out; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci rx_ctl = sr_read_rx_ctl(dev); 7968c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* Read PHYID register *AFTER* the PHY was reset properly */ 7998c2ecf20Sopenharmony_ci phyid = sr_get_phyid(dev); 8008c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* medium mode setting */ 8038c2ecf20Sopenharmony_ci ret = sr9800_set_default_mode(dev); 8048c2ecf20Sopenharmony_ci if (ret < 0) 8058c2ecf20Sopenharmony_ci goto out; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (dev->udev->speed == USB_SPEED_HIGH) { 8088c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_BULKIN_SIZE, 8098c2ecf20Sopenharmony_ci SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_4K].byte_cnt, 8108c2ecf20Sopenharmony_ci SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_4K].threshold, 8118c2ecf20Sopenharmony_ci 0, NULL); 8128c2ecf20Sopenharmony_ci if (ret < 0) { 8138c2ecf20Sopenharmony_ci netdev_err(dev->net, "Reset RX_CTL failed: %d\n", ret); 8148c2ecf20Sopenharmony_ci goto out; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci dev->rx_urb_size = 8178c2ecf20Sopenharmony_ci SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_4K].size; 8188c2ecf20Sopenharmony_ci } else { 8198c2ecf20Sopenharmony_ci ret = sr_write_cmd(dev, SR_CMD_BULKIN_SIZE, 8208c2ecf20Sopenharmony_ci SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_2K].byte_cnt, 8218c2ecf20Sopenharmony_ci SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_2K].threshold, 8228c2ecf20Sopenharmony_ci 0, NULL); 8238c2ecf20Sopenharmony_ci if (ret < 0) { 8248c2ecf20Sopenharmony_ci netdev_err(dev->net, "Reset RX_CTL failed: %d\n", ret); 8258c2ecf20Sopenharmony_ci goto out; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci dev->rx_urb_size = 8288c2ecf20Sopenharmony_ci SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_2K].size; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%s : setting rx_urb_size with : %zu\n", __func__, 8318c2ecf20Sopenharmony_ci dev->rx_urb_size); 8328c2ecf20Sopenharmony_ci return 0; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ciout: 8358c2ecf20Sopenharmony_ci return ret; 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic const struct driver_info sr9800_driver_info = { 8398c2ecf20Sopenharmony_ci .description = "CoreChip SR9800 USB 2.0 Ethernet", 8408c2ecf20Sopenharmony_ci .bind = sr9800_bind, 8418c2ecf20Sopenharmony_ci .status = sr_status, 8428c2ecf20Sopenharmony_ci .link_reset = sr9800_link_reset, 8438c2ecf20Sopenharmony_ci .reset = sr9800_reset, 8448c2ecf20Sopenharmony_ci .flags = DRIVER_FLAG, 8458c2ecf20Sopenharmony_ci .rx_fixup = sr_rx_fixup, 8468c2ecf20Sopenharmony_ci .tx_fixup = sr_tx_fixup, 8478c2ecf20Sopenharmony_ci}; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic const struct usb_device_id products[] = { 8508c2ecf20Sopenharmony_ci { 8518c2ecf20Sopenharmony_ci USB_DEVICE(0x0fe6, 0x9800), /* SR9800 Device */ 8528c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &sr9800_driver_info, 8538c2ecf20Sopenharmony_ci }, 8548c2ecf20Sopenharmony_ci {}, /* END */ 8558c2ecf20Sopenharmony_ci}; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic struct usb_driver sr_driver = { 8608c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 8618c2ecf20Sopenharmony_ci .id_table = products, 8628c2ecf20Sopenharmony_ci .probe = usbnet_probe, 8638c2ecf20Sopenharmony_ci .suspend = usbnet_suspend, 8648c2ecf20Sopenharmony_ci .resume = usbnet_resume, 8658c2ecf20Sopenharmony_ci .disconnect = usbnet_disconnect, 8668c2ecf20Sopenharmony_ci .supports_autosuspend = 1, 8678c2ecf20Sopenharmony_ci}; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_cimodule_usb_driver(sr_driver); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Liu Junliang <liujunliang_ljl@163.com"); 8728c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 8738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SR9800 USB 2.0 USB2NET Dev : http://www.corechip-sz.com"); 8748c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 875