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