diff options
author | Michael Stapelberg <michael@stapelberg.de> | 2013-03-11 14:56:45 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-03-12 16:40:54 +0100 |
commit | 3871c3876f8084a2f40ba3c3fc20a6bb5754d88d (patch) | |
tree | c496bd0fdfdb3dff55305fd97e1874dd160cdc28 /drivers/net/phy/marvell.c | |
parent | phy: add set_wol/get_wol functions (diff) | |
download | linux-3871c3876f8084a2f40ba3c3fc20a6bb5754d88d.tar.xz linux-3871c3876f8084a2f40ba3c3fc20a6bb5754d88d.zip |
mv643xx_eth with 88E1318S: support Wake on LAN
This has been tested on a qnap TS-119P II. Note that enabling WOL with
"ethtool -s eth0 wol g" is not enough; you also need to tell the PIC
microcontroller inside the qnap that WOL should be enabled by sending
0xF2 with qcontrol(1) and you have to disable EUP ("Energy-using
Products", a European power-saving thing) by sending 0xF4.
Signed-off-by: Michael Stapelberg <michael@stapelberg.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/phy/marvell.c')
-rw-r--r-- | drivers/net/phy/marvell.c | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 22dec9c7ef05..202fe1ff1987 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -7,6 +7,8 @@ * * Copyright (c) 2004 Freescale Semiconductor, Inc. * + * Copyright (c) 2013 Michael Stapelberg <michael@stapelberg.de> + * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your @@ -80,6 +82,28 @@ #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) + +/* LED Timer Control Register */ +#define MII_88E1318S_PHY_LED_PAGE 0x03 +#define MII_88E1318S_PHY_LED_TCR 0x12 +#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15) +#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7) +#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11) + +/* Magic Packet MAC address registers */ +#define MII_88E1318S_PHY_MAGIC_PACKET_WORD2 0x17 +#define MII_88E1318S_PHY_MAGIC_PACKET_WORD1 0x18 +#define MII_88E1318S_PHY_MAGIC_PACKET_WORD0 0x19 + +#define MII_88E1318S_PHY_WOL_PAGE 0x11 +#define MII_88E1318S_PHY_WOL_CTRL 0x10 +#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12) +#define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14) + #define MII_88E1121_PHY_LED_CTRL 16 #define MII_88E1121_PHY_LED_PAGE 3 #define MII_88E1121_PHY_LED_DEF 0x0030 @@ -696,6 +720,107 @@ static int m88e1121_did_interrupt(struct phy_device *phydev) return 0; } +static void m88e1318_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + if (phy_write(phydev, MII_MARVELL_PHY_PAGE, + MII_88E1318S_PHY_WOL_PAGE) < 0) + return; + + if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) & + MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE) + wol->wolopts |= WAKE_MAGIC; + + if (phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00) < 0) + return; +} + +static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + int err, oldpage, temp; + + oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); + + if (wol->wolopts & WAKE_MAGIC) { + /* Explicitly switch to page 0x00, just to be sure */ + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00); + if (err < 0) + return err; + + /* Enable the WOL interrupt */ + temp = phy_read(phydev, MII_88E1318S_PHY_CSIER); + temp |= MII_88E1318S_PHY_CSIER_WOL_EIE; + err = phy_write(phydev, MII_88E1318S_PHY_CSIER, temp); + if (err < 0) + return err; + + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, + MII_88E1318S_PHY_LED_PAGE); + if (err < 0) + return err; + + /* Setup LED[2] as interrupt pin (active low) */ + temp = phy_read(phydev, MII_88E1318S_PHY_LED_TCR); + temp &= ~MII_88E1318S_PHY_LED_TCR_FORCE_INT; + temp |= MII_88E1318S_PHY_LED_TCR_INTn_ENABLE; + temp |= MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW; + err = phy_write(phydev, MII_88E1318S_PHY_LED_TCR, temp); + if (err < 0) + return err; + + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, + MII_88E1318S_PHY_WOL_PAGE); + if (err < 0) + return err; + + /* Store the device address for the magic packet */ + err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2, + ((phydev->attached_dev->dev_addr[5] << 8) | + phydev->attached_dev->dev_addr[4])); + if (err < 0) + return err; + err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1, + ((phydev->attached_dev->dev_addr[3] << 8) | + phydev->attached_dev->dev_addr[2])); + if (err < 0) + return err; + err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0, + ((phydev->attached_dev->dev_addr[1] << 8) | + phydev->attached_dev->dev_addr[0])); + if (err < 0) + return err; + + /* Clear WOL status and enable magic packet matching */ + temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL); + temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS; + temp |= MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE; + err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp); + if (err < 0) + return err; + } else { + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, + MII_88E1318S_PHY_WOL_PAGE); + if (err < 0) + return err; + + /* Clear WOL status and disable magic packet matching */ + temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL); + temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS; + temp &= ~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE; + err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp); + if (err < 0) + return err; + } + + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); + if (err < 0) + return err; + + return 0; +} + static struct phy_driver marvell_drivers[] = { { .phy_id = MARVELL_PHY_ID_88E1101, @@ -772,6 +897,8 @@ static struct phy_driver marvell_drivers[] = { .ack_interrupt = &marvell_ack_interrupt, .config_intr = &marvell_config_intr, .did_interrupt = &m88e1121_did_interrupt, + .get_wol = &m88e1318_get_wol, + .set_wol = &m88e1318_set_wol, .driver = { .owner = THIS_MODULE }, }, { |