diff options
Diffstat (limited to 'drivers/net/usb/asix_devices.c')
| -rw-r--r-- | drivers/net/usb/asix_devices.c | 915 |
1 files changed, 724 insertions, 191 deletions
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index ad5d1e4384db..232bbd79a4de 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * ASIX AX8817X based USB 2.0 Ethernet Devices * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com> * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> * Copyright (C) 2006 James Painter <jamie.painter@iname.com> * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 */ #include "asix.h" @@ -36,6 +23,15 @@ #define PHY_MODE_RTL8211CL 0x000C +#define AX88772A_PHY14H 0x14 +#define AX88772A_PHY14H_DEFAULT 0x442C + +#define AX88772A_PHY15H 0x15 +#define AX88772A_PHY15H_DEFAULT 0x03C8 + +#define AX88772A_PHY16H 0x16 +#define AX88772A_PHY16H_DEFAULT 0x4044 + struct ax88172_int_data { __le16 res1; u8 link; @@ -63,7 +59,7 @@ static void asix_status(struct usbnet *dev, struct urb *urb) static void asix_set_netdev_dev_addr(struct usbnet *dev, u8 *addr) { if (is_valid_ether_addr(addr)) { - memcpy(dev->net->dev_addr, addr, ETH_ALEN); + eth_hw_addr_set(dev->net, addr); } else { netdev_info(dev->net, "invalid hw address, using random\n"); eth_hw_addr_random(dev->net); @@ -80,6 +76,8 @@ static u32 asix_get_phyid(struct usbnet *dev) /* Poll for the rare case the FW or phy isn't ready yet. */ for (i = 0; i < 100; i++) { phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1); + if (phy_reg < 0) + return 0; if (phy_reg != 0 && phy_reg != 0xFFFF) break; mdelay(1); @@ -126,9 +124,9 @@ static const struct ethtool_ops ax88172_ethtool_ops = { .get_eeprom_len = asix_get_eeprom_len, .get_eeprom = asix_get_eeprom, .set_eeprom = asix_set_eeprom, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = usbnet_get_link_ksettings_mii, + .set_link_ksettings = usbnet_set_link_ksettings_mii, }; static void ax88172_set_multicast(struct net_device *net) @@ -185,7 +183,7 @@ static int ax88172_link_reset(struct usbnet *dev) netdev_dbg(dev->net, "ax88172_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", ethtool_cmd_speed(&ecmd), ecmd.duplex, mode); - asix_write_medium_mode(dev, mode); + asix_write_medium_mode(dev, mode, 0); return 0; } @@ -196,36 +194,62 @@ static const struct net_device_ops ax88172_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = dev_get_tstats64, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, - .ndo_do_ioctl = asix_ioctl, + .ndo_eth_ioctl = asix_ioctl, .ndo_set_rx_mode = ax88172_set_multicast, }; +static void asix_phy_reset(struct usbnet *dev, unsigned int reset_bits) +{ + unsigned int timeout = 5000; + + asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, reset_bits); + + /* give phy_id a chance to process reset */ + udelay(500); + + /* See IEEE 802.3 "22.2.4.1.1 Reset": 500ms max */ + while (timeout--) { + if (asix_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR) + & BMCR_RESET) + udelay(100); + else + return; + } + + netdev_err(dev->net, "BMCR_RESET timeout on phy_id %d\n", + dev->mii.phy_id); +} + static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf) { int ret = 0; - u8 buf[ETH_ALEN]; + u8 buf[ETH_ALEN] = {0}; int i; unsigned long gpio_bits = dev->driver_info->data; - usbnet_get_endpoints(dev,intf); + ret = usbnet_get_endpoints(dev, intf); + if (ret) + goto out; /* Toggle the GPIOs in a manufacturer/model specific way */ for (i = 2; i >= 0; i--) { ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, - (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL); + (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL, 0); if (ret < 0) goto out; msleep(5); } - ret = asix_write_rx_ctl(dev, 0x80); + ret = asix_write_rx_ctl(dev, 0x80, 0); if (ret < 0) goto out; /* Get the MAC address */ - ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf); + ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID, + 0, 0, ETH_ALEN, buf, 0); if (ret < 0) { netdev_dbg(dev->net, "read AX_CMD_READ_NODE_ID failed: %d\n", ret); @@ -240,14 +264,17 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf) dev->mii.mdio_write = asix_mdio_write; dev->mii.phy_id_mask = 0x3f; dev->mii.reg_num_mask = 0x1f; - dev->mii.phy_id = asix_get_phy_addr(dev); + + dev->mii.phy_id = asix_read_phy_addr(dev, true); + if (dev->mii.phy_id < 0) + return dev->mii.phy_id; dev->net->netdev_ops = &ax88172_netdev_ops; dev->net->ethtool_ops = &ax88172_ethtool_ops; dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ - asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); + asix_phy_reset(dev, BMCR_RESET); asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); mii_nway_restart(&dev->mii); @@ -258,9 +285,47 @@ out: return ret; } +static void ax88772_ethtool_get_strings(struct net_device *netdev, u32 sset, + u8 *data) +{ + switch (sset) { + case ETH_SS_TEST: + net_selftest_get_strings(data); + break; + } +} + +static int ax88772_ethtool_get_sset_count(struct net_device *ndev, int sset) +{ + switch (sset) { + case ETH_SS_TEST: + return net_selftest_get_count(); + default: + return -EOPNOTSUPP; + } +} + +static void ax88772_ethtool_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct usbnet *dev = netdev_priv(ndev); + struct asix_common_private *priv = dev->driver_priv; + + phylink_ethtool_get_pauseparam(priv->phylink, pause); +} + +static int ax88772_ethtool_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct usbnet *dev = netdev_priv(ndev); + struct asix_common_private *priv = dev->driver_priv; + + return phylink_ethtool_set_pauseparam(priv->phylink, pause); +} + static const struct ethtool_ops ax88772_ethtool_ops = { .get_drvinfo = asix_get_drvinfo, - .get_link = asix_get_link, + .get_link = usbnet_get_link, .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, .get_wol = asix_get_wol, @@ -268,109 +333,226 @@ static const struct ethtool_ops ax88772_ethtool_ops = { .get_eeprom_len = asix_get_eeprom_len, .get_eeprom = asix_get_eeprom, .set_eeprom = asix_set_eeprom, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, - .nway_reset = usbnet_nway_reset, + .nway_reset = phy_ethtool_nway_reset, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .self_test = net_selftest, + .get_strings = ax88772_ethtool_get_strings, + .get_sset_count = ax88772_ethtool_get_sset_count, + .get_pauseparam = ax88772_ethtool_get_pauseparam, + .set_pauseparam = ax88772_ethtool_set_pauseparam, }; -static int ax88772_link_reset(struct usbnet *dev) +static int ax88772_reset(struct usbnet *dev) { - u16 mode; - struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; - - mii_check_media(&dev->mii, 1, 1); - mii_ethtool_gset(&dev->mii, &ecmd); - mode = AX88772_MEDIUM_DEFAULT; + struct asix_data *data = (struct asix_data *)&dev->data; + struct asix_common_private *priv = dev->driver_priv; + int ret; - if (ethtool_cmd_speed(&ecmd) != SPEED_100) - mode &= ~AX_MEDIUM_PS; + /* Rewrite MAC address */ + ether_addr_copy(data->mac_addr, dev->net->dev_addr); + ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, + ETH_ALEN, data->mac_addr, 0); + if (ret < 0) + goto out; - if (ecmd.duplex != DUPLEX_FULL) - mode &= ~AX_MEDIUM_FD; + /* Set RX_CTL to default values with 2k buffer, and enable cactus */ + ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0); + if (ret < 0) + goto out; - netdev_dbg(dev->net, "ax88772_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", - ethtool_cmd_speed(&ecmd), ecmd.duplex, mode); + ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, 0); + if (ret < 0) + goto out; - asix_write_medium_mode(dev, mode); + phylink_start(priv->phylink); return 0; + +out: + return ret; } -static int ax88772_reset(struct usbnet *dev) +static int ax88772_hw_reset(struct usbnet *dev, int in_pm) { struct asix_data *data = (struct asix_data *)&dev->data; - int ret, embd_phy; + struct asix_common_private *priv = dev->driver_priv; u16 rx_ctl; + int ret; - ret = asix_write_gpio(dev, - AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5); + ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 | + AX_GPIO_GPO2EN, 5, in_pm); if (ret < 0) goto out; - embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); - - ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); + ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, priv->embd_phy, + 0, 0, NULL, in_pm); if (ret < 0) { netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); goto out; } - ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL); - if (ret < 0) - goto out; + if (priv->embd_phy) { + ret = asix_sw_reset(dev, AX_SWRESET_IPPD, in_pm); + if (ret < 0) + goto out; - msleep(150); + usleep_range(10000, 11000); - ret = asix_sw_reset(dev, AX_SWRESET_CLEAR); - if (ret < 0) - goto out; + ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm); + if (ret < 0) + goto out; - msleep(150); + msleep(60); - if (embd_phy) { - ret = asix_sw_reset(dev, AX_SWRESET_IPRL); + ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL, + in_pm); if (ret < 0) goto out; } else { - ret = asix_sw_reset(dev, AX_SWRESET_PRTE); + ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL, + in_pm); if (ret < 0) goto out; } msleep(150); - rx_ctl = asix_read_rx_ctl(dev); - netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); - ret = asix_write_rx_ctl(dev, 0x0000); + + if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id, + MII_PHYSID1))){ + ret = -EIO; + goto out; + } + + ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm); + if (ret < 0) + goto out; + + ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm); + if (ret < 0) + goto out; + + ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0, + AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, + AX88772_IPG2_DEFAULT, 0, NULL, in_pm); + if (ret < 0) { + netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret); + goto out; + } + + /* Rewrite MAC address */ + ether_addr_copy(data->mac_addr, dev->net->dev_addr); + ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, + ETH_ALEN, data->mac_addr, in_pm); if (ret < 0) goto out; - rx_ctl = asix_read_rx_ctl(dev); - netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); + /* Set RX_CTL to default values with 2k buffer, and enable cactus */ + ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm); + if (ret < 0) + goto out; + + rx_ctl = asix_read_rx_ctl(dev, in_pm); + netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n", + rx_ctl); + + rx_ctl = asix_read_medium_status(dev, in_pm); + netdev_dbg(dev->net, + "Medium Status is 0x%04x after all initializations\n", + rx_ctl); + + return 0; + +out: + return ret; +} - ret = asix_sw_reset(dev, AX_SWRESET_PRL); +static int ax88772a_hw_reset(struct usbnet *dev, int in_pm) +{ + struct asix_data *data = (struct asix_data *)&dev->data; + struct asix_common_private *priv = dev->driver_priv; + u16 rx_ctl, phy14h, phy15h, phy16h; + int ret; + + ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm); if (ret < 0) goto out; - msleep(150); + ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, priv->embd_phy | + AX_PHYSEL_SSEN, 0, 0, NULL, in_pm); + if (ret < 0) { + netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); + goto out; + } + usleep_range(10000, 11000); - ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL); + ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_IPRL, in_pm); if (ret < 0) goto out; - msleep(150); + usleep_range(10000, 11000); - asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); - asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, - ADVERTISE_ALL | ADVERTISE_CSMA); - mii_nway_restart(&dev->mii); + ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm); + if (ret < 0) + goto out; + + msleep(160); - ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT); + ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm); if (ret < 0) goto out; + ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm); + if (ret < 0) + goto out; + + msleep(200); + + if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id, + MII_PHYSID1))) { + ret = -1; + goto out; + } + + if (priv->chipcode == AX_AX88772B_CHIPCODE) { + ret = asix_write_cmd(dev, AX_QCTCTRL, 0x8000, 0x8001, + 0, NULL, in_pm); + if (ret < 0) { + netdev_dbg(dev->net, "Write BQ setting failed: %d\n", + ret); + goto out; + } + } else if (priv->chipcode == AX_AX88772A_CHIPCODE) { + /* Check if the PHY registers have default settings */ + phy14h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id, + AX88772A_PHY14H); + phy15h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id, + AX88772A_PHY15H); + phy16h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id, + AX88772A_PHY16H); + + netdev_dbg(dev->net, + "772a_hw_reset: MR20=0x%x MR21=0x%x MR22=0x%x\n", + phy14h, phy15h, phy16h); + + /* Restore PHY registers default setting if not */ + if (phy14h != AX88772A_PHY14H_DEFAULT) + asix_mdio_write_nopm(dev->net, dev->mii.phy_id, + AX88772A_PHY14H, + AX88772A_PHY14H_DEFAULT); + if (phy15h != AX88772A_PHY15H_DEFAULT) + asix_mdio_write_nopm(dev->net, dev->mii.phy_id, + AX88772A_PHY15H, + AX88772A_PHY15H_DEFAULT); + if (phy16h != AX88772A_PHY16H_DEFAULT) + asix_mdio_write_nopm(dev->net, dev->mii.phy_id, + AX88772A_PHY16H, + AX88772A_PHY16H_DEFAULT); + } + ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0, AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, - AX88772_IPG2_DEFAULT, 0, NULL); + AX88772_IPG2_DEFAULT, 0, NULL, in_pm); if (ret < 0) { netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret); goto out; @@ -379,20 +561,29 @@ static int ax88772_reset(struct usbnet *dev) /* Rewrite MAC address */ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, - data->mac_addr); + data->mac_addr, in_pm); if (ret < 0) goto out; /* Set RX_CTL to default values with 2k buffer, and enable cactus */ - ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL); + ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm); if (ret < 0) goto out; - rx_ctl = asix_read_rx_ctl(dev); + ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm); + if (ret < 0) + return ret; + + /* Set RX_CTL to default values with 2k buffer, and enable cactus */ + ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm); + if (ret < 0) + goto out; + + rx_ctl = asix_read_rx_ctl(dev, in_pm); netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n", rx_ctl); - rx_ctl = asix_read_medium_status(dev); + rx_ctl = asix_read_medium_status(dev, in_pm); netdev_dbg(dev->net, "Medium Status is 0x%04x after all initializations\n", rx_ctl); @@ -401,7 +592,6 @@ static int ax88772_reset(struct usbnet *dev) out: return ret; - } static const struct net_device_ops ax88772_netdev_ops = { @@ -410,79 +600,321 @@ static const struct net_device_ops ax88772_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = dev_get_tstats64, .ndo_set_mac_address = asix_set_mac_address, .ndo_validate_addr = eth_validate_addr, - .ndo_do_ioctl = asix_ioctl, + .ndo_eth_ioctl = phy_do_ioctl_running, .ndo_set_rx_mode = asix_set_multicast, }; -static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) +static void ax88772_suspend(struct usbnet *dev) { - int ret, embd_phy, i; - u8 buf[ETH_ALEN]; - u32 phyid; + struct asix_common_private *priv = dev->driver_priv; + u16 medium; - usbnet_get_endpoints(dev,intf); + if (netif_running(dev->net)) { + rtnl_lock(); + phylink_suspend(priv->phylink, false); + rtnl_unlock(); + } - /* Get the MAC address */ - if (dev->driver_info->data & FLAG_EEPROM_MAC) { - for (i = 0; i < (ETH_ALEN >> 1); i++) { - ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x04 + i, - 0, 2, buf + i * 2); - if (ret < 0) - break; - } - } else { - ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, - 0, 0, ETH_ALEN, buf); + /* Stop MAC operation */ + medium = asix_read_medium_status(dev, 1); + medium &= ~AX_MEDIUM_RE; + asix_write_medium_mode(dev, medium, 1); + + netdev_dbg(dev->net, "ax88772_suspend: medium=0x%04x\n", + asix_read_medium_status(dev, 1)); +} + +/* Notes on PM callbacks and locking context: + * + * - asix_suspend()/asix_resume() are invoked for both runtime PM and + * system-wide suspend/resume. For struct usb_driver the ->resume() + * callback does not receive pm_message_t, so the resume type cannot + * be distinguished here. + * + * - The MAC driver must hold RTNL when calling phylink interfaces such as + * phylink_suspend()/resume(). Those calls will also perform MDIO I/O. + * + * - Taking RTNL and doing MDIO from a runtime-PM resume callback (while + * the USB PM lock is held) is fragile. Since autosuspend brings no + * measurable power saving here, we block it by holding a PM usage + * reference in ax88772_bind(). + */ +static int asix_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usbnet *dev = usb_get_intfdata(intf); + struct asix_common_private *priv = dev->driver_priv; + + if (priv && priv->suspend) + priv->suspend(dev); + + return usbnet_suspend(intf, message); +} + +static void ax88772_resume(struct usbnet *dev) +{ + struct asix_common_private *priv = dev->driver_priv; + int i; + + for (i = 0; i < 3; i++) + if (!priv->reset(dev, 1)) + break; + + if (netif_running(dev->net)) { + rtnl_lock(); + phylink_resume(priv->phylink); + rtnl_unlock(); } +} - if (ret < 0) { - netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret); +static int asix_resume(struct usb_interface *intf) +{ + struct usbnet *dev = usb_get_intfdata(intf); + struct asix_common_private *priv = dev->driver_priv; + + if (priv && priv->resume) + priv->resume(dev); + + return usbnet_resume(intf); +} + +static int ax88772_init_mdio(struct usbnet *dev) +{ + struct asix_common_private *priv = dev->driver_priv; + int ret; + + priv->mdio = mdiobus_alloc(); + if (!priv->mdio) + return -ENOMEM; + + priv->mdio->priv = dev; + priv->mdio->read = &asix_mdio_bus_read; + priv->mdio->write = &asix_mdio_bus_write; + priv->mdio->name = "Asix MDIO Bus"; + priv->mdio->phy_mask = ~(BIT(priv->phy_addr & 0x1f) | BIT(AX_EMBD_PHY_ADDR)); + /* mii bus name is usb-<usb bus number>-<usb device number> */ + snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d", + dev->udev->bus->busnum, dev->udev->devnum); + + ret = mdiobus_register(priv->mdio); + if (ret) { + netdev_err(dev->net, "Could not register MDIO bus (err %d)\n", ret); + mdiobus_free(priv->mdio); + priv->mdio = NULL; + } + + return ret; +} + +static void ax88772_mdio_unregister(struct asix_common_private *priv) +{ + mdiobus_unregister(priv->mdio); + mdiobus_free(priv->mdio); +} + +static int ax88772_init_phy(struct usbnet *dev) +{ + struct asix_common_private *priv = dev->driver_priv; + int ret; + + priv->phydev = mdiobus_get_phy(priv->mdio, priv->phy_addr); + if (!priv->phydev) { + netdev_err(dev->net, "Could not find PHY\n"); + return -ENODEV; + } + + ret = phylink_connect_phy(priv->phylink, priv->phydev); + if (ret) { + netdev_err(dev->net, "Could not connect PHY\n"); return ret; } - asix_set_netdev_dev_addr(dev, buf); + phy_suspend(priv->phydev); + priv->phydev->mac_managed_pm = true; - /* Initialize MII structure */ - dev->mii.dev = dev->net; - dev->mii.mdio_read = asix_mdio_read; - dev->mii.mdio_write = asix_mdio_write; - dev->mii.phy_id_mask = 0x1f; - dev->mii.reg_num_mask = 0x1f; - dev->mii.phy_id = asix_get_phy_addr(dev); + phy_attached_info(priv->phydev); - dev->net->netdev_ops = &ax88772_netdev_ops; - dev->net->ethtool_ops = &ax88772_ethtool_ops; - dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ - dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ + if (priv->embd_phy) + return 0; + + /* In case main PHY is not the embedded PHY and MAC is RMII clock + * provider, we need to suspend embedded PHY by keeping PLL enabled + * (AX_SWRESET_IPPD == 0). + */ + priv->phydev_int = mdiobus_get_phy(priv->mdio, AX_EMBD_PHY_ADDR); + if (!priv->phydev_int) { + rtnl_lock(); + phylink_disconnect_phy(priv->phylink); + rtnl_unlock(); + netdev_err(dev->net, "Could not find internal PHY\n"); + return -ENODEV; + } - embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); + priv->phydev_int->mac_managed_pm = true; + phy_suspend(priv->phydev_int); - /* Reset the PHY to normal operation mode */ - ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); - if (ret < 0) { - netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); + return 0; +} + +static void ax88772_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + /* Nothing to do */ +} + +static void ax88772_mac_link_down(struct phylink_config *config, + unsigned int mode, phy_interface_t interface) +{ + struct usbnet *dev = netdev_priv(to_net_dev(config->dev)); + + asix_write_medium_mode(dev, 0, 0); +} + +static void ax88772_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct usbnet *dev = netdev_priv(to_net_dev(config->dev)); + u16 m = AX_MEDIUM_AC | AX_MEDIUM_RE; + + m |= duplex ? AX_MEDIUM_FD : 0; + + switch (speed) { + case SPEED_100: + m |= AX_MEDIUM_PS; + break; + case SPEED_10: + break; + default: + return; + } + + if (tx_pause) + m |= AX_MEDIUM_TFC; + + if (rx_pause) + m |= AX_MEDIUM_RFC; + + asix_write_medium_mode(dev, m, 0); +} + +static const struct phylink_mac_ops ax88772_phylink_mac_ops = { + .mac_config = ax88772_mac_config, + .mac_link_down = ax88772_mac_link_down, + .mac_link_up = ax88772_mac_link_up, +}; + +static int ax88772_phylink_setup(struct usbnet *dev) +{ + struct asix_common_private *priv = dev->driver_priv; + phy_interface_t phy_if_mode; + struct phylink *phylink; + + priv->phylink_config.dev = &dev->net->dev; + priv->phylink_config.type = PHYLINK_NETDEV; + priv->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | + MAC_10 | MAC_100; + + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + priv->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RMII, + priv->phylink_config.supported_interfaces); + + if (priv->embd_phy) + phy_if_mode = PHY_INTERFACE_MODE_INTERNAL; + else + phy_if_mode = PHY_INTERFACE_MODE_RMII; + + phylink = phylink_create(&priv->phylink_config, dev->net->dev.fwnode, + phy_if_mode, &ax88772_phylink_mac_ops); + if (IS_ERR(phylink)) + return PTR_ERR(phylink); + + priv->phylink = phylink; + return 0; +} + +static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) +{ + struct asix_common_private *priv; + u8 buf[ETH_ALEN] = {0}; + int ret, i; + + priv = devm_kzalloc(&dev->udev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev->driver_priv = priv; + + ret = usbnet_get_endpoints(dev, intf); + if (ret) return ret; + + /* Maybe the boot loader passed the MAC address via device tree */ + if (!eth_platform_get_mac_address(&dev->udev->dev, buf)) { + netif_dbg(dev, ifup, dev->net, + "MAC address read from device tree"); + } else { + /* Try getting the MAC address from EEPROM */ + if (dev->driver_info->data & FLAG_EEPROM_MAC) { + for (i = 0; i < (ETH_ALEN >> 1); i++) { + ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, + 0x04 + i, 0, 2, buf + i * 2, + 0); + if (ret < 0) + break; + } + } else { + ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, + 0, 0, ETH_ALEN, buf, 0); + } + + if (ret < 0) { + netdev_dbg(dev->net, "Failed to read MAC address: %d\n", + ret); + return ret; + } } - ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL); + asix_set_netdev_dev_addr(dev, buf); + + dev->net->netdev_ops = &ax88772_netdev_ops; + dev->net->ethtool_ops = &ax88772_ethtool_ops; + dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ + dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ + + ret = asix_read_phy_addr(dev, true); if (ret < 0) return ret; - msleep(150); + priv->phy_addr = ret; + priv->embd_phy = ((priv->phy_addr & 0x1f) == AX_EMBD_PHY_ADDR); - ret = asix_sw_reset(dev, AX_SWRESET_CLEAR); - if (ret < 0) + ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, + &priv->chipcode, 0); + if (ret < 0) { + netdev_dbg(dev->net, "Failed to read STATMNGSTS_REG: %d\n", ret); return ret; + } - msleep(150); + priv->chipcode &= AX_CHIPCODE_MASK; - ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_PRTE); + priv->resume = ax88772_resume; + priv->suspend = ax88772_suspend; + if (priv->chipcode == AX_AX88772_CHIPCODE) + priv->reset = ax88772_hw_reset; + else + priv->reset = ax88772a_hw_reset; - /* Read PHYID register *AFTER* the PHY was reset properly */ - phyid = asix_get_phyid(dev); - netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid); + ret = priv->reset(dev, 0); + if (ret < 0) { + netdev_dbg(dev->net, "Failed to reset AX88772: %d\n", ret); + return ret; + } /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */ if (dev->driver_info->flags & FLAG_FRAMING_AX) { @@ -491,17 +923,65 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) dev->rx_urb_size = 2048; } - dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL); - if (!dev->driver_priv) - return -ENOMEM; + priv->presvd_phy_bmcr = 0; + priv->presvd_phy_advertise = 0; + + ret = ax88772_init_mdio(dev); + if (ret) + goto mdio_err; + + ret = ax88772_phylink_setup(dev); + if (ret) + goto phylink_err; + + ret = ax88772_init_phy(dev); + if (ret) + goto initphy_err; + + /* Keep this interface runtime-PM active by taking a usage ref. + * Prevents runtime suspend while bound and avoids resume paths + * that could deadlock (autoresume under RTNL while USB PM lock + * is held, phylink/MDIO wants RTNL). + */ + pm_runtime_get_noresume(&intf->dev); + + return 0; + +initphy_err: + phylink_destroy(priv->phylink); +phylink_err: + ax88772_mdio_unregister(priv); +mdio_err: + return ret; +} + +static int ax88772_stop(struct usbnet *dev) +{ + struct asix_common_private *priv = dev->driver_priv; + + phylink_stop(priv->phylink); return 0; } static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf) { - if (dev->driver_priv) - kfree(dev->driver_priv); + struct asix_common_private *priv = dev->driver_priv; + + rtnl_lock(); + phylink_disconnect_phy(priv->phylink); + rtnl_unlock(); + phylink_destroy(priv->phylink); + ax88772_mdio_unregister(priv); + asix_rx_fixup_common_free(dev->driver_priv); + /* Drop the PM usage ref taken in bind() */ + pm_runtime_put(&intf->dev); +} + +static void ax88178_unbind(struct usbnet *dev, struct usb_interface *intf) +{ + asix_rx_fixup_common_free(dev->driver_priv); + kfree(dev->driver_priv); } static const struct ethtool_ops ax88178_ethtool_ops = { @@ -514,9 +994,9 @@ static const struct ethtool_ops ax88178_ethtool_ops = { .get_eeprom_len = asix_get_eeprom_len, .get_eeprom = asix_get_eeprom, .set_eeprom = asix_set_eeprom, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = usbnet_get_link_ksettings_mii, + .set_link_ksettings = usbnet_set_link_ksettings_mii, }; static int marvell_phy_init(struct usbnet *dev) @@ -545,7 +1025,6 @@ static int marvell_phy_init(struct usbnet *dev) reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_MARVELL_LED_CTRL); netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (2) = 0x%04x\n", reg); - reg &= 0xfc0f; } return 0; @@ -607,12 +1086,22 @@ static int ax88178_reset(struct usbnet *dev) int gpio0 = 0; u32 phyid; - asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status); + ret = asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status, 0); + if (ret < 0) { + netdev_dbg(dev->net, "Failed to read GPIOS: %d\n", ret); + return ret; + } + netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status); - asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL); - asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom); - asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL); + asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL, 0); + ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom, 0); + if (ret < 0) { + netdev_dbg(dev->net, "Failed to read EEPROM: %d\n", ret); + return ret; + } + + asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL, 0); netdev_dbg(dev->net, "EEPROM index 0x17 is 0x%04x\n", eeprom); @@ -628,15 +1117,16 @@ static int ax88178_reset(struct usbnet *dev) netdev_dbg(dev->net, "GPIO0: %d, PhyMode: %d\n", gpio0, data->phymode); /* Power up external GigaPHY through AX88178 GPIO pin */ - asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40); + asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | + AX_GPIO_GPO1EN, 40, 0); if ((le16_to_cpu(eeprom) >> 8) != 1) { - asix_write_gpio(dev, 0x003c, 30); - asix_write_gpio(dev, 0x001c, 300); - asix_write_gpio(dev, 0x003c, 30); + asix_write_gpio(dev, 0x003c, 30, 0); + asix_write_gpio(dev, 0x001c, 300, 0); + asix_write_gpio(dev, 0x003c, 30, 0); } else { netdev_dbg(dev->net, "gpio phymode == 1 path\n"); - asix_write_gpio(dev, AX_GPIO_GPO1EN, 30); - asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30); + asix_write_gpio(dev, AX_GPIO_GPO1EN, 30, 0); + asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30, 0); } /* Read PHYID register *AFTER* powering up PHY */ @@ -644,15 +1134,15 @@ static int ax88178_reset(struct usbnet *dev) netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid); /* Set AX88178 to enable MII/GMII/RGMII interface for external PHY */ - asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL); + asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL, 0); - asix_sw_reset(dev, 0); + asix_sw_reset(dev, 0, 0); msleep(150); - asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD); + asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0); msleep(150); - asix_write_rx_ctl(dev, 0); + asix_write_rx_ctl(dev, 0, 0); if (data->phymode == PHY_MODE_MARVELL) { marvell_phy_init(dev); @@ -660,27 +1150,23 @@ static int ax88178_reset(struct usbnet *dev) } else if (data->phymode == PHY_MODE_RTL8211CL) rtl8211cl_phy_init(dev); - asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, - BMCR_RESET | BMCR_ANENABLE); + asix_phy_reset(dev, BMCR_RESET | BMCR_ANENABLE); asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000, ADVERTISE_1000FULL); + asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT, 0); mii_nway_restart(&dev->mii); - ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT); - if (ret < 0) - return ret; - /* Rewrite MAC address */ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, - data->mac_addr); + data->mac_addr, 0); if (ret < 0) return ret; - ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL); + ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0); if (ret < 0) return ret; @@ -718,7 +1204,7 @@ static int ax88178_link_reset(struct usbnet *dev) netdev_dbg(dev->net, "ax88178_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", speed, ecmd.duplex, mode); - asix_write_medium_mode(dev, mode); + asix_write_medium_mode(dev, mode, 0); if (data->phymode == PHY_MODE_MARVELL && data->ledmode) marvell_led_status(dev, speed); @@ -747,15 +1233,15 @@ static void ax88178_set_mfb(struct usbnet *dev) mfb = AX_RX_CTL_MFB_16384; } - rxctl = asix_read_rx_ctl(dev); - asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb); + rxctl = asix_read_rx_ctl(dev, 0); + asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb, 0); - medium = asix_read_medium_status(dev); + medium = asix_read_medium_status(dev, 0); if (dev->net->mtu > 1500) medium |= AX_MEDIUM_JFE; else medium &= ~AX_MEDIUM_JFE; - asix_write_medium_mode(dev, medium); + asix_write_medium_mode(dev, medium, 0); if (dev->rx_urb_size > old_rx_urb_size) usbnet_unlink_rx_urbs(dev); @@ -768,16 +1254,16 @@ static int ax88178_change_mtu(struct net_device *net, int new_mtu) netdev_dbg(dev->net, "ax88178_change_mtu() new_mtu=%d\n", new_mtu); - if (new_mtu <= 0 || ll_mtu > 16384) - return -EINVAL; - if ((ll_mtu % dev->maxpacket) == 0) return -EDOM; - net->mtu = new_mtu; + WRITE_ONCE(net->mtu, new_mtu); dev->hard_mtu = net->mtu + net->hard_header_len; ax88178_set_mfb(dev); + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + return 0; } @@ -786,22 +1272,25 @@ static const struct net_device_ops ax88178_netdev_ops = { .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_get_stats64 = dev_get_tstats64, .ndo_set_mac_address = asix_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = asix_set_multicast, - .ndo_do_ioctl = asix_ioctl, + .ndo_eth_ioctl = asix_ioctl, .ndo_change_mtu = ax88178_change_mtu, }; static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf) { int ret; - u8 buf[ETH_ALEN]; + u8 buf[ETH_ALEN] = {0}; - usbnet_get_endpoints(dev,intf); + ret = usbnet_get_endpoints(dev, intf); + if (ret) + return ret; /* Get the MAC address */ - ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf); + ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0); if (ret < 0) { netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret); return ret; @@ -816,16 +1305,20 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf) dev->mii.phy_id_mask = 0x1f; dev->mii.reg_num_mask = 0xff; dev->mii.supports_gmii = 1; - dev->mii.phy_id = asix_get_phy_addr(dev); + + dev->mii.phy_id = asix_read_phy_addr(dev, true); + if (dev->mii.phy_id < 0) + return dev->mii.phy_id; dev->net->netdev_ops = &ax88178_netdev_ops; dev->net->ethtool_ops = &ax88178_ethtool_ops; + dev->net->max_mtu = 16384 - (dev->net->hard_header_len + 4); /* Blink LEDS so users know driver saw dongle */ - asix_sw_reset(dev, 0); + asix_sw_reset(dev, 0, 0); msleep(150); - asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD); + asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0); msleep(150); /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */ @@ -886,10 +1379,9 @@ static const struct driver_info ax88772_info = { .description = "ASIX AX88772 USB 2.0 Ethernet", .bind = ax88772_bind, .unbind = ax88772_unbind, - .status = asix_status, - .link_reset = ax88772_link_reset, .reset = ax88772_reset, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET, + .stop = ax88772_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET, .rx_fixup = asix_rx_fixup_common, .tx_fixup = asix_tx_fixup, }; @@ -898,11 +1390,21 @@ static const struct driver_info ax88772b_info = { .description = "ASIX AX88772B USB 2.0 Ethernet", .bind = ax88772_bind, .unbind = ax88772_unbind, - .status = asix_status, - .link_reset = ax88772_link_reset, .reset = ax88772_reset, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | - FLAG_MULTI_PACKET, + .stop = ax88772_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET, + .rx_fixup = asix_rx_fixup_common, + .tx_fixup = asix_tx_fixup, + .data = FLAG_EEPROM_MAC, +}; + +static const struct driver_info lxausb_t1l_info = { + .description = "Linux Automation GmbH USB 10Base-T1L", + .bind = ax88772_bind, + .unbind = ax88772_unbind, + .reset = ax88772_reset, + .stop = ax88772_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET, .rx_fixup = asix_rx_fixup_common, .tx_fixup = asix_tx_fixup, .data = FLAG_EEPROM_MAC, @@ -911,11 +1413,12 @@ static const struct driver_info ax88772b_info = { static const struct driver_info ax88178_info = { .description = "ASIX AX88178 USB 2.0 Ethernet", .bind = ax88178_bind, - .unbind = ax88772_unbind, + .unbind = ax88178_unbind, .status = asix_status, .link_reset = ax88178_link_reset, .reset = ax88178_reset, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | + FLAG_MULTI_PACKET, .rx_fixup = asix_rx_fixup_common, .tx_fixup = asix_tx_fixup, }; @@ -933,17 +1436,25 @@ static const struct driver_info hg20f9_info = { .description = "HG20F9 USB 2.0 Ethernet", .bind = ax88772_bind, .unbind = ax88772_unbind, - .status = asix_status, - .link_reset = ax88772_link_reset, .reset = ax88772_reset, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | - FLAG_MULTI_PACKET, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET, .rx_fixup = asix_rx_fixup_common, .tx_fixup = asix_tx_fixup, .data = FLAG_EEPROM_MAC, }; -extern const struct driver_info ax88172a_info; +static const struct driver_info lyconsys_fibergecko100_info = { + .description = "LyconSys FiberGecko 100 USB 2.0 to SFP Adapter", + .bind = ax88178_bind, + .status = asix_status, + .link_reset = ax88178_link_reset, + .reset = ax88178_link_reset, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | + FLAG_MULTI_PACKET, + .rx_fixup = asix_rx_fixup_common, + .tx_fixup = asix_tx_fixup, + .data = 0x20061201, +}; static const struct usb_device_id products [] = { { @@ -971,6 +1482,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x08dd, 0x90ff), .driver_info = (unsigned long) &ax8817x_info, }, { + // Billionton Systems, GUSB2AM-1G-B + USB_DEVICE(0x08dd, 0x0114), + .driver_info = (unsigned long) &ax88178_info, +}, { // ATEN UC210T USB_DEVICE (0x0557, 0x2009), .driver_info = (unsigned long) &ax8817x_info, @@ -991,6 +1506,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x0df6, 0x0056), .driver_info = (unsigned long) &ax88178_info, }, { + // Sitecom LN-028 "USB 2.0 10/100/1000 Ethernet adapter" + USB_DEVICE (0x0df6, 0x061c), + .driver_info = (unsigned long) &ax88178_info, +}, { // corega FEther USB2-TX USB_DEVICE (0x07aa, 0x0017), .driver_info = (unsigned long) &ax8817x_info, @@ -1009,7 +1528,7 @@ static const struct usb_device_id products [] = { }, { // Lenovo U2L100P 10/100 USB_DEVICE (0x17ef, 0x7203), - .driver_info = (unsigned long) &ax88772_info, + .driver_info = (unsigned long)&ax88772b_info, }, { // ASIX AX88772B 10/100 USB_DEVICE (0x0b95, 0x772b), @@ -1077,7 +1596,7 @@ static const struct usb_device_id products [] = { }, { // Asus USB Ethernet Adapter USB_DEVICE (0x0b95, 0x7e2b), - .driver_info = (unsigned long) &ax88772_info, + .driver_info = (unsigned long)&ax88772b_info, }, { /* ASIX 88172a demo board */ USB_DEVICE(0x0b95, 0x172a), @@ -1090,6 +1609,14 @@ static const struct usb_device_id products [] = { */ USB_DEVICE(0x066b, 0x20f9), .driver_info = (unsigned long) &hg20f9_info, +}, { + // Linux Automation GmbH USB 10Base-T1L + USB_DEVICE(0x33f7, 0x0004), + .driver_info = (unsigned long) &lxausb_t1l_info, +}, { + /* LyconSys FiberGecko 100 */ + USB_DEVICE(0x1d2a, 0x0801), + .driver_info = (unsigned long) &lyconsys_fibergecko100_info, }, { }, // END }; @@ -1099,9 +1626,15 @@ static struct usb_driver asix_driver = { .name = DRIVER_NAME, .id_table = products, .probe = usbnet_probe, - .suspend = usbnet_suspend, - .resume = usbnet_resume, + .suspend = asix_suspend, + .resume = asix_resume, + .reset_resume = asix_resume, .disconnect = usbnet_disconnect, + /* usbnet enables autosuspend by default (supports_autosuspend=1). + * We keep runtime-PM active for AX88772* by taking a PM usage + * reference in ax88772_bind() (pm_runtime_get_noresume()) and + * dropping it in unbind(), which effectively blocks autosuspend. + */ .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, }; |
