summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorYe Li <ye.li@nxp.com>2018-10-24 01:08:26 -0700
committerYe Li <ye.li@nxp.com>2022-04-06 14:30:59 +0800
commit55d594ce822286afbde4f207f01b3b785861d6a3 (patch)
tree1afc62fae83c81a011cfca8b39a7257f0029130e /drivers/usb
parente51ecc1651b13946f47ee82abcc3263a3b9174fd (diff)
MLK-20057 usb: ehci-mx6: Fix usb type issue in DM driver
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> (cherry picked from commit d62ffbb7fa3136062a977d4f8bdc0f03b464b8e4) (cherry picked from commit 77f25be0d6ddea71fc28ecc08c8f5477054208d0) (cherry picked from commit 8a7630f936a8b392c46aa291e50bce5415f917e6)
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/ehci-mx6.c68
1 files changed, 36 insertions, 32 deletions
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c
index 6038de9937..a65668d4dd 100644
--- a/drivers/usb/host/ehci-mx6.c
+++ b/drivers/usb/host/ehci-mx6.c
@@ -644,8 +644,8 @@ int __weak board_ehci_usb_phy_mode(struct udevice *dev)
static int ehci_usb_phy_mode(struct udevice *dev)
{
- struct usb_plat *plat = dev_get_plat(dev);
- void *__iomem addr = dev_read_addr_ptr(dev);
+ struct ehci_mx6_priv_data *priv = dev_get_priv(dev);
+ void *__iomem addr = (void *__iomem)devfdt_get_addr(dev);
void *__iomem phy_ctrl, *__iomem phy_status;
const void *blob = gd->fdt_blob;
int offset = dev_of_offset(dev), phy_off;
@@ -683,18 +683,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;
}
@@ -705,30 +705,37 @@ static int ehci_usb_phy_mode(struct udevice *dev)
static int ehci_usb_of_to_plat(struct udevice *dev)
{
struct usb_plat *plat = dev_get_plat(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_ofnode(dev));
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;
default:
- plat->init_type = USB_INIT_UNKNOWN;
+ priv->init_type = USB_INIT_UNKNOWN;
};
+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;
}
@@ -811,10 +818,10 @@ static int ehci_usb_probe(struct udevice *dev)
return ret;
priv->ehci = ehci;
- priv->init_type = type;
priv->phy_type = usb_get_phy_mode(dev_ofnode(dev));
- 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;
@@ -834,20 +841,6 @@ static int ehci_usb_probe(struct udevice *dev)
mdelay(1);
#endif
- /*
- * If the device tree didn't specify host or device,
- * the default is USB_INIT_UNKNOWN, so we need to check
- * the register. For imx8mm and imx8mn, the clocks need to be
- * running first, so we defer the check until they are.
- */
- if (priv->init_type == USB_INIT_UNKNOWN) {
- ret = ehci_usb_phy_mode(dev);
- if (ret)
- goto err_clk;
- else
- priv->init_type = plat->init_type;
- }
-
#if CONFIG_IS_ENABLED(DM_REGULATOR)
ret = device_get_supply_regulator(dev, "vbus-supply",
&priv->vbus_supply);
@@ -869,10 +862,21 @@ static int ehci_usb_probe(struct udevice *dev)
#endif
#endif
+ /* 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)
+ goto err_clk;
+ if (priv->init_type != type) {
+ ret = -ENODEV;
+ goto err_clk;
+ }
+ }
+
#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);
@@ -917,9 +921,6 @@ err_regulator:
err_clk:
#if CONFIG_IS_ENABLED(CLK)
clk_disable(&priv->clk);
-#else
- /* Compatibility with DM_USB and !CLK */
- enable_usboh3_clk(0);
#endif
return ret;
}
@@ -927,6 +928,7 @@ err_clk:
int ehci_usb_remove(struct udevice *dev)
{
struct ehci_mx6_priv_data *priv __maybe_unused = dev_get_priv(dev);
+ struct usb_plat *plat = dev_get_plat(dev);
ehci_deregister(dev);
@@ -943,6 +945,8 @@ int ehci_usb_remove(struct udevice *dev)
clk_disable(&priv->clk);
#endif
+ plat->init_type = 0; /* Clean the requested usb type to host mode */
+
return board_usb_cleanup(dev_seq(dev), priv->init_type);
}