summaryrefslogtreecommitdiffstats
path: root/drivers/net/phy/dp83640.c
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2020-11-25 20:18:41 +0100
committerJakub Kicinski <kuba@kernel.org>2020-11-25 20:18:41 +0100
commit062547380d1fa95a5d37573bf22fd219f021dfd0 (patch)
treec273ad1ea912f8519463a34f956aa90ba49b5b3a /drivers/net/phy/dp83640.c
parentlan743x: replace polling loop by wait_event_timeout() (diff)
parentnet: phy: remove the .did_interrupt() and .ack_interrupt() callback (diff)
downloadlinux-062547380d1fa95a5d37573bf22fd219f021dfd0.tar.xz
linux-062547380d1fa95a5d37573bf22fd219f021dfd0.zip
Merge branch 'net-phy-add-support-for-shared-interrupts-part-3'
Ioana Ciornei says: ==================== net: phy: add support for shared interrupts (part 3) This patch set aims to actually add support for shared interrupts in phylib and not only for multi-PHY devices. While we are at it, streamline the interrupt handling in phylib. For a bit of context, at the moment, there are multiple phy_driver ops that deal with this subject: - .config_intr() - Enable/disable the interrupt line. - .ack_interrupt() - Should quiesce any interrupts that may have been fired. It's also used by phylib in conjunction with .config_intr() to clear any pending interrupts after the line was disabled, and before it is going to be enabled. - .did_interrupt() - Intended for multi-PHY devices with a shared IRQ line and used by phylib to discern which PHY from the package was the one that actually fired the interrupt. - .handle_interrupt() - Completely overrides the default interrupt handling logic from phylib. The PHY driver is responsible for checking if any interrupt was fired by the respective PHY and choose accordingly if it's the one that should trigger the link state machine. From my point of view, the interrupt handling in phylib has become somewhat confusing with all these callbacks that actually read the same PHY register - the interrupt status. A more streamlined approach would be to just move the responsibility to write an interrupt handler to the driver (as any other device driver does) and make .handle_interrupt() the only way to deal with interrupts. Another advantage with this approach would be that phylib would gain support for shared IRQs between different PHY (not just multi-PHY devices), something which at the moment would require extending every PHY driver anyway in order to implement their .did_interrupt() callback and duplicate the same logic as in .ack_interrupt(). The disadvantage of making .did_interrupt() mandatory would be that we are slightly changing the semantics of the phylib API and that would increase confusion instead of reducing it. What I am proposing is the following: - As a first step, make the .ack_interrupt() callback optional so that we do not break any PHY driver amid the transition. - Every PHY driver gains a .handle_interrupt() implementation that, for the most part, would look like below: irq_status = phy_read(phydev, INTR_STATUS); if (irq_status < 0) { phy_error(phydev); return IRQ_NONE; } if (!(irq_status & irq_mask)) return IRQ_NONE; phy_trigger_machine(phydev); return IRQ_HANDLED; - Remove each PHY driver's implementation of the .ack_interrupt() by actually taking care of quiescing any pending interrupts before enabling/after disabling the interrupt line. - Finally, after all drivers have been ported, remove the .ack_interrupt() and .did_interrupt() callbacks from phy_driver. This patch set is part 3 (and final) of the entire change set and it addresses the remaining PHY drivers that have not been migrated previosly. Also, it finally removed the .did_interrupt() and .ack_interrupt() callbacks since they are of no use anymore. I do not have access to most of these PHY's, therefore I Cc-ed the latest contributors to the individual PHY drivers in order to have access, hopefully, to more regression testing. ==================== Link: https://lore.kernel.org/r/20201123153817.1616814-1-ciorneiioana@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers/net/phy/dp83640.c')
-rw-r--r--drivers/net/phy/dp83640.c38
1 files changed, 36 insertions, 2 deletions
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index f2caccaf4408..f1001ae1df51 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -50,6 +50,14 @@
#define MII_DP83640_MISR_LINK_INT_EN 0x20
#define MII_DP83640_MISR_ED_INT_EN 0x40
#define MII_DP83640_MISR_LQ_INT_EN 0x80
+#define MII_DP83640_MISR_ANC_INT 0x400
+#define MII_DP83640_MISR_DUP_INT 0x800
+#define MII_DP83640_MISR_SPD_INT 0x1000
+#define MII_DP83640_MISR_LINK_INT 0x2000
+#define MII_DP83640_MISR_INT_MASK (MII_DP83640_MISR_ANC_INT |\
+ MII_DP83640_MISR_DUP_INT |\
+ MII_DP83640_MISR_SPD_INT |\
+ MII_DP83640_MISR_LINK_INT)
/* phyter seems to miss the mark by 16 ns */
#define ADJTIME_FIX 16
@@ -1151,6 +1159,10 @@ static int dp83640_config_intr(struct phy_device *phydev)
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ err = dp83640_ack_interrupt(phydev);
+ if (err)
+ return err;
+
misr = phy_read(phydev, MII_DP83640_MISR);
if (misr < 0)
return misr;
@@ -1189,10 +1201,32 @@ static int dp83640_config_intr(struct phy_device *phydev)
MII_DP83640_MISR_DUP_INT_EN |
MII_DP83640_MISR_SPD_INT_EN |
MII_DP83640_MISR_LINK_INT_EN);
- return phy_write(phydev, MII_DP83640_MISR, misr);
+ err = phy_write(phydev, MII_DP83640_MISR, misr);
+ if (err)
+ return err;
+
+ return dp83640_ack_interrupt(phydev);
}
}
+static irqreturn_t dp83640_handle_interrupt(struct phy_device *phydev)
+{
+ int irq_status;
+
+ irq_status = phy_read(phydev, MII_DP83640_MISR);
+ if (irq_status < 0) {
+ phy_error(phydev);
+ return IRQ_NONE;
+ }
+
+ if (!(irq_status & MII_DP83640_MISR_INT_MASK))
+ return IRQ_NONE;
+
+ phy_trigger_machine(phydev);
+
+ return IRQ_HANDLED;
+}
+
static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr)
{
struct dp83640_private *dp83640 =
@@ -1515,8 +1549,8 @@ static struct phy_driver dp83640_driver = {
.remove = dp83640_remove,
.soft_reset = dp83640_soft_reset,
.config_init = dp83640_config_init,
- .ack_interrupt = dp83640_ack_interrupt,
.config_intr = dp83640_config_intr,
+ .handle_interrupt = dp83640_handle_interrupt,
};
static int __init dp83640_init(void)