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