[PATCH v6 3/9] phy: phy-can-transceiver: Add dual channel support for TJA1048
From: Peng Fan <peng.fan@nxp.com>
Date: 2025-09-09 05:40:53
Also in:
imx, linux-can, linux-devicetree, linux-phy, lkml
Subsystem:
can network drivers, generic phy framework, the rest · Maintainers:
Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Linus Torvalds
- Introduce new flag CAN_TRANSCEIVER_DUAL_CH to indicate the phy has two channels. - Alloc a phy for each channel - Support TJA1048 which is a dual high-speed CAN transceiver with sleep mode supported. - Add can_transceiver_phy_xlate for parsing phy Reviewed-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Peng Fan <peng.fan@nxp.com> --- drivers/phy/phy-can-transceiver.c | 91 ++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 26 deletions(-)
diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c
index 6415c6af0e8414a6cc8d15958a17ee749a3f28e9..f06b1df76ada023f432dce892c3346f45397ab54 100644
--- a/drivers/phy/phy-can-transceiver.c
+++ b/drivers/phy/phy-can-transceiver.c@@ -17,6 +17,7 @@ struct can_transceiver_data { u32 flags; #define CAN_TRANSCEIVER_STB_PRESENT BIT(0) #define CAN_TRANSCEIVER_EN_PRESENT BIT(1) +#define CAN_TRANSCEIVER_DUAL_CH BIT(2) }; struct can_transceiver_phy {
@@ -29,6 +30,7 @@ struct can_transceiver_phy { struct can_transceiver_priv { struct can_transceiver_phy *can_transceiver_phy; struct mux_state *mux_state; + int num_ch; }; /* Power on function */
@@ -81,6 +83,10 @@ static const struct can_transceiver_data tcan1043_drvdata = { .flags = CAN_TRANSCEIVER_STB_PRESENT | CAN_TRANSCEIVER_EN_PRESENT, }; +static const struct can_transceiver_data tja1048_drvdata = { + .flags = CAN_TRANSCEIVER_STB_PRESENT | CAN_TRANSCEIVER_DUAL_CH, +}; + static const struct of_device_id can_transceiver_phy_ids[] = { { .compatible = "ti,tcan1042",
@@ -90,6 +96,10 @@ static const struct of_device_id can_transceiver_phy_ids[] = { .compatible = "ti,tcan1043", .data = &tcan1043_drvdata }, + { + .compatible = "nxp,tja1048", + .data = &tja1048_drvdata + }, { .compatible = "nxp,tjr1443", .data = &tcan1043_drvdata
@@ -108,6 +118,25 @@ devm_mux_state_get_optional(struct device *dev, const char *mux_name) return devm_mux_state_get(dev, mux_name); } +static struct phy *can_transceiver_phy_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct can_transceiver_priv *priv = dev_get_drvdata(dev); + u32 idx; + + if (priv->num_ch == 1) + return priv->can_transceiver_phy[0].generic_phy; + + if (args->args_count != 1) + return ERR_PTR(-EINVAL); + + idx = args->args[0]; + if (idx >= priv->num_ch) + return ERR_PTR(-EINVAL); + + return priv->can_transceiver_phy[idx].generic_phy; +} + static int can_transceiver_phy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider;
@@ -120,7 +149,8 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) struct gpio_desc *enable_gpio; struct mux_state *mux_state; u32 max_bitrate = 0; - int err; + int num_ch = 1; + int err, i; match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node); drvdata = match->data;
@@ -131,7 +161,11 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - priv->can_transceiver_phy = devm_kzalloc(dev, sizeof(struct can_transceiver_phy), + if (drvdata->flags & CAN_TRANSCEIVER_DUAL_CH) + num_ch = 2; + + priv->num_ch = num_ch; + priv->can_transceiver_phy = devm_kcalloc(dev, num_ch, sizeof(struct can_transceiver_phy), GFP_KERNEL); if (!priv->can_transceiver_phy) return -ENOMEM;
@@ -142,38 +176,43 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) priv->mux_state = mux_state; - phy = devm_phy_create(dev, dev->of_node, - &can_transceiver_phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create can transceiver phy\n"); - return PTR_ERR(phy); - } - err = device_property_read_u32(dev, "max-bitrate", &max_bitrate); if ((err != -EINVAL) && !max_bitrate) dev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit\n"); - phy->attrs.max_link_rate = max_bitrate; - priv->can_transceiver_phy->generic_phy = phy; - priv->can_transceiver_phy->priv = priv; + for (i = 0; i < num_ch; i++) { + phy = devm_phy_create(dev, dev->of_node, &can_transceiver_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create can transceiver phy\n"); + return PTR_ERR(phy); + } - if (drvdata->flags & CAN_TRANSCEIVER_STB_PRESENT) { - standby_gpio = devm_gpiod_get_optional(dev, "standby", GPIOD_OUT_HIGH); - if (IS_ERR(standby_gpio)) - return PTR_ERR(standby_gpio); - priv->can_transceiver_phy->standby_gpio = standby_gpio; - } + phy->attrs.max_link_rate = max_bitrate; - if (drvdata->flags & CAN_TRANSCEIVER_EN_PRESENT) { - enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(enable_gpio)) - return PTR_ERR(enable_gpio); - priv->can_transceiver_phy->enable_gpio = enable_gpio; - } + priv->can_transceiver_phy[i].generic_phy = phy; + priv->can_transceiver_phy[i].priv = priv; - phy_set_drvdata(priv->can_transceiver_phy->generic_phy, priv->can_transceiver_phy); + if (drvdata->flags & CAN_TRANSCEIVER_STB_PRESENT) { + standby_gpio = devm_gpiod_get_index_optional(dev, "standby", i, + GPIOD_OUT_HIGH); + if (IS_ERR(standby_gpio)) + return PTR_ERR(standby_gpio); + priv->can_transceiver_phy[i].standby_gpio = standby_gpio; + } + + if (drvdata->flags & CAN_TRANSCEIVER_EN_PRESENT) { + enable_gpio = devm_gpiod_get_index_optional(dev, "enable", i, + GPIOD_OUT_LOW); + if (IS_ERR(enable_gpio)) + return PTR_ERR(enable_gpio); + priv->can_transceiver_phy[i].enable_gpio = enable_gpio; + } + + phy_set_drvdata(priv->can_transceiver_phy[i].generic_phy, + &priv->can_transceiver_phy[i]); + } - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + phy_provider = devm_of_phy_provider_register(dev, can_transceiver_phy_xlate); return PTR_ERR_OR_ZERO(phy_provider); }
--
2.37.1