]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
phy: lynx-28g: common probe() and remove()
authorVladimir Oltean <vladimir.oltean@nxp.com>
Wed, 10 Jun 2026 15:19:46 +0000 (18:19 +0300)
committerVinod Koul <vkoul@kernel.org>
Thu, 11 Jun 2026 07:09:47 +0000 (12:39 +0530)
Factor the device-agnostic logic from lynx_28g_probe() and
lynx_28g_remove() into lynx_probe() and lynx_remove() inside
phy-fsl-lynx-core.c. These will be shared with the 10G Lynx driver.

Since the PLL configuration, lane configuration and CDR lock detection
procedure are going to be different, introduce lynx_info function
pointers so that this code remains in the 28G Lynx driver.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/20260610151952.2141019-11-vladimir.oltean@nxp.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/phy/freescale/phy-fsl-lynx-28g.c
drivers/phy/freescale/phy-fsl-lynx-core.c
drivers/phy/freescale/phy-fsl-lynx-core.h

index dbbadaa08cb6356a587c9be7f10b93cb4a6be326..50b991870edba3dcfa6be939f8bebab10007ebdd 100644 (file)
@@ -12,7 +12,6 @@
 #include "phy-fsl-lynx-core.h"
 
 #define LYNX_28G_NUM_LANE                      8
-#define LYNX_28G_NUM_PLL                       LYNX_NUM_PLL
 
 /* SoC IP wrapper for protocol converters */
 #define PCC8                                   0x10a0
@@ -781,6 +780,30 @@ static bool lynx_28g_compat_lane_supports_mode(int lane,
        }
 }
 
+static void lynx_28g_cdr_lock_check(struct lynx_lane *lane)
+{
+       u32 rrstctl;
+       int err;
+
+       rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
+       if (!!(rrstctl & LNaRRSTCTL_CDR_LOCK))
+               return;
+
+       lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_RST_REQ,
+                         LNaRRSTCTL_RST_REQ);
+
+       err = read_poll_timeout(lynx_28g_lane_read, rrstctl,
+                               !!(rrstctl & LNaRRSTCTL_RST_DONE),
+                               LYNX_28G_LANE_RESET_SLEEP_US,
+                               LYNX_28G_LANE_RESET_TIMEOUT_US,
+                               false, lane, LNaRRSTCTL);
+       if (err) {
+               dev_warn_once(&lane->phy->dev,
+                             "Lane %c receiver reset failed: %pe\n",
+                             'A' + lane->id, ERR_PTR(err));
+       }
+}
+
 static void lynx_28g_lane_remap_pll(struct lynx_lane *lane,
                                    enum lynx_lane_mode lane_mode)
 {
@@ -1088,50 +1111,6 @@ static void lynx_28g_pll_read_configuration(struct lynx_pll *pll)
        }
 }
 
-#define work_to_lynx(w) container_of((w), struct lynx_28g_priv, cdr_check.work)
-
-static void lynx_28g_cdr_lock_check(struct work_struct *work)
-{
-       struct lynx_28g_priv *priv = work_to_lynx(work);
-       struct lynx_28g_lane *lane;
-       u32 rrstctl;
-       int err, i;
-
-       for (i = priv->info->first_lane; i < LYNX_28G_NUM_LANE; i++) {
-               lane = &priv->lane[i];
-               if (!lane->phy)
-                       continue;
-
-               mutex_lock(&lane->phy->mutex);
-
-               if (!lane->init || !lane->powered_up) {
-                       mutex_unlock(&lane->phy->mutex);
-                       continue;
-               }
-
-               rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
-               if (!(rrstctl & LNaRRSTCTL_CDR_LOCK)) {
-                       lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_RST_REQ,
-                                         LNaRRSTCTL_RST_REQ);
-
-                       err = read_poll_timeout(lynx_28g_lane_read, rrstctl,
-                                               !!(rrstctl & LNaRRSTCTL_RST_DONE),
-                                               LYNX_28G_LANE_RESET_SLEEP_US,
-                                               LYNX_28G_LANE_RESET_TIMEOUT_US,
-                                               false, lane, LNaRRSTCTL);
-                       if (err) {
-                               dev_warn_once(&lane->phy->dev,
-                                             "Lane %c receiver reset failed: %pe\n",
-                                             'A' + lane->id, ERR_PTR(err));
-                       }
-               }
-
-               mutex_unlock(&lane->phy->mutex);
-       }
-       queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
-                          msecs_to_jiffies(1000));
-}
-
 static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane)
 {
        u32 pccr, pss, protocol;
@@ -1157,49 +1136,13 @@ static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane)
        }
 }
 
