[PATCH 00/13] ehci-mx6: update and fix

From: Peng Fan peng.fan@nxp.com
This patchset is trying to upstream NXP downstream ehci-mx6 update and fix.
The driver has not been coverted to full DM, so there are some ifdefs there.
To avoid much conflicts when cherry-pick downstream patches, I prefer to convert after this patchset.
Peng Fan (4): usb: ehci-mx6: Add powerup_fixup implementation usb: ehci-mx6: preparing for ARM64 build usb: ehci-mx6: Add i.MX8 OTG controller support usb: ehci-mx6: configure usb out of suspend state
Ye Li (9): usb: ehci-mx6: Turn on the power domain of USB PHY usb: ehci-mx6: Update driver to support i.MX8MM usb: ehci-mx6: fix controller index for imx8m and imx8 usb: ehci-mx6: Enable iMX EHCI driver for iMX8M Nano usb: ehci-mx6: Fix USB QTD data buffer error ehci-mx6: Add OTG ID detecting by GPIO usb: ehci-mx6: Fix usb type issue in DM driver usb: ehci-mx6: Fix PHY power up issue on iMX8 platforms usb: ehci-mx6: Improve the bind function
arch/arm/include/asm/arch-imx8/clock.h | 8 + drivers/usb/host/Kconfig | 6 +- drivers/usb/host/ehci-mx6.c | 298 +++++++++++++++++++++---- 3 files changed, 263 insertions(+), 49 deletions(-)

