diff options
Diffstat (limited to 'drivers/net/phy/meson-gxl.c')
-rw-r--r-- | drivers/net/phy/meson-gxl.c | 74 |
1 files changed, 73 insertions, 1 deletions
diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c index 1ea69b7585d9..842eb871a6e3 100644 --- a/drivers/net/phy/meson-gxl.c +++ b/drivers/net/phy/meson-gxl.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/ethtool.h> | 22 | #include <linux/ethtool.h> |
23 | #include <linux/phy.h> | 23 | #include <linux/phy.h> |
24 | #include <linux/netdevice.h> | 24 | #include <linux/netdevice.h> |
25 | #include <linux/bitfield.h> | ||
25 | 26 | ||
26 | static int meson_gxl_config_init(struct phy_device *phydev) | 27 | static int meson_gxl_config_init(struct phy_device *phydev) |
27 | { | 28 | { |
@@ -50,6 +51,77 @@ static int meson_gxl_config_init(struct phy_device *phydev) | |||
50 | return 0; | 51 | return 0; |
51 | } | 52 | } |
52 | 53 | ||
54 | /* This function is provided to cope with the possible failures of this phy | ||
55 | * during aneg process. When aneg fails, the PHY reports that aneg is done | ||
56 | * but the value found in MII_LPA is wrong: | ||
57 | * - Early failures: MII_LPA is just 0x0001. if MII_EXPANSION reports that | ||
58 | * the link partner (LP) supports aneg but the LP never acked our base | ||
59 | * code word, it is likely that we never sent it to begin with. | ||
60 | * - Late failures: MII_LPA is filled with a value which seems to make sense | ||
61 | * but it actually is not what the LP is advertising. It seems that we | ||
62 | * can detect this using a magic bit in the WOL bank (reg 12 - bit 12). | ||
63 | * If this particular bit is not set when aneg is reported being done, | ||
64 | * it means MII_LPA is likely to be wrong. | ||
65 | * | ||
66 | * In both case, forcing a restart of the aneg process solve the problem. | ||
67 | * When this failure happens, the first retry is usually successful but, | ||
68 | * in some cases, it may take up to 6 retries to get a decent result | ||
69 | */ | ||
70 | static int meson_gxl_read_status(struct phy_device *phydev) | ||
71 | { | ||
72 | int ret, wol, lpa, exp; | ||
73 | |||
74 | if (phydev->autoneg == AUTONEG_ENABLE) { | ||
75 | ret = genphy_aneg_done(phydev); | ||
76 | if (ret < 0) | ||
77 | return ret; | ||
78 | else if (!ret) | ||
79 | goto read_status_continue; | ||
80 | |||
81 | /* Need to access WOL bank, make sure the access is open */ | ||
82 | ret = phy_write(phydev, 0x14, 0x0000); | ||
83 | if (ret) | ||
84 | return ret; | ||
85 | ret = phy_write(phydev, 0x14, 0x0400); | ||
86 | if (ret) | ||
87 | return ret; | ||
88 | ret = phy_write(phydev, 0x14, 0x0000); | ||
89 | if (ret) | ||
90 | return ret; | ||
91 | ret = phy_write(phydev, 0x14, 0x0400); | ||
92 | if (ret) | ||
93 | return ret; | ||
94 | |||
95 | /* Request LPI_STATUS WOL register */ | ||
96 | ret = phy_write(phydev, 0x14, 0x8D80); | ||
97 | if (ret) | ||
98 | return ret; | ||
99 | |||
100 | /* Read LPI_STATUS value */ | ||
101 | wol = phy_read(phydev, 0x15); | ||
102 | if (wol < 0) | ||
103 | return wol; | ||
104 | |||
105 | lpa = phy_read(phydev, MII_LPA); | ||
106 | if (lpa < 0) | ||
107 | return lpa; | ||
108 | |||
109 | exp = phy_read(phydev, MII_EXPANSION); | ||
110 | if (exp < 0) | ||
111 | return exp; | ||
112 | |||
113 | if (!(wol & BIT(12)) || | ||
114 | ((exp & EXPANSION_NWAY) && !(lpa & LPA_LPACK))) { | ||
115 | /* Looks like aneg failed after all */ | ||
116 | phydev_dbg(phydev, "LPA corruption - aneg restart\n"); | ||
117 | return genphy_restart_aneg(phydev); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | read_status_continue: | ||
122 | return genphy_read_status(phydev); | ||
123 | } | ||
124 | |||
53 | static struct phy_driver meson_gxl_phy[] = { | 125 | static struct phy_driver meson_gxl_phy[] = { |
54 | { | 126 | { |
55 | .phy_id = 0x01814400, | 127 | .phy_id = 0x01814400, |
@@ -60,7 +132,7 @@ static struct phy_driver meson_gxl_phy[] = { | |||
60 | .config_init = meson_gxl_config_init, | 132 | .config_init = meson_gxl_config_init, |
61 | .config_aneg = genphy_config_aneg, | 133 | .config_aneg = genphy_config_aneg, |
62 | .aneg_done = genphy_aneg_done, | 134 | .aneg_done = genphy_aneg_done, |
63 | .read_status = genphy_read_status, | 135 | .read_status = meson_gxl_read_status, |
64 | .suspend = genphy_suspend, | 136 | .suspend = genphy_suspend, |
65 | .resume = genphy_resume, | 137 | .resume = genphy_resume, |
66 | }, | 138 | }, |