diff mbox series

[11/13] usb: ehci-mx6: Fix usb type issue in DM driver

Message ID 20200916125705.4341-12-peng.fan@nxp.com
State New
Delegated to: Marek Vasut
Headers show
Series ehci-mx6: update and fix | expand

Commit Message

Peng Fan Sept. 16, 2020, 12:57 p.m. UTC
From: Ye Li <ye.li@nxp.com>

Currently the clocks and power of USB controller and USB PHY are both
controlled by ehci-mx6 driver in device probe. However, the function
"ehci_usb_ofdata_to_platdata" calls "ehci_usb_phy_mode"
to access PHY registers when "dr_mode" is set to OTG, both "dr_mode" and
"extcon" properties are not set in DTB. This may cause hang at accessing
USB PHY registers if the power and clocks are not enabled.

Change the usb type logic to more clear way:
1. plat->init_type: The requested USB mode type from uplayers
2. priv->init_type: The USB mode type specified by DTB or by the USB ID pin or
   by external controller like tcpc or GPIO.
3. If two init_type are not same, return failure. Align with non-DM driver.
4. USB PHY access is moved after power and clock enabled.

Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/usb/host/ehci-mx6.c | 50 ++++++++++++++++++++++++++-----------
 1 file changed, 36 insertions(+), 14 deletions(-)

Comments

Marek Vasut Sept. 16, 2020, 1:45 p.m. UTC | #1
On 9/16/20 2:57 PM, peng.fan@nxp.com wrote:
> From: Ye Li <ye.li@nxp.com>
> 
> Currently the clocks and power of USB controller and USB PHY are both
> controlled by ehci-mx6 driver in device probe. However, the function
> "ehci_usb_ofdata_to_platdata" calls "ehci_usb_phy_mode"
> to access PHY registers when "dr_mode" is set to OTG, both "dr_mode" and
> "extcon" properties are not set in DTB. This may cause hang at accessing
> USB PHY registers if the power and clocks are not enabled.
> 
> Change the usb type logic to more clear way:
> 1. plat->init_type: The requested USB mode type from uplayers
> 2. priv->init_type: The USB mode type specified by DTB or by the USB ID pin or
>    by external controller like tcpc or GPIO.
> 3. If two init_type are not same, return failure. Align with non-DM driver.
> 4. USB PHY access is moved after power and clock enabled.

Can we get rid of the board-specific function and move the information
into the DT, and then have the driver extract the info from the DT while
correctly managing clock and power domains ?
diff mbox series

Patch

diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c
index 555a810953..7079750a93 100644
--- a/drivers/usb/host/ehci-mx6.c
+++ b/drivers/usb/host/ehci-mx6.c
@@ -510,6 +510,8 @@  int ehci_hcd_stop(int index)
 	return 0;
 }
 #else
