[PATCH 6/6] usb: host: ehci-msm: Register ULPI PHY through NOP wrapper

Stephan Gerhold stephan.gerhold at linaro.org
Mon Apr 7 11:54:26 CEST 2025


The UCLASS_USB device is removed and rebound each time you run "usb stop"
followed by "usb start", or when you switch between USB device and USB host
mode. Unfortunately, this causes issues with the current ehci-msm driver:

In ehci_usb_remove() we call generic_shutdown_phy(), but at that point the
ULPI PHY we registered in ehci_usb_of_bind() was already removed again by
the DM core.

Fix this by adding a UCLASS_NOP driver that keeps the PHY driver bound
permanently, and then just re-probe the actual USB part.

Reported-by: Jianfeng Zhu <JianfengA.Zhu at sony.com>
Closes: https://lore.kernel.org/u-boot/OSQPR04MB774067EBEEADD714EFE18C2A90882@OSQPR04MB7740.apcprd04.prod.outlook.com/
Signed-off-by: Stephan Gerhold <stephan.gerhold at linaro.org>
---
 drivers/usb/host/ehci-msm.c | 107 +++++++++++++++++++++++++++-----------------
 1 file changed, 65 insertions(+), 42 deletions(-)

diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 659a917ad27372f8f5c7138c40881b60413323e5..8aeb6a915563453ad2a02721d4828fd1b300a4e4 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -25,6 +25,9 @@ struct msm_ehci_priv {
 	struct ehci_ctrl ctrl; /* Needed by EHCI */
 	struct usb_ehci *ehci; /* Start of IP core*/
 	struct phy phy;
+};
+
+struct qcom_ci_hdrc_priv {
 	struct clk_bulk clks;
 };
 
@@ -54,34 +57,20 @@ static int ehci_usb_probe(struct udevice *dev)
 	struct ehci_hcor *hcor;
 	int ret;
 
-	ret = clk_get_bulk(dev, &p->clks);
-	if (ret && (ret != -ENOSYS && ret != -ENOENT)) {
-		dev_err(dev, "Failed to get clocks: %d\n", ret);
-		return ret;
-	}
-
-	ret = clk_enable_bulk(&p->clks);
-	if (ret)
-		goto cleanup_clocks;
-
 	hccr = (struct ehci_hccr *)((phys_addr_t)&ehci->caplength);
 	hcor = (struct ehci_hcor *)((phys_addr_t)hccr +
 			HC_LENGTH(ehci_readl(&(hccr)->cr_capbase)));
 
 	ret = generic_setup_phy(dev, &p->phy, 0, PHY_MODE_USB_HOST, 0);
 	if (ret)
-		goto cleanup_clocks;
+		return ret;
 
 	ret = board_usb_init(0, plat->init_type);
 	if (ret < 0)
-		goto cleanup_clocks;
+		return ret;
 
 	return ehci_register(dev, hccr, hcor, &msm_ehci_ops, 0,
 			     plat->init_type);
-
-cleanup_clocks:
-	clk_release_bulk(&p->clks);
-	return ret;
 }
 
 static int ehci_usb_remove(struct udevice *dev)
@@ -101,7 +90,6 @@ static int ehci_usb_remove(struct udevice *dev)
 	if (ret < 0)
 		return ret;
 
-	clk_release_bulk(&p->clks);
 	return 0;
 }
 
@@ -117,24 +105,6 @@ static int ehci_usb_of_to_plat(struct udevice *dev)
 	return 0;
 }
 
-static int ehci_usb_of_bind(struct udevice *dev)
-{
-	ofnode ulpi_node = ofnode_first_subnode(dev_ofnode(dev));
-	ofnode phy_node;
-
-	if (!ofnode_valid(ulpi_node))
-		return 0;
-
-	phy_node = ofnode_first_subnode(ulpi_node);
-	if (!ofnode_valid(phy_node)) {
-		printf("%s: ulpi subnode with no phy\n", __func__);
-		return -ENOENT;
-	}
-
-	return device_bind_driver_to_node(dev, "msm8916_usbphy", "msm8916_usbphy",
-					  phy_node, NULL);
-}
-
 #if defined(CONFIG_CI_UDC)
 /* Little quirk that MSM needs with Chipidea controller
  * Must reinit phy after reset
@@ -147,17 +117,10 @@ void ci_init_after_reset(struct ehci_ctrl *ctrl)
 }
 #endif
 
-static const struct udevice_id ehci_usb_ids[] = {
-	{ .compatible = "qcom,ci-hdrc", },
-	{ }
-};
-
 U_BOOT_DRIVER(usb_ehci) = {
 	.name	= "ehci_msm",
 	.id	= UCLASS_USB,
-	.of_match = ehci_usb_ids,
 	.of_to_plat = ehci_usb_of_to_plat,
-	.bind = ehci_usb_of_bind,
 	.probe = ehci_usb_probe,
 	.remove = ehci_usb_remove,
 	.ops	= &ehci_usb_ops,
@@ -165,3 +128,63 @@ U_BOOT_DRIVER(usb_ehci) = {
 	.plat_auto	= sizeof(struct usb_plat),
 	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
 };
+
+static int qcom_ci_hdrc_probe(struct udevice *dev)
+{
+	struct qcom_ci_hdrc_priv *p = dev_get_priv(dev);
+	int ret;
+
+	ret = clk_get_bulk(dev, &p->clks);
+	if (ret && (ret != -ENOSYS && ret != -ENOENT)) {
+		dev_err(dev, "Failed to get clocks: %d\n", ret);
+		return ret;
+	}
+
+	return clk_enable_bulk(&p->clks);
+}
+
+static int qcom_ci_hdrc_remove(struct udevice *dev)
+{
+	struct qcom_ci_hdrc_priv *p = dev_get_priv(dev);
+
+	return clk_release_bulk(&p->clks);
+}
+
+static int qcom_ci_hdrc_bind(struct udevice *dev)
+{
+	ofnode ulpi_node = ofnode_first_subnode(dev_ofnode(dev));
+	ofnode phy_node;
+	int ret;
+
+	ret = device_bind_driver_to_node(dev, "ehci_msm", "ehci_msm",
+					 dev_ofnode(dev), NULL);
+	if (ret)
+		return ret;
+
+	if (!ofnode_valid(ulpi_node))
+		return 0;
+
+	phy_node = ofnode_first_subnode(ulpi_node);
+	if (!ofnode_valid(phy_node)) {
+		printf("%s: ulpi subnode with no phy\n", __func__);
+		return -ENOENT;
+	}
+
+	return device_bind_driver_to_node(dev, "msm8916_usbphy", "msm8916_usbphy",
+					  phy_node, NULL);
+}
+
+static const struct udevice_id qcom_ci_hdrc_ids[] = {
+	{ .compatible = "qcom,ci-hdrc", },
+	{ }
+};
+
+U_BOOT_DRIVER(qcom_ci_hdrc) = {
+	.name		= "qcom_ci_hdrc",
+	.id		= UCLASS_NOP,
+	.of_match	= qcom_ci_hdrc_ids,
+	.bind		= qcom_ci_hdrc_bind,
+	.probe		= qcom_ci_hdrc_probe,
+	.remove		= qcom_ci_hdrc_remove,
+	.priv_auto	= sizeof(struct qcom_ci_hdrc_priv),
+};

-- 
2.47.2



More information about the U-Boot mailing list