Skip to content

Commit 1ce8b37

Browse files
l1kdavem330
authored andcommitted
usbnet: smsc95xx: Forward PHY interrupts to PHY driver to avoid polling
Link status of SMSC LAN95xx chips is polled once per second, even though they're capable of signaling PHY interrupts through the MAC layer. Forward those interrupts to the PHY driver to avoid polling. Benefits are reduced bus traffic, reduced CPU overhead and quicker interface bringup. Polling was introduced in 2016 by commit d69d169 ("usbnet: smsc95xx: fix link detection for disabled autonegotiation"). Back then, the LAN95xx driver neglected to enable the ENERGYON interrupt, hence couldn't detect link-up events when auto-negotiation was disabled. The proper solution would have been to enable the ENERGYON interrupt instead of polling. Since then, PHY handling was moved from the LAN95xx driver to the SMSC PHY driver with commit 05b35e7 ("smsc95xx: add phylib support"). That PHY driver is capable of link detection with auto-negotiation disabled because it enables the ENERGYON interrupt. Note that signaling interrupts through the MAC layer not only works with the integrated PHY, but also with an external PHY, provided its interrupt pin is attached to LAN95xx's nPHY_INT pin. In the unlikely event that the interrupt pin of an external PHY is attached to a GPIO of the SoC (or not connected at all), the driver can be amended to retrieve the irq from the PHY's of_node. To forward PHY interrupts to phylib, it is not sufficient to call phy_mac_interrupt(). Instead, the PHY's interrupt handler needs to run so that PHY interrupts are cleared. That's because according to page 119 of the LAN950x datasheet, "The source of this interrupt is a level. The interrupt persists until it is cleared in the PHY." https://www.microchip.com/content/dam/mchp/documents/UNG/ProductDocuments/DataSheets/LAN950x-Data-Sheet-DS00001875D.pdf Therefore, create an IRQ domain with a single IRQ for the PHY. In the future, the IRQ domain may be extended to support the 11 GPIOs on the LAN95xx. Normally the PHY interrupt should be masked until the PHY driver has cleared it. However masking requires a (sleeping) USB transaction and interrupts are received in (non-sleepable) softirq context. I decided not to mask the interrupt at all (by using the dummy_irq_chip's noop ->irq_mask() callback): The USB interrupt endpoint is polled in 1 msec intervals and normally that's sufficient to wake the PHY driver's IRQ thread and have it clear the interrupt. If it does take longer, worst thing that can happen is the IRQ thread is woken again. No big deal. Because PHY interrupts are now perpetually enabled, there's no need to selectively enable them on suspend. So remove all invocations of smsc95xx_enable_phy_wakeup_interrupts(). In smsc95xx_resume(), move the call of phy_init_hw() before usbnet_resume() (which restarts the status URB) to ensure that the PHY is fully initialized when an interrupt is handled. Tested-by: Oleksij Rempel <[email protected]> # LAN9514/9512/9500 Tested-by: Ferry Toth <[email protected]> # LAN9514 Signed-off-by: Lukas Wunner <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> # from a PHY perspective Cc: Andre Edich <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8960f87 commit 1ce8b37

File tree

1 file changed

+61
-52
lines changed

1 file changed

+61
-52
lines changed

drivers/net/usb/smsc95xx.c

Lines changed: 61 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <linux/usb/usbnet.h>
1919
#include <linux/slab.h>
2020
#include <linux/of_net.h>
21+
#include <linux/irq.h>
22+
#include <linux/irqdomain.h>
2123
#include <linux/mdio.h>
2224
#include <linux/phy.h>
2325
#include <net/selftests.h>
@@ -53,6 +55,9 @@
5355
#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
5456
SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
5557

