162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ASIX AX8817X based USB 2.0 Ethernet Devices
462306a36Sopenharmony_ci * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com>
562306a36Sopenharmony_ci * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
662306a36Sopenharmony_ci * Copyright (C) 2006 James Painter <jamie.painter@iname.com>
762306a36Sopenharmony_ci * Copyright (c) 2002-2003 TiVo Inc.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "asix.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define PHY_MODE_MARVELL	0x0000
1362306a36Sopenharmony_ci#define MII_MARVELL_LED_CTRL	0x0018
1462306a36Sopenharmony_ci#define MII_MARVELL_STATUS	0x001b
1562306a36Sopenharmony_ci#define MII_MARVELL_CTRL	0x0014
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define MARVELL_LED_MANUAL	0x0019
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define MARVELL_STATUS_HWCFG	0x0004
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define MARVELL_CTRL_TXDELAY	0x0002
2262306a36Sopenharmony_ci#define MARVELL_CTRL_RXDELAY	0x0080
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define	PHY_MODE_RTL8211CL	0x000C
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define AX88772A_PHY14H		0x14
2762306a36Sopenharmony_ci#define AX88772A_PHY14H_DEFAULT 0x442C
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define AX88772A_PHY15H		0x15
3062306a36Sopenharmony_ci#define AX88772A_PHY15H_DEFAULT 0x03C8
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define AX88772A_PHY16H		0x16
3362306a36Sopenharmony_ci#define AX88772A_PHY16H_DEFAULT 0x4044
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct ax88172_int_data {
3662306a36Sopenharmony_ci	__le16 res1;
3762306a36Sopenharmony_ci	u8 link;
3862306a36Sopenharmony_ci	__le16 res2;
3962306a36Sopenharmony_ci	u8 status;
4062306a36Sopenharmony_ci	__le16 res3;
4162306a36Sopenharmony_ci} __packed;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void asix_status(struct usbnet *dev, struct urb *urb)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct ax88172_int_data *event;
4662306a36Sopenharmony_ci	int link;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (urb->actual_length < 8)
4962306a36Sopenharmony_ci		return;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	event = urb->transfer_buffer;
5262306a36Sopenharmony_ci	link = event->link & 0x01;
5362306a36Sopenharmony_ci	if (netif_carrier_ok(dev->net) != link) {
5462306a36Sopenharmony_ci		usbnet_link_change(dev, link, 1);
5562306a36Sopenharmony_ci		netdev_dbg(dev->net, "Link Status is: %d\n", link);
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic void asix_set_netdev_dev_addr(struct usbnet *dev, u8 *addr)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	if (is_valid_ether_addr(addr)) {
6262306a36Sopenharmony_ci		eth_hw_addr_set(dev->net, addr);
6362306a36Sopenharmony_ci	} else {
6462306a36Sopenharmony_ci		netdev_info(dev->net, "invalid hw address, using random\n");
6562306a36Sopenharmony_ci		eth_hw_addr_random(dev->net);
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */
7062306a36Sopenharmony_cistatic u32 asix_get_phyid(struct usbnet *dev)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	int phy_reg;
7362306a36Sopenharmony_ci	u32 phy_id;
7462306a36Sopenharmony_ci	int i;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* Poll for the rare case the FW or phy isn't ready yet.  */
7762306a36Sopenharmony_ci	for (i = 0; i < 100; i++) {
7862306a36Sopenharmony_ci		phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1);
7962306a36Sopenharmony_ci		if (phy_reg < 0)
8062306a36Sopenharmony_ci			return 0;
8162306a36Sopenharmony_ci		if (phy_reg != 0 && phy_reg != 0xFFFF)
8262306a36Sopenharmony_ci			break;
8362306a36Sopenharmony_ci		mdelay(1);
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (phy_reg <= 0 || phy_reg == 0xFFFF)
8762306a36Sopenharmony_ci		return 0;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	phy_id = (phy_reg & 0xffff) << 16;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2);
9262306a36Sopenharmony_ci	if (phy_reg < 0)
9362306a36Sopenharmony_ci		return 0;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	phy_id |= (phy_reg & 0xffff);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return phy_id;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic u32 asix_get_link(struct net_device *net)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return mii_link_ok(&dev->mii);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/* We need to override some ethtool_ops so we require our
11562306a36Sopenharmony_ci   own structure so we don't interfere with other usbnet
11662306a36Sopenharmony_ci   devices that may be connected at the same time. */
11762306a36Sopenharmony_cistatic const struct ethtool_ops ax88172_ethtool_ops = {
11862306a36Sopenharmony_ci	.get_drvinfo		= asix_get_drvinfo,
11962306a36Sopenharmony_ci	.get_link		= asix_get_link,
12062306a36Sopenharmony_ci	.get_msglevel		= usbnet_get_msglevel,
12162306a36Sopenharmony_ci	.set_msglevel		= usbnet_set_msglevel,
12262306a36Sopenharmony_ci	.get_wol		= asix_get_wol,
12362306a36Sopenharmony_ci	.set_wol		= asix_set_wol,
12462306a36Sopenharmony_ci	.get_eeprom_len		= asix_get_eeprom_len,
12562306a36Sopenharmony_ci	.get_eeprom		= asix_get_eeprom,
12662306a36Sopenharmony_ci	.set_eeprom		= asix_set_eeprom,
12762306a36Sopenharmony_ci	.nway_reset		= usbnet_nway_reset,
12862306a36Sopenharmony_ci	.get_link_ksettings	= usbnet_get_link_ksettings_mii,
12962306a36Sopenharmony_ci	.set_link_ksettings	= usbnet_set_link_ksettings_mii,
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void ax88172_set_multicast(struct net_device *net)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
13562306a36Sopenharmony_ci	struct asix_data *data = (struct asix_data *)&dev->data;
13662306a36Sopenharmony_ci	u8 rx_ctl = 0x8c;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (net->flags & IFF_PROMISC) {
13962306a36Sopenharmony_ci		rx_ctl |= 0x01;
14062306a36Sopenharmony_ci	} else if (net->flags & IFF_ALLMULTI ||
14162306a36Sopenharmony_ci		   netdev_mc_count(net) > AX_MAX_MCAST) {
14262306a36Sopenharmony_ci		rx_ctl |= 0x02;
14362306a36Sopenharmony_ci	} else if (netdev_mc_empty(net)) {
14462306a36Sopenharmony_ci		/* just broadcast and directed */
14562306a36Sopenharmony_ci	} else {
14662306a36Sopenharmony_ci		/* We use the 20 byte dev->data
14762306a36Sopenharmony_ci		 * for our 8 byte filter buffer
14862306a36Sopenharmony_ci		 * to avoid allocating memory that
14962306a36Sopenharmony_ci		 * is tricky to free later */
15062306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
15162306a36Sopenharmony_ci		u32 crc_bits;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		/* Build the multicast hash filter. */
15662306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, net) {
15762306a36Sopenharmony_ci			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
15862306a36Sopenharmony_ci			data->multi_filter[crc_bits >> 3] |=
15962306a36Sopenharmony_ci			    1 << (crc_bits & 7);
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
16362306a36Sopenharmony_ci				   AX_MCAST_FILTER_SIZE, data->multi_filter);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		rx_ctl |= 0x10;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int ax88172_link_reset(struct usbnet *dev)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	u8 mode;
17462306a36Sopenharmony_ci	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	mii_check_media(&dev->mii, 1, 1);
17762306a36Sopenharmony_ci	mii_ethtool_gset(&dev->mii, &ecmd);
17862306a36Sopenharmony_ci	mode = AX88172_MEDIUM_DEFAULT;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (ecmd.duplex != DUPLEX_FULL)
18162306a36Sopenharmony_ci		mode |= ~AX88172_MEDIUM_FD;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	netdev_dbg(dev->net, "ax88172_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
18462306a36Sopenharmony_ci		   ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	asix_write_medium_mode(dev, mode, 0);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return 0;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic const struct net_device_ops ax88172_netdev_ops = {
19262306a36Sopenharmony_ci	.ndo_open		= usbnet_open,
19362306a36Sopenharmony_ci	.ndo_stop		= usbnet_stop,
19462306a36Sopenharmony_ci	.ndo_start_xmit		= usbnet_start_xmit,
19562306a36Sopenharmony_ci	.ndo_tx_timeout		= usbnet_tx_timeout,
19662306a36Sopenharmony_ci	.ndo_change_mtu		= usbnet_change_mtu,
19762306a36Sopenharmony_ci	.ndo_get_stats64	= dev_get_tstats64,
19862306a36Sopenharmony_ci	.ndo_set_mac_address 	= eth_mac_addr,
19962306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
20062306a36Sopenharmony_ci	.ndo_eth_ioctl		= asix_ioctl,
20162306a36Sopenharmony_ci	.ndo_set_rx_mode	= ax88172_set_multicast,
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void asix_phy_reset(struct usbnet *dev, unsigned int reset_bits)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	unsigned int timeout = 5000;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, reset_bits);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* give phy_id a chance to process reset */
21162306a36Sopenharmony_ci	udelay(500);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* See IEEE 802.3 "22.2.4.1.1 Reset": 500ms max */
21462306a36Sopenharmony_ci	while (timeout--) {
21562306a36Sopenharmony_ci		if (asix_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR)
21662306a36Sopenharmony_ci							& BMCR_RESET)
21762306a36Sopenharmony_ci			udelay(100);
21862306a36Sopenharmony_ci		else
21962306a36Sopenharmony_ci			return;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	netdev_err(dev->net, "BMCR_RESET timeout on phy_id %d\n",
22362306a36Sopenharmony_ci		   dev->mii.phy_id);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	int ret = 0;
22962306a36Sopenharmony_ci	u8 buf[ETH_ALEN] = {0};
23062306a36Sopenharmony_ci	int i;
23162306a36Sopenharmony_ci	unsigned long gpio_bits = dev->driver_info->data;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	usbnet_get_endpoints(dev,intf);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* Toggle the GPIOs in a manufacturer/model specific way */
23662306a36Sopenharmony_ci	for (i = 2; i >= 0; i--) {
23762306a36Sopenharmony_ci		ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
23862306a36Sopenharmony_ci				(gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL, 0);
23962306a36Sopenharmony_ci		if (ret < 0)
24062306a36Sopenharmony_ci			goto out;
24162306a36Sopenharmony_ci		msleep(5);
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	ret = asix_write_rx_ctl(dev, 0x80, 0);
24562306a36Sopenharmony_ci	if (ret < 0)
24662306a36Sopenharmony_ci		goto out;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Get the MAC address */
24962306a36Sopenharmony_ci	ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID,
25062306a36Sopenharmony_ci			    0, 0, ETH_ALEN, buf, 0);
25162306a36Sopenharmony_ci	if (ret < 0) {
25262306a36Sopenharmony_ci		netdev_dbg(dev->net, "read AX_CMD_READ_NODE_ID failed: %d\n",
25362306a36Sopenharmony_ci			   ret);
25462306a36Sopenharmony_ci		goto out;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	asix_set_netdev_dev_addr(dev, buf);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* Initialize MII structure */
26062306a36Sopenharmony_ci	dev->mii.dev = dev->net;
26162306a36Sopenharmony_ci	dev->mii.mdio_read = asix_mdio_read;
26262306a36Sopenharmony_ci	dev->mii.mdio_write = asix_mdio_write;
26362306a36Sopenharmony_ci	dev->mii.phy_id_mask = 0x3f;
26462306a36Sopenharmony_ci	dev->mii.reg_num_mask = 0x1f;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	dev->mii.phy_id = asix_read_phy_addr(dev, true);
26762306a36Sopenharmony_ci	if (dev->mii.phy_id < 0)
26862306a36Sopenharmony_ci		return dev->mii.phy_id;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	dev->net->netdev_ops = &ax88172_netdev_ops;
27162306a36Sopenharmony_ci	dev->net->ethtool_ops = &ax88172_ethtool_ops;
27262306a36Sopenharmony_ci	dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */
27362306a36Sopenharmony_ci	dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	asix_phy_reset(dev, BMCR_RESET);
27662306a36Sopenharmony_ci	asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
27762306a36Sopenharmony_ci		ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
27862306a36Sopenharmony_ci	mii_nway_restart(&dev->mii);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return 0;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ciout:
28362306a36Sopenharmony_ci	return ret;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic void ax88772_ethtool_get_strings(struct net_device *netdev, u32 sset,
28762306a36Sopenharmony_ci					u8 *data)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	switch (sset) {
29062306a36Sopenharmony_ci	case ETH_SS_TEST:
29162306a36Sopenharmony_ci		net_selftest_get_strings(data);
29262306a36Sopenharmony_ci		break;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int ax88772_ethtool_get_sset_count(struct net_device *ndev, int sset)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	switch (sset) {
29962306a36Sopenharmony_ci	case ETH_SS_TEST:
30062306a36Sopenharmony_ci		return net_selftest_get_count();
30162306a36Sopenharmony_ci	default:
30262306a36Sopenharmony_ci		return -EOPNOTSUPP;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic void ax88772_ethtool_get_pauseparam(struct net_device *ndev,
30762306a36Sopenharmony_ci					  struct ethtool_pauseparam *pause)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(ndev);
31062306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	phylink_ethtool_get_pauseparam(priv->phylink, pause);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int ax88772_ethtool_set_pauseparam(struct net_device *ndev,
31662306a36Sopenharmony_ci					 struct ethtool_pauseparam *pause)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(ndev);
31962306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	return phylink_ethtool_set_pauseparam(priv->phylink, pause);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic const struct ethtool_ops ax88772_ethtool_ops = {
32562306a36Sopenharmony_ci	.get_drvinfo		= asix_get_drvinfo,
32662306a36Sopenharmony_ci	.get_link		= usbnet_get_link,
32762306a36Sopenharmony_ci	.get_msglevel		= usbnet_get_msglevel,
32862306a36Sopenharmony_ci	.set_msglevel		= usbnet_set_msglevel,
32962306a36Sopenharmony_ci	.get_wol		= asix_get_wol,
33062306a36Sopenharmony_ci	.set_wol		= asix_set_wol,
33162306a36Sopenharmony_ci	.get_eeprom_len		= asix_get_eeprom_len,
33262306a36Sopenharmony_ci	.get_eeprom		= asix_get_eeprom,
33362306a36Sopenharmony_ci	.set_eeprom		= asix_set_eeprom,
33462306a36Sopenharmony_ci	.nway_reset		= phy_ethtool_nway_reset,
33562306a36Sopenharmony_ci	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
33662306a36Sopenharmony_ci	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
33762306a36Sopenharmony_ci	.self_test		= net_selftest,
33862306a36Sopenharmony_ci	.get_strings		= ax88772_ethtool_get_strings,
33962306a36Sopenharmony_ci	.get_sset_count		= ax88772_ethtool_get_sset_count,
34062306a36Sopenharmony_ci	.get_pauseparam		= ax88772_ethtool_get_pauseparam,
34162306a36Sopenharmony_ci	.set_pauseparam		= ax88772_ethtool_set_pauseparam,
34262306a36Sopenharmony_ci};
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int ax88772_reset(struct usbnet *dev)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct asix_data *data = (struct asix_data *)&dev->data;
34762306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
34862306a36Sopenharmony_ci	int ret;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* Rewrite MAC address */
35162306a36Sopenharmony_ci	ether_addr_copy(data->mac_addr, dev->net->dev_addr);
35262306a36Sopenharmony_ci	ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
35362306a36Sopenharmony_ci			     ETH_ALEN, data->mac_addr, 0);
35462306a36Sopenharmony_ci	if (ret < 0)
35562306a36Sopenharmony_ci		goto out;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
35862306a36Sopenharmony_ci	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
35962306a36Sopenharmony_ci	if (ret < 0)
36062306a36Sopenharmony_ci		goto out;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, 0);
36362306a36Sopenharmony_ci	if (ret < 0)
36462306a36Sopenharmony_ci		goto out;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	phylink_start(priv->phylink);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ciout:
37162306a36Sopenharmony_ci	return ret;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic int ax88772_hw_reset(struct usbnet *dev, int in_pm)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct asix_data *data = (struct asix_data *)&dev->data;
37762306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
37862306a36Sopenharmony_ci	u16 rx_ctl;
37962306a36Sopenharmony_ci	int ret;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 |
38262306a36Sopenharmony_ci			      AX_GPIO_GPO2EN, 5, in_pm);
38362306a36Sopenharmony_ci	if (ret < 0)
38462306a36Sopenharmony_ci		goto out;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, priv->embd_phy,
38762306a36Sopenharmony_ci			     0, 0, NULL, in_pm);
38862306a36Sopenharmony_ci	if (ret < 0) {
38962306a36Sopenharmony_ci		netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
39062306a36Sopenharmony_ci		goto out;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (priv->embd_phy) {
39462306a36Sopenharmony_ci		ret = asix_sw_reset(dev, AX_SWRESET_IPPD, in_pm);
39562306a36Sopenharmony_ci		if (ret < 0)
39662306a36Sopenharmony_ci			goto out;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		usleep_range(10000, 11000);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
40162306a36Sopenharmony_ci		if (ret < 0)
40262306a36Sopenharmony_ci			goto out;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		msleep(60);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL,
40762306a36Sopenharmony_ci				    in_pm);
40862306a36Sopenharmony_ci		if (ret < 0)
40962306a36Sopenharmony_ci			goto out;
41062306a36Sopenharmony_ci	} else {
41162306a36Sopenharmony_ci		ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL,
41262306a36Sopenharmony_ci				    in_pm);
41362306a36Sopenharmony_ci		if (ret < 0)
41462306a36Sopenharmony_ci			goto out;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	msleep(150);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
42062306a36Sopenharmony_ci					   MII_PHYSID1))){
42162306a36Sopenharmony_ci		ret = -EIO;
42262306a36Sopenharmony_ci		goto out;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
42662306a36Sopenharmony_ci	if (ret < 0)
42762306a36Sopenharmony_ci		goto out;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
43062306a36Sopenharmony_ci	if (ret < 0)
43162306a36Sopenharmony_ci		goto out;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
43462306a36Sopenharmony_ci			     AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
43562306a36Sopenharmony_ci			     AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
43662306a36Sopenharmony_ci	if (ret < 0) {
43762306a36Sopenharmony_ci		netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
43862306a36Sopenharmony_ci		goto out;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/* Rewrite MAC address */
44262306a36Sopenharmony_ci	ether_addr_copy(data->mac_addr, dev->net->dev_addr);
44362306a36Sopenharmony_ci	ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
44462306a36Sopenharmony_ci			     ETH_ALEN, data->mac_addr, in_pm);
44562306a36Sopenharmony_ci	if (ret < 0)
44662306a36Sopenharmony_ci		goto out;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
44962306a36Sopenharmony_ci	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
45062306a36Sopenharmony_ci	if (ret < 0)
45162306a36Sopenharmony_ci		goto out;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	rx_ctl = asix_read_rx_ctl(dev, in_pm);
45462306a36Sopenharmony_ci	netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
45562306a36Sopenharmony_ci		   rx_ctl);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	rx_ctl = asix_read_medium_status(dev, in_pm);
45862306a36Sopenharmony_ci	netdev_dbg(dev->net,
45962306a36Sopenharmony_ci		   "Medium Status is 0x%04x after all initializations\n",
46062306a36Sopenharmony_ci		   rx_ctl);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	return 0;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ciout:
46562306a36Sopenharmony_ci	return ret;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic int ax88772a_hw_reset(struct usbnet *dev, int in_pm)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	struct asix_data *data = (struct asix_data *)&dev->data;
47162306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
47262306a36Sopenharmony_ci	u16 rx_ctl, phy14h, phy15h, phy16h;
47362306a36Sopenharmony_ci	int ret;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm);
47662306a36Sopenharmony_ci	if (ret < 0)
47762306a36Sopenharmony_ci		goto out;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, priv->embd_phy |
48062306a36Sopenharmony_ci			     AX_PHYSEL_SSEN, 0, 0, NULL, in_pm);
48162306a36Sopenharmony_ci	if (ret < 0) {
48262306a36Sopenharmony_ci		netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
48362306a36Sopenharmony_ci		goto out;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	usleep_range(10000, 11000);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_IPRL, in_pm);
48862306a36Sopenharmony_ci	if (ret < 0)
48962306a36Sopenharmony_ci		goto out;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	usleep_range(10000, 11000);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
49462306a36Sopenharmony_ci	if (ret < 0)
49562306a36Sopenharmony_ci		goto out;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	msleep(160);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
50062306a36Sopenharmony_ci	if (ret < 0)
50162306a36Sopenharmony_ci		goto out;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
50462306a36Sopenharmony_ci	if (ret < 0)
50562306a36Sopenharmony_ci		goto out;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	msleep(200);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
51062306a36Sopenharmony_ci					   MII_PHYSID1))) {
51162306a36Sopenharmony_ci		ret = -1;
51262306a36Sopenharmony_ci		goto out;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (priv->chipcode == AX_AX88772B_CHIPCODE) {
51662306a36Sopenharmony_ci		ret = asix_write_cmd(dev, AX_QCTCTRL, 0x8000, 0x8001,
51762306a36Sopenharmony_ci				     0, NULL, in_pm);
51862306a36Sopenharmony_ci		if (ret < 0) {
51962306a36Sopenharmony_ci			netdev_dbg(dev->net, "Write BQ setting failed: %d\n",
52062306a36Sopenharmony_ci				   ret);
52162306a36Sopenharmony_ci			goto out;
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci	} else if (priv->chipcode == AX_AX88772A_CHIPCODE) {
52462306a36Sopenharmony_ci		/* Check if the PHY registers have default settings */
52562306a36Sopenharmony_ci		phy14h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
52662306a36Sopenharmony_ci					     AX88772A_PHY14H);
52762306a36Sopenharmony_ci		phy15h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
52862306a36Sopenharmony_ci					     AX88772A_PHY15H);
52962306a36Sopenharmony_ci		phy16h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
53062306a36Sopenharmony_ci					     AX88772A_PHY16H);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		netdev_dbg(dev->net,
53362306a36Sopenharmony_ci			   "772a_hw_reset: MR20=0x%x MR21=0x%x MR22=0x%x\n",
53462306a36Sopenharmony_ci			   phy14h, phy15h, phy16h);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		/* Restore PHY registers default setting if not */
53762306a36Sopenharmony_ci		if (phy14h != AX88772A_PHY14H_DEFAULT)
53862306a36Sopenharmony_ci			asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
53962306a36Sopenharmony_ci					     AX88772A_PHY14H,
54062306a36Sopenharmony_ci					     AX88772A_PHY14H_DEFAULT);
54162306a36Sopenharmony_ci		if (phy15h != AX88772A_PHY15H_DEFAULT)
54262306a36Sopenharmony_ci			asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
54362306a36Sopenharmony_ci					     AX88772A_PHY15H,
54462306a36Sopenharmony_ci					     AX88772A_PHY15H_DEFAULT);
54562306a36Sopenharmony_ci		if (phy16h != AX88772A_PHY16H_DEFAULT)
54662306a36Sopenharmony_ci			asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
54762306a36Sopenharmony_ci					     AX88772A_PHY16H,
54862306a36Sopenharmony_ci					     AX88772A_PHY16H_DEFAULT);
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
55262306a36Sopenharmony_ci				AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
55362306a36Sopenharmony_ci				AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
55462306a36Sopenharmony_ci	if (ret < 0) {
55562306a36Sopenharmony_ci		netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
55662306a36Sopenharmony_ci		goto out;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* Rewrite MAC address */
56062306a36Sopenharmony_ci	memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
56162306a36Sopenharmony_ci	ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
56262306a36Sopenharmony_ci							data->mac_addr, in_pm);
56362306a36Sopenharmony_ci	if (ret < 0)
56462306a36Sopenharmony_ci		goto out;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
56762306a36Sopenharmony_ci	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
56862306a36Sopenharmony_ci	if (ret < 0)
56962306a36Sopenharmony_ci		goto out;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
57262306a36Sopenharmony_ci	if (ret < 0)
57362306a36Sopenharmony_ci		return ret;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
57662306a36Sopenharmony_ci	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
57762306a36Sopenharmony_ci	if (ret < 0)
57862306a36Sopenharmony_ci		goto out;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	rx_ctl = asix_read_rx_ctl(dev, in_pm);
58162306a36Sopenharmony_ci	netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
58262306a36Sopenharmony_ci		   rx_ctl);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	rx_ctl = asix_read_medium_status(dev, in_pm);
58562306a36Sopenharmony_ci	netdev_dbg(dev->net,
58662306a36Sopenharmony_ci		   "Medium Status is 0x%04x after all initializations\n",
58762306a36Sopenharmony_ci		   rx_ctl);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	return 0;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ciout:
59262306a36Sopenharmony_ci	return ret;
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic const struct net_device_ops ax88772_netdev_ops = {
59662306a36Sopenharmony_ci	.ndo_open		= usbnet_open,
59762306a36Sopenharmony_ci	.ndo_stop		= usbnet_stop,
59862306a36Sopenharmony_ci	.ndo_start_xmit		= usbnet_start_xmit,
59962306a36Sopenharmony_ci	.ndo_tx_timeout		= usbnet_tx_timeout,
60062306a36Sopenharmony_ci	.ndo_change_mtu		= usbnet_change_mtu,
60162306a36Sopenharmony_ci	.ndo_get_stats64	= dev_get_tstats64,
60262306a36Sopenharmony_ci	.ndo_set_mac_address 	= asix_set_mac_address,
60362306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
60462306a36Sopenharmony_ci	.ndo_eth_ioctl		= phy_do_ioctl_running,
60562306a36Sopenharmony_ci	.ndo_set_rx_mode        = asix_set_multicast,
60662306a36Sopenharmony_ci};
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic void ax88772_suspend(struct usbnet *dev)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
61162306a36Sopenharmony_ci	u16 medium;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	if (netif_running(dev->net)) {
61462306a36Sopenharmony_ci		rtnl_lock();
61562306a36Sopenharmony_ci		phylink_suspend(priv->phylink, false);
61662306a36Sopenharmony_ci		rtnl_unlock();
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/* Stop MAC operation */
62062306a36Sopenharmony_ci	medium = asix_read_medium_status(dev, 1);
62162306a36Sopenharmony_ci	medium &= ~AX_MEDIUM_RE;
62262306a36Sopenharmony_ci	asix_write_medium_mode(dev, medium, 1);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	netdev_dbg(dev->net, "ax88772_suspend: medium=0x%04x\n",
62562306a36Sopenharmony_ci		   asix_read_medium_status(dev, 1));
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic int asix_suspend(struct usb_interface *intf, pm_message_t message)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	struct usbnet *dev = usb_get_intfdata(intf);
63162306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (priv && priv->suspend)
63462306a36Sopenharmony_ci		priv->suspend(dev);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return usbnet_suspend(intf, message);
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic void ax88772_resume(struct usbnet *dev)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
64262306a36Sopenharmony_ci	int i;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
64562306a36Sopenharmony_ci		if (!priv->reset(dev, 1))
64662306a36Sopenharmony_ci			break;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (netif_running(dev->net)) {
64962306a36Sopenharmony_ci		rtnl_lock();
65062306a36Sopenharmony_ci		phylink_resume(priv->phylink);
65162306a36Sopenharmony_ci		rtnl_unlock();
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic int asix_resume(struct usb_interface *intf)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct usbnet *dev = usb_get_intfdata(intf);
65862306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (priv && priv->resume)
66162306a36Sopenharmony_ci		priv->resume(dev);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	return usbnet_resume(intf);
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic int ax88772_init_mdio(struct usbnet *dev)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
66962306a36Sopenharmony_ci	int ret;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	priv->mdio = mdiobus_alloc();
67262306a36Sopenharmony_ci	if (!priv->mdio)
67362306a36Sopenharmony_ci		return -ENOMEM;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	priv->mdio->priv = dev;
67662306a36Sopenharmony_ci	priv->mdio->read = &asix_mdio_bus_read;
67762306a36Sopenharmony_ci	priv->mdio->write = &asix_mdio_bus_write;
67862306a36Sopenharmony_ci	priv->mdio->name = "Asix MDIO Bus";
67962306a36Sopenharmony_ci	/* mii bus name is usb-<usb bus number>-<usb device number> */
68062306a36Sopenharmony_ci	snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
68162306a36Sopenharmony_ci		 dev->udev->bus->busnum, dev->udev->devnum);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	ret = mdiobus_register(priv->mdio);
68462306a36Sopenharmony_ci	if (ret) {
68562306a36Sopenharmony_ci		netdev_err(dev->net, "Could not register MDIO bus (err %d)\n", ret);
68662306a36Sopenharmony_ci		mdiobus_free(priv->mdio);
68762306a36Sopenharmony_ci		priv->mdio = NULL;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return ret;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic void ax88772_mdio_unregister(struct asix_common_private *priv)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	mdiobus_unregister(priv->mdio);
69662306a36Sopenharmony_ci	mdiobus_free(priv->mdio);
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic int ax88772_init_phy(struct usbnet *dev)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
70262306a36Sopenharmony_ci	int ret;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	priv->phydev = mdiobus_get_phy(priv->mdio, priv->phy_addr);
70562306a36Sopenharmony_ci	if (!priv->phydev) {
70662306a36Sopenharmony_ci		netdev_err(dev->net, "Could not find PHY\n");
70762306a36Sopenharmony_ci		return -ENODEV;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	ret = phylink_connect_phy(priv->phylink, priv->phydev);
71162306a36Sopenharmony_ci	if (ret) {
71262306a36Sopenharmony_ci		netdev_err(dev->net, "Could not connect PHY\n");
71362306a36Sopenharmony_ci		return ret;
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	phy_suspend(priv->phydev);
71762306a36Sopenharmony_ci	priv->phydev->mac_managed_pm = true;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	phy_attached_info(priv->phydev);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	if (priv->embd_phy)
72262306a36Sopenharmony_ci		return 0;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	/* In case main PHY is not the embedded PHY and MAC is RMII clock
72562306a36Sopenharmony_ci	 * provider, we need to suspend embedded PHY by keeping PLL enabled
72662306a36Sopenharmony_ci	 * (AX_SWRESET_IPPD == 0).
72762306a36Sopenharmony_ci	 */
72862306a36Sopenharmony_ci	priv->phydev_int = mdiobus_get_phy(priv->mdio, AX_EMBD_PHY_ADDR);
72962306a36Sopenharmony_ci	if (!priv->phydev_int) {
73062306a36Sopenharmony_ci		rtnl_lock();
73162306a36Sopenharmony_ci		phylink_disconnect_phy(priv->phylink);
73262306a36Sopenharmony_ci		rtnl_unlock();
73362306a36Sopenharmony_ci		netdev_err(dev->net, "Could not find internal PHY\n");
73462306a36Sopenharmony_ci		return -ENODEV;
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	priv->phydev_int->mac_managed_pm = true;
73862306a36Sopenharmony_ci	phy_suspend(priv->phydev_int);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	return 0;
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cistatic void ax88772_mac_config(struct phylink_config *config, unsigned int mode,
74462306a36Sopenharmony_ci			      const struct phylink_link_state *state)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	/* Nothing to do */
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic void ax88772_mac_link_down(struct phylink_config *config,
75062306a36Sopenharmony_ci				 unsigned int mode, phy_interface_t interface)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	asix_write_medium_mode(dev, 0, 0);
75562306a36Sopenharmony_ci	usbnet_link_change(dev, false, false);
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cistatic void ax88772_mac_link_up(struct phylink_config *config,
75962306a36Sopenharmony_ci			       struct phy_device *phy,
76062306a36Sopenharmony_ci			       unsigned int mode, phy_interface_t interface,
76162306a36Sopenharmony_ci			       int speed, int duplex,
76262306a36Sopenharmony_ci			       bool tx_pause, bool rx_pause)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
76562306a36Sopenharmony_ci	u16 m = AX_MEDIUM_AC | AX_MEDIUM_RE;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	m |= duplex ? AX_MEDIUM_FD : 0;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	switch (speed) {
77062306a36Sopenharmony_ci	case SPEED_100:
77162306a36Sopenharmony_ci		m |= AX_MEDIUM_PS;
77262306a36Sopenharmony_ci		break;
77362306a36Sopenharmony_ci	case SPEED_10:
77462306a36Sopenharmony_ci		break;
77562306a36Sopenharmony_ci	default:
77662306a36Sopenharmony_ci		return;
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (tx_pause)
78062306a36Sopenharmony_ci		m |= AX_MEDIUM_TFC;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (rx_pause)
78362306a36Sopenharmony_ci		m |= AX_MEDIUM_RFC;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	asix_write_medium_mode(dev, m, 0);
78662306a36Sopenharmony_ci	usbnet_link_change(dev, true, false);
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic const struct phylink_mac_ops ax88772_phylink_mac_ops = {
79062306a36Sopenharmony_ci	.mac_config = ax88772_mac_config,
79162306a36Sopenharmony_ci	.mac_link_down = ax88772_mac_link_down,
79262306a36Sopenharmony_ci	.mac_link_up = ax88772_mac_link_up,
79362306a36Sopenharmony_ci};
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic int ax88772_phylink_setup(struct usbnet *dev)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
79862306a36Sopenharmony_ci	phy_interface_t phy_if_mode;
79962306a36Sopenharmony_ci	struct phylink *phylink;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	priv->phylink_config.dev = &dev->net->dev;
80262306a36Sopenharmony_ci	priv->phylink_config.type = PHYLINK_NETDEV;
80362306a36Sopenharmony_ci	priv->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
80462306a36Sopenharmony_ci		MAC_10 | MAC_100;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	__set_bit(PHY_INTERFACE_MODE_INTERNAL,
80762306a36Sopenharmony_ci		  priv->phylink_config.supported_interfaces);
80862306a36Sopenharmony_ci	__set_bit(PHY_INTERFACE_MODE_RMII,
80962306a36Sopenharmony_ci		  priv->phylink_config.supported_interfaces);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (priv->embd_phy)
81262306a36Sopenharmony_ci		phy_if_mode = PHY_INTERFACE_MODE_INTERNAL;
81362306a36Sopenharmony_ci	else
81462306a36Sopenharmony_ci		phy_if_mode = PHY_INTERFACE_MODE_RMII;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	phylink = phylink_create(&priv->phylink_config, dev->net->dev.fwnode,
81762306a36Sopenharmony_ci				 phy_if_mode, &ax88772_phylink_mac_ops);
81862306a36Sopenharmony_ci	if (IS_ERR(phylink))
81962306a36Sopenharmony_ci		return PTR_ERR(phylink);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	priv->phylink = phylink;
82262306a36Sopenharmony_ci	return 0;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct asix_common_private *priv;
82862306a36Sopenharmony_ci	u8 buf[ETH_ALEN] = {0};
82962306a36Sopenharmony_ci	int ret, i;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	priv = devm_kzalloc(&dev->udev->dev, sizeof(*priv), GFP_KERNEL);
83262306a36Sopenharmony_ci	if (!priv)
83362306a36Sopenharmony_ci		return -ENOMEM;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	dev->driver_priv = priv;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	usbnet_get_endpoints(dev, intf);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	/* Maybe the boot loader passed the MAC address via device tree */
84062306a36Sopenharmony_ci	if (!eth_platform_get_mac_address(&dev->udev->dev, buf)) {
84162306a36Sopenharmony_ci		netif_dbg(dev, ifup, dev->net,
84262306a36Sopenharmony_ci			  "MAC address read from device tree");
84362306a36Sopenharmony_ci	} else {
84462306a36Sopenharmony_ci		/* Try getting the MAC address from EEPROM */
84562306a36Sopenharmony_ci		if (dev->driver_info->data & FLAG_EEPROM_MAC) {
84662306a36Sopenharmony_ci			for (i = 0; i < (ETH_ALEN >> 1); i++) {
84762306a36Sopenharmony_ci				ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM,
84862306a36Sopenharmony_ci						    0x04 + i, 0, 2, buf + i * 2,
84962306a36Sopenharmony_ci						    0);
85062306a36Sopenharmony_ci				if (ret < 0)
85162306a36Sopenharmony_ci					break;
85262306a36Sopenharmony_ci			}
85362306a36Sopenharmony_ci		} else {
85462306a36Sopenharmony_ci			ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
85562306a36Sopenharmony_ci					    0, 0, ETH_ALEN, buf, 0);
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		if (ret < 0) {
85962306a36Sopenharmony_ci			netdev_dbg(dev->net, "Failed to read MAC address: %d\n",
86062306a36Sopenharmony_ci				   ret);
86162306a36Sopenharmony_ci			return ret;
86262306a36Sopenharmony_ci		}
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	asix_set_netdev_dev_addr(dev, buf);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	dev->net->netdev_ops = &ax88772_netdev_ops;
86862306a36Sopenharmony_ci	dev->net->ethtool_ops = &ax88772_ethtool_ops;
86962306a36Sopenharmony_ci	dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */
87062306a36Sopenharmony_ci	dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	ret = asix_read_phy_addr(dev, true);
87362306a36Sopenharmony_ci	if (ret < 0)
87462306a36Sopenharmony_ci		return ret;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	priv->phy_addr = ret;
87762306a36Sopenharmony_ci	priv->embd_phy = ((priv->phy_addr & 0x1f) == AX_EMBD_PHY_ADDR);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1,
88062306a36Sopenharmony_ci			    &priv->chipcode, 0);
88162306a36Sopenharmony_ci	if (ret < 0) {
88262306a36Sopenharmony_ci		netdev_dbg(dev->net, "Failed to read STATMNGSTS_REG: %d\n", ret);
88362306a36Sopenharmony_ci		return ret;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	priv->chipcode &= AX_CHIPCODE_MASK;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	priv->resume = ax88772_resume;
88962306a36Sopenharmony_ci	priv->suspend = ax88772_suspend;
89062306a36Sopenharmony_ci	if (priv->chipcode == AX_AX88772_CHIPCODE)
89162306a36Sopenharmony_ci		priv->reset = ax88772_hw_reset;
89262306a36Sopenharmony_ci	else
89362306a36Sopenharmony_ci		priv->reset = ax88772a_hw_reset;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	ret = priv->reset(dev, 0);
89662306a36Sopenharmony_ci	if (ret < 0) {
89762306a36Sopenharmony_ci		netdev_dbg(dev->net, "Failed to reset AX88772: %d\n", ret);
89862306a36Sopenharmony_ci		return ret;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
90262306a36Sopenharmony_ci	if (dev->driver_info->flags & FLAG_FRAMING_AX) {
90362306a36Sopenharmony_ci		/* hard_mtu  is still the default - the device does not support
90462306a36Sopenharmony_ci		   jumbo eth frames */
90562306a36Sopenharmony_ci		dev->rx_urb_size = 2048;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	priv->presvd_phy_bmcr = 0;
90962306a36Sopenharmony_ci	priv->presvd_phy_advertise = 0;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	ret = ax88772_init_mdio(dev);
91262306a36Sopenharmony_ci	if (ret)
91362306a36Sopenharmony_ci		goto mdio_err;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	ret = ax88772_phylink_setup(dev);
91662306a36Sopenharmony_ci	if (ret)
91762306a36Sopenharmony_ci		goto phylink_err;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	ret = ax88772_init_phy(dev);
92062306a36Sopenharmony_ci	if (ret)
92162306a36Sopenharmony_ci		goto initphy_err;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	return 0;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ciinitphy_err:
92662306a36Sopenharmony_ci	phylink_destroy(priv->phylink);
92762306a36Sopenharmony_ciphylink_err:
92862306a36Sopenharmony_ci	ax88772_mdio_unregister(priv);
92962306a36Sopenharmony_cimdio_err:
93062306a36Sopenharmony_ci	return ret;
93162306a36Sopenharmony_ci}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_cistatic int ax88772_stop(struct usbnet *dev)
93462306a36Sopenharmony_ci{
93562306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	phylink_stop(priv->phylink);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	return 0;
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistatic void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct asix_common_private *priv = dev->driver_priv;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	rtnl_lock();
94762306a36Sopenharmony_ci	phylink_disconnect_phy(priv->phylink);
94862306a36Sopenharmony_ci	rtnl_unlock();
94962306a36Sopenharmony_ci	phylink_destroy(priv->phylink);
95062306a36Sopenharmony_ci	ax88772_mdio_unregister(priv);
95162306a36Sopenharmony_ci	asix_rx_fixup_common_free(dev->driver_priv);
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cistatic void ax88178_unbind(struct usbnet *dev, struct usb_interface *intf)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	asix_rx_fixup_common_free(dev->driver_priv);
95762306a36Sopenharmony_ci	kfree(dev->driver_priv);
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic const struct ethtool_ops ax88178_ethtool_ops = {
96162306a36Sopenharmony_ci	.get_drvinfo		= asix_get_drvinfo,
96262306a36Sopenharmony_ci	.get_link		= asix_get_link,
96362306a36Sopenharmony_ci	.get_msglevel		= usbnet_get_msglevel,
96462306a36Sopenharmony_ci	.set_msglevel		= usbnet_set_msglevel,
96562306a36Sopenharmony_ci	.get_wol		= asix_get_wol,
96662306a36Sopenharmony_ci	.set_wol		= asix_set_wol,
96762306a36Sopenharmony_ci	.get_eeprom_len		= asix_get_eeprom_len,
96862306a36Sopenharmony_ci	.get_eeprom		= asix_get_eeprom,
96962306a36Sopenharmony_ci	.set_eeprom		= asix_set_eeprom,
97062306a36Sopenharmony_ci	.nway_reset		= usbnet_nway_reset,
97162306a36Sopenharmony_ci	.get_link_ksettings	= usbnet_get_link_ksettings_mii,
97262306a36Sopenharmony_ci	.set_link_ksettings	= usbnet_set_link_ksettings_mii,
97362306a36Sopenharmony_ci};
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic int marvell_phy_init(struct usbnet *dev)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct asix_data *data = (struct asix_data *)&dev->data;
97862306a36Sopenharmony_ci	u16 reg;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	netdev_dbg(dev->net, "marvell_phy_init()\n");
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_MARVELL_STATUS);
98362306a36Sopenharmony_ci	netdev_dbg(dev->net, "MII_MARVELL_STATUS = 0x%04x\n", reg);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	asix_mdio_write(dev->net, dev->mii.phy_id, MII_MARVELL_CTRL,
98662306a36Sopenharmony_ci			MARVELL_CTRL_RXDELAY | MARVELL_CTRL_TXDELAY);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	if (data->ledmode) {
98962306a36Sopenharmony_ci		reg = asix_mdio_read(dev->net, dev->mii.phy_id,
99062306a36Sopenharmony_ci			MII_MARVELL_LED_CTRL);
99162306a36Sopenharmony_ci		netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (1) = 0x%04x\n", reg);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci		reg &= 0xf8ff;
99462306a36Sopenharmony_ci		reg |= (1 + 0x0100);
99562306a36Sopenharmony_ci		asix_mdio_write(dev->net, dev->mii.phy_id,
99662306a36Sopenharmony_ci			MII_MARVELL_LED_CTRL, reg);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		reg = asix_mdio_read(dev->net, dev->mii.phy_id,
99962306a36Sopenharmony_ci			MII_MARVELL_LED_CTRL);
100062306a36Sopenharmony_ci		netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (2) = 0x%04x\n", reg);
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	return 0;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic int rtl8211cl_phy_init(struct usbnet *dev)
100762306a36Sopenharmony_ci{
100862306a36Sopenharmony_ci	struct asix_data *data = (struct asix_data *)&dev->data;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	netdev_dbg(dev->net, "rtl8211cl_phy_init()\n");
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0005);
101362306a36Sopenharmony_ci	asix_mdio_write (dev->net, dev->mii.phy_id, 0x0c, 0);
101462306a36Sopenharmony_ci	asix_mdio_write (dev->net, dev->mii.phy_id, 0x01,
101562306a36Sopenharmony_ci		asix_mdio_read (dev->net, dev->mii.phy_id, 0x01) | 0x0080);
101662306a36Sopenharmony_ci	asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	if (data->ledmode == 12) {
101962306a36Sopenharmony_ci		asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0002);
102062306a36Sopenharmony_ci		asix_mdio_write (dev->net, dev->mii.phy_id, 0x1a, 0x00cb);
102162306a36Sopenharmony_ci		asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0);
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	return 0;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic int marvell_led_status(struct usbnet *dev, u16 speed)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	u16 reg = asix_mdio_read(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL);
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	netdev_dbg(dev->net, "marvell_led_status() read 0x%04x\n", reg);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/* Clear out the center LED bits - 0x03F0 */
103462306a36Sopenharmony_ci	reg &= 0xfc0f;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	switch (speed) {
103762306a36Sopenharmony_ci		case SPEED_1000:
103862306a36Sopenharmony_ci			reg |= 0x03e0;
103962306a36Sopenharmony_ci			break;
104062306a36Sopenharmony_ci		case SPEED_100:
104162306a36Sopenharmony_ci			reg |= 0x03b0;
104262306a36Sopenharmony_ci			break;
104362306a36Sopenharmony_ci		default:
104462306a36Sopenharmony_ci			reg |= 0x02f0;
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	netdev_dbg(dev->net, "marvell_led_status() writing 0x%04x\n", reg);
104862306a36Sopenharmony_ci	asix_mdio_write(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL, reg);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	return 0;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic int ax88178_reset(struct usbnet *dev)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	struct asix_data *data = (struct asix_data *)&dev->data;
105662306a36Sopenharmony_ci	int ret;
105762306a36Sopenharmony_ci	__le16 eeprom;
105862306a36Sopenharmony_ci	u8 status;
105962306a36Sopenharmony_ci	int gpio0 = 0;
106062306a36Sopenharmony_ci	u32 phyid;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	ret = asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status, 0);
106362306a36Sopenharmony_ci	if (ret < 0) {
106462306a36Sopenharmony_ci		netdev_dbg(dev->net, "Failed to read GPIOS: %d\n", ret);
106562306a36Sopenharmony_ci		return ret;
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL, 0);
107162306a36Sopenharmony_ci	ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom, 0);
107262306a36Sopenharmony_ci	if (ret < 0) {
107362306a36Sopenharmony_ci		netdev_dbg(dev->net, "Failed to read EEPROM: %d\n", ret);
107462306a36Sopenharmony_ci		return ret;
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL, 0);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	netdev_dbg(dev->net, "EEPROM index 0x17 is 0x%04x\n", eeprom);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (eeprom == cpu_to_le16(0xffff)) {
108262306a36Sopenharmony_ci		data->phymode = PHY_MODE_MARVELL;
108362306a36Sopenharmony_ci		data->ledmode = 0;
108462306a36Sopenharmony_ci		gpio0 = 1;
108562306a36Sopenharmony_ci	} else {
108662306a36Sopenharmony_ci		data->phymode = le16_to_cpu(eeprom) & 0x7F;
108762306a36Sopenharmony_ci		data->ledmode = le16_to_cpu(eeprom) >> 8;
108862306a36Sopenharmony_ci		gpio0 = (le16_to_cpu(eeprom) & 0x80) ? 0 : 1;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci	netdev_dbg(dev->net, "GPIO0: %d, PhyMode: %d\n", gpio0, data->phymode);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	/* Power up external GigaPHY through AX88178 GPIO pin */
109362306a36Sopenharmony_ci	asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 |
109462306a36Sopenharmony_ci			AX_GPIO_GPO1EN, 40, 0);
109562306a36Sopenharmony_ci	if ((le16_to_cpu(eeprom) >> 8) != 1) {
109662306a36Sopenharmony_ci		asix_write_gpio(dev, 0x003c, 30, 0);
109762306a36Sopenharmony_ci		asix_write_gpio(dev, 0x001c, 300, 0);
109862306a36Sopenharmony_ci		asix_write_gpio(dev, 0x003c, 30, 0);
109962306a36Sopenharmony_ci	} else {
110062306a36Sopenharmony_ci		netdev_dbg(dev->net, "gpio phymode == 1 path\n");
110162306a36Sopenharmony_ci		asix_write_gpio(dev, AX_GPIO_GPO1EN, 30, 0);
110262306a36Sopenharmony_ci		asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30, 0);
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	/* Read PHYID register *AFTER* powering up PHY */
110662306a36Sopenharmony_ci	phyid = asix_get_phyid(dev);
110762306a36Sopenharmony_ci	netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	/* Set AX88178 to enable MII/GMII/RGMII interface for external PHY */
111062306a36Sopenharmony_ci	asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL, 0);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	asix_sw_reset(dev, 0, 0);
111362306a36Sopenharmony_ci	msleep(150);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
111662306a36Sopenharmony_ci	msleep(150);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	asix_write_rx_ctl(dev, 0, 0);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (data->phymode == PHY_MODE_MARVELL) {
112162306a36Sopenharmony_ci		marvell_phy_init(dev);
112262306a36Sopenharmony_ci		msleep(60);
112362306a36Sopenharmony_ci	} else if (data->phymode == PHY_MODE_RTL8211CL)
112462306a36Sopenharmony_ci		rtl8211cl_phy_init(dev);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	asix_phy_reset(dev, BMCR_RESET | BMCR_ANENABLE);
112762306a36Sopenharmony_ci	asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
112862306a36Sopenharmony_ci			ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
112962306a36Sopenharmony_ci	asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000,
113062306a36Sopenharmony_ci			ADVERTISE_1000FULL);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT, 0);
113362306a36Sopenharmony_ci	mii_nway_restart(&dev->mii);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	/* Rewrite MAC address */
113662306a36Sopenharmony_ci	memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
113762306a36Sopenharmony_ci	ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
113862306a36Sopenharmony_ci							data->mac_addr, 0);
113962306a36Sopenharmony_ci	if (ret < 0)
114062306a36Sopenharmony_ci		return ret;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
114362306a36Sopenharmony_ci	if (ret < 0)
114462306a36Sopenharmony_ci		return ret;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	return 0;
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic int ax88178_link_reset(struct usbnet *dev)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	u16 mode;
115262306a36Sopenharmony_ci	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
115362306a36Sopenharmony_ci	struct asix_data *data = (struct asix_data *)&dev->data;
115462306a36Sopenharmony_ci	u32 speed;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	netdev_dbg(dev->net, "ax88178_link_reset()\n");
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	mii_check_media(&dev->mii, 1, 1);
115962306a36Sopenharmony_ci	mii_ethtool_gset(&dev->mii, &ecmd);
116062306a36Sopenharmony_ci	mode = AX88178_MEDIUM_DEFAULT;
116162306a36Sopenharmony_ci	speed = ethtool_cmd_speed(&ecmd);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (speed == SPEED_1000)
116462306a36Sopenharmony_ci		mode |= AX_MEDIUM_GM;
116562306a36Sopenharmony_ci	else if (speed == SPEED_100)
116662306a36Sopenharmony_ci		mode |= AX_MEDIUM_PS;
116762306a36Sopenharmony_ci	else
116862306a36Sopenharmony_ci		mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	mode |= AX_MEDIUM_ENCK;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (ecmd.duplex == DUPLEX_FULL)
117362306a36Sopenharmony_ci		mode |= AX_MEDIUM_FD;
117462306a36Sopenharmony_ci	else
117562306a36Sopenharmony_ci		mode &= ~AX_MEDIUM_FD;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	netdev_dbg(dev->net, "ax88178_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
117862306a36Sopenharmony_ci		   speed, ecmd.duplex, mode);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	asix_write_medium_mode(dev, mode, 0);
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	if (data->phymode == PHY_MODE_MARVELL && data->ledmode)
118362306a36Sopenharmony_ci		marvell_led_status(dev, speed);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	return 0;
118662306a36Sopenharmony_ci}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_cistatic void ax88178_set_mfb(struct usbnet *dev)
118962306a36Sopenharmony_ci{
119062306a36Sopenharmony_ci	u16 mfb = AX_RX_CTL_MFB_16384;
119162306a36Sopenharmony_ci	u16 rxctl;
119262306a36Sopenharmony_ci	u16 medium;
119362306a36Sopenharmony_ci	int old_rx_urb_size = dev->rx_urb_size;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	if (dev->hard_mtu < 2048) {
119662306a36Sopenharmony_ci		dev->rx_urb_size = 2048;
119762306a36Sopenharmony_ci		mfb = AX_RX_CTL_MFB_2048;
119862306a36Sopenharmony_ci	} else if (dev->hard_mtu < 4096) {
119962306a36Sopenharmony_ci		dev->rx_urb_size = 4096;
120062306a36Sopenharmony_ci		mfb = AX_RX_CTL_MFB_4096;
120162306a36Sopenharmony_ci	} else if (dev->hard_mtu < 8192) {
120262306a36Sopenharmony_ci		dev->rx_urb_size = 8192;
120362306a36Sopenharmony_ci		mfb = AX_RX_CTL_MFB_8192;
120462306a36Sopenharmony_ci	} else if (dev->hard_mtu < 16384) {
120562306a36Sopenharmony_ci		dev->rx_urb_size = 16384;
120662306a36Sopenharmony_ci		mfb = AX_RX_CTL_MFB_16384;
120762306a36Sopenharmony_ci	}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	rxctl = asix_read_rx_ctl(dev, 0);
121062306a36Sopenharmony_ci	asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb, 0);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	medium = asix_read_medium_status(dev, 0);
121362306a36Sopenharmony_ci	if (dev->net->mtu > 1500)
121462306a36Sopenharmony_ci		medium |= AX_MEDIUM_JFE;
121562306a36Sopenharmony_ci	else
121662306a36Sopenharmony_ci		medium &= ~AX_MEDIUM_JFE;
121762306a36Sopenharmony_ci	asix_write_medium_mode(dev, medium, 0);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	if (dev->rx_urb_size > old_rx_urb_size)
122062306a36Sopenharmony_ci		usbnet_unlink_rx_urbs(dev);
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic int ax88178_change_mtu(struct net_device *net, int new_mtu)
122462306a36Sopenharmony_ci{
122562306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
122662306a36Sopenharmony_ci	int ll_mtu = new_mtu + net->hard_header_len + 4;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	netdev_dbg(dev->net, "ax88178_change_mtu() new_mtu=%d\n", new_mtu);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	if ((ll_mtu % dev->maxpacket) == 0)
123162306a36Sopenharmony_ci		return -EDOM;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	net->mtu = new_mtu;
123462306a36Sopenharmony_ci	dev->hard_mtu = net->mtu + net->hard_header_len;
123562306a36Sopenharmony_ci	ax88178_set_mfb(dev);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	/* max qlen depend on hard_mtu and rx_urb_size */
123862306a36Sopenharmony_ci	usbnet_update_max_qlen(dev);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	return 0;
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistatic const struct net_device_ops ax88178_netdev_ops = {
124462306a36Sopenharmony_ci	.ndo_open		= usbnet_open,
124562306a36Sopenharmony_ci	.ndo_stop		= usbnet_stop,
124662306a36Sopenharmony_ci	.ndo_start_xmit		= usbnet_start_xmit,
124762306a36Sopenharmony_ci	.ndo_tx_timeout		= usbnet_tx_timeout,
124862306a36Sopenharmony_ci	.ndo_get_stats64	= dev_get_tstats64,
124962306a36Sopenharmony_ci	.ndo_set_mac_address 	= asix_set_mac_address,
125062306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
125162306a36Sopenharmony_ci	.ndo_set_rx_mode	= asix_set_multicast,
125262306a36Sopenharmony_ci	.ndo_eth_ioctl		= asix_ioctl,
125362306a36Sopenharmony_ci	.ndo_change_mtu 	= ax88178_change_mtu,
125462306a36Sopenharmony_ci};
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_cistatic int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
125762306a36Sopenharmony_ci{
125862306a36Sopenharmony_ci	int ret;
125962306a36Sopenharmony_ci	u8 buf[ETH_ALEN] = {0};
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	usbnet_get_endpoints(dev,intf);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	/* Get the MAC address */
126462306a36Sopenharmony_ci	ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
126562306a36Sopenharmony_ci	if (ret < 0) {
126662306a36Sopenharmony_ci		netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret);
126762306a36Sopenharmony_ci		return ret;
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	asix_set_netdev_dev_addr(dev, buf);
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	/* Initialize MII structure */
127362306a36Sopenharmony_ci	dev->mii.dev = dev->net;
127462306a36Sopenharmony_ci	dev->mii.mdio_read = asix_mdio_read;
127562306a36Sopenharmony_ci	dev->mii.mdio_write = asix_mdio_write;
127662306a36Sopenharmony_ci	dev->mii.phy_id_mask = 0x1f;
127762306a36Sopenharmony_ci	dev->mii.reg_num_mask = 0xff;
127862306a36Sopenharmony_ci	dev->mii.supports_gmii = 1;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	dev->mii.phy_id = asix_read_phy_addr(dev, true);
128162306a36Sopenharmony_ci	if (dev->mii.phy_id < 0)
128262306a36Sopenharmony_ci		return dev->mii.phy_id;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	dev->net->netdev_ops = &ax88178_netdev_ops;
128562306a36Sopenharmony_ci	dev->net->ethtool_ops = &ax88178_ethtool_ops;
128662306a36Sopenharmony_ci	dev->net->max_mtu = 16384 - (dev->net->hard_header_len + 4);
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	/* Blink LEDS so users know driver saw dongle */
128962306a36Sopenharmony_ci	asix_sw_reset(dev, 0, 0);
129062306a36Sopenharmony_ci	msleep(150);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
129362306a36Sopenharmony_ci	msleep(150);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
129662306a36Sopenharmony_ci	if (dev->driver_info->flags & FLAG_FRAMING_AX) {
129762306a36Sopenharmony_ci		/* hard_mtu  is still the default - the device does not support
129862306a36Sopenharmony_ci		   jumbo eth frames */
129962306a36Sopenharmony_ci		dev->rx_urb_size = 2048;
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL);
130362306a36Sopenharmony_ci	if (!dev->driver_priv)
130462306a36Sopenharmony_ci			return -ENOMEM;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	return 0;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic const struct driver_info ax8817x_info = {
131062306a36Sopenharmony_ci	.description = "ASIX AX8817x USB 2.0 Ethernet",
131162306a36Sopenharmony_ci	.bind = ax88172_bind,
131262306a36Sopenharmony_ci	.status = asix_status,
131362306a36Sopenharmony_ci	.link_reset = ax88172_link_reset,
131462306a36Sopenharmony_ci	.reset = ax88172_link_reset,
131562306a36Sopenharmony_ci	.flags =  FLAG_ETHER | FLAG_LINK_INTR,
131662306a36Sopenharmony_ci	.data = 0x00130103,
131762306a36Sopenharmony_ci};
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic const struct driver_info dlink_dub_e100_info = {
132062306a36Sopenharmony_ci	.description = "DLink DUB-E100 USB Ethernet",
132162306a36Sopenharmony_ci	.bind = ax88172_bind,
132262306a36Sopenharmony_ci	.status = asix_status,
132362306a36Sopenharmony_ci	.link_reset = ax88172_link_reset,
132462306a36Sopenharmony_ci	.reset = ax88172_link_reset,
132562306a36Sopenharmony_ci	.flags =  FLAG_ETHER | FLAG_LINK_INTR,
132662306a36Sopenharmony_ci	.data = 0x009f9d9f,
132762306a36Sopenharmony_ci};
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cistatic const struct driver_info netgear_fa120_info = {
133062306a36Sopenharmony_ci	.description = "Netgear FA-120 USB Ethernet",
133162306a36Sopenharmony_ci	.bind = ax88172_bind,
133262306a36Sopenharmony_ci	.status = asix_status,
133362306a36Sopenharmony_ci	.link_reset = ax88172_link_reset,
133462306a36Sopenharmony_ci	.reset = ax88172_link_reset,
133562306a36Sopenharmony_ci	.flags =  FLAG_ETHER | FLAG_LINK_INTR,
133662306a36Sopenharmony_ci	.data = 0x00130103,
133762306a36Sopenharmony_ci};
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_cistatic const struct driver_info hawking_uf200_info = {
134062306a36Sopenharmony_ci	.description = "Hawking UF200 USB Ethernet",
134162306a36Sopenharmony_ci	.bind = ax88172_bind,
134262306a36Sopenharmony_ci	.status = asix_status,
134362306a36Sopenharmony_ci	.link_reset = ax88172_link_reset,
134462306a36Sopenharmony_ci	.reset = ax88172_link_reset,
134562306a36Sopenharmony_ci	.flags =  FLAG_ETHER | FLAG_LINK_INTR,
134662306a36Sopenharmony_ci	.data = 0x001f1d1f,
134762306a36Sopenharmony_ci};
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic const struct driver_info ax88772_info = {
135062306a36Sopenharmony_ci	.description = "ASIX AX88772 USB 2.0 Ethernet",
135162306a36Sopenharmony_ci	.bind = ax88772_bind,
135262306a36Sopenharmony_ci	.unbind = ax88772_unbind,
135362306a36Sopenharmony_ci	.status = asix_status,
135462306a36Sopenharmony_ci	.reset = ax88772_reset,
135562306a36Sopenharmony_ci	.stop = ax88772_stop,
135662306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET,
135762306a36Sopenharmony_ci	.rx_fixup = asix_rx_fixup_common,
135862306a36Sopenharmony_ci	.tx_fixup = asix_tx_fixup,
135962306a36Sopenharmony_ci};
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_cistatic const struct driver_info ax88772b_info = {
136262306a36Sopenharmony_ci	.description = "ASIX AX88772B USB 2.0 Ethernet",
136362306a36Sopenharmony_ci	.bind = ax88772_bind,
136462306a36Sopenharmony_ci	.unbind = ax88772_unbind,
136562306a36Sopenharmony_ci	.status = asix_status,
136662306a36Sopenharmony_ci	.reset = ax88772_reset,
136762306a36Sopenharmony_ci	.stop = ax88772_stop,
136862306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
136962306a36Sopenharmony_ci	         FLAG_MULTI_PACKET,
137062306a36Sopenharmony_ci	.rx_fixup = asix_rx_fixup_common,
137162306a36Sopenharmony_ci	.tx_fixup = asix_tx_fixup,
137262306a36Sopenharmony_ci	.data = FLAG_EEPROM_MAC,
137362306a36Sopenharmony_ci};
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_cistatic const struct driver_info lxausb_t1l_info = {
137662306a36Sopenharmony_ci	.description = "Linux Automation GmbH USB 10Base-T1L",
137762306a36Sopenharmony_ci	.bind = ax88772_bind,
137862306a36Sopenharmony_ci	.unbind = ax88772_unbind,
137962306a36Sopenharmony_ci	.status = asix_status,
138062306a36Sopenharmony_ci	.reset = ax88772_reset,
138162306a36Sopenharmony_ci	.stop = ax88772_stop,
138262306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
138362306a36Sopenharmony_ci		 FLAG_MULTI_PACKET,
138462306a36Sopenharmony_ci	.rx_fixup = asix_rx_fixup_common,
138562306a36Sopenharmony_ci	.tx_fixup = asix_tx_fixup,
138662306a36Sopenharmony_ci	.data = FLAG_EEPROM_MAC,
138762306a36Sopenharmony_ci};
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_cistatic const struct driver_info ax88178_info = {
139062306a36Sopenharmony_ci	.description = "ASIX AX88178 USB 2.0 Ethernet",
139162306a36Sopenharmony_ci	.bind = ax88178_bind,
139262306a36Sopenharmony_ci	.unbind = ax88178_unbind,
139362306a36Sopenharmony_ci	.status = asix_status,
139462306a36Sopenharmony_ci	.link_reset = ax88178_link_reset,
139562306a36Sopenharmony_ci	.reset = ax88178_reset,
139662306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
139762306a36Sopenharmony_ci		 FLAG_MULTI_PACKET,
139862306a36Sopenharmony_ci	.rx_fixup = asix_rx_fixup_common,
139962306a36Sopenharmony_ci	.tx_fixup = asix_tx_fixup,
140062306a36Sopenharmony_ci};
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci/*
140362306a36Sopenharmony_ci * USBLINK 20F9 "USB 2.0 LAN" USB ethernet adapter, typically found in
140462306a36Sopenharmony_ci * no-name packaging.
140562306a36Sopenharmony_ci * USB device strings are:
140662306a36Sopenharmony_ci *   1: Manufacturer: USBLINK
140762306a36Sopenharmony_ci *   2: Product: HG20F9 USB2.0
140862306a36Sopenharmony_ci *   3: Serial: 000003
140962306a36Sopenharmony_ci * Appears to be compatible with Asix 88772B.
141062306a36Sopenharmony_ci */
141162306a36Sopenharmony_cistatic const struct driver_info hg20f9_info = {
141262306a36Sopenharmony_ci	.description = "HG20F9 USB 2.0 Ethernet",
141362306a36Sopenharmony_ci	.bind = ax88772_bind,
141462306a36Sopenharmony_ci	.unbind = ax88772_unbind,
141562306a36Sopenharmony_ci	.status = asix_status,
141662306a36Sopenharmony_ci	.reset = ax88772_reset,
141762306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
141862306a36Sopenharmony_ci	         FLAG_MULTI_PACKET,
141962306a36Sopenharmony_ci	.rx_fixup = asix_rx_fixup_common,
142062306a36Sopenharmony_ci	.tx_fixup = asix_tx_fixup,
142162306a36Sopenharmony_ci	.data = FLAG_EEPROM_MAC,
142262306a36Sopenharmony_ci};
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_cistatic const struct usb_device_id	products [] = {
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	// Linksys USB200M
142762306a36Sopenharmony_ci	USB_DEVICE (0x077b, 0x2226),
142862306a36Sopenharmony_ci	.driver_info =	(unsigned long) &ax8817x_info,
142962306a36Sopenharmony_ci}, {
143062306a36Sopenharmony_ci	// Netgear FA120
143162306a36Sopenharmony_ci	USB_DEVICE (0x0846, 0x1040),
143262306a36Sopenharmony_ci	.driver_info =  (unsigned long) &netgear_fa120_info,
143362306a36Sopenharmony_ci}, {
143462306a36Sopenharmony_ci	// DLink DUB-E100
143562306a36Sopenharmony_ci	USB_DEVICE (0x2001, 0x1a00),
143662306a36Sopenharmony_ci	.driver_info =  (unsigned long) &dlink_dub_e100_info,
143762306a36Sopenharmony_ci}, {
143862306a36Sopenharmony_ci	// Intellinet, ST Lab USB Ethernet
143962306a36Sopenharmony_ci	USB_DEVICE (0x0b95, 0x1720),
144062306a36Sopenharmony_ci	.driver_info =  (unsigned long) &ax8817x_info,
144162306a36Sopenharmony_ci}, {
144262306a36Sopenharmony_ci	// Hawking UF200, TrendNet TU2-ET100
144362306a36Sopenharmony_ci	USB_DEVICE (0x07b8, 0x420a),
144462306a36Sopenharmony_ci	.driver_info =  (unsigned long) &hawking_uf200_info,
144562306a36Sopenharmony_ci}, {
144662306a36Sopenharmony_ci	// Billionton Systems, USB2AR
144762306a36Sopenharmony_ci	USB_DEVICE (0x08dd, 0x90ff),
144862306a36Sopenharmony_ci	.driver_info =  (unsigned long) &ax8817x_info,
144962306a36Sopenharmony_ci}, {
145062306a36Sopenharmony_ci	// Billionton Systems, GUSB2AM-1G-B
145162306a36Sopenharmony_ci	USB_DEVICE(0x08dd, 0x0114),
145262306a36Sopenharmony_ci	.driver_info =  (unsigned long) &ax88178_info,
145362306a36Sopenharmony_ci}, {
145462306a36Sopenharmony_ci	// ATEN UC210T
145562306a36Sopenharmony_ci	USB_DEVICE (0x0557, 0x2009),
145662306a36Sopenharmony_ci	.driver_info =  (unsigned long) &ax8817x_info,
145762306a36Sopenharmony_ci}, {
145862306a36Sopenharmony_ci	// Buffalo LUA-U2-KTX
145962306a36Sopenharmony_ci	USB_DEVICE (0x0411, 0x003d),
146062306a36Sopenharmony_ci	.driver_info =  (unsigned long) &ax8817x_info,
146162306a36Sopenharmony_ci}, {
146262306a36Sopenharmony_ci	// Buffalo LUA-U2-GT 10/100/1000
146362306a36Sopenharmony_ci	USB_DEVICE (0x0411, 0x006e),
146462306a36Sopenharmony_ci	.driver_info =  (unsigned long) &ax88178_info,
146562306a36Sopenharmony_ci}, {
146662306a36Sopenharmony_ci	// Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter"
146762306a36Sopenharmony_ci	USB_DEVICE (0x6189, 0x182d),
146862306a36Sopenharmony_ci	.driver_info =  (unsigned long) &ax8817x_info,
146962306a36Sopenharmony_ci}, {
147062306a36Sopenharmony_ci	// Sitecom LN-031 "USB 2.0 10/100/1000 Ethernet adapter"
147162306a36Sopenharmony_ci	USB_DEVICE (0x0df6, 0x0056),
147262306a36Sopenharmony_ci	.driver_info =  (unsigned long) &ax88178_info,
147362306a36Sopenharmony_ci}, {
147462306a36Sopenharmony_ci	// Sitecom LN-028 "USB 2.0 10/100/1000 Ethernet adapter"
147562306a36Sopenharmony_ci	USB_DEVICE (0x0df6, 0x061c),
147662306a36Sopenharmony_ci	.driver_info =  (unsigned long) &ax88178_info,
147762306a36Sopenharmony_ci}, {
147862306a36Sopenharmony_ci	// corega FEther USB2-TX
147962306a36Sopenharmony_ci	USB_DEVICE (0x07aa, 0x0017),
148062306a36Sopenharmony_ci	.driver_info =  (unsigned long) &ax8817x_info,
148162306a36Sopenharmony_ci}, {
148262306a36Sopenharmony_ci	// Surecom EP-1427X-2
148362306a36Sopenharmony_ci	USB_DEVICE (0x1189, 0x0893),
148462306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax8817x_info,
148562306a36Sopenharmony_ci}, {
148662306a36Sopenharmony_ci	// goodway corp usb gwusb2e
148762306a36Sopenharmony_ci	USB_DEVICE (0x1631, 0x6200),
148862306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax8817x_info,
148962306a36Sopenharmony_ci}, {
149062306a36Sopenharmony_ci	// JVC MP-PRX1 Port Replicator
149162306a36Sopenharmony_ci	USB_DEVICE (0x04f1, 0x3008),
149262306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax8817x_info,
149362306a36Sopenharmony_ci}, {
149462306a36Sopenharmony_ci	// Lenovo U2L100P 10/100
149562306a36Sopenharmony_ci	USB_DEVICE (0x17ef, 0x7203),
149662306a36Sopenharmony_ci	.driver_info = (unsigned long)&ax88772b_info,
149762306a36Sopenharmony_ci}, {
149862306a36Sopenharmony_ci	// ASIX AX88772B 10/100
149962306a36Sopenharmony_ci	USB_DEVICE (0x0b95, 0x772b),
150062306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88772b_info,
150162306a36Sopenharmony_ci}, {
150262306a36Sopenharmony_ci	// ASIX AX88772 10/100
150362306a36Sopenharmony_ci	USB_DEVICE (0x0b95, 0x7720),
150462306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88772_info,
150562306a36Sopenharmony_ci}, {
150662306a36Sopenharmony_ci	// ASIX AX88178 10/100/1000
150762306a36Sopenharmony_ci	USB_DEVICE (0x0b95, 0x1780),
150862306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88178_info,
150962306a36Sopenharmony_ci}, {
151062306a36Sopenharmony_ci	// Logitec LAN-GTJ/U2A
151162306a36Sopenharmony_ci	USB_DEVICE (0x0789, 0x0160),
151262306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88178_info,
151362306a36Sopenharmony_ci}, {
151462306a36Sopenharmony_ci	// Linksys USB200M Rev 2
151562306a36Sopenharmony_ci	USB_DEVICE (0x13b1, 0x0018),
151662306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88772_info,
151762306a36Sopenharmony_ci}, {
151862306a36Sopenharmony_ci	// 0Q0 cable ethernet
151962306a36Sopenharmony_ci	USB_DEVICE (0x1557, 0x7720),
152062306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88772_info,
152162306a36Sopenharmony_ci}, {
152262306a36Sopenharmony_ci	// DLink DUB-E100 H/W Ver B1
152362306a36Sopenharmony_ci	USB_DEVICE (0x07d1, 0x3c05),
152462306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88772_info,
152562306a36Sopenharmony_ci}, {
152662306a36Sopenharmony_ci	// DLink DUB-E100 H/W Ver B1 Alternate
152762306a36Sopenharmony_ci	USB_DEVICE (0x2001, 0x3c05),
152862306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88772_info,
152962306a36Sopenharmony_ci}, {
153062306a36Sopenharmony_ci       // DLink DUB-E100 H/W Ver C1
153162306a36Sopenharmony_ci       USB_DEVICE (0x2001, 0x1a02),
153262306a36Sopenharmony_ci       .driver_info = (unsigned long) &ax88772_info,
153362306a36Sopenharmony_ci}, {
153462306a36Sopenharmony_ci	// Linksys USB1000
153562306a36Sopenharmony_ci	USB_DEVICE (0x1737, 0x0039),
153662306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88178_info,
153762306a36Sopenharmony_ci}, {
153862306a36Sopenharmony_ci	// IO-DATA ETG-US2
153962306a36Sopenharmony_ci	USB_DEVICE (0x04bb, 0x0930),
154062306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88178_info,
154162306a36Sopenharmony_ci}, {
154262306a36Sopenharmony_ci	// Belkin F5D5055
154362306a36Sopenharmony_ci	USB_DEVICE(0x050d, 0x5055),
154462306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88178_info,
154562306a36Sopenharmony_ci}, {
154662306a36Sopenharmony_ci	// Apple USB Ethernet Adapter
154762306a36Sopenharmony_ci	USB_DEVICE(0x05ac, 0x1402),
154862306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88772_info,
154962306a36Sopenharmony_ci}, {
155062306a36Sopenharmony_ci	// Cables-to-Go USB Ethernet Adapter
155162306a36Sopenharmony_ci	USB_DEVICE(0x0b95, 0x772a),
155262306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88772_info,
155362306a36Sopenharmony_ci}, {
155462306a36Sopenharmony_ci	// ABOCOM for pci
155562306a36Sopenharmony_ci	USB_DEVICE(0x14ea, 0xab11),
155662306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88178_info,
155762306a36Sopenharmony_ci}, {
155862306a36Sopenharmony_ci	// ASIX 88772a
155962306a36Sopenharmony_ci	USB_DEVICE(0x0db0, 0xa877),
156062306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88772_info,
156162306a36Sopenharmony_ci}, {
156262306a36Sopenharmony_ci	// Asus USB Ethernet Adapter
156362306a36Sopenharmony_ci	USB_DEVICE (0x0b95, 0x7e2b),
156462306a36Sopenharmony_ci	.driver_info = (unsigned long)&ax88772b_info,
156562306a36Sopenharmony_ci}, {
156662306a36Sopenharmony_ci	/* ASIX 88172a demo board */
156762306a36Sopenharmony_ci	USB_DEVICE(0x0b95, 0x172a),
156862306a36Sopenharmony_ci	.driver_info = (unsigned long) &ax88172a_info,
156962306a36Sopenharmony_ci}, {
157062306a36Sopenharmony_ci	/*
157162306a36Sopenharmony_ci	 * USBLINK HG20F9 "USB 2.0 LAN"
157262306a36Sopenharmony_ci	 * Appears to have gazumped Linksys's manufacturer ID but
157362306a36Sopenharmony_ci	 * doesn't (yet) conflict with any known Linksys product.
157462306a36Sopenharmony_ci	 */
157562306a36Sopenharmony_ci	USB_DEVICE(0x066b, 0x20f9),
157662306a36Sopenharmony_ci	.driver_info = (unsigned long) &hg20f9_info,
157762306a36Sopenharmony_ci}, {
157862306a36Sopenharmony_ci	// Linux Automation GmbH USB 10Base-T1L
157962306a36Sopenharmony_ci	USB_DEVICE(0x33f7, 0x0004),
158062306a36Sopenharmony_ci	.driver_info = (unsigned long) &lxausb_t1l_info,
158162306a36Sopenharmony_ci},
158262306a36Sopenharmony_ci	{ },		// END
158362306a36Sopenharmony_ci};
158462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_cistatic struct usb_driver asix_driver = {
158762306a36Sopenharmony_ci	.name =		DRIVER_NAME,
158862306a36Sopenharmony_ci	.id_table =	products,
158962306a36Sopenharmony_ci	.probe =	usbnet_probe,
159062306a36Sopenharmony_ci	.suspend =	asix_suspend,
159162306a36Sopenharmony_ci	.resume =	asix_resume,
159262306a36Sopenharmony_ci	.reset_resume =	asix_resume,
159362306a36Sopenharmony_ci	.disconnect =	usbnet_disconnect,
159462306a36Sopenharmony_ci	.supports_autosuspend = 1,
159562306a36Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
159662306a36Sopenharmony_ci};
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_cimodule_usb_driver(asix_driver);
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ciMODULE_AUTHOR("David Hollis");
160162306a36Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION);
160262306a36Sopenharmony_ciMODULE_DESCRIPTION("ASIX AX8817X based USB 2.0 Ethernet Devices");
160362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
160462306a36Sopenharmony_ci
1605