[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20200723115507.26194-4-andre.edich@microchip.com>
Date: Thu, 23 Jul 2020 13:55:04 +0200
From: Andre Edich <andre.edich@...rochip.com>
To: <netdev@...r.kernel.org>, <UNGLinuxDriver@...rochip.com>,
<steve.glendinning@...well.net>
CC: <Parthiban.Veerasooran@...rochip.com>,
Andre Edich <andre.edich@...rochip.com>
Subject: [PATCH net-next v2 3/6] smsc95xx: add PAL support to use external PHY drivers
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@...rochip.com>
---
drivers/net/usb/smsc95xx.c | 201 ++++++++++++++++++++++++-------------
1 file changed, 132 insertions(+), 69 deletions(-)
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,
--
2.27.0
Powered by blists - more mailing lists