diff options
author | Michael Stapelberg <michael@stapelberg.de> | 2013-03-11 09:56:45 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-03-12 11:40:54 -0400 |
commit | 3871c3876f8084a2f40ba3c3fc20a6bb5754d88d (patch) | |
tree | c496bd0fdfdb3dff55305fd97e1874dd160cdc28 | |
parent | 42e836eb4527fb635cb799a701fe4c9fe741c03a (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.c | 32 | ||||
-rw-r--r-- | drivers/net/phy/marvell.c | 127 |
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 | ||
1528 | static void | ||
1529 | mv643xx_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 | |||
1538 | static int | ||
1539 | mv643xx_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 | |||
1526 | static int | 1556 | static int |
1527 | mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | 1557 | mv643xx_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 | ||
723 | static 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 | |||
740 | static 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 | |||
699 | static struct phy_driver marvell_drivers[] = { | 824 | static 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 | { |