diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c old mode 100644 new mode 100755 index 3b449c4ecf7238..e59d1751527bd0 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1,12 +1,13 @@ /* - * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices - * - * Copyright (C) 2011-2013 ASIX + * ASIX AX88179 based USB 3.0 Ethernet Devices + * Copyright (C) 2003-2005 David Hollis + * Copyright (C) 2005 Phil Chang + * Copyright (c) 2002-2003 TiVo Inc. * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,187 +16,74 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* debug messages, extra info */ +/* #define DEBUG */ + +#include +/*#include */ +#ifdef CONFIG_USB_DEBUG +#define DEBUG +#endif #include +#include +#include +#include +#include #include +#include +#include #include #include #include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) #include +#else +#include <../drivers/usb/net/usbnet.h> +#endif -#define AX88179_PHY_ID 0x03 -#define AX_EEPROM_LEN 0x100 -#define AX88179_EEPROM_MAGIC 0x17900b95 -#define AX_MCAST_FLTSIZE 8 -#define AX_MAX_MCAST 64 -#define AX_INT_PPLS_LINK ((u32)BIT(16)) -#define AX_RXHDR_L4_TYPE_MASK 0x1c -#define AX_RXHDR_L4_TYPE_UDP 4 -#define AX_RXHDR_L4_TYPE_TCP 16 -#define AX_RXHDR_L3CSUM_ERR 2 -#define AX_RXHDR_L4CSUM_ERR 1 -#define AX_RXHDR_CRC_ERR ((u32)BIT(31)) -#define AX_RXHDR_DROP_ERR ((u32)BIT(30)) -#define AX_ACCESS_MAC 0x01 -#define AX_ACCESS_PHY 0x02 -#define AX_ACCESS_EEPROM 0x04 -#define AX_ACCESS_EFUS 0x05 -#define AX_PAUSE_WATERLVL_HIGH 0x54 -#define AX_PAUSE_WATERLVL_LOW 0x55 - -#define PHYSICAL_LINK_STATUS 0x02 - #define AX_USB_SS 0x04 - #define AX_USB_HS 0x02 - -#define GENERAL_STATUS 0x03 -/* Check AX88179 version. UA1:Bit2 = 0, UA2:Bit2 = 1 */ - #define AX_SECLD 0x04 - -#define AX_SROM_ADDR 0x07 -#define AX_SROM_CMD 0x0a - #define EEP_RD 0x04 - #define EEP_BUSY 0x10 - -#define AX_SROM_DATA_LOW 0x08 -#define AX_SROM_DATA_HIGH 0x09 - -#define AX_RX_CTL 0x0b - #define AX_RX_CTL_DROPCRCERR 0x0100 - #define AX_RX_CTL_IPE 0x0200 - #define AX_RX_CTL_START 0x0080 - #define AX_RX_CTL_AP 0x0020 - #define AX_RX_CTL_AM 0x0010 - #define AX_RX_CTL_AB 0x0008 - #define AX_RX_CTL_AMALL 0x0002 - #define AX_RX_CTL_PRO 0x0001 - #define AX_RX_CTL_STOP 0x0000 - -#define AX_NODE_ID 0x10 -#define AX_MULFLTARY 0x16 - -#define AX_MEDIUM_STATUS_MODE 0x22 - #define AX_MEDIUM_GIGAMODE 0x01 - #define AX_MEDIUM_FULL_DUPLEX 0x02 - #define AX_MEDIUM_ALWAYS_ONE 0x04 - #define AX_MEDIUM_EN_125MHZ 0x08 - #define AX_MEDIUM_RXFLOW_CTRLEN 0x10 - #define AX_MEDIUM_TXFLOW_CTRLEN 0x20 - #define AX_MEDIUM_RECEIVE_EN 0x100 - #define AX_MEDIUM_PS 0x200 - #define AX_MEDIUM_JUMBO_EN 0x8040 - -#define AX_MONITOR_MOD 0x24 - #define AX_MONITOR_MODE_RWLC 0x02 - #define AX_MONITOR_MODE_RWMP 0x04 - #define AX_MONITOR_MODE_PMEPOL 0x20 - #define AX_MONITOR_MODE_PMETYPE 0x40 - -#define AX_GPIO_CTRL 0x25 - #define AX_GPIO_CTRL_GPIO3EN 0x80 - #define AX_GPIO_CTRL_GPIO2EN 0x40 - #define AX_GPIO_CTRL_GPIO1EN 0x20 - -#define AX_PHYPWR_RSTCTL 0x26 - #define AX_PHYPWR_RSTCTL_BZ 0x0010 - #define AX_PHYPWR_RSTCTL_IPRL 0x0020 - #define AX_PHYPWR_RSTCTL_AT 0x1000 - -#define AX_RX_BULKIN_QCTRL 0x2e -#define AX_CLK_SELECT 0x33 - #define AX_CLK_SELECT_BCS 0x01 - #define AX_CLK_SELECT_ACS 0x02 - #define AX_CLK_SELECT_ULR 0x08 - -#define AX_RXCOE_CTL 0x34 - #define AX_RXCOE_IP 0x01 - #define AX_RXCOE_TCP 0x02 - #define AX_RXCOE_UDP 0x04 - #define AX_RXCOE_TCPV6 0x20 - #define AX_RXCOE_UDPV6 0x40 - -#define AX_TXCOE_CTL 0x35 - #define AX_TXCOE_IP 0x01 - #define AX_TXCOE_TCP 0x02 - #define AX_TXCOE_UDP 0x04 - #define AX_TXCOE_TCPV6 0x20 - #define AX_TXCOE_UDPV6 0x40 - -#define AX_LEDCTRL 0x73 - -#define GMII_PHY_PHYSR 0x11 - #define GMII_PHY_PHYSR_SMASK 0xc000 - #define GMII_PHY_PHYSR_GIGA 0x8000 - #define GMII_PHY_PHYSR_100 0x4000 - #define GMII_PHY_PHYSR_FULL 0x2000 - #define GMII_PHY_PHYSR_LINK 0x400 - -#define GMII_LED_ACT 0x1a - #define GMII_LED_ACTIVE_MASK 0xff8f - #define GMII_LED0_ACTIVE BIT(4) - #define GMII_LED1_ACTIVE BIT(5) - #define GMII_LED2_ACTIVE BIT(6) - -#define GMII_LED_LINK 0x1c - #define GMII_LED_LINK_MASK 0xf888 - #define GMII_LED0_LINK_10 BIT(0) - #define GMII_LED0_LINK_100 BIT(1) - #define GMII_LED0_LINK_1000 BIT(2) - #define GMII_LED1_LINK_10 BIT(4) - #define GMII_LED1_LINK_100 BIT(5) - #define GMII_LED1_LINK_1000 BIT(6) - #define GMII_LED2_LINK_10 BIT(8) - #define GMII_LED2_LINK_100 BIT(9) - #define GMII_LED2_LINK_1000 BIT(10) - #define LED0_ACTIVE BIT(0) - #define LED0_LINK_10 BIT(1) - #define LED0_LINK_100 BIT(2) - #define LED0_LINK_1000 BIT(3) - #define LED0_FD BIT(4) - #define LED0_USB3_MASK 0x001f - #define LED1_ACTIVE BIT(5) - #define LED1_LINK_10 BIT(6) - #define LED1_LINK_100 BIT(7) - #define LED1_LINK_1000 BIT(8) - #define LED1_FD BIT(9) - #define LED1_USB3_MASK 0x03e0 - #define LED2_ACTIVE BIT(10) - #define LED2_LINK_1000 BIT(13) - #define LED2_LINK_100 BIT(12) - #define LED2_LINK_10 BIT(11) - #define LED2_FD BIT(14) - #define LED_VALID BIT(15) - #define LED2_USB3_MASK 0x7c00 - -#define GMII_PHYPAGE 0x1e -#define GMII_PHY_PAGE_SELECT 0x1f - #define GMII_PHY_PGSEL_EXT 0x0007 - #define GMII_PHY_PGSEL_PAGE0 0x0000 - -struct ax88179_data { - u16 rxctl; - u16 reserved; -}; +#include "ax88179_178a.h" -struct ax88179_int_data { - __le32 intdata1; - __le32 intdata2; -}; +#define DRV_VERSION "1.13.0" -static const struct { - unsigned char ctrl, timer_l, timer_h, size, ifg; -} AX88179_BULKIN_SIZE[] = { - {7, 0x4f, 0, 0x12, 0xff}, - {7, 0x20, 3, 0x16, 0xff}, - {7, 0xae, 7, 0x18, 0xff}, - {7, 0xcc, 0x4c, 0x18, 8}, -}; +static char version[] = +KERN_INFO "ASIX USB Ethernet Adapter:v" DRV_VERSION + " " __TIME__ " " __DATE__ "\n" +" http://www.asix.com.tw\n"; + +static int msg_enable; +module_param(msg_enable, int, 0); +MODULE_PARM_DESC(msg_enable, "usbnet msg_enable"); + +static int bsize = -1; +module_param(bsize, int, 0); +MODULE_PARM_DESC(bsize, "RX Bulk IN Queue Size"); + +static int ifg = -1; +module_param(ifg, int, 0); +MODULE_PARM_DESC(ifg, "RX Bulk IN Inter Frame Gap"); + + +/* EEE advertisement is disabled in default setting */ +static int bEEE = 0; +module_param(bEEE, int, 0); +MODULE_PARM_DESC(bEEE, "EEE advertisement configuration"); + +/* Green ethernet advertisement is disabled in default setting */ +static int bGETH = 0; +module_param(bGETH, int, 0); +MODULE_PARM_DESC(bGETH, "Green ethernet configuration"); +/* ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices */ static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data, int in_pm) { int ret; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); BUG_ON(!dev); @@ -205,13 +93,24 @@ static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, else fn = usbnet_read_cmd_nopm; - ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, data, size); + ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, data, size); if (unlikely(ret < 0)) netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n", index, ret); - +#else + ret = usb_control_msg( + dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + cmd, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + data, + size, + USB_CTRL_GET_TIMEOUT); +#endif return ret; } @@ -219,6 +118,7 @@ static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data, int in_pm) { int ret; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); BUG_ON(!dev); @@ -228,46 +128,40 @@ static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, else fn = usbnet_write_cmd_nopm; - ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, data, size); + ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, data, size); if (unlikely(ret < 0)) netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n", index, ret); - +#else + ret = usb_control_msg( + dev->udev, + usb_sndctrlpipe(dev->udev, 0), + cmd, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + data, + size, + USB_CTRL_SET_TIMEOUT); + +#endif return ret; } -static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, - u16 index, u16 size, void *data) -{ - u16 buf; - - if (2 == size) { - buf = *((u16 *)data); - cpu_to_le16s(&buf); - usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, value, index, &buf, - size); - } else { - usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, value, index, data, - size); - } -} - static int ax88179_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, - u16 index, u16 size, void *data) + u16 index, u16 size, void *data, int eflag) { int ret; - if (2 == size) { - u16 buf; + if (eflag && (2 == size)) { + u16 buf = 0; ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1); le16_to_cpus(&buf); *((u16 *)data) = buf; - } else if (4 == size) { - u32 buf; + } else if (eflag && (4 == size)) { + u32 buf = 0; ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1); le32_to_cpus(&buf); *((u32 *)data) = buf; @@ -284,7 +178,7 @@ static int ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, int ret; if (2 == size) { - u16 buf; + u16 buf = 0; buf = *((u16 *)data); cpu_to_le16s(&buf); ret = __ax88179_write_cmd(dev, cmd, value, index, @@ -298,17 +192,18 @@ static int ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, } static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, - u16 size, void *data) + u16 size, void *data, int eflag) { + int ret; - if (2 == size) { - u16 buf; + if (eflag && (2 == size)) { + u16 buf = 0; ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0); le16_to_cpus(&buf); *((u16 *)data) = buf; - } else if (4 == size) { - u32 buf; + } else if (eflag && (4 == size)) { + u32 buf = 0; ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0); le32_to_cpus(&buf); *((u32 *)data) = buf; @@ -325,7 +220,7 @@ static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, int ret; if (2 == size) { - u16 buf; + u16 buf = 0; buf = *((u16 *)data); cpu_to_le16s(&buf); ret = __ax88179_write_cmd(dev, cmd, value, index, @@ -338,31 +233,138 @@ static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, return ret; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void ax88179_async_cmd_callback(struct urb *urb, struct pt_regs *regs) +#else +static void ax88179_async_cmd_callback(struct urb *urb) +#endif +{ + struct ax88179_async_handle *asyncdata = (struct ax88179_async_handle *)urb->context; + + if (urb->status < 0) + printk(KERN_ERR "ax88179_async_cmd_callback() failed with %d", + urb->status); + + kfree(asyncdata->req); + kfree(asyncdata); + usb_free_urb(urb); + +} + +static void +ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + struct usb_ctrlrequest *req = NULL; + int status = 0; + struct urb *urb = NULL; + void *buf = NULL; + struct ax88179_async_handle *asyncdata = NULL; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urb == NULL) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_err(dev->net, "Error allocating URB in write_cmd_async!"); +#else + deverr(dev, "Error allocating URB in write_cmd_async!"); +#endif + return; + } + + req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); + if (req == NULL) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_err(dev->net, "Failed to allocate memory for control request"); +#else + deverr(dev, "Failed to allocate memory for control request"); +#endif + usb_free_urb(urb); + return; + } + + asyncdata = (struct ax88179_async_handle*) + kmalloc(sizeof(struct ax88179_async_handle), GFP_ATOMIC); + if (asyncdata == NULL) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_err(dev->net, "Failed to allocate memory for async data"); +#else + deverr(dev, "Failed to allocate memory for async data"); +#endif + kfree(req); + usb_free_urb(urb); + return; + } + + asyncdata->req = req; + + if (size == 2) { + asyncdata->rxctl = *((u16 *)data); + cpu_to_le16s(&asyncdata->rxctl); + buf = &asyncdata->rxctl; + } else { + memcpy(asyncdata->m_filter, data, size); + buf = asyncdata->m_filter; + } + + req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + req->bRequest = cmd; + req->wValue = cpu_to_le16(value); + req->wIndex = cpu_to_le16(index); + req->wLength = cpu_to_le16(size); + + usb_fill_control_urb(urb, dev->udev, + usb_sndctrlpipe(dev->udev, 0), + (void *)req, buf, size, + ax88179_async_cmd_callback, asyncdata); + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_err(dev->net, "Error submitting the control message: status=%d", + status); +#else + deverr(dev, "Error submitting the control message: status=%d", + status); +#endif + kfree(req); + kfree(asyncdata); + usb_free_urb(urb); + } +} + static void ax88179_status(struct usbnet *dev, struct urb *urb) { - struct ax88179_int_data *event; - u32 link; + struct ax88179_int_data *event = NULL; + int link = 0; if (urb->actual_length < 8) return; event = urb->transfer_buffer; - le32_to_cpus((void *)&event->intdata1); - - link = (((__force u32)event->intdata1) & AX_INT_PPLS_LINK) >> 16; + link = event->link & AX_INT_PPLS_LINK; if (netif_carrier_ok(dev->net) != link) { - usbnet_link_change(dev, link, 1); - netdev_info(dev->net, "ax88179 - Link status is: %d\n", link); + if (link) { + netif_carrier_on(dev->net); + usbnet_defer_kevent(dev, EVENT_LINK_RESET); + } + else + netif_carrier_off(dev->net); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_info(dev->net, "ax88179_178a - Link status is: %d\n", + link); +#else + devinfo(dev, "ax88179_178a - Link status is: %d\n", link); +#endif } } static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc) { struct usbnet *dev = netdev_priv(netdev); - u16 res; + u16 res = 0; - ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res); + ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res, 1); return res; } @@ -370,30 +372,34 @@ static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc, int val) { struct usbnet *dev = netdev_priv(netdev); - u16 res = (u16) val; + u16 res = (u16)val; ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res); } -static int ax88179_suspend(struct usb_interface *intf, pm_message_t message) +static int ax88179_suspend(struct usb_interface *intf, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10) + pm_message_t message) +#else + u32 message) +#endif { struct usbnet *dev = usb_get_intfdata(intf); - u16 tmp16; - u8 tmp8; + u16 tmp16 = 0; + u8 tmp8 = 0; usbnet_suspend(intf, message); /* Disable RX path */ ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, - 2, 2, &tmp16); + 2, 2, &tmp16, 1); tmp16 &= ~AX_MEDIUM_RECEIVE_EN; - ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16); - /* Force bulk-in zero length */ + /* Force bz */ ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, - 2, 2, &tmp16); - + 2, 2, &tmp16, 1); tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL; ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); @@ -409,73 +415,127 @@ static int ax88179_suspend(struct usb_interface *intf, pm_message_t message) return 0; } -/* This function is used to enable the autodetach function. */ -/* This function is determined by offset 0x43 of EEPROM */ -static int ax88179_auto_detach(struct usbnet *dev, int in_pm) + +static void ax88179_EEE_setting(struct usbnet *dev) { u16 tmp16; - u8 tmp8; - int (*fnr)(struct usbnet *, u8, u16, u16, u16, void *); - int (*fnw)(struct usbnet *, u8, u16, u16, u16, void *); - - if (!in_pm) { - fnr = ax88179_read_cmd; - fnw = ax88179_write_cmd; + + if (bEEE) { + // Enable EEE + tmp16 = 0x07; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_MACR, 2, &tmp16); + + tmp16 = 0x3c; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_MAADR, 2, &tmp16); + + tmp16 = 0x4007; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_MACR, 2, &tmp16); + + tmp16 = 0x06; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_MAADR, 2, &tmp16); } else { - fnr = ax88179_read_cmd_nopm; - fnw = ax88179_write_cmd_nopm; + // Disable EEE + tmp16 = 0x07; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_MACR, 2, &tmp16); + + tmp16 = 0x3c; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_MAADR, 2, &tmp16); + + tmp16 = 0x4007; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_MACR, 2, &tmp16); + + tmp16 = 0x00; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_MAADR, 2, &tmp16); } +} - if (fnr(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16) < 0) - return 0; +static void ax88179_Gether_setting(struct usbnet *dev) +{ - if ((tmp16 == 0xFFFF) || (!(tmp16 & 0x0100))) - return 0; + u16 tmp16; - /* Enable Auto Detach bit */ - tmp8 = 0; - fnr(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); - tmp8 |= AX_CLK_SELECT_ULR; - fnw(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); + if (bGETH) { + // Enable Green Ethernet + tmp16 = 0x03; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + 31, 2, &tmp16); - fnr(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); - tmp16 |= AX_PHYPWR_RSTCTL_AT; - fnw(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); + tmp16 = 0x3247; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + 25, 2, &tmp16); - return 0; + tmp16 = 0x05; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + 31, 2, &tmp16); + + tmp16 = 0x0680; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + 1, 2, &tmp16); + + tmp16 = 0; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + 31, 2, &tmp16); + } else { + // Disable Green Ethernet + tmp16 = 0x03; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + 31, 2, &tmp16); + + tmp16 = 0x3246; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + 25, 2, &tmp16); + + tmp16 = 0; + ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + 31, 2, &tmp16); + } } static int ax88179_resume(struct usb_interface *intf) { struct usbnet *dev = usb_get_intfdata(intf); - u16 tmp16; - u8 tmp8; + u16 tmp16 = 0; + u8 tmp8 = 0; - usbnet_link_change(dev, 0, 0); + netif_carrier_off(dev->net); /* Power up ethernet PHY */ tmp16 = 0; ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); - udelay(1000); - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + usleep_range(1000, 2000); +#else + msleep(1); +#endif tmp16 = AX_PHYPWR_RSTCTL_IPRL; ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); msleep(200); /* Ethernet PHY Auto Detach*/ - ax88179_auto_detach(dev, 1); + ax88179_AutoDetach(dev, 1); - /* Enable clock */ - ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); + /* change clock */ + ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, + 1, 1, &tmp8, 0); tmp8 |= AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); msleep(100); /* Configure RX control register => start operation */ - tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | - AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; + tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_START | AX_RX_CTL_AP | + AX_RX_CTL_AMALL | AX_RX_CTL_AB; + if (NET_IP_ALIGN == 0) + tmp16 |= AX_RX_CTL_IPE; ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); return usbnet_resume(intf); @@ -484,38 +544,57 @@ static int ax88179_resume(struct usb_interface *intf) static void ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) { + struct usbnet *dev = netdev_priv(net); - u8 opt; + u8 *opt = NULL; + + opt = kmalloc(1, GFP_KERNEL); + if (!opt) + return; - if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, - 1, 1, &opt) < 0) { + if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, + 1, 1, opt, 0) < 0) { wolinfo->supported = 0; wolinfo->wolopts = 0; + kfree(opt); return; } wolinfo->supported = WAKE_PHY | WAKE_MAGIC; - wolinfo->wolopts = 0; - if (opt & AX_MONITOR_MODE_RWLC) + + if (*opt & AX_MONITOR_MODE_RWLC) wolinfo->wolopts |= WAKE_PHY; - if (opt & AX_MONITOR_MODE_RWMP) + if (*opt & AX_MONITOR_MODE_RWMP) wolinfo->wolopts |= WAKE_MAGIC; + + kfree(opt); } static int ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) { struct usbnet *dev = netdev_priv(net); - u8 opt = 0; + u8 *opt = NULL; + + opt = kmalloc(1, GFP_KERNEL); + if (!opt) + return -ENOMEM; + + *opt = 0; if (wolinfo->wolopts & WAKE_PHY) - opt |= AX_MONITOR_MODE_RWLC; + *opt |= AX_MONITOR_MODE_RWLC; + else + *opt &= ~AX_MONITOR_MODE_RWLC; + if (wolinfo->wolopts & WAKE_MAGIC) - opt |= AX_MONITOR_MODE_RWMP; + *opt |= AX_MONITOR_MODE_RWMP; + else + *opt &= ~AX_MONITOR_MODE_RWMP; - if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, - 1, 1, &opt) < 0) - return -EINVAL; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, opt); + + kfree(opt); return 0; } @@ -530,9 +609,9 @@ ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data) { struct usbnet *dev = netdev_priv(net); - u16 *eeprom_buff; - int first_word, last_word; - int i, ret; + u16 *eeprom_buff = NULL; + int first_word = 0, last_word = 0; + int i = 0; if (eeprom->len == 0) return -EINVAL; @@ -548,10 +627,8 @@ ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, /* ax88179/178A returns 2 bytes from eeprom on read */ for (i = first_word; i <= last_word; i++) { - ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2, - &eeprom_buff[i - first_word], - 0); - if (ret < 0) { + if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2, + &(eeprom_buff[i - first_word]), 0) < 0) { kfree(eeprom_buff); return -EIO; } @@ -562,6 +639,14 @@ ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, return 0; } +static void ax88179_get_drvinfo(struct net_device *net, + struct ethtool_drvinfo *info) +{ + /* Inherit standard device info */ + usbnet_get_drvinfo(net, info); + info->eedump_len = 0x3e; +} + static int ax88179_get_settings(struct net_device *net, struct ethtool_cmd *cmd) { struct usbnet *dev = netdev_priv(net); @@ -574,14 +659,105 @@ static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd) return mii_ethtool_sset(&dev->mii, cmd); } - static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd) { struct usbnet *dev = netdev_priv(net); - return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); + return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 28) +static int ax88179_netdev_stop(struct net_device *net) +{ + struct usbnet *dev = netdev_priv(net); + u16 tmp16 = 0; + + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &tmp16, 1); + tmp16 &= ~AX_MEDIUM_RECEIVE_EN; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &tmp16); + return 0; +} +#endif + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) +static int ax88179_set_csums(struct usbnet *dev) +{ + struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; + u8 checksum = 0; + + if (ax179_data->checksum & AX_RX_CHECKSUM) + checksum = AX_RXCOE_DEF_CSUM; + else + checksum = 0; + + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &checksum); + + if (ax179_data->checksum & AX_TX_CHECKSUM) + checksum = AX_TXCOE_DEF_CSUM; + else + checksum = 0; + + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &checksum); + + return 0; +} + +static u32 ax88179_get_tx_csum(struct net_device *netdev) +{ + struct usbnet *dev = netdev_priv(netdev); + struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; + return ax179_data->checksum & AX_TX_CHECKSUM; } -static const struct ethtool_ops ax88179_ethtool_ops = { +static u32 ax88179_get_rx_csum(struct net_device *netdev) +{ + struct usbnet *dev = netdev_priv(netdev); + struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; + return ax179_data->checksum & AX_RX_CHECKSUM; +} + +static int ax88179_set_rx_csum(struct net_device *netdev, u32 val) +{ + struct usbnet *dev = netdev_priv(netdev); + struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; + + if (val) + ax179_data->checksum |= AX_RX_CHECKSUM; + else + ax179_data->checksum &= ~AX_RX_CHECKSUM; + return ax88179_set_csums(dev); +} + +static int ax88179_set_tx_csum(struct net_device *netdev, u32 val) +{ + struct usbnet *dev = netdev_priv(netdev); + struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; + + if (val) + ax179_data->checksum |= AX_TX_CHECKSUM; + else + ax179_data->checksum &= ~AX_TX_CHECKSUM; + + ethtool_op_set_tx_csum(netdev, val); + + return ax88179_set_csums(dev); +} + +static int ax88179_set_tso(struct net_device *netdev, u32 data) +{ + if (data) + netdev->features |= NETIF_F_TSO; + else + netdev->features &= ~NETIF_F_TSO; + + return 0; +} +#endif + +static struct ethtool_ops ax88179_ethtool_ops = { + .get_drvinfo = ax88179_get_drvinfo, .get_link = ethtool_op_get_link, .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, @@ -591,41 +767,77 @@ static const struct ethtool_ops ax88179_ethtool_ops = { .get_eeprom = ax88179_get_eeprom, .get_settings = ax88179_get_settings, .set_settings = ax88179_set_settings, - .nway_reset = usbnet_nway_reset, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + .set_tx_csum = ax88179_set_tx_csum, + .get_tx_csum = ax88179_get_tx_csum, + .get_rx_csum = ax88179_get_rx_csum, + .set_rx_csum = ax88179_set_rx_csum, + .get_tso = ethtool_op_get_tso, + .set_tso = ax88179_set_tso, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg +#endif }; static void ax88179_set_multicast(struct net_device *net) { struct usbnet *dev = netdev_priv(net); - struct ax88179_data *data = (struct ax88179_data *)dev->data; + struct ax88179_data *data = (struct ax88179_data *)&dev->data; u8 *m_filter = ((u8 *)dev->data) + 12; + int mc_count = 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + mc_count = net->mc_count; +#else + mc_count = netdev_mc_count(net); +#endif - data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE); + data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB); + if (NET_IP_ALIGN == 0) + data->rxctl |= AX_RX_CTL_IPE; if (net->flags & IFF_PROMISC) { data->rxctl |= AX_RX_CTL_PRO; - } else if (net->flags & IFF_ALLMULTI || - netdev_mc_count(net) > AX_MAX_MCAST) { + } else if (net->flags & IFF_ALLMULTI + || mc_count > AX_MAX_MCAST) { data->rxctl |= AX_RX_CTL_AMALL; - } else if (netdev_mc_empty(net)) { + } else if (mc_count == 0) { /* just broadcast and directed */ } else { - /* We use the 20 byte dev->data for our 8 byte filter buffer - * to avoid allocating memory that is tricky to free later - */ - u32 crc_bits; - struct netdev_hw_addr *ha; - - memset(m_filter, 0, AX_MCAST_FLTSIZE); - + /* We use the 20 byte dev->data + * for our 8 byte filter buffer + * to avoid allocating memory that + * is tricky to free later */ + u32 crc_bits = 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + struct dev_mc_list *mc_list = net->mc_list; + int i = 0; + + memset(m_filter, 0, AX_MCAST_FILTER_SIZE); + + /* Build the multicast hash filter. */ + for (i = 0; i < net->mc_count; i++) { + crc_bits = + ether_crc(ETH_ALEN, + mc_list->dmi_addr) >> 26; + *(m_filter + (crc_bits >> 3)) |= + 1 << (crc_bits & 7); + mc_list = mc_list->next; + } +#else + struct netdev_hw_addr *ha = NULL; + memset(m_filter, 0, AX_MCAST_FILTER_SIZE); netdev_for_each_mc_addr(ha, net) { crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; - *(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7)); + *(m_filter + (crc_bits >> 3)) |= + 1 << (crc_bits & 7); } - - ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY, - AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE, - m_filter); +#endif + ax88179_write_cmd_async(dev, AX_ACCESS_MAC, + AX_MULTI_FILTER_ARRY, + AX_MCAST_FILTER_SIZE, + AX_MCAST_FILTER_SIZE, m_filter); data->rxctl |= AX_RX_CTL_AM; } @@ -634,27 +846,40 @@ static void ax88179_set_multicast(struct net_device *net) 2, 2, &data->rxctl); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) static int +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) ax88179_set_features(struct net_device *net, netdev_features_t features) +#else +ax88179_set_features(struct net_device *net, u32 features) +#endif + { - u8 tmp; + u8 tmp = 0; struct usbnet *dev = netdev_priv(net); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) netdev_features_t changed = net->features ^ features; +#else + u32 changed = net->features ^ features; +#endif if (changed & NETIF_F_IP_CSUM) { - ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, + 1, 1, &tmp, 0); tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); } if (changed & NETIF_F_IPV6_CSUM) { - ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, + 1, 1, &tmp, 0); tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); } if (changed & NETIF_F_RXCSUM) { - ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp); + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, + 1, 1, &tmp, 0); tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp); @@ -662,11 +887,12 @@ ax88179_set_features(struct net_device *net, netdev_features_t features) return 0; } +#endif static int ax88179_change_mtu(struct net_device *net, int new_mtu) { struct usbnet *dev = netdev_priv(net); - u16 tmp16; + u16 tmp16 = 0; if (new_mtu <= 0 || new_mtu > 4088) return -EINVAL; @@ -676,13 +902,13 @@ static int ax88179_change_mtu(struct net_device *net, int new_mtu) if (net->mtu > 1500) { ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, - 2, 2, &tmp16); + 2, 2, &tmp16, 1); tmp16 |= AX_MEDIUM_JUMBO_EN; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16); } else { ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, - 2, 2, &tmp16); + 2, 2, &tmp16, 1); tmp16 &= ~AX_MEDIUM_JUMBO_EN; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16); @@ -695,7 +921,6 @@ static int ax88179_set_mac_addr(struct net_device *net, void *p) { struct usbnet *dev = netdev_priv(net); struct sockaddr *addr = p; - int ret; if (netif_running(net)) return -EBUSY; @@ -705,57 +930,64 @@ static int ax88179_set_mac_addr(struct net_device *net, void *p) memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); /* Set the MAC address */ - ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, + return ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN, net->dev_addr); - if (ret < 0) - return ret; - return 0; } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) static const struct net_device_ops ax88179_netdev_ops = { .ndo_open = usbnet_open, .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = ax88179_change_mtu, + .ndo_do_ioctl = ax88179_ioctl, .ndo_set_mac_address = ax88179_set_mac_addr, .ndo_validate_addr = eth_validate_addr, - .ndo_do_ioctl = ax88179_ioctl, +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 2, 0) + .ndo_set_multicast_list = ax88179_set_multicast, +#else .ndo_set_rx_mode = ax88179_set_multicast, +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) .ndo_set_features = ax88179_set_features, +#endif }; +#endif static int ax88179_check_eeprom(struct usbnet *dev) { - u8 i, buf, eeprom[20]; - u16 csum, delay = HZ / 10; - unsigned long jtimeout; + u8 i = 0; + u8 buf[2] = {0}; + u8 eeprom[20] = {0}; + u16 csum = 0, delay = HZ / 10; + unsigned long jtimeout = 0; /* Read EEPROM content */ - for (i = 0; i < 6; i++) { - buf = i; + for (i = 0 ; i < 6; i++) { + + buf[0] = i; if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR, - 1, 1, &buf) < 0) + 1, 1, buf) < 0) return -EINVAL; - buf = EEP_RD; + buf[0] = EEP_RD; if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, - 1, 1, &buf) < 0) + 1, 1, buf) < 0) return -EINVAL; jtimeout = jiffies + delay; do { ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, - 1, 1, &buf); + 1, 1, buf, 0); if (time_after(jiffies, jtimeout)) return -EINVAL; + } while (buf[0] & EEP_BUSY); - } while (buf & EEP_BUSY); - - __ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW, - 2, 2, &eeprom[i * 2], 0); + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW, + 2, 2, &eeprom[i * 2], 0); if ((i == 0) && (eeprom[0] == 0xFF)) return -EINVAL; @@ -763,22 +995,23 @@ static int ax88179_check_eeprom(struct usbnet *dev) csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9]; csum = (csum >> 8) + (csum & 0xff); - if ((csum + eeprom[10]) != 0xff) - return -EINVAL; - return 0; + if ((csum + eeprom[10]) == 0xff) + return AX_EEP_EFUSE_CORRECT; + else + return -EINVAL; } -static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode) +static int ax88179_check_efuse(struct usbnet *dev, void *ledmode) { - u8 i; - u8 efuse[64]; + u8 i = 0; + u8 efuse[64] = {0x00}; u16 csum = 0; - if (ax88179_read_cmd(dev, AX_ACCESS_EFUS, 0, 64, 64, efuse) < 0) + if (ax88179_read_cmd(dev, AX_ACCESS_EFUSE, 0, 64, 64, efuse, 0) < 0) return -EINVAL; - if (*efuse == 0xFF) + if (efuse[0] == 0xFF) return -EINVAL; for (i = 0; i < 64; i++) @@ -787,24 +1020,38 @@ static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode) while (csum > 255) csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF); - if (csum != 0xFF) + if (csum == 0xFF) { + memcpy((u8 *)ledmode, &efuse[51], 2); + return AX_EEP_EFUSE_CORRECT; + } else { return -EINVAL; - - *ledmode = (efuse[51] << 8) | efuse[52]; - - return 0; + } } -static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue) +static int ax88179_convert_old_led(struct usbnet *dev, u8 efuse, void *ledvalue) { - u16 led; - - /* Loaded the old eFuse LED Mode */ - if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0) - return -EINVAL; - - led >>= 8; - switch (led) { + u8 ledmode = 0; + u16 tmp = 0; + u16 led = 0; + + /* loaded the old eFuse LED Mode */ + if (efuse) { + if (ax88179_read_cmd(dev, AX_ACCESS_EFUSE, 0x18, + 1, 2, &tmp, 1) < 0) + return -EINVAL; + ledmode = (u8)(tmp & 0xFF); + } else { /* loaded the old EEprom LED Mode */ + if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, + 1, 2, &tmp, 1) < 0) + return -EINVAL; + ledmode = (u8) (tmp >> 8); + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_dbg(dev->net, "Old LED Mode = %02X\n", ledmode); +#else + devdbg(dev, "Old LED Mode = %02X\n", ledmode); +#endif + switch (ledmode) { case 0xFF: led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 | LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 | @@ -828,21 +1075,22 @@ static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue) break; } - *ledvalue = led; + memcpy((u8 *)ledvalue, &led, 2); return 0; } static int ax88179_led_setting(struct usbnet *dev) { - u8 ledfd, value = 0; - u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10; - unsigned long jtimeout; + u8 ledfd = 0, value = 0; + u16 tmp = 0, ledact = 0, ledlink = 0, ledvalue = 0, delay = HZ / 10; + unsigned long jtimeout = 0; - /* Check AX88179 version. UA1 or UA2*/ - ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value); + /* Check AX88179 version. UA1 or UA2 */ + ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value, 0); - if (!(value & AX_SECLD)) { /* UA1 */ + /* UA1 */ + if (!(value & AX_SECLD)) { value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN | AX_GPIO_CTRL_GPIO1EN; if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_GPIO_CTRL, @@ -850,8 +1098,8 @@ static int ax88179_led_setting(struct usbnet *dev) return -EINVAL; } - /* Check EEPROM */ - if (!ax88179_check_eeprom(dev)) { + /* check EEprom */ + if (ax88179_check_eeprom(dev) == AX_EEP_EFUSE_CORRECT) { value = 0x42; if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR, 1, 1, &value) < 0) @@ -865,33 +1113,35 @@ static int ax88179_led_setting(struct usbnet *dev) jtimeout = jiffies + delay; do { ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, - 1, 1, &value); + 1, 1, &value, 0); + + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, + 1, 1, &value, 0); if (time_after(jiffies, jtimeout)) return -EINVAL; - } while (value & EEP_BUSY); ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_HIGH, - 1, 1, &value); + 1, 1, &value, 0); ledvalue = (value << 8); - ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW, - 1, 1, &value); + 1, 1, &value, 0); ledvalue |= value; /* load internal ROM for defaule setting */ if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0)) - ax88179_convert_old_led(dev, &ledvalue); + ax88179_convert_old_led(dev, 0, &ledvalue); - } else if (!ax88179_check_efuse(dev, &ledvalue)) { + } else if (ax88179_check_efuse(dev, &ledvalue) == + AX_EEP_EFUSE_CORRECT) { /* check efuse */ if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0)) - ax88179_convert_old_led(dev, &ledvalue); + ax88179_convert_old_led(dev, 0, &ledvalue); } else { - ax88179_convert_old_led(dev, &ledvalue); + ax88179_convert_old_led(dev, 0, &ledvalue); } - tmp = GMII_PHY_PGSEL_EXT; + tmp = GMII_PHY_PAGE_SELECT_EXT; ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_PHY_PAGE_SELECT, 2, &tmp); @@ -900,59 +1150,51 @@ static int ax88179_led_setting(struct usbnet *dev) GMII_PHYPAGE, 2, &tmp); ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, - GMII_LED_ACT, 2, &ledact); + GMII_LED_ACTIVE, 2, &ledact, 1); ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, - GMII_LED_LINK, 2, &ledlink); + GMII_LED_LINK, 2, &ledlink, 1); ledact &= GMII_LED_ACTIVE_MASK; ledlink &= GMII_LED_LINK_MASK; if (ledvalue & LED0_ACTIVE) ledact |= GMII_LED0_ACTIVE; - if (ledvalue & LED1_ACTIVE) ledact |= GMII_LED1_ACTIVE; - if (ledvalue & LED2_ACTIVE) ledact |= GMII_LED2_ACTIVE; if (ledvalue & LED0_LINK_10) ledlink |= GMII_LED0_LINK_10; - if (ledvalue & LED1_LINK_10) ledlink |= GMII_LED1_LINK_10; - if (ledvalue & LED2_LINK_10) ledlink |= GMII_LED2_LINK_10; if (ledvalue & LED0_LINK_100) ledlink |= GMII_LED0_LINK_100; - if (ledvalue & LED1_LINK_100) ledlink |= GMII_LED1_LINK_100; - if (ledvalue & LED2_LINK_100) ledlink |= GMII_LED2_LINK_100; if (ledvalue & LED0_LINK_1000) ledlink |= GMII_LED0_LINK_1000; - if (ledvalue & LED1_LINK_1000) ledlink |= GMII_LED1_LINK_1000; - if (ledvalue & LED2_LINK_1000) ledlink |= GMII_LED2_LINK_1000; tmp = ledact; ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, - GMII_LED_ACT, 2, &tmp); + GMII_LED_ACTIVE, 2, &tmp); tmp = ledlink; ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_LED_LINK, 2, &tmp); - tmp = GMII_PHY_PGSEL_PAGE0; + tmp = GMII_PHY_PAGE_SELECT_PAGE0; ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_PHY_PAGE_SELECT, 2, &tmp); @@ -963,30 +1205,215 @@ static int ax88179_led_setting(struct usbnet *dev) else if ((ledvalue & LED0_USB3_MASK) == 0) ledfd |= 0x02; + if (ledvalue & LED1_FD) ledfd |= 0x04; else if ((ledvalue & LED1_USB3_MASK) == 0) ledfd |= 0x08; - if (ledvalue & LED2_FD) + if (ledvalue & LED2_FD) /* LED2_FD */ ledfd |= 0x10; - else if ((ledvalue & LED2_USB3_MASK) == 0) + else if ((ledvalue & LED2_USB3_MASK) == 0) /* LED2_USB3 */ ledfd |= 0x20; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd); + ax88179_write_cmd(dev, AX_ACCESS_MAC, 0x73, 1, 1, &ledfd); + + return 0; +} + +static int ax88179_AutoDetach(struct usbnet *dev, int in_pm) +{ + u16 tmp16 = 0; + u8 tmp8 = 0; + int (*fnr)(struct usbnet *, u8, u16, u16, u16, void *, int); + int (*fnw)(struct usbnet *, u8, u16, u16, u16, void *); + + if (!in_pm) { + fnr = ax88179_read_cmd; + fnw = ax88179_write_cmd; + } else { + fnr = ax88179_read_cmd_nopm; + fnw = ax88179_write_cmd_nopm; + } + + if (fnr(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16, 1) < 0) + return 0; + + if ((tmp16 == 0xFFFF) || (!(tmp16 & 0x0100))) + return 0; + + /* Enable Auto Detach bit */ + tmp8 = 0; + fnr(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8, 0); + tmp8 |= AX_CLK_SELECT_ULR; + fnw(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); + + fnr(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16, 1); + tmp16 |= AX_PHYPWR_RSTCTL_AUTODETACH; + fnw(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); return 0; } +static int access_eeprom_mac(struct usbnet *dev, u8 *buf, u8 offset, bool wflag) +{ + int ret = 0, i; + u16* tmp = (u16*)buf; + + for (i = 0; i < (ETH_ALEN >> 1); i++) { + if (wflag) { + u16 wd = cpu_to_le16(*(tmp + i)); + ret = ax88179_write_cmd(dev, AX_ACCESS_EEPROM, + offset + i, 1, 2, &wd); + if (ret < 0) + break; + + mdelay(15); + } + else { + ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, + offset + i, 1, 2, tmp + i, 0); + if (ret < 0) + break; + } + } + + if (!wflag) { + if (ret < 0) { + netdev_dbg(dev->net, "Failed to read MAC address from EEPROM: %d\n", ret); + return ret; + } + memcpy(dev->net->dev_addr, buf, ETH_ALEN); + } + else { + /* reload eeprom data */ + ret = ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0, 0, 0, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ax88179_check_ether_addr(struct usbnet *dev) +{ + unsigned char *tmp = (unsigned char*)dev->net->dev_addr; + u8 default_mac[6] = {0, 0x0e, 0xc6, 0x81, 0x79, 0x01}; + + if (((*((u8*)tmp) == 0) && (*((u8*)tmp + 1) == 0) && (*((u8*)tmp + 2) == 0)) || + !is_valid_ether_addr((u8*)tmp) || + !memcmp(dev->net->dev_addr, default_mac, ETH_ALEN)) { + int i; + + printk("Found invalid EEPROM MAC address value "); + + for (i = 0; i < ETH_ALEN; i++) { + printk("%02X", *((u8*)tmp + i)); + if (i != 5) + printk("-"); + } + printk("\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + eth_hw_addr_random(dev->net); +#else + dev->net->addr_assign_type |= NET_ADDR_RANDOM; + random_ether_addr(dev->net->dev_addr); +#endif + *tmp = 0; + *(tmp + 1) = 0x0E; + *(tmp + 2) = 0xC6; + *(tmp + 3) = 0x8E; + + return -EADDRNOTAVAIL; + } + return 0; +} + +static int ax88179_get_mac(struct usbnet *dev, u8* buf) +{ + int ret, i; + + ret = access_eeprom_mac(dev, buf, 0x0, 0); + if (ret < 0) + goto out; + + if (ax88179_check_ether_addr(dev)) { + ret = access_eeprom_mac(dev, dev->net->dev_addr, 0x0, 1); + if (ret < 0) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_err(dev->net, "Failed to write MAC to EEPROM: %d", ret); +#else + deverr(dev, "Failed to write MAC to EEPROM: %d", ret); +#endif + goto out; + } + + msleep(5); + + ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, + ETH_ALEN, ETH_ALEN, buf, 0); + if (ret < 0) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_err(dev->net, "Failed to read MAC address: %d", ret); +#else + deverr(dev, "Failed to read MAC address: %d", ret); +#endif + goto out; + } + + for (i = 0; i < ETH_ALEN; i++) + if (*(dev->net->dev_addr + i) != *((u8*)buf + i)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_warn(dev->net, "Found invalid EEPROM part or non-EEPROM"); +#else + devwarn(dev, "Found invalid EEPROM part or non-EEPROM"); +#endif + break; + } + } + + memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN); + + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, + ETH_ALEN, dev->net->dev_addr); + + if (ret < 0) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_err(dev->net, "Failed to write MAC address: %d", ret); +#else + deverr(dev, "Failed to write MAC address: %d", ret); +#endif + goto out; + } + + return 0; +out: + return ret; +} + static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) { - u8 buf[5]; - u16 *tmp16; - u8 *tmp; + void *buf = NULL; + u16 *tmp16 = NULL; + u8 *tmp = NULL; + int ret; + struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; usbnet_get_endpoints(dev, intf); + if (msg_enable != 0) + dev->msg_enable = msg_enable; + + buf = kmalloc(6, GFP_KERNEL); + if (!buf) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_err(dev->net, "Cannot allocate memory for buffer"); +#else + deverr(dev, "Cannot allocate memory for buffer"); +#endif + return -ENOMEM; + } tmp16 = (u16 *)buf; tmp = (u8 *)buf; @@ -1003,26 +1430,53 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); msleep(100); - ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, - ETH_ALEN, dev->net->dev_addr); - memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN); - - /* RX bulk configuration */ + /* Get the MAC address */ + memset(buf, 0, ETH_ALEN); + ret = ax88179_get_mac(dev, buf); + if (ret) + goto out; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_dbg(dev->net, "MAC [%02x-%02x-%02x-%02x-%02x-%02x]\n", + dev->net->dev_addr[0], dev->net->dev_addr[1], + dev->net->dev_addr[2], dev->net->dev_addr[3], + dev->net->dev_addr[4], dev->net->dev_addr[5]); +#else + devdbg(dev, "MAC [%02x-%02x-%02x-%02x-%02x-%02x]\n", + dev->net->dev_addr[0], dev->net->dev_addr[1], + dev->net->dev_addr[2], dev->net->dev_addr[3], + dev->net->dev_addr[4], dev->net->dev_addr[5]); +#endif + + /* RX bulk configuration, default for USB3.0 to Giga*/ memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); dev->rx_urb_size = 1024 * 20; - *tmp = 0x34; + tmp[0] = 0x34; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp); - *tmp = 0x52; + tmp[0] = 0x52; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + dev->net->do_ioctl = ax88179_ioctl; + dev->net->set_multicast_list = ax88179_set_multicast; + dev->net->set_mac_address = ax88179_set_mac_addr; + dev->net->change_mtu = ax88179_change_mtu; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 28) + dev->net->stop = ax88179_netdev_stop; +#endif +#else dev->net->netdev_ops = &ax88179_netdev_ops; +#endif + dev->net->ethtool_ops = &ax88179_ethtool_ops; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) dev->net->needed_headroom = 8; +#endif /* Initialize MII structure */ dev->mii.dev = dev->net; @@ -1033,11 +1487,21 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) dev->mii.phy_id = 0x03; dev->mii.supports_gmii = 1; - dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_RXCSUM; - - dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_RXCSUM; + dev->net->features |= NETIF_F_IP_CSUM; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22) + dev->net->features |= NETIF_F_IPV6_CSUM; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (usb_device_no_sg_constraint(dev->udev)) + dev->can_dma_sg = 1; + dev->net->features |= NETIF_F_SG | NETIF_F_TSO; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + dev->net->hw_features |= NETIF_F_IP_CSUM; + dev->net->hw_features |= NETIF_F_IPV6_CSUM; + dev->net->hw_features |= NETIF_F_SG | NETIF_F_TSO; +#endif /* Enable checksum offload */ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | @@ -1048,46 +1512,80 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp); + ax179_data->checksum |= AX_RX_CHECKSUM | AX_TX_CHECKSUM; + /* Configure RX control register => start operation */ - *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | - AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; + *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_START | AX_RX_CTL_AP | + AX_RX_CTL_AMALL | AX_RX_CTL_AB; + if (NET_IP_ALIGN == 0) + *tmp16 |= AX_RX_CTL_IPE; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16); *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | - AX_MONITOR_MODE_RWMP; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp); - + AX_MONITOR_MODE_RWMP; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, tmp); + + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, tmp, 0); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_dbg(dev->net, "Monitor mode = 0x%02x\n", *tmp); +#else + devdbg(dev, "Monitor mode = 0x%02x\n", *tmp); +#endif /* Configure default medium type => giga */ - *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | - AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE | - AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE; + *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | + AX_MEDIUM_GIGAMODE; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16); ax88179_led_setting(dev); + ax88179_EEE_setting(dev); + + ax88179_Gether_setting(dev); + /* Restart autoneg */ mii_nway_restart(&dev->mii); - usbnet_link_change(dev, 0, 0); + netif_carrier_off(dev->net); + kfree(buf); + printk(version); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_info(dev->net, "mtu %d\n", dev->net->mtu); +#else + devinfo(dev, "mtu %d\n", dev->net->mtu); +#endif return 0; + +out: + kfree(buf); + return ret; + } static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf) { - u16 tmp16; - - /* Configure RX control register => stop operation */ - tmp16 = AX_RX_CTL_STOP; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); - - tmp16 = 0; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16); - - /* Power down ethernet PHY */ - tmp16 = 0; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); + u16 tmp16 = 0; + u8 tmp8 = 0; + struct ax88179_data *ax179_data = (struct ax88179_data *) dev->data; + + if (ax179_data) { + /* Configure RX control register => stop operation */ + tmp16 = AX_RX_CTL_STOP; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); + + tmp8 = 0x0; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, + 1, 1, &tmp8); + + /* Power down ethernet PHY */ + tmp16 = AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, + 2, 2, &tmp16); + msleep(200); + } } static void @@ -1108,18 +1606,18 @@ ax88179_rx_checksum(struct sk_buff *skb, u32 *pkt_hdr) static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb) { - struct sk_buff *ax_skb; - int pkt_cnt; - u32 rx_hdr; - u16 hdr_off; - u32 *pkt_hdr; - - /* This check is no longer done by usbnet */ - if (skb->len < dev->net->hard_header_len) - return 0; + struct sk_buff *ax_skb = NULL; + int pkt_cnt = 0; + u32 rx_hdr = 0; + u16 hdr_off = 0; + u32 *pkt_hdr = NULL; skb_trim(skb, skb->len - 4); - memcpy(&rx_hdr, skb_tail_pointer(skb), 4); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + memcpy(&rx_hdr, skb_tail_pointer(skb), sizeof(rx_hdr)); +#else + memcpy(&rx_hdr, skb->tail, sizeof(rx_hdr)); +#endif le32_to_cpus(&rx_hdr); pkt_cnt = (u16)rx_hdr; @@ -1140,22 +1638,53 @@ static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb) continue; } - if (pkt_cnt == 0) { - /* Skip IP alignment psudo header */ - skb_pull(skb, 2); + if (pkt_cnt == 0) { skb->len = pkt_len; - skb_set_tail_pointer(skb, pkt_len); - skb->truesize = pkt_len + sizeof(struct sk_buff); + + /* Skip IP alignment psudo header */ + if (NET_IP_ALIGN == 0) + skb_pull(skb, 2); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + skb->tail = skb->data + skb->len; +#else + skb_set_tail_pointer(skb, skb->len); +#endif + skb->truesize = skb->len + sizeof(struct sk_buff); ax88179_rx_checksum(skb, pkt_hdr); + return 1; } +#ifndef RX_SKB_COPY ax_skb = skb_clone(skb, GFP_ATOMIC); +#else + ax_skb = alloc_skb(pkt_len + NET_IP_ALIGN, GFP_ATOMIC); + skb_reserve(ax_skb, NET_IP_ALIGN); +#endif + if (ax_skb) { +#ifndef RX_SKB_COPY ax_skb->len = pkt_len; - ax_skb->data = skb->data + 2; - skb_set_tail_pointer(ax_skb, pkt_len); - ax_skb->truesize = pkt_len + sizeof(struct sk_buff); + + /* Skip IP alignment psudo header */ + if (NET_IP_ALIGN == 0) + skb_pull(ax_skb, 2); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + ax_skb->tail = ax_skb->data + ax_skb->len; +#else + skb_set_tail_pointer(ax_skb, ax_skb->len); +#endif + +#else + skb_put(ax_skb, pkt_len); + memcpy(ax_skb->data, skb->data, pkt_len); + + if (NET_IP_ALIGN == 0) + skb_pull(ax_skb, 2); +#endif + ax_skb->truesize = ax_skb->len + sizeof(struct sk_buff); ax88179_rx_checksum(ax_skb, pkt_hdr); usbnet_skb_return(dev, ax_skb); } else { @@ -1171,30 +1700,40 @@ static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb) static struct sk_buff * ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { - u32 tx_hdr1, tx_hdr2; + u32 tx_hdr1 = 0, tx_hdr2 = 0; int frame_size = dev->maxpacket; int mss = skb_shinfo(skb)->gso_size; - int headroom; - int tailroom; + int headroom = 0; + int tailroom = 0; tx_hdr1 = skb->len; tx_hdr2 = mss; if (((skb->len + 8) % frame_size) == 0) tx_hdr2 |= 0x80008000; /* Enable padding */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) && + skb_linearize(skb)) + return NULL; +#else + if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb)) + return NULL; +#endif + headroom = skb_headroom(skb); tailroom = skb_tailroom(skb); - if (!skb_header_cloned(skb) && - !skb_cloned(skb) && - (headroom + tailroom) >= 8) { + if ((headroom + tailroom) >= 8) { if (headroom < 8) { skb->data = memmove(skb->head + 8, skb->data, skb->len); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + skb->tail = skb->data + skb->len; +#else skb_set_tail_pointer(skb, skb->len); +#endif } } else { - struct sk_buff *skb2; - + struct sk_buff *skb2 = NULL; skb2 = skb_copy_expand(skb, 8, 0, flags); dev_kfree_skb_any(skb); skb = skb2; @@ -1204,49 +1743,57 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) skb_push(skb, 4); cpu_to_le32s(&tx_hdr2); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + memcpy(skb->data, &tx_hdr2, 4); +#else skb_copy_to_linear_data(skb, &tx_hdr2, 4); +#endif skb_push(skb, 4); cpu_to_le32s(&tx_hdr1); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + memcpy(skb->data, &tx_hdr1, 4); +#else skb_copy_to_linear_data(skb, &tx_hdr1, 4); +#endif return skb; } static int ax88179_link_reset(struct usbnet *dev) { - struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; - u8 tmp[5], link_sts; - u16 mode, tmp16, delay = HZ / 10; + struct ax88179_data *data = (struct ax88179_data *)&dev->data; + u8 tmp[5] = {0}, link_sts = 0; + u16 mode = 0, tmp16 = 0, delay = HZ/10; u32 tmp32 = 0x40000000; - unsigned long jtimeout; + unsigned long jtimeout = 0; jtimeout = jiffies + delay; + while (tmp32 & 0x40000000) { mode = 0; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &mode); - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, - &ax179_data->rxctl); + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, + 2, 2, &data->rxctl); - /*link up, check the usb device control TX FIFO full or empty*/ - ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32); + /* link up, check the usb device control TX FIFO full or empty*/ + ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32, 1); if (time_after(jiffies, jtimeout)) return 0; } - mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | - AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE; + mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN; ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, - 1, 1, &link_sts); - + 1, 1, &link_sts, 0); ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, - GMII_PHY_PHYSR, 2, &tmp16); + GMII_PHY_PHYSR, 2, &tmp16, 1); - if (!(tmp16 & GMII_PHY_PHYSR_LINK)) { + if (!(tmp16 & GMII_PHY_PHYSR_LINK)) return 0; - } else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) { + else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) { mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ; if (dev->net->mtu > 1500) mode |= AX_MEDIUM_JUMBO_EN; @@ -1258,36 +1805,69 @@ static int ax88179_link_reset(struct usbnet *dev) else memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); } else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) { - mode |= AX_MEDIUM_PS; - + mode |= AX_MEDIUM_PS; /* Bit 9 : PS */ if (link_sts & (AX_USB_SS | AX_USB_HS)) memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5); else memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); - } else { + } else memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); + + if (bsize != -1) { + if (bsize > 24) + bsize = 24; + + else if (bsize == 0) { + tmp[1] = 0; + tmp[2] = 0; + } + + tmp[3] = (u8)bsize; + } + + if (ifg != -1) { + if (ifg > 255) + ifg = 255; + tmp[4] = (u8)ifg; } /* RX bulk configuration */ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); + if (tmp16 & GMII_PHY_PHYSR_FULL) + mode |= AX_MEDIUM_FULL_DUPLEX; /* Bit 1 : FD */ + dev->rx_urb_size = (1024 * (tmp[3] + 2)); - if (tmp16 & GMII_PHY_PHYSR_FULL) - mode |= AX_MEDIUM_FULL_DUPLEX; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_info(dev->net, "Write medium type: 0x%04x\n", mode); +#else + devinfo(dev, "Write medium type: 0x%04x\n", mode); +#endif + /* Configure default medium type => giga */ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &mode); - - netif_carrier_on(dev->net); + mii_check_media(&dev->mii, 1, 1); return 0; } static int ax88179_reset(struct usbnet *dev) { - u8 buf[5]; - u16 *tmp16; - u8 *tmp; + void *buf = NULL; + u16 *tmp16 = NULL; + u8 *tmp = NULL; + struct ax88179_data *ax179_data = (struct ax88179_data *) dev->data; + buf = kmalloc(6, GFP_KERNEL); + + if (!buf) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_err(dev->net, "Cannot allocate memory for buffer"); +#else + deverr(dev, "Cannot allocate memory for buffer"); +#endif + return -ENOMEM; + } tmp16 = (u16 *)buf; tmp = (u8 *)buf; @@ -1295,7 +1875,6 @@ static int ax88179_reset(struct usbnet *dev) /* Power up ethernet PHY */ *tmp16 = 0; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); - *tmp16 = AX_PHYPWR_RSTCTL_IPRL; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); msleep(200); @@ -1305,11 +1884,23 @@ static int ax88179_reset(struct usbnet *dev) msleep(100); /* Ethernet PHY Auto Detach*/ - ax88179_auto_detach(dev, 0); + ax88179_AutoDetach(dev, 0); - ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN, - dev->net->dev_addr); - memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN); + /* Set the MAC address */ + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, + ETH_ALEN, dev->net->dev_addr); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_dbg(dev->net, "MAC [%02x-%02x-%02x-%02x-%02x-%02x]\n", + dev->net->dev_addr[0], dev->net->dev_addr[1], + dev->net->dev_addr[2], dev->net->dev_addr[3], + dev->net->dev_addr[4], dev->net->dev_addr[5]); +#else + devdbg(dev, "MAC [%02x-%02x-%02x-%02x-%02x-%02x]\n", + dev->net->dev_addr[0], dev->net->dev_addr[1], + dev->net->dev_addr[2], dev->net->dev_addr[3], + dev->net->dev_addr[4], dev->net->dev_addr[5]); +#endif /* RX bulk configuration */ memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); @@ -1317,18 +1908,33 @@ static int ax88179_reset(struct usbnet *dev) dev->rx_urb_size = 1024 * 20; - *tmp = 0x34; + tmp[0] = 0x34; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp); - *tmp = 0x52; + tmp[0] = 0x52; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp); - dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_RXCSUM; - - dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_RXCSUM; + dev->net->features |= NETIF_F_IP_CSUM; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22) + dev->net->features |= NETIF_F_IPV6_CSUM; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (usb_device_no_sg_constraint(dev->udev)) + dev->can_dma_sg = 1; + dev->net->features |= NETIF_F_SG | NETIF_F_TSO; +#endif + +#if defined(CONFIG_MACH_ODROIDXU3) + /* Hack to set such features by default on driver is loaded */ + dev->net->features |= NETIF_F_SG | NETIF_F_TSO; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + dev->net->hw_features |= NETIF_F_IP_CSUM; + dev->net->hw_features |= NETIF_F_IPV6_CSUM; + dev->net->hw_features |= NETIF_F_SG | NETIF_F_TSO; +#endif /* Enable checksum offload */ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | @@ -1339,114 +1945,226 @@ static int ax88179_reset(struct usbnet *dev) AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp); + ax179_data->checksum |= AX_RX_CHECKSUM | AX_TX_CHECKSUM; + /* Configure RX control register => start operation */ - *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | - AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; + *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_START | AX_RX_CTL_AP | + AX_RX_CTL_AMALL | AX_RX_CTL_AB; + if (NET_IP_ALIGN == 0) + *tmp16 |= AX_RX_CTL_IPE; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16); *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | - AX_MONITOR_MODE_RWMP; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp); + AX_MONITOR_MODE_RWMP; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, tmp); + + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, tmp, 0); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_dbg(dev->net, "Monitor mode = 0x%02x\n", *tmp); +#else + devdbg(dev, "Monitor mode = 0x%02x\n", *tmp); +#endif /* Configure default medium type => giga */ - *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | - AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE | - AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE; + *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | + AX_MEDIUM_GIGAMODE; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16); ax88179_led_setting(dev); + ax88179_EEE_setting(dev); + + ax88179_Gether_setting(dev); + /* Restart autoneg */ mii_nway_restart(&dev->mii); - usbnet_link_change(dev, 0, 0); + netif_carrier_off(dev->net); + + kfree(buf); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + netdev_dbg(dev->net, "mtu %d\n", dev->net->mtu); +#else + devdbg(dev, "mtu %d\n", dev->net->mtu); +#endif return 0; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) static int ax88179_stop(struct usbnet *dev) { - u16 tmp16; + u16 tmp16 = 0; ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, - 2, 2, &tmp16); + 2, 2, &tmp16, 1); tmp16 &= ~AX_MEDIUM_RECEIVE_EN; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16); - return 0; } +#endif static const struct driver_info ax88179_info = { - .description = "ASIX AX88179 USB 3.0 Gigibit Ethernet", - .bind = ax88179_bind, - .unbind = ax88179_unbind, - .status = ax88179_status, + .description = "ASIX AX88179 USB 3.0 Gigabit Ethernet", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, - .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .reset = ax88179_reset, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS, +#else + .flags = FLAG_ETHER | FLAG_FRAMING_AX, +#endif .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; + static const struct driver_info ax88178a_info = { - .description = "ASIX AX88178A USB 2.0 Gigibit Ethernet", - .bind = ax88179_bind, - .unbind = ax88179_unbind, - .status = ax88179_status, + .description = "ASIX AX88178A USB 2.0 Gigabit Ethernet", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, - .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .reset = ax88179_reset, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS, +#else + .flags = FLAG_ETHER | FLAG_FRAMING_AX, +#endif .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; static const struct driver_info sitecom_info = { .description = "Sitecom USB 3.0 to Gigabit Adapter", - .bind = ax88179_bind, + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .status = ax88179_status, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS, +#else + .flags = FLAG_ETHER | FLAG_FRAMING_AX, +#endif + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info lenovo_info = { + .description = "ThinkPad OneLinkDock USB GigaLAN", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .status = ax88179_status, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS, +#else + .flags = FLAG_ETHER | FLAG_FRAMING_AX, +#endif + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info toshiba_info = { + .description = "Toshiba USB 3.0 to Gigabit LAN Adapter", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .status = ax88179_status, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS, +#else + .flags = FLAG_ETHER | FLAG_FRAMING_AX, +#endif + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info samsung_info = { + .description = "Samsung USB Ethernet Adapter", + .bind = ax88179_bind, .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, - .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .reset = ax88179_reset, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS, +#else + .flags = FLAG_ETHER | FLAG_FRAMING_AX, +#endif .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; -static const struct usb_device_id products[] = { +static const struct usb_device_id products[] = { { /* ASIX AX88179 10/100/1000 */ USB_DEVICE(0x0b95, 0x1790), - .driver_info = (unsigned long)&ax88179_info, + .driver_info = (unsigned long) &ax88179_info, }, { /* ASIX AX88178A 10/100/1000 */ USB_DEVICE(0x0b95, 0x178a), - .driver_info = (unsigned long)&ax88178a_info, + .driver_info = (unsigned long) &ax88178a_info, }, { /* Sitecom USB 3.0 to Gigabit Adapter */ USB_DEVICE(0x0df6, 0x0072), .driver_info = (unsigned long) &sitecom_info, +}, { + /* ThinkPad OneLinkDock USB GigaLAN */ + USB_DEVICE(0x17ef, 0x304b), + .driver_info = (unsigned long) &lenovo_info, +}, { + /* Toshiba USB3.0 to Gigabit LAN Adapter */ + USB_DEVICE(0x0930, 0x0a13), + .driver_info = (unsigned long) &toshiba_info, +}, { + /* Samsung USB Ethernet Adapter */ + USB_DEVICE(0x04e8, 0xa100), + .driver_info = (unsigned long) &samsung_info, }, - { }, + { }, /* END */ }; MODULE_DEVICE_TABLE(usb, products); -static struct usb_driver ax88179_178a_driver = { +static struct usb_driver asix_driver = { .name = "ax88179_178a", .id_table = products, .probe = usbnet_probe, .suspend = ax88179_suspend, .resume = ax88179_resume, .disconnect = usbnet_disconnect, - .supports_autosuspend = 1, - .disable_hub_initiated_lpm = 1, }; -module_usb_driver(ax88179_178a_driver); -MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices"); +static int __init asix_init(void) +{ + return usb_register(&asix_driver); +} +module_init(asix_init); + +static void __exit asix_exit(void) +{ + usb_deregister(&asix_driver); +} +module_exit(asix_exit); + +MODULE_AUTHOR("David Hollis"); +MODULE_DESCRIPTION("ASIX AX88179_178A based USB 2.0/3.0 Gigabit Ethernet Devices"); MODULE_LICENSE("GPL"); + diff --git a/drivers/net/usb/ax88179_178a.h b/drivers/net/usb/ax88179_178a.h new file mode 100755 index 00000000000000..cbe4e9fa6f303a --- /dev/null +++ b/drivers/net/usb/ax88179_178a.h @@ -0,0 +1,365 @@ +#ifndef __LINUX_USBNET_ASIX_H +#define __LINUX_USBNET_ASIX_H + +//#define RX_SKB_COPY + +#define AX88179_PHY_ID 0x03 +#define AX_MCAST_FILTER_SIZE 8 +#define AX_MAX_MCAST 64 +#define AX_EEPROM_LEN 0x40 +#define AX_RX_CHECKSUM 1 +#define AX_TX_CHECKSUM 2 + +#define AX_BULKIN_24K 0x18; /* 24k */ + +#define AX_ACCESS_MAC 0x01 +#define AX_ACCESS_PHY 0x02 +#define AX_ACCESS_WAKEUP 0x03 +#define AX_ACCESS_EEPROM 0x04 +#define AX_ACCESS_EFUSE 0x05 +#define AX_RELOAD_EEPROM_EFUSE 0x06 +#define AX_WRITE_EFUSE_EN 0x09 +#define AX_WRITE_EFUSE_DIS 0x0A +#define AX_ACCESS_MFAB 0x10 + +#define PHYSICAL_LINK_STATUS 0x02 + #define AX_USB_SS 0x04 + #define AX_USB_HS 0x02 + #define AX_USB_FS 0x01 + +#define GENERAL_STATUS 0x03 +/* Check AX88179 version. UA1:Bit2 = 0, UA2:Bit2 = 1 */ + #define AX_SECLD 0x04 + + + +#define AX_SROM_ADDR 0x07 +#define AX_SROM_CMD 0x0a + #define EEP_RD 0x04 /* EEprom read command */ + #define EEP_WR 0x08 /* EEprom write command */ + #define EEP_BUSY 0x10 /* EEprom access module busy */ + + +#define AX_SROM_DATA_LOW 0x08 +#define AX_SROM_DATA_HIGH 0x09 + +#define AX_RX_CTL 0x0b + #define AX_RX_CTL_DROPCRCERR 0x0100 /* Drop CRC error packet */ + #define AX_RX_CTL_IPE 0x0200 /* Enable IP header in receive buffer aligned on 32-bit aligment */ + #define AX_RX_CTL_TXPADCRC 0x0400 /* checksum value in rx header 3 */ + #define AX_RX_CTL_START 0x0080 /* Ethernet MAC start */ + #define AX_RX_CTL_AP 0x0020 /* Accept physcial address from Multicast array */ + #define AX_RX_CTL_AM 0x0010 /* Accetp Brocadcast frames*/ + #define AX_RX_CTL_AB 0x0008 /* HW auto-added 8-bytes data when meet USB bulk in transfer boundary (1024/512/64)*/ + #define AX_RX_CTL_HA8B 0x0004 + #define AX_RX_CTL_AMALL 0x0002 /* Accetp all multicast frames */ + #define AX_RX_CTL_PRO 0x0001 /* Promiscuous Mode */ + #define AX_RX_CTL_STOP 0x0000 /* Stop MAC */ + +#define AX_NODE_ID 0x10 +#define AX_MULTI_FILTER_ARRY 0x16 + +#define AX_MEDIUM_STATUS_MODE 0x22 + #define AX_MEDIUM_GIGAMODE 0x01 + #define AX_MEDIUM_FULL_DUPLEX 0x02 +// #define AX_MEDIUM_ALWAYS_ONE 0x04 + #define AX_MEDIUM_EN_125MHZ 0x08 + #define AX_MEDIUM_RXFLOW_CTRLEN 0x10 + #define AX_MEDIUM_TXFLOW_CTRLEN 0x20 + #define AX_MEDIUM_RECEIVE_EN 0x100 + #define AX_MEDIUM_PS 0x200 + #define AX_MEDIUM_JUMBO_EN 0x8040 + +#define AX_MONITOR_MODE 0x24 + #define AX_MONITOR_MODE_RWLC 0x02 + #define AX_MONITOR_MODE_RWMP 0x04 + #define AX_MONITOR_MODE_RWWF 0x08 + #define AX_MONITOR_MODE_RW_FLAG 0x10 + #define AX_MONITOR_MODE_PMEPOL 0x20 + #define AX_MONITOR_MODE_PMETYPE 0x40 + +#define AX_GPIO_CTRL 0x25 + #define AX_GPIO_CTRL_GPIO3EN 0x80 + #define AX_GPIO_CTRL_GPIO2EN 0x40 + #define AX_GPIO_CTRL_GPIO1EN 0x20 + +#define AX_PHYPWR_RSTCTL 0x26 + #define AX_PHYPWR_RSTCTL_BZ 0x0010 + #define AX_PHYPWR_RSTCTL_IPRL 0x0020 + #define AX_PHYPWR_RSTCTL_AUTODETACH 0x1000 + +#define AX_RX_BULKIN_QCTRL 0x2e + #define AX_RX_BULKIN_QCTRL_TIME 0x01 + #define AX_RX_BULKIN_QCTRL_IFG 0x02 + #define AX_RX_BULKIN_QCTRL_SIZE 0x04 + +#define AX_RX_BULKIN_QTIMR_LOW 0x2f +#define AX_RX_BULKIN_QTIMR_HIGH 0x30 +#define AX_RX_BULKIN_QSIZE 0x31 +#define AX_RX_BULKIN_QIFG 0x32 + +#define AX_CLK_SELECT 0x33 + #define AX_CLK_SELECT_BCS 0x01 + #define AX_CLK_SELECT_ACS 0x02 + #define AX_CLK_SELECT_ACSREQ 0x10 + #define AX_CLK_SELECT_ULR 0x08 + +#define AX_RXCOE_CTL 0x34 + #define AX_RXCOE_IP 0x01 + #define AX_RXCOE_TCP 0x02 + #define AX_RXCOE_UDP 0x04 + #define AX_RXCOE_ICMP 0x08 + #define AX_RXCOE_IGMP 0x10 + #define AX_RXCOE_TCPV6 0x20 + #define AX_RXCOE_UDPV6 0x40 + #define AX_RXCOE_ICMV6 0x80 + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22) + #define AX_RXCOE_DEF_CSUM (AX_RXCOE_IP | AX_RXCOE_TCP | \ + AX_RXCOE_UDP | AX_RXCOE_ICMV6 | \ + AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6) +#else + #define AX_RXCOE_DEF_CSUM (AX_RXCOE_IP | AX_RXCOE_TCP | \ + AX_RXCOE_UDP) +#endif + +#define AX_TXCOE_CTL 0x35 + #define AX_TXCOE_IP 0x01 + #define AX_TXCOE_TCP 0x02 + #define AX_TXCOE_UDP 0x04 + #define AX_TXCOE_ICMP 0x08 + #define AX_TXCOE_IGMP 0x10 + #define AX_TXCOE_TCPV6 0x20 + #define AX_TXCOE_UDPV6 0x40 + #define AX_TXCOE_ICMV6 0x80 +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22) + #define AX_TXCOE_DEF_CSUM (AX_TXCOE_TCP | AX_TXCOE_UDP | \ + AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6) +#else + #define AX_TXCOE_DEF_CSUM (AX_TXCOE_TCP | AX_TXCOE_UDP) +#endif + +#define AX_PAUSE_WATERLVL_HIGH 0x54 +#define AX_PAUSE_WATERLVL_LOW 0x55 + + +#define AX_EEP_EFUSE_CORRECT 0x00 +#define AX88179_EEPROM_MAGIC 0x17900b95 + + +/*****************************************************************************/ +/* GMII register definitions */ +#define GMII_PHY_CONTROL 0x00 /* control reg */ + /* Bit definitions: GMII Control */ + #define GMII_CONTROL_RESET 0x8000 /* reset bit in control reg */ + #define GMII_CONTROL_LOOPBACK 0x4000 /* loopback bit in control reg */ + #define GMII_CONTROL_10MB 0x0000 /* 10 Mbit */ + #define GMII_CONTROL_100MB 0x2000 /* 100Mbit */ + #define GMII_CONTROL_1000MB 0x0040 /* 1000Mbit */ + #define GMII_CONTROL_SPEED_BITS 0x2040 /* speed bit mask */ + #define GMII_CONTROL_ENABLE_AUTO 0x1000 /* autonegotiate enable */ + #define GMII_CONTROL_POWER_DOWN 0x0800 + #define GMII_CONTROL_ISOLATE 0x0400 /* islolate bit */ + #define GMII_CONTROL_START_AUTO 0x0200 /* restart autonegotiate */ + #define GMII_CONTROL_FULL_DUPLEX 0x0100 + +#define GMII_PHY_STATUS 0x01 /* status reg */ + /* Bit definitions: GMII Status */ + #define GMII_STATUS_100MB_MASK 0xE000 /* any of these indicate 100 Mbit */ + #define GMII_STATUS_10MB_MASK 0x1800 /* either of these indicate 10 Mbit */ + #define GMII_STATUS_AUTO_DONE 0x0020 /* auto negotiation complete */ + #define GMII_STATUS_AUTO 0x0008 /* auto negotiation is available */ + #define GMII_STATUS_LINK_UP 0x0004 /* link status bit */ + #define GMII_STATUS_EXTENDED 0x0001 /* extended regs exist */ + #define GMII_STATUS_100T4 0x8000 /* capable of 100BT4 */ + #define GMII_STATUS_100TXFD 0x4000 /* capable of 100BTX full duplex */ + #define GMII_STATUS_100TX 0x2000 /* capable of 100BTX */ + #define GMII_STATUS_10TFD 0x1000 /* capable of 10BT full duplex */ + #define GMII_STATUS_10T 0x0800 /* capable of 10BT */ + +#define GMII_PHY_OUI 0x02 /* most of the OUI bits */ +#define GMII_PHY_MODEL 0x03 /* model/rev bits, and rest of OUI */ +#define GMII_PHY_ANAR 0x04 /* AN advertisement reg */ + /* Bit definitions: Auto-Negotiation Advertisement */ + #define GMII_ANAR_ASYM_PAUSE 0x0800 /* support asymetric pause */ + #define GMII_ANAR_PAUSE 0x0400 /* support pause packets */ + #define GMII_ANAR_100T4 0x0200 /* support 100BT4 */ + #define GMII_ANAR_100TXFD 0x0100 /* support 100BTX full duplex */ + #define GMII_ANAR_100TX 0x0080 /* support 100BTX half duplex */ + #define GMII_ANAR_10TFD 0x0040 /* support 10BT full duplex */ + #define GMII_ANAR_10T 0x0020 /* support 10BT half duplex */ + #define GMII_SELECTOR_FIELD 0x001F /* selector field. */ + +#define GMII_PHY_ANLPAR 0x05 /* AN Link Partner */ + /* Bit definitions: Auto-Negotiation Link Partner Ability */ + #define GMII_ANLPAR_100T4 0x0200 /* support 100BT4 */ + #define GMII_ANLPAR_100TXFD 0x0100 /* support 100BTX full duplex */ + #define GMII_ANLPAR_100TX 0x0080 /* support 100BTX half duplex */ + #define GMII_ANLPAR_10TFD 0x0040 /* support 10BT full duplex */ + #define GMII_ANLPAR_10T 0x0020 /* support 10BT half duplex */ + #define GMII_ANLPAR_PAUSE 0x0400 /* support pause packets */ + #define GMII_ANLPAR_ASYM_PAUSE 0x0800 /* support asymetric pause */ + #define GMII_ANLPAR_ACK 0x4000 /* means LCB was successfully rx'd */ + #define GMII_SELECTOR_8023 0x0001; + +#define GMII_PHY_ANER 0x06 /* AN expansion reg */ +#define GMII_PHY_1000BT_CONTROL 0x09 /* control reg for 1000BT */ +#define GMII_PHY_1000BT_STATUS 0x0A /* status reg for 1000BT */ + +#define GMII_PHY_MACR 0x0D +#define GMII_PHY_MAADR 0x0E + +#define GMII_PHY_PHYSR 0x11 /* PHY specific status register */ + #define GMII_PHY_PHYSR_SMASK 0xc000 + #define GMII_PHY_PHYSR_GIGA 0x8000 + #define GMII_PHY_PHYSR_100 0x4000 + #define GMII_PHY_PHYSR_FULL 0x2000 + #define GMII_PHY_PHYSR_LINK 0x400 + +/* Bit definitions: 1000BaseT AUX Control */ +#define GMII_1000_AUX_CTRL_MASTER_SLAVE 0x1000 +#define GMII_1000_AUX_CTRL_FD_CAPABLE 0x0200 /* full duplex capable */ +#define GMII_1000_AUX_CTRL_HD_CAPABLE 0x0100 /* half duplex capable */ + +/* Bit definitions: 1000BaseT AUX Status */ +#define GMII_1000_AUX_STATUS_FD_CAPABLE 0x0800 /* full duplex capable */ +#define GMII_1000_AUX_STATUS_HD_CAPABLE 0x0400 /* half duplex capable */ + +/*Cicada MII Registers */ +#define GMII_AUX_CTRL_STATUS 0x1C +#define GMII_AUX_ANEG_CPLT 0x8000 +#define GMII_AUX_FDX 0x0020 +#define GMII_AUX_SPEED_1000 0x0010 +#define GMII_AUX_SPEED_100 0x0008 + +#define GMII_LED_ACTIVE 0x1a + #define GMII_LED_ACTIVE_MASK 0xff8f + #define GMII_LED0_ACTIVE (1 << 4) + #define GMII_LED1_ACTIVE (1 << 5) + #define GMII_LED2_ACTIVE (1 << 6) + +#define GMII_LED_LINK 0x1c + #define GMII_LED_LINK_MASK 0xf888 + #define GMII_LED0_LINK_10 (1 << 0) + #define GMII_LED0_LINK_100 (1 << 1) + #define GMII_LED0_LINK_1000 (1 << 2) + #define GMII_LED1_LINK_10 (1 << 4) + #define GMII_LED1_LINK_100 (1 << 5) + #define GMII_LED1_LINK_1000 (1 << 6) + #define GMII_LED2_LINK_10 (1 << 8) + #define GMII_LED2_LINK_100 (1 << 9) + #define GMII_LED2_LINK_1000 (1 << 10) + + #define LED_VALID (1 << 15) /* UA2 LED Setting */ + + #define LED0_ACTIVE (1 << 0) + #define LED0_LINK_10 (1 << 1) + #define LED0_LINK_100 (1 << 2) + #define LED0_LINK_1000 (1 << 3) + #define LED0_FD (1 << 4) + #define LED0_USB3_MASK 0x001f + + #define LED1_ACTIVE (1 << 5) + #define LED1_LINK_10 (1 << 6) + #define LED1_LINK_100 (1 << 7) + #define LED1_LINK_1000 (1 << 8) + #define LED1_FD (1 << 9) + #define LED1_USB3_MASK 0x03e0 + + #define LED2_ACTIVE (1 << 10) + #define LED2_LINK_1000 (1 << 13) + #define LED2_LINK_100 (1 << 12) + #define LED2_LINK_10 (1 << 11) + #define LED2_FD (1 << 14) + #define LED2_USB3_MASK 0x7c00 + +#define GMII_PHYPAGE 0x1e + +#define GMII_PHY_PAGE_SELECT 0x1f + #define GMII_PHY_PAGE_SELECT_EXT 0x0007 + #define GMII_PHY_PAGE_SELECT_PAGE0 0X0000 + #define GMII_PHY_PAGE_SELECT_PAGE1 0X0001 + #define GMII_PHY_PAGE_SELECT_PAGE2 0X0002 + #define GMII_PHY_PAGE_SELECT_PAGE3 0X0003 + #define GMII_PHY_PAGE_SELECT_PAGE4 0X0004 + #define GMII_PHY_PAGE_SELECT_PAGE5 0X0005 + #define GMII_PHY_PAGE_SELECT_PAGE6 0X0006 + +/******************************************************************************/ + +struct ax88179_data { + u16 rxctl; + u8 checksum; +} __attribute__ ((packed)); + +struct ax88179_async_handle { + struct usb_ctrlrequest *req; + u8 m_filter[8]; + u16 rxctl; +} __attribute__ ((packed)); + +struct ax88179_int_data { + __le16 res1; +#define AX_INT_PPLS_LINK (1 << 0) +#define AX_INT_SPLS_LINK (1 << 1) +#define AX_INT_CABOFF_UNPLUG (1 << 7) + u8 link; + __le16 res2; + u8 status; + __le16 res3; +} __attribute__ ((packed)); + +#define AX_RXHDR_L4_ERR (1 << 8) +#define AX_RXHDR_L3_ERR (1 << 9) + + +#define AX_RXHDR_L4_TYPE_ICMP 2 +#define AX_RXHDR_L4_TYPE_IGMP 3 +#define AX_RXHDR_L4_TYPE_TCMPV6 5 + +#define AX_RXHDR_L3_TYPE_IP 1 +#define AX_RXHDR_L3_TYPE_IPV6 2 + +#define AX_RXHDR_L4_TYPE_MASK 0x1c +#define AX_RXHDR_L4_TYPE_UDP 4 +#define AX_RXHDR_L4_TYPE_TCP 16 +#define AX_RXHDR_L3CSUM_ERR 2 +#define AX_RXHDR_L4CSUM_ERR 1 +#define AX_RXHDR_CRC_ERR 0x20000000 +#define AX_RXHDR_MII_ERR 0x40000000 +#define AX_RXHDR_DROP_ERR 0x80000000 +#if 0 +struct ax88179_rx_pkt_header { + + u8 l4_csum_err:1, + l3_csum_err:1, + l4_type:3, + l3_type:2, + ce:1; + + u8 vlan_ind:3, + rx_ok:1, + pri:3, + bmc:1; + + u16 len:13, + crc:1, + mii:1, + drop:1; + +} __attribute__ ((packed)); +#endif +static struct {unsigned char ctrl, timer_l, timer_h, size, ifg; } +AX88179_BULKIN_SIZE[] = { + {7, 0x4f, 0, 0x12, 0xff}, + {7, 0x20, 3, 0x16, 0xff}, + {7, 0xae, 7, 0x18, 0xff}, + {7, 0xcc, 0x4c, 0x18, 8}, +}; + +static int ax88179_reset(struct usbnet *dev); +static int ax88179_link_reset(struct usbnet *dev); +static int ax88179_AutoDetach(struct usbnet *dev, int in_pm); + +#endif /* __LINUX_USBNET_ASIX_H */ +