diff options
author | Felix Fietkau <nbd@openwrt.org> | 2016-01-03 23:13:11 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2016-01-03 23:13:11 +0000 |
commit | fdf2cdd1ea5b5f58de54883f6a9adc150581704d (patch) | |
tree | 600f293f409bcc81ec5f63deb7374a2870494b44 /target/linux/generic | |
parent | e66198d15e2529d86aee3941cc8d23304d998e85 (diff) | |
download | master-187ad058-fdf2cdd1ea5b5f58de54883f6a9adc150581704d.tar.gz master-187ad058-fdf2cdd1ea5b5f58de54883f6a9adc150581704d.tar.bz2 master-187ad058-fdf2cdd1ea5b5f58de54883f6a9adc150581704d.zip |
kernel: move at803x phy patch to generic
Signed-off-by: Roman Yeryomin <roman@advem.lv>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@48108 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/generic')
4 files changed, 720 insertions, 0 deletions
diff --git a/target/linux/generic/patches-3.18/734-net-phy-at803x-allow-to-configure-via-pdata.patch b/target/linux/generic/patches-3.18/734-net-phy-at803x-allow-to-configure-via-pdata.patch new file mode 100644 index 0000000000..53abcc33ed --- /dev/null +++ b/target/linux/generic/patches-3.18/734-net-phy-at803x-allow-to-configure-via-pdata.patch @@ -0,0 +1,180 @@ +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -12,12 +12,14 @@ + */ + + #include <linux/phy.h> ++#include <linux/mdio.h> + #include <linux/module.h> + #include <linux/string.h> + #include <linux/netdevice.h> + #include <linux/etherdevice.h> + #include <linux/of_gpio.h> + #include <linux/gpio/consumer.h> ++#include <linux/platform_data/phy-at803x.h> + + #define AT803X_INTR_ENABLE 0x12 + #define AT803X_INTR_STATUS 0x13 +@@ -34,8 +36,16 @@ + #define AT803X_INER 0x0012 + #define AT803X_INER_INIT 0xec00 + #define AT803X_INSR 0x0013 ++ ++#define AT803X_PCS_SMART_EEE_CTRL3 0x805D ++#define AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_MASK 0x3 ++#define AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_SHIFT 12 ++#define AT803X_SMART_EEE_CTRL3_LPI_EN BIT(8) ++ + #define AT803X_DEBUG_ADDR 0x1D + #define AT803X_DEBUG_DATA 0x1E ++#define AT803X_DBG0_REG 0x00 ++#define AT803X_DEBUG_RGMII_RX_CLK_DLY BIT(8) + #define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 + #define AT803X_DEBUG_RGMII_TX_CLK_DLY BIT(8) + +@@ -50,6 +60,7 @@ MODULE_LICENSE("GPL"); + struct at803x_priv { + bool phy_reset:1; + struct gpio_desc *gpiod_reset; ++ int prev_speed; + }; + + struct at803x_context { +@@ -61,6 +72,43 @@ struct at803x_context { + u16 led_control; + }; + ++static u16 ++at803x_dbg_reg_rmw(struct phy_device *phydev, u16 reg, u16 clear, u16 set) ++{ ++ struct mii_bus *bus = phydev->bus; ++ int val; ++ ++ mutex_lock(&bus->mdio_lock); ++ ++ bus->write(bus, phydev->addr, AT803X_DEBUG_ADDR, reg); ++ val = bus->read(bus, phydev->addr, AT803X_DEBUG_DATA); ++ if (val < 0) { ++ val = 0xffff; ++ goto out; ++ } ++ ++ val &= ~clear; ++ val |= set; ++ bus->write(bus, phydev->addr, AT803X_DEBUG_DATA, val); ++ ++out: ++ mutex_unlock(&bus->mdio_lock); ++ return val; ++} ++ ++static inline void ++at803x_dbg_reg_set(struct phy_device *phydev, u16 reg, u16 set) ++{ ++ at803x_dbg_reg_rmw(phydev, reg, 0, set); ++} ++ ++static inline void ++at803x_dbg_reg_clr(struct phy_device *phydev, u16 reg, u16 clear) ++{ ++ at803x_dbg_reg_rmw(phydev, reg, clear, 0); ++} ++ ++ + /* save relevant PHY registers to private copy */ + static void at803x_context_save(struct phy_device *phydev, + struct at803x_context *context) +@@ -209,8 +257,16 @@ static int at803x_probe(struct phy_devic + return 0; + } + ++static void at803x_disable_smarteee(struct phy_device *phydev) ++{ ++ phy_write_mmd(phydev, MDIO_MMD_PCS, AT803X_PCS_SMART_EEE_CTRL3, ++ 1 << AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_SHIFT); ++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); ++} ++ + static int at803x_config_init(struct phy_device *phydev) + { ++ struct at803x_platform_data *pdata; + int ret; + + ret = genphy_config_init(phydev); +@@ -228,6 +284,26 @@ static int at803x_config_init(struct phy + return ret; + } + ++ pdata = dev_get_platdata(&phydev->dev); ++ if (pdata) { ++ if (pdata->disable_smarteee) ++ at803x_disable_smarteee(phydev); ++ ++ if (pdata->enable_rgmii_rx_delay) ++ at803x_dbg_reg_set(phydev, AT803X_DBG0_REG, ++ AT803X_DEBUG_RGMII_RX_CLK_DLY); ++ else ++ at803x_dbg_reg_clr(phydev, AT803X_DBG0_REG, ++ AT803X_DEBUG_RGMII_RX_CLK_DLY); ++ ++ if (pdata->enable_rgmii_tx_delay) ++ at803x_dbg_reg_set(phydev, AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ else ++ at803x_dbg_reg_clr(phydev, AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ } ++ + return 0; + } + +@@ -259,6 +335,8 @@ static int at803x_config_intr(struct phy + static void at803x_link_change_notify(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; ++ struct at803x_platform_data *pdata; ++ pdata = dev_get_platdata(&phydev->dev); + + /* + * Conduct a hardware reset for AT8030 every time a link loss is +@@ -289,6 +367,26 @@ static void at803x_link_change_notify(st + priv->phy_reset = false; + } + } ++ if (pdata && pdata->fixup_rgmii_tx_delay && ++ phydev->speed != priv->prev_speed) { ++ switch (phydev->speed) { ++ case SPEED_10: ++ case SPEED_100: ++ at803x_dbg_reg_set(phydev, ++ AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ break; ++ case SPEED_1000: ++ at803x_dbg_reg_clr(phydev, ++ AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ break; ++ default: ++ break; ++ } ++ ++ priv->prev_speed = phydev->speed; ++ } + } + + static struct phy_driver at803x_driver[] = { +--- /dev/null ++++ b/include/linux/platform_data/phy-at803x.h +@@ -0,0 +1,11 @@ ++#ifndef _PHY_AT803X_PDATA_H ++#define _PHY_AT803X_PDATA_H ++ ++struct at803x_platform_data { ++ int disable_smarteee:1; ++ int enable_rgmii_tx_delay:1; ++ int enable_rgmii_rx_delay:1; ++ int fixup_rgmii_tx_delay:1; ++}; ++ ++#endif /* _PHY_AT803X_PDATA_H */ diff --git a/target/linux/generic/patches-4.1/734-net-phy-at803x-allow-to-configure-via-pdata.patch b/target/linux/generic/patches-4.1/734-net-phy-at803x-allow-to-configure-via-pdata.patch new file mode 100644 index 0000000000..53abcc33ed --- /dev/null +++ b/target/linux/generic/patches-4.1/734-net-phy-at803x-allow-to-configure-via-pdata.patch @@ -0,0 +1,180 @@ +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -12,12 +12,14 @@ + */ + + #include <linux/phy.h> ++#include <linux/mdio.h> + #include <linux/module.h> + #include <linux/string.h> + #include <linux/netdevice.h> + #include <linux/etherdevice.h> + #include <linux/of_gpio.h> + #include <linux/gpio/consumer.h> ++#include <linux/platform_data/phy-at803x.h> + + #define AT803X_INTR_ENABLE 0x12 + #define AT803X_INTR_STATUS 0x13 +@@ -34,8 +36,16 @@ + #define AT803X_INER 0x0012 + #define AT803X_INER_INIT 0xec00 + #define AT803X_INSR 0x0013 ++ ++#define AT803X_PCS_SMART_EEE_CTRL3 0x805D ++#define AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_MASK 0x3 ++#define AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_SHIFT 12 ++#define AT803X_SMART_EEE_CTRL3_LPI_EN BIT(8) ++ + #define AT803X_DEBUG_ADDR 0x1D + #define AT803X_DEBUG_DATA 0x1E ++#define AT803X_DBG0_REG 0x00 ++#define AT803X_DEBUG_RGMII_RX_CLK_DLY BIT(8) + #define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 + #define AT803X_DEBUG_RGMII_TX_CLK_DLY BIT(8) + +@@ -50,6 +60,7 @@ MODULE_LICENSE("GPL"); + struct at803x_priv { + bool phy_reset:1; + struct gpio_desc *gpiod_reset; ++ int prev_speed; + }; + + struct at803x_context { +@@ -61,6 +72,43 @@ struct at803x_context { + u16 led_control; + }; + ++static u16 ++at803x_dbg_reg_rmw(struct phy_device *phydev, u16 reg, u16 clear, u16 set) ++{ ++ struct mii_bus *bus = phydev->bus; ++ int val; ++ ++ mutex_lock(&bus->mdio_lock); ++ ++ bus->write(bus, phydev->addr, AT803X_DEBUG_ADDR, reg); ++ val = bus->read(bus, phydev->addr, AT803X_DEBUG_DATA); ++ if (val < 0) { ++ val = 0xffff; ++ goto out; ++ } ++ ++ val &= ~clear; ++ val |= set; ++ bus->write(bus, phydev->addr, AT803X_DEBUG_DATA, val); ++ ++out: ++ mutex_unlock(&bus->mdio_lock); ++ return val; ++} ++ ++static inline void ++at803x_dbg_reg_set(struct phy_device *phydev, u16 reg, u16 set) ++{ ++ at803x_dbg_reg_rmw(phydev, reg, 0, set); ++} ++ ++static inline void ++at803x_dbg_reg_clr(struct phy_device *phydev, u16 reg, u16 clear) ++{ ++ at803x_dbg_reg_rmw(phydev, reg, clear, 0); ++} ++ ++ + /* save relevant PHY registers to private copy */ + static void at803x_context_save(struct phy_device *phydev, + struct at803x_context *context) +@@ -209,8 +257,16 @@ static int at803x_probe(struct phy_devic + return 0; + } + ++static void at803x_disable_smarteee(struct phy_device *phydev) ++{ ++ phy_write_mmd(phydev, MDIO_MMD_PCS, AT803X_PCS_SMART_EEE_CTRL3, ++ 1 << AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_SHIFT); ++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); ++} ++ + static int at803x_config_init(struct phy_device *phydev) + { ++ struct at803x_platform_data *pdata; + int ret; + + ret = genphy_config_init(phydev); +@@ -228,6 +284,26 @@ static int at803x_config_init(struct phy + return ret; + } + ++ pdata = dev_get_platdata(&phydev->dev); ++ if (pdata) { ++ if (pdata->disable_smarteee) ++ at803x_disable_smarteee(phydev); ++ ++ if (pdata->enable_rgmii_rx_delay) ++ at803x_dbg_reg_set(phydev, AT803X_DBG0_REG, ++ AT803X_DEBUG_RGMII_RX_CLK_DLY); ++ else ++ at803x_dbg_reg_clr(phydev, AT803X_DBG0_REG, ++ AT803X_DEBUG_RGMII_RX_CLK_DLY); ++ ++ if (pdata->enable_rgmii_tx_delay) ++ at803x_dbg_reg_set(phydev, AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ else ++ at803x_dbg_reg_clr(phydev, AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ } ++ + return 0; + } + +@@ -259,6 +335,8 @@ static int at803x_config_intr(struct phy + static void at803x_link_change_notify(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; ++ struct at803x_platform_data *pdata; ++ pdata = dev_get_platdata(&phydev->dev); + + /* + * Conduct a hardware reset for AT8030 every time a link loss is +@@ -289,6 +367,26 @@ static void at803x_link_change_notify(st + priv->phy_reset = false; + } + } ++ if (pdata && pdata->fixup_rgmii_tx_delay && ++ phydev->speed != priv->prev_speed) { ++ switch (phydev->speed) { ++ case SPEED_10: ++ case SPEED_100: ++ at803x_dbg_reg_set(phydev, ++ AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ break; ++ case SPEED_1000: ++ at803x_dbg_reg_clr(phydev, ++ AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ break; ++ default: ++ break; ++ } ++ ++ priv->prev_speed = phydev->speed; ++ } + } + + static struct phy_driver at803x_driver[] = { +--- /dev/null ++++ b/include/linux/platform_data/phy-at803x.h +@@ -0,0 +1,11 @@ ++#ifndef _PHY_AT803X_PDATA_H ++#define _PHY_AT803X_PDATA_H ++ ++struct at803x_platform_data { ++ int disable_smarteee:1; ++ int enable_rgmii_tx_delay:1; ++ int enable_rgmii_rx_delay:1; ++ int fixup_rgmii_tx_delay:1; ++}; ++ ++#endif /* _PHY_AT803X_PDATA_H */ diff --git a/target/linux/generic/patches-4.3/734-net-phy-at803x-allow-to-configure-via-pdata.patch b/target/linux/generic/patches-4.3/734-net-phy-at803x-allow-to-configure-via-pdata.patch new file mode 100644 index 0000000000..53abcc33ed --- /dev/null +++ b/target/linux/generic/patches-4.3/734-net-phy-at803x-allow-to-configure-via-pdata.patch @@ -0,0 +1,180 @@ +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -12,12 +12,14 @@ + */ + + #include <linux/phy.h> ++#include <linux/mdio.h> + #include <linux/module.h> + #include <linux/string.h> + #include <linux/netdevice.h> + #include <linux/etherdevice.h> + #include <linux/of_gpio.h> + #include <linux/gpio/consumer.h> ++#include <linux/platform_data/phy-at803x.h> + + #define AT803X_INTR_ENABLE 0x12 + #define AT803X_INTR_STATUS 0x13 +@@ -34,8 +36,16 @@ + #define AT803X_INER 0x0012 + #define AT803X_INER_INIT 0xec00 + #define AT803X_INSR 0x0013 ++ ++#define AT803X_PCS_SMART_EEE_CTRL3 0x805D ++#define AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_MASK 0x3 ++#define AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_SHIFT 12 ++#define AT803X_SMART_EEE_CTRL3_LPI_EN BIT(8) ++ + #define AT803X_DEBUG_ADDR 0x1D + #define AT803X_DEBUG_DATA 0x1E ++#define AT803X_DBG0_REG 0x00 ++#define AT803X_DEBUG_RGMII_RX_CLK_DLY BIT(8) + #define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 + #define AT803X_DEBUG_RGMII_TX_CLK_DLY BIT(8) + +@@ -50,6 +60,7 @@ MODULE_LICENSE("GPL"); + struct at803x_priv { + bool phy_reset:1; + struct gpio_desc *gpiod_reset; ++ int prev_speed; + }; + + struct at803x_context { +@@ -61,6 +72,43 @@ struct at803x_context { + u16 led_control; + }; + ++static u16 ++at803x_dbg_reg_rmw(struct phy_device *phydev, u16 reg, u16 clear, u16 set) ++{ ++ struct mii_bus *bus = phydev->bus; ++ int val; ++ ++ mutex_lock(&bus->mdio_lock); ++ ++ bus->write(bus, phydev->addr, AT803X_DEBUG_ADDR, reg); ++ val = bus->read(bus, phydev->addr, AT803X_DEBUG_DATA); ++ if (val < 0) { ++ val = 0xffff; ++ goto out; ++ } ++ ++ val &= ~clear; ++ val |= set; ++ bus->write(bus, phydev->addr, AT803X_DEBUG_DATA, val); ++ ++out: ++ mutex_unlock(&bus->mdio_lock); ++ return val; ++} ++ ++static inline void ++at803x_dbg_reg_set(struct phy_device *phydev, u16 reg, u16 set) ++{ ++ at803x_dbg_reg_rmw(phydev, reg, 0, set); ++} ++ ++static inline void ++at803x_dbg_reg_clr(struct phy_device *phydev, u16 reg, u16 clear) ++{ ++ at803x_dbg_reg_rmw(phydev, reg, clear, 0); ++} ++ ++ + /* save relevant PHY registers to private copy */ + static void at803x_context_save(struct phy_device *phydev, + struct at803x_context *context) +@@ -209,8 +257,16 @@ static int at803x_probe(struct phy_devic + return 0; + } + ++static void at803x_disable_smarteee(struct phy_device *phydev) ++{ ++ phy_write_mmd(phydev, MDIO_MMD_PCS, AT803X_PCS_SMART_EEE_CTRL3, ++ 1 << AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_SHIFT); ++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); ++} ++ + static int at803x_config_init(struct phy_device *phydev) + { ++ struct at803x_platform_data *pdata; + int ret; + + ret = genphy_config_init(phydev); +@@ -228,6 +284,26 @@ static int at803x_config_init(struct phy + return ret; + } + ++ pdata = dev_get_platdata(&phydev->dev); ++ if (pdata) { ++ if (pdata->disable_smarteee) ++ at803x_disable_smarteee(phydev); ++ ++ if (pdata->enable_rgmii_rx_delay) ++ at803x_dbg_reg_set(phydev, AT803X_DBG0_REG, ++ AT803X_DEBUG_RGMII_RX_CLK_DLY); ++ else ++ at803x_dbg_reg_clr(phydev, AT803X_DBG0_REG, ++ AT803X_DEBUG_RGMII_RX_CLK_DLY); ++ ++ if (pdata->enable_rgmii_tx_delay) ++ at803x_dbg_reg_set(phydev, AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ else ++ at803x_dbg_reg_clr(phydev, AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ } ++ + return 0; + } + +@@ -259,6 +335,8 @@ static int at803x_config_intr(struct phy + static void at803x_link_change_notify(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; ++ struct at803x_platform_data *pdata; ++ pdata = dev_get_platdata(&phydev->dev); + + /* + * Conduct a hardware reset for AT8030 every time a link loss is +@@ -289,6 +367,26 @@ static void at803x_link_change_notify(st + priv->phy_reset = false; + } + } ++ if (pdata && pdata->fixup_rgmii_tx_delay && ++ phydev->speed != priv->prev_speed) { ++ switch (phydev->speed) { ++ case SPEED_10: ++ case SPEED_100: ++ at803x_dbg_reg_set(phydev, ++ AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ break; ++ case SPEED_1000: ++ at803x_dbg_reg_clr(phydev, ++ AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ break; ++ default: ++ break; ++ } ++ ++ priv->prev_speed = phydev->speed; ++ } + } + + static struct phy_driver at803x_driver[] = { +--- /dev/null ++++ b/include/linux/platform_data/phy-at803x.h +@@ -0,0 +1,11 @@ ++#ifndef _PHY_AT803X_PDATA_H ++#define _PHY_AT803X_PDATA_H ++ ++struct at803x_platform_data { ++ int disable_smarteee:1; ++ int enable_rgmii_tx_delay:1; ++ int enable_rgmii_rx_delay:1; ++ int fixup_rgmii_tx_delay:1; ++}; ++ ++#endif /* _PHY_AT803X_PDATA_H */ diff --git a/target/linux/generic/patches-4.4/734-net-phy-at803x-allow-to-configure-via-pdata.patch b/target/linux/generic/patches-4.4/734-net-phy-at803x-allow-to-configure-via-pdata.patch new file mode 100644 index 0000000000..53abcc33ed --- /dev/null +++ b/target/linux/generic/patches-4.4/734-net-phy-at803x-allow-to-configure-via-pdata.patch @@ -0,0 +1,180 @@ +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -12,12 +12,14 @@ + */ + + #include <linux/phy.h> ++#include <linux/mdio.h> + #include <linux/module.h> + #include <linux/string.h> + #include <linux/netdevice.h> + #include <linux/etherdevice.h> + #include <linux/of_gpio.h> + #include <linux/gpio/consumer.h> ++#include <linux/platform_data/phy-at803x.h> + + #define AT803X_INTR_ENABLE 0x12 + #define AT803X_INTR_STATUS 0x13 +@@ -34,8 +36,16 @@ + #define AT803X_INER 0x0012 + #define AT803X_INER_INIT 0xec00 + #define AT803X_INSR 0x0013 ++ ++#define AT803X_PCS_SMART_EEE_CTRL3 0x805D ++#define AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_MASK 0x3 ++#define AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_SHIFT 12 ++#define AT803X_SMART_EEE_CTRL3_LPI_EN BIT(8) ++ + #define AT803X_DEBUG_ADDR 0x1D + #define AT803X_DEBUG_DATA 0x1E ++#define AT803X_DBG0_REG 0x00 ++#define AT803X_DEBUG_RGMII_RX_CLK_DLY BIT(8) + #define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 + #define AT803X_DEBUG_RGMII_TX_CLK_DLY BIT(8) + +@@ -50,6 +60,7 @@ MODULE_LICENSE("GPL"); + struct at803x_priv { + bool phy_reset:1; + struct gpio_desc *gpiod_reset; ++ int prev_speed; + }; + + struct at803x_context { +@@ -61,6 +72,43 @@ struct at803x_context { + u16 led_control; + }; + ++static u16 ++at803x_dbg_reg_rmw(struct phy_device *phydev, u16 reg, u16 clear, u16 set) ++{ ++ struct mii_bus *bus = phydev->bus; ++ int val; ++ ++ mutex_lock(&bus->mdio_lock); ++ ++ bus->write(bus, phydev->addr, AT803X_DEBUG_ADDR, reg); ++ val = bus->read(bus, phydev->addr, AT803X_DEBUG_DATA); ++ if (val < 0) { ++ val = 0xffff; ++ goto out; ++ } ++ ++ val &= ~clear; ++ val |= set; ++ bus->write(bus, phydev->addr, AT803X_DEBUG_DATA, val); ++ ++out: ++ mutex_unlock(&bus->mdio_lock); ++ return val; ++} ++ ++static inline void ++at803x_dbg_reg_set(struct phy_device *phydev, u16 reg, u16 set) ++{ ++ at803x_dbg_reg_rmw(phydev, reg, 0, set); ++} ++ ++static inline void ++at803x_dbg_reg_clr(struct phy_device *phydev, u16 reg, u16 clear) ++{ ++ at803x_dbg_reg_rmw(phydev, reg, clear, 0); ++} ++ ++ + /* save relevant PHY registers to private copy */ + static void at803x_context_save(struct phy_device *phydev, + struct at803x_context *context) +@@ -209,8 +257,16 @@ static int at803x_probe(struct phy_devic + return 0; + } + ++static void at803x_disable_smarteee(struct phy_device *phydev) ++{ ++ phy_write_mmd(phydev, MDIO_MMD_PCS, AT803X_PCS_SMART_EEE_CTRL3, ++ 1 << AT803X_SMART_EEE_CTRL3_LPI_TX_DELAY_SEL_SHIFT); ++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); ++} ++ + static int at803x_config_init(struct phy_device *phydev) + { ++ struct at803x_platform_data *pdata; + int ret; + + ret = genphy_config_init(phydev); +@@ -228,6 +284,26 @@ static int at803x_config_init(struct phy + return ret; + } + ++ pdata = dev_get_platdata(&phydev->dev); ++ if (pdata) { ++ if (pdata->disable_smarteee) ++ at803x_disable_smarteee(phydev); ++ ++ if (pdata->enable_rgmii_rx_delay) ++ at803x_dbg_reg_set(phydev, AT803X_DBG0_REG, ++ AT803X_DEBUG_RGMII_RX_CLK_DLY); ++ else ++ at803x_dbg_reg_clr(phydev, AT803X_DBG0_REG, ++ AT803X_DEBUG_RGMII_RX_CLK_DLY); ++ ++ if (pdata->enable_rgmii_tx_delay) ++ at803x_dbg_reg_set(phydev, AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ else ++ at803x_dbg_reg_clr(phydev, AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ } ++ + return 0; + } + +@@ -259,6 +335,8 @@ static int at803x_config_intr(struct phy + static void at803x_link_change_notify(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; ++ struct at803x_platform_data *pdata; ++ pdata = dev_get_platdata(&phydev->dev); + + /* + * Conduct a hardware reset for AT8030 every time a link loss is +@@ -289,6 +367,26 @@ static void at803x_link_change_notify(st + priv->phy_reset = false; + } + } ++ if (pdata && pdata->fixup_rgmii_tx_delay && ++ phydev->speed != priv->prev_speed) { ++ switch (phydev->speed) { ++ case SPEED_10: ++ case SPEED_100: ++ at803x_dbg_reg_set(phydev, ++ AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ break; ++ case SPEED_1000: ++ at803x_dbg_reg_clr(phydev, ++ AT803X_DEBUG_SYSTEM_MODE_CTRL, ++ AT803X_DEBUG_RGMII_TX_CLK_DLY); ++ break; ++ default: ++ break; ++ } ++ ++ priv->prev_speed = phydev->speed; ++ } + } + + static struct phy_driver at803x_driver[] = { +--- /dev/null ++++ b/include/linux/platform_data/phy-at803x.h +@@ -0,0 +1,11 @@ ++#ifndef _PHY_AT803X_PDATA_H ++#define _PHY_AT803X_PDATA_H ++ ++struct at803x_platform_data { ++ int disable_smarteee:1; ++ int enable_rgmii_tx_delay:1; ++ int enable_rgmii_rx_delay:1; ++ int fixup_rgmii_tx_delay:1; ++}; ++ ++#endif /* _PHY_AT803X_PDATA_H */ |