From: Marc Kleine-Budde <m...@pengutronix.de>

This imports the micrel KSZ9131 gigabit phy driver from Linux, commit
0316c7e66bbd16cf2d01a4e2f5afa6afb01278f2.

Signed-off-by: Marc Kleine-Budde <m...@pengutronix.de>
Signed-off-by: Stefan Kerkmann <s.kerkm...@pengutronix.de>
---
 drivers/net/phy/micrel.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 188 insertions(+)

diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 36cc857a2c..a203669353 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -546,6 +546,186 @@ static int ksz9031_config_init(struct phy_device *phydev)
        return ret;
 }
 
+#define KSZ9131_SKEW_5BIT_MAX  2400
+#define KSZ9131_SKEW_4BIT_MAX  800
+#define KSZ9131_OFFSET         700
+#define KSZ9131_STEP           100
+
+static int ksz9131_of_load_skew_values(struct phy_device *phydev,
+                                      const struct device_node *of_node,
+                                      u16 reg, size_t field_sz,
+                                      const char *field[], u8 numfields)
+{
+       int val[4] = {-(1 + KSZ9131_OFFSET), -(2 + KSZ9131_OFFSET),
+                     -(3 + KSZ9131_OFFSET), -(4 + KSZ9131_OFFSET)};
+       int skewval, skewmax = 0;
+       int matches = 0;
+       u16 maxval;
+       u16 newval;
+       u16 mask;
+       int i;
+
+       /* psec properties in dts should mean x pico seconds */
+       if (field_sz == 5)
+               skewmax = KSZ9131_SKEW_5BIT_MAX;
+       else
+               skewmax = KSZ9131_SKEW_4BIT_MAX;
+
+       for (i = 0; i < numfields; i++)
+               if (!of_property_read_s32(of_node, field[i], &skewval)) {
+                       if (skewval < -KSZ9131_OFFSET)
+                               skewval = -KSZ9131_OFFSET;
+                       else if (skewval > skewmax)
+                               skewval = skewmax;
+
+                       val[i] = skewval + KSZ9131_OFFSET;
+                       matches++;
+               }
+
+       if (!matches)
+               return 0;
+
+       if (matches < numfields)
+               newval = phy_read_mmd(phydev, 2, reg);
+       else
+               newval = 0;
+
+       maxval = (field_sz == 4) ? 0xf : 0x1f;
+       for (i = 0; i < numfields; i++)
+               if (val[i] != -(i + 1 + KSZ9131_OFFSET)) {
+                       mask = 0xffff;
+                       mask ^= maxval << (field_sz * i);
+                       newval = (newval & mask) |
+                               (((val[i] / KSZ9131_STEP) & maxval)
+                                       << (field_sz * i));
+               }
+
+       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 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_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
+                            KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS,
+                            rxcdll_val);
+       if (ret < 0)
+               return ret;
+
+       return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
+                             KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS,
+                             txcdll_val);
+}
+
+/* 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->dev;
+       const struct device_node *of_node = dev->of_node;
+       static const char *clk_skews[2] = {"rxc-skew-psec", "txc-skew-psec"};
+       static const char *rx_data_skews[4] = {
+               "rxd0-skew-psec", "rxd1-skew-psec",
+               "rxd2-skew-psec", "rxd3-skew-psec"
+       };
+       static const char *tx_data_skews[4] = {
+               "txd0-skew-psec", "txd1-skew-psec",
+               "txd2-skew-psec", "txd3-skew-psec"
+       };
+       static const char *control_skews[2] = {"txen-skew-psec", 
"rxdv-skew-psec"};
+       int ret;
+
+       if (!of_node && dev->parent->of_node)
+               of_node = dev->parent->of_node;
+
+       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);
+       if (ret < 0)
+               return ret;
+
+       ret = ksz9131_of_load_skew_values(phydev, of_node,
+                                         MII_KSZ9031RN_CONTROL_PAD_SKEW, 4,
+                                         control_skews, 2);
+       if (ret < 0)
+               return ret;
+
+       ret = ksz9131_of_load_skew_values(phydev, of_node,
+                                         MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4,
+                                         rx_data_skews, 4);
+       if (ret < 0)
+               return ret;
+
+       ret = ksz9131_of_load_skew_values(phydev, of_node,
+                                         MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
+                                         tx_data_skews, 4);
+       if (ret < 0)
+               return ret;
+
+       ret = ksz9131_led_errata(phydev);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
 #define KSZ8873MLL_GLOBAL_CONTROL_4    0x06
 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX     BIT(6)
 #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED      BIT(4)
@@ -757,6 +937,14 @@ static struct phy_driver ksphy_driver[] = {
        .config_init    = ksz9031_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = ksz9031_read_status,
+}, {
+       .phy_id         = PHY_ID_KSZ9131,
+       .phy_id_mask    = 0x00fffff0,
+       .drv.name       = "Microchip KSZ9131 Gigabit PHY",
+       .features       = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
+       .config_init    = ksz9131_config_init,
+       .config_aneg    = genphy_config_aneg,
+       .read_status    = genphy_read_status,
 }, {
        .phy_id         = PHY_ID_KSZ8873MLL,
        .phy_id_mask    = 0x00fffff0,

-- 
2.39.2


Reply via email to