net: phy: improve phy_driver callback handle_interrupt
authorHeiner Kallweit <hkallweit1@gmail.com>
Mon, 16 Mar 2020 21:32:33 +0000 (22:32 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 18 Mar 2020 03:58:22 +0000 (20:58 -0700)
did_interrupt() clears the interrupt, therefore handle_interrupt() can
not check which event triggered the interrupt. To overcome this
constraint and allow more flexibility for customer interrupt handlers,
let's decouple handle_interrupt() from parts of the phylib interrupt
handling. Custom interrupt handlers now have to implement the
did_interrupt() functionality in handle_interrupt() if needed.

Fortunately we have just one custom interrupt handler so far (in the
mscc PHY driver), convert it to the changed API.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/mscc/mscc_main.c
drivers/net/phy/phy.c
include/linux/phy.h

index cb4d65f..4727aba 100644 (file)
@@ -1429,11 +1429,18 @@ err:
        return ret;
 }
 
-static int vsc8584_handle_interrupt(struct phy_device *phydev)
+static irqreturn_t vsc8584_handle_interrupt(struct phy_device *phydev)
 {
+       int irq_status;
+
+       irq_status = phy_read(phydev, MII_VSC85XX_INT_STATUS);
+       if (irq_status < 0 || !(irq_status & MII_VSC85XX_INT_MASK_MASK))
+               return IRQ_NONE;
+
        vsc8584_handle_macsec_interrupt(phydev);
        phy_mac_interrupt(phydev);
-       return 0;
+
+       return IRQ_HANDLED;
 }
 
 static int vsc85xx_config_init(struct phy_device *phydev)
index 355bfde..d71212a 100644 (file)
@@ -715,26 +715,24 @@ static int phy_disable_interrupts(struct phy_device *phydev)
 static irqreturn_t phy_interrupt(int irq, void *phy_dat)
 {
        struct phy_device *phydev = phy_dat;
+       struct phy_driver *drv = phydev->drv;
 
-       if (phydev->drv->did_interrupt && !phydev->drv->did_interrupt(phydev))
+       if (drv->handle_interrupt)
+               return drv->handle_interrupt(phydev);
+
+       if (drv->did_interrupt && !drv->did_interrupt(phydev))
                return IRQ_NONE;
 
-       if (phydev->drv->handle_interrupt) {
-               if (phydev->drv->handle_interrupt(phydev))
-                       goto phy_err;
-       } else {
-               /* reschedule state queue work to run as soon as possible */
-               phy_trigger_machine(phydev);
-       }
+       /* reschedule state queue work to run as soon as possible */
+       phy_trigger_machine(phydev);
 
        /* did_interrupt() may have cleared the interrupt already */
-       if (!phydev->drv->did_interrupt && phy_clear_interrupt(phydev))
-               goto phy_err;
-       return IRQ_HANDLED;
+       if (!drv->did_interrupt && phy_clear_interrupt(phydev)) {
+               phy_error(phydev);
+               return IRQ_NONE;
+       }
 
-phy_err:
-       phy_error(phydev);
-       return IRQ_NONE;
+       return IRQ_HANDLED;
 }
 
 /**
index 6b872ae..cb5a218 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/workqueue.h>
 #include <linux/mod_devicetable.h>
 #include <linux/u64_stats_sync.h>
+#include <linux/irqreturn.h>
 
 #include <linux/atomic.h>
 
@@ -568,7 +569,7 @@ struct phy_driver {
        int (*did_interrupt)(struct phy_device *phydev);
 
        /* Override default interrupt handling */
-       int (*handle_interrupt)(struct phy_device *phydev);
+       irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
 
        /* Clears up any memory if needed */
        void (*remove)(struct phy_device *phydev);