359 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * ASIX AX88172A based USB 2.0 Ethernet Devices4 * Copyright (C) 2012 OMICRON electronics GmbH5 *6 * Supports external PHYs via phylib. Based on the driver for the7 * AX88772. Original copyrights follow:8 *9 * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com>10 * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>11 * Copyright (C) 2006 James Painter <jamie.painter@iname.com>12 * Copyright (c) 2002-2003 TiVo Inc.13 */14 15#include "asix.h"16#include <linux/phy.h>17 18struct ax88172a_private {19 struct mii_bus *mdio;20 struct phy_device *phydev;21 char phy_name[20];22 u16 phy_addr;23 u16 oldmode;24 int use_embdphy;25 struct asix_rx_fixup_info rx_fixup_info;26};27 28/* set MAC link settings according to information from phylib */29static void ax88172a_adjust_link(struct net_device *netdev)30{31 struct phy_device *phydev = netdev->phydev;32 struct usbnet *dev = netdev_priv(netdev);33 struct ax88172a_private *priv = dev->driver_priv;34 u16 mode = 0;35 36 if (phydev->link) {37 mode = AX88772_MEDIUM_DEFAULT;38 39 if (phydev->duplex == DUPLEX_HALF)40 mode &= ~AX_MEDIUM_FD;41 42 if (phydev->speed != SPEED_100)43 mode &= ~AX_MEDIUM_PS;44 }45 46 if (mode != priv->oldmode) {47 asix_write_medium_mode(dev, mode, 0);48 priv->oldmode = mode;49 netdev_dbg(netdev, "speed %u duplex %d, setting mode to 0x%04x\n",50 phydev->speed, phydev->duplex, mode);51 phy_print_status(phydev);52 }53}54 55static void ax88172a_status(struct usbnet *dev, struct urb *urb)56{57 /* link changes are detected by polling the phy */58}59 60/* use phylib infrastructure */61static int ax88172a_init_mdio(struct usbnet *dev)62{63 struct ax88172a_private *priv = dev->driver_priv;64 int ret;65 66 priv->mdio = mdiobus_alloc();67 if (!priv->mdio) {68 netdev_err(dev->net, "Could not allocate MDIO bus\n");69 return -ENOMEM;70 }71 72 priv->mdio->priv = (void *)dev;73 priv->mdio->read = &asix_mdio_bus_read;74 priv->mdio->write = &asix_mdio_bus_write;75 priv->mdio->name = "Asix MDIO Bus";76 /* mii bus name is usb-<usb bus number>-<usb device number> */77 snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",78 dev->udev->bus->busnum, dev->udev->devnum);79 80 ret = mdiobus_register(priv->mdio);81 if (ret) {82 netdev_err(dev->net, "Could not register MDIO bus\n");83 goto mfree;84 }85 86 netdev_info(dev->net, "registered mdio bus %s\n", priv->mdio->id);87 return 0;88 89mfree:90 mdiobus_free(priv->mdio);91 return ret;92}93 94static void ax88172a_remove_mdio(struct usbnet *dev)95{96 struct ax88172a_private *priv = dev->driver_priv;97 98 netdev_info(dev->net, "deregistering mdio bus %s\n", priv->mdio->id);99 mdiobus_unregister(priv->mdio);100 mdiobus_free(priv->mdio);101}102 103static const struct net_device_ops ax88172a_netdev_ops = {104 .ndo_open = usbnet_open,105 .ndo_stop = usbnet_stop,106 .ndo_start_xmit = usbnet_start_xmit,107 .ndo_tx_timeout = usbnet_tx_timeout,108 .ndo_change_mtu = usbnet_change_mtu,109 .ndo_get_stats64 = dev_get_tstats64,110 .ndo_set_mac_address = asix_set_mac_address,111 .ndo_validate_addr = eth_validate_addr,112 .ndo_eth_ioctl = phy_do_ioctl_running,113 .ndo_set_rx_mode = asix_set_multicast,114};115 116static const struct ethtool_ops ax88172a_ethtool_ops = {117 .get_drvinfo = asix_get_drvinfo,118 .get_link = usbnet_get_link,119 .get_msglevel = usbnet_get_msglevel,120 .set_msglevel = usbnet_set_msglevel,121 .get_wol = asix_get_wol,122 .set_wol = asix_set_wol,123 .get_eeprom_len = asix_get_eeprom_len,124 .get_eeprom = asix_get_eeprom,125 .set_eeprom = asix_set_eeprom,126 .nway_reset = phy_ethtool_nway_reset,127 .get_link_ksettings = phy_ethtool_get_link_ksettings,128 .set_link_ksettings = phy_ethtool_set_link_ksettings,129};130 131static int ax88172a_reset_phy(struct usbnet *dev, int embd_phy)132{133 int ret;134 135 ret = asix_sw_reset(dev, AX_SWRESET_IPPD, 0);136 if (ret < 0)137 goto err;138 139 msleep(150);140 ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, 0);141 if (ret < 0)142 goto err;143 144 msleep(150);145 146 ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD,147 0);148 if (ret < 0)149 goto err;150 151 return 0;152 153err:154 return ret;155}156 157 158static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf)159{160 int ret;161 u8 buf[ETH_ALEN];162 struct ax88172a_private *priv;163 164 ret = usbnet_get_endpoints(dev, intf);165 if (ret)166 return ret;167 168 priv = kzalloc(sizeof(*priv), GFP_KERNEL);169 if (!priv)170 return -ENOMEM;171 172 dev->driver_priv = priv;173 174 /* Get the MAC address */175 ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);176 if (ret < ETH_ALEN) {177 netdev_err(dev->net, "Failed to read MAC address: %d\n", ret);178 ret = -EIO;179 goto free;180 }181 eth_hw_addr_set(dev->net, buf);182 183 dev->net->netdev_ops = &ax88172a_netdev_ops;184 dev->net->ethtool_ops = &ax88172a_ethtool_ops;185 186 /* are we using the internal or the external phy? */187 ret = asix_read_cmd(dev, AX_CMD_SW_PHY_STATUS, 0, 0, 1, buf, 0);188 if (ret < 0) {189 netdev_err(dev->net, "Failed to read software interface selection register: %d\n",190 ret);191 goto free;192 }193 194 netdev_dbg(dev->net, "AX_CMD_SW_PHY_STATUS = 0x%02x\n", buf[0]);195 switch (buf[0] & AX_PHY_SELECT_MASK) {196 case AX_PHY_SELECT_INTERNAL:197 netdev_dbg(dev->net, "use internal phy\n");198 priv->use_embdphy = 1;199 break;200 case AX_PHY_SELECT_EXTERNAL:201 netdev_dbg(dev->net, "use external phy\n");202 priv->use_embdphy = 0;203 break;204 default:205 netdev_err(dev->net, "Interface mode not supported by driver\n");206 ret = -ENOTSUPP;207 goto free;208 }209 210 ret = asix_read_phy_addr(dev, priv->use_embdphy);211 if (ret < 0)212 goto free;213 214 priv->phy_addr = ret;215 216 ax88172a_reset_phy(dev, priv->use_embdphy);217 218 /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */219 if (dev->driver_info->flags & FLAG_FRAMING_AX) {220 /* hard_mtu is still the default - the device does not support221 jumbo eth frames */222 dev->rx_urb_size = 2048;223 }224 225 /* init MDIO bus */226 ret = ax88172a_init_mdio(dev);227 if (ret)228 goto free;229 230 return 0;231 232free:233 kfree(priv);234 return ret;235}236 237static int ax88172a_stop(struct usbnet *dev)238{239 struct ax88172a_private *priv = dev->driver_priv;240 241 netdev_dbg(dev->net, "Stopping interface\n");242 243 if (priv->phydev) {244 netdev_info(dev->net, "Disconnecting from phy %s\n",245 priv->phy_name);246 phy_stop(priv->phydev);247 phy_disconnect(priv->phydev);248 }249 250 return 0;251}252 253static void ax88172a_unbind(struct usbnet *dev, struct usb_interface *intf)254{255 struct ax88172a_private *priv = dev->driver_priv;256 257 ax88172a_remove_mdio(dev);258 kfree(priv);259}260 261static int ax88172a_reset(struct usbnet *dev)262{263 struct asix_data *data = (struct asix_data *)&dev->data;264 struct ax88172a_private *priv = dev->driver_priv;265 int ret;266 u16 rx_ctl;267 268 ax88172a_reset_phy(dev, priv->use_embdphy);269 270 msleep(150);271 rx_ctl = asix_read_rx_ctl(dev, 0);272 netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl);273 ret = asix_write_rx_ctl(dev, 0x0000, 0);274 if (ret < 0)275 goto out;276 277 rx_ctl = asix_read_rx_ctl(dev, 0);278 netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl);279 280 msleep(150);281 282 ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,283 AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,284 AX88772_IPG2_DEFAULT, 0, NULL, 0);285 if (ret < 0) {286 netdev_err(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);287 goto out;288 }289 290 /* Rewrite MAC address */291 memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);292 ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,293 data->mac_addr, 0);294 if (ret < 0)295 goto out;296 297 /* Set RX_CTL to default values with 2k buffer, and enable cactus */298 ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);299 if (ret < 0)300 goto out;301 302 rx_ctl = asix_read_rx_ctl(dev, 0);303 netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",304 rx_ctl);305 306 rx_ctl = asix_read_medium_status(dev, 0);307 netdev_dbg(dev->net, "Medium Status is 0x%04x after all initializations\n",308 rx_ctl);309 310 /* Connect to PHY */311 snprintf(priv->phy_name, 20, PHY_ID_FMT,312 priv->mdio->id, priv->phy_addr);313 314 priv->phydev = phy_connect(dev->net, priv->phy_name,315 &ax88172a_adjust_link,316 PHY_INTERFACE_MODE_MII);317 if (IS_ERR(priv->phydev)) {318 netdev_err(dev->net, "Could not connect to PHY device %s\n",319 priv->phy_name);320 ret = PTR_ERR(priv->phydev);321 goto out;322 }323 324 netdev_info(dev->net, "Connected to phy %s\n", priv->phy_name);325 326 /* During power-up, the AX88172A set the power down (BMCR_PDOWN)327 * bit of the PHY. Bring the PHY up again.328 */329 genphy_resume(priv->phydev);330 phy_start(priv->phydev);331 332 return 0;333 334out:335 return ret;336 337}338 339static int ax88172a_rx_fixup(struct usbnet *dev, struct sk_buff *skb)340{341 struct ax88172a_private *dp = dev->driver_priv;342 struct asix_rx_fixup_info *rx = &dp->rx_fixup_info;343 344 return asix_rx_fixup_internal(dev, skb, rx);345}346 347const struct driver_info ax88172a_info = {348 .description = "ASIX AX88172A USB 2.0 Ethernet",349 .bind = ax88172a_bind,350 .reset = ax88172a_reset,351 .stop = ax88172a_stop,352 .unbind = ax88172a_unbind,353 .status = ax88172a_status,354 .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |355 FLAG_MULTI_PACKET,356 .rx_fixup = ax88172a_rx_fixup,357 .tx_fixup = asix_tx_fixup,358};359