-static struct phy *lynx_28g_xlate(struct device *dev,
-                                 const struct of_phandle_args *args)
-{
-       struct lynx_28g_priv *priv = dev_get_drvdata(dev);
-       int idx;
-
-       if (args->args_count == 0)
-               return of_phy_simple_xlate(dev, args);
-       else if (args->args_count != 1)
-               return ERR_PTR(-ENODEV);
-
-       idx = args->args[0];
-
-       if (WARN_ON(idx >= LYNX_28G_NUM_LANE ||
-                   idx < priv->info->first_lane))
-               return ERR_PTR(-EINVAL);
-
-       return priv->lane[idx].phy ?: ERR_PTR(-ENODEV);
-}
-
-static int lynx_28g_probe_lane(struct lynx_28g_priv *priv, int id,
-                              struct device_node *dn)
-{
-       struct lynx_28g_lane *lane = &priv->lane[id];
-       struct phy *phy;
-
-       phy = devm_phy_create(priv->dev, dn, &lynx_28g_ops);
-       if (IS_ERR(phy))
-               return PTR_ERR(phy);
-
-       lane->priv = priv;
-       lane->phy = phy;
-       lane->id = id;
-       phy_set_drvdata(phy, lane);
-       lynx_28g_lane_read_configuration(lane);
-
-       return 0;
-}
-
 static const struct lynx_info lynx_info_compat = {
        .get_pccr = lynx_28g_get_pccr,
        .get_pcvt_offset = lynx_28g_get_pcvt_offset,
        .lane_supports_mode = lynx_28g_compat_lane_supports_mode,
+       .pll_read_configuration = lynx_28g_pll_read_configuration,
+       .lane_read_configuration = lynx_28g_lane_read_configuration,
+       .cdr_lock_check = lynx_28g_cdr_lock_check,
        .num_lanes = LYNX_28G_NUM_LANE,
 };
 
@@ -1207,6 +1150,9 @@ static const struct lynx_info lynx_info_lx2160a_serdes1 = {
        .get_pccr = lynx_28g_get_pccr,
        .get_pcvt_offset = lynx_28g_get_pcvt_offset,
        .lane_supports_mode = lx2160a_serdes1_lane_supports_mode,
+       .pll_read_configuration = lynx_28g_pll_read_configuration,
+       .lane_read_configuration = lynx_28g_lane_read_configuration,
+       .cdr_lock_check = lynx_28g_cdr_lock_check,
        .num_lanes = LYNX_28G_NUM_LANE,
 };
 
@@ -1214,6 +1160,9 @@ static const struct lynx_info lynx_info_lx2160a_serdes2 = {
        .get_pccr = lynx_28g_get_pccr,
        .get_pcvt_offset = lynx_28g_get_pcvt_offset,
        .lane_supports_mode = lx2160a_serdes2_lane_supports_mode,
+       .pll_read_configuration = lynx_28g_pll_read_configuration,
+       .lane_read_configuration = lynx_28g_lane_read_configuration,
+       .cdr_lock_check = lynx_28g_cdr_lock_check,
        .num_lanes = LYNX_28G_NUM_LANE,
 };
 
@@ -1221,6 +1170,9 @@ static const struct lynx_info lynx_info_lx2160a_serdes3 = {
        .get_pccr = lynx_28g_get_pccr,
        .get_pcvt_offset = lynx_28g_get_pcvt_offset,
        .lane_supports_mode = lx2160a_serdes3_lane_supports_mode,
+       .pll_read_configuration = lynx_28g_pll_read_configuration,
+       .lane_read_configuration = lynx_28g_lane_read_configuration,
+       .cdr_lock_check = lynx_28g_cdr_lock_check,
        .num_lanes = LYNX_28G_NUM_LANE,
 };
 
