diff options
Diffstat (limited to 'drivers/net/phy/micrel.c')
-rw-r--r-- | drivers/net/phy/micrel.c | 96 |
1 files changed, 95 insertions, 1 deletions
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 18cc5e4280e8..e297be3449e6 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -705,6 +705,90 @@ static int ksz9131_of_load_skew_values(struct phy_device *phydev, return phy_write_mmd(phydev, 2, reg, newval); } +#define KSZ9131RN_MMD_COMMON_CTRL_REG 2 +#define KSZ9131RN_RXC_DLL_CTRL 76 +#define KSZ9131RN_TXC_DLL_CTRL 77 +#define KSZ9131RN_DLL_CTRL_BYPASS BIT_MASK(12) +#define KSZ9131RN_DLL_ENABLE_DELAY 0 +#define KSZ9131RN_DLL_DISABLE_DELAY BIT(12) + +static int ksz9131_config_rgmii_delay(struct phy_device *phydev) +{ + u16 rxcdll_val, txcdll_val; + int new, ret; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + break; + default: + return 0; + } + + ret = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_RXC_DLL_CTRL); + if (ret < 0) + return ret; + + new = (ret & ~KSZ9131RN_DLL_CTRL_BYPASS) | rxcdll_val; + + if (new != ret) { + ret = phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_RXC_DLL_CTRL, new); + if (ret < 0) + return ret; + } + + ret = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_TXC_DLL_CTRL); + if (ret < 0) + return ret; + + new = (ret & ~KSZ9131RN_DLL_CTRL_BYPASS) | txcdll_val; + + if (new != ret) { + ret = phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_TXC_DLL_CTRL, new); + if (ret < 0) + return ret; + } + + return 0; +} + +/* Silicon Errata DS80000693B + * + * When LEDs are configured in Individual Mode, LED1 is ON in a no-link + * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves + * according to the datasheet (off if there is no link). + */ +static int ksz9131_led_errata(struct phy_device *phydev) +{ + int reg; + + reg = phy_read_mmd(phydev, 2, 0); + if (reg < 0) + return reg; + + if (!(reg & BIT(4))) + return 0; + + return phy_set_bits(phydev, 0x1e, BIT(9)); +} + static int ksz9131_config_init(struct phy_device *phydev) { const struct device *dev = &phydev->mdio.dev; @@ -731,6 +815,12 @@ static int ksz9131_config_init(struct phy_device *phydev) if (!of_node) return 0; + if (phy_interface_is_rgmii(phydev)) { + ret = ksz9131_config_rgmii_delay(phydev); + if (ret < 0) + return ret; + } + ret = ksz9131_of_load_skew_values(phydev, of_node, MII_KSZ9031RN_CLK_PAD_SKEW, 5, clk_skews, 2); @@ -755,6 +845,10 @@ static int ksz9131_config_init(struct phy_device *phydev) if (ret < 0) return ret; + ret = ksz9131_led_errata(phydev); + if (ret < 0) + return ret; + return 0; } @@ -1162,7 +1256,7 @@ static struct phy_driver ksphy_driver[] = { .get_sset_count = kszphy_get_sset_count, .get_strings = kszphy_get_strings, .get_stats = kszphy_get_stats, - .suspend = genphy_suspend, + .suspend = kszphy_suspend, .resume = kszphy_resume, }, { .phy_id = PHY_ID_KSZ8873MLL, |