162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * CoreChip-sz SR9700 one chip USB 1.1 Ethernet Devices 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Author : Liu Junliang <liujunliang_ljl@163.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based on dm9601.c 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License 962306a36Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of any 1062306a36Sopenharmony_ci * kind, whether express or implied. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/sched.h> 1562306a36Sopenharmony_ci#include <linux/stddef.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/etherdevice.h> 1862306a36Sopenharmony_ci#include <linux/ethtool.h> 1962306a36Sopenharmony_ci#include <linux/mii.h> 2062306a36Sopenharmony_ci#include <linux/usb.h> 2162306a36Sopenharmony_ci#include <linux/crc32.h> 2262306a36Sopenharmony_ci#include <linux/usb/usbnet.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "sr9700.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int sr_read(struct usbnet *dev, u8 reg, u16 length, void *data) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci int err; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci err = usbnet_read_cmd(dev, SR_RD_REGS, SR_REQ_RD_REG, 0, reg, data, 3162306a36Sopenharmony_ci length); 3262306a36Sopenharmony_ci if ((err != length) && (err >= 0)) 3362306a36Sopenharmony_ci err = -EINVAL; 3462306a36Sopenharmony_ci return err; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int sr_write(struct usbnet *dev, u8 reg, u16 length, void *data) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci int err; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci err = usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, 0, reg, data, 4262306a36Sopenharmony_ci length); 4362306a36Sopenharmony_ci if ((err >= 0) && (err < length)) 4462306a36Sopenharmony_ci err = -EINVAL; 4562306a36Sopenharmony_ci return err; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int sr_read_reg(struct usbnet *dev, u8 reg, u8 *value) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci return sr_read(dev, reg, 1, value); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int sr_write_reg(struct usbnet *dev, u8 reg, u8 value) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci return usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, 5662306a36Sopenharmony_ci value, reg, NULL, 0); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void sr_write_async(struct usbnet *dev, u8 reg, u16 length, 6062306a36Sopenharmony_ci const void *data) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, 6362306a36Sopenharmony_ci 0, reg, data, length); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void sr_write_reg_async(struct usbnet *dev, u8 reg, u8 value) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, 6962306a36Sopenharmony_ci value, reg, NULL, 0); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int wait_phy_eeprom_ready(struct usbnet *dev, int phy) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int i; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci for (i = 0; i < SR_SHARE_TIMEOUT; i++) { 7762306a36Sopenharmony_ci u8 tmp = 0; 7862306a36Sopenharmony_ci int ret; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci udelay(1); 8162306a36Sopenharmony_ci ret = sr_read_reg(dev, SR_EPCR, &tmp); 8262306a36Sopenharmony_ci if (ret < 0) 8362306a36Sopenharmony_ci return ret; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* ready */ 8662306a36Sopenharmony_ci if (!(tmp & EPCR_ERRE)) 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci netdev_err(dev->net, "%s write timed out!\n", phy ? "phy" : "eeprom"); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return -EIO; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int sr_share_read_word(struct usbnet *dev, int phy, u8 reg, 9662306a36Sopenharmony_ci __le16 *value) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci int ret; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci mutex_lock(&dev->phy_mutex); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci sr_write_reg(dev, SR_EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); 10362306a36Sopenharmony_ci sr_write_reg(dev, SR_EPCR, phy ? (EPCR_EPOS | EPCR_ERPRR) : EPCR_ERPRR); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci ret = wait_phy_eeprom_ready(dev, phy); 10662306a36Sopenharmony_ci if (ret < 0) 10762306a36Sopenharmony_ci goto out_unlock; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci sr_write_reg(dev, SR_EPCR, 0x0); 11062306a36Sopenharmony_ci ret = sr_read(dev, SR_EPDR, 2, value); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci netdev_dbg(dev->net, "read shared %d 0x%02x returned 0x%04x, %d\n", 11362306a36Sopenharmony_ci phy, reg, *value, ret); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciout_unlock: 11662306a36Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 11762306a36Sopenharmony_ci return ret; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int sr_share_write_word(struct usbnet *dev, int phy, u8 reg, 12162306a36Sopenharmony_ci __le16 value) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci int ret; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci mutex_lock(&dev->phy_mutex); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ret = sr_write(dev, SR_EPDR, 2, &value); 12862306a36Sopenharmony_ci if (ret < 0) 12962306a36Sopenharmony_ci goto out_unlock; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci sr_write_reg(dev, SR_EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); 13262306a36Sopenharmony_ci sr_write_reg(dev, SR_EPCR, phy ? (EPCR_WEP | EPCR_EPOS | EPCR_ERPRW) : 13362306a36Sopenharmony_ci (EPCR_WEP | EPCR_ERPRW)); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci ret = wait_phy_eeprom_ready(dev, phy); 13662306a36Sopenharmony_ci if (ret < 0) 13762306a36Sopenharmony_ci goto out_unlock; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci sr_write_reg(dev, SR_EPCR, 0x0); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciout_unlock: 14262306a36Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int sr_read_eeprom_word(struct usbnet *dev, u8 offset, void *value) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci return sr_share_read_word(dev, 0, offset, value); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int sr9700_get_eeprom_len(struct net_device *netdev) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci return SR_EEPROM_LEN; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int sr9700_get_eeprom(struct net_device *netdev, 15762306a36Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 16062306a36Sopenharmony_ci __le16 *buf = (__le16 *)data; 16162306a36Sopenharmony_ci int ret = 0; 16262306a36Sopenharmony_ci int i; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* access is 16bit */ 16562306a36Sopenharmony_ci if ((eeprom->offset & 0x01) || (eeprom->len & 0x01)) 16662306a36Sopenharmony_ci return -EINVAL; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci for (i = 0; i < eeprom->len / 2; i++) { 16962306a36Sopenharmony_ci ret = sr_read_eeprom_word(dev, eeprom->offset / 2 + i, buf + i); 17062306a36Sopenharmony_ci if (ret < 0) 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return ret; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int sr_mdio_read(struct net_device *netdev, int phy_id, int loc) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 18062306a36Sopenharmony_ci __le16 res; 18162306a36Sopenharmony_ci int rc = 0; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (phy_id) { 18462306a36Sopenharmony_ci netdev_dbg(netdev, "Only internal phy supported\n"); 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Access NSR_LINKST bit for link status instead of MII_BMSR */ 18962306a36Sopenharmony_ci if (loc == MII_BMSR) { 19062306a36Sopenharmony_ci u8 value; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci sr_read_reg(dev, SR_NSR, &value); 19362306a36Sopenharmony_ci if (value & NSR_LINKST) 19462306a36Sopenharmony_ci rc = 1; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci sr_share_read_word(dev, 1, loc, &res); 19762306a36Sopenharmony_ci if (rc == 1) 19862306a36Sopenharmony_ci res = le16_to_cpu(res) | BMSR_LSTATUS; 19962306a36Sopenharmony_ci else 20062306a36Sopenharmony_ci res = le16_to_cpu(res) & ~BMSR_LSTATUS; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci netdev_dbg(netdev, "sr_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", 20362306a36Sopenharmony_ci phy_id, loc, res); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return res; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void sr_mdio_write(struct net_device *netdev, int phy_id, int loc, 20962306a36Sopenharmony_ci int val) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 21262306a36Sopenharmony_ci __le16 res = cpu_to_le16(val); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (phy_id) { 21562306a36Sopenharmony_ci netdev_dbg(netdev, "Only internal phy supported\n"); 21662306a36Sopenharmony_ci return; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci netdev_dbg(netdev, "sr_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", 22062306a36Sopenharmony_ci phy_id, loc, val); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci sr_share_write_word(dev, 1, loc, res); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic u32 sr9700_get_link(struct net_device *netdev) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 22862306a36Sopenharmony_ci u8 value = 0; 22962306a36Sopenharmony_ci int rc = 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Get the Link Status directly */ 23262306a36Sopenharmony_ci sr_read_reg(dev, SR_NSR, &value); 23362306a36Sopenharmony_ci if (value & NSR_LINKST) 23462306a36Sopenharmony_ci rc = 1; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return rc; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int sr9700_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const struct ethtool_ops sr9700_ethtool_ops = { 24762306a36Sopenharmony_ci .get_drvinfo = usbnet_get_drvinfo, 24862306a36Sopenharmony_ci .get_link = sr9700_get_link, 24962306a36Sopenharmony_ci .get_msglevel = usbnet_get_msglevel, 25062306a36Sopenharmony_ci .set_msglevel = usbnet_set_msglevel, 25162306a36Sopenharmony_ci .get_eeprom_len = sr9700_get_eeprom_len, 25262306a36Sopenharmony_ci .get_eeprom = sr9700_get_eeprom, 25362306a36Sopenharmony_ci .nway_reset = usbnet_nway_reset, 25462306a36Sopenharmony_ci .get_link_ksettings = usbnet_get_link_ksettings_mii, 25562306a36Sopenharmony_ci .set_link_ksettings = usbnet_set_link_ksettings_mii, 25662306a36Sopenharmony_ci}; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void sr9700_set_multicast(struct net_device *netdev) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 26162306a36Sopenharmony_ci /* We use the 20 byte dev->data for our 8 byte filter buffer 26262306a36Sopenharmony_ci * to avoid allocating memory that is tricky to free later 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci u8 *hashes = (u8 *)&dev->data; 26562306a36Sopenharmony_ci /* rx_ctl setting : enable, disable_long, disable_crc */ 26662306a36Sopenharmony_ci u8 rx_ctl = RCR_RXEN | RCR_DIS_CRC | RCR_DIS_LONG; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci memset(hashes, 0x00, SR_MCAST_SIZE); 26962306a36Sopenharmony_ci /* broadcast address */ 27062306a36Sopenharmony_ci hashes[SR_MCAST_SIZE - 1] |= SR_MCAST_ADDR_FLAG; 27162306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 27262306a36Sopenharmony_ci rx_ctl |= RCR_PRMSC; 27362306a36Sopenharmony_ci } else if (netdev->flags & IFF_ALLMULTI || 27462306a36Sopenharmony_ci netdev_mc_count(netdev) > SR_MCAST_MAX) { 27562306a36Sopenharmony_ci rx_ctl |= RCR_RUNT; 27662306a36Sopenharmony_ci } else if (!netdev_mc_empty(netdev)) { 27762306a36Sopenharmony_ci struct netdev_hw_addr *ha; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 28062306a36Sopenharmony_ci u32 crc = ether_crc(ETH_ALEN, ha->addr) >> 26; 28162306a36Sopenharmony_ci hashes[crc >> 3] |= 1 << (crc & 0x7); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci sr_write_async(dev, SR_MAR, SR_MCAST_SIZE, hashes); 28662306a36Sopenharmony_ci sr_write_reg_async(dev, SR_RCR, rx_ctl); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int sr9700_set_mac_address(struct net_device *netdev, void *p) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 29262306a36Sopenharmony_ci struct sockaddr *addr = p; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) { 29562306a36Sopenharmony_ci netdev_err(netdev, "not setting invalid mac address %pM\n", 29662306a36Sopenharmony_ci addr->sa_data); 29762306a36Sopenharmony_ci return -EINVAL; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci eth_hw_addr_set(netdev, addr->sa_data); 30162306a36Sopenharmony_ci sr_write_async(dev, SR_PAR, 6, netdev->dev_addr); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic const struct net_device_ops sr9700_netdev_ops = { 30762306a36Sopenharmony_ci .ndo_open = usbnet_open, 30862306a36Sopenharmony_ci .ndo_stop = usbnet_stop, 30962306a36Sopenharmony_ci .ndo_start_xmit = usbnet_start_xmit, 31062306a36Sopenharmony_ci .ndo_tx_timeout = usbnet_tx_timeout, 31162306a36Sopenharmony_ci .ndo_change_mtu = usbnet_change_mtu, 31262306a36Sopenharmony_ci .ndo_get_stats64 = dev_get_tstats64, 31362306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 31462306a36Sopenharmony_ci .ndo_eth_ioctl = sr9700_ioctl, 31562306a36Sopenharmony_ci .ndo_set_rx_mode = sr9700_set_multicast, 31662306a36Sopenharmony_ci .ndo_set_mac_address = sr9700_set_mac_address, 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int sr9700_bind(struct usbnet *dev, struct usb_interface *intf) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct net_device *netdev; 32262306a36Sopenharmony_ci struct mii_if_info *mii; 32362306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 32462306a36Sopenharmony_ci int ret; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ret = usbnet_get_endpoints(dev, intf); 32762306a36Sopenharmony_ci if (ret) 32862306a36Sopenharmony_ci goto out; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci netdev = dev->net; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci netdev->netdev_ops = &sr9700_netdev_ops; 33362306a36Sopenharmony_ci netdev->ethtool_ops = &sr9700_ethtool_ops; 33462306a36Sopenharmony_ci netdev->hard_header_len += SR_TX_OVERHEAD; 33562306a36Sopenharmony_ci dev->hard_mtu = netdev->mtu + netdev->hard_header_len; 33662306a36Sopenharmony_ci /* bulkin buffer is preferably not less than 3K */ 33762306a36Sopenharmony_ci dev->rx_urb_size = 3072; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci mii = &dev->mii; 34062306a36Sopenharmony_ci mii->dev = netdev; 34162306a36Sopenharmony_ci mii->mdio_read = sr_mdio_read; 34262306a36Sopenharmony_ci mii->mdio_write = sr_mdio_write; 34362306a36Sopenharmony_ci mii->phy_id_mask = 0x1f; 34462306a36Sopenharmony_ci mii->reg_num_mask = 0x1f; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci sr_write_reg(dev, SR_NCR, NCR_RST); 34762306a36Sopenharmony_ci udelay(20); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* read MAC 35062306a36Sopenharmony_ci * After Chip Power on, the Chip will reload the MAC from 35162306a36Sopenharmony_ci * EEPROM automatically to PAR. In case there is no EEPROM externally, 35262306a36Sopenharmony_ci * a default MAC address is stored in PAR for making chip work properly. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci if (sr_read(dev, SR_PAR, ETH_ALEN, addr) < 0) { 35562306a36Sopenharmony_ci netdev_err(netdev, "Error reading MAC address\n"); 35662306a36Sopenharmony_ci ret = -ENODEV; 35762306a36Sopenharmony_ci goto out; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci eth_hw_addr_set(netdev, addr); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* power up and reset phy */ 36262306a36Sopenharmony_ci sr_write_reg(dev, SR_PRR, PRR_PHY_RST); 36362306a36Sopenharmony_ci /* at least 10ms, here 20ms for safe */ 36462306a36Sopenharmony_ci msleep(20); 36562306a36Sopenharmony_ci sr_write_reg(dev, SR_PRR, 0); 36662306a36Sopenharmony_ci /* at least 1ms, here 2ms for reading right register */ 36762306a36Sopenharmony_ci udelay(2 * 1000); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* receive broadcast packets */ 37062306a36Sopenharmony_ci sr9700_set_multicast(netdev); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci sr_mdio_write(netdev, mii->phy_id, MII_BMCR, BMCR_RESET); 37362306a36Sopenharmony_ci sr_mdio_write(netdev, mii->phy_id, MII_ADVERTISE, ADVERTISE_ALL | 37462306a36Sopenharmony_ci ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); 37562306a36Sopenharmony_ci mii_nway_restart(mii); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ciout: 37862306a36Sopenharmony_ci return ret; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int sr9700_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct sk_buff *sr_skb; 38462306a36Sopenharmony_ci int len; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* skb content (packets) format : 38762306a36Sopenharmony_ci * p0 p1 p2 ...... pm 38862306a36Sopenharmony_ci * / \ 38962306a36Sopenharmony_ci * / \ 39062306a36Sopenharmony_ci * / \ 39162306a36Sopenharmony_ci * / \ 39262306a36Sopenharmony_ci * p0b0 p0b1 p0b2 p0b3 ...... p0b(n-4) p0b(n-3)...p0bn 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * p0 : packet 0 39562306a36Sopenharmony_ci * p0b0 : packet 0 byte 0 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * b0: rx status 39862306a36Sopenharmony_ci * b1: packet length (incl crc) low 39962306a36Sopenharmony_ci * b2: packet length (incl crc) high 40062306a36Sopenharmony_ci * b3..n-4: packet data 40162306a36Sopenharmony_ci * bn-3..bn: ethernet packet crc 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci if (unlikely(skb->len < SR_RX_OVERHEAD)) { 40462306a36Sopenharmony_ci netdev_err(dev->net, "unexpected tiny rx frame\n"); 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* one skb may contains multiple packets */ 40962306a36Sopenharmony_ci while (skb->len > SR_RX_OVERHEAD) { 41062306a36Sopenharmony_ci if (skb->data[0] != 0x40) 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* ignore the CRC length */ 41462306a36Sopenharmony_ci len = (skb->data[1] | (skb->data[2] << 8)) - 4; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (len > ETH_FRAME_LEN || len > skb->len || len < 0) 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* the last packet of current skb */ 42062306a36Sopenharmony_ci if (skb->len == (len + SR_RX_OVERHEAD)) { 42162306a36Sopenharmony_ci skb_pull(skb, 3); 42262306a36Sopenharmony_ci skb->len = len; 42362306a36Sopenharmony_ci skb_set_tail_pointer(skb, len); 42462306a36Sopenharmony_ci skb->truesize = len + sizeof(struct sk_buff); 42562306a36Sopenharmony_ci return 2; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* skb_clone is used for address align */ 42962306a36Sopenharmony_ci sr_skb = skb_clone(skb, GFP_ATOMIC); 43062306a36Sopenharmony_ci if (!sr_skb) 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci sr_skb->len = len; 43462306a36Sopenharmony_ci sr_skb->data = skb->data + 3; 43562306a36Sopenharmony_ci skb_set_tail_pointer(sr_skb, len); 43662306a36Sopenharmony_ci sr_skb->truesize = len + sizeof(struct sk_buff); 43762306a36Sopenharmony_ci usbnet_skb_return(dev, sr_skb); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci skb_pull(skb, len + SR_RX_OVERHEAD); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic struct sk_buff *sr9700_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 44662306a36Sopenharmony_ci gfp_t flags) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci int len; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* SR9700 can only send out one ethernet packet at once. 45162306a36Sopenharmony_ci * 45262306a36Sopenharmony_ci * b0 b1 b2 b3 ...... b(n-4) b(n-3)...bn 45362306a36Sopenharmony_ci * 45462306a36Sopenharmony_ci * b0: rx status 45562306a36Sopenharmony_ci * b1: packet length (incl crc) low 45662306a36Sopenharmony_ci * b2: packet length (incl crc) high 45762306a36Sopenharmony_ci * b3..n-4: packet data 45862306a36Sopenharmony_ci * bn-3..bn: ethernet packet crc 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci len = skb->len; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (skb_cow_head(skb, SR_TX_OVERHEAD)) { 46462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 46562306a36Sopenharmony_ci return NULL; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci __skb_push(skb, SR_TX_OVERHEAD); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* usbnet adds padding if length is a multiple of packet size 47162306a36Sopenharmony_ci * if so, adjust length value in header 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_ci if ((skb->len % dev->maxpacket) == 0) 47462306a36Sopenharmony_ci len++; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci skb->data[0] = len; 47762306a36Sopenharmony_ci skb->data[1] = len >> 8; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return skb; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic void sr9700_status(struct usbnet *dev, struct urb *urb) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci int link; 48562306a36Sopenharmony_ci u8 *buf; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* format: 48862306a36Sopenharmony_ci b0: net status 48962306a36Sopenharmony_ci b1: tx status 1 49062306a36Sopenharmony_ci b2: tx status 2 49162306a36Sopenharmony_ci b3: rx status 49262306a36Sopenharmony_ci b4: rx overflow 49362306a36Sopenharmony_ci b5: rx count 49462306a36Sopenharmony_ci b6: tx count 49562306a36Sopenharmony_ci b7: gpr 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (urb->actual_length < 8) 49962306a36Sopenharmony_ci return; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci buf = urb->transfer_buffer; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci link = !!(buf[0] & 0x40); 50462306a36Sopenharmony_ci if (netif_carrier_ok(dev->net) != link) { 50562306a36Sopenharmony_ci usbnet_link_change(dev, link, 1); 50662306a36Sopenharmony_ci netdev_dbg(dev->net, "Link Status is: %d\n", link); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int sr9700_link_reset(struct usbnet *dev) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct ethtool_cmd ecmd; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci mii_check_media(&dev->mii, 1, 1); 51562306a36Sopenharmony_ci mii_ethtool_gset(&dev->mii, &ecmd); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci netdev_dbg(dev->net, "link_reset() speed: %d duplex: %d\n", 51862306a36Sopenharmony_ci ecmd.speed, ecmd.duplex); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic const struct driver_info sr9700_driver_info = { 52462306a36Sopenharmony_ci .description = "CoreChip SR9700 USB Ethernet", 52562306a36Sopenharmony_ci .flags = FLAG_ETHER, 52662306a36Sopenharmony_ci .bind = sr9700_bind, 52762306a36Sopenharmony_ci .rx_fixup = sr9700_rx_fixup, 52862306a36Sopenharmony_ci .tx_fixup = sr9700_tx_fixup, 52962306a36Sopenharmony_ci .status = sr9700_status, 53062306a36Sopenharmony_ci .link_reset = sr9700_link_reset, 53162306a36Sopenharmony_ci .reset = sr9700_link_reset, 53262306a36Sopenharmony_ci}; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic const struct usb_device_id products[] = { 53562306a36Sopenharmony_ci { 53662306a36Sopenharmony_ci USB_DEVICE(0x0fe6, 0x9700), /* SR9700 device */ 53762306a36Sopenharmony_ci .driver_info = (unsigned long)&sr9700_driver_info, 53862306a36Sopenharmony_ci }, 53962306a36Sopenharmony_ci {}, /* END */ 54062306a36Sopenharmony_ci}; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic struct usb_driver sr9700_usb_driver = { 54562306a36Sopenharmony_ci .name = "sr9700", 54662306a36Sopenharmony_ci .id_table = products, 54762306a36Sopenharmony_ci .probe = usbnet_probe, 54862306a36Sopenharmony_ci .disconnect = usbnet_disconnect, 54962306a36Sopenharmony_ci .suspend = usbnet_suspend, 55062306a36Sopenharmony_ci .resume = usbnet_resume, 55162306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 55262306a36Sopenharmony_ci}; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cimodule_usb_driver(sr9700_usb_driver); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ciMODULE_AUTHOR("liujl <liujunliang_ljl@163.com>"); 55762306a36Sopenharmony_ciMODULE_DESCRIPTION("SR9700 one chip USB 1.1 USB to Ethernet device from http://www.corechip-sz.com/"); 55862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 559