mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-09-18 22:14:16 +00:00 
			
		
		
		
	 058722ee35
			
		
	
	
		058722ee35
		
	
	
	
	
		
			
			Avoid spurious link status logs that may ultimately be wrong; for example,
if the link is set to down with the cable plugged, then the cable is
unplugged and after this the link is set to up, the last new log that is
appearing is incorrectly telling that the link is up.
In order to avoid errors, show link status logs after link_reset
processing, and in order to avoid spurious as much as possible, only show
the link loss when some link status change is detected.
cc: stable@vger.kernel.org
Fixes: e2ca90c276 ("ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver")
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
		
	
			
		
			
				
	
	
		
			1958 lines
		
	
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1958 lines
		
	
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
|  * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices
 | |
|  *
 | |
|  * Copyright (C) 2011-2013 ASIX
 | |
|  */
 | |
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/etherdevice.h>
 | |
| #include <linux/mii.h>
 | |
| #include <linux/usb.h>
 | |
| #include <linux/crc32.h>
 | |
| #include <linux/usb/usbnet.h>
 | |
| #include <uapi/linux/mdio.h>
 | |
| #include <linux/mdio.h>
 | |
| 
 | |
| #define AX88179_PHY_ID				0x03
 | |
| #define AX_EEPROM_LEN				0x100
 | |
| #define AX88179_EEPROM_MAGIC			0x17900b95
 | |
| #define AX_MCAST_FLTSIZE			8
 | |
| #define AX_MAX_MCAST				64
 | |
| #define AX_INT_PPLS_LINK			((u32)BIT(16))
 | |
| #define AX_RXHDR_L4_TYPE_MASK			0x1c
 | |
| #define AX_RXHDR_L4_TYPE_UDP			4
 | |
| #define AX_RXHDR_L4_TYPE_TCP			16
 | |
| #define AX_RXHDR_L3CSUM_ERR			2
 | |
| #define AX_RXHDR_L4CSUM_ERR			1
 | |
| #define AX_RXHDR_CRC_ERR			((u32)BIT(29))
 | |
| #define AX_RXHDR_DROP_ERR			((u32)BIT(31))
 | |
| #define AX_ACCESS_MAC				0x01
 | |
| #define AX_ACCESS_PHY				0x02
 | |
| #define AX_ACCESS_EEPROM			0x04
 | |
| #define AX_ACCESS_EFUS				0x05
 | |
| #define AX_RELOAD_EEPROM_EFUSE			0x06
 | |
| #define AX_PAUSE_WATERLVL_HIGH			0x54
 | |
| #define AX_PAUSE_WATERLVL_LOW			0x55
 | |
| 
 | |
| #define PHYSICAL_LINK_STATUS			0x02
 | |
| 	#define	AX_USB_SS		0x04
 | |
| 	#define	AX_USB_HS		0x02
 | |
| 
 | |
| #define GENERAL_STATUS				0x03
 | |
| /* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */
 | |
| 	#define	AX_SECLD		0x04
 | |
| 
 | |
| #define AX_SROM_ADDR				0x07
 | |
| #define AX_SROM_CMD				0x0a
 | |
| 	#define EEP_RD			0x04
 | |
| 	#define EEP_BUSY		0x10
 | |
| 
 | |
| #define AX_SROM_DATA_LOW			0x08
 | |
| #define AX_SROM_DATA_HIGH			0x09
 | |
| 
 | |
| #define AX_RX_CTL				0x0b
 | |
| 	#define AX_RX_CTL_DROPCRCERR	0x0100
 | |
| 	#define AX_RX_CTL_IPE		0x0200
 | |
| 	#define AX_RX_CTL_START		0x0080
 | |
| 	#define AX_RX_CTL_AP		0x0020
 | |
| 	#define AX_RX_CTL_AM		0x0010
 | |
| 	#define AX_RX_CTL_AB		0x0008
 | |
| 	#define AX_RX_CTL_AMALL		0x0002
 | |
| 	#define AX_RX_CTL_PRO		0x0001
 | |
| 	#define AX_RX_CTL_STOP		0x0000
 | |
| 
 | |
| #define AX_NODE_ID				0x10
 | |
| #define AX_MULFLTARY				0x16
 | |
| 
 | |
| #define AX_MEDIUM_STATUS_MODE			0x22
 | |
| 	#define AX_MEDIUM_GIGAMODE	0x01
 | |
| 	#define AX_MEDIUM_FULL_DUPLEX	0x02
 | |
| 	#define AX_MEDIUM_EN_125MHZ	0x08
 | |
| 	#define AX_MEDIUM_RXFLOW_CTRLEN	0x10
 | |
| 	#define AX_MEDIUM_TXFLOW_CTRLEN	0x20
 | |
| 	#define AX_MEDIUM_RECEIVE_EN	0x100
 | |
| 	#define AX_MEDIUM_PS		0x200
 | |
| 	#define AX_MEDIUM_JUMBO_EN	0x8040
 | |
| 
 | |
| #define AX_MONITOR_MOD				0x24
 | |
| 	#define AX_MONITOR_MODE_RWLC	0x02
 | |
| 	#define AX_MONITOR_MODE_RWMP	0x04
 | |
| 	#define AX_MONITOR_MODE_PMEPOL	0x20
 | |
| 	#define AX_MONITOR_MODE_PMETYPE	0x40
 | |
| 
 | |
| #define AX_GPIO_CTRL				0x25
 | |
| 	#define AX_GPIO_CTRL_GPIO3EN	0x80
 | |
| 	#define AX_GPIO_CTRL_GPIO2EN	0x40
 | |
| 	#define AX_GPIO_CTRL_GPIO1EN	0x20
 | |
| 
 | |
| #define AX_PHYPWR_RSTCTL			0x26
 | |
| 	#define AX_PHYPWR_RSTCTL_BZ	0x0010
 | |
| 	#define AX_PHYPWR_RSTCTL_IPRL	0x0020
 | |
| 	#define AX_PHYPWR_RSTCTL_AT	0x1000
 | |
| 
 | |
| #define AX_RX_BULKIN_QCTRL			0x2e
 | |
| #define AX_CLK_SELECT				0x33
 | |
| 	#define AX_CLK_SELECT_BCS	0x01
 | |
| 	#define AX_CLK_SELECT_ACS	0x02
 | |
| 	#define AX_CLK_SELECT_ULR	0x08
 | |
| 
 | |
| #define AX_RXCOE_CTL				0x34
 | |
| 	#define AX_RXCOE_IP		0x01
 | |
| 	#define AX_RXCOE_TCP		0x02
 | |
| 	#define AX_RXCOE_UDP		0x04
 | |
| 	#define AX_RXCOE_TCPV6		0x20
 | |
| 	#define AX_RXCOE_UDPV6		0x40
 | |
| 
 | |
| #define AX_TXCOE_CTL				0x35
 | |
| 	#define AX_TXCOE_IP		0x01
 | |
| 	#define AX_TXCOE_TCP		0x02
 | |
| 	#define AX_TXCOE_UDP		0x04
 | |
| 	#define AX_TXCOE_TCPV6		0x20
 | |
| 	#define AX_TXCOE_UDPV6		0x40
 | |
| 
 | |
| #define AX_LEDCTRL				0x73
 | |
| 
 | |
| #define GMII_PHY_PHYSR				0x11
 | |
| 	#define GMII_PHY_PHYSR_SMASK	0xc000
 | |
| 	#define GMII_PHY_PHYSR_GIGA	0x8000
 | |
| 	#define GMII_PHY_PHYSR_100	0x4000
 | |
| 	#define GMII_PHY_PHYSR_FULL	0x2000
 | |
| 	#define GMII_PHY_PHYSR_LINK	0x400
 | |
| 
 | |
| #define GMII_LED_ACT				0x1a
 | |
| 	#define	GMII_LED_ACTIVE_MASK	0xff8f
 | |
| 	#define	GMII_LED0_ACTIVE	BIT(4)
 | |
| 	#define	GMII_LED1_ACTIVE	BIT(5)
 | |
| 	#define	GMII_LED2_ACTIVE	BIT(6)
 | |
| 
 | |
| #define GMII_LED_LINK				0x1c
 | |
| 	#define	GMII_LED_LINK_MASK	0xf888
 | |
| 	#define	GMII_LED0_LINK_10	BIT(0)
 | |
| 	#define	GMII_LED0_LINK_100	BIT(1)
 | |
| 	#define	GMII_LED0_LINK_1000	BIT(2)
 | |
| 	#define	GMII_LED1_LINK_10	BIT(4)
 | |
| 	#define	GMII_LED1_LINK_100	BIT(5)
 | |
| 	#define	GMII_LED1_LINK_1000	BIT(6)
 | |
| 	#define	GMII_LED2_LINK_10	BIT(8)
 | |
| 	#define	GMII_LED2_LINK_100	BIT(9)
 | |
| 	#define	GMII_LED2_LINK_1000	BIT(10)
 | |
