Commit 06254738 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

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: default avatarJakub Kicinski <kuba@kernel.org>
parents 470dfd80 6527b938
Loading
Loading
Loading
Loading
+36 −2
Original line number Diff line number Diff line
@@ -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,8 +1201,30 @@ 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)
@@ -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)
+37 −17
Original line number Diff line number Diff line
@@ -119,21 +119,6 @@ struct dp83822_private {
	u16 fx_sd_enable;
};

static int dp83822_ack_interrupt(struct phy_device *phydev)
{
	int err;

	err = phy_read(phydev, MII_DP83822_MISR1);
	if (err < 0)
		return err;

	err = phy_read(phydev, MII_DP83822_MISR2);
	if (err < 0)
		return err;

	return 0;
}

static int dp83822_set_wol(struct phy_device *phydev,
			   struct ethtool_wolinfo *wol)
{
@@ -303,6 +288,41 @@ static int dp83822_config_intr(struct phy_device *phydev)
	return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status);
}

static irqreturn_t dp83822_handle_interrupt(struct phy_device *phydev)
{
	int irq_status;

	/* The MISR1 and MISR2 registers are holding the interrupt status in
	 * the upper half (15:8), while the lower half (7:0) is used for
	 * controlling the interrupt enable state of those individual interrupt
	 * sources. To determine the possible interrupt sources, just read the
	 * MISR* register and use it directly to know which interrupts have
	 * been enabled previously or not.
	 */
	irq_status = phy_read(phydev, MII_DP83822_MISR1);
	if (irq_status < 0) {
		phy_error(phydev);
		return IRQ_NONE;
	}
	if (irq_status & ((irq_status & GENMASK(7, 0)) << 8))
		goto trigger_machine;

	irq_status = phy_read(phydev, MII_DP83822_MISR2);
	if (irq_status < 0) {
		phy_error(phydev);
		return IRQ_NONE;
	}
	if (irq_status & ((irq_status & GENMASK(7, 0)) << 8))
		goto trigger_machine;

	return IRQ_NONE;

trigger_machine:
	phy_trigger_machine(phydev);

	return IRQ_HANDLED;
}

static int dp8382x_disable_wol(struct phy_device *phydev)
{
	int value = DP83822_WOL_EN | DP83822_WOL_MAGIC_EN |
@@ -574,8 +594,8 @@ static int dp83822_resume(struct phy_device *phydev)
		.read_status	= dp83822_read_status,		\
		.get_wol = dp83822_get_wol,			\
		.set_wol = dp83822_set_wol,			\
		.ack_interrupt = dp83822_ack_interrupt,		\
		.config_intr = dp83822_config_intr,		\
		.handle_interrupt = dp83822_handle_interrupt,	\
		.suspend = dp83822_suspend,			\
		.resume = dp83822_resume,			\
	}
@@ -589,8 +609,8 @@ static int dp83822_resume(struct phy_device *phydev)
		.config_init	= dp8382x_config_init,		\
		.get_wol = dp83822_get_wol,			\
		.set_wol = dp83822_set_wol,			\
		.ack_interrupt = dp83822_ack_interrupt,		\
		.config_intr = dp83822_config_intr,		\
		.handle_interrupt = dp83822_handle_interrupt,	\
		.suspend = dp83822_suspend,			\
		.resume = dp83822_resume,			\
	}
+45 −2
Original line number Diff line number Diff line
@@ -37,6 +37,20 @@
	 DP83848_MISR_SPD_INT_EN |	\
	 DP83848_MISR_LINK_INT_EN)

#define DP83848_MISR_RHF_INT		BIT(8)
#define DP83848_MISR_FHF_INT		BIT(9)
#define DP83848_MISR_ANC_INT		BIT(10)
#define DP83848_MISR_DUP_INT		BIT(11)
#define DP83848_MISR_SPD_INT		BIT(12)
#define DP83848_MISR_LINK_INT		BIT(13)
#define DP83848_MISR_ED_INT		BIT(14)

#define DP83848_INT_MASK		\
	(DP83848_MISR_ANC_INT |	\
	 DP83848_MISR_DUP_INT |	\
	 DP83848_MISR_SPD_INT |	\
	 DP83848_MISR_LINK_INT)

static int dp83848_ack_interrupt(struct phy_device *phydev)
{
	int err = phy_read(phydev, DP83848_MISR);
@@ -53,17 +67,46 @@ static int dp83848_config_intr(struct phy_device *phydev)
		return control;

	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
		ret = dp83848_ack_interrupt(phydev);
		if (ret)
			return ret;

		control |= DP83848_MICR_INT_OE;
		control |= DP83848_MICR_INTEN;

		ret = phy_write(phydev, DP83848_MISR, DP83848_INT_EN_MASK);
		if (ret < 0)
			return ret;

		ret = phy_write(phydev, DP83848_MICR, control);
	} else {
		control &= ~DP83848_MICR_INTEN;
		ret = phy_write(phydev, DP83848_MICR, control);
		if (ret)
			return ret;

		ret = dp83848_ack_interrupt(phydev);
	}

	return phy_write(phydev, DP83848_MICR, control);
	return ret;
}

