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

Philipp Zabel <[email protected]>
Newsgroups org.infradead.lists.linux-arm-kernel,org.infradead.lists.linux-phy,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel,org.kernel.vger.linux-usb
Message-ID <[email protected]>
On Fr, 2026-08-21 at 18:23 +0200, Fabrice Gasnier wrote:
> From: Christian Bruel <[email protected]>
> 
> 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 <[email protected]>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
>  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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.