58+
#define SMSC95XX_NR_IRQS (1) /* raise to 12 for GPIOs */
59+
#define PHY_HWIRQ (SMSC95XX_NR_IRQS - 1)
60+
5661
struct smsc95xx_priv {
5762
u32 mac_cr;
5863
u32 hash_hi;
@@ -61,6 +66,9 @@ struct smsc95xx_priv {
6166
spinlock_t mac_cr_lock;
6267
u8 features;
6368
u8 suspend_flags;
69+
struct irq_chip irqchip;
70+
struct irq_domain *irqdomain;
71+
struct fwnode_handle *irqfwnode;
6472
struct mii_bus *mdiobus;
6573
struct phy_device *phydev;
6674
};
@@ -597,6 +605,8 @@ static void smsc95xx_mac_update_fullduplex(struct usbnet *dev)
597605

598606
static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
599607
{
608+
struct smsc95xx_priv *pdata = dev->driver_priv;
609+
unsigned long flags;
600610
u32 intdata;
601611

602612
if (urb->actual_length != 4) {
@@ -608,11 +618,15 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
608618
intdata = get_unaligned_le32(urb->transfer_buffer);
609619
netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata);
610620

621+
local_irq_save(flags);
622+
611623
if (intdata & INT_ENP_PHY_INT_)
612-
;
624+
generic_handle_domain_irq(pdata->irqdomain, PHY_HWIRQ);
613625
else
614626
netdev_warn(dev->net, "unexpected interrupt, intdata=0x%08X\n",
615627
intdata);
628+
629+
local_irq_restore(flags);
616630
}
617631

618632
/* Enable or disable Tx & Rx checksum offload engines */
@@ -1080,8 +1094,9 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
10801094
{
10811095
struct smsc95xx_priv *pdata;
10821096
bool is_internal_phy;
1097+
char usb_path[64];
1098+
int ret, phy_irq;
10831099
u32 val;
1084-
int ret;
10851100

10861101
printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n");
10871102

@@ -1121,10 +1136,38 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
11211136
if (ret)
11221137
goto free_pdata;
11231138

1139+
/* create irq domain for use by PHY driver and GPIO consumers */
1140+
usb_make_path(dev->udev, usb_path, sizeof(usb_path));
1141+
pdata->irqfwnode = irq_domain_alloc_named_fwnode(usb_path);
1142+
if (!pdata->irqfwnode) {
1143+
ret = -ENOMEM;
1144+
goto free_pdata;
1145+
}
1146+
1147+
pdata->irqdomain = irq_domain_create_linear(pdata->irqfwnode,
1148+
SMSC95XX_NR_IRQS,
1149+
&irq_domain_simple_ops,
1150+
pdata);
1151+
if (!pdata->irqdomain) {
1152+
ret = -ENOMEM;
1153+
goto free_irqfwnode;
1154+
}
1155+
1156+
phy_irq = irq_create_mapping(pdata->irqdomain, PHY_HWIRQ);
1157+
if (!phy_irq) {
1158+
ret = -ENOENT;
1159+
goto remove_irqdomain;
1160+
}
1161+
1162+
pdata->irqchip = dummy_irq_chip;
1163+
pdata->irqchip.name = SMSC_CHIPNAME;
1164+
irq_set_chip_and_handler_name(phy_irq, &pdata->irqchip,
1165+
handle_simple_irq, "phy");
1166+
11241167
pdata->mdiobus = mdiobus_alloc();
11251168
if (!pdata->mdiobus) {
11261169
ret = -ENOMEM;
1127-
goto free_pdata;
1170+
goto dispose_irq;
11281171
}
11291172

11301173
ret = smsc95xx_read_reg(dev, HW_CFG, &val);
@@ -1157,6 +1200,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
11571200
goto unregister_mdio;
11581201
}
11591202

1203+
pdata->phydev->irq = phy_irq;
11601204
pdata->phydev->is_internal = is_internal_phy;
11611205

11621206
/* detect device revision as different features may be available */
@@ -1199,6 +1243,15 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
11991243
free_mdio:
12001244
mdiobus_free(pdata->mdiobus);
12011245

1246+
dispose_irq:
1247+
irq_dispose_mapping(phy_irq);
1248+
1249+
remove_irqdomain:
1250+
irq_domain_remove(pdata->irqdomain);
1251+
1252+
free_irqfwnode:
1253+
irq_domain_free_fwnode(pdata->irqfwnode);
1254+
12021255
free_pdata:
12031256
kfree(pdata);
12041257
return ret;
@@ -1211,6 +1264,9 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
12111264
phy_disconnect(dev->net->phydev);
12121265
mdiobus_unregister(pdata->mdiobus);
12131266
mdiobus_free(pdata->mdiobus);
1267+
irq_dispose_mapping(irq_find_mapping(pdata->irqdomain, PHY_HWIRQ));
1268+
irq_domain_remove(pdata->irqdomain);
1269+
irq_domain_free_fwnode(pdata->irqfwnode);
12141270
netif_dbg(dev, ifdown, dev->net, "free pdata\n");
12151271
kfree(pdata);
12161272
}
@@ -1235,29 +1291,6 @@ static u32 smsc_crc(const u8 *buffer, size_t len, int filter)
12351291
return crc << ((filter % 2) * 16);
12361292
}
12371293

