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