Commit 0676a4ea authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-phy-add-support-for-shared-interrupts-part-2'

Ioana Ciornei says:

====================
net: phy: add support for shared interrupts (part 2)

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 2 of the entire change set and it addresses the
changes needed in 9 PHY drivers. The rest can be found on my Github
branch here:
https://github.com/IoanaCiornei/linux/commits/phylib-shared-irq
====================

Link: https://lore.kernel.org/r/20201113165226.561153-1-ciorneiioana@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 97f53a08 1d8300d3
......@@ -471,12 +471,43 @@ static int adin_phy_ack_intr(struct phy_device *phydev)
static int adin_phy_config_intr(struct phy_device *phydev)
{
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
return phy_set_bits(phydev, ADIN1300_INT_MASK_REG,
ADIN1300_INT_MASK_EN);
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
err = adin_phy_ack_intr(phydev);
if (err)
return err;
err = phy_set_bits(phydev, ADIN1300_INT_MASK_REG,
ADIN1300_INT_MASK_EN);
} else {
err = phy_clear_bits(phydev, ADIN1300_INT_MASK_REG,
ADIN1300_INT_MASK_EN);
if (err)
return err;
err = adin_phy_ack_intr(phydev);
}
return err;
}
static irqreturn_t adin_phy_handle_interrupt(struct phy_device *phydev)
{
int irq_status;
irq_status = phy_read(phydev, ADIN1300_INT_STATUS_REG);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (!(irq_status & ADIN1300_INT_LINK_STAT_CHNG_EN))
return IRQ_NONE;
phy_trigger_machine(phydev);
return phy_clear_bits(phydev, ADIN1300_INT_MASK_REG,
ADIN1300_INT_MASK_EN);
return IRQ_HANDLED;
}
static int adin_cl45_to_adin_reg(struct phy_device *phydev, int devad,
......@@ -877,8 +908,8 @@ static struct phy_driver adin_driver[] = {
.read_status = adin_read_status,
.get_tunable = adin_get_tunable,
.set_tunable = adin_set_tunable,
.ack_interrupt = adin_phy_ack_intr,
.config_intr = adin_phy_config_intr,
.handle_interrupt = adin_phy_handle_interrupt,
.get_sset_count = adin_get_sset_count,
.get_strings = adin_get_strings,
.get_stats = adin_get_stats,
......@@ -900,8 +931,8 @@ static struct phy_driver adin_driver[] = {
.read_status = adin_read_status,
.get_tunable = adin_get_tunable,
.set_tunable = adin_set_tunable,
.ack_interrupt = adin_phy_ack_intr,
.config_intr = adin_phy_config_intr,
.handle_interrupt = adin_phy_handle_interrupt,
.get_sset_count = adin_get_sset_count,
.get_strings = adin_get_strings,
.get_stats = adin_get_stats,
......
......@@ -20,6 +20,10 @@
#define MII_AM79C_IR_EN_ANEG 0x0100 /* IR enable Aneg Complete */
#define MII_AM79C_IR_IMASK_INIT (MII_AM79C_IR_EN_LINK | MII_AM79C_IR_EN_ANEG)
#define MII_AM79C_IR_LINK_DOWN BIT(2)
#define MII_AM79C_IR_ANEG_DONE BIT(0)
#define MII_AM79C_IR_IMASK_STAT (MII_AM79C_IR_LINK_DOWN | MII_AM79C_IR_ANEG_DONE)
MODULE_DESCRIPTION("AMD PHY driver");
MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
MODULE_LICENSE("GPL");
......@@ -48,22 +52,49 @@ static int am79c_config_intr(struct phy_device *phydev)
{
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
err = am79c_ack_interrupt(phydev);
if (err)
return err;
err = phy_write(phydev, MII_AM79C_IR, MII_AM79C_IR_IMASK_INIT);
else
} else {
err = phy_write(phydev, MII_AM79C_IR, 0);
if (err)
return err;
err = am79c_ack_interrupt(phydev);
}
return err;
}
static irqreturn_t am79c_handle_interrupt(struct phy_device *phydev)
{
int irq_status;
irq_status = phy_read(phydev, MII_AM79C_IR);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (!(irq_status & MII_AM79C_IR_IMASK_STAT))
return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static struct phy_driver am79c_driver[] = { {
.phy_id = PHY_ID_AM79C874,
.name = "AM79C874",
.phy_id_mask = 0xfffffff0,
/* PHY_BASIC_FEATURES */
.config_init = am79c_config_init,
.ack_interrupt = am79c_ack_interrupt,
.config_intr = am79c_config_intr,
.handle_interrupt = am79c_handle_interrupt,
} };
module_phy_driver(am79c_driver);
......
......@@ -37,6 +37,8 @@
#define MII_LXT970_ISR 18 /* Interrupt Status Register */
#define MII_LXT970_IRS_MINT BIT(15)
#define MII_LXT970_CONFIG 19 /* Configuration Register */
/* ------------------------------------------------------------------------- */
......@@ -47,6 +49,7 @@
#define MII_LXT971_IER_IEN 0x00f2
#define MII_LXT971_ISR 19 /* Interrupt Status Register */
#define MII_LXT971_ISR_MASK 0x00f0
/* register definitions for the 973 */
#define MII_LXT973_PCR 16 /* Port Configuration Register */
......@@ -75,10 +78,50 @@ static int lxt970_ack_interrupt(struct phy_device *phydev)
static int lxt970_config_intr(struct phy_device *phydev)
{
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
return phy_write(phydev, MII_LXT970_IER, MII_LXT970_IER_IEN);
else
return phy_write(phydev, MII_LXT970_IER, 0);
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
err = lxt970_ack_interrupt(phydev);
if (err)
return err;
err = phy_write(phydev, MII_LXT970_IER, MII_LXT970_IER_IEN);
} else {
err = phy_write(phydev, MII_LXT970_IER, 0);
if (err)
return err;
err = lxt970_ack_interrupt(phydev);
}
return err;
}
static irqreturn_t lxt970_handle_interrupt(struct phy_device *phydev)
{
int irq_status;
/* The interrupt status register is cleared by reading BMSR
* followed by MII_LXT970_ISR
*/
irq_status = phy_read(phydev, MII_BMSR);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
irq_status = phy_read(phydev, MII_LXT970_ISR);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (!(irq_status & MII_LXT970_IRS_MINT))
return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static int lxt970_config_init(struct phy_device *phydev)
......@@ -99,10 +142,41 @@ static int lxt971_ack_interrupt(struct phy_device *phydev)
static int lxt971_config_intr(struct phy_device *phydev)
{
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
return phy_write(phydev, MII_LXT971_IER, MII_LXT971_IER_IEN);
else
return phy_write(phydev, MII_LXT971_IER, 0);
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
err = lxt971_ack_interrupt(phydev);
if (err)
return err;
err = phy_write(phydev, MII_LXT971_IER, MII_LXT971_IER_IEN);
} else {
err = phy_write(phydev, MII_LXT971_IER, 0);
if (err)
return err;
err = lxt971_ack_interrupt(phydev);
}
return err;
}
static irqreturn_t lxt971_handle_interrupt(struct phy_device *phydev)
{
int irq_status;
irq_status = phy_read(phydev, MII_LXT971_ISR);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (!(irq_status & MII_LXT971_ISR_MASK))
return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
/*
......@@ -237,15 +311,15 @@ static struct phy_driver lxt97x_driver[] = {
.phy_id_mask = 0xfffffff0,
/* PHY_BASIC_FEATURES */
.config_init = lxt970_config_init,
.ack_interrupt = lxt970_ack_interrupt,
.config_intr = lxt970_config_intr,
.handle_interrupt = lxt970_handle_interrupt,
}, {
.phy_id = 0x001378e0,
.name = "LXT971",
.phy_id_mask = 0xfffffff0,
/* PHY_BASIC_FEATURES */
.ack_interrupt = lxt971_ack_interrupt,
.config_intr = lxt971_config_intr,
.handle_interrupt = lxt971_handle_interrupt,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
......
......@@ -317,16 +317,43 @@ static int marvell_config_intr(struct phy_device *phydev)
{
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
err = marvell_ack_interrupt(phydev);
if (err)
return err;
err = phy_write(phydev, MII_M1011_IMASK,
MII_M1011_IMASK_INIT);
else
} else {
err = phy_write(phydev, MII_M1011_IMASK,
MII_M1011_IMASK_CLEAR);
if (err)
return err;
err = marvell_ack_interrupt(phydev);
}
return err;
}
static irqreturn_t marvell_handle_interrupt(struct phy_device *phydev)
{
int irq_status;
irq_status = phy_read(phydev, MII_M1011_IEVENT);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (!(irq_status & MII_M1011_IMASK_INIT))
return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static int marvell_set_polarity(struct phy_device *phydev, int polarity)
{
int reg;
......@@ -1659,18 +1686,6 @@ static int marvell_aneg_done(struct phy_device *phydev)
return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
}
static int m88e1121_did_interrupt(struct phy_device *phydev)
{
int imask;
imask = phy_read(phydev, MII_M1011_IEVENT);
if (imask & MII_M1011_IMASK_INIT)
return 1;
return 0;
}
static void m88e1318_get_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
......@@ -2697,8 +2712,8 @@ static struct phy_driver marvell_drivers[] = {
.probe = marvell_probe,
.config_init = marvell_config_init,
.config_aneg = m88e1101_config_aneg,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2715,8 +2730,8 @@ static struct phy_driver marvell_drivers[] = {
.probe = marvell_probe,
.config_init = m88e1111_config_init,
.config_aneg = marvell_config_aneg,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2736,8 +2751,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = m88e1111_config_init,
.config_aneg = m88e1111_config_aneg,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2757,8 +2772,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = m88e1111_config_init,
.config_aneg = m88e1111_config_aneg,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2777,8 +2792,8 @@ static struct phy_driver marvell_drivers[] = {
.probe = marvell_probe,
.config_init = m88e1118_config_init,
.config_aneg = m88e1118_config_aneg,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2796,9 +2811,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = marvell_config_init,
.config_aneg = m88e1121_config_aneg,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.did_interrupt = m88e1121_did_interrupt,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2818,9 +2832,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = m88e1318_config_init,
.config_aneg = m88e1318_config_aneg,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.did_interrupt = m88e1121_did_interrupt,
.handle_interrupt = marvell_handle_interrupt,
.get_wol = m88e1318_get_wol,
.set_wol = m88e1318_set_wol,
.resume = genphy_resume,
......@@ -2840,8 +2853,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = m88e1145_config_init,
.config_aneg = m88e1101_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2860,8 +2873,8 @@ static struct phy_driver marvell_drivers[] = {
.probe = marvell_probe,
.config_init = m88e1149_config_init,
.config_aneg = m88e1118_config_aneg,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2878,8 +2891,8 @@ static struct phy_driver marvell_drivers[] = {
.probe = marvell_probe,
.config_init = m88e1111_config_init,
.config_aneg = marvell_config_aneg,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2895,8 +2908,8 @@ static struct phy_driver marvell_drivers[] = {
/* PHY_GBIT_FEATURES */
.probe = marvell_probe,
.config_init = m88e1116r_config_init,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2917,9 +2930,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = m88e1510_config_init,
.config_aneg = m88e1510_config_aneg,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.did_interrupt = m88e1121_did_interrupt,
.handle_interrupt = marvell_handle_interrupt,
.get_wol = m88e1318_get_wol,
.set_wol = m88e1318_set_wol,
.resume = marvell_resume,
......@@ -2946,9 +2958,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = marvell_config_init,
.config_aneg = m88e1510_config_aneg,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.did_interrupt = m88e1121_did_interrupt,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2972,9 +2983,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = marvell_config_init,
.config_aneg = m88e1510_config_aneg,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.did_interrupt = m88e1121_did_interrupt,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -2997,9 +3007,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = m88e3016_config_init,
.aneg_done = marvell_aneg_done,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.did_interrupt = m88e1121_did_interrupt,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -3018,9 +3027,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = marvell_config_init,
.config_aneg = m88e6390_config_aneg,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.did_interrupt = m88e1121_did_interrupt,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -3043,9 +3051,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = marvell_config_init,
.config_aneg = m88e1510_config_aneg,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.did_interrupt = m88e1121_did_interrupt,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......@@ -3065,9 +3072,8 @@ static struct phy_driver marvell_drivers[] = {
.config_init = marvell_config_init,
.config_aneg = m88e1510_config_aneg,
.read_status = marvell_read_status,
.ack_interrupt = marvell_ack_interrupt,
.config_intr = marvell_config_intr,
.did_interrupt = m88e1121_did_interrupt,
.handle_interrupt = marvell_handle_interrupt,
.resume = genphy_resume,
.suspend = genphy_suspend,
.read_page = marvell_read_page,
......
......@@ -44,16 +44,32 @@ static int lan88xx_phy_config_intr(struct phy_device *phydev)
LAN88XX_INT_MASK_LINK_CHANGE_);
} else {
rc = phy_write(phydev, LAN88XX_INT_MASK, 0);
if (rc)
return rc;
/* Ack interrupts after they have been disabled */
rc = phy_read(phydev, LAN88XX_INT_STS);
}
return rc < 0 ? rc : 0;
}
static int lan88xx_phy_ack_interrupt(struct phy_device *phydev)
static irqreturn_t lan88xx_handle_interrupt(struct phy_device *phydev)
{
int rc = phy_read(phydev, LAN88XX_INT_STS);
int irq_status;
return rc < 0 ? rc : 0;
irq_status = phy_read(phydev, LAN88XX_INT_STS);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (!(irq_status & LAN88XX_INT_STS_LINK_CHANGE_))
return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static int lan88xx_suspend(struct phy_device *phydev)
......@@ -340,8 +356,8 @@ static struct phy_driver microchip_phy_driver[] = {
.config_init = lan88xx_config_init,
.config_aneg = lan88xx_config_aneg,
.ack_interrupt = lan88xx_phy_ack_interrupt,
.config_intr = lan88xx_phy_config_intr,
.handle_interrupt = lan88xx_handle_interrupt,
.suspend = lan88xx_suspend,
.resume = genphy_resume,
......
......@@ -189,18 +189,34 @@ static int lan87xx_phy_config_intr(struct phy_device *phydev)
rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, 0x7FFF);
rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
val = LAN87XX_MASK_LINK_UP | LAN87XX_MASK_LINK_DOWN;
}
rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val);
} else {
rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val);
if (rc)
return rc;
rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val);
rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
}
return rc < 0 ? rc : 0;
}
static int lan87xx_phy_ack_interrupt(struct phy_device *phydev)
static irqreturn_t lan87xx_handle_interrupt(struct phy_device *phydev)
{
int rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
int irq_status;
return rc < 0 ? rc : 0;
irq_status = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (irq_status == 0)
return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static int lan87xx_config_init(struct phy_device *phydev)
......@@ -220,8 +236,8 @@ static struct phy_driver microchip_t1_phy_driver[] = {
.config_init = lan87xx_config_init,
.ack_interrupt = lan87xx_phy_ack_interrupt,
.config_intr = lan87xx_phy_config_intr,
.handle_interrupt = lan87xx_handle_interrupt,
.suspend = genphy_suspend,
.resume = genphy_resume,
......
......@@ -44,6 +44,9 @@
#define MII_CFG2_SLEEP_REQUEST_TO_16MS 0x3
#define MII_INTSRC 21
#define MII_INTSRC_LINK_FAIL BIT(10)
#define MII_INTSRC_LINK_UP BIT(9)
#define MII_INTSRC_MASK (MII_INTSRC_LINK_FAIL | MII_INTSRC_LINK_UP)
#define MII_INTSRC_TEMP_ERR BIT(1)
#define MII_INTSRC_UV_ERR BIT(3)
......@@ -597,11 +600,42 @@ static int tja11xx_ack_interrupt(struct phy_device *phydev)
static int tja11xx_config_intr(struct phy_device *phydev)
{
int value = 0;
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
err = tja11xx_ack_interrupt(phydev);
if (err)
return err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
value = MII_INTEN_LINK_FAIL | MII_INTEN_LINK_UP;
err = phy_write(phydev, MII_INTEN, value);
} else {
err = phy_write(phydev, MII_INTEN, value);
if (err)
return err;
err = tja11xx_ack_interrupt(phydev);
}
return err;
}
static irqreturn_t tja11xx_handle_interrupt(struct phy_device *phydev)
{
int irq_status;
irq_status = phy_read(phydev, MII_INTSRC);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (!(irq_status & MII_INTSRC_MASK))
return IRQ_NONE;
phy_trigger_machine(phydev);
return phy_write(phydev, MII_INTEN, value);
return IRQ_HANDLED;
}
static int tja11xx_cable_test_start(struct phy_device *phydev)
......@@ -747,8 +781,8 @@ static struct phy_driver tja11xx_driver[] = {
.get_sset_count = tja11xx_get_sset_count,
.get_strings = tja11xx_get_strings,
.get_stats = tja11xx_get_stats,
.ack_interrupt = tja11xx_ack_interrupt,
.config_intr = tja11xx_config_intr,
.handle_interrupt = tja11xx_handle_interrupt,
.cable_test_start = tja11xx_cable_test_start,
.cable_test_get_status = tja11xx_cable_test_get_status,
}, {
......@@ -770,8 +804,8 @@ static struct phy_driver tja11xx_driver[] = {
.get_sset_count = tja11xx_get_sset_count,
.get_strings = tja11xx_get_strings,
.get_stats = tja11xx_get_stats,
.ack_interrupt = tja11xx_ack_interrupt,
.config_intr = tja11xx_config_intr,
.handle_interrupt = tja11xx_handle_interrupt,
.cable_test_start = tja11xx_cable_test_start,
.cable_test_get_status = tja11xx_cable_test_get_status,
}
......
......@@ -48,6 +48,13 @@ struct smsc_phy_priv {
struct clk *refclk;
};
static int smsc_phy_ack_interrupt(struct phy_device *phydev)
{
int rc = phy_read(phydev, MII_LAN83C185_ISF);
return rc < 0 ? rc : 0;
}
static int smsc_phy_config_intr(struct phy_device *phydev)
{
struct smsc_phy_priv *priv = phydev->priv;
......@@ -55,21 +62,47 @@ static int smsc_phy_config_intr(struct phy_device *phydev)
int rc;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
rc = smsc_phy_ack_interrupt(phydev);
if (rc)
return rc;
intmask = MII_LAN83C185_ISF_INT4 | MII_LAN83C185_ISF_INT6;
if (priv->energy_enable)
intmask |= MII_LAN83C185_ISF_INT7;
}
rc = phy_write(phydev, MII_LAN83C185_IM, intmask);
} else {
rc = phy_write(phydev, MII_LAN83C185_IM, intmask);
if (rc)
return rc;
rc = phy_write(phydev, MII_LAN83C185_IM, intmask);
rc = smsc_phy_ack_interrupt(phydev);
}
return rc < 0 ? rc : 0;
}
static int smsc_phy_ack_interrupt(struct phy_device *phydev)
static irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev)
{
int rc = phy_read (phydev, MII_LAN83C185_ISF);
int irq_status, irq_enabled;
return rc < 0 ? rc : 0;
irq_enabled = phy_read(phydev, MII_LAN83C185_IM);
if (irq_enabled < 0) {
phy_error(phydev);
return IRQ_NONE;
}
irq_status = phy_read(phydev, MII_LAN83C185_ISF);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (!(irq_status & irq_enabled))
return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static int smsc_phy_config_init(struct phy_device *phydev)
......@@ -312,8 +345,8 @@ static struct phy_driver smsc_phy_driver[] = {
.soft_reset = smsc_phy_reset,
/* IRQ related */
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
.handle_interrupt = smsc_phy_handle_interrupt,
.suspend = genphy_suspend,
.resume = genphy_resume,
......@@ -331,8 +364,8 @@ static struct phy_driver smsc_phy_driver[] = {
.soft_reset = smsc_phy_reset,
/* IRQ related */
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
.handle_interrupt = smsc_phy_handle_interrupt,
/* Statistics */
.get_sset_count = smsc_get_sset_count,
......@@ -360,8 +393,8 @@ static struct phy_driver smsc_phy_driver[] = {
.config_aneg = lan87xx_config_aneg,
/* IRQ related */
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
.handle_interrupt = smsc_phy_handle_interrupt,
/* Statistics */
.get_sset_count = smsc_get_sset_count,
......@@ -383,8 +416,8 @@ static struct phy_driver smsc_phy_driver[] = {
.config_init = lan911x_config_init,
/* IRQ related */
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
.handle_interrupt = smsc_phy_handle_interrupt,
.suspend = genphy_suspend,
.resume = genphy_resume,
......@@ -408,8 +441,8 @@ static struct phy_driver smsc_phy_driver[] = {
.config_aneg = lan87xx_config_aneg_ext,
/* IRQ related */
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
.handle_interrupt = smsc_phy_handle_interrupt,
/* Statistics */
.get_sset_count = smsc_get_sset_count,
......@@ -434,8 +467,8 @@ static struct phy_driver smsc_phy_driver[] = {
.soft_reset = smsc_phy_reset,
/* IRQ related */
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
.handle_interrupt = smsc_phy_handle_interrupt,
/* Statistics */
.get_sset_count = smsc_get_sset_count,
......
......@@ -48,32 +48,55 @@ static int ste10Xp_config_init(struct phy_device *phydev)
return 0;
}
static int ste10Xp_ack_interrupt(struct phy_device *phydev)
{
int err = phy_read(phydev, MII_XCIIS);
if (err < 0)
return err;
return 0;
}
static int ste10Xp_config_intr(struct phy_device *phydev)
{
int err, value;
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
/* clear any pending interrupts */
err = ste10Xp_ack_interrupt(phydev);
if (err)
return err;
/* Enable all STe101P interrupts (PR12) */
err = phy_write(phydev, MII_XIE, MII_XIE_DEFAULT_MASK);
/* clear any pending interrupts */
if (err == 0) {
value = phy_read(phydev, MII_XCIIS);
if (value < 0)
err = value;
}
} else
} else {
err = phy_write(phydev, MII_XIE, 0);
if (err)
return err;
err = ste10Xp_ack_interrupt(phydev);
}
return err;
}
static int ste10Xp_ack_interrupt(struct phy_device *phydev)
static irqreturn_t ste10Xp_handle_interrupt(struct phy_device *phydev)
{
int err = phy_read(phydev, MII_XCIIS);
if (err < 0)
return err;
int irq_status;
return 0;
irq_status = phy_read(phydev, MII_XCIIS);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (!(irq_status & MII_XIE_DEFAULT_MASK))
return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static struct phy_driver ste10xp_pdriver[] = {
......@@ -83,8 +106,8 @@ static struct phy_driver ste10xp_pdriver[] = {
.name = "STe101p",
/* PHY_BASIC_FEATURES */
.config_init = ste10Xp_config_init,
.ack_interrupt = ste10Xp_ack_interrupt,
.config_intr = ste10Xp_config_intr,
.handle_interrupt = ste10Xp_handle_interrupt,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
......@@ -93,8 +116,8 @@ static struct phy_driver ste10xp_pdriver[] = {
.name = "STe100p",
/* PHY_BASIC_FEATURES */
.config_init = ste10Xp_config_init,
.ack_interrupt = ste10Xp_ack_interrupt,
.config_intr = ste10Xp_config_intr,
.handle_interrupt = ste10Xp_handle_interrupt,
.suspend = genphy_suspend,
.resume = genphy_resume,
} };
......
......@@ -40,6 +40,11 @@
#define MII_VSC8244_ISTAT_SPEED 0x4000
#define MII_VSC8244_ISTAT_LINK 0x2000
#define MII_VSC8244_ISTAT_DUPLEX 0x1000
#define MII_VSC8244_ISTAT_MASK (MII_VSC8244_ISTAT_SPEED | \
MII_VSC8244_ISTAT_LINK | \
MII_VSC8244_ISTAT_DUPLEX)
#define MII_VSC8221_ISTAT_MASK MII_VSC8244_ISTAT_LINK
/* Vitesse Auxiliary Control/Status Register */
#define MII_VSC8244_AUX_CONSTAT 0x1c
......@@ -270,25 +275,14 @@ static int vsc8601_config_init(struct phy_device *phydev)
return 0;
}
static int vsc824x_ack_interrupt(struct phy_device *phydev)
{
int err = 0;
/* Don't bother to ACK the interrupts if interrupts
* are disabled. The 824x cannot clear the interrupts
* if they are disabled.
*/
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_read(phydev, MII_VSC8244_ISTAT);
return (err < 0) ? err : 0;
}
static int vsc82xx_config_intr(struct phy_device *phydev)
{
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
/* Don't bother to ACK the interrupts since the 824x cannot
* clear the interrupts if they are disabled.
*/
err = phy_write(phydev, MII_VSC8244_IMASK,
(phydev->drv->phy_id == PHY_ID_VSC8234 ||
phydev->drv->phy_id == PHY_ID_VSC8244 ||
......@@ -311,6 +305,31 @@ static int vsc82xx_config_intr(struct phy_device *phydev)
return err;
}
static irqreturn_t vsc82xx_handle_interrupt(struct phy_device *phydev)
{
int irq_status, irq_mask;
if (phydev->drv->phy_id == PHY_ID_VSC8244 ||
phydev->drv->phy_id == PHY_ID_VSC8572 ||
phydev->drv->phy_id == PHY_ID_VSC8601)
irq_mask = MII_VSC8244_ISTAT_MASK;
else
irq_mask = MII_VSC8221_ISTAT_MASK;
irq_status = phy_read(phydev, MII_VSC8244_ISTAT);
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;
}
static int vsc8221_config_init(struct phy_device *phydev)
{
int err;
......@@ -390,8 +409,8 @@ static struct phy_driver vsc82xx_driver[] = {
/* PHY_GBIT_FEATURES */
.config_init = &vsc824x_config_init,
.config_aneg = &vsc82x4_config_aneg,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
.handle_interrupt = &vsc82xx_handle_interrupt,
}, {
.phy_id = PHY_ID_VSC8244,
.name = "Vitesse VSC8244",
......@@ -399,8 +418,8 @@ static struct phy_driver vsc82xx_driver[] = {
/* PHY_GBIT_FEATURES */
.config_init = &vsc824x_config_init,
.config_aneg = &vsc82x4_config_aneg,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
.handle_interrupt = &vsc82xx_handle_interrupt,
}, {
.phy_id = PHY_ID_VSC8572,
.name = "Vitesse VSC8572",
......@@ -408,16 +427,16 @@ static struct phy_driver vsc82xx_driver[] = {
/* PHY_GBIT_FEATURES */
.config_init = &vsc824x_config_init,
.config_aneg = &vsc82x4_config_aneg,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
.handle_interrupt = &vsc82xx_handle_interrupt,
}, {
.phy_id = PHY_ID_VSC8601,
.name = "Vitesse VSC8601",
.phy_id_mask = 0x000ffff0,
/* PHY_GBIT_FEATURES */
.config_init = &vsc8601_config_init,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
.handle_interrupt = &vsc82xx_handle_interrupt,
}, {
.phy_id = PHY_ID_VSC7385,
.name = "Vitesse VSC7385",
......@@ -461,8 +480,8 @@ static struct phy_driver vsc82xx_driver[] = {
/* PHY_GBIT_FEATURES */
.config_init = &vsc824x_config_init,
.config_aneg = &vsc82x4_config_aneg,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
.handle_interrupt = &vsc82xx_handle_interrupt,
}, {
/* Vitesse 8221 */
.phy_id = PHY_ID_VSC8221,
......@@ -470,8 +489,8 @@ static struct phy_driver vsc82xx_driver[] = {
.name = "Vitesse VSC8221",
/* PHY_GBIT_FEATURES */
.config_init = &vsc8221_config_init,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
.handle_interrupt = &vsc82xx_handle_interrupt,
}, {
/* Vitesse 8211 */
.phy_id = PHY_ID_VSC8211,
......@@ -479,8 +498,8 @@ static struct phy_driver vsc82xx_driver[] = {
.name = "Vitesse VSC8211",
/* PHY_GBIT_FEATURES */
.config_init = &vsc8221_config_init,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
.handle_interrupt = &vsc82xx_handle_interrupt,
} };
module_phy_driver(vsc82xx_driver);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment