From: Tony LIU Date: Wed, 7 Mar 2012 07:53:56 +0000 (+0800) Subject: ENGR00176299-1 usb host suspend/resume can't work randomly X-Git-Tag: v3.0.35-fsl~1313 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=ab2a00be76b6d4730f27ff511761f30828f08730;p=karo-tx-linux.git ENGR00176299-1 usb host suspend/resume can't work randomly MSL part - after suspend bit is set, we need to set PWD bit and clear it right now to let PHY know the state change - after suspend bit is set, disconnect detection should be clear - after set resume bit, disconnect detection should be set after 30 ms - IC issue PDM refer to TKT092876 TKT092872 Signed-off-by: Tony LIU --- diff --git a/arch/arm/mach-mx6/usb_dr.c b/arch/arm/mach-mx6/usb_dr.c index f49bb1096eed..96922a200b82 100644 --- a/arch/arm/mach-mx6/usb_dr.c +++ b/arch/arm/mach-mx6/usb_dr.c @@ -44,7 +44,6 @@ static u8 otg_used; static void usbotg_wakeup_event_clear(void); extern int clk_get_usecount(struct clk *clk); - /* Beginning of Common operation for DR port */ /* @@ -71,6 +70,51 @@ static struct fsl_usb2_wakeup_platform_data dr_wakeup_config = { .usb_wakeup_exhandle = usbotg_wakeup_event_clear, }; +static void fsl_platform_otg_set_usb_phy_dis( + struct fsl_usb2_platform_data *pdata, bool enable) +{ + u32 usb_phy_ctrl_dcdt = 0; + void __iomem *anatop_base_addr = MX6_IO_ADDRESS(ANATOP_BASE_ADDR); + usb_phy_ctrl_dcdt = __raw_readl( + MX6_IO_ADDRESS(pdata->phy_regs) + HW_USBPHY_CTRL) & + BM_USBPHY_CTRL_ENHOSTDISCONDETECT; + if (enable) { + if (usb_phy_ctrl_dcdt == 0) { + __raw_writel(BM_ANADIG_USB1_PLL_480_CTRL_EN_USB_CLKS, + anatop_base_addr + HW_ANADIG_USB1_PLL_480_CTRL_CLR); + + __raw_writel(BM_USBPHY_PWD_RXPWDENV, + MX6_IO_ADDRESS(pdata->phy_regs) + HW_USBPHY_PWD_SET); + + udelay(300); + + __raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + MX6_IO_ADDRESS(pdata->phy_regs) + + HW_USBPHY_CTRL_SET); + + UOG_USBSTS |= (1 << 7); + + while ((UOG_USBSTS & (1 << 7)) == 0) + ; + + udelay(2); + + __raw_writel(BM_USBPHY_PWD_RXPWDENV, + MX6_IO_ADDRESS(pdata->phy_regs) + HW_USBPHY_PWD_CLR); + + __raw_writel(BM_ANADIG_USB1_PLL_480_CTRL_EN_USB_CLKS, + anatop_base_addr + HW_ANADIG_USB1_PLL_480_CTRL_SET); + + } + } else { + if (usb_phy_ctrl_dcdt + == BM_USBPHY_CTRL_ENHOSTDISCONDETECT) + __raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + MX6_IO_ADDRESS(pdata->phy_regs) + + HW_USBPHY_CTRL_CLR); + } +} + static void usbotg_internal_phy_clock_gate(bool on) { void __iomem *phy_reg = MX6_IO_ADDRESS(USB_PHY0_BASE_ADDR); @@ -325,10 +369,30 @@ static void usbotg_wakeup_event_clear(void) #ifdef CONFIG_USB_EHCI_ARC_OTG /* Beginning of host related operation for DR port */ -static void _host_platform_suspend(struct fsl_usb2_platform_data *pdata) +static void _host_platform_rh_suspend(struct fsl_usb2_platform_data *pdata) { void __iomem *phy_reg = MX6_IO_ADDRESS(USB_PHY0_BASE_ADDR); u32 tmp; + u32 index = 0; + + /* before we set and then clear PWD bit, + * we must wait LS to be suspend */ + if ((UOG_PORTSC1 & (3 << 26)) != (1 << 26)) { + while (((UOG_PORTSC1 & PORTSC_LS_MASK) != PORTSC_LS_J_STATE) && + (index < 1000)) { + index++; + udelay(4); + } + } else { + while (((UOG_PORTSC1 & PORTSC_LS_MASK) != PORTSC_LS_K_STATE) && + (index < 1000)) { + index++; + udelay(4); + } + } + + if (index >= 1000) + printk(KERN_INFO "%s big error\n", __func__); tmp = (BM_USBPHY_PWD_TXPWDFS | BM_USBPHY_PWD_TXPWDIBIAS @@ -338,21 +402,30 @@ static void _host_platform_suspend(struct fsl_usb2_platform_data *pdata) | BM_USBPHY_PWD_RXPWDDIFF | BM_USBPHY_PWD_RXPWDRX); __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_SET); + + __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_CLR); + + fsl_platform_otg_set_usb_phy_dis(pdata, 0); } -static void _host_platform_resume(struct fsl_usb2_platform_data *pdata) +static void _host_platform_rh_resume(struct fsl_usb2_platform_data *pdata) { - void __iomem *phy_reg = MX6_IO_ADDRESS(USB_PHY0_BASE_ADDR); - u32 tmp; + u32 index = 0; - tmp = (BM_USBPHY_PWD_TXPWDFS - | BM_USBPHY_PWD_TXPWDIBIAS - | BM_USBPHY_PWD_TXPWDV2I - | BM_USBPHY_PWD_RXPWDENV - | BM_USBPHY_PWD_RXPWD1PT1 - | BM_USBPHY_PWD_RXPWDDIFF - | BM_USBPHY_PWD_RXPWDRX); - __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_CLR); + if ((UOG_PORTSC1 & (3 << 26)) != (2 << 26)) + return ; + + while ((UOG_PORTSC1 & PORTSC_PORT_FORCE_RESUME) + && (index < 1000)) { + msleep(1); + index++; + } + + if (index >= 1000) + printk(KERN_INFO "%s big error\n", __func__); + + msleep(1); + fsl_platform_otg_set_usb_phy_dis(pdata, 1); } static void _host_phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, bool enable) @@ -487,8 +560,9 @@ void __init mx6_usb_dr_init(void) #ifdef CONFIG_USB_EHCI_ARC_OTG dr_utmi_config.operating_mode = DR_HOST_MODE; dr_utmi_config.wake_up_enable = _host_wakeup_enable; - dr_utmi_config.platform_suspend = _host_platform_suspend; - dr_utmi_config.platform_resume = _host_platform_resume; + dr_utmi_config.platform_rh_suspend = _host_platform_rh_suspend; + dr_utmi_config.platform_rh_resume = _host_platform_rh_resume; + dr_utmi_config.platform_set_disconnect_det = fsl_platform_otg_set_usb_phy_dis; dr_utmi_config.phy_lowpower_suspend = _host_phy_lowpower_suspend; dr_utmi_config.is_wakeup_event = _is_host_wakeup; dr_utmi_config.wakeup_pdata = &dr_wakeup_config; @@ -499,8 +573,9 @@ void __init mx6_usb_dr_init(void) #ifdef CONFIG_USB_GADGET_ARC dr_utmi_config.operating_mode = DR_UDC_MODE; dr_utmi_config.wake_up_enable = _device_wakeup_enable; - dr_utmi_config.platform_suspend = NULL; - dr_utmi_config.platform_resume = NULL; + dr_utmi_config.platform_rh_suspend = NULL; + dr_utmi_config.platform_rh_resume = NULL; + dr_utmi_config.platform_set_disconnect_det = NULL; dr_utmi_config.phy_lowpower_suspend = _device_phy_lowpower_suspend; dr_utmi_config.is_wakeup_event = _is_device_wakeup; dr_utmi_config.wakeup_pdata = &dr_wakeup_config; diff --git a/arch/arm/mach-mx6/usb_h1.c b/arch/arm/mach-mx6/usb_h1.c index 9d2a1e758bee..8865556b8256 100644 --- a/arch/arm/mach-mx6/usb_h1.c +++ b/arch/arm/mach-mx6/usb_h1.c @@ -35,6 +35,51 @@ static struct clk *usb_oh3_clk; extern int clk_get_usecount(struct clk *clk); static struct fsl_usb2_platform_data usbh1_config; +static void fsl_platform_h1_set_usb_phy_dis( + struct fsl_usb2_platform_data *pdata, bool enable) +{ + u32 usb_phy_ctrl_dcdt = 0; + void __iomem *anatop_base_addr = MX6_IO_ADDRESS(ANATOP_BASE_ADDR); + usb_phy_ctrl_dcdt = __raw_readl( + MX6_IO_ADDRESS(pdata->phy_regs) + HW_USBPHY_CTRL) & + BM_USBPHY_CTRL_ENHOSTDISCONDETECT; + if (enable) { + if (usb_phy_ctrl_dcdt == 0) { + __raw_writel(BM_ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS, + anatop_base_addr + HW_ANADIG_USB2_PLL_480_CTRL_CLR); + + __raw_writel(BM_USBPHY_PWD_RXPWDENV, + MX6_IO_ADDRESS(pdata->phy_regs) + HW_USBPHY_PWD_SET); + + udelay(300); + + __raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + MX6_IO_ADDRESS(pdata->phy_regs) + + HW_USBPHY_CTRL_SET); + + UH1_USBSTS |= (1 << 7); + + while ((UH1_USBSTS & (1 << 7)) == 0) + ; + + udelay(2); + + __raw_writel(BM_USBPHY_PWD_RXPWDENV, + MX6_IO_ADDRESS(pdata->phy_regs) + HW_USBPHY_PWD_CLR); + + __raw_writel(BM_ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS, + anatop_base_addr + HW_ANADIG_USB2_PLL_480_CTRL_SET); + + } + } else { + if (usb_phy_ctrl_dcdt + == BM_USBPHY_CTRL_ENHOSTDISCONDETECT) + __raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + MX6_IO_ADDRESS(pdata->phy_regs) + + HW_USBPHY_CTRL_CLR); + } +} + static void usbh1_internal_phy_clock_gate(bool on) { void __iomem *phy_reg = MX6_IO_ADDRESS(USB_PHY1_BASE_ADDR); @@ -148,10 +193,30 @@ static void _wake_up_enable(struct fsl_usb2_platform_data *pdata, bool enable) } } -static void usbh1_platform_suspend(struct fsl_usb2_platform_data *pdata) +static void usbh1_platform_rh_suspend(struct fsl_usb2_platform_data *pdata) { void __iomem *phy_reg = MX6_IO_ADDRESS(USB_PHY1_BASE_ADDR); u32 tmp; + u32 index = 0; + + /* before we set and then clear PWD bit, + * we must wait LS to be J */ + if ((UH1_PORTSC1 & (3 << 26)) != (1 << 26)) { + while (((UH1_PORTSC1 & PORTSC_LS_MASK) != PORTSC_LS_J_STATE) && + (index < 1000)) { + index++; + udelay(4); + } + } else { + while (((UH1_PORTSC1 & PORTSC_LS_MASK) != PORTSC_LS_K_STATE) && + (index < 1000)) { + index++; + udelay(4); + } + } + + if (index >= 1000) + printk(KERN_INFO "%s big error\n", __func__); tmp = (BM_USBPHY_PWD_TXPWDFS | BM_USBPHY_PWD_TXPWDIBIAS @@ -161,21 +226,30 @@ static void usbh1_platform_suspend(struct fsl_usb2_platform_data *pdata) | BM_USBPHY_PWD_RXPWDDIFF | BM_USBPHY_PWD_RXPWDRX); __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_SET); + + __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_CLR); + + fsl_platform_h1_set_usb_phy_dis(pdata, 0); } -static void usbh1_platform_resume(struct fsl_usb2_platform_data *pdata) +static void usbh1_platform_rh_resume(struct fsl_usb2_platform_data *pdata) { - void __iomem *phy_reg = MX6_IO_ADDRESS(USB_PHY1_BASE_ADDR); - u32 tmp; + u32 index = 0; - tmp = (BM_USBPHY_PWD_TXPWDFS - | BM_USBPHY_PWD_TXPWDIBIAS - | BM_USBPHY_PWD_TXPWDV2I - | BM_USBPHY_PWD_RXPWDENV - | BM_USBPHY_PWD_RXPWD1PT1 - | BM_USBPHY_PWD_RXPWDDIFF - | BM_USBPHY_PWD_RXPWDRX); - __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_CLR); + if ((UH1_PORTSC1 & (3 << 26)) != (2 << 26)) + return ; + + while ((UH1_PORTSC1 & PORTSC_PORT_FORCE_RESUME) + && (index < 1000)) { + msleep(1); + index++; + } + + if (index >= 1000) + printk(KERN_INFO "%s big error\n", __func__); + + msleep(1); + fsl_platform_h1_set_usb_phy_dis(pdata, 1); } static void _phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, bool enable) @@ -253,8 +327,9 @@ static struct fsl_usb2_platform_data usbh1_config = { .power_budget = 500, /* 500 mA max power */ .wake_up_enable = _wake_up_enable, .usb_clock_for_pm = usbh1_clock_gate, - .platform_suspend = usbh1_platform_suspend, - .platform_resume = usbh1_platform_resume, + .platform_rh_suspend = usbh1_platform_rh_suspend, + .platform_rh_resume = usbh1_platform_rh_resume, + .platform_set_disconnect_det = fsl_platform_h1_set_usb_phy_dis, .phy_lowpower_suspend = _phy_lowpower_suspend, .is_wakeup_event = _is_usbh1_wakeup, .wakeup_handler = h1_wakeup_handler, diff --git a/arch/arm/plat-mxc/usb_common.c b/arch/arm/plat-mxc/usb_common.c index bb9c45a0127e..a4a3721246f8 100755 --- a/arch/arm/plat-mxc/usb_common.c +++ b/arch/arm/plat-mxc/usb_common.c @@ -900,16 +900,25 @@ EXPORT_SYMBOL(usb_event_is_otg_wakeup); void fsl_platform_set_usb_phy_dis(struct fsl_usb2_platform_data *pdata, bool enable) { + u32 usb_phy_ctrl_dcdt = 0; /* for HSIC, we do not need to enable disconnect detection */ if (pdata->phy_mode == FSL_USB2_PHY_HSIC) return; - - if (enable) - __raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, - MX6_IO_ADDRESS(pdata->phy_regs) + HW_USBPHY_CTRL_SET); - else - __raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, - MX6_IO_ADDRESS(pdata->phy_regs) + HW_USBPHY_CTRL_CLR); + usb_phy_ctrl_dcdt = __raw_readl( + MX6_IO_ADDRESS(pdata->phy_regs) + HW_USBPHY_CTRL) & + BM_USBPHY_CTRL_ENHOSTDISCONDETECT; + if (enable) { + if (usb_phy_ctrl_dcdt == 0) + __raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + MX6_IO_ADDRESS(pdata->phy_regs) + + HW_USBPHY_CTRL_SET); + } else { + if (usb_phy_ctrl_dcdt + == BM_USBPHY_CTRL_ENHOSTDISCONDETECT) + __raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + MX6_IO_ADDRESS(pdata->phy_regs) + + HW_USBPHY_CTRL_CLR); + } } EXPORT_SYMBOL(fsl_platform_set_usb_phy_dis); #endif