| 	#define	LED0_ACTIVE		BIT(0)
 | |
| 	#define	LED0_LINK_10		BIT(1)
 | |
| 	#define	LED0_LINK_100		BIT(2)
 | |
| 	#define	LED0_LINK_1000		BIT(3)
 | |
| 	#define	LED0_FD			BIT(4)
 | |
| 	#define	LED0_USB3_MASK		0x001f
 | |
| 	#define	LED1_ACTIVE		BIT(5)
 | |
| 	#define	LED1_LINK_10		BIT(6)
 | |
| 	#define	LED1_LINK_100		BIT(7)
 | |
| 	#define	LED1_LINK_1000		BIT(8)
 | |
| 	#define	LED1_FD			BIT(9)
 | |
| 	#define	LED1_USB3_MASK		0x03e0
 | |
| 	#define	LED2_ACTIVE		BIT(10)
 | |
| 	#define	LED2_LINK_1000		BIT(13)
 | |
| 	#define	LED2_LINK_100		BIT(12)
 | |
| 	#define	LED2_LINK_10		BIT(11)
 | |
| 	#define	LED2_FD			BIT(14)
 | |
| 	#define	LED_VALID		BIT(15)
 | |
| 	#define	LED2_USB3_MASK		0x7c00
 | |
| 
 | |
| #define GMII_PHYPAGE				0x1e
 | |
| #define GMII_PHY_PAGE_SELECT			0x1f
 | |
| 	#define GMII_PHY_PGSEL_EXT	0x0007
 | |
| 	#define GMII_PHY_PGSEL_PAGE0	0x0000
 | |
| 	#define GMII_PHY_PGSEL_PAGE3	0x0003
 | |
| 	#define GMII_PHY_PGSEL_PAGE5	0x0005
 | |
| 
 | |
| static int ax88179_reset(struct usbnet *dev);
 | |
| 
 | |
| struct ax88179_data {
 | |
| 	u8  eee_enabled;
 | |
| 	u8  eee_active;
 | |
| 	u16 rxctl;
 | |
| 	u8 in_pm;
 | |
| 	u32 wol_supported;
 | |
| 	u32 wolopts;
 | |
| 	u8 disconnecting;
 | |
| };
 | |
| 
 | |
| struct ax88179_int_data {
 | |
| 	__le32 intdata1;
 | |
| 	__le32 intdata2;
 | |
| };
 | |
| 
 | |
| static const struct {
 | |
| 	unsigned char ctrl, timer_l, timer_h, size, ifg;
 | |
| } AX88179_BULKIN_SIZE[] =	{
 | |
| 	{7, 0x4f, 0,	0x12, 0xff},
 | |
| 	{7, 0x20, 3,	0x16, 0xff},
 | |
| 	{7, 0xae, 7,	0x18, 0xff},
 | |
| 	{7, 0xcc, 0x4c, 0x18, 8},
 | |
| };
 | |
| 
 | |
| static void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode)
 | |
| {
 | |
| 	struct ax88179_data *ax179_data = dev->driver_priv;
 | |
| 
 | |
| 	ax179_data->in_pm = pm_mode;
 | |
| }
 | |
| 
 | |
| static int ax88179_in_pm(struct usbnet *dev)
 | |
| {
 | |
| 	struct ax88179_data *ax179_data = dev->driver_priv;
 | |
| 
 | |
| 	return ax179_data->in_pm;
 | |
| }
 | |
| 
 | |
| static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
 | |
| 			      u16 size, void *data)
 | |
| {
 | |
| 	int ret;
 | |
| 	int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
 | |
| 	struct ax88179_data *ax179_data = dev->driver_priv;
 | |
| 
 | |
| 	BUG_ON(!dev);
 | |
| 
 | |
| 	if (!ax88179_in_pm(dev))
 | |
| 		fn = usbnet_read_cmd;
 | |
| 	else
 | |
| 		fn = usbnet_read_cmd_nopm;
 | |
| 
 | |
| 	ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 | |
| 		 value, index, data, size);
 | |
| 
 | |
| 	if (unlikely((ret < 0) && !(ret == -ENODEV && ax179_data->disconnecting)))
 | |
| 		netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n",
 | |
| 			    index, ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
 | |
| 			       u16 size, const void *data)
 | |
| {
 | |
| 	int ret;
 | |
| 	int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
 | |
| 	struct ax88179_data *ax179_data = dev->driver_priv;
 | |
| 
 | |
| 	BUG_ON(!dev);
 | |
| 
 | |
| 	if (!ax88179_in_pm(dev))
 | |
| 		fn = usbnet_write_cmd;
 | |
| 	else
 | |
| 		fn = usbnet_write_cmd_nopm;
 | |
| 
 | |
| 	ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 | |
| 		 value, index, data, size);
 | |
| 
 | |
| 	if (unlikely((ret < 0) && !(ret == -ENODEV && ax179_data->disconnecting)))
 | |
| 		netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n",
 | |
| 			    index, ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
 | |
| 				    u16 index, u16 size, void *data)
 | |
| {
 | |
| 	u16 buf;
 | |
| 
 | |
| 	if (2 == size) {
 | |
| 		buf = *((u16 *)data);
 | |
| 		cpu_to_le16s(&buf);
 | |
| 		usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
 | |
| 				       USB_RECIP_DEVICE, value, index, &buf,
 | |
| 				       size);
 | |
| 	} else {
 | |
| 		usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
 | |
| 				       USB_RECIP_DEVICE, value, index, data,
 | |
| 				       size);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
 | |
| 			    u16 size, void *data)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	if (2 == size) {
 | |
| 		u16 buf = 0;
 | |
| 		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
 | |
| 		le16_to_cpus(&buf);
 | |
| 		*((u16 *)data) = buf;
 | |
| 	} else if (4 == size) {
 | |
| 		u32 buf = 0;
 | |
| 		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
 | |
| 		le32_to_cpus(&buf);
 | |
| 		*((u32 *)data) = buf;
 | |
| 	} else {
 | |
| 		ret = __ax88179_read_cmd(dev, cmd, value, index, size, data);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
 | |
| 			     u16 size, const void *data)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	if (2 == size) {
 | |
| 		u16 buf;
 | |
| 		buf = *((u16 *)data);
 | |
| 		cpu_to_le16s(&buf);
 | |
| 		ret = __ax88179_write_cmd(dev, cmd, value, index,
 | |
| 					  size, &buf);
 | |
| 	} else {
 | |
| 		ret = __ax88179_write_cmd(dev, cmd, value, index,
 | |
| 					  size, data);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void ax88179_status(struct usbnet *dev, struct urb *urb)
 | |
| {
 | |
| 	struct ax88179_int_data *event;
 | |
| 	u32 link;
 | |
| 
 | |
| 	if (urb->actual_length < 8)
 | |
| 		return;
 | |
| 
 | |
| 	event = urb->transfer_buffer;
 | |
| 	le32_to_cpus((void *)&event->intdata1);
 | |
| 
 | |
| 	link = (((__force u32)event->intdata1) & AX_INT_PPLS_LINK) >> 16;
 | |
| 
 | |
| 	if (netif_carrier_ok(dev->net) != link) {
 | |
| 		usbnet_link_change(dev, link, 1);
 | |
| 		if (!link)
 | |
| 			netdev_info(dev->net, "ax88179 - Link status is: 0\n");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(netdev);
 | |
| 	u16 res;
 | |
| 
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc,
 | |
| 			       int val)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(netdev);
 | |
| 	u16 res = (u16) val;
 | |
| 
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
 | |
| }
 | |
| 
 | |
| static inline int ax88179_phy_mmd_indirect(struct usbnet *dev, u16 prtad,
 | |
| 					   u16 devad)
 | |
| {
 | |
| 	u16 tmp16;
 | |
| 	int ret;
 | |
| 
 | |
| 	tmp16 = devad;
 | |
| 	ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 				MII_MMD_CTRL, 2, &tmp16);
 | |
| 
 | |
| 	tmp16 = prtad;
 | |
| 	ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 				MII_MMD_DATA, 2, &tmp16);
 | |
| 
 | |
| 	tmp16 = devad | MII_MMD_CTRL_NOINCR;
 | |
| 	ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 				MII_MMD_CTRL, 2, &tmp16);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| ax88179_phy_read_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad)
 | |
| {
 | |
| 	int ret;
 | |
| 	u16 tmp16;
 | |
| 
 | |
| 	ax88179_phy_mmd_indirect(dev, prtad, devad);
 | |
| 
 | |
| 	ret = ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			       MII_MMD_DATA, 2, &tmp16);
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	return tmp16;
 | |
| }
 | |
| 
 | |
| static int
 | |
| ax88179_phy_write_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad,
 | |
| 			       u16 data)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ax88179_phy_mmd_indirect(dev, prtad, devad);
 | |
| 
 | |
| 	ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 				MII_MMD_DATA, 2, &data);
 | |
