From: Vladimir Oltean Date: Wed, 10 Jun 2026 15:19:46 +0000 (+0300) Subject: phy: lynx-28g: common probe() and remove() X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=719a2da392349db052532db89637622a71d14d49;p=thirdparty%2Fkernel%2Flinux.git phy: lynx-28g: common probe() and remove() 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 Link: https://patch.msgid.link/20260610151952.2141019-11-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index dbbadaa08cb63..50b991870edba 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -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", ®)) { - 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, diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c index 802e32dc6dca1..3fb89bb4b0d66 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.c +++ b/drivers/phy/freescale/phy-fsl-lynx-core.c @@ -2,6 +2,7 @@ /* Copyright 2025-2026 NXP */ #include +#include #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", ®)) { + 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"); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h index 5cd86c9543cb8..e8b280cc9b38c 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.h +++ b/drivers/phy/freescale/phy-fsl-lynx-core.h @@ -10,14 +10,15 @@ #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);