From f1e2400a80ff55eb7c5f4fd9d7eb163fd0de9a2c Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 8 Dec 2017 12:08:11 +0100 Subject: net: phy: meson-gxl: detect LPA corruption The purpose of this change is to fix the incorrect detection of the link partner (LP) advertised capabilities which sometimes happens with this PHY (roughly 1 time in a dozen) This issue may cause the link to be negotiated at 10Mbps/Full or 10Mbps/Half when 100MBps/Full is actually possible. In some case, the link is even completely broken and no communication is possible. To detect the corruption, we must look for a magic undocumented bit in the WOL bank (hint given by the SoC vendor kernel) but this is not enough to cover all cases. We also have to look at the LPA ack. If the LP supports Aneg but did not ack our base code when aneg is completed, we assume something went wrong. The detection of a corrupted LPA triggers a restart of the aneg process. This solves the problem but may take up to 6 retries to complete. Fixes: 7334b3e47aee ("net: phy: Add Meson GXL Internal PHY driver") Signed-off-by: Jerome Brunet Signed-off-by: David S. Miller --- drivers/net/phy/meson-gxl.c | 74 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'drivers/net/phy/meson-gxl.c') diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c index 1ea69b7585d9..700007dd4be5 100644 --- a/drivers/net/phy/meson-gxl.c +++ b/drivers/net/phy/meson-gxl.c @@ -22,6 +22,7 @@ #include #include #include +#include static int meson_gxl_config_init(struct phy_device *phydev) { @@ -50,6 +51,77 @@ static int meson_gxl_config_init(struct phy_device *phydev) return 0; } +/* This function is provided to cope with the possible failures of this phy + * during aneg process. When aneg fails, the PHY reports that aneg is done + * but the value found in MII_LPA is wrong: + * - Early failures: MII_LPA is just 0x0001. if MII_EXPANSION reports that + * the link partner (LP) supports aneg but the LP never acked our base + * code word, it is likely that we never sent it to begin with. + * - Late failures: MII_LPA is filled with a value which seems to make sense + * but it actually is not what the LP is advertising. It seems that we + * can detect this using a magic bit in the WOL bank (reg 12 - bit 12). + * If this particular bit is not set when aneg is reported being done, + * it means MII_LPA is likely to be wrong. + * + * In both case, forcing a restart of the aneg process solve the problem. + * When this failure happens, the first retry is usually successful but, + * in some cases, it may take up to 6 retries to get a decent result + */ +int meson_gxl_read_status(struct phy_device *phydev) +{ + int ret, wol, lpa, exp; + + if (phydev->autoneg == AUTONEG_ENABLE) { + ret = genphy_aneg_done(phydev); + if (ret < 0) + return ret; + else if (!ret) + goto read_status_continue; + + /* Need to access WOL bank, make sure the access is open */ + ret = phy_write(phydev, 0x14, 0x0000); + if (ret) + return ret; + ret = phy_write(phydev, 0x14, 0x0400); + if (ret) + return ret; + ret = phy_write(phydev, 0x14, 0x0000); + if (ret) + return ret; + ret = phy_write(phydev, 0x14, 0x0400); + if (ret) + return ret; + + /* Request LPI_STATUS WOL register */ + ret = phy_write(phydev, 0x14, 0x8D80); + if (ret) + return ret; + + /* Read LPI_STATUS value */ + wol = phy_read(phydev, 0x15); + if (wol < 0) + return wol; + + lpa = phy_read(phydev, MII_LPA); + if (lpa < 0) + return lpa; + + exp = phy_read(phydev, MII_EXPANSION); + if (exp < 0) + return exp; + + if (!(wol & BIT(12)) || + ((exp & EXPANSION_NWAY) && !(lpa & LPA_LPACK))) { + /* Looks like aneg failed after all */ + phydev_dbg(phydev, "LPA corruption - aneg restart\n"); + return genphy_restart_aneg(phydev); + } + } + +read_status_continue: + return genphy_read_status(phydev); +} + static struct phy_driver meson_gxl_phy[] = { { .phy_id = 0x01814400, @@ -60,7 +132,7 @@ static struct phy_driver meson_gxl_phy[] = { .config_init = meson_gxl_config_init, .config_aneg = genphy_config_aneg, .aneg_done = genphy_aneg_done, - .read_status = genphy_read_status, + .read_status = meson_gxl_read_status, .suspend = genphy_suspend, .resume = genphy_resume, }, -- cgit v1.2.3-59-g8ed1b From 3b3397e2031564db07022e99f04d4b9f3df6fced Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 12 Dec 2017 13:03:11 +0000 Subject: net: phy: meson-gxl: make function meson_gxl_read_status static The function meson_gxl_read_status is local to the source and does not need to be in global scope, so make it static. Cleans up sparse warning: symbol 'meson_gxl_read_status' was not declared. Should it be static? Signed-off-by: Colin Ian King Reviewed-by: Jerome Brunet Signed-off-by: David S. Miller --- drivers/net/phy/meson-gxl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/phy/meson-gxl.c') diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c index 700007dd4be5..842eb871a6e3 100644 --- a/drivers/net/phy/meson-gxl.c +++ b/drivers/net/phy/meson-gxl.c @@ -67,7 +67,7 @@ static int meson_gxl_config_init(struct phy_device *phydev) * When this failure happens, the first retry is usually successful but, * in some cases, it may take up to 6 retries to get a decent result */ -int meson_gxl_read_status(struct phy_device *phydev) +static int meson_gxl_read_status(struct phy_device *phydev) { int ret, wol, lpa, exp; -- cgit v1.2.3-59-g8ed1b