| 
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
 | |
| {
 | |
| 	struct usbnet *dev = usb_get_intfdata(intf);
 | |
| 	struct ax88179_data *priv = dev->driver_priv;
 | |
| 	u16 tmp16;
 | |
| 	u8 tmp8;
 | |
| 
 | |
| 	ax88179_set_pm_mode(dev, true);
 | |
| 
 | |
| 	usbnet_suspend(intf, message);
 | |
| 
 | |
| 	/* Enable WoL */
 | |
| 	if (priv->wolopts) {
 | |
| 		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
 | |
| 				 1, 1, &tmp8);
 | |
| 		if (priv->wolopts & WAKE_PHY)
 | |
| 			tmp8 |= AX_MONITOR_MODE_RWLC;
 | |
| 		if (priv->wolopts & WAKE_MAGIC)
 | |
| 			tmp8 |= AX_MONITOR_MODE_RWMP;
 | |
| 
 | |
| 		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
 | |
| 				  1, 1, &tmp8);
 | |
| 	}
 | |
| 
 | |
| 	/* Disable RX path */
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 			 2, 2, &tmp16);
 | |
| 	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 			  2, 2, &tmp16);
 | |
| 
 | |
| 	/* Force bulk-in zero length */
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
 | |
| 			 2, 2, &tmp16);
 | |
| 
 | |
| 	tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
 | |
| 			  2, 2, &tmp16);
 | |
| 
 | |
| 	/* change clock */
 | |
| 	tmp8 = 0;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
 | |
| 
 | |
| 	/* Configure RX control register => stop operation */
 | |
| 	tmp16 = AX_RX_CTL_STOP;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
 | |
| 
 | |
| 	ax88179_set_pm_mode(dev, false);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* This function is used to enable the autodetach function. */
 | |
| /* This function is determined by offset 0x43 of EEPROM */
 | |
| static int ax88179_auto_detach(struct usbnet *dev)
 | |
| {
 | |
| 	u16 tmp16;
 | |
| 	u8 tmp8;
 | |
| 
 | |
| 	if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16) < 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	if ((tmp16 == 0xFFFF) || (!(tmp16 & 0x0100)))
 | |
| 		return 0;
 | |
| 
 | |
| 	/* Enable Auto Detach bit */
 | |
| 	tmp8 = 0;
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
 | |
| 	tmp8 |= AX_CLK_SELECT_ULR;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
 | |
| 
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
 | |
| 	tmp16 |= AX_PHYPWR_RSTCTL_AT;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_resume(struct usb_interface *intf)
 | |
| {
 | |
| 	struct usbnet *dev = usb_get_intfdata(intf);
 | |
| 
 | |
| 	ax88179_set_pm_mode(dev, true);
 | |
| 
 | |
| 	usbnet_link_change(dev, 0, 0);
 | |
| 
 | |
| 	ax88179_reset(dev);
 | |
| 
 | |
| 	ax88179_set_pm_mode(dev, false);
 | |
| 
 | |
| 	return usbnet_resume(intf);
 | |
| }
 | |
| 
 | |
| static void ax88179_disconnect(struct usb_interface *intf)
 | |
| {
 | |
| 	struct usbnet *dev = usb_get_intfdata(intf);
 | |
| 	struct ax88179_data *ax179_data;
 | |
| 
 | |
| 	if (!dev)
 | |
| 		return;
 | |
| 
 | |
| 	ax179_data = dev->driver_priv;
 | |
| 	ax179_data->disconnecting = 1;
 | |
| 
 | |
| 	usbnet_disconnect(intf);
 | |
| }
 | |
| 
 | |
| static void
 | |
| ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	struct ax88179_data *priv = dev->driver_priv;
 | |
| 
 | |
| 	wolinfo->supported = priv->wol_supported;
 | |
| 	wolinfo->wolopts = priv->wolopts;
 | |
| }
 | |
| 
 | |
| static int
 | |
| ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	struct ax88179_data *priv = dev->driver_priv;
 | |
| 
 | |
| 	if (wolinfo->wolopts & ~(priv->wol_supported))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	priv->wolopts = wolinfo->wolopts;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_get_eeprom_len(struct net_device *net)
 | |
| {
 | |
| 	return AX_EEPROM_LEN;
 | |
| }
 | |
| 
 | |
| static int
 | |
| ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
 | |
| 		   u8 *data)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	u16 *eeprom_buff;
 | |
| 	int first_word, last_word;
 | |
| 	int i, ret;
 | |
| 
 | |
| 	if (eeprom->len == 0)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	eeprom->magic = AX88179_EEPROM_MAGIC;
 | |
| 
 | |
| 	first_word = eeprom->offset >> 1;
 | |
| 	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
 | |
| 	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
 | |
| 				    GFP_KERNEL);
 | |
| 	if (!eeprom_buff)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	/* ax88179/178A returns 2 bytes from eeprom on read */
 | |
| 	for (i = first_word; i <= last_word; i++) {
 | |
| 		ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
 | |
| 					 &eeprom_buff[i - first_word]);
 | |
| 		if (ret < 0) {
 | |
| 			kfree(eeprom_buff);
 | |
| 			return -EIO;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
 | |
| 	kfree(eeprom_buff);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
 | |
| 		   u8 *data)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	u16 *eeprom_buff;
 | |
| 	int first_word;
 | |
| 	int last_word;
 | |
| 	int ret;
 | |
| 	int i;
 | |
| 
 | |
| 	netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n",
 | |
| 		   eeprom->len, eeprom->offset, eeprom->magic);
 | |
| 
 | |
| 	if (eeprom->len == 0)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (eeprom->magic != AX88179_EEPROM_MAGIC)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	first_word = eeprom->offset >> 1;
 | |
| 	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
 | |
| 
 | |
| 	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
 | |
| 				    GFP_KERNEL);
 | |
| 	if (!eeprom_buff)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	/* align data to 16 bit boundaries, read the missing data from
 | |
| 	   the EEPROM */
 | |
| 	if (eeprom->offset & 1) {
 | |
| 		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, first_word, 1, 2,
 | |
| 				       &eeprom_buff[0]);
 | |
| 		if (ret < 0) {
 | |
| 			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
 | |
| 			goto free;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if ((eeprom->offset + eeprom->len) & 1) {
 | |
| 		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, last_word, 1, 2,
 | |
| 				       &eeprom_buff[last_word - first_word]);
 | |
| 		if (ret < 0) {
 | |
| 			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
 | |
| 			goto free;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
 | |
| 
 | |
| 	for (i = first_word; i <= last_word; i++) {
 | |
| 		netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
 | |
| 			   i, eeprom_buff[i - first_word]);
 | |
| 		ret = ax88179_write_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
 | |
| 					&eeprom_buff[i - first_word]);
 | |
| 		if (ret < 0) {
 | |
| 			netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", i);
 | |
| 			goto free;
 | |
| 		}
 | |
| 		msleep(20);
 | |
| 	}
 | |
| 
 | |
| 	/* reload EEPROM data */
 | |
| 	ret = ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0x0000, 0, 0, NULL);
 | |
| 	if (ret < 0) {
 | |
| 		netdev_err(net, "Failed to reload EEPROM data\n");
 | |
| 		goto free;
 | |
| 	}
 | |
| 
 | |
| 	ret = 0;
 | |
| free:
 | |
| 	kfree(eeprom_buff);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int ax88179_get_link_ksettings(struct net_device *net,
 | |
| 				      struct ethtool_link_ksettings *cmd)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 
 | |
| 	mii_ethtool_get_link_ksettings(&dev->mii, cmd);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_set_link_ksettings(struct net_device *net,
 | |
| 				      const struct ethtool_link_ksettings *cmd)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	return mii_ethtool_set_link_ksettings(&dev->mii, cmd);
 | |
| }
 | |
| 
 | |
| static int
 | |
| ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_keee *data)
 | |
| {
 | |
| 	int val;
 | |
| 
 | |
| 	/* Get Supported EEE */
 | |
| 	val = ax88179_phy_read_mmd_indirect(dev, MDIO_PCS_EEE_ABLE,
 | |
| 					    MDIO_MMD_PCS);
 | |
| 	if (val < 0)
 | |
| 		return val;
 | |
| 	mii_eee_cap1_mod_linkmode_t(data->supported, val);
 | |
| 
 | |
| 	/* Get advertisement EEE */
 | |
| 	val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_ADV,
 | |
| 					    MDIO_MMD_AN);
 | |
| 	if (val < 0)
 | |
| 		return val;
 | |
| 	mii_eee_cap1_mod_linkmode_t(data->advertised, val);
 | |
| 
 | |
| 	/* Get LP advertisement EEE */
 | |
| 	val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_LPABLE,
 | |
| 					    MDIO_MMD_AN);
 | |
| 	if (val < 0)
 | |
| 		return val;
 | |
| 	mii_eee_cap1_mod_linkmode_t(data->lp_advertised, val);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_keee *data)
 | |
