18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * CoreChip-sz SR9700 one chip USB 1.1 Ethernet Devices 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Author : Liu Junliang <liujunliang_ljl@163.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on dm9601.c 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License 98c2ecf20Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of any 108c2ecf20Sopenharmony_ci * kind, whether express or implied. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/sched.h> 158c2ecf20Sopenharmony_ci#include <linux/stddef.h> 168c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 188c2ecf20Sopenharmony_ci#include <linux/ethtool.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 248c2ecf20Sopenharmony_ci#include "sr9700.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int sr_read(struct usbnet *dev, u8 reg, u16 length, void *data) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci int err; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci err = usbnet_read_cmd(dev, SR_RD_REGS, SR_REQ_RD_REG, 0, reg, data, 318c2ecf20Sopenharmony_ci length); 328c2ecf20Sopenharmony_ci if ((err != length) && (err >= 0)) 338c2ecf20Sopenharmony_ci err = -EINVAL; 348c2ecf20Sopenharmony_ci return err; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int sr_write(struct usbnet *dev, u8 reg, u16 length, void *data) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci int err; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci err = usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, 0, reg, data, 428c2ecf20Sopenharmony_ci length); 438c2ecf20Sopenharmony_ci if ((err >= 0) && (err < length)) 448c2ecf20Sopenharmony_ci err = -EINVAL; 458c2ecf20Sopenharmony_ci return err; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int sr_read_reg(struct usbnet *dev, u8 reg, u8 *value) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci return sr_read(dev, reg, 1, value); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int sr_write_reg(struct usbnet *dev, u8 reg, u8 value) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci return usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, 568c2ecf20Sopenharmony_ci value, reg, NULL, 0); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void sr_write_async(struct usbnet *dev, u8 reg, u16 length, void *data) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, 628c2ecf20Sopenharmony_ci 0, reg, data, length); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void sr_write_reg_async(struct usbnet *dev, u8 reg, u8 value) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, 688c2ecf20Sopenharmony_ci value, reg, NULL, 0); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int wait_phy_eeprom_ready(struct usbnet *dev, int phy) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci int i; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci for (i = 0; i < SR_SHARE_TIMEOUT; i++) { 768c2ecf20Sopenharmony_ci u8 tmp = 0; 778c2ecf20Sopenharmony_ci int ret; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci udelay(1); 808c2ecf20Sopenharmony_ci ret = sr_read_reg(dev, SR_EPCR, &tmp); 818c2ecf20Sopenharmony_ci if (ret < 0) 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* ready */ 858c2ecf20Sopenharmony_ci if (!(tmp & EPCR_ERRE)) 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci netdev_err(dev->net, "%s write timed out!\n", phy ? "phy" : "eeprom"); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return -EIO; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int sr_share_read_word(struct usbnet *dev, int phy, u8 reg, 958c2ecf20Sopenharmony_ci __le16 *value) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int ret; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci sr_write_reg(dev, SR_EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); 1028c2ecf20Sopenharmony_ci sr_write_reg(dev, SR_EPCR, phy ? (EPCR_EPOS | EPCR_ERPRR) : EPCR_ERPRR); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = wait_phy_eeprom_ready(dev, phy); 1058c2ecf20Sopenharmony_ci if (ret < 0) 1068c2ecf20Sopenharmony_ci goto out_unlock; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci sr_write_reg(dev, SR_EPCR, 0x0); 1098c2ecf20Sopenharmony_ci ret = sr_read(dev, SR_EPDR, 2, value); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "read shared %d 0x%02x returned 0x%04x, %d\n", 1128c2ecf20Sopenharmony_ci phy, reg, *value, ret); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ciout_unlock: 1158c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 1168c2ecf20Sopenharmony_ci return ret; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int sr_share_write_word(struct usbnet *dev, int phy, u8 reg, 1208c2ecf20Sopenharmony_ci __le16 value) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int ret; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ret = sr_write(dev, SR_EPDR, 2, &value); 1278c2ecf20Sopenharmony_ci if (ret < 0) 1288c2ecf20Sopenharmony_ci goto out_unlock; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci sr_write_reg(dev, SR_EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); 1318c2ecf20Sopenharmony_ci sr_write_reg(dev, SR_EPCR, phy ? (EPCR_WEP | EPCR_EPOS | EPCR_ERPRW) : 1328c2ecf20Sopenharmony_ci (EPCR_WEP | EPCR_ERPRW)); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = wait_phy_eeprom_ready(dev, phy); 1358c2ecf20Sopenharmony_ci if (ret < 0) 1368c2ecf20Sopenharmony_ci goto out_unlock; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci sr_write_reg(dev, SR_EPCR, 0x0); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciout_unlock: 1418c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int sr_read_eeprom_word(struct usbnet *dev, u8 offset, void *value) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci return sr_share_read_word(dev, 0, offset, value); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int sr9700_get_eeprom_len(struct net_device *netdev) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci return SR_EEPROM_LEN; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int sr9700_get_eeprom(struct net_device *netdev, 1568c2ecf20Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 1598c2ecf20Sopenharmony_ci __le16 *buf = (__le16 *)data; 1608c2ecf20Sopenharmony_ci int ret = 0; 1618c2ecf20Sopenharmony_ci int i; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* access is 16bit */ 1648c2ecf20Sopenharmony_ci if ((eeprom->offset & 0x01) || (eeprom->len & 0x01)) 1658c2ecf20Sopenharmony_ci return -EINVAL; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci for (i = 0; i < eeprom->len / 2; i++) { 1688c2ecf20Sopenharmony_ci ret = sr_read_eeprom_word(dev, eeprom->offset / 2 + i, buf + i); 1698c2ecf20Sopenharmony_ci if (ret < 0) 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int sr_mdio_read(struct net_device *netdev, int phy_id, int loc) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 1798c2ecf20Sopenharmony_ci __le16 res; 1808c2ecf20Sopenharmony_ci int rc = 0; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (phy_id) { 1838c2ecf20Sopenharmony_ci netdev_dbg(netdev, "Only internal phy supported\n"); 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* Access NSR_LINKST bit for link status instead of MII_BMSR */ 1888c2ecf20Sopenharmony_ci if (loc == MII_BMSR) { 1898c2ecf20Sopenharmony_ci u8 value; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci sr_read_reg(dev, SR_NSR, &value); 1928c2ecf20Sopenharmony_ci if (value & NSR_LINKST) 1938c2ecf20Sopenharmony_ci rc = 1; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci sr_share_read_word(dev, 1, loc, &res); 1968c2ecf20Sopenharmony_ci if (rc == 1) 1978c2ecf20Sopenharmony_ci res = le16_to_cpu(res) | BMSR_LSTATUS; 1988c2ecf20Sopenharmony_ci else 1998c2ecf20Sopenharmony_ci res = le16_to_cpu(res) & ~BMSR_LSTATUS; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci netdev_dbg(netdev, "sr_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", 2028c2ecf20Sopenharmony_ci phy_id, loc, res); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return res; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void sr_mdio_write(struct net_device *netdev, int phy_id, int loc, 2088c2ecf20Sopenharmony_ci int val) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 2118c2ecf20Sopenharmony_ci __le16 res = cpu_to_le16(val); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (phy_id) { 2148c2ecf20Sopenharmony_ci netdev_dbg(netdev, "Only internal phy supported\n"); 2158c2ecf20Sopenharmony_ci return; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci netdev_dbg(netdev, "sr_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", 2198c2ecf20Sopenharmony_ci phy_id, loc, val); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci sr_share_write_word(dev, 1, loc, res); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic u32 sr9700_get_link(struct net_device *netdev) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 2278c2ecf20Sopenharmony_ci u8 value = 0; 2288c2ecf20Sopenharmony_ci int rc = 0; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Get the Link Status directly */ 2318c2ecf20Sopenharmony_ci sr_read_reg(dev, SR_NSR, &value); 2328c2ecf20Sopenharmony_ci if (value & NSR_LINKST) 2338c2ecf20Sopenharmony_ci rc = 1; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return rc; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int sr9700_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic const struct ethtool_ops sr9700_ethtool_ops = { 2468c2ecf20Sopenharmony_ci .get_drvinfo = usbnet_get_drvinfo, 2478c2ecf20Sopenharmony_ci .get_link = sr9700_get_link, 2488c2ecf20Sopenharmony_ci .get_msglevel = usbnet_get_msglevel, 2498c2ecf20Sopenharmony_ci .set_msglevel = usbnet_set_msglevel, 2508c2ecf20Sopenharmony_ci .get_eeprom_len = sr9700_get_eeprom_len, 2518c2ecf20Sopenharmony_ci .get_eeprom = sr9700_get_eeprom, 2528c2ecf20Sopenharmony_ci .nway_reset = usbnet_nway_reset, 2538c2ecf20Sopenharmony_ci .get_link_ksettings = usbnet_get_link_ksettings, 2548c2ecf20Sopenharmony_ci .set_link_ksettings = usbnet_set_link_ksettings, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void sr9700_set_multicast(struct net_device *netdev) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 2608c2ecf20Sopenharmony_ci /* We use the 20 byte dev->data for our 8 byte filter buffer 2618c2ecf20Sopenharmony_ci * to avoid allocating memory that is tricky to free later 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci u8 *hashes = (u8 *)&dev->data; 2648c2ecf20Sopenharmony_ci /* rx_ctl setting : enable, disable_long, disable_crc */ 2658c2ecf20Sopenharmony_ci u8 rx_ctl = RCR_RXEN | RCR_DIS_CRC | RCR_DIS_LONG; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci memset(hashes, 0x00, SR_MCAST_SIZE); 2688c2ecf20Sopenharmony_ci /* broadcast address */ 2698c2ecf20Sopenharmony_ci hashes[SR_MCAST_SIZE - 1] |= SR_MCAST_ADDR_FLAG; 2708c2ecf20Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 2718c2ecf20Sopenharmony_ci rx_ctl |= RCR_PRMSC; 2728c2ecf20Sopenharmony_ci } else if (netdev->flags & IFF_ALLMULTI || 2738c2ecf20Sopenharmony_ci netdev_mc_count(netdev) > SR_MCAST_MAX) { 2748c2ecf20Sopenharmony_ci rx_ctl |= RCR_RUNT; 2758c2ecf20Sopenharmony_ci } else if (!netdev_mc_empty(netdev)) { 2768c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 2798c2ecf20Sopenharmony_ci u32 crc = ether_crc(ETH_ALEN, ha->addr) >> 26; 2808c2ecf20Sopenharmony_ci hashes[crc >> 3] |= 1 << (crc & 0x7); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci sr_write_async(dev, SR_MAR, SR_MCAST_SIZE, hashes); 2858c2ecf20Sopenharmony_ci sr_write_reg_async(dev, SR_RCR, rx_ctl); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic int sr9700_set_mac_address(struct net_device *netdev, void *p) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 2918c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) { 2948c2ecf20Sopenharmony_ci netdev_err(netdev, "not setting invalid mac address %pM\n", 2958c2ecf20Sopenharmony_ci addr->sa_data); 2968c2ecf20Sopenharmony_ci return -EINVAL; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); 3008c2ecf20Sopenharmony_ci sr_write_async(dev, SR_PAR, 6, netdev->dev_addr); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic const struct net_device_ops sr9700_netdev_ops = { 3068c2ecf20Sopenharmony_ci .ndo_open = usbnet_open, 3078c2ecf20Sopenharmony_ci .ndo_stop = usbnet_stop, 3088c2ecf20Sopenharmony_ci .ndo_start_xmit = usbnet_start_xmit, 3098c2ecf20Sopenharmony_ci .ndo_tx_timeout = usbnet_tx_timeout, 3108c2ecf20Sopenharmony_ci .ndo_change_mtu = usbnet_change_mtu, 3118c2ecf20Sopenharmony_ci .ndo_get_stats64 = usbnet_get_stats64, 3128c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 3138c2ecf20Sopenharmony_ci .ndo_do_ioctl = sr9700_ioctl, 3148c2ecf20Sopenharmony_ci .ndo_set_rx_mode = sr9700_set_multicast, 3158c2ecf20Sopenharmony_ci .ndo_set_mac_address = sr9700_set_mac_address, 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int sr9700_bind(struct usbnet *dev, struct usb_interface *intf) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct net_device *netdev; 3218c2ecf20Sopenharmony_ci struct mii_if_info *mii; 3228c2ecf20Sopenharmony_ci int ret; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci ret = usbnet_get_endpoints(dev, intf); 3258c2ecf20Sopenharmony_ci if (ret) 3268c2ecf20Sopenharmony_ci goto out; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci netdev = dev->net; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci netdev->netdev_ops = &sr9700_netdev_ops; 3318c2ecf20Sopenharmony_ci netdev->ethtool_ops = &sr9700_ethtool_ops; 3328c2ecf20Sopenharmony_ci netdev->hard_header_len += SR_TX_OVERHEAD; 3338c2ecf20Sopenharmony_ci dev->hard_mtu = netdev->mtu + netdev->hard_header_len; 3348c2ecf20Sopenharmony_ci /* bulkin buffer is preferably not less than 3K */ 3358c2ecf20Sopenharmony_ci dev->rx_urb_size = 3072; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci mii = &dev->mii; 3388c2ecf20Sopenharmony_ci mii->dev = netdev; 3398c2ecf20Sopenharmony_ci mii->mdio_read = sr_mdio_read; 3408c2ecf20Sopenharmony_ci mii->mdio_write = sr_mdio_write; 3418c2ecf20Sopenharmony_ci mii->phy_id_mask = 0x1f; 3428c2ecf20Sopenharmony_ci mii->reg_num_mask = 0x1f; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci sr_write_reg(dev, SR_NCR, NCR_RST); 3458c2ecf20Sopenharmony_ci udelay(20); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* read MAC 3488c2ecf20Sopenharmony_ci * After Chip Power on, the Chip will reload the MAC from 3498c2ecf20Sopenharmony_ci * EEPROM automatically to PAR. In case there is no EEPROM externally, 3508c2ecf20Sopenharmony_ci * a default MAC address is stored in PAR for making chip work properly. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci if (sr_read(dev, SR_PAR, ETH_ALEN, netdev->dev_addr) < 0) { 3538c2ecf20Sopenharmony_ci netdev_err(netdev, "Error reading MAC address\n"); 3548c2ecf20Sopenharmony_ci ret = -ENODEV; 3558c2ecf20Sopenharmony_ci goto out; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* power up and reset phy */ 3598c2ecf20Sopenharmony_ci sr_write_reg(dev, SR_PRR, PRR_PHY_RST); 3608c2ecf20Sopenharmony_ci /* at least 10ms, here 20ms for safe */ 3618c2ecf20Sopenharmony_ci msleep(20); 3628c2ecf20Sopenharmony_ci sr_write_reg(dev, SR_PRR, 0); 3638c2ecf20Sopenharmony_ci /* at least 1ms, here 2ms for reading right register */ 3648c2ecf20Sopenharmony_ci udelay(2 * 1000); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* receive broadcast packets */ 3678c2ecf20Sopenharmony_ci sr9700_set_multicast(netdev); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci sr_mdio_write(netdev, mii->phy_id, MII_BMCR, BMCR_RESET); 3708c2ecf20Sopenharmony_ci sr_mdio_write(netdev, mii->phy_id, MII_ADVERTISE, ADVERTISE_ALL | 3718c2ecf20Sopenharmony_ci ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); 3728c2ecf20Sopenharmony_ci mii_nway_restart(mii); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ciout: 3758c2ecf20Sopenharmony_ci return ret; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int sr9700_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct sk_buff *sr_skb; 3818c2ecf20Sopenharmony_ci int len; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* skb content (packets) format : 3848c2ecf20Sopenharmony_ci * p0 p1 p2 ...... pm 3858c2ecf20Sopenharmony_ci * / \ 3868c2ecf20Sopenharmony_ci * / \ 3878c2ecf20Sopenharmony_ci * / \ 3888c2ecf20Sopenharmony_ci * / \ 3898c2ecf20Sopenharmony_ci * p0b0 p0b1 p0b2 p0b3 ...... p0b(n-4) p0b(n-3)...p0bn 3908c2ecf20Sopenharmony_ci * 3918c2ecf20Sopenharmony_ci * p0 : packet 0 3928c2ecf20Sopenharmony_ci * p0b0 : packet 0 byte 0 3938c2ecf20Sopenharmony_ci * 3948c2ecf20Sopenharmony_ci * b0: rx status 3958c2ecf20Sopenharmony_ci * b1: packet length (incl crc) low 3968c2ecf20Sopenharmony_ci * b2: packet length (incl crc) high 3978c2ecf20Sopenharmony_ci * b3..n-4: packet data 3988c2ecf20Sopenharmony_ci * bn-3..bn: ethernet packet crc 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci if (unlikely(skb->len < SR_RX_OVERHEAD)) { 4018c2ecf20Sopenharmony_ci netdev_err(dev->net, "unexpected tiny rx frame\n"); 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* one skb may contains multiple packets */ 4068c2ecf20Sopenharmony_ci while (skb->len > SR_RX_OVERHEAD) { 4078c2ecf20Sopenharmony_ci if (skb->data[0] != 0x40) 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* ignore the CRC length */ 4118c2ecf20Sopenharmony_ci len = (skb->data[1] | (skb->data[2] << 8)) - 4; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (len > ETH_FRAME_LEN || len > skb->len || len < 0) 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* the last packet of current skb */ 4178c2ecf20Sopenharmony_ci if (skb->len == (len + SR_RX_OVERHEAD)) { 4188c2ecf20Sopenharmony_ci skb_pull(skb, 3); 4198c2ecf20Sopenharmony_ci skb->len = len; 4208c2ecf20Sopenharmony_ci skb_set_tail_pointer(skb, len); 4218c2ecf20Sopenharmony_ci skb->truesize = len + sizeof(struct sk_buff); 4228c2ecf20Sopenharmony_ci return 2; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* skb_clone is used for address align */ 4268c2ecf20Sopenharmony_ci sr_skb = skb_clone(skb, GFP_ATOMIC); 4278c2ecf20Sopenharmony_ci if (!sr_skb) 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci sr_skb->len = len; 4318c2ecf20Sopenharmony_ci sr_skb->data = skb->data + 3; 4328c2ecf20Sopenharmony_ci skb_set_tail_pointer(sr_skb, len); 4338c2ecf20Sopenharmony_ci sr_skb->truesize = len + sizeof(struct sk_buff); 4348c2ecf20Sopenharmony_ci usbnet_skb_return(dev, sr_skb); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci skb_pull(skb, len + SR_RX_OVERHEAD); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic struct sk_buff *sr9700_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 4438c2ecf20Sopenharmony_ci gfp_t flags) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci int len; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* SR9700 can only send out one ethernet packet at once. 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * b0 b1 b2 b3 ...... b(n-4) b(n-3)...bn 4508c2ecf20Sopenharmony_ci * 4518c2ecf20Sopenharmony_ci * b0: rx status 4528c2ecf20Sopenharmony_ci * b1: packet length (incl crc) low 4538c2ecf20Sopenharmony_ci * b2: packet length (incl crc) high 4548c2ecf20Sopenharmony_ci * b3..n-4: packet data 4558c2ecf20Sopenharmony_ci * bn-3..bn: ethernet packet crc 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci len = skb->len; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (skb_cow_head(skb, SR_TX_OVERHEAD)) { 4618c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4628c2ecf20Sopenharmony_ci return NULL; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci __skb_push(skb, SR_TX_OVERHEAD); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* usbnet adds padding if length is a multiple of packet size 4688c2ecf20Sopenharmony_ci * if so, adjust length value in header 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci if ((skb->len % dev->maxpacket) == 0) 4718c2ecf20Sopenharmony_ci len++; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci skb->data[0] = len; 4748c2ecf20Sopenharmony_ci skb->data[1] = len >> 8; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return skb; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic void sr9700_status(struct usbnet *dev, struct urb *urb) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci int link; 4828c2ecf20Sopenharmony_ci u8 *buf; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* format: 4858c2ecf20Sopenharmony_ci b0: net status 4868c2ecf20Sopenharmony_ci b1: tx status 1 4878c2ecf20Sopenharmony_ci b2: tx status 2 4888c2ecf20Sopenharmony_ci b3: rx status 4898c2ecf20Sopenharmony_ci b4: rx overflow 4908c2ecf20Sopenharmony_ci b5: rx count 4918c2ecf20Sopenharmony_ci b6: tx count 4928c2ecf20Sopenharmony_ci b7: gpr 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (urb->actual_length < 8) 4968c2ecf20Sopenharmony_ci return; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci buf = urb->transfer_buffer; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci link = !!(buf[0] & 0x40); 5018c2ecf20Sopenharmony_ci if (netif_carrier_ok(dev->net) != link) { 5028c2ecf20Sopenharmony_ci usbnet_link_change(dev, link, 1); 5038c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "Link Status is: %d\n", link); 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int sr9700_link_reset(struct usbnet *dev) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct ethtool_cmd ecmd; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci mii_check_media(&dev->mii, 1, 1); 5128c2ecf20Sopenharmony_ci mii_ethtool_gset(&dev->mii, &ecmd); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "link_reset() speed: %d duplex: %d\n", 5158c2ecf20Sopenharmony_ci ecmd.speed, ecmd.duplex); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic const struct driver_info sr9700_driver_info = { 5218c2ecf20Sopenharmony_ci .description = "CoreChip SR9700 USB Ethernet", 5228c2ecf20Sopenharmony_ci .flags = FLAG_ETHER, 5238c2ecf20Sopenharmony_ci .bind = sr9700_bind, 5248c2ecf20Sopenharmony_ci .rx_fixup = sr9700_rx_fixup, 5258c2ecf20Sopenharmony_ci .tx_fixup = sr9700_tx_fixup, 5268c2ecf20Sopenharmony_ci .status = sr9700_status, 5278c2ecf20Sopenharmony_ci .link_reset = sr9700_link_reset, 5288c2ecf20Sopenharmony_ci .reset = sr9700_link_reset, 5298c2ecf20Sopenharmony_ci}; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic const struct usb_device_id products[] = { 5328c2ecf20Sopenharmony_ci { 5338c2ecf20Sopenharmony_ci USB_DEVICE(0x0fe6, 0x9700), /* SR9700 device */ 5348c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&sr9700_driver_info, 5358c2ecf20Sopenharmony_ci }, 5368c2ecf20Sopenharmony_ci {}, /* END */ 5378c2ecf20Sopenharmony_ci}; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic struct usb_driver sr9700_usb_driver = { 5428c2ecf20Sopenharmony_ci .name = "sr9700", 5438c2ecf20Sopenharmony_ci .id_table = products, 5448c2ecf20Sopenharmony_ci .probe = usbnet_probe, 5458c2ecf20Sopenharmony_ci .disconnect = usbnet_disconnect, 5468c2ecf20Sopenharmony_ci .suspend = usbnet_suspend, 5478c2ecf20Sopenharmony_ci .resume = usbnet_resume, 5488c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 5498c2ecf20Sopenharmony_ci}; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cimodule_usb_driver(sr9700_usb_driver); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ciMODULE_AUTHOR("liujl <liujunliang_ljl@163.com>"); 5548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SR9700 one chip USB 1.1 USB to Ethernet device from http://www.corechip-sz.com/"); 5558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 556