diff options
Diffstat (limited to 'drivers/net/phy/marvell.c')
| -rw-r--r-- | drivers/net/phy/marvell.c | 102 |
1 files changed, 80 insertions, 22 deletions
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index f71cffa88406..2c688d661297 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -55,6 +55,8 @@ #define MII_M1011_IMASK 0x12 #define MII_M1011_IMASK_INIT 0x6400 #define MII_M1011_IMASK_CLEAR 0x0000 +/* WOL Event Interrupt Enable, 88E1318S and 88E1510 only */ +#define MII_M1011_IMASK_WOL BIT(7) #define MII_M1011_PHY_SCR 0x10 #define MII_M1011_PHY_SCR_DOWNSHIFT_EN BIT(11) @@ -139,11 +141,6 @@ #define MII_88E1318S_PHY_MSCR1_REG 16 #define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6) -/* Copper Specific Interrupt Enable Register */ -#define MII_88E1318S_PHY_CSIER 0x12 -/* WOL Event Interrupt Enable */ -#define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7) - #define MII_88E1318S_PHY_LED_FUNC 0x10 #define MII_88E1318S_PHY_LED_FUNC_OFF (0x8) #define MII_88E1318S_PHY_LED_FUNC_ON (0x9) @@ -354,6 +351,7 @@ struct marvell_priv { u32 step; s8 pair; u8 vct_phase; + bool wol_enabled; }; static int marvell_read_page(struct phy_device *phydev) @@ -407,7 +405,8 @@ static int marvell_config_intr(struct phy_device *phydev) return err; } -static irqreturn_t marvell_handle_interrupt(struct phy_device *phydev) +static irqreturn_t __marvell_handle_interrupt(struct phy_device *phydev, + u16 mask) { int irq_status; @@ -417,7 +416,7 @@ static irqreturn_t marvell_handle_interrupt(struct phy_device *phydev) return IRQ_NONE; } - if (!(irq_status & MII_M1011_IMASK_INIT)) + if (!(irq_status & mask)) return IRQ_NONE; phy_trigger_machine(phydev); @@ -425,6 +424,57 @@ static irqreturn_t marvell_handle_interrupt(struct phy_device *phydev) return IRQ_HANDLED; } +static irqreturn_t marvell_handle_interrupt(struct phy_device *phydev) +{ + return __marvell_handle_interrupt(phydev, MII_M1011_IMASK_INIT); +} + +/* WOL_EIE also routes WoL events to INTn. Keep it set while WoL is enabled, + * unless INTn is the PHY interrupt and that interrupt is being disabled: + * nothing would read IEVENT after free_irq() to release the line. + */ +static u16 m88e1318_wol_imask(struct phy_device *phydev) +{ + struct marvell_priv *priv = phydev->priv; + + if (priv->wol_enabled && + (phydev->interrupts == PHY_INTERRUPT_ENABLED || + !phy_interrupt_is_valid(phydev))) + return MII_M1011_IMASK_WOL; + + return 0; +} + +static int m88e1318_config_intr(struct phy_device *phydev) +{ + bool enable = phydev->interrupts == PHY_INTERRUPT_ENABLED; + u16 imask; + int err; + + if (enable) { + err = marvell_ack_interrupt(phydev); + if (err) + return err; + } + + /* Serialise against m88e1318_set_wol() updating wol_enabled */ + phy_lock_mdio_bus(phydev); + imask = enable ? MII_M1011_IMASK_INIT : MII_M1011_IMASK_CLEAR; + imask |= m88e1318_wol_imask(phydev); + err = __phy_write(phydev, MII_M1011_IMASK, imask); + phy_unlock_mdio_bus(phydev); + if (err || enable) + return err; + + return marvell_ack_interrupt(phydev); +} + +static irqreturn_t m88e1318_handle_interrupt(struct phy_device *phydev) +{ + return __marvell_handle_interrupt(phydev, MII_M1011_IMASK_INIT | + MII_M1011_IMASK_WOL); +} + static int marvell_set_polarity(struct phy_device *phydev, int polarity) { u16 val; @@ -1969,18 +2019,27 @@ static void m88e1318_get_wol(struct phy_device *phydev, static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { + struct marvell_priv *priv = phydev->priv; int err = 0, oldpage; oldpage = phy_save_page(phydev); if (oldpage < 0) goto error; - if (wol->wolopts & (WAKE_MAGIC | WAKE_PHY)) { - /* Explicitly switch to page 0x00, just to be sure */ - err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE); - if (err < 0) - goto error; + priv->wol_enabled = !!(wol->wolopts & (WAKE_MAGIC | WAKE_PHY)); + + /* Explicitly switch to page 0x00, just to be sure */ + err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE); + if (err < 0) + goto error; + + /* Update the WOL interrupt enable */ + err = __phy_modify(phydev, MII_M1011_IMASK, MII_M1011_IMASK_WOL, + m88e1318_wol_imask(phydev)); + if (err < 0) + goto error; + if (priv->wol_enabled) { /* If WOL event happened once, the LED[2] interrupt pin * will not be cleared unless we reading the interrupt status * register. If interrupts are in use, the normal interrupt @@ -1990,12 +2049,6 @@ static int m88e1318_set_wol(struct phy_device *phydev, if (!phy_interrupt_is_valid(phydev)) __phy_read(phydev, MII_M1011_IEVENT); - /* Enable the WOL interrupt */ - err = __phy_set_bits(phydev, MII_88E1318S_PHY_CSIER, - MII_88E1318S_PHY_CSIER_WOL_EIE); - if (err < 0) - goto error; - err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE); if (err < 0) goto error; @@ -3817,8 +3870,8 @@ static struct phy_driver marvell_drivers[] = { .config_init = m88e1318_config_init, .config_aneg = m88e1318_config_aneg, .read_status = marvell_read_status, - .config_intr = marvell_config_intr, - .handle_interrupt = marvell_handle_interrupt, + .config_intr = m88e1318_config_intr, + .handle_interrupt = m88e1318_handle_interrupt, .get_wol = m88e1318_get_wol, .set_wol = m88e1318_set_wol, .resume = genphy_resume, @@ -3913,6 +3966,11 @@ static struct phy_driver marvell_drivers[] = { .get_stats = marvell_get_stats, .get_tunable = m88e1011_get_tunable, .set_tunable = m88e1011_set_tunable, + .led_brightness_set = m88e1318_led_brightness_set, + .led_blink_set = m88e1318_led_blink_set, + .led_hw_is_supported = m88e1318_led_hw_is_supported, + .led_hw_control_set = m88e1318_led_hw_control_set, + .led_hw_control_get = m88e1318_led_hw_control_get, }, { .phy_id = MARVELL_PHY_ID_88E1510, @@ -3925,8 +3983,8 @@ static struct phy_driver marvell_drivers[] = { .config_init = m88e1510_config_init, .config_aneg = m88e1510_config_aneg, .read_status = marvell_read_status, - .config_intr = marvell_config_intr, - .handle_interrupt = marvell_handle_interrupt, + .config_intr = m88e1318_config_intr, + .handle_interrupt = m88e1318_handle_interrupt, .get_wol = m88e1318_get_wol, .set_wol = m88e1318_set_wol, .resume = m88e1510_resume, |