| {
 | |
| 	u16 tmp16 = linkmode_to_mii_eee_cap1_t(data->advertised);
 | |
| 
 | |
| 	return ax88179_phy_write_mmd_indirect(dev, MDIO_AN_EEE_ADV,
 | |
| 					      MDIO_MMD_AN, tmp16);
 | |
| }
 | |
| 
 | |
| static int ax88179_chk_eee(struct usbnet *dev)
 | |
| {
 | |
| 	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
 | |
| 	struct ax88179_data *priv = dev->driver_priv;
 | |
| 
 | |
| 	mii_ethtool_gset(&dev->mii, &ecmd);
 | |
| 
 | |
| 	if (ecmd.duplex & DUPLEX_FULL) {
 | |
| 		int eee_lp, eee_cap, eee_adv;
 | |
| 		u32 lp, cap, adv, supported = 0;
 | |
| 
 | |
| 		eee_cap = ax88179_phy_read_mmd_indirect(dev,
 | |
| 							MDIO_PCS_EEE_ABLE,
 | |
| 							MDIO_MMD_PCS);
 | |
| 		if (eee_cap < 0) {
 | |
| 			priv->eee_active = 0;
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap);
 | |
| 		if (!cap) {
 | |
| 			priv->eee_active = 0;
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		eee_lp = ax88179_phy_read_mmd_indirect(dev,
 | |
| 						       MDIO_AN_EEE_LPABLE,
 | |
| 						       MDIO_MMD_AN);
 | |
| 		if (eee_lp < 0) {
 | |
| 			priv->eee_active = 0;
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		eee_adv = ax88179_phy_read_mmd_indirect(dev,
 | |
| 							MDIO_AN_EEE_ADV,
 | |
| 							MDIO_MMD_AN);
 | |
| 
 | |
| 		if (eee_adv < 0) {
 | |
| 			priv->eee_active = 0;
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv);
 | |
| 		lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp);
 | |
| 		supported = (ecmd.speed == SPEED_1000) ?
 | |
| 			     SUPPORTED_1000baseT_Full :
 | |
| 			     SUPPORTED_100baseT_Full;
 | |
| 
 | |
| 		if (!(lp & adv & supported)) {
 | |
| 			priv->eee_active = 0;
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		priv->eee_active = 1;
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	priv->eee_active = 0;
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| static void ax88179_disable_eee(struct usbnet *dev)
 | |
| {
 | |
| 	u16 tmp16;
 | |
| 
 | |
| 	tmp16 = GMII_PHY_PGSEL_PAGE3;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
 | |
| 
 | |
| 	tmp16 = 0x3246;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  MII_PHYADDR, 2, &tmp16);
 | |
| 
 | |
| 	tmp16 = GMII_PHY_PGSEL_PAGE0;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
 | |
| }
 | |
| 
 | |
| static void ax88179_enable_eee(struct usbnet *dev)
 | |
| {
 | |
| 	u16 tmp16;
 | |
| 
 | |
| 	tmp16 = GMII_PHY_PGSEL_PAGE3;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
 | |
| 
 | |
| 	tmp16 = 0x3247;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  MII_PHYADDR, 2, &tmp16);
 | |
| 
 | |
| 	tmp16 = GMII_PHY_PGSEL_PAGE5;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
 | |
| 
 | |
| 	tmp16 = 0x0680;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  MII_BMSR, 2, &tmp16);
 | |
| 
 | |
| 	tmp16 = GMII_PHY_PGSEL_PAGE0;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
 | |
| }
 | |
| 
 | |
| static int ax88179_get_eee(struct net_device *net, struct ethtool_keee *edata)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	struct ax88179_data *priv = dev->driver_priv;
 | |
| 
 | |
| 	edata->eee_enabled = priv->eee_enabled;
 | |
| 	edata->eee_active = priv->eee_active;
 | |
| 
 | |
| 	return ax88179_ethtool_get_eee(dev, edata);
 | |
| }
 | |
| 
 | |
| static int ax88179_set_eee(struct net_device *net, struct ethtool_keee *edata)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	struct ax88179_data *priv = dev->driver_priv;
 | |
| 	int ret;
 | |
| 
 | |
| 	priv->eee_enabled = edata->eee_enabled;
 | |
| 	if (!priv->eee_enabled) {
 | |
| 		ax88179_disable_eee(dev);
 | |
| 	} else {
 | |
| 		priv->eee_enabled = ax88179_chk_eee(dev);
 | |
| 		if (!priv->eee_enabled)
 | |
| 			return -EOPNOTSUPP;
 | |
| 
 | |
| 		ax88179_enable_eee(dev);
 | |
| 	}
 | |
| 
 | |
| 	ret = ax88179_ethtool_set_eee(dev, edata);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	mii_nway_restart(&dev->mii);
 | |
| 
 | |
| 	usbnet_link_change(dev, 0, 0);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
 | |
| }
 | |
| 
 | |
| static const struct ethtool_ops ax88179_ethtool_ops = {
 | |
| 	.get_link		= ethtool_op_get_link,
 | |
| 	.get_msglevel		= usbnet_get_msglevel,
 | |
| 	.set_msglevel		= usbnet_set_msglevel,
 | |
| 	.get_wol		= ax88179_get_wol,
 | |
| 	.set_wol		= ax88179_set_wol,
 | |
| 	.get_eeprom_len		= ax88179_get_eeprom_len,
 | |
| 	.get_eeprom		= ax88179_get_eeprom,
 | |
| 	.set_eeprom		= ax88179_set_eeprom,
 | |
| 	.get_eee		= ax88179_get_eee,
 | |
| 	.set_eee		= ax88179_set_eee,
 | |
| 	.nway_reset		= usbnet_nway_reset,
 | |
| 	.get_link_ksettings	= ax88179_get_link_ksettings,
 | |
| 	.set_link_ksettings	= ax88179_set_link_ksettings,
 | |
| 	.get_ts_info		= ethtool_op_get_ts_info,
 | |
| };
 | |
| 
 | |
| static void ax88179_set_multicast(struct net_device *net)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	struct ax88179_data *data = dev->driver_priv;
 | |
| 	u8 *m_filter = ((u8 *)dev->data);
 | |
| 
 | |
| 	data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);
 | |
| 
 | |
| 	if (net->flags & IFF_PROMISC) {
 | |
| 		data->rxctl |= AX_RX_CTL_PRO;
 | |
| 	} else if (net->flags & IFF_ALLMULTI ||
 | |
| 		   netdev_mc_count(net) > AX_MAX_MCAST) {
 | |
| 		data->rxctl |= AX_RX_CTL_AMALL;
 | |
| 	} else if (netdev_mc_empty(net)) {
 | |
| 		/* just broadcast and directed */
 | |
| 	} else {
 | |
| 		/* We use dev->data for our 8 byte filter buffer
 | |
| 		 * to avoid allocating memory that is tricky to free later
 | |
| 		 */
 | |
| 		u32 crc_bits;
 | |
| 		struct netdev_hw_addr *ha;
 | |
| 
 | |
| 		memset(m_filter, 0, AX_MCAST_FLTSIZE);
 | |
| 
 | |
| 		netdev_for_each_mc_addr(ha, net) {
 | |
| 			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
 | |
| 			*(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7));
 | |
| 		}
 | |
| 
 | |
| 		ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY,
 | |
| 					AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE,
 | |
| 					m_filter);
 | |
| 
 | |
| 		data->rxctl |= AX_RX_CTL_AM;
 | |
| 	}
 | |
| 
 | |
| 	ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL,
 | |
| 				2, 2, &data->rxctl);
 | |
| }
 | |
| 
 | |
| static int
 | |
| ax88179_set_features(struct net_device *net, netdev_features_t features)
 | |
| {
 | |
| 	u8 tmp;
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	netdev_features_t changed = net->features ^ features;
 | |
| 
 | |
| 	if (changed & NETIF_F_IP_CSUM) {
 | |
| 		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
 | |
| 		tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;
 | |
| 		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
 | |
| 	}
 | |
| 
 | |
| 	if (changed & NETIF_F_IPV6_CSUM) {
 | |
| 		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
 | |
| 		tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
 | |
| 		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
 | |
| 	}
 | |
| 
 | |
| 	if (changed & NETIF_F_RXCSUM) {
 | |
| 		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
 | |
| 		tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
 | |
| 		       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
 | |
| 		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_change_mtu(struct net_device *net, int new_mtu)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	u16 tmp16;
 | |
| 
 | |
| 	WRITE_ONCE(net->mtu, new_mtu);
 | |
| 	dev->hard_mtu = net->mtu + net->hard_header_len;
 | |
| 
 | |
| 	if (net->mtu > 1500) {
 | |
| 		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 				 2, 2, &tmp16);
 | |
| 		tmp16 |= AX_MEDIUM_JUMBO_EN;
 | |
| 		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 				  2, 2, &tmp16);
 | |
| 	} else {
 | |
| 		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 				 2, 2, &tmp16);
 | |
| 		tmp16 &= ~AX_MEDIUM_JUMBO_EN;
 | |
| 		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 				  2, 2, &tmp16);
 | |
| 	}
 | |
| 
 | |
| 	/* max qlen depend on hard_mtu and rx_urb_size */
 | |
| 	usbnet_update_max_qlen(dev);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_set_mac_addr(struct net_device *net, void *p)
 | |
| {
 | |
| 	struct usbnet *dev = netdev_priv(net);
 | |
| 	struct sockaddr *addr = p;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (netif_running(net))
 | |
| 		return -EBUSY;
 | |
| 	if (!is_valid_ether_addr(addr->sa_data))
 | |
| 		return -EADDRNOTAVAIL;
 | |
| 
 | |
| 	eth_hw_addr_set(net, addr->sa_data);
 | |
| 
 | |
| 	/* Set the MAC address */
 | |
| 	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
 | |
| 				 ETH_ALEN, net->dev_addr);
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct net_device_ops ax88179_netdev_ops = {
 | |
| 	.ndo_open		= usbnet_open,
 | |
| 	.ndo_stop		= usbnet_stop,
 | |
| 	.ndo_start_xmit		= usbnet_start_xmit,
 | |
| 	.ndo_tx_timeout		= usbnet_tx_timeout,
 | |
| 	.ndo_get_stats64	= dev_get_tstats64,
 | |
| 	.ndo_change_mtu		= ax88179_change_mtu,
 | |
| 	.ndo_set_mac_address	= ax88179_set_mac_addr,
 | |
| 	.ndo_validate_addr	= eth_validate_addr,
 | |
| 	.ndo_eth_ioctl		= ax88179_ioctl,
 | |
| 	.ndo_set_rx_mode	= ax88179_set_multicast,
 | |
| 	.ndo_set_features	= ax88179_set_features,
 | |
| };
 | |
| 
 | |
| static int ax88179_check_eeprom(struct usbnet *dev)
 | |
| {
 | |
| 	u8 i, buf, eeprom[20];
 | |
| 	u16 csum, delay = HZ / 10;
 | |
| 	unsigned long jtimeout;
 | |
| 
 | |
| 	/* Read EEPROM content */
 | |
| 	for (i = 0; i < 6; i++) {
 | |
| 		buf = i;
 | |
| 		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
 | |
| 				      1, 1, &buf) < 0)
 | |
| 			return -EINVAL;
 | |
| 
 | |
| 		buf = EEP_RD;
 | |
| 		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
 | |
| 				      1, 1, &buf) < 0)
 | |
| 			return -EINVAL;
 | |
| 
 | |
| 		jtimeout = jiffies + delay;
 | |
| 		do {
 | |
| 			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
 | |
| 					 1, 1, &buf);
 | |
| 
 | |
| 			if (time_after(jiffies, jtimeout))
 | |
| 				return -EINVAL;
 | |
| 
 | |
| 		} while (buf & EEP_BUSY);
 | |
| 
 | |
| 		__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
 | |
| 				   2, 2, &eeprom[i * 2]);
 | |
| 
 | |
| 		if ((i == 0) && (eeprom[0] == 0xFF))
 | |
| 			return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9];
 | |
| 	csum = (csum >> 8) + (csum & 0xff);
 | |
| 	if ((csum + eeprom[10]) != 0xff)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode)
 | |
| {
 | |
| 	u8	i;
 | |
| 	u8	efuse[64];
 | |
| 	u16	csum = 0;
 | |
| 
 | |
| 	if (ax88179_read_cmd(dev, AX_ACCESS_EFUS, 0, 64, 64, efuse) < 0)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (*efuse == 0xFF)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	for (i = 0; i < 64; i++)
 | |
| 		csum = csum + efuse[i];
 | |
| 
 | |
| 	while (csum > 255)
 | |
| 		csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF);
 | |
| 
 | |
| 	if (csum != 0xFF)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	*ledmode = (efuse[51] << 8) | efuse[52];
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue)
 | |
| {
 | |
| 	u16 led;
 | |
| 
 | |
| 	/* Loaded the old eFuse LED Mode */
 | |
| 	if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	led >>= 8;
 | |
| 	switch (led) {
 | |
| 	case 0xFF:
 | |
| 		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
 | |
| 		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
 | |
| 		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
 | |
| 		break;
 | |
| 	case 0xFE:
 | |
| 		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | LED_VALID;
 | |
| 		break;
 | |
| 	case 0xFD:
 | |
| 		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 |
 | |
| 		      LED2_LINK_10 | LED_VALID;
 | |
| 		break;
 | |
| 	case 0xFC:
 | |
| 		led = LED0_ACTIVE | LED1_ACTIVE | LED1_LINK_1000 | LED2_ACTIVE |
 | |
| 		      LED2_LINK_100 | LED2_LINK_10 | LED_VALID;
 | |
| 		break;
 | |
| 	default:
 | |
| 		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
 | |
| 		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
 | |
| 		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	*ledvalue = led;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_led_setting(struct usbnet *dev)
 | |
| {
 | |
| 	u8 ledfd, value = 0;
 | |
| 	u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10;
 | |
| 	unsigned long jtimeout;
 | |
| 
 | |
| 	/* Check AX88179 version. UA1 or UA2*/
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value);
 | |
| 
 | |
| 	if (!(value & AX_SECLD)) {	/* UA1 */
 | |
| 		value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN |
 | |
| 			AX_GPIO_CTRL_GPIO1EN;
 | |
| 		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_GPIO_CTRL,
 | |
| 				      1, 1, &value) < 0)
 | |
| 			return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	/* Check EEPROM */
 | |
| 	if (!ax88179_check_eeprom(dev)) {
 | |
| 		value = 0x42;
 | |
| 		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
 | |
| 				      1, 1, &value) < 0)
 | |
| 			return -EINVAL;
 | |
| 
 | |
| 		value = EEP_RD;
 | |
| 		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
 | |
| 				      1, 1, &value) < 0)
 | |
| 			return -EINVAL;
 | |
| 
 | |
| 		jtimeout = jiffies + delay;
 | |
| 		do {
 | |
| 			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
 | |
| 					 1, 1, &value);
 | |
| 
 | |
| 			if (time_after(jiffies, jtimeout))
 | |
| 				return -EINVAL;
 | |
| 
 | |
| 		} while (value & EEP_BUSY);
 | |
| 
 | |
| 		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_HIGH,
 | |
| 				 1, 1, &value);
 | |
| 		ledvalue = (value << 8);
 | |
| 
 | |
| 		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
 | |
| 				 1, 1, &value);
 | |
| 		ledvalue |= value;
 | |
| 
 | |
| 		/* load internal ROM for defaule setting */
 | |
| 		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
 | |
| 			ax88179_convert_old_led(dev, &ledvalue);
 | |
| 
 | |
| 	} else if (!ax88179_check_efuse(dev, &ledvalue)) {
 | |
| 		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
 | |
| 			ax88179_convert_old_led(dev, &ledvalue);
 | |
| 	} else {
 | |
| 		ax88179_convert_old_led(dev, &ledvalue);
 | |
| 	}
 | |
| 
 | |
| 	tmp = GMII_PHY_PGSEL_EXT;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  GMII_PHY_PAGE_SELECT, 2, &tmp);
 | |
| 
 | |
| 	tmp = 0x2c;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  GMII_PHYPAGE, 2, &tmp);
 | |
| 
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			 GMII_LED_ACT, 2, &ledact);
 | |
| 
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			 GMII_LED_LINK, 2, &ledlink);
 | |
| 
 | |
| 	ledact &= GMII_LED_ACTIVE_MASK;
 | |
| 	ledlink &= GMII_LED_LINK_MASK;
 | |
| 
 | |
| 	if (ledvalue & LED0_ACTIVE)
 | |
| 		ledact |= GMII_LED0_ACTIVE;
 | |
| 
 | |
| 	if (ledvalue & LED1_ACTIVE)
 | |
| 		ledact |= GMII_LED1_ACTIVE;
 | |
| 
 | |
| 	if (ledvalue & LED2_ACTIVE)
 | |
| 		ledact |= GMII_LED2_ACTIVE;
 | |
| 
 | |
| 	if (ledvalue & LED0_LINK_10)
 | |
| 		ledlink |= GMII_LED0_LINK_10;
 | |
| 
 | |
| 	if (ledvalue & LED1_LINK_10)
 | |
| 		ledlink |= GMII_LED1_LINK_10;
 | |
| 
 | |
| 	if (ledvalue & LED2_LINK_10)
 | |
| 		ledlink |= GMII_LED2_LINK_10;
 | |
| 
 | |
| 	if (ledvalue & LED0_LINK_100)
 | |
| 		ledlink |= GMII_LED0_LINK_100;
 | |
| 
 | |
| 	if (ledvalue & LED1_LINK_100)
 | |
