diff options
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 @@ | |||
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 | { |