net: phy: tja11xx: add interface mode and RMII REF_CLK support
authorWei Fang <wei.fang@nxp.com>
Mon, 22 Aug 2022 01:59:49 +0000 (09:59 +0800)
committerJakub Kicinski <kuba@kernel.org>
Wed, 24 Aug 2022 00:43:28 +0000 (17:43 -0700)
Add below features support for both TJA1100 and TJA1101 cards:
- Add MII and RMII mode support.
- Add REF_CLK input/output support for RMII mode.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/nxp-tja11xx.c

index 2a8195c..ec91e67 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/mdio.h>
 #include <linux/mii.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/phy.h>
 #include <linux/hwmon.h>
 #include <linux/bitfield.h>
 #define MII_CFG1                       18
 #define MII_CFG1_MASTER_SLAVE          BIT(15)
 #define MII_CFG1_AUTO_OP               BIT(14)
+#define MII_CFG1_INTERFACE_MODE_MASK   GENMASK(9, 8)
+#define MII_CFG1_MII_MODE                              (0x0 << 8)
+#define MII_CFG1_RMII_MODE_REFCLK_IN   BIT(8)
+#define MII_CFG1_RMII_MODE_REFCLK_OUT  BIT(9)
+#define MII_CFG1_REVMII_MODE                   GENMASK(9, 8)
 #define MII_CFG1_SLEEP_CONFIRM         BIT(6)
 #define MII_CFG1_LED_MODE_MASK         GENMASK(5, 4)
 #define MII_CFG1_LED_MODE_LINKUP       0
 #define MII_COMMCFG                    27
 #define MII_COMMCFG_AUTO_OP            BIT(15)
 
+/* Configure REF_CLK as input in RMII mode */
+#define TJA110X_RMII_MODE_REFCLK_IN       BIT(0)
+
 struct tja11xx_priv {
        char            *hwmon_name;
        struct device   *hwmon_dev;
        struct phy_device *phydev;
        struct work_struct phy_register_work;
+       u32 flags;
 };
 
 struct tja11xx_phy_stats {
@@ -251,8 +261,34 @@ do_test:
        return __genphy_config_aneg(phydev, changed);
 }
 
+static int tja11xx_get_interface_mode(struct phy_device *phydev)
+{
+       struct tja11xx_priv *priv = phydev->priv;
+       int mii_mode;
+
+       switch (phydev->interface) {
+       case PHY_INTERFACE_MODE_MII:
+               mii_mode = MII_CFG1_MII_MODE;
+               break;
+       case PHY_INTERFACE_MODE_REVMII:
+               mii_mode = MII_CFG1_REVMII_MODE;
+               break;
+       case PHY_INTERFACE_MODE_RMII:
+               if (priv->flags & TJA110X_RMII_MODE_REFCLK_IN)
+                       mii_mode = MII_CFG1_RMII_MODE_REFCLK_IN;
+               else
+                       mii_mode = MII_CFG1_RMII_MODE_REFCLK_OUT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return mii_mode;
+}
+
 static int tja11xx_config_init(struct phy_device *phydev)
 {
+       u16 reg_mask, reg_val;
        int ret;
 
        ret = tja11xx_enable_reg_write(phydev);
@@ -265,15 +301,32 @@ static int tja11xx_config_init(struct phy_device *phydev)
 
        switch (phydev->phy_id & PHY_ID_MASK) {
        case PHY_ID_TJA1100:
-               ret = phy_modify(phydev, MII_CFG1,
-                                MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_MASK |
-                                MII_CFG1_LED_ENABLE,
-                                MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_LINKUP |
-                                MII_CFG1_LED_ENABLE);
+               reg_mask = MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_MASK |
+                          MII_CFG1_LED_ENABLE;
+               reg_val = MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_LINKUP |
+                         MII_CFG1_LED_ENABLE;
+
+               reg_mask |= MII_CFG1_INTERFACE_MODE_MASK;
+               ret = tja11xx_get_interface_mode(phydev);
+               if (ret < 0)
+                       return ret;
+
+               reg_val |= (ret & 0xffff);
+               ret = phy_modify(phydev, MII_CFG1, reg_mask, reg_val);
                if (ret)
                        return ret;
                break;
        case PHY_ID_TJA1101:
+               reg_mask = MII_CFG1_INTERFACE_MODE_MASK;
+               ret = tja11xx_get_interface_mode(phydev);
+               if (ret < 0)
+                       return ret;
+
+               reg_val = ret & 0xffff;
+               ret = phy_modify(phydev, MII_CFG1, reg_mask, reg_val);
+               if (ret)
+                       return ret;
+               fallthrough;
        case PHY_ID_TJA1102:
                ret = phy_set_bits(phydev, MII_COMMCFG, MII_COMMCFG_AUTO_OP);
                if (ret)
@@ -458,16 +511,36 @@ static int tja11xx_hwmon_register(struct phy_device *phydev,
        return PTR_ERR_OR_ZERO(priv->hwmon_dev);
 }
 
+static int tja11xx_parse_dt(struct phy_device *phydev)
+{
+       struct device_node *node = phydev->mdio.dev.of_node;
+       struct tja11xx_priv *priv = phydev->priv;
+
+       if (!IS_ENABLED(CONFIG_OF_MDIO))
+               return 0;
+
+       if (of_property_read_bool(node, "nxp,rmii-refclk-in"))
+               priv->flags |= TJA110X_RMII_MODE_REFCLK_IN;
+
+       return 0;
+}
+
 static int tja11xx_probe(struct phy_device *phydev)
 {
        struct device *dev = &phydev->mdio.dev;
        struct tja11xx_priv *priv;
+       int ret;
 
        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
        priv->phydev = phydev;
+       phydev->priv = priv;
+
+       ret = tja11xx_parse_dt(phydev);
+       if (ret)
+               return ret;
 
        return tja11xx_hwmon_register(phydev, priv);
 }