| 		ledlink |= GMII_LED1_LINK_100;
 | |
| 
 | |
| 	if (ledvalue & LED2_LINK_100)
 | |
| 		ledlink |= GMII_LED2_LINK_100;
 | |
| 
 | |
| 	if (ledvalue & LED0_LINK_1000)
 | |
| 		ledlink |= GMII_LED0_LINK_1000;
 | |
| 
 | |
| 	if (ledvalue & LED1_LINK_1000)
 | |
| 		ledlink |= GMII_LED1_LINK_1000;
 | |
| 
 | |
| 	if (ledvalue & LED2_LINK_1000)
 | |
| 		ledlink |= GMII_LED2_LINK_1000;
 | |
| 
 | |
| 	tmp = ledact;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  GMII_LED_ACT, 2, &tmp);
 | |
| 
 | |
| 	tmp = ledlink;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  GMII_LED_LINK, 2, &tmp);
 | |
| 
 | |
| 	tmp = GMII_PHY_PGSEL_PAGE0;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			  GMII_PHY_PAGE_SELECT, 2, &tmp);
 | |
| 
 | |
| 	/* LED full duplex setting */
 | |
| 	ledfd = 0;
 | |
| 	if (ledvalue & LED0_FD)
 | |
| 		ledfd |= 0x01;
 | |
| 	else if ((ledvalue & LED0_USB3_MASK) == 0)
 | |
| 		ledfd |= 0x02;
 | |
| 
 | |
| 	if (ledvalue & LED1_FD)
 | |
| 		ledfd |= 0x04;
 | |
| 	else if ((ledvalue & LED1_USB3_MASK) == 0)
 | |
| 		ledfd |= 0x08;
 | |
| 
 | |
| 	if (ledvalue & LED2_FD)
 | |
| 		ledfd |= 0x10;
 | |
| 	else if ((ledvalue & LED2_USB3_MASK) == 0)
 | |
| 		ledfd |= 0x20;
 | |
| 
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void ax88179_get_mac_addr(struct usbnet *dev)
 | |
| {
 | |
| 	u8 mac[ETH_ALEN];
 | |
| 
 | |
| 	memset(mac, 0, sizeof(mac));
 | |
| 
 | |
| 	/* Maybe the boot loader passed the MAC address via device tree */
 | |
| 	if (!eth_platform_get_mac_address(&dev->udev->dev, mac)) {
 | |
| 		netif_dbg(dev, ifup, dev->net,
 | |
| 			  "MAC address read from device tree");
 | |
| 	} else {
 | |
| 		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
 | |
| 				 ETH_ALEN, mac);
 | |
| 		netif_dbg(dev, ifup, dev->net,
 | |
| 			  "MAC address read from ASIX chip");
 | |
| 	}
 | |
| 
 | |
| 	if (is_valid_ether_addr(mac)) {
 | |
| 		eth_hw_addr_set(dev->net, mac);
 | |
| 		if (!is_local_ether_addr(mac))
 | |
| 			dev->net->addr_assign_type = NET_ADDR_PERM;
 | |
| 	} else {
 | |
| 		netdev_info(dev->net, "invalid MAC address, using random\n");
 | |
| 	}
 | |
| 
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
 | |
| 			  dev->net->dev_addr);
 | |
| }
 | |
| 
 | |
| static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
 | |
