[U-Boot] [PATCH 0/2] net: phy: allow disabling SmartEEE for Atheros PHYs

The first patch moves the implementations of phy_read_mmd_indirect and phy_write_mmd_indirect from the TI PHY driver into the core PHY driver code, and the second patch makes use of these functions to allow users to disable SmartEEE.
Vladimir Oltean (2): net: phy: promote phy_{read,write}_mmd_indirect from ti.c to generic code net: phy: at803x: Address packet drops at low traffic rate due to SmartEEE feature
drivers/net/phy/Kconfig | 21 +++++++++++++ drivers/net/phy/atheros.c | 28 +++++++++++++++++ drivers/net/phy/phy.c | 68 ++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/ti.c | 76 ----------------------------------------------- include/linux/mdio.h | 10 +++++++ include/phy.h | 37 +++++++++++++++++++++++ 6 files changed, 164 insertions(+), 76 deletions(-)

* These are convenience functions for accessing PHY MMD registers through clause 22 (indirectly through registers 0xD and 0xE), and are exported in the Linux phylib as well.
Signed-off-by: Vladimir Oltean vladimir.oltean@nxp.com --- drivers/net/phy/phy.c | 68 +++++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/ti.c | 76 --------------------------------------------------- include/linux/mdio.h | 10 +++++++ include/phy.h | 37 +++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 76 deletions(-)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index e837eb7..e71a269 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -17,6 +17,7 @@ #include <phy.h> #include <errno.h> #include <linux/err.h> +#include <linux/mdio.h> #include <linux/compiler.h>
DECLARE_GLOBAL_DATA_PTR; @@ -954,3 +955,70 @@ int phy_get_interface_by_name(const char *str)
return -1; } + +/** + * phy_read_mmd_indirect - reads data from the MMD registers + * @phydev: The PHY device bus + * @prtad: MMD Address + * @devad: MMD DEVAD + * @addr: PHY address on the MII bus + * + * Description: it reads data from the MMD registers (clause 22 to access to + * clause 45) of the specified phy address. + * To read these registers we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Read reg 14 // Read MMD data + */ +int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, + int devad, int addr) +{ + int value = -1; + + /* Write the desired MMD Devad */ + phy_write(phydev, addr, MII_MMD_CTRL, devad); + + /* Write the desired MMD register address */ + phy_write(phydev, addr, MII_MMD_DATA, prtad); + + /* Select the Function : DATA with no post increment */ + phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); + + /* Read the content of the MMD's selected register */ + value = phy_read(phydev, addr, MII_MMD_DATA); + return value; +} + +/** + * phy_write_mmd_indirect - writes data to the MMD registers + * @phydev: The PHY device + * @prtad: MMD Address + * @devad: MMD DEVAD + * @addr: PHY address on the MII bus + * @data: data to write in the MMD register + * + * Description: Write data from the MMD registers of the specified + * phy address. + * To write these registers we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Write reg 14 // Write MMD data + */ +void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, + int devad, int addr, u32 data) +{ + /* Write the desired MMD Devad */ + phy_write(phydev, addr, MII_MMD_CTRL, devad); + + /* Write the desired MMD register address */ + phy_write(phydev, addr, MII_MMD_DATA, prtad); + + /* Select the Function : DATA with no post increment */ + phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); + + /* Write the data into MMD's selected register */ + phy_write(phydev, addr, MII_MMD_DATA, data); +} + diff --git a/drivers/net/phy/ti.c b/drivers/net/phy/ti.c index 6db6edd..915d6bf 100644 --- a/drivers/net/phy/ti.c +++ b/drivers/net/phy/ti.c @@ -73,16 +73,6 @@ #define MII_DP83867_CFG2_SPEEDOPT_INTLOW 0x2000 #define MII_DP83867_CFG2_MASK 0x003F
-#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ -#define MII_MMD_DATA 0x0e /* MMD Access Data Register */ - -/* MMD Access Control register fields */ -#define MII_MMD_CTRL_DEVAD_MASK 0x1f /* Mask MMD DEVAD*/ -#define MII_MMD_CTRL_ADDR 0x0000 /* Address */ -#define MII_MMD_CTRL_NOINCR 0x4000 /* no post increment */ -#define MII_MMD_CTRL_INCR_RDWT 0x8000 /* post increment on reads & writes */ -#define MII_MMD_CTRL_INCR_ON_WT 0xC000 /* post increment on writes only */ - /* User setting - can be taken from DTS */ #define DEFAULT_RX_ID_DELAY DP83867_RGMIIDCTL_2_25_NS #define DEFAULT_TX_ID_DELAY DP83867_RGMIIDCTL_2_75_NS @@ -116,72 +106,6 @@ struct dp83867_private { int clk_output_sel; };
-/** - * phy_read_mmd_indirect - reads data from the MMD registers - * @phydev: The PHY device bus - * @prtad: MMD Address - * @devad: MMD DEVAD - * @addr: PHY address on the MII bus - * - * Description: it reads data from the MMD registers (clause 22 to access to - * clause 45) of the specified phy address. - * To read these registers we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Read reg 14 // Read MMD data - */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, int addr) -{ - int value = -1; - - /* Write the desired MMD Devad */ - phy_write(phydev, addr, MII_MMD_CTRL, devad); - - /* Write the desired MMD register address */ - phy_write(phydev, addr, MII_MMD_DATA, prtad); - - /* Select the Function : DATA with no post increment */ - phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); - - /* Read the content of the MMD's selected register */ - value = phy_read(phydev, addr, MII_MMD_DATA); - return value; -} - -/** - * phy_write_mmd_indirect - writes data to the MMD registers - * @phydev: The PHY device - * @prtad: MMD Address - * @devad: MMD DEVAD - * @addr: PHY address on the MII bus - * @data: data to write in the MMD register - * - * Description: Write data from the MMD registers of the specified - * phy address. - * To write these registers we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Write reg 14 // Write MMD data - */ -void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, int addr, u32 data) -{ - /* Write the desired MMD Devad */ - phy_write(phydev, addr, MII_MMD_CTRL, devad); - - /* Write the desired MMD register address */ - phy_write(phydev, addr, MII_MMD_DATA, prtad); - - /* Select the Function : DATA with no post increment */ - phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); - - /* Write the data into MMD's selected register */ - phy_write(phydev, addr, MII_MMD_DATA, data); -} - static int dp83867_config_port_mirroring(struct phy_device *phydev) { struct dp83867_private *dp83867 = diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 6e821d9..87704a0 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -13,6 +13,16 @@
#include <linux/mii.h>
+#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ +#define MII_MMD_DATA 0x0e /* MMD Access Data Register */ + +/* MMD Access Control register fields */ +#define MII_MMD_CTRL_DEVAD_MASK 0x1f /* Mask MMD DEVAD*/ +#define MII_MMD_CTRL_ADDR 0x0000 /* Address */ +#define MII_MMD_CTRL_NOINCR 0x4000 /* no post increment */ +#define MII_MMD_CTRL_INCR_RDWT 0x8000 /* post increment on reads & writes */ +#define MII_MMD_CTRL_INCR_ON_WT 0xC000 /* post increment on writes only */ + /* MDIO Manageable Devices (MMDs). */ #define MDIO_MMD_PMAPMD 1 /* Physical Medium Attachment/ * Physical Medium Dependent */ diff --git a/include/phy.h b/include/phy.h index b86fdfb..71fe151 100644 --- a/include/phy.h +++ b/include/phy.h @@ -274,6 +274,43 @@ static inline bool phy_interface_is_sgmii(struct phy_device *phydev) phydev->interface <= PHY_INTERFACE_MODE_QSGMII; }
+/** + * phy_read_mmd_indirect - reads data from the MMD registers + * @phydev: The PHY device bus + * @prtad: MMD Address + * @devad: MMD DEVAD + * @addr: PHY address on the MII bus + * + * Description: it reads data from the MMD registers (clause 22 to access to + * clause 45) of the specified phy address. + * To read these registers we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Read reg 14 // Read MMD data + */ +int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, + int devad, int addr); + +/** + * phy_write_mmd_indirect - writes data to the MMD registers + * @phydev: The PHY device + * @prtad: MMD Address + * @devad: MMD DEVAD + * @addr: PHY address on the MII bus + * @data: data to write in the MMD register + * + * Description: Write data from the MMD registers of the specified + * phy address. + * To write these registers we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Write reg 14 // Write MMD data + */ +void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, + int devad, int addr, u32 data); + /* PHY UIDs for various PHYs that are referenced in external code */ #define PHY_UID_CS4340 0x13e51002 #define PHY_UID_CS4223 0x03e57003