+#define  USB_INIT_UNKNOWN (USB_INIT_DEVICE + 1)
+
 struct ehci_mx6_priv_data {
 	struct ehci_ctrl ctrl;
 	struct usb_ehci *ehci;
@@ -589,7 +591,7 @@  int __weak board_ehci_usb_phy_mode(struct udevice *dev)
 
 static int ehci_usb_phy_mode(struct udevice *dev)
 {
-	struct usb_platdata *plat = dev_get_platdata(dev);
+	struct ehci_mx6_priv_data *priv = dev_get_priv(dev);
 	void *__iomem addr = dev_read_addr_ptr(dev);
 	void *__iomem phy_ctrl, *__iomem phy_status;
 	const void *blob = gd->fdt_blob;
@@ -630,18 +632,18 @@  static int ehci_usb_phy_mode(struct udevice *dev)
 		val = readl(phy_ctrl);
 
 		if (val & USBPHY_CTRL_OTG_ID)
-			plat->init_type = USB_INIT_DEVICE;
+			priv->init_type = USB_INIT_DEVICE;
 		else
-			plat->init_type = USB_INIT_HOST;
+			priv->init_type = USB_INIT_HOST;
 	} else if (is_mx7() || is_imx8mm() || is_imx8mn()) {
 		phy_status = (void __iomem *)(addr +
 					      USBNC_PHY_STATUS_OFFSET);
 		val = readl(phy_status);
 
 		if (val & USBNC_PHYSTATUS_ID_DIG)
-			plat->init_type = USB_INIT_DEVICE;
+			priv->init_type = USB_INIT_DEVICE;
 		else
-			plat->init_type = USB_INIT_HOST;
+			priv->init_type = USB_INIT_HOST;
 	} else {
 		return -EINVAL;
 	}
@@ -652,31 +654,39 @@  static int ehci_usb_phy_mode(struct udevice *dev)
 static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
 {
 	struct usb_platdata *plat = dev_get_platdata(dev);
+	struct ehci_mx6_priv_data *priv = dev_get_priv(dev);
 	enum usb_dr_mode dr_mode;
 	const struct fdt_property *extcon;
 
 	extcon = fdt_get_property(gd->fdt_blob, dev_of_offset(dev),
 			"extcon", NULL);
 	if (extcon) {
-		plat->init_type = board_ehci_usb_phy_mode(dev);
-
-		return 0;
+		priv->init_type = board_ehci_usb_phy_mode(dev);
+		goto check_type;
 	}
 
 	dr_mode = usb_get_dr_mode(dev->node);
 
 	switch (dr_mode) {
 	case USB_DR_MODE_HOST:
-		plat->init_type = USB_INIT_HOST;
+		priv->init_type = USB_INIT_HOST;
 		break;
 	case USB_DR_MODE_PERIPHERAL:
-		plat->init_type = USB_INIT_DEVICE;
+		priv->init_type = USB_INIT_DEVICE;
 		break;
 	case USB_DR_MODE_OTG:
 	case USB_DR_MODE_UNKNOWN:
-		return ehci_usb_phy_mode(dev);
+		priv->init_type = USB_INIT_UNKNOWN;
+		break;
 	};
 
+check_type:
+	if (priv->init_type != USB_INIT_UNKNOWN && priv->init_type != plat->init_type) {
+		debug("Request USB type is %u, board forced type is %u\n",
+			plat->init_type, priv->init_type);
+		return -ENODEV;
+	}
+
 	return 0;
 }
 
@@ -741,9 +751,9 @@  static int ehci_usb_probe(struct udevice *dev)
 
 	priv->ehci = ehci;
 	priv->portnr = dev->req_seq;
-	priv->init_type = type;
 
-	ret = board_usb_init(priv->portnr, priv->init_type);
+	/* Init usb board level according to the requested init type */
+	ret = board_usb_init(priv->portnr, type);
 	if (ret) {
 		printf("Failed to initialize board for USB\n");
 		return ret;
@@ -759,10 +769,19 @@  static int ehci_usb_probe(struct udevice *dev)
 	if (ret)
 		return ret;
 
+	/* If the init_type is unknown due to it is not forced in DTB, we use USB ID to detect */
+	if (priv->init_type == USB_INIT_UNKNOWN) {
+		ret = ehci_usb_phy_mode(dev);
+		if (ret)
+			return ret;
+		if (priv->init_type != type)
+			return -ENODEV;
+	}
+
 #if CONFIG_IS_ENABLED(DM_REGULATOR)
 	if (priv->vbus_supply) {
 		ret = regulator_set_enable(priv->vbus_supply,
-					   (type == USB_INIT_DEVICE) ?
+					   (priv->init_type == USB_INIT_DEVICE) ?
 					   false : true);
 		if (ret && ret != -ENOSYS) {
 			printf("Error enabling VBUS supply (ret=%i)\n", ret);
@@ -789,9 +808,12 @@  static int ehci_usb_probe(struct udevice *dev)
 int ehci_usb_remove(struct udevice *dev)
 {
 	struct ehci_mx6_priv_data *priv = dev_get_priv(dev);
+	struct usb_platdata *plat = dev_get_platdata(dev);
 
 	ehci_deregister(dev);
 
+	plat->init_type = 0; /* Clean the requested usb type to host mode */
+
 	return board_usb_cleanup(dev->req_seq, priv->init_type);
 }