| {
 | |
| 	struct ax88179_data *ax179_data;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = usbnet_get_endpoints(dev, intf);
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	ax179_data = kzalloc(sizeof(*ax179_data), GFP_KERNEL);
 | |
| 	if (!ax179_data)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	dev->driver_priv = ax179_data;
 | |
| 
 | |
| 	dev->net->netdev_ops = &ax88179_netdev_ops;
 | |
| 	dev->net->ethtool_ops = &ax88179_ethtool_ops;
 | |
| 	dev->net->needed_headroom = 8;
 | |
| 	dev->net->max_mtu = 4088;
 | |
| 
 | |
| 	/* Initialize MII structure */
 | |
| 	dev->mii.dev = dev->net;
 | |
| 	dev->mii.mdio_read = ax88179_mdio_read;
 | |
| 	dev->mii.mdio_write = ax88179_mdio_write;
 | |
| 	dev->mii.phy_id_mask = 0xff;
 | |
| 	dev->mii.reg_num_mask = 0xff;
 | |
| 	dev->mii.phy_id = 0x03;
 | |
| 	dev->mii.supports_gmii = 1;
 | |
| 
 | |
| 	dev->net->features |= NETIF_F_SG | NETIF_F_IP_CSUM |
 | |
| 			      NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO;
 | |
| 
 | |
| 	dev->net->hw_features |= dev->net->features;
 | |
| 
 | |
| 	netif_set_tso_max_size(dev->net, 16384);
 | |
| 
 | |
| 	ax88179_reset(dev);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
 | |
| {
 | |
| 	struct ax88179_data *ax179_data = dev->driver_priv;
 | |
| 	u16 tmp16;
 | |
| 
 | |
| 	/* Configure RX control register => stop operation */
 | |
| 	tmp16 = AX_RX_CTL_STOP;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
 | |
| 
 | |
| 	tmp16 = 0;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
 | |
| 
 | |
| 	/* Power down ethernet PHY */
 | |
| 	tmp16 = 0;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
 | |
| 
 | |
| 	kfree(ax179_data);
 | |
| }
 | |
| 
 | |
| static void
 | |
| ax88179_rx_checksum(struct sk_buff *skb, u32 *pkt_hdr)
 | |
| {
 | |
| 	skb->ip_summed = CHECKSUM_NONE;
 | |
| 
 | |
| 	/* checksum error bit is set */
 | |
| 	if ((*pkt_hdr & AX_RXHDR_L3CSUM_ERR) ||
 | |
| 	    (*pkt_hdr & AX_RXHDR_L4CSUM_ERR))
 | |
| 		return;
 | |
| 
 | |
| 	/* It must be a TCP or UDP packet with a valid checksum */
 | |
| 	if (((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_TCP) ||
 | |
| 	    ((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_UDP))
 | |
| 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 | |
| }
 | |
| 
 | |
| static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 | |
| {
 | |
| 	struct sk_buff *ax_skb;
 | |
| 	int pkt_cnt;
 | |
| 	u32 rx_hdr;
 | |
| 	u16 hdr_off;
 | |
| 	u32 *pkt_hdr;
 | |
| 
 | |
| 	/* At the end of the SKB, there's a header telling us how many packets
 | |
| 	 * are bundled into this buffer and where we can find an array of
 | |
| 	 * per-packet metadata (which contains elements encoded into u16).
 | |
| 	 */
 | |
| 
 | |
| 	/* SKB contents for current firmware:
 | |
| 	 *   <packet 1> <padding>
 | |
| 	 *   ...
 | |
| 	 *   <packet N> <padding>
 | |
| 	 *   <per-packet metadata entry 1> <dummy header>
 | |
| 	 *   ...
 | |
| 	 *   <per-packet metadata entry N> <dummy header>
 | |
| 	 *   <padding2> <rx_hdr>
 | |
| 	 *
 | |
| 	 * where:
 | |
| 	 *   <packet N> contains pkt_len bytes:
 | |
| 	 *		2 bytes of IP alignment pseudo header
 | |
| 	 *		packet received
 | |
| 	 *   <per-packet metadata entry N> contains 4 bytes:
 | |
| 	 *		pkt_len and fields AX_RXHDR_*
 | |
| 	 *   <padding>	0-7 bytes to terminate at
 | |
| 	 *		8 bytes boundary (64-bit).
 | |
| 	 *   <padding2> 4 bytes to make rx_hdr terminate at
 | |
| 	 *		8 bytes boundary (64-bit)
 | |
| 	 *   <dummy-header> contains 4 bytes:
 | |
| 	 *		pkt_len=0 and AX_RXHDR_DROP_ERR
 | |
| 	 *   <rx-hdr>	contains 4 bytes:
 | |
| 	 *		pkt_cnt and hdr_off (offset of
 | |
| 	 *		  <per-packet metadata entry 1>)
 | |
| 	 *
 | |
| 	 * pkt_cnt is number of entrys in the per-packet metadata.
 | |
| 	 * In current firmware there is 2 entrys per packet.
 | |
| 	 * The first points to the packet and the
 | |
| 	 *  second is a dummy header.
 | |
| 	 * This was done probably to align fields in 64-bit and
 | |
| 	 *  maintain compatibility with old firmware.
 | |
| 	 * This code assumes that <dummy header> and <padding2> are
 | |
| 	 *  optional.
 | |
| 	 */
 | |
| 
 | |
| 	if (skb->len < 4)
 | |
| 		return 0;
 | |
| 	skb_trim(skb, skb->len - 4);
 | |
| 	rx_hdr = get_unaligned_le32(skb_tail_pointer(skb));
 | |
| 	pkt_cnt = (u16)rx_hdr;
 | |
| 	hdr_off = (u16)(rx_hdr >> 16);
 | |
| 
 | |
| 	if (pkt_cnt == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	/* Make sure that the bounds of the metadata array are inside the SKB
 | |
| 	 * (and in front of the counter at the end).
 | |
| 	 */
 | |
| 	if (pkt_cnt * 4 + hdr_off > skb->len)
 | |
| 		return 0;
 | |
| 	pkt_hdr = (u32 *)(skb->data + hdr_off);
 | |
| 
 | |
| 	/* Packets must not overlap the metadata array */
 | |
| 	skb_trim(skb, hdr_off);
 | |
| 
 | |
| 	for (; pkt_cnt > 0; pkt_cnt--, pkt_hdr++) {
 | |
| 		u16 pkt_len_plus_padd;
 | |
| 		u16 pkt_len;
 | |
| 
 | |
| 		le32_to_cpus(pkt_hdr);
 | |
| 		pkt_len = (*pkt_hdr >> 16) & 0x1fff;
 | |
| 		pkt_len_plus_padd = (pkt_len + 7) & 0xfff8;
 | |
| 
 | |
| 		/* Skip dummy header used for alignment
 | |
| 		 */
 | |
| 		if (pkt_len == 0)
 | |
| 			continue;
 | |
| 
 | |
| 		if (pkt_len_plus_padd > skb->len)
 | |
| 			return 0;
 | |
| 
 | |
| 		/* Check CRC or runt packet */
 | |
| 		if ((*pkt_hdr & (AX_RXHDR_CRC_ERR | AX_RXHDR_DROP_ERR)) ||
 | |
| 		    pkt_len < 2 + ETH_HLEN) {
 | |
| 			dev->net->stats.rx_errors++;
 | |
| 			skb_pull(skb, pkt_len_plus_padd);
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* last packet */
 | |
| 		if (pkt_len_plus_padd == skb->len) {
 | |
| 			skb_trim(skb, pkt_len);
 | |
| 
 | |
| 			/* Skip IP alignment pseudo header */
 | |
| 			skb_pull(skb, 2);
 | |
| 
 | |
| 			ax88179_rx_checksum(skb, pkt_hdr);
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		ax_skb = netdev_alloc_skb_ip_align(dev->net, pkt_len);
 | |
| 		if (!ax_skb)
 | |
| 			return 0;
 | |
| 		skb_put(ax_skb, pkt_len);
 | |
| 		memcpy(ax_skb->data, skb->data + 2, pkt_len);
 | |
| 
 | |
| 		ax88179_rx_checksum(ax_skb, pkt_hdr);
 | |
| 		usbnet_skb_return(dev, ax_skb);
 | |
| 
 | |
| 		skb_pull(skb, pkt_len_plus_padd);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct sk_buff *
 | |
| ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
 | |
| {
 | |
| 	u32 tx_hdr1, tx_hdr2;
 | |
| 	int frame_size = dev->maxpacket;
 | |
| 	int headroom;
 | |
| 	void *ptr;
 | |
| 
 | |
| 	tx_hdr1 = skb->len;
 | |
| 	tx_hdr2 = skb_shinfo(skb)->gso_size; /* Set TSO mss */
 | |
| 	if (((skb->len + 8) % frame_size) == 0)
 | |
| 		tx_hdr2 |= 0x80008000;	/* Enable padding */
 | |
| 
 | |
| 	headroom = skb_headroom(skb) - 8;
 | |
| 
 | |
| 	if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb))
 | |
| 		return NULL;
 | |
| 
 | |
| 	if ((skb_header_cloned(skb) || headroom < 0) &&
 | |
| 	    pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) {
 | |
| 		dev_kfree_skb_any(skb);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	ptr = skb_push(skb, 8);
 | |
| 	put_unaligned_le32(tx_hdr1, ptr);
 | |
| 	put_unaligned_le32(tx_hdr2, ptr + 4);
 | |
| 
 | |
| 	usbnet_set_skb_tx_stats(skb, (skb_shinfo(skb)->gso_segs ?: 1), 0);
 | |
| 
 | |
| 	return skb;
 | |
| }
 | |
| 
 | |
| static int ax88179_link_reset(struct usbnet *dev)
 | |
| {
 | |
| 	struct ax88179_data *ax179_data = dev->driver_priv;
 | |
| 	u8 tmp[5], link_sts;
 | |
| 	u16 mode, tmp16, delay = HZ / 10;
 | |
| 	u32 tmp32 = 0x40000000;
 | |
| 	unsigned long jtimeout;
 | |
| 
 | |
| 	jtimeout = jiffies + delay;
 | |
| 	while (tmp32 & 0x40000000) {
 | |
| 		mode = 0;
 | |
| 		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &mode);
 | |
| 		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2,
 | |
| 				  &ax179_data->rxctl);
 | |
| 
 | |
| 		/*link up, check the usb device control TX FIFO full or empty*/
 | |
| 		ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32);
 | |
| 
 | |
| 		if (time_after(jiffies, jtimeout))
 | |
| 			return 0;
 | |
| 	}
 | |
| 
 | |
| 	mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
 | |
| 	       AX_MEDIUM_RXFLOW_CTRLEN;
 | |
| 
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS,
 | |
| 			 1, 1, &link_sts);
 | |
| 
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
 | |
| 			 GMII_PHY_PHYSR, 2, &tmp16);
 | |
| 
 | |
| 	if (!(tmp16 & GMII_PHY_PHYSR_LINK)) {
 | |
| 		netdev_info(dev->net, "ax88179 - Link status is: 0\n");
 | |
| 		return 0;
 | |
| 	} else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
 | |
| 		mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ;
 | |
| 		if (dev->net->mtu > 1500)
 | |
| 			mode |= AX_MEDIUM_JUMBO_EN;
 | |
| 
 | |
| 		if (link_sts & AX_USB_SS)
 | |
| 			memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
 | |
| 		else if (link_sts & AX_USB_HS)
 | |
| 			memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5);
 | |
| 		else
 | |
| 			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
 | |
| 	} else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
 | |
| 		mode |= AX_MEDIUM_PS;
 | |
| 
 | |
| 		if (link_sts & (AX_USB_SS | AX_USB_HS))
 | |
| 			memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5);
 | |
| 		else
 | |
| 			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
 | |
| 	} else {
 | |
| 		memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
 | |
| 	}
 | |
| 
 | |
| 	/* RX bulk configuration */
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
 | |
| 
 | |
| 	dev->rx_urb_size = (1024 * (tmp[3] + 2));
 | |
| 
 | |
| 	if (tmp16 & GMII_PHY_PHYSR_FULL)
 | |
| 		mode |= AX_MEDIUM_FULL_DUPLEX;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 			  2, 2, &mode);
 | |
| 
 | |
| 	ax179_data->eee_enabled = ax88179_chk_eee(dev);
 | |
| 
 | |
| 	netif_carrier_on(dev->net);
 | |
| 
 | |
| 	netdev_info(dev->net, "ax88179 - Link status is: 1\n");
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_reset(struct usbnet *dev)
 | |
| {
 | |
| 	u8 buf[5];
 | |
| 	u16 *tmp16;
 | |
| 	u8 *tmp;
 | |
| 	struct ax88179_data *ax179_data = dev->driver_priv;
 | |
| 	struct ethtool_keee eee_data;
 | |
| 
 | |
| 	tmp16 = (u16 *)buf;
 | |
| 	tmp = (u8 *)buf;
 | |
| 
 | |
| 	/* Power up ethernet PHY */
 | |
| 	*tmp16 = 0;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
 | |
| 
 | |
| 	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
 | |
| 	msleep(500);
 | |
| 
 | |
| 	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
 | |
| 	msleep(200);
 | |
| 
 | |
| 	/* Ethernet PHY Auto Detach*/
 | |
| 	ax88179_auto_detach(dev);
 | |
| 
 | |
| 	/* Read MAC address from DTB or asix chip */
 | |
| 	ax88179_get_mac_addr(dev);
 | |
| 	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
 | |
| 
 | |
| 	/* RX bulk configuration */
 | |
| 	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
 | |
| 
 | |
| 	dev->rx_urb_size = 1024 * 20;
 | |
| 
 | |
| 	*tmp = 0x34;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
 | |
| 
 | |
| 	*tmp = 0x52;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
 | |
| 			  1, 1, tmp);
 | |
| 
 | |
| 	/* Enable checksum offload */
 | |
| 	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
 | |
| 	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
 | |
| 
 | |
| 	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
 | |
| 	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
 | |
| 
 | |
| 	/* Configure RX control register => start operation */
 | |
| 	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
 | |
| 		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
 | |
| 
 | |
| 	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
 | |
| 	       AX_MONITOR_MODE_RWMP;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
 | |
| 
 | |
| 	/* Configure default medium type => giga */
 | |
| 	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
 | |
| 		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX |
 | |
| 		 AX_MEDIUM_GIGAMODE;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 			  2, 2, tmp16);
 | |