1238-
static int smsc95xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask)
1239-
{
1240-
int ret;
1241-
1242-
netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n");
1243-
1244-
/* read to clear */
1245-
ret = smsc95xx_mdio_read_nopm(dev, PHY_INT_SRC);
1246-
if (ret < 0)
1247-
return ret;
1248-
1249-
/* enable interrupt source */
1250-
ret = smsc95xx_mdio_read_nopm(dev, PHY_INT_MASK);
1251-
if (ret < 0)
1252-
return ret;
1253-
1254-
ret |= mask;
1255-
1256-
smsc95xx_mdio_write_nopm(dev, PHY_INT_MASK, ret);
1257-
1258-
return 0;
1259-
}
1260-
12611294
static int smsc95xx_link_ok_nopm(struct usbnet *dev)
12621295
{
12631296
int ret;
@@ -1424,7 +1457,6 @@ static int smsc95xx_enter_suspend3(struct usbnet *dev)
14241457
static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up)
14251458
{
14261459
struct smsc95xx_priv *pdata = dev->driver_priv;
1427-
int ret;
14281460

14291461
if (!netif_running(dev->net)) {
14301462
/* interface is ifconfig down so fully power down hw */
@@ -1443,27 +1475,10 @@ static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up)
14431475
}
14441476

14451477
netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n");
1446-
1447-
/* enable PHY wakeup events for if cable is attached */
1448-
ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
1449-
PHY_INT_MASK_ANEG_COMP_);
1450-
if (ret < 0) {
1451-
netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
1452-
return ret;
1453-
}
1454-
14551478
netdev_info(dev->net, "entering SUSPEND1 mode\n");
14561479
return smsc95xx_enter_suspend1(dev);
14571480
}
14581481

1459-
/* enable PHY wakeup events so we remote wakeup if cable is pulled */
1460-
ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
1461-
PHY_INT_MASK_LINK_DOWN_);
1462-
if (ret < 0) {
1463-
netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
1464-
return ret;
1465-
}
1466-
14671482
netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n");
14681483
return smsc95xx_enter_suspend3(dev);
14691484
}
@@ -1529,13 +1544,6 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
15291544
}
15301545

15311546
if (pdata->wolopts & WAKE_PHY) {
1532-
ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
1533-
(PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_LINK_DOWN_));
1534-
if (ret < 0) {
1535-
netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
1536-
goto done;
1537-
}
1538-
15391547
/* if link is down then configure EDPD and enter SUSPEND1,
15401548
* otherwise enter SUSPEND0 below
15411549
*/
@@ -1769,11 +1777,12 @@ static int smsc95xx_resume(struct usb_interface *intf)
17691777
return ret;
17701778
}
17711779

1780+
phy_init_hw(pdata->phydev);
1781+
17721782
ret = usbnet_resume(intf);
17731783
if (ret < 0)
17741784
netdev_warn(dev->net, "usbnet_resume error\n");
17751785

1776-
phy_init_hw(pdata->phydev);
17771786
return ret;
17781787
}
17791788

0 commit comments

Comments
 (0)