static irqreturn_t dp83848_handle_interrupt(struct phy_device *phydev)
{
	int irq_status;

	irq_status = phy_read(phydev, DP83848_MISR);
	if (irq_status < 0) {
		phy_error(phydev);
		return IRQ_NONE;
	}

	if (!(irq_status & DP83848_INT_MASK))
		return IRQ_NONE;

	phy_trigger_machine(phydev);

	return IRQ_HANDLED;
}

static int dp83848_config_init(struct phy_device *phydev)
@@ -102,8 +145,8 @@ MODULE_DEVICE_TABLE(mdio, dp83848_tbl);
		.resume		= genphy_resume,		\
								\
		/* IRQ related */				\
		.ack_interrupt	= dp83848_ack_interrupt,	\
		.config_intr	= dp83848_config_intr,		\
		.handle_interrupt = dp83848_handle_interrupt,	\
	}

static struct phy_driver dp83848_driver[] = {
+39 −5
Original line number Diff line number Diff line
@@ -288,9 +288,13 @@ static void dp83867_get_wol(struct phy_device *phydev,

static int dp83867_config_intr(struct phy_device *phydev)
{
	int micr_status;
	int micr_status, err;

	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
		err = dp83867_ack_interrupt(phydev);
		if (err)
			return err;

		micr_status = phy_read(phydev, MII_DP83867_MICR);
		if (micr_status < 0)
			return micr_status;
@@ -303,11 +307,41 @@ static int dp83867_config_intr(struct phy_device *phydev)
			MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN |
			MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN);

		return phy_write(phydev, MII_DP83867_MICR, micr_status);
		err = phy_write(phydev, MII_DP83867_MICR, micr_status);
	} else {
		micr_status = 0x0;
		err = phy_write(phydev, MII_DP83867_MICR, micr_status);
		if (err)
			return err;

		err = dp83867_ack_interrupt(phydev);
	}

	micr_status = 0x0;
	return phy_write(phydev, MII_DP83867_MICR, micr_status);
	return err;
}

static irqreturn_t dp83867_handle_interrupt(struct phy_device *phydev)
{
	int irq_status, irq_enabled;

	irq_status = phy_read(phydev, MII_DP83867_ISR);
	if (irq_status < 0) {
		phy_error(phydev);
		return IRQ_NONE;
	}

	irq_enabled = phy_read(phydev, MII_DP83867_MICR);
	if (irq_enabled < 0) {
		phy_error(phydev);
		return IRQ_NONE;
	}

	if (!(irq_status & irq_enabled))
		return IRQ_NONE;

	phy_trigger_machine(phydev);

	return IRQ_HANDLED;
}

static int dp83867_read_status(struct phy_device *phydev)
@@ -825,8 +859,8 @@ static struct phy_driver dp83867_driver[] = {
		.set_wol	= dp83867_set_wol,

		/* IRQ related */
		.ack_interrupt	= dp83867_ack_interrupt,
		.config_intr	= dp83867_config_intr,
		.handle_interrupt = dp83867_handle_interrupt,

		.suspend	= genphy_suspend,
		.resume		= genphy_resume,
+38 −4
Original line number Diff line number Diff line
@@ -186,9 +186,13 @@ static int dp83869_ack_interrupt(struct phy_device *phydev)

static int dp83869_config_intr(struct phy_device *phydev)
{
	int micr_status = 0;
	int micr_status = 0, err;

	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
		err = dp83869_ack_interrupt(phydev);
		if (err)
			return err;

		micr_status = phy_read(phydev, MII_DP83869_MICR);
		if (micr_status < 0)
			return micr_status;
@@ -201,10 +205,40 @@ static int dp83869_config_intr(struct phy_device *phydev)
			MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN |
			MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN);

		return phy_write(phydev, MII_DP83869_MICR, micr_status);
		err = phy_write(phydev, MII_DP83869_MICR, micr_status);
	} else {
		err = phy_write(phydev, MII_DP83869_MICR, micr_status);
		if (err)
			return err;

		err = dp83869_ack_interrupt(phydev);
	}

	return err;
}

static irqreturn_t dp83869_handle_interrupt(struct phy_device *phydev)
{
	int irq_status, irq_enabled;

	irq_status = phy_read(phydev, MII_DP83869_ISR);
	if (irq_status < 0) {
		phy_error(phydev);
		return IRQ_NONE;
	}

	return phy_write(phydev, MII_DP83869_MICR, micr_status);
	irq_enabled = phy_read(phydev, MII_DP83869_MICR);
	if (irq_enabled < 0) {
		phy_error(phydev);
		return IRQ_NONE;
	}

	if (!(irq_status & irq_enabled))
		return IRQ_NONE;

	phy_trigger_machine(phydev);

	return IRQ_HANDLED;
}

static int dp83869_set_wol(struct phy_device *phydev,
@@ -850,8 +884,8 @@ static struct phy_driver dp83869_driver[] = {
		.soft_reset	= dp83869_phy_reset,

		/* IRQ related */
		.ack_interrupt	= dp83869_ack_interrupt,
		.config_intr	= dp83869_config_intr,
		.handle_interrupt = dp83869_handle_interrupt,
		.read_status	= dp83869_read_status,

		.get_tunable	= dp83869_get_tunable,
Loading