| 
 | |
| 	/* Check if WoL is supported */
 | |
| 	ax179_data->wol_supported = 0;
 | |
| 	if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
 | |
| 			     1, 1, &tmp) > 0)
 | |
| 		ax179_data->wol_supported = WAKE_MAGIC | WAKE_PHY;
 | |
| 
 | |
| 	ax88179_led_setting(dev);
 | |
| 
 | |
| 	ax179_data->eee_enabled = 0;
 | |
| 	ax179_data->eee_active = 0;
 | |
| 
 | |
| 	ax88179_disable_eee(dev);
 | |
| 
 | |
| 	ax88179_ethtool_get_eee(dev, &eee_data);
 | |
| 	linkmode_zero(eee_data.advertised);
 | |
| 	ax88179_ethtool_set_eee(dev, &eee_data);
 | |
| 
 | |
| 	/* Restart autoneg */
 | |
| 	mii_nway_restart(&dev->mii);
 | |
| 
 | |
| 	usbnet_link_change(dev, 0, 0);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_net_reset(struct usbnet *dev)
 | |
| {
 | |
| 	u16 tmp16;
 | |
| 
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_PHY_PHYSR,
 | |
| 			 2, &tmp16);
 | |
| 	if (tmp16) {
 | |
| 		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 				 2, 2, &tmp16);
 | |
| 		if (!(tmp16 & AX_MEDIUM_RECEIVE_EN)) {
 | |
| 			tmp16 |= AX_MEDIUM_RECEIVE_EN;
 | |
| 			ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 					  2, 2, &tmp16);
 | |
| 		}
 | |
| 	} else {
 | |
| 		ax88179_reset(dev);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ax88179_stop(struct usbnet *dev)
 | |
| {
 | |
| 	u16 tmp16;
 | |
| 
 | |
| 	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 			 2, 2, &tmp16);
 | |
| 	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
 | |
| 	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
 | |
| 			  2, 2, &tmp16);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct driver_info ax88179_info = {
 | |
| 	.description = "ASIX AX88179 USB 3.0 Gigabit Ethernet",
 | |
| 	.bind = ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset = ax88179_net_reset,
 | |
| 	.stop = ax88179_stop,
 | |
| 	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info ax88178a_info = {
 | |
| 	.description = "ASIX AX88178A USB 2.0 Gigabit Ethernet",
 | |
| 	.bind = ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset = ax88179_net_reset,
 | |
| 	.stop = ax88179_stop,
 | |
| 	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info cypress_GX3_info = {
 | |
| 	.description = "Cypress GX3 SuperSpeed to Gigabit Ethernet Controller",
 | |
| 	.bind = ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset = ax88179_net_reset,
 | |
| 	.stop = ax88179_stop,
 | |
| 	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info dlink_dub1312_info = {
 | |
| 	.description = "D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter",
 | |
| 	.bind = ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset = ax88179_net_reset,
 | |
| 	.stop = ax88179_stop,
 | |
| 	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info sitecom_info = {
 | |
| 	.description = "Sitecom USB 3.0 to Gigabit Adapter",
 | |
| 	.bind = ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset = ax88179_net_reset,
 | |
| 	.stop = ax88179_stop,
 | |
| 	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info samsung_info = {
 | |
| 	.description = "Samsung USB Ethernet Adapter",
 | |
| 	.bind = ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset = ax88179_net_reset,
 | |
| 	.stop = ax88179_stop,
 | |
| 	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info lenovo_info = {
 | |
| 	.description = "Lenovo OneLinkDock Gigabit LAN",
 | |
| 	.bind = ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset = ax88179_net_reset,
 | |
| 	.stop = ax88179_stop,
 | |
| 	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info belkin_info = {
 | |
| 	.description = "Belkin USB Ethernet Adapter",
 | |
| 	.bind	= ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset	= ax88179_net_reset,
 | |
| 	.stop	= ax88179_stop,
 | |
| 	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info toshiba_info = {
 | |
| 	.description = "Toshiba USB Ethernet Adapter",
 | |
| 	.bind	= ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset	= ax88179_net_reset,
 | |
| 	.stop = ax88179_stop,
 | |
| 	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info mct_info = {
 | |
| 	.description = "MCT USB 3.0 Gigabit Ethernet Adapter",
 | |
| 	.bind	= ax88179_bind,
 | |
| 	.unbind	= ax88179_unbind,
 | |
| 	.status	= ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset	= ax88179_net_reset,
 | |
| 	.stop	= ax88179_stop,
 | |
| 	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info at_umc2000_info = {
 | |
| 	.description = "AT-UMC2000 USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter",
 | |
| 	.bind   = ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset  = ax88179_net_reset,
 | |
| 	.stop   = ax88179_stop,
 | |
| 	.flags  = FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info at_umc200_info = {
 | |
| 	.description = "AT-UMC200 USB 3.0/USB 3.1 Gen 1 to Fast Ethernet Adapter",
 | |
| 	.bind   = ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset  = ax88179_net_reset,
 | |
| 	.stop   = ax88179_stop,
 | |
| 	.flags  = FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct driver_info at_umc2000sp_info = {
 | |
| 	.description = "AT-UMC2000/SP USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter",
 | |
| 	.bind   = ax88179_bind,
 | |
| 	.unbind = ax88179_unbind,
 | |
| 	.status = ax88179_status,
 | |
| 	.link_reset = ax88179_link_reset,
 | |
| 	.reset  = ax88179_net_reset,
 | |
| 	.stop   = ax88179_stop,
 | |
| 	.flags  = FLAG_ETHER | FLAG_FRAMING_AX,
 | |
| 	.rx_fixup = ax88179_rx_fixup,
 | |
| 	.tx_fixup = ax88179_tx_fixup,
 | |
| };
 | |
| 
 | |
| static const struct usb_device_id products[] = {
 | |
| {
 | |
| 	/* ASIX AX88179 10/100/1000 */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x1790, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&ax88179_info,
 | |
| }, {
 | |
| 	/* ASIX AX88178A 10/100/1000 */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x178a, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&ax88178a_info,
 | |
| }, {
 | |
| 	/* Cypress GX3 SuperSpeed to Gigabit Ethernet Bridge Controller */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x04b4, 0x3610, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&cypress_GX3_info,
 | |
| }, {
 | |
| 	/* D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x4a00, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&dlink_dub1312_info,
 | |
| }, {
 | |
| 	/* Sitecom USB 3.0 to Gigabit Adapter */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x0df6, 0x0072, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&sitecom_info,
 | |
| }, {
 | |
| 	/* Samsung USB Ethernet Adapter */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x04e8, 0xa100, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&samsung_info,
 | |
| }, {
 | |
| 	/* Lenovo OneLinkDock Gigabit LAN */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x17ef, 0x304b, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&lenovo_info,
 | |
| }, {
 | |
| 	/* Belkin B2B128 USB 3.0 Hub + Gigabit Ethernet Adapter */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x050d, 0x0128, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&belkin_info,
 | |
| }, {
 | |
| 	/* Toshiba USB 3.0 GBit Ethernet Adapter */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x0a13, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&toshiba_info,
 | |
| }, {
 | |
| 	/* Magic Control Technology U3-A9003 USB 3.0 Gigabit Ethernet Adapter */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x0711, 0x0179, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&mct_info,
 | |
| }, {
 | |
| 	/* Allied Telesis AT-UMC2000 USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x07c9, 0x000e, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&at_umc2000_info,
 | |
| }, {
 | |
| 	/* Allied Telesis AT-UMC200 USB 3.0/USB 3.1 Gen 1 to Fast Ethernet Adapter */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x07c9, 0x000f, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&at_umc200_info,
 | |
| }, {
 | |
| 	/* Allied Telesis AT-UMC2000/SP USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter */
 | |
| 	USB_DEVICE_AND_INTERFACE_INFO(0x07c9, 0x0010, 0xff, 0xff, 0),
 | |
| 	.driver_info = (unsigned long)&at_umc2000sp_info,
 | |
| },
 | |
| 	{ },
 | |
| };
 | |
| MODULE_DEVICE_TABLE(usb, products);
 | |
| 
 | |
| static struct usb_driver ax88179_178a_driver = {
 | |
| 	.name =		"ax88179_178a",
 | |
| 	.id_table =	products,
 | |
| 	.probe =	usbnet_probe,
 | |
| 	.suspend =	ax88179_suspend,
 | |
| 	.resume =	ax88179_resume,
 | |
| 	.reset_resume =	ax88179_resume,
 | |
| 	.disconnect =	ax88179_disconnect,
 | |
| 	.supports_autosuspend = 1,
 | |
| 	.disable_hub_initiated_lpm = 1,
 | |
| };
 | |
| 
 | |
| module_usb_driver(ax88179_178a_driver);
 | |
| 
 | |
| MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices");
 | |
| MODULE_LICENSE("GPL");
 |