On Tue, Dec 18, 2018 at 7:22 PM Vladimir Oltean vladimir.oltean@nxp.com wrote:
- These are convenience functions for accessing PHY MMD registers through clause 22 (indirectly through registers 0xD and 0xE), and are exported in the Linux phylib as well.
Signed-off-by: Vladimir Oltean vladimir.oltean@nxp.com
Acked-by: Joe Hershberger joe.hershberger@ni.com

* According to the AR8031 and AR8035 datasheets, smartEEE mode (active by default) makes the PHY enter sleep after a configurable idle time. It does this autonomously, without LPI (Low Power Idle) signals coming from MAC. AR8021 does not appear to support this. * This patch allows disabling the SmartEEE feature of above PHYs. * Tested with ping (default of 1 second interval) over back-to-back RGMII between 2 boards having AR8035 at both ends: - Without SmartEEE: 225 packets transmitted, 145 received, 35% packet loss, time 229334ms - With SmartEEE: 144 packets transmitted, 144 received, 0% packet loss, time 146378ms
Signed-off-by: Vladimir Oltean vladimir.oltean@nxp.com --- drivers/net/phy/Kconfig | 21 +++++++++++++++++++++ drivers/net/phy/atheros.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+)
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 3dc0822..6abe8c5 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -94,6 +94,27 @@ config PHY_AQUANTIA_FW_NAME config PHY_ATHEROS bool "Atheros Ethernet PHYs support"
+config PHY_ATHEROS_SMART_EEE + depends on PHY_ATHEROS + default y + tristate "SmartEEE feature for Atheros PHYs" + help + Enables the Atheros SmartEEE feature (not IEEE 802.3az). When 2 PHYs + which support this feature are connected back-to-back, they may + negotiate a low-power sleep mode autonomously, without the Ethernet + controller's knowledge. This setting may cause issues under 2 known + circumstances (both noticed at low traffic rates): + - If the voltage rails on the PHY are unstable, then the PHY can + actually reset as it enters the low power state. This means that + the frames it is supposed to buffer until it wakes up are going + to be dropped instead. + - If 1588/PTP synchronization is the only traffic source over this + PHY, the delays caused by the sleep/wakeup time are going to add + to the synchronization error between the master and the slave. + Default y, which means that the PHY's out-of-reset state is not + changed (SmartEEE active). To work around the issues described + above, change to n. + config PHY_BROADCOM bool "Broadcom Ethernet PHYs support"
diff --git a/drivers/net/phy/atheros.c b/drivers/net/phy/atheros.c index 3783d15..a3c84fd 100644 --- a/drivers/net/phy/atheros.c +++ b/drivers/net/phy/atheros.c @@ -5,6 +5,7 @@ * Copyright 2011, 2013 Freescale Semiconductor, Inc. * author Andy Fleming */ +#include <linux/bitops.h> #include <common.h> #include <phy.h>
@@ -17,6 +18,23 @@ #define AR803x_DEBUG_REG_0 0x0 #define AR803x_RGMII_RX_CLK_DLY 0x8000
+#define AR803X_LPI_EN BIT(8) + +static void ar803x_enable_smart_eee(struct phy_device *phydev, bool on) +{ + int value; + + /* 5.1.11 Smart_eee control3 */ + value = phy_read_mmd_indirect(phydev, 0x805D, MDIO_MMD_PCS, + MDIO_DEVAD_NONE); + if (on) + value |= AR803X_LPI_EN; + else + value &= ~AR803X_LPI_EN; + phy_write_mmd_indirect(phydev, 0x805D, MDIO_MMD_PCS, + MDIO_DEVAD_NONE, value); +} + static int ar8021_config(struct phy_device *phydev) { phy_write(phydev, MDIO_DEVAD_NONE, 0x00, 0x1200); @@ -29,6 +47,11 @@ static int ar8021_config(struct phy_device *phydev)
static int ar8031_config(struct phy_device *phydev) { +#ifdef CONFIG_PHY_ATHEROS_SMART_EEE + ar803x_enable_smart_eee(phydev, true); +#else + ar803x_enable_smart_eee(phydev, false); +#endif if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID || phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { phy_write(phydev, MDIO_DEVAD_NONE, AR803x_PHY_DEBUG_ADDR_REG, @@ -57,6 +80,11 @@ static int ar8035_config(struct phy_device *phydev) { int regval;
+#ifdef CONFIG_PHY_ATHEROS_SMART_EEE + ar803x_enable_smart_eee(phydev, true); +#else + ar803x_enable_smart_eee(phydev, false); +#endif phy_write(phydev, MDIO_DEVAD_NONE, 0xd, 0x0007); phy_write(phydev, MDIO_DEVAD_NONE, 0xe, 0x8016); phy_write(phydev, MDIO_DEVAD_NONE, 0xd, 0x4007);

On Tue, Dec 18, 2018 at 7:22 PM Vladimir Oltean vladimir.oltean@nxp.com wrote:
- According to the AR8031 and AR8035 datasheets, smartEEE mode (active by default) makes the PHY enter sleep after a configurable idle time. It does this autonomously, without LPI (Low Power Idle) signals coming from MAC. AR8021 does not appear to support this.
- This patch allows disabling the SmartEEE feature of above PHYs.
- Tested with ping (default of 1 second interval) over back-to-back RGMII between 2 boards having AR8035 at both ends:
225 packets transmitted, 145 received, 35% packet loss, time 229334ms
- Without SmartEEE:
144 packets transmitted, 144 received, 0% packet loss, time 146378ms
- With SmartEEE:
Signed-off-by: Vladimir Oltean vladimir.oltean@nxp.com
Acked-by: Joe Hershberger joe.hershberger@ni.com
participants (2)
-
Joe Hershberger
-
Vladimir Oltean