From af54c0148b4a4e3ba519f949d942cbce07112735 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Sat, 9 May 2015 11:09:49 +0200 Subject: [PATCH] linux: add net phy backports for phy port patch --- ...ink-partner-features-through-ethtool.patch | 87 ++++++++++ ...hy_init_hw-instead-of-open-coding-it.patch | 36 ++++ ...consolidate-PHY-reset-in-phy_init_hw.patch | 161 ++++++++++++++++++ ...-software-reset-to-genphy_soft_reset.patch | 81 +++++++++ 4 files changed, 365 insertions(+) create mode 100644 target/linux/generic/952-net-phy-report-link-partner-features-through-ethtool.patch create mode 100644 target/linux/generic/953-net-phy-use-phy_init_hw-instead-of-open-coding-it.patch create mode 100644 target/linux/generic/955-net-phy-consolidate-PHY-reset-in-phy_init_hw.patch create mode 100644 target/linux/generic/957-net-phy-move-PHY-software-reset-to-genphy_soft_reset.patch diff --git a/target/linux/generic/952-net-phy-report-link-partner-features-through-ethtool.patch b/target/linux/generic/952-net-phy-report-link-partner-features-through-ethtool.patch new file mode 100644 index 0000000..a9960e2 --- /dev/null +++ b/target/linux/generic/952-net-phy-report-link-partner-features-through-ethtool.patch @@ -0,0 +1,87 @@ +From 114002bc1af6c63de4c003f8c5d3ca0bb430d987 Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +Date: Fri, 6 Dec 2013 13:01:30 -0800 +Subject: [PATCH] net: phy: report link partner features through ethtool + +The PHY library already reads the MII_STAT1000 and MII_LPA registers in +genphy_read_status(), so extend it to also populate the PHY device link +partner advertised features such that we can feed this back into ethtool +when asked for it in phy_ethtool_gset(). + +Signed-off-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/phy/phy.c | 1 + + drivers/net/phy/phy_device.c | 6 ++++++ + include/linux/phy.h | 5 +++-- + 3 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c +index 36c6994..05cb8fe 100644 +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -289,6 +289,7 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd) + cmd->supported = phydev->supported; + + cmd->advertising = phydev->advertising; ++ cmd->lp_advertising = phydev->lp_advertising; + + ethtool_cmd_speed_set(cmd, phydev->speed); + cmd->duplex = phydev->duplex; +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index d6447b3..6db3659 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -839,6 +839,8 @@ int genphy_read_status(struct phy_device *phydev) + if (err) + return err; + ++ phydev->lp_advertising = 0; ++ + if (AUTONEG_ENABLE == phydev->autoneg) { + if (phydev->supported & (SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full)) { +@@ -852,6 +854,8 @@ int genphy_read_status(struct phy_device *phydev) + if (adv < 0) + return adv; + ++ phydev->lp_advertising = ++ mii_stat1000_to_ethtool_lpa_t(lpagb); + lpagb &= adv << 2; + } + +@@ -860,6 +864,8 @@ int genphy_read_status(struct phy_device *phydev) + if (lpa < 0) + return lpa; + ++ phydev->lp_advertising |= mii_lpa_to_ethtool_lpa_t(lpa); ++ + adv = phy_read(phydev, MII_ADVERTISE); + + if (adv < 0) +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 7ff751a..90a666e 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -287,8 +287,8 @@ struct phy_c45_device_ids { + * adjust_state: Callback for the enet driver to respond to + * changes in the state machine. + * +- * speed, duplex, pause, supported, advertising, and +- * autoneg are used like in mii_if_info ++ * speed, duplex, pause, supported, advertising, lp_advertising, ++ * and autoneg are used like in mii_if_info + * + * interrupts currently only supports enabled or disabled, + * but could be changed in the future to support enabling +@@ -340,6 +340,7 @@ struct phy_device { + /* See mii.h for more info */ + u32 supported; + u32 advertising; ++ u32 lp_advertising; + + int autoneg; + +-- +2.3.5 + diff --git a/target/linux/generic/953-net-phy-use-phy_init_hw-instead-of-open-coding-it.patch b/target/linux/generic/953-net-phy-use-phy_init_hw-instead-of-open-coding-it.patch new file mode 100644 index 0000000..6f86af0 --- /dev/null +++ b/target/linux/generic/953-net-phy-use-phy_init_hw-instead-of-open-coding-it.patch @@ -0,0 +1,36 @@ +From 2613f95f614b39681f77a5036046ea395e306e28 Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +Date: Fri, 6 Dec 2013 13:01:31 -0800 +Subject: [PATCH] net: phy: use phy_init_hw instead of open-coding it + +Use phy_init_hw() instead of open-coding it in phy_mii_ioctl(), this +improves consistenty and makes sure that we will not duplicate the same +routine somewhere else. + +Signed-off-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/phy/phy.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c +index 05cb8fe..5d7101b 100644 +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -361,11 +361,8 @@ int phy_mii_ioctl(struct phy_device *phydev, + mii_data->reg_num, val); + + if (mii_data->reg_num == MII_BMCR && +- val & BMCR_RESET && +- phydev->drv->config_init) { +- phy_scan_fixups(phydev); +- phydev->drv->config_init(phydev); +- } ++ val & BMCR_RESET) ++ phy_init_hw(phydev); + break; + + case SIOCSHWTSTAMP: +-- +2.3.5 + diff --git a/target/linux/generic/955-net-phy-consolidate-PHY-reset-in-phy_init_hw.patch b/target/linux/generic/955-net-phy-consolidate-PHY-reset-in-phy_init_hw.patch new file mode 100644 index 0000000..e8d0a33 --- /dev/null +++ b/target/linux/generic/955-net-phy-consolidate-PHY-reset-in-phy_init_hw.patch @@ -0,0 +1,161 @@ +From 87aa9f9c61ad56d505641681812e92ad976f8608 Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +Date: Fri, 6 Dec 2013 13:01:34 -0800 +Subject: [PATCH] net: phy: consolidate PHY reset in phy_init_hw() + +There are quite a lot of drivers touching a PHY device MII_BMCR +register to reset the PHY without taking care of: + +1) ensuring that BMCR_RESET is cleared after a given timeout +2) the PHY state machine resuming to the proper state and re-applying +potentially changed settings such as auto-negotiation + +Introduce phy_poll_reset() which will take care of polling the MII_BMCR +for the BMCR_RESET bit to be cleared after a given timeout or return a +timeout error code. + +In order to make sure the PHY is in a correct state, phy_init_hw() first +issues a software reset through MII_BMCR and then applies any fixups. + +Signed-off-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + Documentation/networking/phy.txt | 3 ++- + drivers/net/phy/phy.c | 5 ++-- + drivers/net/phy/phy_device.c | 56 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 60 insertions(+), 4 deletions(-) + +diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt +index d5b1a39..ebf2707 100644 +--- a/Documentation/networking/phy.txt ++++ b/Documentation/networking/phy.txt +@@ -255,7 +255,8 @@ Writing a PHY driver + + config_init: configures PHY into a sane state after a reset. + For instance, a Davicom PHY requires descrambling disabled. +- probe: Does any setup needed by the driver ++ probe: Allocate phy->priv, optionally refuse to bind. ++ PHY may not have been reset or had fixups run yet. + suspend/resume: power management + config_aneg: Changes the speed/duplex/negotiation settings + read_status: Reads the current speed/duplex/negotiation settings +diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c +index 5d7101b..e3dd691 100644 +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -318,6 +318,7 @@ int phy_mii_ioctl(struct phy_device *phydev, + { + struct mii_ioctl_data *mii_data = if_mii(ifr); + u16 val = mii_data->val_in; ++ int ret = 0; + + switch (cmd) { + case SIOCGMIIPHY: +@@ -362,7 +363,7 @@ int phy_mii_ioctl(struct phy_device *phydev, + + if (mii_data->reg_num == MII_BMCR && + val & BMCR_RESET) +- phy_init_hw(phydev); ++ ret = phy_init_hw(phydev); + break; + + case SIOCSHWTSTAMP: +@@ -374,7 +375,7 @@ int phy_mii_ioctl(struct phy_device *phydev, + return -EOPNOTSUPP; + } + +- return 0; ++ return ret; + } + EXPORT_SYMBOL(phy_mii_ioctl); + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index 6db3659..5a619f0 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -364,7 +364,11 @@ int phy_device_register(struct phy_device *phydev) + phydev->bus->phy_map[phydev->addr] = phydev; + + /* Run all of the fixups for this PHY */ +- phy_scan_fixups(phydev); ++ err = phy_init_hw(phydev); ++ if (err) { ++ pr_err("PHY %d failed to initialize\n", phydev->addr); ++ goto out; ++ } + + err = device_add(&phydev->dev); + if (err) { +@@ -497,6 +501,47 @@ void phy_disconnect(struct phy_device *phydev) + } + EXPORT_SYMBOL(phy_disconnect); + ++/** ++ * phy_poll_reset - Safely wait until a PHY reset has properly completed ++ * @phydev: The PHY device to poll ++ * ++ * Description: According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1, as ++ * published in 2008, a PHY reset may take up to 0.5 seconds. The MII BMCR ++ * register must be polled until the BMCR_RESET bit clears. ++ * ++ * Furthermore, any attempts to write to PHY registers may have no effect ++ * or even generate MDIO bus errors until this is complete. ++ * ++ * Some PHYs (such as the Marvell 88E1111) don't entirely conform to the ++ * standard and do not fully reset after the BMCR_RESET bit is set, and may ++ * even *REQUIRE* a soft-reset to properly restart autonegotiation. In an ++ * effort to support such broken PHYs, this function is separate from the ++ * standard phy_init_hw() which will zero all the other bits in the BMCR ++ * and reapply all driver-specific and board-specific fixups. ++ */ ++static int phy_poll_reset(struct phy_device *phydev) ++{ ++ /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ ++ unsigned int retries = 12; ++ int ret; ++ ++ do { ++ msleep(50); ++ ret = phy_read(phydev, MII_BMCR); ++ if (ret < 0) ++ return ret; ++ } while (ret & BMCR_RESET && --retries); ++ if (ret & BMCR_RESET) ++ return -ETIMEDOUT; ++ ++ /* ++ * Some chips (smsc911x) may still need up to another 1ms after the ++ * BMCR_RESET bit is cleared before they are usable. ++ */ ++ msleep(1); ++ return 0; ++} ++ + int phy_init_hw(struct phy_device *phydev) + { + int ret; +@@ -504,12 +549,21 @@ int phy_init_hw(struct phy_device *phydev) + if (!phydev->drv || !phydev->drv->config_init) + return 0; + ++ ret = phy_write(phydev, MII_BMCR, BMCR_RESET); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_poll_reset(phydev); ++ if (ret < 0) ++ return ret; ++ + ret = phy_scan_fixups(phydev); + if (ret < 0) + return ret; + + return phydev->drv->config_init(phydev); + } ++EXPORT_SYMBOL(phy_init_hw); + + /** + * phy_attach_direct - attach a network device to a given PHY device pointer +-- +2.3.5 + diff --git a/target/linux/generic/957-net-phy-move-PHY-software-reset-to-genphy_soft_reset.patch b/target/linux/generic/957-net-phy-move-PHY-software-reset-to-genphy_soft_reset.patch new file mode 100644 index 0000000..a2f55a7 --- /dev/null +++ b/target/linux/generic/957-net-phy-move-PHY-software-reset-to-genphy_soft_reset.patch @@ -0,0 +1,81 @@ +From 797ac07137d9ae8572008e21e6123a9ae17dae50 Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +Date: Mon, 17 Feb 2014 13:34:02 -0800 +Subject: [PATCH] net: phy: move PHY software reset to genphy_soft_reset + +As pointed out by Shaohui, this function is generic for 10/100/1000 +PHYs, but 10G PHYs might have a slightly different reset sequence which +prevents most of them from using this function. + +Move the BMCR_RESET based software resent sequence to +genphy_soft_reset() in preparation for allowing PHY drivers to implement +a soft_reset() callback. + +Reported-by: Shaohui Xie +Signed-off-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/phy/phy_device.c | 27 ++++++++++++++++++++++----- + include/linux/phy.h | 1 + + 2 files changed, 23 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index c2d778d..7c21b82 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -540,11 +540,7 @@ int phy_init_hw(struct phy_device *phydev) + if (!phydev->drv || !phydev->drv->config_init) + return 0; + +- ret = phy_write(phydev, MII_BMCR, BMCR_RESET); +- if (ret < 0) +- return ret; +- +- ret = phy_poll_reset(phydev); ++ ret = genphy_soft_reset(phydev); + if (ret < 0) + return ret; + +@@ -1045,6 +1041,27 @@ static int gen10g_read_status(struct phy_device *phydev) + return 0; + } + ++/** ++ * genphy_soft_reset - software reset the PHY via BMCR_RESET bit ++ * @phydev: target phy_device struct ++ * ++ * Description: Perform a software PHY reset using the standard ++ * BMCR_RESET bit and poll for the reset bit to be cleared. ++ * ++ * Returns: 0 on success, < 0 on failure ++ */ ++int genphy_soft_reset(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = phy_write(phydev, MII_BMCR, BMCR_RESET); ++ if (ret < 0) ++ return ret; ++ ++ return phy_poll_reset(phydev); ++} ++EXPORT_SYMBOL(genphy_soft_reset); ++ + static int genphy_config_init(struct phy_device *phydev) + { + int val; +diff --git a/include/linux/phy.h b/include/linux/phy.h +index f7fe546..bffe0ec 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -666,6 +666,7 @@ int genphy_update_link(struct phy_device *phydev); + int genphy_read_status(struct phy_device *phydev); + int genphy_suspend(struct phy_device *phydev); + int genphy_resume(struct phy_device *phydev); ++int genphy_soft_reset(struct phy_device *phydev); + void phy_driver_unregister(struct phy_driver *drv); + void phy_drivers_unregister(struct phy_driver *drv, int n); + int phy_driver_register(struct phy_driver *new_driver); +-- +2.3.5 +