Various signal skew values may be set in the device tree for the ksz9131
Ethernet PHY. For example, the RZ/G2L board requires non-default values
for rxc-skew-psec & txc-skew-psec.

This is based on the ksz9131 phy driver in Linux v6.11.

Signed-off-by: Paul Barker <paul.barker...@bp.renesas.com>
---
 drivers/net/phy/micrel_ksz90x1.c | 115 +++++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c
index 4f99b115a3c7..b64046e0bc72 100644
--- a/drivers/net/phy/micrel_ksz90x1.c
+++ b/drivers/net/phy/micrel_ksz90x1.c
@@ -389,6 +389,117 @@ U_BOOT_PHY_DRIVER(ksz9031) = {
 #define KSZ9131RN_DLL_ENABLE_DELAY     0
 #define KSZ9131RN_DLL_DISABLE_DELAY    BIT(12)
 
+#define KSZ9131RN_CONTROL_PAD_SKEW     4
+#define KSZ9131RN_RX_DATA_PAD_SKEW     5
+#define KSZ9131RN_TX_DATA_PAD_SKEW     6
+#define KSZ9131RN_CLK_PAD_SKEW         8
+
+#define KSZ9131RN_SKEW_5BIT_MAX                2400
+#define KSZ9131RN_SKEW_4BIT_MAX                800
+#define KSZ9131RN_OFFSET               700
+#define KSZ9131RN_STEP                 100
+
+static int ksz9131_of_load_skew_values(struct phy_device *phydev,
+                                      ofnode of_node,
+                                      u16 reg, size_t field_sz,
+                                      const char *field[], u8 numfields)
+{
+       int val[4] = {-(1 + KSZ9131RN_OFFSET), -(2 + KSZ9131RN_OFFSET),
+                     -(3 + KSZ9131RN_OFFSET), -(4 + KSZ9131RN_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 = KSZ9131RN_SKEW_5BIT_MAX;
+       else
+               skewmax = KSZ9131RN_SKEW_4BIT_MAX;
+
+       for (i = 0; i < numfields; i++)
+               if (!ofnode_read_s32(of_node, field[i], &skewval)) {
+                       if (skewval < -KSZ9131RN_OFFSET)
+                               skewval = -KSZ9131RN_OFFSET;
+                       else if (skewval > skewmax)
+                               skewval = skewmax;
+
+                       val[i] = skewval + KSZ9131RN_OFFSET;
+                       matches++;
+               }
+
+       if (!matches)
+               return 0;
+
+       if (matches < numfields)
+               newval = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 
reg);
+       else
+               newval = 0;
+
+       maxval = (field_sz == 4) ? 0xf : 0x1f;
+       for (i = 0; i < numfields; i++)
+               if (val[i] != -(i + 1 + KSZ9131RN_OFFSET)) {
+                       mask = 0xffff;
+                       mask ^= maxval << (field_sz * i);
+                       newval = (newval & mask) |
+                               (((val[i] / KSZ9131RN_STEP) & maxval)
+                                       << (field_sz * i));
+               }
+
+       return phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, reg, 
newval);
+}
+
+static int ksz9131_of_load_all_skew_values(struct phy_device *phydev)
+{
+       const char *control_skews[2] = { "txen-skew-psec", "rxdv-skew-psec" };
+       const char *clk_skews[2] = { "rxc-skew-psec", "txc-skew-psec" };
+       const char *rx_data_skews[4] = {
+               "rxd0-skew-psec", "rxd1-skew-psec",
+               "rxd2-skew-psec", "rxd3-skew-psec"
+       };
+       const char *tx_data_skews[4] = {
+               "txd0-skew-psec", "txd1-skew-psec",
+               "txd2-skew-psec", "txd3-skew-psec"
+       };
+       struct ofnode_phandle_args phandle_args;
+       int ret;
+
+       /*
+        * Silently ignore failure here as the device tree is not required to
+        * contain a phy node.
+        */
+       if (dev_read_phandle_with_args(phydev->dev, "phy-handle", NULL, 0, 0,
+                                      &phandle_args))
+               return 0;
+
+       if (!ofnode_valid(phandle_args.node))
+               return 0;
+
+       ret = ksz9131_of_load_skew_values(phydev, phandle_args.node,
+                                         KSZ9131RN_CLK_PAD_SKEW, 5,
+                                         clk_skews, 2);
+       if (ret < 0)
+               return ret;
+
+       ret = ksz9131_of_load_skew_values(phydev, phandle_args.node,
+                                         KSZ9131RN_CONTROL_PAD_SKEW, 4,
+                                         control_skews, 2);
+       if (ret < 0)
+               return ret;
+
+       ret = ksz9131_of_load_skew_values(phydev, phandle_args.node,
+                                         KSZ9131RN_RX_DATA_PAD_SKEW, 4,
+                                         rx_data_skews, 4);
+       if (ret < 0)
+               return ret;
+
+       return ksz9131_of_load_skew_values(phydev, phandle_args.node,
+                                          KSZ9131RN_TX_DATA_PAD_SKEW, 4,
+                                          tx_data_skews, 4);
+}
+
 static int ksz9131_config_rgmii_delay(struct phy_device *phydev)
 {
        struct phy_driver *drv = phydev->drv;
@@ -466,6 +577,10 @@ static int ksz9131_config(struct phy_device *phydev)
                        return ret;
        }
 
+       ret = ksz9131_of_load_all_skew_values(phydev);
+       if (ret < 0)
+               return ret;
+
        ret = ksz9131_led_errata(phydev);
        if (ret < 0)
                return ret;
-- 
2.43.0

Reply via email to