@@ -1228,6 +1180,9 @@ static const struct lynx_info lynx_info_lx2162a_serdes1 = {
        .get_pccr = lynx_28g_get_pccr,
        .get_pcvt_offset = lynx_28g_get_pcvt_offset,
        .lane_supports_mode = lx2162a_serdes1_lane_supports_mode,
+       .pll_read_configuration = lynx_28g_pll_read_configuration,
+       .lane_read_configuration = lynx_28g_lane_read_configuration,
+       .cdr_lock_check = lynx_28g_cdr_lock_check,
        .first_lane = 4,
        .num_lanes = LYNX_28G_NUM_LANE,
 };
@@ -1236,112 +1191,26 @@ static const struct lynx_info lynx_info_lx2162a_serdes2 = {
        .get_pccr = lynx_28g_get_pccr,
        .get_pcvt_offset = lynx_28g_get_pcvt_offset,
        .lane_supports_mode = lx2162a_serdes2_lane_supports_mode,
+       .pll_read_configuration = lynx_28g_pll_read_configuration,
+       .lane_read_configuration = lynx_28g_lane_read_configuration,
+       .cdr_lock_check = lynx_28g_cdr_lock_check,
        .num_lanes = LYNX_28G_NUM_LANE,
 };
 
 static int lynx_28g_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct phy_provider *provider;
-       struct lynx_28g_priv *priv;
-       struct device_node *dn;
-       int err;
-
-       dn = dev_of_node(dev);
-       if (!dn) {
-               dev_err(dev, "Device requires an OF node\n");
-               return -EINVAL;
-       }
-
-       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       priv->dev = dev;
-       priv->info = of_device_get_match_data(dev);
-       if (!priv->info)
-               return -ENODEV;
-
-       dev_set_drvdata(dev, priv);
-       spin_lock_init(&priv->pcc_lock);
-       INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check);
+       const struct lynx_info *info;
 
        /*
         * If we get here it means we probed on a device tree where
         * "fsl,lynx-28g" wasn't the fallback, but the sole compatible string.
         */
-       if (priv->info == &lynx_info_compat)
+       info = of_device_get_match_data(dev);
+       if (info == &lynx_info_compat)
                dev_warn(dev, "Please update device tree to use per-device compatible strings\n");
 
