Message ID | 20200723115507.26194-4-andre.edich@microchip.com |
---|---|
State | Changes Requested |
Delegated to: | David Miller |
Headers | show |
Series | Add PAL support to smsc95xx | expand |
On Thu, Jul 23, 2020 at 01:55:04PM +0200, Andre Edich wrote: > Generally, each PHY has their own configuration and it can be done > through an external PHY driver. The smsc95xx driver uses only the > hard-coded internal PHY configuration. > > This patch adds PAL (PHY Abstraction Layer) support to probe external > PHY drivers for configuring external PHYs. Hi Andre We call it phylib, not PAL. > static int __must_check smsc95xx_wait_eeprom(struct usbnet *dev) > { > unsigned long start_time = jiffies; > @@ -559,15 +580,20 @@ static int smsc95xx_link_reset(struct usbnet *dev) > u16 lcladv, rmtadv; > int ret; > > - /* clear interrupt status */ > - ret = smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); > - if (ret < 0) > - return ret; > - > ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); > if (ret < 0) > return ret; > > + if (pdata->internal_phy) { > + /* clear interrupt status */ > + ret = smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); > + if (ret < 0) > + return ret; > + > + smsc95xx_mdio_write(dev->net, mii->phy_id, PHY_INT_MASK, > + PHY_INT_MASK_DEFAULT_); > + } The PHY driver should do this, not the MAC driver. Which PHY driver is used for the internal PHY? In theory, you should not need to know if it is internal or external, it is just a PHY. That might mean you need to move some code from this driver into the PHY driver, if it is currently missing in the PHY driver. > + > mii_check_media(mii, 1, 1); > mii_ethtool_gset(&dev->mii, &ecmd); > lcladv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE); > @@ -851,10 +877,10 @@ static int smsc95xx_get_link_ksettings(struct net_device *net, > int retval; > > retval = usbnet_get_link_ksettings(net, cmd); > - > - cmd->base.eth_tp_mdix = pdata->mdix_ctrl; > - cmd->base.eth_tp_mdix_ctrl = pdata->mdix_ctrl; > - > + if (pdata->internal_phy) { > + cmd->base.eth_tp_mdix = pdata->mdix_ctrl; > + cmd->base.eth_tp_mdix_ctrl = pdata->mdix_ctrl; > + } Again, they PHY driver should take care of this. You need to set phydev->mdix_ctrl before starting the PHY. The PHY driver should set phdev->mdix to the current status. > +static void smsc95xx_handle_link_change(struct net_device *net) > +{ > + phy_print_status(net->phydev); So the MAC does not care about the speed? The pause configuration? Duplex? Andrew
On Fri, 2020-07-24 at 00:39 +0200, Andrew Lunn wrote: > EXTERNAL EMAIL: Do not click links or open attachments unless you > know the content is safe > > On Thu, Jul 23, 2020 at 01:55:04PM +0200, Andre Edich wrote: > > Generally, each PHY has their own configuration and it can be done > > through an external PHY driver. The smsc95xx driver uses only the > > hard-coded internal PHY configuration. > > > > This patch adds PAL (PHY Abstraction Layer) support to probe > > external > > PHY drivers for configuring external PHYs. > > Hi Andre > > We call it phylib, not PAL. Hi Andrew, thank you for the feedback. In the next version, I will correct these wordings as well. > > > static int __must_check smsc95xx_wait_eeprom(struct usbnet *dev) > > { > > unsigned long start_time = jiffies; > > @@ -559,15 +580,20 @@ static int smsc95xx_link_reset(struct usbnet > > *dev) > > u16 lcladv, rmtadv; > > int ret; > > > > - /* clear interrupt status */ > > - ret = smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); > > - if (ret < 0) > > - return ret; > > - > > ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); > > if (ret < 0) > > return ret; > > > > + if (pdata->internal_phy) { > > + /* clear interrupt status */ > > + ret = smsc95xx_mdio_read(dev->net, mii->phy_id, > > PHY_INT_SRC); > > + if (ret < 0) > > + return ret; > > + > > + smsc95xx_mdio_write(dev->net, mii->phy_id, > > PHY_INT_MASK, > > + PHY_INT_MASK_DEFAULT_); > > + } > > The PHY driver should do this, not the MAC driver. > > Which PHY driver is used for the internal PHY? In theory, you should > not need to know if it is internal or external, it is just a PHY. Yes sure, you are right. I see the drivers/net/phy/smsc.c that is probed for the internal PHY of the DUT's Ethernet controller. > > That > might mean you need to move some code from this driver into the PHY > driver, if it is currently missing in the PHY driver. Correct, the PHY driver does interrupt setup activities, so that they can be removed from the smsc95xx. > > > + > > mii_check_media(mii, 1, 1); > > mii_ethtool_gset(&dev->mii, &ecmd); > > lcladv = smsc95xx_mdio_read(dev->net, mii->phy_id, > > MII_ADVERTISE); > > @@ -851,10 +877,10 @@ static int smsc95xx_get_link_ksettings(struct > > net_device *net, > > int retval; > > > > retval = usbnet_get_link_ksettings(net, cmd); > > - > > - cmd->base.eth_tp_mdix = pdata->mdix_ctrl; > > - cmd->base.eth_tp_mdix_ctrl = pdata->mdix_ctrl; > > - > > + if (pdata->internal_phy) { > > + cmd->base.eth_tp_mdix = pdata->mdix_ctrl; > > + cmd->base.eth_tp_mdix_ctrl = pdata->mdix_ctrl; > > + } > > Again, they PHY driver should take care of this. You need to set > phydev->mdix_ctrl before starting the PHY. The PHY driver should set > phdev->mdix to the current status. The SMSC Phy driver does not have any MDIX setup code, but I think I've got the idea now. > > > +static void smsc95xx_handle_link_change(struct net_device *net) > > +{ > > + phy_print_status(net->phydev); > > So the MAC does not care about the speed? The pause configuration? > Duplex? Now, I'm wondering how those "care about speed", "pause", and "duplex" work in the current smsc95xx. I guess, we did not touch any of those activities with our patches. Thanks a lot. Andre > > Andrew
> > > +static void smsc95xx_handle_link_change(struct net_device *net) > > > +{ > > > + phy_print_status(net->phydev); > > > > So the MAC does not care about the speed? The pause configuration? > > Duplex? > > Now, I'm wondering how those "care about speed", "pause", and "duplex" > work in the current smsc95xx. I guess, we did not touch any of those > activities with our patches. Yes, this patchset itself is not necessarily wrong. It seems more like the driver could be broken with respect to these things. It is something you might want to put on your TODO list to look at later. Andrew
Hi Andre, Thank you for the patch! Yet something to improve: [auto build test ERROR on net-next/master] url: https://github.com/0day-ci/linux/commits/Andre-Edich/Add-PAL-support-to-smsc95xx/20200723-195824 base: https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 7fc3b978a8971305d456b32d3f2ac13191f5a0d7 config: x86_64-randconfig-a016-20200724 (attached as .config) compiler: gcc-9 (Debian 9.3.0-14) 9.3.0 reproduce (this is a W=1 build): # save the attached .config to linux build tree make W=1 ARCH=x86_64 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All errors (new ones prefixed by >>): ld: drivers/net/usb/smsc95xx.o: in function `smsc95xx_disconnect_phy': >> drivers/net/usb/smsc95xx.c:1395: undefined reference to `phy_stop' >> ld: drivers/net/usb/smsc95xx.c:1396: undefined reference to `phy_disconnect' ld: drivers/net/usb/smsc95xx.o: in function `smsc95xx_unbind': >> drivers/net/usb/smsc95xx.c:1359: undefined reference to `mdiobus_unregister' >> ld: drivers/net/usb/smsc95xx.c:1360: undefined reference to `mdiobus_free' ld: drivers/net/usb/smsc95xx.o: in function `smsc95xx_start_phy': >> drivers/net/usb/smsc95xx.c:1380: undefined reference to `phy_connect_direct' >> ld: drivers/net/usb/smsc95xx.c:1388: undefined reference to `phy_attached_info' >> ld: drivers/net/usb/smsc95xx.c:1389: undefined reference to `phy_start' ld: drivers/net/usb/smsc95xx.o: in function `smsc95xx_resume': >> drivers/net/usb/smsc95xx.c:1951: undefined reference to `phy_init_hw' ld: drivers/net/usb/smsc95xx.o: in function `smsc95xx_handle_link_change': >> drivers/net/usb/smsc95xx.c:1367: undefined reference to `phy_print_status' ld: drivers/net/usb/smsc95xx.o: in function `mdiobus_alloc': >> include/linux/phy.h:318: undefined reference to `mdiobus_alloc_size' ld: drivers/net/usb/smsc95xx.o: in function `smsc95xx_bind': >> drivers/net/usb/smsc95xx.c:1293: undefined reference to `__mdiobus_register' >> ld: drivers/net/usb/smsc95xx.c:1299: undefined reference to `phy_find_first' ld: drivers/net/usb/smsc95xx.c:1344: undefined reference to `mdiobus_unregister' ld: drivers/net/usb/smsc95xx.c:1347: undefined reference to `mdiobus_free' vim +1395 drivers/net/usb/smsc95xx.c 1222 1223 static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) 1224 { 1225 struct smsc95xx_priv *pdata; 1226 u32 val; 1227 int ret; 1228 1229 printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); 1230 1231 ret = usbnet_get_endpoints(dev, intf); 1232 if (ret < 0) { 1233 netdev_warn(dev->net, "usbnet_get_endpoints failed: %d\n", ret); 1234 return ret; 1235 } 1236 1237 pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); 1238 if (!pdata) 1239 return -ENOMEM; 1240 1241 dev->driver_priv = pdata; 1242 1243 spin_lock_init(&pdata->mac_cr_lock); 1244 1245 /* LAN95xx devices do not alter the computed checksum of 0 to 0xffff. 1246 * RFC 2460, ipv6 UDP calculated checksum yields a result of zero must 1247 * be changed to 0xffff. RFC 768, ipv4 UDP computed checksum is zero, 1248 * it is transmitted as all ones. The zero transmitted checksum means 1249 * transmitter generated no checksum. Hence, enable csum offload only 1250 * for ipv4 packets. 1251 */ 1252 if (DEFAULT_TX_CSUM_ENABLE) 1253 dev->net->features |= NETIF_F_IP_CSUM; 1254 if (DEFAULT_RX_CSUM_ENABLE) 1255 dev->net->features |= NETIF_F_RXCSUM; 1256 1257 dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM; 1258 set_bit(EVENT_NO_IP_ALIGN, &dev->flags); 1259 1260 smsc95xx_init_mac_address(dev); 1261 1262 /* Init all registers */ 1263 ret = smsc95xx_reset(dev); 1264 if (ret) 1265 goto free_pdata; 1266 1267 pdata->mdiobus = mdiobus_alloc(); 1268 if (!pdata->mdiobus) { 1269 ret = -ENOMEM; 1270 goto free_pdata; 1271 } 1272 1273 ret = smsc95xx_read_reg(dev, HW_CFG, &val); 1274 if (ret < 0) 1275 goto free_mdio; 1276 1277 pdata->internal_phy = !(val & HW_CFG_PSEL_); 1278 if (pdata->internal_phy) 1279 pdata->mdiobus->phy_mask = ~(1u << SMSC95XX_INTERNAL_PHY_ID); 1280 1281 pdata->mdiobus->priv = dev; 1282 pdata->mdiobus->read = smsc95xx_mdiobus_read; 1283 pdata->mdiobus->write = smsc95xx_mdiobus_write; 1284 pdata->mdiobus->name = "smsc95xx-mdiobus"; 1285 pdata->mdiobus->parent = &dev->udev->dev; 1286 1287 dev->mii.phy_id_mask = 0x1f; 1288 dev->mii.reg_num_mask = 0x1f; 1289 1290 snprintf(pdata->mdiobus->id, ARRAY_SIZE(pdata->mdiobus->id), 1291 "usb-%03d:%03d", dev->udev->bus->busnum, dev->udev->devnum); 1292 > 1293 ret = mdiobus_register(pdata->mdiobus); 1294 if (ret) { 1295 netdev_err(dev->net, "Could not register MDIO bus\n"); 1296 goto free_mdio; 1297 } 1298 > 1299 pdata->phydev = phy_find_first(pdata->mdiobus); 1300 if (!pdata->phydev) { 1301 netdev_err(dev->net, "no PHY found\n"); 1302 ret = -ENODEV; 1303 goto unregister_mdio; 1304 } 1305 1306 dev->mii.dev = dev->net; 1307 dev->mii.mdio_read = smsc95xx_mdio_read; 1308 dev->mii.mdio_write = smsc95xx_mdio_write; 1309 dev->mii.phy_id = pdata->phydev->mdio.addr; 1310 1311 /* detect device revision as different features may be available */ 1312 ret = smsc95xx_read_reg(dev, ID_REV, &val); 1313 if (ret < 0) 1314 goto unregister_mdio; 1315 1316 val >>= 16; 1317 pdata->chip_id = val; 1318 if (pdata->internal_phy) 1319 pdata->mdix_ctrl = get_mdix_status(dev->net); 1320 1321 if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9530_) || 1322 (val == ID_REV_CHIP_ID_89530_) || (val == ID_REV_CHIP_ID_9730_)) 1323 pdata->features = (FEATURE_8_WAKEUP_FILTERS | 1324 FEATURE_PHY_NLP_CROSSOVER | 1325 FEATURE_REMOTE_WAKEUP); 1326 else if (val == ID_REV_CHIP_ID_9512_) 1327 pdata->features = FEATURE_8_WAKEUP_FILTERS; 1328 1329 dev->net->netdev_ops = &smsc95xx_netdev_ops; 1330 dev->net->ethtool_ops = &smsc95xx_ethtool_ops; 1331 dev->net->flags |= IFF_MULTICAST; 1332 dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM; 1333 dev->net->min_mtu = ETH_MIN_MTU; 1334 dev->net->max_mtu = ETH_DATA_LEN; 1335 dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; 1336 1337 pdata->dev = dev; 1338 INIT_DELAYED_WORK(&pdata->carrier_check, check_carrier); 1339 schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY); 1340 1341 return 0; 1342 1343 unregister_mdio: 1344 mdiobus_unregister(pdata->mdiobus); 1345 1346 free_mdio: 1347 mdiobus_free(pdata->mdiobus); 1348 1349 free_pdata: 1350 kfree(pdata); 1351 return ret; 1352 } 1353 1354 static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf) 1355 { 1356 struct smsc95xx_priv *pdata = dev->driver_priv; 1357 1358 cancel_delayed_work_sync(&pdata->carrier_check); > 1359 mdiobus_unregister(pdata->mdiobus); > 1360 mdiobus_free(pdata->mdiobus); 1361 netif_dbg(dev, ifdown, dev->net, "free pdata\n"); 1362 kfree(pdata); 1363 } 1364 1365 static void smsc95xx_handle_link_change(struct net_device *net) 1366 { > 1367 phy_print_status(net->phydev); 1368 } 1369 1370 static int smsc95xx_start_phy(struct usbnet *dev) 1371 { 1372 struct smsc95xx_priv *pdata = dev->driver_priv; 1373 struct net_device *net = dev->net; 1374 int ret; 1375 1376 ret = smsc95xx_reset(dev); 1377 if (ret < 0) 1378 return ret; 1379 > 1380 ret = phy_connect_direct(net, pdata->phydev, 1381 &smsc95xx_handle_link_change, 1382 PHY_INTERFACE_MODE_MII); 1383 if (ret) { 1384 netdev_err(net, "can't attach PHY to %s\n", pdata->mdiobus->id); 1385 return ret; 1386 } 1387 > 1388 phy_attached_info(net->phydev); > 1389 phy_start(net->phydev); 1390 return 0; 1391 } 1392 1393 static int smsc95xx_disconnect_phy(struct usbnet *dev) 1394 { > 1395 phy_stop(dev->net->phydev); > 1396 phy_disconnect(dev->net->phydev); 1397 return 0; 1398 } 1399 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index f200684875fb..9d2710f6d396 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -18,6 +18,8 @@ #include <linux/usb/usbnet.h> #include <linux/slab.h> #include <linux/of_net.h> +#include <linux/mdio.h> +#include <linux/phy.h> #include "smsc95xx.h" #define SMSC_CHIPNAME "smsc95xx" @@ -64,6 +66,9 @@ struct smsc95xx_priv { bool link_ok; struct delayed_work carrier_check; struct usbnet *dev; + struct mii_bus *mdiobus; + struct phy_device *phydev; + bool internal_phy; }; static bool turbo_mode = true; @@ -286,6 +291,22 @@ static void smsc95xx_mdio_write(struct net_device *netdev, int phy_id, int idx, __smsc95xx_mdio_write(netdev, phy_id, idx, regval, 0); } +static int smsc95xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx) +{ + struct usbnet *dev = bus->priv; + + return __smsc95xx_mdio_read(dev->net, phy_id, idx, 0); +} + +static int smsc95xx_mdiobus_write(struct mii_bus *bus, int phy_id, int idx, + u16 regval) +{ + struct usbnet *dev = bus->priv; + + __smsc95xx_mdio_write(dev->net, phy_id, idx, regval, 0); + return 0; +} + static int __must_check smsc95xx_wait_eeprom(struct usbnet *dev) { unsigned long start_time = jiffies; @@ -559,15 +580,20 @@ static int smsc95xx_link_reset(struct usbnet *dev) u16 lcladv, rmtadv; int ret; - /* clear interrupt status */ - ret = smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); - if (ret < 0) - return ret; - ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); if (ret < 0) return ret; + if (pdata->internal_phy) { + /* clear interrupt status */ + ret = smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); + if (ret < 0) + return ret; + + smsc95xx_mdio_write(dev->net, mii->phy_id, PHY_INT_MASK, + PHY_INT_MASK_DEFAULT_); + } + mii_check_media(mii, 1, 1); mii_ethtool_gset(&dev->mii, &ecmd); lcladv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE); @@ -851,10 +877,10 @@ static int smsc95xx_get_link_ksettings(struct net_device *net, int retval; retval = usbnet_get_link_ksettings(net, cmd); - - cmd->base.eth_tp_mdix = pdata->mdix_ctrl; - cmd->base.eth_tp_mdix_ctrl = pdata->mdix_ctrl; - + if (pdata->internal_phy) { + cmd->base.eth_tp_mdix = pdata->mdix_ctrl; + cmd->base.eth_tp_mdix_ctrl = pdata->mdix_ctrl; + } return retval; } @@ -863,14 +889,12 @@ static int smsc95xx_set_link_ksettings(struct net_device *net, { struct usbnet *dev = netdev_priv(net); struct smsc95xx_priv *pdata = dev->driver_priv; - int retval; - - if (pdata->mdix_ctrl != cmd->base.eth_tp_mdix_ctrl) - set_mdix_status(net, cmd->base.eth_tp_mdix_ctrl); + u8 mdix_ctrl = cmd->base.eth_tp_mdix_ctrl; - retval = usbnet_set_link_ksettings(net, cmd); + if (pdata->mdix_ctrl != mdix_ctrl && pdata->internal_phy) + set_mdix_status(net, mdix_ctrl); - return retval; + return usbnet_set_link_ksettings(net, cmd); } static const struct ethtool_ops smsc95xx_ethtool_ops = { @@ -974,51 +998,6 @@ static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm) return __smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr, in_pm); } -static int smsc95xx_phy_initialize(struct usbnet *dev) -{ - int bmcr, ret, timeout = 0; - - /* Initialize MII structure */ - dev->mii.dev = dev->net; - dev->mii.mdio_read = smsc95xx_mdio_read; - dev->mii.mdio_write = smsc95xx_mdio_write; - dev->mii.phy_id_mask = 0x1f; - dev->mii.reg_num_mask = 0x1f; - dev->mii.phy_id = SMSC95XX_INTERNAL_PHY_ID; - - /* reset phy and wait for reset to complete */ - smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); - - do { - msleep(10); - bmcr = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); - timeout++; - } while ((bmcr & BMCR_RESET) && (timeout < 100)); - - if (timeout >= 100) { - netdev_warn(dev->net, "timeout on PHY Reset"); - return -EIO; - } - - smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, - ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | - ADVERTISE_PAUSE_ASYM); - - /* read to clear */ - ret = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); - if (ret < 0) { - netdev_warn(dev->net, "Failed to read PHY_INT_SRC during init\n"); - return ret; - } - - smsc95xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, - PHY_INT_MASK_DEFAULT_); - mii_nway_restart(&dev->mii); - - netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n"); - return 0; -} - static int smsc95xx_reset(struct usbnet *dev) { struct smsc95xx_priv *pdata = dev->driver_priv; @@ -1200,12 +1179,6 @@ static int smsc95xx_reset(struct usbnet *dev) smsc95xx_set_multicast(dev->net); - ret = smsc95xx_phy_initialize(dev); - if (ret < 0) { - netdev_warn(dev->net, "Failed to init PHY\n"); - return ret; - } - ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf); if (ret < 0) return ret; @@ -1291,14 +1264,59 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) if (ret) goto free_pdata; + pdata->mdiobus = mdiobus_alloc(); + if (!pdata->mdiobus) { + ret = -ENOMEM; + goto free_pdata; + } + + ret = smsc95xx_read_reg(dev, HW_CFG, &val); + if (ret < 0) + goto free_mdio; + + pdata->internal_phy = !(val & HW_CFG_PSEL_); + if (pdata->internal_phy) + pdata->mdiobus->phy_mask = ~(1u << SMSC95XX_INTERNAL_PHY_ID); + + pdata->mdiobus->priv = dev; + pdata->mdiobus->read = smsc95xx_mdiobus_read; + pdata->mdiobus->write = smsc95xx_mdiobus_write; + pdata->mdiobus->name = "smsc95xx-mdiobus"; + pdata->mdiobus->parent = &dev->udev->dev; + + dev->mii.phy_id_mask = 0x1f; + dev->mii.reg_num_mask = 0x1f; + + snprintf(pdata->mdiobus->id, ARRAY_SIZE(pdata->mdiobus->id), + "usb-%03d:%03d", dev->udev->bus->busnum, dev->udev->devnum); + + ret = mdiobus_register(pdata->mdiobus); + if (ret) { + netdev_err(dev->net, "Could not register MDIO bus\n"); + goto free_mdio; + } + + pdata->phydev = phy_find_first(pdata->mdiobus); + if (!pdata->phydev) { + netdev_err(dev->net, "no PHY found\n"); + ret = -ENODEV; + goto unregister_mdio; + } + + dev->mii.dev = dev->net; + dev->mii.mdio_read = smsc95xx_mdio_read; + dev->mii.mdio_write = smsc95xx_mdio_write; + dev->mii.phy_id = pdata->phydev->mdio.addr; + /* detect device revision as different features may be available */ ret = smsc95xx_read_reg(dev, ID_REV, &val); if (ret < 0) - goto free_pdata; + goto unregister_mdio; val >>= 16; pdata->chip_id = val; - pdata->mdix_ctrl = get_mdix_status(dev->net); + if (pdata->internal_phy) + pdata->mdix_ctrl = get_mdix_status(dev->net); if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9530_) || (val == ID_REV_CHIP_ID_89530_) || (val == ID_REV_CHIP_ID_9730_)) @@ -1322,6 +1340,12 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) return 0; +unregister_mdio: + mdiobus_unregister(pdata->mdiobus); + +free_mdio: + mdiobus_free(pdata->mdiobus); + free_pdata: kfree(pdata); return ret; @@ -1332,10 +1356,47 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf) struct smsc95xx_priv *pdata = dev->driver_priv; cancel_delayed_work_sync(&pdata->carrier_check); + mdiobus_unregister(pdata->mdiobus); + mdiobus_free(pdata->mdiobus); netif_dbg(dev, ifdown, dev->net, "free pdata\n"); kfree(pdata); } +static void smsc95xx_handle_link_change(struct net_device *net) +{ + phy_print_status(net->phydev); +} + +static int smsc95xx_start_phy(struct usbnet *dev) +{ + struct smsc95xx_priv *pdata = dev->driver_priv; + struct net_device *net = dev->net; + int ret; + + ret = smsc95xx_reset(dev); + if (ret < 0) + return ret; + + ret = phy_connect_direct(net, pdata->phydev, + &smsc95xx_handle_link_change, + PHY_INTERFACE_MODE_MII); + if (ret) { + netdev_err(net, "can't attach PHY to %s\n", pdata->mdiobus->id); + return ret; + } + + phy_attached_info(net->phydev); + phy_start(net->phydev); + return 0; +} + +static int smsc95xx_disconnect_phy(struct usbnet *dev) +{ + phy_stop(dev->net->phydev); + phy_disconnect(dev->net->phydev); + return 0; +} + static u32 smsc_crc(const u8 *buffer, size_t len, int filter) { u32 crc = bitrev16(crc16(0xFFFF, buffer, len)); @@ -1887,6 +1948,7 @@ static int smsc95xx_resume(struct usb_interface *intf) if (ret < 0) netdev_warn(dev->net, "usbnet_resume error\n"); + phy_init_hw(pdata->phydev); return ret; } @@ -2092,7 +2154,8 @@ static const struct driver_info smsc95xx_info = { .bind = smsc95xx_bind, .unbind = smsc95xx_unbind, .link_reset = smsc95xx_link_reset, - .reset = smsc95xx_reset, + .reset = smsc95xx_start_phy, + .stop = smsc95xx_disconnect_phy, .rx_fixup = smsc95xx_rx_fixup, .tx_fixup = smsc95xx_tx_fixup, .status = smsc95xx_status,
Generally, each PHY has their own configuration and it can be done through an external PHY driver. The smsc95xx driver uses only the hard-coded internal PHY configuration. This patch adds PAL (PHY Abstraction Layer) support to probe external PHY drivers for configuring external PHYs. Signed-off-by: Andre Edich <andre.edich@microchip.com> --- drivers/net/usb/smsc95xx.c | 201 ++++++++++++++++++++++++------------- 1 file changed, 132 insertions(+), 69 deletions(-)