Skip to content

Commit 809ff97

Browse files
tachicialexPaolo Abeni
authored andcommitted
net: usb: smsc95xx: fix external PHY reset
An external PHY needs settling time after power up or reset. In the bind() function an mdio bus is registered. If at this point the external PHY is still initialising, no valid PHY ID will be read and on phy_find_first() the bind() function will fail. If an external PHY is present, wait the maximum time specified in 802.3 45.2.7.1.1. Fixes: 05b35e7 ("smsc95xx: add phylib support") Signed-off-by: Alexandru Tachici <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent e103ba3 commit 809ff97

File tree

1 file changed

+42
-4
lines changed

1 file changed

+42
-4
lines changed

drivers/net/usb/smsc95xx.c

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ struct smsc95xx_priv {
6666
spinlock_t mac_cr_lock;
6767
u8 features;
6868
u8 suspend_flags;
69+
bool is_internal_phy;
6970
struct irq_chip irqchip;
7071
struct irq_domain *irqdomain;
7172
struct fwnode_handle *irqfwnode;
@@ -252,6 +253,43 @@ static void smsc95xx_mdio_write(struct usbnet *dev, int phy_id, int idx,
252253
mutex_unlock(&dev->phy_mutex);
253254
}
254255

256+
static int smsc95xx_mdiobus_reset(struct mii_bus *bus)
257+
{
258+
struct smsc95xx_priv *pdata;
259+
struct usbnet *dev;
260+
u32 val;
261+
int ret;
262+
263+
dev = bus->priv;
264+
pdata = dev->driver_priv;
265+
266+
if (pdata->is_internal_phy)
267+
return 0;
268+
269+
mutex_lock(&dev->phy_mutex);
270+
271+
ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
272+
if (ret < 0)
273+
goto reset_out;
274+
275+
val |= PM_CTL_PHY_RST_;
276+
277+
ret = smsc95xx_write_reg(dev, PM_CTRL, val);
278+
if (ret < 0)
279+
goto reset_out;
280+
281+
/* Driver has no knowledge at this point about the external PHY.
282+
* The 802.3 specifies that the reset process shall
283+
* be completed within 0.5 s.
284+
*/
285+
fsleep(500000);
286+
287+
reset_out:
288+
mutex_unlock(&dev->phy_mutex);
289+
290+
return 0;
291+
}
292+
255293
static int smsc95xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx)
256294
{
257295
struct usbnet *dev = bus->priv;
@@ -1052,7 +1090,6 @@ static void smsc95xx_handle_link_change(struct net_device *net)
10521090
static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
10531091
{
10541092
struct smsc95xx_priv *pdata;
1055-
bool is_internal_phy;
10561093
char usb_path[64];
10571094
int ret, phy_irq;
10581095
u32 val;
@@ -1133,13 +1170,14 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
11331170
if (ret < 0)
11341171
goto free_mdio;
11351172

1136-
is_internal_phy = !(val & HW_CFG_PSEL_);
1137-
if (is_internal_phy)
1173+
pdata->is_internal_phy = !(val & HW_CFG_PSEL_);
1174+
if (pdata->is_internal_phy)
11381175
pdata->mdiobus->phy_mask = ~(1u << SMSC95XX_INTERNAL_PHY_ID);
11391176

11401177
pdata->mdiobus->priv = dev;
11411178
pdata->mdiobus->read = smsc95xx_mdiobus_read;
11421179
pdata->mdiobus->write = smsc95xx_mdiobus_write;
1180+
pdata->mdiobus->reset = smsc95xx_mdiobus_reset;
11431181
pdata->mdiobus->name = "smsc95xx-mdiobus";
11441182
pdata->mdiobus->parent = &dev->udev->dev;
11451183

@@ -1160,7 +1198,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
11601198
}
11611199

11621200
pdata->phydev->irq = phy_irq;
1163-
pdata->phydev->is_internal = is_internal_phy;
1201+
pdata->phydev->is_internal = pdata->is_internal_phy;
11641202

11651203
/* detect device revision as different features may be available */
11661204
ret = smsc95xx_read_reg(dev, ID_REV, &val);

0 commit comments

Comments
 (0)