-       priv->lane = devm_kcalloc(dev, priv->info->num_lanes,
-                                 sizeof(*priv->lane), GFP_KERNEL);
-       if (!priv->lane)
-               return -ENOMEM;
-
-       priv->base = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(priv->base))
-               return PTR_ERR(priv->base);
-
-       for (int i = 0; i < LYNX_28G_NUM_PLL; i++) {
-               struct lynx_28g_pll *pll = &priv->pll[i];
-
-               pll->priv = priv;
-               pll->id = i;
-               lynx_28g_pll_read_configuration(pll);
-       }
-
-       if (of_get_child_count(dn)) {
-               struct device_node *child;
-
-               for_each_available_child_of_node(dn, child) {
-                       u32 reg;
-
-                       /* PHY subnode name must be 'phy'. */
-                       if (!(of_node_name_eq(child, "phy")))
-                               continue;
-
-                       if (of_property_read_u32(child, "reg", &reg)) {
-                               dev_err(dev, "No \"reg\" property for %pOF\n", child);
-                               of_node_put(child);
-                               return -EINVAL;
-                       }
-
-                       if (reg < priv->info->first_lane || reg >= LYNX_28G_NUM_LANE) {
-                               dev_err(dev, "\"reg\" property out of range for %pOF\n", child);
-                               of_node_put(child);
-                               return -EINVAL;
-                       }
-
-                       err = lynx_28g_probe_lane(priv, reg, child);
-                       if (err) {
-                               of_node_put(child);
-                               return err;
-                       }
-               }
-       } else {
-               for (int i = priv->info->first_lane; i < LYNX_28G_NUM_LANE; i++) {
-                       err = lynx_28g_probe_lane(priv, i, NULL);
-                       if (err)
-                               return err;
-               }
-       }
-
-       provider = devm_of_phy_provider_register(dev, lynx_28g_xlate);
-       if (IS_ERR(provider))
-               return PTR_ERR(provider);
-
-       queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
-                          msecs_to_jiffies(1000));
-
-       return 0;
-}
-
-static void lynx_28g_remove(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct lynx_28g_priv *priv = dev_get_drvdata(dev);
-
-       cancel_delayed_work_sync(&priv->cdr_check);
+       return lynx_probe(pdev, info, &lynx_28g_ops);
 }
 
 static const struct of_device_id lynx_28g_of_match_table[] = {
@@ -1357,7 +1226,7 @@ MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table);
 
 static struct platform_driver lynx_28g_driver = {
        .probe = lynx_28g_probe,
-       .remove = lynx_28g_remove,
+       .remove = lynx_remove,
        .driver = {
                .name = "lynx-28g",
                .of_match_table = lynx_28g_of_match_table,
index 802e32dc6dca19a3b4aa7e0a6e241aa41a85bd37..3fb89bb4b0d66a31b018521f00879b9952bd01ce 100644 (file)
@@ -2,6 +2,7 @@
 /* Copyright 2025-2026 NXP */
 
 #include <linux/module.h>
+#include <linux/platform_device.h>
 
 #include "phy-fsl-lynx-core.h"
 
@@ -202,5 +203,174 @@ int lynx_pcvt_rmw(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr,
 }
 EXPORT_SYMBOL_NS_GPL(lynx_pcvt_rmw, "PHY_FSL_LYNX");
 
+#define work_to_lynx(w) container_of((w), struct lynx_priv, cdr_check.work)
+
+static void lynx_cdr_lock_check(struct work_struct *work)
+{
+       struct lynx_priv *priv = work_to_lynx(work);
+       struct lynx_lane *lane;
+
+       for (int i = priv->info->first_lane; i < priv->info->num_lanes; i++) {
+               lane = &priv->lane[i];
+               if (!lane->phy)
+                       continue;
+
+               mutex_lock(&lane->phy->mutex);
+
+               if (!lane->init || !lane->powered_up) {
+                       mutex_unlock(&lane->phy->mutex);
+                       continue;
+               }
+
+               priv->info->cdr_lock_check(lane);
+
+               mutex_unlock(&lane->phy->mutex);
+       }
+
+       queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
+                          msecs_to_jiffies(1000));
+}
+
+static struct phy *lynx_xlate(struct device *dev,
+                             const struct of_phandle_args *args)
+{
+       struct lynx_priv *priv = dev_get_drvdata(dev);
+       int idx;
+
+       if (args->args_count == 0)
+               return of_phy_simple_xlate(dev, args);
+       else if (args->args_count != 1)
+               return ERR_PTR(-ENODEV);
+
+       idx = args->args[0];
+
+       if (WARN_ON(idx >= priv->info->num_lanes ||
+                   idx < priv->info->first_lane))
+               return ERR_PTR(-EINVAL);
+
+       return priv->lane[idx].phy ?: ERR_PTR(-ENODEV);
+}
+
+static int lynx_probe_lane(struct lynx_priv *priv, int id,
+                          struct device_node *dn,
+                          const struct phy_ops *phy_ops)
+{
+       struct lynx_lane *lane = &priv->lane[id];
+       struct phy *phy;
+
+       phy = devm_phy_create(priv->dev, dn, phy_ops);
+       if (IS_ERR(phy))
+               return PTR_ERR(phy);
+
+       lane->priv = priv;
+       lane->phy = phy;
+       lane->id = id;
+       phy_set_drvdata(phy, lane);
+       priv->info->lane_read_configuration(lane);
+
+       return 0;
+}
+
+int lynx_probe(struct platform_device *pdev, const struct lynx_info *info,
+              const struct phy_ops *phy_ops)
+{
+       struct device *dev = &pdev->dev;
+       struct phy_provider *provider;
+       struct device_node *dn;
+       struct lynx_priv *priv;
+       int err;
+
+       dn = dev_of_node(dev);
+       if (!dn) {
+               dev_err(dev, "Device requires an OF node\n");
+               return -EINVAL;
+       }
+
+       if (!info)
+               return -ENODEV;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->dev = dev;
+       priv->info = info;
+       dev_set_drvdata(dev, priv);
+       spin_lock_init(&priv->pcc_lock);
+       INIT_DELAYED_WORK(&priv->cdr_check, lynx_cdr_lock_check);
+
+       priv->lane = devm_kcalloc(dev, priv->info->num_lanes,
+                                 sizeof(*priv->lane), GFP_KERNEL);
+       if (!priv->lane)
+               return -ENOMEM;
+
+       priv->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       for (int i = 0; i < LYNX_NUM_PLL; i++) {
+               struct lynx_pll *pll = &priv->pll[i];
+
+               pll->priv = priv;
+               pll->id = i;
+               priv->info->pll_read_configuration(pll);
+       }
+
+       if (of_get_child_count(dn)) {
+               struct device_node *child;
+
+               for_each_available_child_of_node(dn, child) {
+                       u32 reg;
+
+                       /* PHY subnode name must be 'phy'. */
+                       if (!(of_node_name_eq(child, "phy")))
+                               continue;
+
+                       if (of_property_read_u32(child, "reg", &reg)) {
+                               dev_err(dev, "No \"reg\" property for %pOF\n", child);
+                               of_node_put(child);
+                               return -EINVAL;
+                       }
+
+                       if (reg < priv->info->first_lane || reg >= priv->info->num_lanes) {
+                               dev_err(dev, "\"reg\" property out of range for %pOF\n", child);
+                               of_node_put(child);
+                               return -EINVAL;
+                       }
+
+                       err = lynx_probe_lane(priv, reg, child, phy_ops);
+                       if (err) {
+                               of_node_put(child);
+                               return err;
+                       }
+               }
+       } else {
+               for (int i = priv->info->first_lane; i < priv->info->num_lanes; i++) {
+                       err = lynx_probe_lane(priv, i, NULL, phy_ops);
+                       if (err)
+                               return err;
+               }
+       }
+
+       provider = devm_of_phy_provider_register(dev, lynx_xlate);
+       if (IS_ERR(provider))
+               return PTR_ERR(provider);
+
+       queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
+                          msecs_to_jiffies(1000));
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(lynx_probe, "PHY_FSL_LYNX");
+
+void lynx_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct lynx_priv *priv = dev_get_drvdata(dev);
+
+       cancel_delayed_work_sync(&priv->cdr_check);
+}
+EXPORT_SYMBOL_NS_GPL(lynx_remove, "PHY_FSL_LYNX");
+
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Freescale Lynx SerDes core functionality");
index 5cd86c9543cb8eb6da6df76e3785fd326a737898..e8b280cc9b38c4ca4a30dfcdce58e1c84860cad0 100644 (file)
 
 #define LYNX_NUM_PLL                           2
 
+struct lynx_priv;
+struct lynx_lane;
+
 struct lynx_pccr {
        int offset;
        int width;
        int shift;
 };
 
-struct lynx_priv;
-
 struct lynx_pll {
        struct lynx_priv *priv;
        int id;
@@ -42,6 +43,9 @@ struct lynx_info {
                        struct lynx_pccr *pccr);
        int (*get_pcvt_offset)(int lane, enum lynx_lane_mode mode);
        bool (*lane_supports_mode)(int lane, enum lynx_lane_mode mode);
+       void (*pll_read_configuration)(struct lynx_pll *pll);
+       void (*lane_read_configuration)(struct lynx_lane *lane);
+       void (*cdr_lock_check)(struct lynx_lane *lane);
        int first_lane;
        int num_lanes;
 };
@@ -85,6 +89,10 @@ static inline void lynx_rmw(struct lynx_priv *priv, unsigned long off, u32 val,
 #define lynx_pll_read(pll, reg)                        \
        ioread32((pll)->priv->base + reg((pll)->id))
 
+int lynx_probe(struct platform_device *pdev, const struct lynx_info *info,
+              const struct phy_ops *phy_ops);
+void lynx_remove(struct platform_device *pdev);
+
 const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode);
 enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf);
 bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode);