aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Stapelberg <michael@stapelberg.de>2013-03-11 09:56:45 -0400
committerDavid S. Miller <davem@davemloft.net>2013-03-12 11:40:54 -0400
commit3871c3876f8084a2f40ba3c3fc20a6bb5754d88d (patch)
treec496bd0fdfdb3dff55305fd97e1874dd160cdc28
parent42e836eb4527fb635cb799a701fe4c9fe741c03a (diff)
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>
-rw-r--r--drivers/net/ethernet/marvell/mv643xx_eth.c32
-rw-r--r--drivers/net/phy/marvell.c127
2 files changed, 159 insertions, 0 deletions
diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
index 6562c736a1d8..d1ecf4bf7da7 100644
--- a/drivers/net/ethernet/marvell/mv643xx_eth.c
+++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
@@ -20,6 +20,8 @@
20 * Copyright (C) 2007-2008 Marvell Semiconductor 20 * Copyright (C) 2007-2008 Marvell Semiconductor
21 * Lennert Buytenhek <buytenh@marvell.com> 21 * Lennert Buytenhek <buytenh@marvell.com>
22 * 22 *
23 * Copyright (C) 2013 Michael Stapelberg <michael@stapelberg.de>
24 *
23 * This program is free software; you can redistribute it and/or 25 * This program is free software; you can redistribute it and/or
24 * modify it under the terms of the GNU General Public License 26 * modify it under the terms of the GNU General Public License
25 * as published by the Free Software Foundation; either version 2 27 * as published by the Free Software Foundation; either version 2
@@ -1523,6 +1525,34 @@ mv643xx_eth_get_settings_phyless(struct mv643xx_eth_private *mp,
1523 return 0; 1525 return 0;
1524} 1526}
1525 1527
1528static void
1529mv643xx_eth_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
1530{
1531 struct mv643xx_eth_private *mp = netdev_priv(dev);
1532 wol->supported = 0;
1533 wol->wolopts = 0;
1534 if (mp->phy)
1535 phy_ethtool_get_wol(mp->phy, wol);
1536}
1537
1538static int
1539mv643xx_eth_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
1540{
1541 struct mv643xx_eth_private *mp = netdev_priv(dev);
1542 int err;
1543
1544 if (mp->phy == NULL)
1545 return -EOPNOTSUPP;
1546
1547 err = phy_ethtool_set_wol(mp->phy, wol);
1548 /* Given that mv643xx_eth works without the marvell-specific PHY driver,
1549 * this debugging hint is useful to have.
1550 */
1551 if (err == -EOPNOTSUPP)
1552 netdev_info(dev, "The PHY does not support set_wol, was CONFIG_MARVELL_PHY enabled?\n");
1553 return err;
1554}
1555
1526static int 1556static int
1527mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) 1557mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
1528{ 1558{
@@ -1708,6 +1738,8 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops = {
1708 .get_ethtool_stats = mv643xx_eth_get_ethtool_stats, 1738 .get_ethtool_stats = mv643xx_eth_get_ethtool_stats,
1709 .get_sset_count = mv643xx_eth_get_sset_count, 1739 .get_sset_count = mv643xx_eth_get_sset_count,
1710 .get_ts_info = ethtool_op_get_ts_info, 1740 .get_ts_info = ethtool_op_get_ts_info,
1741 .get_wol = mv643xx_eth_get_wol,
1742 .set_wol = mv643xx_eth_set_wol,
1711}; 1743};
1712 1744
1713 1745
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 @@
7 * 7 *
8 * Copyright (c) 2004 Freescale Semiconductor, Inc. 8 * Copyright (c) 2004 Freescale Semiconductor, Inc.
9 * 9 *
10 * Copyright (c) 2013 Michael Stapelberg <michael@stapelberg.de>
11 *
10 * This program is free software; you can redistribute it and/or modify it 12 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the 13 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your 14 * Free Software Foundation; either version 2 of the License, or (at your
@@ -80,6 +82,28 @@
80#define MII_88E1318S_PHY_MSCR1_REG 16 82#define MII_88E1318S_PHY_MSCR1_REG 16
81#define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6) 83#define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6)
82 84
85/* Copper Specific Interrupt Enable Register */
86#define MII_88E1318S_PHY_CSIER 0x12
87/* WOL Event Interrupt Enable */
88#define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7)
89
90/* LED Timer Control Register */
91#define MII_88E1318S_PHY_LED_PAGE 0x03
92#define MII_88E1318S_PHY_LED_TCR 0x12
93#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15)
94#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7)
95#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11)
96
97/* Magic Packet MAC address registers */
98#define MII_88E1318S_PHY_MAGIC_PACKET_WORD2 0x17
99#define MII_88E1318S_PHY_MAGIC_PACKET_WORD1 0x18
100#define MII_88E1318S_PHY_MAGIC_PACKET_WORD0 0x19
101
102#define MII_88E1318S_PHY_WOL_PAGE 0x11
103#define MII_88E1318S_PHY_WOL_CTRL 0x10
104#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12)
105#define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14)
106
83#define MII_88E1121_PHY_LED_CTRL 16 107#define MII_88E1121_PHY_LED_CTRL 16
84#define MII_88E1121_PHY_LED_PAGE 3 108#define MII_88E1121_PHY_LED_PAGE 3
85#define MII_88E1121_PHY_LED_DEF 0x0030 109#define MII_88E1121_PHY_LED_DEF 0x0030
@@ -696,6 +720,107 @@ static int m88e1121_did_interrupt(struct phy_device *phydev)
696 return 0; 720 return 0;
697} 721}
698 722
723static void m88e1318_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
724{
725 wol->supported = WAKE_MAGIC;
726 wol->wolopts = 0;
727
728 if (phy_write(phydev, MII_MARVELL_PHY_PAGE,
729 MII_88E1318S_PHY_WOL_PAGE) < 0)
730 return;
731
732 if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) &
733 MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
734 wol->wolopts |= WAKE_MAGIC;
735
736 if (phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00) < 0)
737 return;
738}
739
740static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
741{
742 int err, oldpage, temp;
743
744 oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
745
746 if (wol->wolopts & WAKE_MAGIC) {
747 /* Explicitly switch to page 0x00, just to be sure */
748 err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00);
749 if (err < 0)
750 return err;
751
752 /* Enable the WOL interrupt */
753 temp = phy_read(phydev, MII_88E1318S_PHY_CSIER);
754 temp |= MII_88E1318S_PHY_CSIER_WOL_EIE;
755 err = phy_write(phydev, MII_88E1318S_PHY_CSIER, temp);
756 if (err < 0)
757 return err;
758
759 err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
760 MII_88E1318S_PHY_LED_PAGE);
761 if (err < 0)
762 return err;
763
764 /* Setup LED[2] as interrupt pin (active low) */
765 temp = phy_read(phydev, MII_88E1318S_PHY_LED_TCR);
766 temp &= ~MII_88E1318S_PHY_LED_TCR_FORCE_INT;
767 temp |= MII_88E1318S_PHY_LED_TCR_INTn_ENABLE;
768 temp |= MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW;
769 err = phy_write(phydev, MII_88E1318S_PHY_LED_TCR, temp);
770 if (err < 0)
771 return err;
772
773 err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
774 MII_88E1318S_PHY_WOL_PAGE);
775 if (err < 0)
776 return err;
777
778 /* Store the device address for the magic packet */
779 err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
780 ((phydev->attached_dev->dev_addr[5] << 8) |
781 phydev->attached_dev->dev_addr[4]));
782 if (err < 0)
783 return err;
784 err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
785 ((phydev->attached_dev->dev_addr[3] << 8) |
786 phydev->attached_dev->dev_addr[2]));
787 if (err < 0)
788 return err;
789 err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
790 ((phydev->attached_dev->dev_addr[1] << 8) |
791 phydev->attached_dev->dev_addr[0]));
792 if (err < 0)
793 return err;
794
795 /* Clear WOL status and enable magic packet matching */
796 temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
797 temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
798 temp |= MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
799 err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
800 if (err < 0)
801 return err;
802 } else {
803 err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
804 MII_88E1318S_PHY_WOL_PAGE);
805 if (err < 0)
806 return err;
807
808 /* Clear WOL status and disable magic packet matching */
809 temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
810 temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
811 temp &= ~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
812 err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
813 if (err < 0)
814 return err;
815 }
816
817 err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
818 if (err < 0)
819 return err;
820
821 return 0;
822}
823
699static struct phy_driver marvell_drivers[] = { 824static struct phy_driver marvell_drivers[] = {
700 { 825 {
701 .phy_id = MARVELL_PHY_ID_88E1101, 826 .phy_id = MARVELL_PHY_ID_88E1101,
@@ -772,6 +897,8 @@ static struct phy_driver marvell_drivers[] = {
772 .ack_interrupt = &marvell_ack_interrupt, 897 .ack_interrupt = &marvell_ack_interrupt,
773 .config_intr = &marvell_config_intr, 898 .config_intr = &marvell_config_intr,
774 .did_interrupt = &m88e1121_did_interrupt, 899 .did_interrupt = &m88e1121_did_interrupt,
900 .get_wol = &m88e1318_get_wol,
901 .set_wol = &m88e1318_set_wol,
775 .driver = { .owner = THIS_MODULE }, 902 .driver = { .owner = THIS_MODULE },
776 }, 903 },
777 { 904 {