From 05ace63d0bcfe131e741923394c7ce03322a141e Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 1 Oct 2025 21:22:32 +0800 Subject: [PATCH 1/5] dt-bindings: phy: ti,tcan104x-can: Document NXP TJA105X/1048 The TJA1048 is a dual high-speed CAN transceiver with sleep mode supported and no EN pin. The TJA1051 is a high-speed CAN transceiver with slient mode supported, but only TJA1051T/E has EN pin. To make it simple, make enable-gpios as optional for TJA1051. The TJA1057 is a high-speed CAN transceiver with slient mode supported and no EN pin. Reviewed-by: Frank Li Reviewed-by: Rob Herring (Arm) Signed-off-by: Peng Fan Acked-by: Marc Kleine-Budde Link: https://patch.msgid.link/20251001-can-v7-1-fad29efc3884@nxp.com Signed-off-by: Vinod Koul --- .../bindings/phy/ti,tcan104x-can.yaml | 69 ++++++++++++++++++- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml b/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml index 4a8c3829d85d..14d833213175 100644 --- a/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml +++ b/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml @@ -22,15 +22,25 @@ properties: - enum: - ti,tcan1042 - ti,tcan1043 + - nxp,tja1048 + - nxp,tja1051 + - nxp,tja1057 - nxp,tjr1443 '#phy-cells': - const: 0 + enum: [0, 1] + + silent-gpios: + description: + gpio node to toggle silent signal on transceiver + maxItems: 1 standby-gpios: description: - gpio node to toggle standby signal on transceiver - maxItems: 1 + gpio node to toggle standby signal on transceiver. For two Items, item 1 + is for stbn1, item 2 is for stbn2. + minItems: 1 + maxItems: 2 enable-gpios: description: @@ -53,6 +63,59 @@ required: - compatible - '#phy-cells' +allOf: + - if: + properties: + compatible: + enum: + - nxp,tjr1443 + - ti,tcan1042 + - ti,tcan1043 + then: + properties: + '#phy-cells': + const: 0 + silent-gpios: false + standby-gpios: + maxItems: 1 + + - if: + properties: + compatible: + contains: + const: nxp,tja1048 + then: + properties: + '#phy-cells': + const: 1 + enable-gpios: false + silent-gpios: false + standby-gpios: + minItems: 2 + + - if: + properties: + compatible: + contains: + const: nxp,tja1051 + then: + properties: + '#phy-cells': + const: 0 + standby-gpios: false + + - if: + properties: + compatible: + contains: + const: nxp,tja1057 + then: + properties: + '#phy-cells': + const: 0 + enable-gpios: false + standby-gpios: false + additionalProperties: false examples: From c77464bd9b4155891a135e51f8e916e1ab94fc14 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 1 Oct 2025 21:22:33 +0800 Subject: [PATCH 2/5] phy: phy-can-transceiver: Introduce can_transceiver_priv To prepare for dual-channel phy support, introduce can_transceiver_priv as a higher level encapsulation for phy. No functional changes. Reviewed-by: Frank Li Signed-off-by: Peng Fan Acked-by: Marc Kleine-Budde Link: https://patch.msgid.link/20251001-can-v7-2-fad29efc3884@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 34 ++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index f59caff4b3d4..b06ba42854c1 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -23,17 +23,24 @@ struct can_transceiver_phy { struct phy *generic_phy; struct gpio_desc *standby_gpio; struct gpio_desc *enable_gpio; + struct can_transceiver_priv *priv; +}; + +struct can_transceiver_priv { struct mux_state *mux_state; + int num_ch; + struct can_transceiver_phy can_transceiver_phy[] __counted_by(num_ch); }; /* Power on function */ static int can_transceiver_phy_power_on(struct phy *phy) { struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy); + struct can_transceiver_priv *priv = can_transceiver_phy->priv; int ret; - if (can_transceiver_phy->mux_state) { - ret = mux_state_select(can_transceiver_phy->mux_state); + if (priv->mux_state) { + ret = mux_state_select(priv->mux_state); if (ret) { dev_err(&phy->dev, "Failed to select CAN mux: %d\n", ret); return ret; @@ -51,13 +58,14 @@ static int can_transceiver_phy_power_on(struct phy *phy) static int can_transceiver_phy_power_off(struct phy *phy) { struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy); + struct can_transceiver_priv *priv = can_transceiver_phy->priv; if (can_transceiver_phy->standby_gpio) gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 1); if (can_transceiver_phy->enable_gpio) gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 0); - if (can_transceiver_phy->mux_state) - mux_state_deselect(can_transceiver_phy->mux_state); + if (priv->mux_state) + mux_state_deselect(priv->mux_state); return 0; } @@ -108,6 +116,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) struct phy_provider *phy_provider; struct device *dev = &pdev->dev; struct can_transceiver_phy *can_transceiver_phy; + struct can_transceiver_priv *priv; const struct can_transceiver_data *drvdata; const struct of_device_id *match; struct phy *phy; @@ -115,20 +124,25 @@ 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; - - can_transceiver_phy = devm_kzalloc(dev, sizeof(struct can_transceiver_phy), GFP_KERNEL); - if (!can_transceiver_phy) - return -ENOMEM; + int err, num_ch = 1; match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node); drvdata = match->data; + priv = devm_kzalloc(dev, struct_size(priv, can_transceiver_phy, num_ch), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->num_ch = num_ch; + platform_set_drvdata(pdev, priv); + can_transceiver_phy = &priv->can_transceiver_phy[0]; + can_transceiver_phy->priv = priv; + mux_state = devm_mux_state_get_optional(dev, NULL); if (IS_ERR(mux_state)) return PTR_ERR(mux_state); - can_transceiver_phy->mux_state = mux_state; + priv->mux_state = mux_state; phy = devm_phy_create(dev, dev->of_node, &can_transceiver_phy_ops); From 6e9fe9409e10ed25b43928062832037752630979 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 1 Oct 2025 21:22:34 +0800 Subject: [PATCH 3/5] phy: phy-can-transceiver: Add dual channel support for TJA1048 - 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 Signed-off-by: Peng Fan Acked-by: Marc Kleine-Budde Link: https://patch.msgid.link/20251001-can-v7-3-fad29efc3884@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 90 +++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index b06ba42854c1..09d168489556 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 { @@ -84,6 +85,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", @@ -93,6 +98,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 @@ -111,6 +120,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; @@ -124,10 +152,12 @@ 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, num_ch = 1; + int err, i, num_ch = 1; match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node); drvdata = match->data; + if (drvdata->flags & CAN_TRANSCEIVER_DUAL_CH) + num_ch = 2; priv = devm_kzalloc(dev, struct_size(priv, can_transceiver_phy, num_ch), GFP_KERNEL); if (!priv) @@ -135,8 +165,6 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) priv->num_ch = num_ch; platform_set_drvdata(pdev, priv); - can_transceiver_phy = &priv->can_transceiver_phy[0]; - can_transceiver_phy->priv = priv; mux_state = devm_mux_state_get_optional(dev, NULL); if (IS_ERR(mux_state)) @@ -144,37 +172,45 @@ 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; - can_transceiver_phy->generic_phy = phy; + for (i = 0; i < num_ch; i++) { + can_transceiver_phy = &priv->can_transceiver_phy[i]; + can_transceiver_phy->priv = priv; - 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); - can_transceiver_phy->standby_gpio = standby_gpio; + 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); + } + + phy->attrs.max_link_rate = max_bitrate; + + can_transceiver_phy->generic_phy = phy; + can_transceiver_phy->priv = priv; + + 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); + can_transceiver_phy->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); + can_transceiver_phy->enable_gpio = enable_gpio; + } + + phy_set_drvdata(can_transceiver_phy->generic_phy, can_transceiver_phy); } - 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); - can_transceiver_phy->enable_gpio = enable_gpio; - } - - phy_set_drvdata(can_transceiver_phy->generic_phy, can_transceiver_phy); - - 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); } From d02a7eb12924b7473a62d5a6c9e670fe5bf6e4b7 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 1 Oct 2025 21:22:35 +0800 Subject: [PATCH 4/5] phy: phy-can-transceiver: Drop the gpio desc check gpiod_set_value_cansleep has an internal check on gpio_desc using 'VALIDATE_DESC(desc)', the check before invoking gpiod_set_value_cansleep could be removed. Reviewed-by: Frank Li Signed-off-by: Peng Fan Acked-by: Marc Kleine-Budde Link: https://patch.msgid.link/20251001-can-v7-4-fad29efc3884@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 09d168489556..89189be9b795 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -47,10 +47,8 @@ static int can_transceiver_phy_power_on(struct phy *phy) return ret; } } - if (can_transceiver_phy->standby_gpio) - gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 0); - if (can_transceiver_phy->enable_gpio) - gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 1); + gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 0); + gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 1); return 0; } @@ -61,10 +59,8 @@ static int can_transceiver_phy_power_off(struct phy *phy) struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy); struct can_transceiver_priv *priv = can_transceiver_phy->priv; - if (can_transceiver_phy->standby_gpio) - gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 1); - if (can_transceiver_phy->enable_gpio) - gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 0); + gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 1); + gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 0); if (priv->mux_state) mux_state_deselect(priv->mux_state); From b817f505926b8ffbdea8aa87b66a622acb9b96e9 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 1 Oct 2025 21:22:36 +0800 Subject: [PATCH 5/5] phy: phy-can-transceiver: Add support for TJA105{1,7} Support TJA105{1,7} which are a single channel high-speed CAN transceiver with silent mode supported. phy mode is not implemented as of now. silent settings are kept in phy_power_on and phy_power_off. After phy mode is supported, the silent settings could be moved to phy_set_mode. Reviewed-by: Frank Li Signed-off-by: Peng Fan Acked-by: Marc Kleine-Budde Link: https://patch.msgid.link/20251001-can-v7-5-fad29efc3884@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 89189be9b795..330356706ad7 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -18,10 +18,12 @@ struct can_transceiver_data { #define CAN_TRANSCEIVER_STB_PRESENT BIT(0) #define CAN_TRANSCEIVER_EN_PRESENT BIT(1) #define CAN_TRANSCEIVER_DUAL_CH BIT(2) +#define CAN_TRANSCEIVER_SILENT_PRESENT BIT(3) }; struct can_transceiver_phy { struct phy *generic_phy; + struct gpio_desc *silent_gpio; struct gpio_desc *standby_gpio; struct gpio_desc *enable_gpio; struct can_transceiver_priv *priv; @@ -47,6 +49,7 @@ static int can_transceiver_phy_power_on(struct phy *phy) return ret; } } + gpiod_set_value_cansleep(can_transceiver_phy->silent_gpio, 0); gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 0); gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 1); @@ -59,6 +62,7 @@ static int can_transceiver_phy_power_off(struct phy *phy) struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy); struct can_transceiver_priv *priv = can_transceiver_phy->priv; + gpiod_set_value_cansleep(can_transceiver_phy->silent_gpio, 1); gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 1); gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 0); if (priv->mux_state) @@ -85,6 +89,14 @@ static const struct can_transceiver_data tja1048_drvdata = { .flags = CAN_TRANSCEIVER_STB_PRESENT | CAN_TRANSCEIVER_DUAL_CH, }; +static const struct can_transceiver_data tja1051_drvdata = { + .flags = CAN_TRANSCEIVER_SILENT_PRESENT | CAN_TRANSCEIVER_EN_PRESENT, +}; + +static const struct can_transceiver_data tja1057_drvdata = { + .flags = CAN_TRANSCEIVER_SILENT_PRESENT, +}; + static const struct of_device_id can_transceiver_phy_ids[] = { { .compatible = "ti,tcan1042", @@ -98,6 +110,14 @@ static const struct of_device_id can_transceiver_phy_ids[] = { .compatible = "nxp,tja1048", .data = &tja1048_drvdata }, + { + .compatible = "nxp,tja1051", + .data = &tja1051_drvdata + }, + { + .compatible = "nxp,tja1057", + .data = &tja1057_drvdata + }, { .compatible = "nxp,tjr1443", .data = &tcan1043_drvdata @@ -144,6 +164,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) const struct can_transceiver_data *drvdata; const struct of_device_id *match; struct phy *phy; + struct gpio_desc *silent_gpio; struct gpio_desc *standby_gpio; struct gpio_desc *enable_gpio; struct mux_state *mux_state; @@ -203,7 +224,16 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) can_transceiver_phy->enable_gpio = enable_gpio; } + if (drvdata->flags & CAN_TRANSCEIVER_SILENT_PRESENT) { + silent_gpio = devm_gpiod_get_index_optional(dev, "silent", i, + GPIOD_OUT_LOW); + if (IS_ERR(silent_gpio)) + return PTR_ERR(silent_gpio); + can_transceiver_phy->silent_gpio = silent_gpio; + } + phy_set_drvdata(can_transceiver_phy->generic_phy, can_transceiver_phy); + } phy_provider = devm_of_phy_provider_register(dev, can_transceiver_phy_xlate);