summaryrefslogtreecommitdiff
path: root/drivers/net/phy/micrel.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/micrel.c')
-rw-r--r--drivers/net/phy/micrel.c96
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,