Re: [PATCH 04/16] usb: typec: tcpm: Add STM32 UCPD driver

From: Philipp Zabel

Date: Mon Aug 24 2026 - 10:44:58 EST


On Fr, 2026-08-21 at 18:23 +0200, Fabrice Gasnier wrote:
> From: Christian Bruel <christian.bruel@xxxxxxxxxxx>
>
> Add support for the STM32 UCPD controller providing USB Type‑C
> Configuration Channel (CC) management and USB Power Delivery (PD)
> protocol handling.
>
> The driver integrates with the TCPM (Type-C Port Manager) framework.
> On this platform, VBUS monitoring is handled by an external TCPP03
>
> Note the driver doesn't allow PD, so the role can only be set at
> plug time.
>
> The UCPD can be a wakeup source, in such a case, keep the hardware
> enabled. Else, keep track of the vbus state as provider and as
> consumer, to be restored after low power.
>
> Signed-off-by: Christian Bruel <christian.bruel@xxxxxxxxxxx>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@xxxxxxxxxxx>
> ---
> drivers/usb/typec/tcpm/Kconfig | 12 +
> drivers/usb/typec/tcpm/Makefile | 1 +
> drivers/usb/typec/tcpm/stm32_ucpd.c | 818 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 831 insertions(+)
>
[...]
> diff --git a/drivers/usb/typec/tcpm/stm32_ucpd.c b/drivers/usb/typec/tcpm/stm32_ucpd.c
> new file mode 100644
> index 000000000000..b5c9dc417814
> --- /dev/null
> +++ b/drivers/usb/typec/tcpm/stm32_ucpd.c
> @@ -0,0 +1,818 @@
[...]
> +static int stm32_ucpd_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct i2c_client *tcpp_client;
> + struct device_node *tcpp_np;
> + struct stm32_ucpd *ucpd;
> + int ret;
> +
> + ucpd = devm_kzalloc(dev, sizeof(*ucpd), GFP_KERNEL);
> + if (!ucpd)
> + return -ENOMEM;
> +
> + ucpd->dev = dev;
> + platform_set_drvdata(pdev, ucpd);
> +
> + ucpd->vdd = devm_regulator_get(&pdev->dev, "ucpd");
> + if (IS_ERR(ucpd->vdd))
> + return dev_err_probe(&pdev->dev, PTR_ERR(ucpd->vdd), "vdd get failed\n");
> +
> + ucpd->vconn = devm_regulator_get_optional(&pdev->dev, "vconn");
> + if (IS_ERR(ucpd->vconn)) {
> + if (PTR_ERR(ucpd->vconn) != -ENODEV)
> + return dev_err_probe(&pdev->dev, PTR_ERR(ucpd->vconn),
> + "vconn get failed\n");
> + ucpd->vconn = NULL;
> + }
> +
> + ucpd->base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(ucpd->base))
> + return PTR_ERR(ucpd->base);
> +
> + ucpd->num_clks = devm_clk_bulk_get_all(dev, &ucpd->clks);
> + if (ucpd->num_clks <= 0)
> + return ucpd->num_clks ? : -ENOENT;
> +
> + ucpd->reset = devm_reset_control_get_exclusive(dev, "ucpd");
> + if (IS_ERR(ucpd->reset))
> + return dev_err_probe(dev, PTR_ERR(ucpd->reset), "Failed to get UCPD reset\n");
> +
> + ret = stm32_ucpd_get_trim_values(ucpd);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to get trim values\n");
> +
> + ucpd->vconn_cc1_gpio = devm_gpiod_get_optional(dev, "vconn-cc1", GPIOD_OUT_LOW);
> + if (IS_ERR(ucpd->vconn_cc1_gpio))
> + return dev_err_probe(dev, PTR_ERR(ucpd->vconn_cc1_gpio), "vconn-cc1 get failed\n");
> +
> + ucpd->vconn_cc2_gpio = devm_gpiod_get_optional(dev, "vconn-cc2", GPIOD_OUT_LOW);
> + if (IS_ERR(ucpd->vconn_cc2_gpio))
> + return dev_err_probe(dev, PTR_ERR(ucpd->vconn_cc2_gpio), "vconn-cc2 get failed\n");
> +
> + ucpd->ucpd_irq = platform_get_irq_byname(pdev, "ucpd");
> + if (ucpd->ucpd_irq < 0)
> + return ucpd->ucpd_irq;
> +
> + ucpd->tcpc_dev.fwnode = device_get_named_child_node(dev, "connector");
> + if (IS_ERR(ucpd->tcpc_dev.fwnode))
> + return dev_err_probe(dev, PTR_ERR(ucpd->tcpc_dev.fwnode), "connector not found");
> +
> + tcpp_np = of_parse_phandle(dev->of_node, "st,tcpp", 0);
> + if (!tcpp_np)
> + return -EINVAL;
> +
> + tcpp_client = of_find_i2c_device_by_node(tcpp_np);
> + of_node_put(tcpp_np);
> +
> + /* tcpp driver must be probed so tcpp_init() can be called from tcpm_register_port() */
> + if (!tcpp_client || !tcpp_client->dev.driver)
> + return -EPROBE_DEFER;
> +
> + ucpd->tcpp_dev = &tcpp_client->dev;
> +
> + /* No irq until tcpm_port is ready */
> + irq_set_status_flags(ucpd->ucpd_irq, IRQ_NOAUTOEN);
> + ret = devm_request_threaded_irq(dev, ucpd->ucpd_irq, NULL, ucpd_irq_handler, IRQF_ONESHOT,
> + dev_name(dev), ucpd);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to get irq\n");
> + goto put_tcpp;
> + }
> +
> + init_tcpc_dev(&ucpd->tcpc_dev);
> +
> + ret = regulator_enable(ucpd->vdd);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to enable regulator\n");
> + goto put_tcpp;
> + }
> +
> + ret = clk_bulk_prepare_enable(ucpd->num_clks, ucpd->clks);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to enable clocks\n");
> + goto disable_regulator;
> + }
> +
> + ret = reset_control_deassert(ucpd->reset);

No reset_control_assert() in the error path and in stm32_ucpd_remove()?

regards
Philipp