From: Peng Fan peng.fan@nxp.com
When doing port reset, the PR bit of PORTSC1 will be automatically cleared by our IP, but standard EHCI needs explicit clear by software. The EHCI-HCD driver follow the EHCI specification, so after 50ms wait, it clear the PR bit by writting to the PORTSC1 register with value loaded before setting PR.
This sequence is ok for our IP when the delay time is exact. But when the timer is slower, some bits like PE, PSPD have been set by controller automatically after the PR is automatically cleared. So the writing to the PORTSC1 will overwrite these bits set by controller. And eventually the driver gets wrong status.
We implement the powerup_fixup operation which delays 50ms and will check the PR until it is cleared by controller. And will update the reg value which is written to PORTSC register by EHCI-HCD driver. This is much safer than depending on the delay time to be accurate and aligining with controller's behaiver.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/ehci-mx6.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 37b59758bb..9531fefce0 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -20,6 +20,7 @@ #include <dm.h> #include <asm/mach-types.h> #include <power/regulator.h> +#include <linux/iopoll.h> #include <linux/usb/otg.h>
#include "ehci.h" @@ -267,6 +268,21 @@ int usb_phy_mode(int port) } #endif
+static void ehci_mx6_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, + uint32_t *reg) +{ + u32 result; + int ret; + + mdelay(50); + + ret = read_poll_timeout(ehci_readl, status_reg, result, !(result & EHCI_PS_PR), 5, 2000); + if (ret) + printf("%s timeout\n", __func__); + + *reg = ehci_readl(status_reg); +} + static void usb_oc_config(int index) { #if defined(CONFIG_MX6) @@ -366,6 +382,10 @@ int ehci_mx6_common_init(struct usb_ehci *ehci, int index) }
#if !CONFIG_IS_ENABLED(DM_USB) +static const struct ehci_ops mx6_ehci_ops = { + .powerup_fixup = ehci_mx6_powerup_fixup, +}; + int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, struct ehci_hcor **hcor) { @@ -394,6 +414,8 @@ int ehci_hcd_init(int index, enum usb_init_type init, if (ret) return ret;
+ ehci_set_controller_priv(index, NULL, &mx6_ehci_ops); + type = board_usb_phy_mode(index);
if (hccr && hcor) { @@ -467,7 +489,8 @@ static int mx6_init_after_reset(struct ehci_ctrl *dev) }
static const struct ehci_ops mx6_ehci_ops = { - .init_after_reset = mx6_init_after_reset + .powerup_fixup = ehci_mx6_powerup_fixup, + .init_after_reset = mx6_init_after_reset };
static int ehci_usb_phy_mode(struct udevice *dev)

From: Peng Fan peng.fan@nxp.com
Use uintptr_t and ulong to avoid build warning for ARM64 platforms
Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/ehci-mx6.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 9531fefce0..4b8b3087ec 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -68,7 +68,7 @@ DECLARE_GLOBAL_DATA_PTR; #define UCMD_RESET (1 << 1) /* controller reset */
#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP) -static const unsigned phy_bases[] = { +static const ulong phy_bases[] = { USB_PHY0_BASE_ADDR, #if defined(USB_PHY1_BASE_ADDR) USB_PHY1_BASE_ADDR, @@ -241,7 +241,7 @@ struct usbnc_regs {
static void usb_power_config(int index) { - struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR + + struct usbnc_regs *usbnc = (struct usbnc_regs *)(uintptr_t)(USB_BASE_ADDR + (0x10000 * index) + USBNC_OFFSET); void __iomem *phy_cfg2 = (void __iomem *)(&usbnc->phy_cfg2);
@@ -254,7 +254,7 @@ static void usb_power_config(int index)
int usb_phy_mode(int port) { - struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR + + struct usbnc_regs *usbnc = (struct usbnc_regs *)(uintptr_t)(USB_BASE_ADDR + (0x10000 * port) + USBNC_OFFSET); void __iomem *status = (void __iomem *)(&usbnc->phy_status); u32 val; @@ -286,11 +286,11 @@ static void ehci_mx6_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, static void usb_oc_config(int index) { #if defined(CONFIG_MX6) - struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR + + struct usbnc_regs *usbnc = (struct usbnc_regs *)(uintptr_t)(USB_BASE_ADDR + USB_OTHERREGS_OFFSET); void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl[index]); #elif defined(CONFIG_MX7) || defined(CONFIG_MX7ULP) - struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR + + struct usbnc_regs *usbnc = (struct usbnc_regs *)(uintptr_t)(USB_BASE_ADDR + (0x10000 * index) + USBNC_OFFSET); void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl1); #endif @@ -395,7 +395,7 @@ int ehci_hcd_init(int index, enum usb_init_type init, #elif defined(CONFIG_MX7) || defined(CONFIG_MX7ULP) u32 controller_spacing = 0x10000; #endif - struct usb_ehci *ehci = (struct usb_ehci *)(USB_BASE_ADDR + + struct usb_ehci *ehci = (struct usb_ehci *)(uintptr_t)(USB_BASE_ADDR + (controller_spacing * index)); int ret;
@@ -419,8 +419,8 @@ int ehci_hcd_init(int index, enum usb_init_type init, type = board_usb_phy_mode(index);
if (hccr && hcor) { - *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); - *hcor = (struct ehci_hcor *)((uint32_t)*hccr + + *hccr = (struct ehci_hccr *)((uintptr_t)&ehci->caplength); + *hcor = (struct ehci_hcor *)((uintptr_t)*hccr + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); }
@@ -652,8 +652,8 @@ static int ehci_usb_probe(struct udevice *dev)
mdelay(10);
- hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); - hcor = (struct ehci_hcor *)((uint32_t)hccr + + hccr = (struct ehci_hccr *)((uintptr_t)&ehci->caplength); + hcor = (struct ehci_hcor *)((uintptr_t)hccr + HC_LENGTH(ehci_readl(&(hccr)->cr_capbase)));
return ehci_register(dev, hccr, hcor, &mx6_ehci_ops, 0, priv->init_type);

On 9/16/20 2:56 PM, peng.fan@nxp.com wrote:
From: Peng Fan peng.fan@nxp.com
Use uintptr_t and ulong to avoid build warning for ARM64 platforms
Signed-off-by: Peng Fan peng.fan@nxp.com
drivers/usb/host/ehci-mx6.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 9531fefce0..4b8b3087ec 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -68,7 +68,7 @@ DECLARE_GLOBAL_DATA_PTR; #define UCMD_RESET (1 << 1) /* controller reset */
#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP) -static const unsigned phy_bases[] = { +static const ulong phy_bases[] = {
Use unsigned long please, not all libc headers define ulong (e.g. termux -- android + bionic libc -- then fails to build).
[...]

From: Peng Fan peng.fan@nxp.com
The i.MX8 has two USB controllers: USBOH and USB3. The USBOH reuses previous i.MX6/7. It has same PHY IP as i.MX7ULP but NC registers are same as i.MX7D. So add its support in ehci-mx6 driver.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- arch/arm/include/asm/arch-imx8/clock.h | 8 ++++ drivers/usb/host/Kconfig | 4 +- drivers/usb/host/ehci-mx6.c | 61 +++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 7 deletions(-)
diff --git a/arch/arm/include/asm/arch-imx8/clock.h b/arch/arm/include/asm/arch-imx8/clock.h index bea157171f..55475f2bc4 100644 --- a/arch/arm/include/asm/arch-imx8/clock.h +++ b/arch/arm/include/asm/arch-imx8/clock.h @@ -24,4 +24,12 @@ enum mxc_clock {
u32 mxc_get_clock(enum mxc_clock clk);
+#define PLL_USB_EN_USB_CLKS_MASK (1 << 6) +#define PLL_USB_PWR_MASK (1 << 12) +#define PLL_USB_ENABLE_MASK (1 << 13) +#define PLL_USB_BYPASS_MASK (1 << 16) +#define PLL_USB_REG_ENABLE_MASK (1 << 21) +#define PLL_USB_DIV_SEL_MASK (7 << 22) +#define PLL_USB_LOCK_MASK (1 << 31) + #endif /* __ASM_ARCH_IMX8_CLOCK_H__ */ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1c374a7bd8..f48547caa0 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -139,8 +139,8 @@ config USB_EHCI_MX5 Enables support for the on-chip EHCI controller on i.MX5 SoCs.
config USB_EHCI_MX6 - bool "Support for i.MX6/i.MX7ULP on-chip EHCI USB controller" - depends on ARCH_MX6 || ARCH_MX7ULP + bool "Support for i.MX6/i.MX7ULP/i.MX8 on-chip EHCI USB controller" + depends on ARCH_MX6 || ARCH_MX7ULP || ARCH_IMX8 default y ---help--- Enables support for the on-chip EHCI controller on i.MX6 SoCs. diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 4b8b3087ec..b1721cd915 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -67,7 +67,7 @@ DECLARE_GLOBAL_DATA_PTR; #define UCMD_RUN_STOP (1 << 0) /* controller run/stop */ #define UCMD_RESET (1 << 1) /* controller reset */
-#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP) +#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP) || defined(CONFIG_IMX8) static const ulong phy_bases[] = { USB_PHY0_BASE_ADDR, #if defined(USB_PHY1_BASE_ADDR) @@ -101,7 +101,44 @@ static void usb_power_config(int index) &usbphy->usb1_chrg_detect);
scg_enable_usb_pll(true); +#elif defined(CONFIG_IMX8) + struct usbphy_regs __iomem *usbphy = (struct usbphy_regs __iomem *)USB_PHY0_BASE_ADDR; + int ret; + u32 val; + + if (index > 0) + return; + + writel(ANADIG_USB2_CHRG_DETECT_EN_B | ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B, + &usbphy->usb1_chrg_detect); + + if (!(readl(&usbphy->usb1_pll_480_ctrl) & PLL_USB_LOCK_MASK)) { + + /* Enable the regulator first */ + writel(PLL_USB_REG_ENABLE_MASK, &usbphy->usb1_pll_480_ctrl_set); + + /* Wait at least 25us */ + udelay(25); + + /* Enable the power */ + writel(PLL_USB_PWR_MASK, &usbphy->usb1_pll_480_ctrl_set); + + /* Wait lock */ + ret = readl_poll_sleep_timeout(&usbphy->usb1_pll_480_ctrl, val, + val & PLL_USB_LOCK_MASK, 10, 1000000); + + if (ret) { + /* If timeout, we power down the pll */ + writel(PLL_USB_PWR_MASK, &usbphy->usb1_pll_480_ctrl_clr); + return; + } + }
+ /* Clear the bypass */ + writel(PLL_USB_BYPASS_MASK, &usbphy->usb1_pll_480_ctrl_clr); + + /* Enable the PLL clock out to USB */ + writel((PLL_USB_EN_USB_CLKS_MASK | PLL_USB_ENABLE_MASK), &usbphy->usb1_pll_480_ctrl_set); #else struct anatop_regs __iomem *anatop = (struct anatop_regs __iomem *)ANATOP_BASE_ADDR; @@ -213,6 +250,20 @@ struct usbnc_regs { u32 reserve0[2]; u32 hsic_ctrl; }; +#elif defined(CONFIG_IMX8) +struct usbnc_regs { + u32 ctrl1; + u32 ctrl2; + u32 reserve1[10]; + u32 phy_cfg1; + u32 phy_cfg2; + u32 reserve2; + u32 phy_status; + u32 reserve3[4]; + u32 adp_cfg1; + u32 adp_cfg2; + u32 adp_status; +}; #else /* Base address for this IP block is 0x02184800 */ struct usbnc_regs { @@ -289,7 +340,7 @@ static void usb_oc_config(int index) struct usbnc_regs *usbnc = (struct usbnc_regs *)(uintptr_t)(USB_BASE_ADDR + USB_OTHERREGS_OFFSET); void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl[index]); -#elif defined(CONFIG_MX7) || defined(CONFIG_MX7ULP) +#elif defined(CONFIG_MX7) || defined(CONFIG_MX7ULP) || defined(CONFIG_IMX8) struct usbnc_regs *usbnc = (struct usbnc_regs *)(uintptr_t)(USB_BASE_ADDR + (0x10000 * index) + USBNC_OFFSET); void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl1); @@ -373,7 +424,7 @@ int ehci_mx6_common_init(struct usb_ehci *ehci, int index) usb_power_config(index); usb_oc_config(index);
-#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP) +#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP) || defined(CONFIG_IMX8) usb_internal_phy_clock_gate(index, 1); usb_phy_enable(index, ehci); #endif @@ -392,7 +443,7 @@ int ehci_hcd_init(int index, enum usb_init_type init, enum usb_init_type type; #if defined(CONFIG_MX6) u32 controller_spacing = 0x200; -#elif defined(CONFIG_MX7) || defined(CONFIG_MX7ULP) +#elif defined(CONFIG_MX7) || defined(CONFIG_MX7ULP) || defined(CONFIG_IMX8) u32 controller_spacing = 0x10000; #endif struct usb_ehci *ehci = (struct usb_ehci *)(uintptr_t)(USB_BASE_ADDR + @@ -506,7 +557,7 @@ static int ehci_usb_phy_mode(struct udevice *dev) * About fsl,usbphy, Refer to * Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt. */ - if (is_mx6() || is_mx7ulp()) { + if (is_mx6() || is_mx7ulp() || is_imx8()) { phy_off = fdtdec_lookup_phandle(blob, offset, "fsl,usbphy");

On 9/16/20 2:56 PM, peng.fan@nxp.com wrote: [...]
+++ b/arch/arm/include/asm/arch-imx8/clock.h @@ -24,4 +24,12 @@ enum mxc_clock {
u32 mxc_get_clock(enum mxc_clock clk);
+#define PLL_USB_EN_USB_CLKS_MASK (1 << 6)
Use BIT(6)
[...]
+++ b/drivers/usb/host/ehci-mx6.c @@ -67,7 +67,7 @@ DECLARE_GLOBAL_DATA_PTR; #define UCMD_RUN_STOP (1 << 0) /* controller run/stop */ #define UCMD_RESET (1 << 1) /* controller reset */
-#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP) +#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP) || defined(CONFIG_IMX8) static const ulong phy_bases[] = { USB_PHY0_BASE_ADDR, #if defined(USB_PHY1_BASE_ADDR) @@ -101,7 +101,44 @@ static void usb_power_config(int index) &usbphy->usb1_chrg_detect);
scg_enable_usb_pll(true); +#elif defined(CONFIG_IMX8)
- struct usbphy_regs __iomem *usbphy = (struct usbphy_regs __iomem *)USB_PHY0_BASE_ADDR;
- int ret;
- u32 val;
- if (index > 0)
return;
- writel(ANADIG_USB2_CHRG_DETECT_EN_B | ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B,
&usbphy->usb1_chrg_detect);
Can the ANATOP stuff be made into separate driver finally ? It seems the USB driver is doing too much unrelated work already.

From: Ye Li ye.li@nxp.com
Since there is no uclass for USB PHY. The device won't be setup for the USB PHY node in DTB. And its associated power domain device won't be turned on neither by DM framework.
This patch modifies the ehci-mx6 driver to enable the power domain device before access the USB PHY. This is only for DM driver. For non-DM part, users still need to power on the USB PHY in boards/SoC codes.
Reviewed-by: Peng Fan peng.fan@nxp.com Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/ehci-mx6.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index b1721cd915..0727aafc92 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2009 Daniel Mack daniel@caiaq.de * Copyright (C) 2010 Freescale Semiconductor, Inc. + * */
#include <common.h> @@ -19,6 +20,7 @@ #include <asm/mach-imx/sys_proto.h> #include <dm.h> #include <asm/mach-types.h> +#include <power-domain.h> #include <power/regulator.h> #include <linux/iopoll.h> #include <linux/usb/otg.h> @@ -569,6 +571,20 @@ static int ehci_usb_phy_mode(struct udevice *dev) if ((fdt_addr_t)addr == FDT_ADDR_T_NONE) return -EINVAL;
+ /* Need to power on the PHY before access it */ +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + struct udevice phy_dev; + struct power_domain pd; + int ret; + + phy_dev.node = offset_to_ofnode(phy_off); + if (!power_domain_get(&phy_dev, &pd)) { + ret = power_domain_on(&pd); + if (ret) + return ret; + } +#endif + phy_ctrl = (void __iomem *)(addr + USBPHY_CTRL); val = readl(phy_ctrl);

On 9/16/20 2:56 PM, peng.fan@nxp.com wrote: [...]
@@ -569,6 +571,20 @@ static int ehci_usb_phy_mode(struct udevice *dev) if ((fdt_addr_t)addr == FDT_ADDR_T_NONE) return -EINVAL;
/* Need to power on the PHY before access it */
+#if CONFIG_IS_ENABLED(POWER_DOMAIN)
struct udevice phy_dev;
struct power_domain pd;
int ret;
phy_dev.node = offset_to_ofnode(phy_off);
if (!power_domain_get(&phy_dev, &pd)) {
ret = power_domain_on(&pd);
if (ret)
return ret;
}
+#endif
Should we also turn it OFF somewhere ?

From: Ye Li ye.li@nxp.com
Since the i.MX8MM reuses the otg controllers on i.MX7D. We can use CONFIG_USB_EHCI_MX7 for them.
Due the TCPC and load switch are used on Typec circuit. Add the board_usb_init and board_usb_cleanup to ehci-mx6 DM driver. So we can implement the TCPC settings in these board functions.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/Kconfig | 2 +- drivers/usb/host/ehci-mx6.c | 33 +++++++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f48547caa0..1e2c5006d4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -147,7 +147,7 @@ config USB_EHCI_MX6
config USB_EHCI_MX7 bool "Support for i.MX7 on-chip EHCI USB controller" - depends on ARCH_MX7 + depends on ARCH_MX7 || IMX8MM default y ---help--- Enables support for the on-chip EHCI controller on i.MX7 SoCs. diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 0727aafc92..1db27949c6 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -277,7 +277,7 @@ struct usbnc_regs { }; #endif
-#elif defined(CONFIG_MX7) +#elif defined(CONFIG_USB_EHCI_MX7) struct usbnc_regs { u32 ctrl1; u32 ctrl2; @@ -342,8 +342,8 @@ static void usb_oc_config(int index) struct usbnc_regs *usbnc = (struct usbnc_regs *)(uintptr_t)(USB_BASE_ADDR + USB_OTHERREGS_OFFSET); void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl[index]); -#elif defined(CONFIG_MX7) || defined(CONFIG_MX7ULP) || defined(CONFIG_IMX8) - struct usbnc_regs *usbnc = (struct usbnc_regs *)(uintptr_t)(USB_BASE_ADDR + +#elif defined(CONFIG_USB_EHCI_MX7) || defined(CONFIG_MX7ULP) || defined(CONFIG_IMX8) + struct usbnc_regs *usbnc = (struct usbnc_regs *)(ulong)(USB_BASE_ADDR + (0x10000 * index) + USBNC_OFFSET); void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl1); #endif @@ -445,7 +445,7 @@ int ehci_hcd_init(int index, enum usb_init_type init, enum usb_init_type type; #if defined(CONFIG_MX6) u32 controller_spacing = 0x200; -#elif defined(CONFIG_MX7) || defined(CONFIG_MX7ULP) || defined(CONFIG_IMX8) +#elif defined(CONFIG_USB_EHCI_MX7) || defined(CONFIG_MX7ULP) || defined(CONFIG_IMX8) u32 controller_spacing = 0x10000; #endif struct usb_ehci *ehci = (struct usb_ehci *)(uintptr_t)(USB_BASE_ADDR + @@ -513,6 +513,12 @@ static int mx6_init_after_reset(struct ehci_ctrl *dev) struct usb_ehci *ehci = priv->ehci; int ret;
+ ret = board_usb_init(priv->portnr, priv->init_type); + if (ret) { + printf("Failed to initialize board for USB\n"); + return ret; + } + ret = ehci_mx6_common_init(priv->ehci, priv->portnr); if (ret) return ret; @@ -592,7 +598,7 @@ static int ehci_usb_phy_mode(struct udevice *dev) plat->init_type = USB_INIT_DEVICE; else plat->init_type = USB_INIT_HOST; - } else if (is_mx7()) { + } else if (is_mx7() || is_imx8mm()) { phy_status = (void __iomem *)(addr + USBNC_PHY_STATUS_OFFSET); val = readl(phy_status); @@ -689,6 +695,12 @@ static int ehci_usb_probe(struct udevice *dev) priv->portnr = dev->seq; priv->init_type = type;
+ ret = board_usb_init(priv->portnr, priv->init_type); + if (ret) { + printf("Failed to initialize board for USB\n"); + return ret; + } + #if CONFIG_IS_ENABLED(DM_REGULATOR) ret = device_get_supply_regulator(dev, "vbus-supply", &priv->vbus_supply); @@ -726,6 +738,15 @@ static int ehci_usb_probe(struct udevice *dev) return ehci_register(dev, hccr, hcor, &mx6_ehci_ops, 0, priv->init_type); }
+int ehci_usb_remove(struct udevice *dev) +{ + struct ehci_mx6_priv_data *priv = dev_get_priv(dev); + + ehci_deregister(dev); + + return board_usb_cleanup(dev->seq, priv->init_type); +} + static const struct udevice_id mx6_usb_ids[] = { { .compatible = "fsl,imx27-usb" }, { } @@ -738,7 +759,7 @@ U_BOOT_DRIVER(usb_mx6) = { .ofdata_to_platdata = ehci_usb_ofdata_to_platdata, .bind = ehci_usb_bind, .probe = ehci_usb_probe, - .remove = ehci_deregister, + .remove = ehci_usb_remove, .ops = &ehci_usb_ops, .platdata_auto_alloc_size = sizeof(struct usb_platdata), .priv_auto_alloc_size = sizeof(struct ehci_mx6_priv_data),

On 9/16/20 2:56 PM, peng.fan@nxp.com wrote:
From: Ye Li ye.li@nxp.com
Since the i.MX8MM reuses the otg controllers on i.MX7D. We can use CONFIG_USB_EHCI_MX7 for them.
Due the TCPC and load switch are used on Typec circuit. Add the board_usb_init and board_usb_cleanup to ehci-mx6 DM driver. So we can implement the TCPC settings in these board functions.
Do we really want board-specific callbacks in the USB driver ? That doesn't work too well with DM, so maybe this could be fixed otherwise ?

From: Ye Li ye.li@nxp.com
The bind codes calculate the address offset for controller index, but it is wrong for imx8m and imx8. It causes secondary controller fails to work. So fix this problem, and also change all seq to req_seq.
There is another issue here for imx8, the codes use runtime CPU type check. One iMX8, this check depends on IPC communication with SCFW, and need MU driver been probed. But the usbotg is configured with "u-boot,dm_spl" property for SDP download in SPL, so it will bind in early DM in u-boot while the MU is not bound at same time, so the CPU type check will hang. Fix it to use static check.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/ehci-mx6.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 1db27949c6..96dcf7655b 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -665,7 +665,11 @@ static int ehci_usb_bind(struct udevice *dev) * With these changes in place, the ad-hoc indexing goes away and * the driver is fully converted to DT probing. */ - u32 controller_spacing = is_mx7() ? 0x10000 : 0x200; + u32 controller_spacing; + if (IS_ENABLED(CONFIG_MX6)) + controller_spacing = 0x200; + else + controller_spacing = 0x10000; fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing; @@ -692,7 +696,7 @@ static int ehci_usb_probe(struct udevice *dev) }
priv->ehci = ehci; - priv->portnr = dev->seq; + priv->portnr = dev->req_seq; priv->init_type = type;
ret = board_usb_init(priv->portnr, priv->init_type); @@ -744,7 +748,7 @@ int ehci_usb_remove(struct udevice *dev)
ehci_deregister(dev);
- return board_usb_cleanup(dev->seq, priv->init_type); + return board_usb_cleanup(dev->req_seq, priv->init_type); }
static const struct udevice_id mx6_usb_ids[] = {

On 9/16/20 2:56 PM, peng.fan@nxp.com wrote: [...]
+++ b/drivers/usb/host/ehci-mx6.c @@ -665,7 +665,11 @@ static int ehci_usb_bind(struct udevice *dev) * With these changes in place, the ad-hoc indexing goes away and * the driver is fully converted to DT probing. */
- u32 controller_spacing = is_mx7() ? 0x10000 : 0x200;
- u32 controller_spacing;
- if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
- else
controller_spacing = 0x10000;
I suspect this changes the behavior from runtime detection to compile-time detection of the SoC ?
I would much prefer if we could extract the controller-PHY binding from DT instead of this workaround, finally. I recall that was the reason why this stuff above was added originally.

From: Ye Li ye.li@nxp.com
Add the IMX8MN to the EHCI-MX7 kconfig dependency.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/Kconfig | 2 +- drivers/usb/host/ehci-mx6.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1e2c5006d4..36eb1138c4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -147,7 +147,7 @@ config USB_EHCI_MX6
config USB_EHCI_MX7 bool "Support for i.MX7 on-chip EHCI USB controller" - depends on ARCH_MX7 || IMX8MM + depends on ARCH_MX7 || IMX8MM || IMX8MN default y ---help--- Enables support for the on-chip EHCI controller on i.MX7 SoCs. diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 96dcf7655b..ca015ef2f3 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -598,7 +598,7 @@ static int ehci_usb_phy_mode(struct udevice *dev) plat->init_type = USB_INIT_DEVICE; else plat->init_type = USB_INIT_HOST; - } else if (is_mx7() || is_imx8mm()) { + } else if (is_mx7() || is_imx8mm() || is_imx8mn()) { phy_status = (void __iomem *)(addr + USBNC_PHY_STATUS_OFFSET); val = readl(phy_status);

From: Peng Fan peng.fan@nxp.com
When moving to support partition reboot or android auto on XEN, linux kernel will runs into runtime suspend state, and the usb will be configured to low power suspend state by Linux.
Then we reboot and runs into U-Boot, however the usb already in suspended state and uboot not able to lock the phy pll, after clearing PHCD to out of suspended state, the phy pll could be locked and fastboot works.
Suggested-by: Li Jun jun.li@nxp.com Reviewed-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/ehci-mx6.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index ca015ef2f3..de0b0c3156 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -414,10 +414,17 @@ int __weak board_ehci_power(int port, int on) int ehci_mx6_common_init(struct usb_ehci *ehci, int index) { int ret; + u32 portsc;
enable_usboh3_clk(1); mdelay(1);
+ portsc = readl(&ehci->portsc); + if (portsc & PORT_PTS_PHCD) { + debug("suspended: portsc %x, enabled it.\n", portsc); + clrbits_le32(&ehci->portsc, PORT_PTS_PHCD); + } + /* Do board specific initialization */ ret = board_ehci_hcd_init(index); if (ret)

From: Ye Li ye.li@nxp.com
Some iMX6 platforms will meet "EHCI timed out on TD" when reading or writing data to USB disk. The root cause is last QTD reports data buffer error. Accroding to RM, this event indicates that an overrun of incoming data or a underrun of outgoing data has occurred for this transaction. This would generally be caused by the host controller not being able to access required data buffers in memory within necessary latency requirements.
Follow kernel's method to fix the issue by disabling stream mode
Signed-off-by: Ye Li ye.li@nxp.com Reviewed-by: Peter Chen peter.chen@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/ehci-mx6.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index de0b0c3156..aa0c1c355d 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -488,6 +488,11 @@ int ehci_hcd_init(int index, enum usb_init_type init, board_ehci_power(index, (type == USB_INIT_DEVICE) ? 0 : 1); if (type != init) return -ENODEV; + + if (is_mx6dqp() || is_mx6dq() || is_mx6sdl() || + ((is_mx6sl() || is_mx6sx()) && type == USB_INIT_HOST)) + setbits_le32(&ehci->usbmode, SDIS); + if (type == USB_INIT_DEVICE) return 0;
@@ -542,6 +547,10 @@ static int mx6_init_after_reset(struct ehci_ctrl *dev) } #endif
+ if (is_mx6dqp() || is_mx6dq() || is_mx6sdl() || + ((is_mx6sl() || is_mx6sx()) && type == USB_INIT_HOST)) + setbits_le32(&ehci->usbmode, SDIS); + if (type == USB_INIT_DEVICE) return 0;

On 9/16/20 2:57 PM, peng.fan@nxp.com wrote:
From: Ye Li ye.li@nxp.com
Some iMX6 platforms will meet "EHCI timed out on TD" when reading or writing data to USB disk. The root cause is last QTD reports data buffer error. Accroding to RM, this event indicates that an overrun of incoming data or a underrun of outgoing data has occurred for this transaction. This would generally be caused by the host controller not being able to access required data buffers in memory within necessary latency requirements.
How do you even trigger this in U-Boot ?

From: Ye Li ye.li@nxp.com
The i.MX7ulp EVK board uses GPIO to detect ID for USB OTG0, but when using DM USB driver, it is hard coded to use OTG ID pin. Add a board override function that when extcon property is provided, the function can check the GPIO to get ID.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/ehci-mx6.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index aa0c1c355d..555a810953 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -568,6 +568,25 @@ static const struct ehci_ops mx6_ehci_ops = { .init_after_reset = mx6_init_after_reset };
+/** + * board_ehci_usb_phy_mode - override usb phy mode + * @port: usb host/otg port + * + * Target board specific, override usb_phy_mode. + * When usb-otg is used as usb host port, iomux pad usb_otg_id can be + * left disconnected in this case usb_phy_mode will not be able to identify + * the phy mode that usb port is used. + * Machine file overrides board_usb_phy_mode. + * When the extcon property is set in DTB, machine must provide this function, otherwise + * it will default return HOST. + * + * Return: USB_INIT_DEVICE or USB_INIT_HOST + */ +int __weak board_ehci_usb_phy_mode(struct udevice *dev) +{ + return USB_INIT_HOST; +} + static int ehci_usb_phy_mode(struct udevice *dev) { struct usb_platdata *plat = dev_get_platdata(dev); @@ -634,6 +653,15 @@ static int ehci_usb_ofdata_to_platdata(struct udevice *dev) { struct usb_platdata *plat = dev_get_platdata(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; + }
dr_mode = usb_get_dr_mode(dev->node);

On 9/16/20 2:57 PM, peng.fan@nxp.com wrote: [...]
+int __weak board_ehci_usb_phy_mode(struct udevice *dev) +{
- return USB_INIT_HOST;
+}
static int ehci_usb_phy_mode(struct udevice *dev) { struct usb_platdata *plat = dev_get_platdata(dev); @@ -634,6 +653,15 @@ static int ehci_usb_ofdata_to_platdata(struct udevice *dev) { struct usb_platdata *plat = dev_get_platdata(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);
Shouldn't this information be available in the DT too ?

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(-)
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); }

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:
- plat->init_type: The requested USB mode type from uplayers
- priv->init_type: The USB mode type specified by DTB or by the USB ID pin or by external controller like tcpc or GPIO.
- If two init_type are not same, return failure. Align with non-DM driver.
- 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 ?

From: Ye Li ye.li@nxp.com
On iMX8 platforms like 8QM/QXP, we must power up the USB PHY resource before accessing the PHY. However, current init flow access the USB PHY in ehci_mx6_common_init prior than ehci_usb_phy_mode where the PHY is power up.
Fix the issue by adding ehci_get_usb_phy function to parse the PHY address from DTB and power up the PHY before ehci_mx6_common_init.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/ehci-mx6.c | 58 +++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 18 deletions(-)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 7079750a93..080bde71d3 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -517,6 +517,7 @@ struct ehci_mx6_priv_data { struct usb_ehci *ehci; struct udevice *vbus_supply; enum usb_init_type init_type; + void *__iomem phy_base; int portnr; };
@@ -592,11 +593,39 @@ int __weak board_ehci_usb_phy_mode(struct udevice *dev) static int ehci_usb_phy_mode(struct udevice *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; + u32 val; + + if (is_mx6() || is_mx7ulp() || is_imx8()) { + phy_ctrl = (void __iomem *)(priv->phy_base + USBPHY_CTRL); + val = readl(phy_ctrl); + + if (val & USBPHY_CTRL_OTG_ID) + priv->init_type = USB_INIT_DEVICE; + else + priv->init_type = USB_INIT_HOST; + } else if (is_mx7() || is_imx8mm() || is_imx8mn()) { + phy_status = (void __iomem *)(priv->phy_base + + USBNC_PHY_STATUS_OFFSET); + val = readl(phy_status); + + if (val & USBNC_PHYSTATUS_ID_DIG) + priv->init_type = USB_INIT_DEVICE; + else + priv->init_type = USB_INIT_HOST; + } else { + return -EINVAL; + } + + return 0; +} + +static int ehci_get_usb_phy(struct udevice *dev) +{ + struct ehci_mx6_priv_data *priv = dev_get_priv(dev); + void *__iomem addr = (void *__iomem)devfdt_get_addr(dev); const void *blob = gd->fdt_blob; int offset = dev_of_offset(dev), phy_off; - u32 val;
/* * About fsl,usbphy, Refer to @@ -627,23 +656,9 @@ static int ehci_usb_phy_mode(struct udevice *dev) return ret; } #endif - - phy_ctrl = (void __iomem *)(addr + USBPHY_CTRL); - val = readl(phy_ctrl); - - if (val & USBPHY_CTRL_OTG_ID) - priv->init_type = USB_INIT_DEVICE; - else - priv->init_type = USB_INIT_HOST; + priv->phy_base = addr; } 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) - priv->init_type = USB_INIT_DEVICE; - else - priv->init_type = USB_INIT_HOST; + priv->phy_base = addr; } else { return -EINVAL; } @@ -765,6 +780,13 @@ static int ehci_usb_probe(struct udevice *dev) if (ret) debug("%s: No vbus supply\n", dev->name); #endif + + ret = ehci_get_usb_phy(dev); + if (ret) { + debug("%s: fail to get USB PHY base\n", dev->name); + return ret; + } + ret = ehci_mx6_common_init(ehci, priv->portnr); if (ret) return ret;

From: Ye Li ye.li@nxp.com
To avoid calling devfdt_get_addr_index in bind, which introduces much overhead, checks the req_seq and only call the devfdt_get_addr_index when the req_seq (usb alias) is not set in DTS
Signed-off-by: Ye Li ye.li@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/usb/host/ehci-mx6.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 080bde71d3..20617850f3 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -735,13 +735,16 @@ static int ehci_usb_bind(struct udevice *dev) * the driver is fully converted to DT probing. */ u32 controller_spacing; - if (IS_ENABLED(CONFIG_MX6)) - controller_spacing = 0x200; - else - controller_spacing = 0x10000; - fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
- dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing; + if (dev->req_seq == -1) { + if (IS_ENABLED(CONFIG_MX6)) + controller_spacing = 0x200; + else + controller_spacing = 0x10000; + fdt_addr_t addr = devfdt_get_addr_index(dev, 0); + + dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing; + }
return 0; }

On 9/16/20 2:57 PM, peng.fan@nxp.com wrote: [...]
+++ b/drivers/usb/host/ehci-mx6.c @@ -735,13 +735,16 @@ static int ehci_usb_bind(struct udevice *dev) * the driver is fully converted to DT probing. */ u32 controller_spacing;
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing;
- if (dev->req_seq == -1) {
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
Can we get rid of the whole req_seq stuff ? It is a workaround, see 501547cec1 ("usb: ehci-mx6: Fix bus enumeration for DM case")

Hi Marek,
Subject: Re: [PATCH 13/13] usb: ehci-mx6: Improve the bind function
On 9/16/20 2:57 PM, peng.fan@nxp.com wrote: [...]
+++ b/drivers/usb/host/ehci-mx6.c @@ -735,13 +735,16 @@ static int ehci_usb_bind(struct udevice *dev) * the driver is fully converted to DT probing. */ u32 controller_spacing;
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing;
- if (dev->req_seq == -1) {
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
Can we get rid of the whole req_seq stuff ?
Could the restructure be done after the patchset? Or you need NXP to restructure the driver, then upstream NXP production ready patches?
If restructure first, there will be lots conflicts when I pick downstream patches, and error prone.
Thanks, Peng.
It is a workaround, see 501547cec1 ("usb: ehci-mx6: Fix bus enumeration for DM case")

On 9/16/20 3:56 PM, Peng Fan wrote:
Hi Marek,
Hi,
Subject: Re: [PATCH 13/13] usb: ehci-mx6: Improve the bind function
On 9/16/20 2:57 PM, peng.fan@nxp.com wrote: [...]
+++ b/drivers/usb/host/ehci-mx6.c @@ -735,13 +735,16 @@ static int ehci_usb_bind(struct udevice *dev) * the driver is fully converted to DT probing. */ u32 controller_spacing;
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing;
- if (dev->req_seq == -1) {
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
Can we get rid of the whole req_seq stuff ?
Could the restructure be done after the patchset? Or you need NXP to restructure the driver, then upstream NXP production ready patches?
If restructure first, there will be lots conflicts when I pick downstream patches, and error prone.
Can you prepare an RFC patchset for the restructuring on top of this one, so you can bisect breakage caused by the restructuring, and post those patches ? Then you will get your production-ready patches in without much changes and also there will be the long-overdue cleanup on the ML.
I really want NXP to do the cleanup, because the driver is becoming real bad and it is piling up a lot of unrelated code in it. I don't care about the order in which the patches go in though.
Does that work ?

Hi Marek,
Subject: Re: [PATCH 13/13] usb: ehci-mx6: Improve the bind function
On 9/16/20 3:56 PM, Peng Fan wrote:
Hi Marek,
Hi,
Subject: Re: [PATCH 13/13] usb: ehci-mx6: Improve the bind function
On 9/16/20 2:57 PM, peng.fan@nxp.com wrote: [...]
+++ b/drivers/usb/host/ehci-mx6.c @@ -735,13 +735,16 @@ static int ehci_usb_bind(struct udevice *dev) * the driver is fully converted to DT probing. */ u32 controller_spacing;
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing;
- if (dev->req_seq == -1) {
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
Can we get rid of the whole req_seq stuff ?
Could the restructure be done after the patchset? Or you need NXP to restructure the driver, then upstream NXP production ready patches?
If restructure first, there will be lots conflicts when I pick downstream patches, and error prone.
Can you prepare an RFC patchset for the restructuring on top of this one, so you can bisect breakage caused by the restructuring, and post those patches ? Then you will get your production-ready patches in without much changes and also there will be the long-overdue cleanup on the ML.
I really want NXP to do the cleanup, because the driver is becoming real bad and it is piling up a lot of unrelated code in it. I don't care about the order in which the patches go in though.
Does that work ?
Ok, so you agree that we do a cleanup patch based on the pachset, but you wanna to see NXP has start the real work before considering take the patchset, if I understand correct.
Then let me reserve time or ask other NXP guys doing this.
Thanks, Peng.

On 9/27/20 4:38 AM, Peng Fan wrote:
Hi Marek,
Subject: Re: [PATCH 13/13] usb: ehci-mx6: Improve the bind function
On 9/16/20 3:56 PM, Peng Fan wrote:
Hi Marek,
Hi,
Subject: Re: [PATCH 13/13] usb: ehci-mx6: Improve the bind function
On 9/16/20 2:57 PM, peng.fan@nxp.com wrote: [...]
+++ b/drivers/usb/host/ehci-mx6.c @@ -735,13 +735,16 @@ static int ehci_usb_bind(struct udevice *dev) * the driver is fully converted to DT probing. */ u32 controller_spacing;
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing;
- if (dev->req_seq == -1) {
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
Can we get rid of the whole req_seq stuff ?
Could the restructure be done after the patchset? Or you need NXP to restructure the driver, then upstream NXP production ready patches?
If restructure first, there will be lots conflicts when I pick downstream patches, and error prone.
Can you prepare an RFC patchset for the restructuring on top of this one, so you can bisect breakage caused by the restructuring, and post those patches ? Then you will get your production-ready patches in without much changes and also there will be the long-overdue cleanup on the ML.
I really want NXP to do the cleanup, because the driver is becoming real bad and it is piling up a lot of unrelated code in it. I don't care about the order in which the patches go in though.
Does that work ?
Ok, so you agree that we do a cleanup patch based on the pachset, but you wanna to see NXP has start the real work before considering take the patchset, if I understand correct.
Then let me reserve time or ask other NXP guys doing this.
I just want to see that cleanup happen, yes, it is already way overdue.

Marek,
Subject: Re: [PATCH 13/13] usb: ehci-mx6: Improve the bind function
On 9/27/20 4:38 AM, Peng Fan wrote:
Hi Marek,
Subject: Re: [PATCH 13/13] usb: ehci-mx6: Improve the bind function
On 9/16/20 3:56 PM, Peng Fan wrote:
Hi Marek,
Hi,
Subject: Re: [PATCH 13/13] usb: ehci-mx6: Improve the bind function
On 9/16/20 2:57 PM, peng.fan@nxp.com wrote: [...]
+++ b/drivers/usb/host/ehci-mx6.c @@ -735,13 +735,16 @@ static int ehci_usb_bind(struct udevice
*dev)
* the driver is fully converted to DT probing. */
u32 controller_spacing;
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing;
- if (dev->req_seq == -1) {
if (IS_ENABLED(CONFIG_MX6))
controller_spacing = 0x200;
else
controller_spacing = 0x10000;
fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
Can we get rid of the whole req_seq stuff ?
Could the restructure be done after the patchset? Or you need NXP to restructure the driver, then upstream NXP production ready patches?
If restructure first, there will be lots conflicts when I pick downstream patches, and error prone.
Can you prepare an RFC patchset for the restructuring on top of this one, so you can bisect breakage caused by the restructuring, and post
those patches ?
Then you will get your production-ready patches in without much changes and also there will be the long-overdue cleanup on the ML.
I really want NXP to do the cleanup, because the driver is becoming real bad and it is piling up a lot of unrelated code in it. I don't care about the order in which the patches go in though.
Does that work ?
Ok, so you agree that we do a cleanup patch based on the pachset, but you wanna to see NXP has start the real work before considering take the patchset, if I understand correct.
Then let me reserve time or ask other NXP guys doing this.
I just want to see that cleanup happen, yes, it is already way overdue.
I have posted the dts patch to Linux Community, https://patchwork.kernel.org/project/linux-arm-kernel/patch/ 1602638861-20278-1-git-send-email-peng.fan@nxp.com/
After that gets into Shawn's tree, I will sync with U-Boot.
So before that, could you please drop this patch 13 and only pick up the other patches? Or you need to wait the dts sync and then remove the bind function in ehci-mx6.c?
Thanks, Peng.

On 10/15/20 11:45 AM, Peng Fan wrote: Hi,
[...]
Subject: Re: [PATCH 13/13] usb: ehci-mx6: Improve the bind function
On 9/16/20 2:57 PM, peng.fan@nxp.com wrote: [...] > +++ b/drivers/usb/host/ehci-mx6.c > @@ -735,13 +735,16 @@ static int ehci_usb_bind(struct udevice
*dev)
> * the driver is fully converted to DT probing. > */ > u32 controller_spacing; > - if (IS_ENABLED(CONFIG_MX6)) > - controller_spacing = 0x200; > - else > - controller_spacing = 0x10000; > - fdt_addr_t addr = devfdt_get_addr_index(dev, 0); > > - dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing; > + if (dev->req_seq == -1) { > + if (IS_ENABLED(CONFIG_MX6)) > + controller_spacing = 0x200; > + else > + controller_spacing = 0x10000; > + fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
Can we get rid of the whole req_seq stuff ?
Could the restructure be done after the patchset? Or you need NXP to restructure the driver, then upstream NXP production ready patches?
If restructure first, there will be lots conflicts when I pick downstream patches, and error prone.
Can you prepare an RFC patchset for the restructuring on top of this one, so you can bisect breakage caused by the restructuring, and post
those patches ?
Then you will get your production-ready patches in without much changes and also there will be the long-overdue cleanup on the ML.
I really want NXP to do the cleanup, because the driver is becoming real bad and it is piling up a lot of unrelated code in it. I don't care about the order in which the patches go in though.
Does that work ?
Ok, so you agree that we do a cleanup patch based on the pachset, but you wanna to see NXP has start the real work before considering take the patchset, if I understand correct.
Then let me reserve time or ask other NXP guys doing this.
I just want to see that cleanup happen, yes, it is already way overdue.
I have posted the dts patch to Linux Community, https://patchwork.kernel.org/project/linux-arm-kernel/patch/ 1602638861-20278-1-git-send-email-peng.fan@nxp.com/
After that gets into Shawn's tree, I will sync with U-Boot.
So before that, could you please drop this patch 13 and only pick up the other patches? Or you need to wait the dts sync and then remove the bind function in ehci-mx6.c?
I am waiting for a cleanup of ehci-mx6.c patch series.
Also, setting any aliases in DT does not fix the problem described in 501547cec1 ("usb: ehci-mx6: Fix bus enumeration for DM case") does it ?
participants (3)
-
Marek Vasut
-
Peng Fan
-
peng.fan@nxp.com