162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2011-2013 ASIX
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/etherdevice.h>
1062306a36Sopenharmony_ci#include <linux/mii.h>
1162306a36Sopenharmony_ci#include <linux/usb.h>
1262306a36Sopenharmony_ci#include <linux/crc32.h>
1362306a36Sopenharmony_ci#include <linux/usb/usbnet.h>
1462306a36Sopenharmony_ci#include <uapi/linux/mdio.h>
1562306a36Sopenharmony_ci#include <linux/mdio.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define AX88179_PHY_ID				0x03
1862306a36Sopenharmony_ci#define AX_EEPROM_LEN				0x100
1962306a36Sopenharmony_ci#define AX88179_EEPROM_MAGIC			0x17900b95
2062306a36Sopenharmony_ci#define AX_MCAST_FLTSIZE			8
2162306a36Sopenharmony_ci#define AX_MAX_MCAST				64
2262306a36Sopenharmony_ci#define AX_INT_PPLS_LINK			((u32)BIT(16))
2362306a36Sopenharmony_ci#define AX_RXHDR_L4_TYPE_MASK			0x1c
2462306a36Sopenharmony_ci#define AX_RXHDR_L4_TYPE_UDP			4
2562306a36Sopenharmony_ci#define AX_RXHDR_L4_TYPE_TCP			16
2662306a36Sopenharmony_ci#define AX_RXHDR_L3CSUM_ERR			2
2762306a36Sopenharmony_ci#define AX_RXHDR_L4CSUM_ERR			1
2862306a36Sopenharmony_ci#define AX_RXHDR_CRC_ERR			((u32)BIT(29))
2962306a36Sopenharmony_ci#define AX_RXHDR_DROP_ERR			((u32)BIT(31))
3062306a36Sopenharmony_ci#define AX_ACCESS_MAC				0x01
3162306a36Sopenharmony_ci#define AX_ACCESS_PHY				0x02
3262306a36Sopenharmony_ci#define AX_ACCESS_EEPROM			0x04
3362306a36Sopenharmony_ci#define AX_ACCESS_EFUS				0x05
3462306a36Sopenharmony_ci#define AX_RELOAD_EEPROM_EFUSE			0x06
3562306a36Sopenharmony_ci#define AX_PAUSE_WATERLVL_HIGH			0x54
3662306a36Sopenharmony_ci#define AX_PAUSE_WATERLVL_LOW			0x55
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define PHYSICAL_LINK_STATUS			0x02
3962306a36Sopenharmony_ci	#define	AX_USB_SS		0x04
4062306a36Sopenharmony_ci	#define	AX_USB_HS		0x02
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define GENERAL_STATUS				0x03
4362306a36Sopenharmony_ci/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */
4462306a36Sopenharmony_ci	#define	AX_SECLD		0x04
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define AX_SROM_ADDR				0x07
4762306a36Sopenharmony_ci#define AX_SROM_CMD				0x0a
4862306a36Sopenharmony_ci	#define EEP_RD			0x04
4962306a36Sopenharmony_ci	#define EEP_BUSY		0x10
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define AX_SROM_DATA_LOW			0x08
5262306a36Sopenharmony_ci#define AX_SROM_DATA_HIGH			0x09
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define AX_RX_CTL				0x0b
5562306a36Sopenharmony_ci	#define AX_RX_CTL_DROPCRCERR	0x0100
5662306a36Sopenharmony_ci	#define AX_RX_CTL_IPE		0x0200
5762306a36Sopenharmony_ci	#define AX_RX_CTL_START		0x0080
5862306a36Sopenharmony_ci	#define AX_RX_CTL_AP		0x0020
5962306a36Sopenharmony_ci	#define AX_RX_CTL_AM		0x0010
6062306a36Sopenharmony_ci	#define AX_RX_CTL_AB		0x0008
6162306a36Sopenharmony_ci	#define AX_RX_CTL_AMALL		0x0002
6262306a36Sopenharmony_ci	#define AX_RX_CTL_PRO		0x0001
6362306a36Sopenharmony_ci	#define AX_RX_CTL_STOP		0x0000
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define AX_NODE_ID				0x10
6662306a36Sopenharmony_ci#define AX_MULFLTARY				0x16
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define AX_MEDIUM_STATUS_MODE			0x22
6962306a36Sopenharmony_ci	#define AX_MEDIUM_GIGAMODE	0x01
7062306a36Sopenharmony_ci	#define AX_MEDIUM_FULL_DUPLEX	0x02
7162306a36Sopenharmony_ci	#define AX_MEDIUM_EN_125MHZ	0x08
7262306a36Sopenharmony_ci	#define AX_MEDIUM_RXFLOW_CTRLEN	0x10
7362306a36Sopenharmony_ci	#define AX_MEDIUM_TXFLOW_CTRLEN	0x20
7462306a36Sopenharmony_ci	#define AX_MEDIUM_RECEIVE_EN	0x100
7562306a36Sopenharmony_ci	#define AX_MEDIUM_PS		0x200
7662306a36Sopenharmony_ci	#define AX_MEDIUM_JUMBO_EN	0x8040
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#define AX_MONITOR_MOD				0x24
7962306a36Sopenharmony_ci	#define AX_MONITOR_MODE_RWLC	0x02
8062306a36Sopenharmony_ci	#define AX_MONITOR_MODE_RWMP	0x04
8162306a36Sopenharmony_ci	#define AX_MONITOR_MODE_PMEPOL	0x20
8262306a36Sopenharmony_ci	#define AX_MONITOR_MODE_PMETYPE	0x40
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define AX_GPIO_CTRL				0x25
8562306a36Sopenharmony_ci	#define AX_GPIO_CTRL_GPIO3EN	0x80
8662306a36Sopenharmony_ci	#define AX_GPIO_CTRL_GPIO2EN	0x40
8762306a36Sopenharmony_ci	#define AX_GPIO_CTRL_GPIO1EN	0x20
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define AX_PHYPWR_RSTCTL			0x26
9062306a36Sopenharmony_ci	#define AX_PHYPWR_RSTCTL_BZ	0x0010
9162306a36Sopenharmony_ci	#define AX_PHYPWR_RSTCTL_IPRL	0x0020
9262306a36Sopenharmony_ci	#define AX_PHYPWR_RSTCTL_AT	0x1000
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define AX_RX_BULKIN_QCTRL			0x2e
9562306a36Sopenharmony_ci#define AX_CLK_SELECT				0x33
9662306a36Sopenharmony_ci	#define AX_CLK_SELECT_BCS	0x01
9762306a36Sopenharmony_ci	#define AX_CLK_SELECT_ACS	0x02
9862306a36Sopenharmony_ci	#define AX_CLK_SELECT_ULR	0x08
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define AX_RXCOE_CTL				0x34
10162306a36Sopenharmony_ci	#define AX_RXCOE_IP		0x01
10262306a36Sopenharmony_ci	#define AX_RXCOE_TCP		0x02
10362306a36Sopenharmony_ci	#define AX_RXCOE_UDP		0x04
10462306a36Sopenharmony_ci	#define AX_RXCOE_TCPV6		0x20
10562306a36Sopenharmony_ci	#define AX_RXCOE_UDPV6		0x40
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci#define AX_TXCOE_CTL				0x35
10862306a36Sopenharmony_ci	#define AX_TXCOE_IP		0x01
10962306a36Sopenharmony_ci	#define AX_TXCOE_TCP		0x02
11062306a36Sopenharmony_ci	#define AX_TXCOE_UDP		0x04
11162306a36Sopenharmony_ci	#define AX_TXCOE_TCPV6		0x20
11262306a36Sopenharmony_ci	#define AX_TXCOE_UDPV6		0x40
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci#define AX_LEDCTRL				0x73
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define GMII_PHY_PHYSR				0x11
11762306a36Sopenharmony_ci	#define GMII_PHY_PHYSR_SMASK	0xc000
11862306a36Sopenharmony_ci	#define GMII_PHY_PHYSR_GIGA	0x8000
11962306a36Sopenharmony_ci	#define GMII_PHY_PHYSR_100	0x4000
12062306a36Sopenharmony_ci	#define GMII_PHY_PHYSR_FULL	0x2000
12162306a36Sopenharmony_ci	#define GMII_PHY_PHYSR_LINK	0x400
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define GMII_LED_ACT				0x1a
12462306a36Sopenharmony_ci	#define	GMII_LED_ACTIVE_MASK	0xff8f
12562306a36Sopenharmony_ci	#define	GMII_LED0_ACTIVE	BIT(4)
12662306a36Sopenharmony_ci	#define	GMII_LED1_ACTIVE	BIT(5)
12762306a36Sopenharmony_ci	#define	GMII_LED2_ACTIVE	BIT(6)
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define GMII_LED_LINK				0x1c
13062306a36Sopenharmony_ci	#define	GMII_LED_LINK_MASK	0xf888
13162306a36Sopenharmony_ci	#define	GMII_LED0_LINK_10	BIT(0)
13262306a36Sopenharmony_ci	#define	GMII_LED0_LINK_100	BIT(1)
13362306a36Sopenharmony_ci	#define	GMII_LED0_LINK_1000	BIT(2)
13462306a36Sopenharmony_ci	#define	GMII_LED1_LINK_10	BIT(4)
13562306a36Sopenharmony_ci	#define	GMII_LED1_LINK_100	BIT(5)
13662306a36Sopenharmony_ci	#define	GMII_LED1_LINK_1000	BIT(6)
13762306a36Sopenharmony_ci	#define	GMII_LED2_LINK_10	BIT(8)
13862306a36Sopenharmony_ci	#define	GMII_LED2_LINK_100	BIT(9)
13962306a36Sopenharmony_ci	#define	GMII_LED2_LINK_1000	BIT(10)
14062306a36Sopenharmony_ci	#define	LED0_ACTIVE		BIT(0)
14162306a36Sopenharmony_ci	#define	LED0_LINK_10		BIT(1)
14262306a36Sopenharmony_ci	#define	LED0_LINK_100		BIT(2)
14362306a36Sopenharmony_ci	#define	LED0_LINK_1000		BIT(3)
14462306a36Sopenharmony_ci	#define	LED0_FD			BIT(4)
14562306a36Sopenharmony_ci	#define	LED0_USB3_MASK		0x001f
14662306a36Sopenharmony_ci	#define	LED1_ACTIVE		BIT(5)
14762306a36Sopenharmony_ci	#define	LED1_LINK_10		BIT(6)
14862306a36Sopenharmony_ci	#define	LED1_LINK_100		BIT(7)
14962306a36Sopenharmony_ci	#define	LED1_LINK_1000		BIT(8)
15062306a36Sopenharmony_ci	#define	LED1_FD			BIT(9)
15162306a36Sopenharmony_ci	#define	LED1_USB3_MASK		0x03e0
15262306a36Sopenharmony_ci	#define	LED2_ACTIVE		BIT(10)
15362306a36Sopenharmony_ci	#define	LED2_LINK_1000		BIT(13)
15462306a36Sopenharmony_ci	#define	LED2_LINK_100		BIT(12)
15562306a36Sopenharmony_ci	#define	LED2_LINK_10		BIT(11)
15662306a36Sopenharmony_ci	#define	LED2_FD			BIT(14)
15762306a36Sopenharmony_ci	#define	LED_VALID		BIT(15)
15862306a36Sopenharmony_ci	#define	LED2_USB3_MASK		0x7c00
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#define GMII_PHYPAGE				0x1e
16162306a36Sopenharmony_ci#define GMII_PHY_PAGE_SELECT			0x1f
16262306a36Sopenharmony_ci	#define GMII_PHY_PGSEL_EXT	0x0007
16362306a36Sopenharmony_ci	#define GMII_PHY_PGSEL_PAGE0	0x0000
16462306a36Sopenharmony_ci	#define GMII_PHY_PGSEL_PAGE3	0x0003
16562306a36Sopenharmony_ci	#define GMII_PHY_PGSEL_PAGE5	0x0005
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic int ax88179_reset(struct usbnet *dev);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistruct ax88179_data {
17062306a36Sopenharmony_ci	u8  eee_enabled;
17162306a36Sopenharmony_ci	u8  eee_active;
17262306a36Sopenharmony_ci	u16 rxctl;
17362306a36Sopenharmony_ci	u8 in_pm;
17462306a36Sopenharmony_ci	u32 wol_supported;
17562306a36Sopenharmony_ci	u32 wolopts;
17662306a36Sopenharmony_ci	u8 disconnecting;
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistruct ax88179_int_data {
18062306a36Sopenharmony_ci	__le32 intdata1;
18162306a36Sopenharmony_ci	__le32 intdata2;
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic const struct {
18562306a36Sopenharmony_ci	unsigned char ctrl, timer_l, timer_h, size, ifg;
18662306a36Sopenharmony_ci} AX88179_BULKIN_SIZE[] =	{
18762306a36Sopenharmony_ci	{7, 0x4f, 0,	0x12, 0xff},
18862306a36Sopenharmony_ci	{7, 0x20, 3,	0x16, 0xff},
18962306a36Sopenharmony_ci	{7, 0xae, 7,	0x18, 0xff},
19062306a36Sopenharmony_ci	{7, 0xcc, 0x4c, 0x18, 8},
19162306a36Sopenharmony_ci};
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct ax88179_data *ax179_data = dev->driver_priv;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ax179_data->in_pm = pm_mode;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int ax88179_in_pm(struct usbnet *dev)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct ax88179_data *ax179_data = dev->driver_priv;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return ax179_data->in_pm;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
20862306a36Sopenharmony_ci			      u16 size, void *data)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	int ret;
21162306a36Sopenharmony_ci	int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
21262306a36Sopenharmony_ci	struct ax88179_data *ax179_data = dev->driver_priv;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	BUG_ON(!dev);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (!ax88179_in_pm(dev))
21762306a36Sopenharmony_ci		fn = usbnet_read_cmd;
21862306a36Sopenharmony_ci	else
21962306a36Sopenharmony_ci		fn = usbnet_read_cmd_nopm;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
22262306a36Sopenharmony_ci		 value, index, data, size);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (unlikely((ret < 0) && !(ret == -ENODEV && ax179_data->disconnecting)))
22562306a36Sopenharmony_ci		netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n",
22662306a36Sopenharmony_ci			    index, ret);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return ret;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
23262306a36Sopenharmony_ci			       u16 size, const void *data)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	int ret;
23562306a36Sopenharmony_ci	int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
23662306a36Sopenharmony_ci	struct ax88179_data *ax179_data = dev->driver_priv;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	BUG_ON(!dev);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (!ax88179_in_pm(dev))
24162306a36Sopenharmony_ci		fn = usbnet_write_cmd;
24262306a36Sopenharmony_ci	else
24362306a36Sopenharmony_ci		fn = usbnet_write_cmd_nopm;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
24662306a36Sopenharmony_ci		 value, index, data, size);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (unlikely((ret < 0) && !(ret == -ENODEV && ax179_data->disconnecting)))
24962306a36Sopenharmony_ci		netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n",
25062306a36Sopenharmony_ci			    index, ret);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return ret;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
25662306a36Sopenharmony_ci				    u16 index, u16 size, void *data)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	u16 buf;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (2 == size) {
26162306a36Sopenharmony_ci		buf = *((u16 *)data);
26262306a36Sopenharmony_ci		cpu_to_le16s(&buf);
26362306a36Sopenharmony_ci		usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
26462306a36Sopenharmony_ci				       USB_RECIP_DEVICE, value, index, &buf,
26562306a36Sopenharmony_ci				       size);
26662306a36Sopenharmony_ci	} else {
26762306a36Sopenharmony_ci		usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
26862306a36Sopenharmony_ci				       USB_RECIP_DEVICE, value, index, data,
26962306a36Sopenharmony_ci				       size);
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
27462306a36Sopenharmony_ci			    u16 size, void *data)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	int ret;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (2 == size) {
27962306a36Sopenharmony_ci		u16 buf = 0;
28062306a36Sopenharmony_ci		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
28162306a36Sopenharmony_ci		le16_to_cpus(&buf);
28262306a36Sopenharmony_ci		*((u16 *)data) = buf;
28362306a36Sopenharmony_ci	} else if (4 == size) {
28462306a36Sopenharmony_ci		u32 buf = 0;
28562306a36Sopenharmony_ci		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
28662306a36Sopenharmony_ci		le32_to_cpus(&buf);
28762306a36Sopenharmony_ci		*((u32 *)data) = buf;
28862306a36Sopenharmony_ci	} else {
28962306a36Sopenharmony_ci		ret = __ax88179_read_cmd(dev, cmd, value, index, size, data);
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return ret;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
29662306a36Sopenharmony_ci			     u16 size, const void *data)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	int ret;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (2 == size) {
30162306a36Sopenharmony_ci		u16 buf;
30262306a36Sopenharmony_ci		buf = *((u16 *)data);
30362306a36Sopenharmony_ci		cpu_to_le16s(&buf);
30462306a36Sopenharmony_ci		ret = __ax88179_write_cmd(dev, cmd, value, index,
30562306a36Sopenharmony_ci					  size, &buf);
30662306a36Sopenharmony_ci	} else {
30762306a36Sopenharmony_ci		ret = __ax88179_write_cmd(dev, cmd, value, index,
30862306a36Sopenharmony_ci					  size, data);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return ret;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void ax88179_status(struct usbnet *dev, struct urb *urb)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct ax88179_int_data *event;
31762306a36Sopenharmony_ci	u32 link;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (urb->actual_length < 8)
32062306a36Sopenharmony_ci		return;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	event = urb->transfer_buffer;
32362306a36Sopenharmony_ci	le32_to_cpus((void *)&event->intdata1);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	link = (((__force u32)event->intdata1) & AX_INT_PPLS_LINK) >> 16;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (netif_carrier_ok(dev->net) != link) {
32862306a36Sopenharmony_ci		usbnet_link_change(dev, link, 1);
32962306a36Sopenharmony_ci		netdev_info(dev->net, "ax88179 - Link status is: %d\n", link);
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(netdev);
33662306a36Sopenharmony_ci	u16 res;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
33962306a36Sopenharmony_ci	return res;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc,
34362306a36Sopenharmony_ci			       int val)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(netdev);
34662306a36Sopenharmony_ci	u16 res = (u16) val;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic inline int ax88179_phy_mmd_indirect(struct usbnet *dev, u16 prtad,
35262306a36Sopenharmony_ci					   u16 devad)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	u16 tmp16;
35562306a36Sopenharmony_ci	int ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	tmp16 = devad;
35862306a36Sopenharmony_ci	ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
35962306a36Sopenharmony_ci				MII_MMD_CTRL, 2, &tmp16);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	tmp16 = prtad;
36262306a36Sopenharmony_ci	ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
36362306a36Sopenharmony_ci				MII_MMD_DATA, 2, &tmp16);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	tmp16 = devad | MII_MMD_CTRL_NOINCR;
36662306a36Sopenharmony_ci	ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
36762306a36Sopenharmony_ci				MII_MMD_CTRL, 2, &tmp16);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return ret;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic int
37362306a36Sopenharmony_ciax88179_phy_read_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	int ret;
37662306a36Sopenharmony_ci	u16 tmp16;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	ax88179_phy_mmd_indirect(dev, prtad, devad);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	ret = ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
38162306a36Sopenharmony_ci			       MII_MMD_DATA, 2, &tmp16);
38262306a36Sopenharmony_ci	if (ret < 0)
38362306a36Sopenharmony_ci		return ret;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return tmp16;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic int
38962306a36Sopenharmony_ciax88179_phy_write_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad,
39062306a36Sopenharmony_ci			       u16 data)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	int ret;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	ax88179_phy_mmd_indirect(dev, prtad, devad);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
39762306a36Sopenharmony_ci				MII_MMD_DATA, 2, &data);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (ret < 0)
40062306a36Sopenharmony_ci		return ret;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return 0;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct usbnet *dev = usb_get_intfdata(intf);
40862306a36Sopenharmony_ci	struct ax88179_data *priv = dev->driver_priv;
40962306a36Sopenharmony_ci	u16 tmp16;
41062306a36Sopenharmony_ci	u8 tmp8;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	ax88179_set_pm_mode(dev, true);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	usbnet_suspend(intf, message);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* Enable WoL */
41762306a36Sopenharmony_ci	if (priv->wolopts) {
41862306a36Sopenharmony_ci		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
41962306a36Sopenharmony_ci				 1, 1, &tmp8);
42062306a36Sopenharmony_ci		if (priv->wolopts & WAKE_PHY)
42162306a36Sopenharmony_ci			tmp8 |= AX_MONITOR_MODE_RWLC;
42262306a36Sopenharmony_ci		if (priv->wolopts & WAKE_MAGIC)
42362306a36Sopenharmony_ci			tmp8 |= AX_MONITOR_MODE_RWMP;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
42662306a36Sopenharmony_ci				  1, 1, &tmp8);
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* Disable RX path */
43062306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
43162306a36Sopenharmony_ci			 2, 2, &tmp16);
43262306a36Sopenharmony_ci	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
43362306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
43462306a36Sopenharmony_ci			  2, 2, &tmp16);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	/* Force bulk-in zero length */
43762306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
43862306a36Sopenharmony_ci			 2, 2, &tmp16);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL;
44162306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
44262306a36Sopenharmony_ci			  2, 2, &tmp16);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* change clock */
44562306a36Sopenharmony_ci	tmp8 = 0;
44662306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Configure RX control register => stop operation */
44962306a36Sopenharmony_ci	tmp16 = AX_RX_CTL_STOP;
45062306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	ax88179_set_pm_mode(dev, false);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci/* This function is used to enable the autodetach function. */
45862306a36Sopenharmony_ci/* This function is determined by offset 0x43 of EEPROM */
45962306a36Sopenharmony_cistatic int ax88179_auto_detach(struct usbnet *dev)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	u16 tmp16;
46262306a36Sopenharmony_ci	u8 tmp8;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16) < 0)
46562306a36Sopenharmony_ci		return 0;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if ((tmp16 == 0xFFFF) || (!(tmp16 & 0x0100)))
46862306a36Sopenharmony_ci		return 0;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* Enable Auto Detach bit */
47162306a36Sopenharmony_ci	tmp8 = 0;
47262306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
47362306a36Sopenharmony_ci	tmp8 |= AX_CLK_SELECT_ULR;
47462306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
47762306a36Sopenharmony_ci	tmp16 |= AX_PHYPWR_RSTCTL_AT;
47862306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return 0;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic int ax88179_resume(struct usb_interface *intf)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct usbnet *dev = usb_get_intfdata(intf);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ax88179_set_pm_mode(dev, true);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	usbnet_link_change(dev, 0, 0);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	ax88179_reset(dev);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	ax88179_set_pm_mode(dev, false);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return usbnet_resume(intf);
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic void ax88179_disconnect(struct usb_interface *intf)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct usbnet *dev = usb_get_intfdata(intf);
50162306a36Sopenharmony_ci	struct ax88179_data *ax179_data;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (!dev)
50462306a36Sopenharmony_ci		return;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	ax179_data = dev->driver_priv;
50762306a36Sopenharmony_ci	ax179_data->disconnecting = 1;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	usbnet_disconnect(intf);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic void
51362306a36Sopenharmony_ciax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
51662306a36Sopenharmony_ci	struct ax88179_data *priv = dev->driver_priv;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	wolinfo->supported = priv->wol_supported;
51962306a36Sopenharmony_ci	wolinfo->wolopts = priv->wolopts;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic int
52362306a36Sopenharmony_ciax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
52662306a36Sopenharmony_ci	struct ax88179_data *priv = dev->driver_priv;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (wolinfo->wolopts & ~(priv->wol_supported))
52962306a36Sopenharmony_ci		return -EINVAL;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	priv->wolopts = wolinfo->wolopts;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	return 0;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic int ax88179_get_eeprom_len(struct net_device *net)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	return AX_EEPROM_LEN;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic int
54262306a36Sopenharmony_ciax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
54362306a36Sopenharmony_ci		   u8 *data)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
54662306a36Sopenharmony_ci	u16 *eeprom_buff;
54762306a36Sopenharmony_ci	int first_word, last_word;
54862306a36Sopenharmony_ci	int i, ret;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (eeprom->len == 0)
55162306a36Sopenharmony_ci		return -EINVAL;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	eeprom->magic = AX88179_EEPROM_MAGIC;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	first_word = eeprom->offset >> 1;
55662306a36Sopenharmony_ci	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
55762306a36Sopenharmony_ci	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
55862306a36Sopenharmony_ci				    GFP_KERNEL);
55962306a36Sopenharmony_ci	if (!eeprom_buff)
56062306a36Sopenharmony_ci		return -ENOMEM;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* ax88179/178A returns 2 bytes from eeprom on read */
56362306a36Sopenharmony_ci	for (i = first_word; i <= last_word; i++) {
56462306a36Sopenharmony_ci		ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
56562306a36Sopenharmony_ci					 &eeprom_buff[i - first_word]);
56662306a36Sopenharmony_ci		if (ret < 0) {
56762306a36Sopenharmony_ci			kfree(eeprom_buff);
56862306a36Sopenharmony_ci			return -EIO;
56962306a36Sopenharmony_ci		}
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
57362306a36Sopenharmony_ci	kfree(eeprom_buff);
57462306a36Sopenharmony_ci	return 0;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int
57862306a36Sopenharmony_ciax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
57962306a36Sopenharmony_ci		   u8 *data)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
58262306a36Sopenharmony_ci	u16 *eeprom_buff;
58362306a36Sopenharmony_ci	int first_word;
58462306a36Sopenharmony_ci	int last_word;
58562306a36Sopenharmony_ci	int ret;
58662306a36Sopenharmony_ci	int i;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n",
58962306a36Sopenharmony_ci		   eeprom->len, eeprom->offset, eeprom->magic);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (eeprom->len == 0)
59262306a36Sopenharmony_ci		return -EINVAL;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (eeprom->magic != AX88179_EEPROM_MAGIC)
59562306a36Sopenharmony_ci		return -EINVAL;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	first_word = eeprom->offset >> 1;
59862306a36Sopenharmony_ci	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
60162306a36Sopenharmony_ci				    GFP_KERNEL);
60262306a36Sopenharmony_ci	if (!eeprom_buff)
60362306a36Sopenharmony_ci		return -ENOMEM;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	/* align data to 16 bit boundaries, read the missing data from
60662306a36Sopenharmony_ci	   the EEPROM */
60762306a36Sopenharmony_ci	if (eeprom->offset & 1) {
60862306a36Sopenharmony_ci		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, first_word, 1, 2,
60962306a36Sopenharmony_ci				       &eeprom_buff[0]);
61062306a36Sopenharmony_ci		if (ret < 0) {
61162306a36Sopenharmony_ci			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
61262306a36Sopenharmony_ci			goto free;
61362306a36Sopenharmony_ci		}
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if ((eeprom->offset + eeprom->len) & 1) {
61762306a36Sopenharmony_ci		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, last_word, 1, 2,
61862306a36Sopenharmony_ci				       &eeprom_buff[last_word - first_word]);
61962306a36Sopenharmony_ci		if (ret < 0) {
62062306a36Sopenharmony_ci			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
62162306a36Sopenharmony_ci			goto free;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	for (i = first_word; i <= last_word; i++) {
62862306a36Sopenharmony_ci		netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
62962306a36Sopenharmony_ci			   i, eeprom_buff[i - first_word]);
63062306a36Sopenharmony_ci		ret = ax88179_write_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
63162306a36Sopenharmony_ci					&eeprom_buff[i - first_word]);
63262306a36Sopenharmony_ci		if (ret < 0) {
63362306a36Sopenharmony_ci			netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", i);
63462306a36Sopenharmony_ci			goto free;
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci		msleep(20);
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	/* reload EEPROM data */
64062306a36Sopenharmony_ci	ret = ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0x0000, 0, 0, NULL);
64162306a36Sopenharmony_ci	if (ret < 0) {
64262306a36Sopenharmony_ci		netdev_err(net, "Failed to reload EEPROM data\n");
64362306a36Sopenharmony_ci		goto free;
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	ret = 0;
64762306a36Sopenharmony_cifree:
64862306a36Sopenharmony_ci	kfree(eeprom_buff);
64962306a36Sopenharmony_ci	return ret;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic int ax88179_get_link_ksettings(struct net_device *net,
65362306a36Sopenharmony_ci				      struct ethtool_link_ksettings *cmd)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	mii_ethtool_get_link_ksettings(&dev->mii, cmd);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	return 0;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic int ax88179_set_link_ksettings(struct net_device *net,
66362306a36Sopenharmony_ci				      const struct ethtool_link_ksettings *cmd)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
66662306a36Sopenharmony_ci	return mii_ethtool_set_link_ksettings(&dev->mii, cmd);
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int
67062306a36Sopenharmony_ciax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_eee *data)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	int val;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	/* Get Supported EEE */
67562306a36Sopenharmony_ci	val = ax88179_phy_read_mmd_indirect(dev, MDIO_PCS_EEE_ABLE,
67662306a36Sopenharmony_ci					    MDIO_MMD_PCS);
67762306a36Sopenharmony_ci	if (val < 0)
67862306a36Sopenharmony_ci		return val;
67962306a36Sopenharmony_ci	data->supported = mmd_eee_cap_to_ethtool_sup_t(val);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/* Get advertisement EEE */
68262306a36Sopenharmony_ci	val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_ADV,
68362306a36Sopenharmony_ci					    MDIO_MMD_AN);
68462306a36Sopenharmony_ci	if (val < 0)
68562306a36Sopenharmony_ci		return val;
68662306a36Sopenharmony_ci	data->advertised = mmd_eee_adv_to_ethtool_adv_t(val);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/* Get LP advertisement EEE */
68962306a36Sopenharmony_ci	val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_LPABLE,
69062306a36Sopenharmony_ci					    MDIO_MMD_AN);
69162306a36Sopenharmony_ci	if (val < 0)
69262306a36Sopenharmony_ci		return val;
69362306a36Sopenharmony_ci	data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic int
69962306a36Sopenharmony_ciax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_eee *data)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	u16 tmp16 = ethtool_adv_to_mmd_eee_adv_t(data->advertised);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return ax88179_phy_write_mmd_indirect(dev, MDIO_AN_EEE_ADV,
70462306a36Sopenharmony_ci					      MDIO_MMD_AN, tmp16);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic int ax88179_chk_eee(struct usbnet *dev)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
71062306a36Sopenharmony_ci	struct ax88179_data *priv = dev->driver_priv;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	mii_ethtool_gset(&dev->mii, &ecmd);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (ecmd.duplex & DUPLEX_FULL) {
71562306a36Sopenharmony_ci		int eee_lp, eee_cap, eee_adv;
71662306a36Sopenharmony_ci		u32 lp, cap, adv, supported = 0;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		eee_cap = ax88179_phy_read_mmd_indirect(dev,
71962306a36Sopenharmony_ci							MDIO_PCS_EEE_ABLE,
72062306a36Sopenharmony_ci							MDIO_MMD_PCS);
72162306a36Sopenharmony_ci		if (eee_cap < 0) {
72262306a36Sopenharmony_ci			priv->eee_active = 0;
72362306a36Sopenharmony_ci			return false;
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci		cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap);
72762306a36Sopenharmony_ci		if (!cap) {
72862306a36Sopenharmony_ci			priv->eee_active = 0;
72962306a36Sopenharmony_ci			return false;
73062306a36Sopenharmony_ci		}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		eee_lp = ax88179_phy_read_mmd_indirect(dev,
73362306a36Sopenharmony_ci						       MDIO_AN_EEE_LPABLE,
73462306a36Sopenharmony_ci						       MDIO_MMD_AN);
73562306a36Sopenharmony_ci		if (eee_lp < 0) {
73662306a36Sopenharmony_ci			priv->eee_active = 0;
73762306a36Sopenharmony_ci			return false;
73862306a36Sopenharmony_ci		}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		eee_adv = ax88179_phy_read_mmd_indirect(dev,
74162306a36Sopenharmony_ci							MDIO_AN_EEE_ADV,
74262306a36Sopenharmony_ci							MDIO_MMD_AN);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci		if (eee_adv < 0) {
74562306a36Sopenharmony_ci			priv->eee_active = 0;
74662306a36Sopenharmony_ci			return false;
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv);
75062306a36Sopenharmony_ci		lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp);
75162306a36Sopenharmony_ci		supported = (ecmd.speed == SPEED_1000) ?
75262306a36Sopenharmony_ci			     SUPPORTED_1000baseT_Full :
75362306a36Sopenharmony_ci			     SUPPORTED_100baseT_Full;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		if (!(lp & adv & supported)) {
75662306a36Sopenharmony_ci			priv->eee_active = 0;
75762306a36Sopenharmony_ci			return false;
75862306a36Sopenharmony_ci		}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		priv->eee_active = 1;
76162306a36Sopenharmony_ci		return true;
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	priv->eee_active = 0;
76562306a36Sopenharmony_ci	return false;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic void ax88179_disable_eee(struct usbnet *dev)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	u16 tmp16;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	tmp16 = GMII_PHY_PGSEL_PAGE3;
77362306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
77462306a36Sopenharmony_ci			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	tmp16 = 0x3246;
77762306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
77862306a36Sopenharmony_ci			  MII_PHYADDR, 2, &tmp16);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	tmp16 = GMII_PHY_PGSEL_PAGE0;
78162306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
78262306a36Sopenharmony_ci			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic void ax88179_enable_eee(struct usbnet *dev)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	u16 tmp16;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	tmp16 = GMII_PHY_PGSEL_PAGE3;
79062306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
79162306a36Sopenharmony_ci			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	tmp16 = 0x3247;
79462306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
79562306a36Sopenharmony_ci			  MII_PHYADDR, 2, &tmp16);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	tmp16 = GMII_PHY_PGSEL_PAGE5;
79862306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
79962306a36Sopenharmony_ci			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	tmp16 = 0x0680;
80262306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
80362306a36Sopenharmony_ci			  MII_BMSR, 2, &tmp16);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	tmp16 = GMII_PHY_PGSEL_PAGE0;
80662306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
80762306a36Sopenharmony_ci			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic int ax88179_get_eee(struct net_device *net, struct ethtool_eee *edata)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
81362306a36Sopenharmony_ci	struct ax88179_data *priv = dev->driver_priv;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	edata->eee_enabled = priv->eee_enabled;
81662306a36Sopenharmony_ci	edata->eee_active = priv->eee_active;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	return ax88179_ethtool_get_eee(dev, edata);
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic int ax88179_set_eee(struct net_device *net, struct ethtool_eee *edata)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
82462306a36Sopenharmony_ci	struct ax88179_data *priv = dev->driver_priv;
82562306a36Sopenharmony_ci	int ret;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	priv->eee_enabled = edata->eee_enabled;
82862306a36Sopenharmony_ci	if (!priv->eee_enabled) {
82962306a36Sopenharmony_ci		ax88179_disable_eee(dev);
83062306a36Sopenharmony_ci	} else {
83162306a36Sopenharmony_ci		priv->eee_enabled = ax88179_chk_eee(dev);
83262306a36Sopenharmony_ci		if (!priv->eee_enabled)
83362306a36Sopenharmony_ci			return -EOPNOTSUPP;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		ax88179_enable_eee(dev);
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	ret = ax88179_ethtool_set_eee(dev, edata);
83962306a36Sopenharmony_ci	if (ret)
84062306a36Sopenharmony_ci		return ret;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	mii_nway_restart(&dev->mii);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	usbnet_link_change(dev, 0, 0);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	return ret;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
85262306a36Sopenharmony_ci	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic const struct ethtool_ops ax88179_ethtool_ops = {
85662306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
85762306a36Sopenharmony_ci	.get_msglevel		= usbnet_get_msglevel,
85862306a36Sopenharmony_ci	.set_msglevel		= usbnet_set_msglevel,
85962306a36Sopenharmony_ci	.get_wol		= ax88179_get_wol,
86062306a36Sopenharmony_ci	.set_wol		= ax88179_set_wol,
86162306a36Sopenharmony_ci	.get_eeprom_len		= ax88179_get_eeprom_len,
86262306a36Sopenharmony_ci	.get_eeprom		= ax88179_get_eeprom,
86362306a36Sopenharmony_ci	.set_eeprom		= ax88179_set_eeprom,
86462306a36Sopenharmony_ci	.get_eee		= ax88179_get_eee,
86562306a36Sopenharmony_ci	.set_eee		= ax88179_set_eee,
86662306a36Sopenharmony_ci	.nway_reset		= usbnet_nway_reset,
86762306a36Sopenharmony_ci	.get_link_ksettings	= ax88179_get_link_ksettings,
86862306a36Sopenharmony_ci	.set_link_ksettings	= ax88179_set_link_ksettings,
86962306a36Sopenharmony_ci	.get_ts_info		= ethtool_op_get_ts_info,
87062306a36Sopenharmony_ci};
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_cistatic void ax88179_set_multicast(struct net_device *net)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
87562306a36Sopenharmony_ci	struct ax88179_data *data = dev->driver_priv;
87662306a36Sopenharmony_ci	u8 *m_filter = ((u8 *)dev->data);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (net->flags & IFF_PROMISC) {
88162306a36Sopenharmony_ci		data->rxctl |= AX_RX_CTL_PRO;
88262306a36Sopenharmony_ci	} else if (net->flags & IFF_ALLMULTI ||
88362306a36Sopenharmony_ci		   netdev_mc_count(net) > AX_MAX_MCAST) {
88462306a36Sopenharmony_ci		data->rxctl |= AX_RX_CTL_AMALL;
88562306a36Sopenharmony_ci	} else if (netdev_mc_empty(net)) {
88662306a36Sopenharmony_ci		/* just broadcast and directed */
88762306a36Sopenharmony_ci	} else {
88862306a36Sopenharmony_ci		/* We use dev->data for our 8 byte filter buffer
88962306a36Sopenharmony_ci		 * to avoid allocating memory that is tricky to free later
89062306a36Sopenharmony_ci		 */
89162306a36Sopenharmony_ci		u32 crc_bits;
89262306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci		memset(m_filter, 0, AX_MCAST_FLTSIZE);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, net) {
89762306a36Sopenharmony_ci			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
89862306a36Sopenharmony_ci			*(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7));
89962306a36Sopenharmony_ci		}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY,
90262306a36Sopenharmony_ci					AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE,
90362306a36Sopenharmony_ci					m_filter);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		data->rxctl |= AX_RX_CTL_AM;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL,
90962306a36Sopenharmony_ci				2, 2, &data->rxctl);
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic int
91362306a36Sopenharmony_ciax88179_set_features(struct net_device *net, netdev_features_t features)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	u8 tmp;
91662306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
91762306a36Sopenharmony_ci	netdev_features_t changed = net->features ^ features;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (changed & NETIF_F_IP_CSUM) {
92062306a36Sopenharmony_ci		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
92162306a36Sopenharmony_ci		tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;
92262306a36Sopenharmony_ci		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (changed & NETIF_F_IPV6_CSUM) {
92662306a36Sopenharmony_ci		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
92762306a36Sopenharmony_ci		tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
92862306a36Sopenharmony_ci		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
92962306a36Sopenharmony_ci	}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	if (changed & NETIF_F_RXCSUM) {
93262306a36Sopenharmony_ci		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
93362306a36Sopenharmony_ci		tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
93462306a36Sopenharmony_ci		       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
93562306a36Sopenharmony_ci		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	return 0;
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic int ax88179_change_mtu(struct net_device *net, int new_mtu)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
94462306a36Sopenharmony_ci	u16 tmp16;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	net->mtu = new_mtu;
94762306a36Sopenharmony_ci	dev->hard_mtu = net->mtu + net->hard_header_len;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	if (net->mtu > 1500) {
95062306a36Sopenharmony_ci		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
95162306a36Sopenharmony_ci				 2, 2, &tmp16);
95262306a36Sopenharmony_ci		tmp16 |= AX_MEDIUM_JUMBO_EN;
95362306a36Sopenharmony_ci		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
95462306a36Sopenharmony_ci				  2, 2, &tmp16);
95562306a36Sopenharmony_ci	} else {
95662306a36Sopenharmony_ci		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
95762306a36Sopenharmony_ci				 2, 2, &tmp16);
95862306a36Sopenharmony_ci		tmp16 &= ~AX_MEDIUM_JUMBO_EN;
95962306a36Sopenharmony_ci		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
96062306a36Sopenharmony_ci				  2, 2, &tmp16);
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/* max qlen depend on hard_mtu and rx_urb_size */
96462306a36Sopenharmony_ci	usbnet_update_max_qlen(dev);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	return 0;
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic int ax88179_set_mac_addr(struct net_device *net, void *p)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	struct usbnet *dev = netdev_priv(net);
97262306a36Sopenharmony_ci	struct sockaddr *addr = p;
97362306a36Sopenharmony_ci	int ret;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (netif_running(net))
97662306a36Sopenharmony_ci		return -EBUSY;
97762306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
97862306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	eth_hw_addr_set(net, addr->sa_data);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/* Set the MAC address */
98362306a36Sopenharmony_ci	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
98462306a36Sopenharmony_ci				 ETH_ALEN, net->dev_addr);
98562306a36Sopenharmony_ci	if (ret < 0)
98662306a36Sopenharmony_ci		return ret;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	return 0;
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic const struct net_device_ops ax88179_netdev_ops = {
99262306a36Sopenharmony_ci	.ndo_open		= usbnet_open,
99362306a36Sopenharmony_ci	.ndo_stop		= usbnet_stop,
99462306a36Sopenharmony_ci	.ndo_start_xmit		= usbnet_start_xmit,
99562306a36Sopenharmony_ci	.ndo_tx_timeout		= usbnet_tx_timeout,
99662306a36Sopenharmony_ci	.ndo_get_stats64	= dev_get_tstats64,
99762306a36Sopenharmony_ci	.ndo_change_mtu		= ax88179_change_mtu,
99862306a36Sopenharmony_ci	.ndo_set_mac_address	= ax88179_set_mac_addr,
99962306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
100062306a36Sopenharmony_ci	.ndo_eth_ioctl		= ax88179_ioctl,
100162306a36Sopenharmony_ci	.ndo_set_rx_mode	= ax88179_set_multicast,
100262306a36Sopenharmony_ci	.ndo_set_features	= ax88179_set_features,
100362306a36Sopenharmony_ci};
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_cistatic int ax88179_check_eeprom(struct usbnet *dev)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	u8 i, buf, eeprom[20];
100862306a36Sopenharmony_ci	u16 csum, delay = HZ / 10;
100962306a36Sopenharmony_ci	unsigned long jtimeout;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	/* Read EEPROM content */
101262306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
101362306a36Sopenharmony_ci		buf = i;
101462306a36Sopenharmony_ci		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
101562306a36Sopenharmony_ci				      1, 1, &buf) < 0)
101662306a36Sopenharmony_ci			return -EINVAL;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci		buf = EEP_RD;
101962306a36Sopenharmony_ci		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
102062306a36Sopenharmony_ci				      1, 1, &buf) < 0)
102162306a36Sopenharmony_ci			return -EINVAL;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		jtimeout = jiffies + delay;
102462306a36Sopenharmony_ci		do {
102562306a36Sopenharmony_ci			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
102662306a36Sopenharmony_ci					 1, 1, &buf);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci			if (time_after(jiffies, jtimeout))
102962306a36Sopenharmony_ci				return -EINVAL;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci		} while (buf & EEP_BUSY);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci		__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
103462306a36Sopenharmony_ci				   2, 2, &eeprom[i * 2]);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci		if ((i == 0) && (eeprom[0] == 0xFF))
103762306a36Sopenharmony_ci			return -EINVAL;
103862306a36Sopenharmony_ci	}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9];
104162306a36Sopenharmony_ci	csum = (csum >> 8) + (csum & 0xff);
104262306a36Sopenharmony_ci	if ((csum + eeprom[10]) != 0xff)
104362306a36Sopenharmony_ci		return -EINVAL;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	return 0;
104662306a36Sopenharmony_ci}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_cistatic int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode)
104962306a36Sopenharmony_ci{
105062306a36Sopenharmony_ci	u8	i;
105162306a36Sopenharmony_ci	u8	efuse[64];
105262306a36Sopenharmony_ci	u16	csum = 0;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (ax88179_read_cmd(dev, AX_ACCESS_EFUS, 0, 64, 64, efuse) < 0)
105562306a36Sopenharmony_ci		return -EINVAL;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (*efuse == 0xFF)
105862306a36Sopenharmony_ci		return -EINVAL;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	for (i = 0; i < 64; i++)
106162306a36Sopenharmony_ci		csum = csum + efuse[i];
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	while (csum > 255)
106462306a36Sopenharmony_ci		csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	if (csum != 0xFF)
106762306a36Sopenharmony_ci		return -EINVAL;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	*ledmode = (efuse[51] << 8) | efuse[52];
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	return 0;
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	u16 led;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	/* Loaded the old eFuse LED Mode */
107962306a36Sopenharmony_ci	if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0)
108062306a36Sopenharmony_ci		return -EINVAL;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	led >>= 8;
108362306a36Sopenharmony_ci	switch (led) {
108462306a36Sopenharmony_ci	case 0xFF:
108562306a36Sopenharmony_ci		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
108662306a36Sopenharmony_ci		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
108762306a36Sopenharmony_ci		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
108862306a36Sopenharmony_ci		break;
108962306a36Sopenharmony_ci	case 0xFE:
109062306a36Sopenharmony_ci		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | LED_VALID;
109162306a36Sopenharmony_ci		break;
109262306a36Sopenharmony_ci	case 0xFD:
109362306a36Sopenharmony_ci		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 |
109462306a36Sopenharmony_ci		      LED2_LINK_10 | LED_VALID;
109562306a36Sopenharmony_ci		break;
109662306a36Sopenharmony_ci	case 0xFC:
109762306a36Sopenharmony_ci		led = LED0_ACTIVE | LED1_ACTIVE | LED1_LINK_1000 | LED2_ACTIVE |
109862306a36Sopenharmony_ci		      LED2_LINK_100 | LED2_LINK_10 | LED_VALID;
109962306a36Sopenharmony_ci		break;
110062306a36Sopenharmony_ci	default:
110162306a36Sopenharmony_ci		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
110262306a36Sopenharmony_ci		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
110362306a36Sopenharmony_ci		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
110462306a36Sopenharmony_ci		break;
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	*ledvalue = led;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return 0;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic int ax88179_led_setting(struct usbnet *dev)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	u8 ledfd, value = 0;
111562306a36Sopenharmony_ci	u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10;
111662306a36Sopenharmony_ci	unsigned long jtimeout;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	/* Check AX88179 version. UA1 or UA2*/
111962306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (!(value & AX_SECLD)) {	/* UA1 */
112262306a36Sopenharmony_ci		value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN |
112362306a36Sopenharmony_ci			AX_GPIO_CTRL_GPIO1EN;
112462306a36Sopenharmony_ci		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_GPIO_CTRL,
112562306a36Sopenharmony_ci				      1, 1, &value) < 0)
112662306a36Sopenharmony_ci			return -EINVAL;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	/* Check EEPROM */
113062306a36Sopenharmony_ci	if (!ax88179_check_eeprom(dev)) {
113162306a36Sopenharmony_ci		value = 0x42;
113262306a36Sopenharmony_ci		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
113362306a36Sopenharmony_ci				      1, 1, &value) < 0)
113462306a36Sopenharmony_ci			return -EINVAL;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci		value = EEP_RD;
113762306a36Sopenharmony_ci		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
113862306a36Sopenharmony_ci				      1, 1, &value) < 0)
113962306a36Sopenharmony_ci			return -EINVAL;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci		jtimeout = jiffies + delay;
114262306a36Sopenharmony_ci		do {
114362306a36Sopenharmony_ci			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
114462306a36Sopenharmony_ci					 1, 1, &value);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci			if (time_after(jiffies, jtimeout))
114762306a36Sopenharmony_ci				return -EINVAL;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci		} while (value & EEP_BUSY);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_HIGH,
115262306a36Sopenharmony_ci				 1, 1, &value);
115362306a36Sopenharmony_ci		ledvalue = (value << 8);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
115662306a36Sopenharmony_ci				 1, 1, &value);
115762306a36Sopenharmony_ci		ledvalue |= value;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci		/* load internal ROM for defaule setting */
116062306a36Sopenharmony_ci		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
116162306a36Sopenharmony_ci			ax88179_convert_old_led(dev, &ledvalue);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	} else if (!ax88179_check_efuse(dev, &ledvalue)) {
116462306a36Sopenharmony_ci		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
116562306a36Sopenharmony_ci			ax88179_convert_old_led(dev, &ledvalue);
116662306a36Sopenharmony_ci	} else {
116762306a36Sopenharmony_ci		ax88179_convert_old_led(dev, &ledvalue);
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	tmp = GMII_PHY_PGSEL_EXT;
117162306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
117262306a36Sopenharmony_ci			  GMII_PHY_PAGE_SELECT, 2, &tmp);
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	tmp = 0x2c;
117562306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
117662306a36Sopenharmony_ci			  GMII_PHYPAGE, 2, &tmp);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
117962306a36Sopenharmony_ci			 GMII_LED_ACT, 2, &ledact);
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
118262306a36Sopenharmony_ci			 GMII_LED_LINK, 2, &ledlink);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	ledact &= GMII_LED_ACTIVE_MASK;
118562306a36Sopenharmony_ci	ledlink &= GMII_LED_LINK_MASK;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (ledvalue & LED0_ACTIVE)
118862306a36Sopenharmony_ci		ledact |= GMII_LED0_ACTIVE;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	if (ledvalue & LED1_ACTIVE)
119162306a36Sopenharmony_ci		ledact |= GMII_LED1_ACTIVE;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	if (ledvalue & LED2_ACTIVE)
119462306a36Sopenharmony_ci		ledact |= GMII_LED2_ACTIVE;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	if (ledvalue & LED0_LINK_10)
119762306a36Sopenharmony_ci		ledlink |= GMII_LED0_LINK_10;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	if (ledvalue & LED1_LINK_10)
120062306a36Sopenharmony_ci		ledlink |= GMII_LED1_LINK_10;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	if (ledvalue & LED2_LINK_10)
120362306a36Sopenharmony_ci		ledlink |= GMII_LED2_LINK_10;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	if (ledvalue & LED0_LINK_100)
120662306a36Sopenharmony_ci		ledlink |= GMII_LED0_LINK_100;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (ledvalue & LED1_LINK_100)
120962306a36Sopenharmony_ci		ledlink |= GMII_LED1_LINK_100;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if (ledvalue & LED2_LINK_100)
121262306a36Sopenharmony_ci		ledlink |= GMII_LED2_LINK_100;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if (ledvalue & LED0_LINK_1000)
121562306a36Sopenharmony_ci		ledlink |= GMII_LED0_LINK_1000;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	if (ledvalue & LED1_LINK_1000)
121862306a36Sopenharmony_ci		ledlink |= GMII_LED1_LINK_1000;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (ledvalue & LED2_LINK_1000)
122162306a36Sopenharmony_ci		ledlink |= GMII_LED2_LINK_1000;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	tmp = ledact;
122462306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
122562306a36Sopenharmony_ci			  GMII_LED_ACT, 2, &tmp);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	tmp = ledlink;
122862306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
122962306a36Sopenharmony_ci			  GMII_LED_LINK, 2, &tmp);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	tmp = GMII_PHY_PGSEL_PAGE0;
123262306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
123362306a36Sopenharmony_ci			  GMII_PHY_PAGE_SELECT, 2, &tmp);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	/* LED full duplex setting */
123662306a36Sopenharmony_ci	ledfd = 0;
123762306a36Sopenharmony_ci	if (ledvalue & LED0_FD)
123862306a36Sopenharmony_ci		ledfd |= 0x01;
123962306a36Sopenharmony_ci	else if ((ledvalue & LED0_USB3_MASK) == 0)
124062306a36Sopenharmony_ci		ledfd |= 0x02;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	if (ledvalue & LED1_FD)
124362306a36Sopenharmony_ci		ledfd |= 0x04;
124462306a36Sopenharmony_ci	else if ((ledvalue & LED1_USB3_MASK) == 0)
124562306a36Sopenharmony_ci		ledfd |= 0x08;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	if (ledvalue & LED2_FD)
124862306a36Sopenharmony_ci		ledfd |= 0x10;
124962306a36Sopenharmony_ci	else if ((ledvalue & LED2_USB3_MASK) == 0)
125062306a36Sopenharmony_ci		ledfd |= 0x20;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	return 0;
125562306a36Sopenharmony_ci}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_cistatic void ax88179_get_mac_addr(struct usbnet *dev)
125862306a36Sopenharmony_ci{
125962306a36Sopenharmony_ci	u8 mac[ETH_ALEN];
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	memset(mac, 0, sizeof(mac));
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	/* Maybe the boot loader passed the MAC address via device tree */
126462306a36Sopenharmony_ci	if (!eth_platform_get_mac_address(&dev->udev->dev, mac)) {
126562306a36Sopenharmony_ci		netif_dbg(dev, ifup, dev->net,
126662306a36Sopenharmony_ci			  "MAC address read from device tree");
126762306a36Sopenharmony_ci	} else {
126862306a36Sopenharmony_ci		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
126962306a36Sopenharmony_ci				 ETH_ALEN, mac);
127062306a36Sopenharmony_ci		netif_dbg(dev, ifup, dev->net,
127162306a36Sopenharmony_ci			  "MAC address read from ASIX chip");
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	if (is_valid_ether_addr(mac)) {
127562306a36Sopenharmony_ci		eth_hw_addr_set(dev->net, mac);
127662306a36Sopenharmony_ci	} else {
127762306a36Sopenharmony_ci		netdev_info(dev->net, "invalid MAC address, using random\n");
127862306a36Sopenharmony_ci		eth_hw_addr_random(dev->net);
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
128262306a36Sopenharmony_ci			  dev->net->dev_addr);
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_cistatic int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	struct ax88179_data *ax179_data;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	usbnet_get_endpoints(dev, intf);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	ax179_data = kzalloc(sizeof(*ax179_data), GFP_KERNEL);
129262306a36Sopenharmony_ci	if (!ax179_data)
129362306a36Sopenharmony_ci		return -ENOMEM;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	dev->driver_priv = ax179_data;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	dev->net->netdev_ops = &ax88179_netdev_ops;
129862306a36Sopenharmony_ci	dev->net->ethtool_ops = &ax88179_ethtool_ops;
129962306a36Sopenharmony_ci	dev->net->needed_headroom = 8;
130062306a36Sopenharmony_ci	dev->net->max_mtu = 4088;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	/* Initialize MII structure */
130362306a36Sopenharmony_ci	dev->mii.dev = dev->net;
130462306a36Sopenharmony_ci	dev->mii.mdio_read = ax88179_mdio_read;
130562306a36Sopenharmony_ci	dev->mii.mdio_write = ax88179_mdio_write;
130662306a36Sopenharmony_ci	dev->mii.phy_id_mask = 0xff;
130762306a36Sopenharmony_ci	dev->mii.reg_num_mask = 0xff;
130862306a36Sopenharmony_ci	dev->mii.phy_id = 0x03;
130962306a36Sopenharmony_ci	dev->mii.supports_gmii = 1;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	dev->net->features |= NETIF_F_SG | NETIF_F_IP_CSUM |
131262306a36Sopenharmony_ci			      NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	dev->net->hw_features |= dev->net->features;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	netif_set_tso_max_size(dev->net, 16384);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	return 0;
131962306a36Sopenharmony_ci}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cistatic void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	struct ax88179_data *ax179_data = dev->driver_priv;
132462306a36Sopenharmony_ci	u16 tmp16;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	/* Configure RX control register => stop operation */
132762306a36Sopenharmony_ci	tmp16 = AX_RX_CTL_STOP;
132862306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	tmp16 = 0;
133162306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	/* Power down ethernet PHY */
133462306a36Sopenharmony_ci	tmp16 = 0;
133562306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	kfree(ax179_data);
133862306a36Sopenharmony_ci}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_cistatic void
134162306a36Sopenharmony_ciax88179_rx_checksum(struct sk_buff *skb, u32 *pkt_hdr)
134262306a36Sopenharmony_ci{
134362306a36Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	/* checksum error bit is set */
134662306a36Sopenharmony_ci	if ((*pkt_hdr & AX_RXHDR_L3CSUM_ERR) ||
134762306a36Sopenharmony_ci	    (*pkt_hdr & AX_RXHDR_L4CSUM_ERR))
134862306a36Sopenharmony_ci		return;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	/* It must be a TCP or UDP packet with a valid checksum */
135162306a36Sopenharmony_ci	if (((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_TCP) ||
135262306a36Sopenharmony_ci	    ((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_UDP))
135362306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_UNNECESSARY;
135462306a36Sopenharmony_ci}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_cistatic int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
135762306a36Sopenharmony_ci{
135862306a36Sopenharmony_ci	struct sk_buff *ax_skb;
135962306a36Sopenharmony_ci	int pkt_cnt;
136062306a36Sopenharmony_ci	u32 rx_hdr;
136162306a36Sopenharmony_ci	u16 hdr_off;
136262306a36Sopenharmony_ci	u32 *pkt_hdr;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	/* At the end of the SKB, there's a header telling us how many packets
136562306a36Sopenharmony_ci	 * are bundled into this buffer and where we can find an array of
136662306a36Sopenharmony_ci	 * per-packet metadata (which contains elements encoded into u16).
136762306a36Sopenharmony_ci	 */
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	/* SKB contents for current firmware:
137062306a36Sopenharmony_ci	 *   <packet 1> <padding>
137162306a36Sopenharmony_ci	 *   ...
137262306a36Sopenharmony_ci	 *   <packet N> <padding>
137362306a36Sopenharmony_ci	 *   <per-packet metadata entry 1> <dummy header>
137462306a36Sopenharmony_ci	 *   ...
137562306a36Sopenharmony_ci	 *   <per-packet metadata entry N> <dummy header>
137662306a36Sopenharmony_ci	 *   <padding2> <rx_hdr>
137762306a36Sopenharmony_ci	 *
137862306a36Sopenharmony_ci	 * where:
137962306a36Sopenharmony_ci	 *   <packet N> contains pkt_len bytes:
138062306a36Sopenharmony_ci	 *		2 bytes of IP alignment pseudo header
138162306a36Sopenharmony_ci	 *		packet received
138262306a36Sopenharmony_ci	 *   <per-packet metadata entry N> contains 4 bytes:
138362306a36Sopenharmony_ci	 *		pkt_len and fields AX_RXHDR_*
138462306a36Sopenharmony_ci	 *   <padding>	0-7 bytes to terminate at
138562306a36Sopenharmony_ci	 *		8 bytes boundary (64-bit).
138662306a36Sopenharmony_ci	 *   <padding2> 4 bytes to make rx_hdr terminate at
138762306a36Sopenharmony_ci	 *		8 bytes boundary (64-bit)
138862306a36Sopenharmony_ci	 *   <dummy-header> contains 4 bytes:
138962306a36Sopenharmony_ci	 *		pkt_len=0 and AX_RXHDR_DROP_ERR
139062306a36Sopenharmony_ci	 *   <rx-hdr>	contains 4 bytes:
139162306a36Sopenharmony_ci	 *		pkt_cnt and hdr_off (offset of
139262306a36Sopenharmony_ci	 *		  <per-packet metadata entry 1>)
139362306a36Sopenharmony_ci	 *
139462306a36Sopenharmony_ci	 * pkt_cnt is number of entrys in the per-packet metadata.
139562306a36Sopenharmony_ci	 * In current firmware there is 2 entrys per packet.
139662306a36Sopenharmony_ci	 * The first points to the packet and the
139762306a36Sopenharmony_ci	 *  second is a dummy header.
139862306a36Sopenharmony_ci	 * This was done probably to align fields in 64-bit and
139962306a36Sopenharmony_ci	 *  maintain compatibility with old firmware.
140062306a36Sopenharmony_ci	 * This code assumes that <dummy header> and <padding2> are
140162306a36Sopenharmony_ci	 *  optional.
140262306a36Sopenharmony_ci	 */
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if (skb->len < 4)
140562306a36Sopenharmony_ci		return 0;
140662306a36Sopenharmony_ci	skb_trim(skb, skb->len - 4);
140762306a36Sopenharmony_ci	rx_hdr = get_unaligned_le32(skb_tail_pointer(skb));
140862306a36Sopenharmony_ci	pkt_cnt = (u16)rx_hdr;
140962306a36Sopenharmony_ci	hdr_off = (u16)(rx_hdr >> 16);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	if (pkt_cnt == 0)
141262306a36Sopenharmony_ci		return 0;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	/* Make sure that the bounds of the metadata array are inside the SKB
141562306a36Sopenharmony_ci	 * (and in front of the counter at the end).
141662306a36Sopenharmony_ci	 */
141762306a36Sopenharmony_ci	if (pkt_cnt * 4 + hdr_off > skb->len)
141862306a36Sopenharmony_ci		return 0;
141962306a36Sopenharmony_ci	pkt_hdr = (u32 *)(skb->data + hdr_off);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	/* Packets must not overlap the metadata array */
142262306a36Sopenharmony_ci	skb_trim(skb, hdr_off);
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	for (; pkt_cnt > 0; pkt_cnt--, pkt_hdr++) {
142562306a36Sopenharmony_ci		u16 pkt_len_plus_padd;
142662306a36Sopenharmony_ci		u16 pkt_len;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci		le32_to_cpus(pkt_hdr);
142962306a36Sopenharmony_ci		pkt_len = (*pkt_hdr >> 16) & 0x1fff;
143062306a36Sopenharmony_ci		pkt_len_plus_padd = (pkt_len + 7) & 0xfff8;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci		/* Skip dummy header used for alignment
143362306a36Sopenharmony_ci		 */
143462306a36Sopenharmony_ci		if (pkt_len == 0)
143562306a36Sopenharmony_ci			continue;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci		if (pkt_len_plus_padd > skb->len)
143862306a36Sopenharmony_ci			return 0;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci		/* Check CRC or runt packet */
144162306a36Sopenharmony_ci		if ((*pkt_hdr & (AX_RXHDR_CRC_ERR | AX_RXHDR_DROP_ERR)) ||
144262306a36Sopenharmony_ci		    pkt_len < 2 + ETH_HLEN) {
144362306a36Sopenharmony_ci			dev->net->stats.rx_errors++;
144462306a36Sopenharmony_ci			skb_pull(skb, pkt_len_plus_padd);
144562306a36Sopenharmony_ci			continue;
144662306a36Sopenharmony_ci		}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci		/* last packet */
144962306a36Sopenharmony_ci		if (pkt_len_plus_padd == skb->len) {
145062306a36Sopenharmony_ci			skb_trim(skb, pkt_len);
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci			/* Skip IP alignment pseudo header */
145362306a36Sopenharmony_ci			skb_pull(skb, 2);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci			skb->truesize = SKB_TRUESIZE(pkt_len_plus_padd);
145662306a36Sopenharmony_ci			ax88179_rx_checksum(skb, pkt_hdr);
145762306a36Sopenharmony_ci			return 1;
145862306a36Sopenharmony_ci		}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci		ax_skb = skb_clone(skb, GFP_ATOMIC);
146162306a36Sopenharmony_ci		if (!ax_skb)
146262306a36Sopenharmony_ci			return 0;
146362306a36Sopenharmony_ci		skb_trim(ax_skb, pkt_len);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci		/* Skip IP alignment pseudo header */
146662306a36Sopenharmony_ci		skb_pull(ax_skb, 2);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci		skb->truesize = pkt_len_plus_padd +
146962306a36Sopenharmony_ci				SKB_DATA_ALIGN(sizeof(struct sk_buff));
147062306a36Sopenharmony_ci		ax88179_rx_checksum(ax_skb, pkt_hdr);
147162306a36Sopenharmony_ci		usbnet_skb_return(dev, ax_skb);
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci		skb_pull(skb, pkt_len_plus_padd);
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	return 0;
147762306a36Sopenharmony_ci}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_cistatic struct sk_buff *
148062306a36Sopenharmony_ciax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
148162306a36Sopenharmony_ci{
148262306a36Sopenharmony_ci	u32 tx_hdr1, tx_hdr2;
148362306a36Sopenharmony_ci	int frame_size = dev->maxpacket;
148462306a36Sopenharmony_ci	int headroom;
148562306a36Sopenharmony_ci	void *ptr;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	tx_hdr1 = skb->len;
148862306a36Sopenharmony_ci	tx_hdr2 = skb_shinfo(skb)->gso_size; /* Set TSO mss */
148962306a36Sopenharmony_ci	if (((skb->len + 8) % frame_size) == 0)
149062306a36Sopenharmony_ci		tx_hdr2 |= 0x80008000;	/* Enable padding */
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	headroom = skb_headroom(skb) - 8;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb))
149562306a36Sopenharmony_ci		return NULL;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	if ((skb_header_cloned(skb) || headroom < 0) &&
149862306a36Sopenharmony_ci	    pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) {
149962306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
150062306a36Sopenharmony_ci		return NULL;
150162306a36Sopenharmony_ci	}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	ptr = skb_push(skb, 8);
150462306a36Sopenharmony_ci	put_unaligned_le32(tx_hdr1, ptr);
150562306a36Sopenharmony_ci	put_unaligned_le32(tx_hdr2, ptr + 4);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	usbnet_set_skb_tx_stats(skb, (skb_shinfo(skb)->gso_segs ?: 1), 0);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	return skb;
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_cistatic int ax88179_link_reset(struct usbnet *dev)
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	struct ax88179_data *ax179_data = dev->driver_priv;
151562306a36Sopenharmony_ci	u8 tmp[5], link_sts;
151662306a36Sopenharmony_ci	u16 mode, tmp16, delay = HZ / 10;
151762306a36Sopenharmony_ci	u32 tmp32 = 0x40000000;
151862306a36Sopenharmony_ci	unsigned long jtimeout;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	jtimeout = jiffies + delay;
152162306a36Sopenharmony_ci	while (tmp32 & 0x40000000) {
152262306a36Sopenharmony_ci		mode = 0;
152362306a36Sopenharmony_ci		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &mode);
152462306a36Sopenharmony_ci		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2,
152562306a36Sopenharmony_ci				  &ax179_data->rxctl);
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci		/*link up, check the usb device control TX FIFO full or empty*/
152862306a36Sopenharmony_ci		ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci		if (time_after(jiffies, jtimeout))
153162306a36Sopenharmony_ci			return 0;
153262306a36Sopenharmony_ci	}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
153562306a36Sopenharmony_ci	       AX_MEDIUM_RXFLOW_CTRLEN;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS,
153862306a36Sopenharmony_ci			 1, 1, &link_sts);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
154162306a36Sopenharmony_ci			 GMII_PHY_PHYSR, 2, &tmp16);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	if (!(tmp16 & GMII_PHY_PHYSR_LINK)) {
154462306a36Sopenharmony_ci		return 0;
154562306a36Sopenharmony_ci	} else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
154662306a36Sopenharmony_ci		mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ;
154762306a36Sopenharmony_ci		if (dev->net->mtu > 1500)
154862306a36Sopenharmony_ci			mode |= AX_MEDIUM_JUMBO_EN;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci		if (link_sts & AX_USB_SS)
155162306a36Sopenharmony_ci			memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
155262306a36Sopenharmony_ci		else if (link_sts & AX_USB_HS)
155362306a36Sopenharmony_ci			memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5);
155462306a36Sopenharmony_ci		else
155562306a36Sopenharmony_ci			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
155662306a36Sopenharmony_ci	} else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
155762306a36Sopenharmony_ci		mode |= AX_MEDIUM_PS;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci		if (link_sts & (AX_USB_SS | AX_USB_HS))
156062306a36Sopenharmony_ci			memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5);
156162306a36Sopenharmony_ci		else
156262306a36Sopenharmony_ci			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
156362306a36Sopenharmony_ci	} else {
156462306a36Sopenharmony_ci		memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	/* RX bulk configuration */
156862306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	dev->rx_urb_size = (1024 * (tmp[3] + 2));
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	if (tmp16 & GMII_PHY_PHYSR_FULL)
157362306a36Sopenharmony_ci		mode |= AX_MEDIUM_FULL_DUPLEX;
157462306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
157562306a36Sopenharmony_ci			  2, 2, &mode);
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	ax179_data->eee_enabled = ax88179_chk_eee(dev);
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	netif_carrier_on(dev->net);
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	return 0;
158262306a36Sopenharmony_ci}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_cistatic int ax88179_reset(struct usbnet *dev)
158562306a36Sopenharmony_ci{
158662306a36Sopenharmony_ci	u8 buf[5];
158762306a36Sopenharmony_ci	u16 *tmp16;
158862306a36Sopenharmony_ci	u8 *tmp;
158962306a36Sopenharmony_ci	struct ax88179_data *ax179_data = dev->driver_priv;
159062306a36Sopenharmony_ci	struct ethtool_eee eee_data;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	tmp16 = (u16 *)buf;
159362306a36Sopenharmony_ci	tmp = (u8 *)buf;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	/* Power up ethernet PHY */
159662306a36Sopenharmony_ci	*tmp16 = 0;
159762306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
160062306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
160162306a36Sopenharmony_ci	msleep(500);
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
160462306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
160562306a36Sopenharmony_ci	msleep(200);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	/* Ethernet PHY Auto Detach*/
160862306a36Sopenharmony_ci	ax88179_auto_detach(dev);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	/* Read MAC address from DTB or asix chip */
161162306a36Sopenharmony_ci	ax88179_get_mac_addr(dev);
161262306a36Sopenharmony_ci	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	/* RX bulk configuration */
161562306a36Sopenharmony_ci	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
161662306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	dev->rx_urb_size = 1024 * 20;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	*tmp = 0x34;
162162306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	*tmp = 0x52;
162462306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
162562306a36Sopenharmony_ci			  1, 1, tmp);
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	/* Enable checksum offload */
162862306a36Sopenharmony_ci	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
162962306a36Sopenharmony_ci	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
163062306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
163362306a36Sopenharmony_ci	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
163462306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	/* Configure RX control register => start operation */
163762306a36Sopenharmony_ci	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
163862306a36Sopenharmony_ci		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
163962306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
164262306a36Sopenharmony_ci	       AX_MONITOR_MODE_RWMP;
164362306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	/* Configure default medium type => giga */
164662306a36Sopenharmony_ci	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
164762306a36Sopenharmony_ci		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX |
164862306a36Sopenharmony_ci		 AX_MEDIUM_GIGAMODE;
164962306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
165062306a36Sopenharmony_ci			  2, 2, tmp16);
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	/* Check if WoL is supported */
165362306a36Sopenharmony_ci	ax179_data->wol_supported = 0;
165462306a36Sopenharmony_ci	if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
165562306a36Sopenharmony_ci			     1, 1, &tmp) > 0)
165662306a36Sopenharmony_ci		ax179_data->wol_supported = WAKE_MAGIC | WAKE_PHY;
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	ax88179_led_setting(dev);
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	ax179_data->eee_enabled = 0;
166162306a36Sopenharmony_ci	ax179_data->eee_active = 0;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	ax88179_disable_eee(dev);
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	ax88179_ethtool_get_eee(dev, &eee_data);
166662306a36Sopenharmony_ci	eee_data.advertised = 0;
166762306a36Sopenharmony_ci	ax88179_ethtool_set_eee(dev, &eee_data);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	/* Restart autoneg */
167062306a36Sopenharmony_ci	mii_nway_restart(&dev->mii);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	usbnet_link_change(dev, 0, 0);
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	return 0;
167562306a36Sopenharmony_ci}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_cistatic int ax88179_stop(struct usbnet *dev)
167862306a36Sopenharmony_ci{
167962306a36Sopenharmony_ci	u16 tmp16;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
168262306a36Sopenharmony_ci			 2, 2, &tmp16);
168362306a36Sopenharmony_ci	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
168462306a36Sopenharmony_ci	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
168562306a36Sopenharmony_ci			  2, 2, &tmp16);
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	return 0;
168862306a36Sopenharmony_ci}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_cistatic const struct driver_info ax88179_info = {
169162306a36Sopenharmony_ci	.description = "ASIX AX88179 USB 3.0 Gigabit Ethernet",
169262306a36Sopenharmony_ci	.bind = ax88179_bind,
169362306a36Sopenharmony_ci	.unbind = ax88179_unbind,
169462306a36Sopenharmony_ci	.status = ax88179_status,
169562306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
169662306a36Sopenharmony_ci	.reset = ax88179_reset,
169762306a36Sopenharmony_ci	.stop = ax88179_stop,
169862306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
169962306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
170062306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
170162306a36Sopenharmony_ci};
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_cistatic const struct driver_info ax88178a_info = {
170462306a36Sopenharmony_ci	.description = "ASIX AX88178A USB 2.0 Gigabit Ethernet",
170562306a36Sopenharmony_ci	.bind = ax88179_bind,
170662306a36Sopenharmony_ci	.unbind = ax88179_unbind,
170762306a36Sopenharmony_ci	.status = ax88179_status,
170862306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
170962306a36Sopenharmony_ci	.reset = ax88179_reset,
171062306a36Sopenharmony_ci	.stop = ax88179_stop,
171162306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
171262306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
171362306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
171462306a36Sopenharmony_ci};
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_cistatic const struct driver_info cypress_GX3_info = {
171762306a36Sopenharmony_ci	.description = "Cypress GX3 SuperSpeed to Gigabit Ethernet Controller",
171862306a36Sopenharmony_ci	.bind = ax88179_bind,
171962306a36Sopenharmony_ci	.unbind = ax88179_unbind,
172062306a36Sopenharmony_ci	.status = ax88179_status,
172162306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
172262306a36Sopenharmony_ci	.reset = ax88179_reset,
172362306a36Sopenharmony_ci	.stop = ax88179_stop,
172462306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
172562306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
172662306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
172762306a36Sopenharmony_ci};
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_cistatic const struct driver_info dlink_dub1312_info = {
173062306a36Sopenharmony_ci	.description = "D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter",
173162306a36Sopenharmony_ci	.bind = ax88179_bind,
173262306a36Sopenharmony_ci	.unbind = ax88179_unbind,
173362306a36Sopenharmony_ci	.status = ax88179_status,
173462306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
173562306a36Sopenharmony_ci	.reset = ax88179_reset,
173662306a36Sopenharmony_ci	.stop = ax88179_stop,
173762306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
173862306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
173962306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
174062306a36Sopenharmony_ci};
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_cistatic const struct driver_info sitecom_info = {
174362306a36Sopenharmony_ci	.description = "Sitecom USB 3.0 to Gigabit Adapter",
174462306a36Sopenharmony_ci	.bind = ax88179_bind,
174562306a36Sopenharmony_ci	.unbind = ax88179_unbind,
174662306a36Sopenharmony_ci	.status = ax88179_status,
174762306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
174862306a36Sopenharmony_ci	.reset = ax88179_reset,
174962306a36Sopenharmony_ci	.stop = ax88179_stop,
175062306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
175162306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
175262306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
175362306a36Sopenharmony_ci};
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_cistatic const struct driver_info samsung_info = {
175662306a36Sopenharmony_ci	.description = "Samsung USB Ethernet Adapter",
175762306a36Sopenharmony_ci	.bind = ax88179_bind,
175862306a36Sopenharmony_ci	.unbind = ax88179_unbind,
175962306a36Sopenharmony_ci	.status = ax88179_status,
176062306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
176162306a36Sopenharmony_ci	.reset = ax88179_reset,
176262306a36Sopenharmony_ci	.stop = ax88179_stop,
176362306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
176462306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
176562306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
176662306a36Sopenharmony_ci};
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_cistatic const struct driver_info lenovo_info = {
176962306a36Sopenharmony_ci	.description = "Lenovo OneLinkDock Gigabit LAN",
177062306a36Sopenharmony_ci	.bind = ax88179_bind,
177162306a36Sopenharmony_ci	.unbind = ax88179_unbind,
177262306a36Sopenharmony_ci	.status = ax88179_status,
177362306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
177462306a36Sopenharmony_ci	.reset = ax88179_reset,
177562306a36Sopenharmony_ci	.stop = ax88179_stop,
177662306a36Sopenharmony_ci	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
177762306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
177862306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
177962306a36Sopenharmony_ci};
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_cistatic const struct driver_info belkin_info = {
178262306a36Sopenharmony_ci	.description = "Belkin USB Ethernet Adapter",
178362306a36Sopenharmony_ci	.bind	= ax88179_bind,
178462306a36Sopenharmony_ci	.unbind = ax88179_unbind,
178562306a36Sopenharmony_ci	.status = ax88179_status,
178662306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
178762306a36Sopenharmony_ci	.reset	= ax88179_reset,
178862306a36Sopenharmony_ci	.stop	= ax88179_stop,
178962306a36Sopenharmony_ci	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
179062306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
179162306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
179262306a36Sopenharmony_ci};
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_cistatic const struct driver_info toshiba_info = {
179562306a36Sopenharmony_ci	.description = "Toshiba USB Ethernet Adapter",
179662306a36Sopenharmony_ci	.bind	= ax88179_bind,
179762306a36Sopenharmony_ci	.unbind = ax88179_unbind,
179862306a36Sopenharmony_ci	.status = ax88179_status,
179962306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
180062306a36Sopenharmony_ci	.reset	= ax88179_reset,
180162306a36Sopenharmony_ci	.stop = ax88179_stop,
180262306a36Sopenharmony_ci	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
180362306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
180462306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
180562306a36Sopenharmony_ci};
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_cistatic const struct driver_info mct_info = {
180862306a36Sopenharmony_ci	.description = "MCT USB 3.0 Gigabit Ethernet Adapter",
180962306a36Sopenharmony_ci	.bind	= ax88179_bind,
181062306a36Sopenharmony_ci	.unbind	= ax88179_unbind,
181162306a36Sopenharmony_ci	.status	= ax88179_status,
181262306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
181362306a36Sopenharmony_ci	.reset	= ax88179_reset,
181462306a36Sopenharmony_ci	.stop	= ax88179_stop,
181562306a36Sopenharmony_ci	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
181662306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
181762306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
181862306a36Sopenharmony_ci};
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_cistatic const struct driver_info at_umc2000_info = {
182162306a36Sopenharmony_ci	.description = "AT-UMC2000 USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter",
182262306a36Sopenharmony_ci	.bind   = ax88179_bind,
182362306a36Sopenharmony_ci	.unbind = ax88179_unbind,
182462306a36Sopenharmony_ci	.status = ax88179_status,
182562306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
182662306a36Sopenharmony_ci	.reset  = ax88179_reset,
182762306a36Sopenharmony_ci	.stop   = ax88179_stop,
182862306a36Sopenharmony_ci	.flags  = FLAG_ETHER | FLAG_FRAMING_AX,
182962306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
183062306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
183162306a36Sopenharmony_ci};
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_cistatic const struct driver_info at_umc200_info = {
183462306a36Sopenharmony_ci	.description = "AT-UMC200 USB 3.0/USB 3.1 Gen 1 to Fast Ethernet Adapter",
183562306a36Sopenharmony_ci	.bind   = ax88179_bind,
183662306a36Sopenharmony_ci	.unbind = ax88179_unbind,
183762306a36Sopenharmony_ci	.status = ax88179_status,
183862306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
183962306a36Sopenharmony_ci	.reset  = ax88179_reset,
184062306a36Sopenharmony_ci	.stop   = ax88179_stop,
184162306a36Sopenharmony_ci	.flags  = FLAG_ETHER | FLAG_FRAMING_AX,
184262306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
184362306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
184462306a36Sopenharmony_ci};
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic const struct driver_info at_umc2000sp_info = {
184762306a36Sopenharmony_ci	.description = "AT-UMC2000/SP USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter",
184862306a36Sopenharmony_ci	.bind   = ax88179_bind,
184962306a36Sopenharmony_ci	.unbind = ax88179_unbind,
185062306a36Sopenharmony_ci	.status = ax88179_status,
185162306a36Sopenharmony_ci	.link_reset = ax88179_link_reset,
185262306a36Sopenharmony_ci	.reset  = ax88179_reset,
185362306a36Sopenharmony_ci	.stop   = ax88179_stop,
185462306a36Sopenharmony_ci	.flags  = FLAG_ETHER | FLAG_FRAMING_AX,
185562306a36Sopenharmony_ci	.rx_fixup = ax88179_rx_fixup,
185662306a36Sopenharmony_ci	.tx_fixup = ax88179_tx_fixup,
185762306a36Sopenharmony_ci};
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_cistatic const struct usb_device_id products[] = {
186062306a36Sopenharmony_ci{
186162306a36Sopenharmony_ci	/* ASIX AX88179 10/100/1000 */
186262306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x1790, 0xff, 0xff, 0),
186362306a36Sopenharmony_ci	.driver_info = (unsigned long)&ax88179_info,
186462306a36Sopenharmony_ci}, {
186562306a36Sopenharmony_ci	/* ASIX AX88178A 10/100/1000 */
186662306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x178a, 0xff, 0xff, 0),
186762306a36Sopenharmony_ci	.driver_info = (unsigned long)&ax88178a_info,
186862306a36Sopenharmony_ci}, {
186962306a36Sopenharmony_ci	/* Cypress GX3 SuperSpeed to Gigabit Ethernet Bridge Controller */
187062306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x04b4, 0x3610, 0xff, 0xff, 0),
187162306a36Sopenharmony_ci	.driver_info = (unsigned long)&cypress_GX3_info,
187262306a36Sopenharmony_ci}, {
187362306a36Sopenharmony_ci	/* D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter */
187462306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x4a00, 0xff, 0xff, 0),
187562306a36Sopenharmony_ci	.driver_info = (unsigned long)&dlink_dub1312_info,
187662306a36Sopenharmony_ci}, {
187762306a36Sopenharmony_ci	/* Sitecom USB 3.0 to Gigabit Adapter */
187862306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x0df6, 0x0072, 0xff, 0xff, 0),
187962306a36Sopenharmony_ci	.driver_info = (unsigned long)&sitecom_info,
188062306a36Sopenharmony_ci}, {
188162306a36Sopenharmony_ci	/* Samsung USB Ethernet Adapter */
188262306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x04e8, 0xa100, 0xff, 0xff, 0),
188362306a36Sopenharmony_ci	.driver_info = (unsigned long)&samsung_info,
188462306a36Sopenharmony_ci}, {
188562306a36Sopenharmony_ci	/* Lenovo OneLinkDock Gigabit LAN */
188662306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x17ef, 0x304b, 0xff, 0xff, 0),
188762306a36Sopenharmony_ci	.driver_info = (unsigned long)&lenovo_info,
188862306a36Sopenharmony_ci}, {
188962306a36Sopenharmony_ci	/* Belkin B2B128 USB 3.0 Hub + Gigabit Ethernet Adapter */
189062306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x050d, 0x0128, 0xff, 0xff, 0),
189162306a36Sopenharmony_ci	.driver_info = (unsigned long)&belkin_info,
189262306a36Sopenharmony_ci}, {
189362306a36Sopenharmony_ci	/* Toshiba USB 3.0 GBit Ethernet Adapter */
189462306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x0a13, 0xff, 0xff, 0),
189562306a36Sopenharmony_ci	.driver_info = (unsigned long)&toshiba_info,
189662306a36Sopenharmony_ci}, {
189762306a36Sopenharmony_ci	/* Magic Control Technology U3-A9003 USB 3.0 Gigabit Ethernet Adapter */
189862306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x0711, 0x0179, 0xff, 0xff, 0),
189962306a36Sopenharmony_ci	.driver_info = (unsigned long)&mct_info,
190062306a36Sopenharmony_ci}, {
190162306a36Sopenharmony_ci	/* Allied Telesis AT-UMC2000 USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter */
190262306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x07c9, 0x000e, 0xff, 0xff, 0),
190362306a36Sopenharmony_ci	.driver_info = (unsigned long)&at_umc2000_info,
190462306a36Sopenharmony_ci}, {
190562306a36Sopenharmony_ci	/* Allied Telesis AT-UMC200 USB 3.0/USB 3.1 Gen 1 to Fast Ethernet Adapter */
190662306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x07c9, 0x000f, 0xff, 0xff, 0),
190762306a36Sopenharmony_ci	.driver_info = (unsigned long)&at_umc200_info,
190862306a36Sopenharmony_ci}, {
190962306a36Sopenharmony_ci	/* Allied Telesis AT-UMC2000/SP USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter */
191062306a36Sopenharmony_ci	USB_DEVICE_AND_INTERFACE_INFO(0x07c9, 0x0010, 0xff, 0xff, 0),
191162306a36Sopenharmony_ci	.driver_info = (unsigned long)&at_umc2000sp_info,
191262306a36Sopenharmony_ci},
191362306a36Sopenharmony_ci	{ },
191462306a36Sopenharmony_ci};
191562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products);
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_cistatic struct usb_driver ax88179_178a_driver = {
191862306a36Sopenharmony_ci	.name =		"ax88179_178a",
191962306a36Sopenharmony_ci	.id_table =	products,
192062306a36Sopenharmony_ci	.probe =	usbnet_probe,
192162306a36Sopenharmony_ci	.suspend =	ax88179_suspend,
192262306a36Sopenharmony_ci	.resume =	ax88179_resume,
192362306a36Sopenharmony_ci	.reset_resume =	ax88179_resume,
192462306a36Sopenharmony_ci	.disconnect =	ax88179_disconnect,
192562306a36Sopenharmony_ci	.supports_autosuspend = 1,
192662306a36Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
192762306a36Sopenharmony_ci};
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_cimodule_usb_driver(ax88179_178a_driver);
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ciMODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices");
193262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1933