summaryrefslogtreecommitdiff
path: root/arch/arm/mach-mvf/usb_dr2.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-mvf/usb_dr2.c')
-rw-r--r--arch/arm/mach-mvf/usb_dr2.c199
1 files changed, 189 insertions, 10 deletions
diff --git a/arch/arm/mach-mvf/usb_dr2.c b/arch/arm/mach-mvf/usb_dr2.c
index d26cfde125e0..c1d6ebf51791 100644
--- a/arch/arm/mach-mvf/usb_dr2.c
+++ b/arch/arm/mach-mvf/usb_dr2.c
@@ -20,6 +20,7 @@
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeup.h>
#include <linux/fsl_devices.h>
#include <linux/delay.h>
#include <linux/io.h>
@@ -31,10 +32,11 @@
#include "regs-anadig.h"
#include "usb.h"
-static int usbotg2_init_ext(struct platform_device *pdev);
-static void usbotg2_uninit_ext(struct platform_device *pdev);
+static int usbotg_init_ext(struct platform_device *pdev);
+static void usbotg_uninit_ext(struct platform_device *pdev);
static void usbotg_clock_gate(bool on);
static void _dr_discharge_line(bool enable);
+static void usbotg_wakeup_event_clear(void);
static struct clk *usb_phy1_clk;
static u8 otg2_used;
@@ -47,9 +49,9 @@ static u8 otg2_used;
* - operating_mode plugged at run time
*/
static struct fsl_usb2_platform_data dr_utmi_config = {
- .name = "DR",
- .init = usbotg2_init_ext,
- .exit = usbotg2_uninit_ext,
+ .name = "DR2",
+ .init = usbotg_init_ext,
+ .exit = usbotg_uninit_ext,
.phy_mode = FSL_USB2_PHY_UTMI_WIDE,
.power_budget = 500, /* 500 mA max power */
.usb_clock_for_pm = usbotg_clock_gate,
@@ -58,6 +60,13 @@ static struct fsl_usb2_platform_data dr_utmi_config = {
.dr_discharge_line = _dr_discharge_line,
};
+/* Platform data for wakeup operation */
+static struct fsl_usb2_wakeup_platform_data dr_wakeup_config = {
+ .name = "Host wakeup",
+ .usb_clock_for_pm = usbotg_clock_gate,
+ .usb_wakeup_exhandle = usbotg_wakeup_event_clear,
+};
+
static void usbotg_internal_phy_clock_gate(bool on)
{
void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY1_BASE_ADDR);
@@ -111,7 +120,7 @@ static int usb_phy_enable(struct fsl_usb2_platform_data *pdata)
return 0;
}
/* Notes: configure USB clock*/
-static int usbotg2_init_ext(struct platform_device *pdev)
+static int usbotg_init_ext(struct platform_device *pdev)
{
struct clk *usb_clk;
u32 ret;
@@ -139,7 +148,7 @@ static int usbotg2_init_ext(struct platform_device *pdev)
return ret;
}
-static void usbotg2_uninit_ext(struct platform_device *pdev)
+static void usbotg_uninit_ext(struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
@@ -181,6 +190,113 @@ static void _dr_discharge_line(bool enable)
/* Below two macros are used at otg mode to indicate usb mode*/
#define ENABLED_BY_HOST (0x1 << 0)
#define ENABLED_BY_DEVICE (0x1 << 1)
+static void enter_phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata,
+ bool enable)
+{
+ void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY1_BASE_ADDR);
+ u32 tmp;
+ pr_debug("DR: %s begins, enable is %d\n", __func__, enable);
+
+ if (enable) {
+ UOG2_PORTSC1 |= PORTSC_PHCD;
+ 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_SET);
+ usbotg_internal_phy_clock_gate(false);
+
+ } else {
+ if (UOG2_PORTSC1 & PORTSC_PHCD) {
+ UOG2_PORTSC1 &= ~PORTSC_PHCD;
+ mdelay(1);
+ }
+ usbotg_internal_phy_clock_gate(true);
+ 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);
+
+ }
+ pr_debug("DR: %s ends, enable is %d\n", __func__, enable);
+}
+
+static void __phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata,
+ bool enable, int source)
+{
+ if (enable)
+ enter_phy_lowpower_suspend(pdata, enable);
+ else {
+ pr_debug("phy lowpower disable\n");
+ enter_phy_lowpower_suspend(pdata, enable);
+ }
+}
+
+static void otg_wake_up_enable(struct fsl_usb2_platform_data *pdata,
+ bool enable)
+{
+ void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY1_BASE_ADDR);
+
+ pr_debug("%s, enable is %d\n", __func__, enable);
+ if (enable) {
+ __raw_writel(BM_USBPHY_CTRL_ENIDCHG_WKUP
+ | BM_USBPHY_CTRL_ENVBUSCHG_WKUP
+ | BM_USBPHY_CTRL_ENDPDMCHG_WKUP
+ | BM_USBPHY_CTRL_ENAUTOSET_USBCLKS
+ | BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD
+ | BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE
+ | BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE
+ | BM_USBPHY_CTRL_ENAUTO_PWRON_PLL,
+ phy_reg + HW_USBPHY_CTRL_SET);
+ USBC1_CTRL |= UCTRL_OWIE;
+ } else {
+ USBC1_CTRL &= ~UCTRL_OWIE;
+ /* The interrupt must be disabled for at least 3 clock
+ * cycles of the standby clock(32k Hz) , that is 0.094 ms*/
+ udelay(100);
+ }
+}
+
+static void __wakeup_irq_enable(struct fsl_usb2_platform_data *pdata,
+ bool on, int source)
+{
+ pr_debug("USB Host %s,on=%d\n", __func__, on);
+ mdelay(3);
+ if (on) {
+ otg_wake_up_enable(pdata, on);
+ } else {
+ otg_wake_up_enable(pdata, on);
+ /* The interrupt must be disabled for at least 3 clock
+ * cycles of the standby clock(32k Hz) , that is 0.094 ms*/
+ udelay(100);
+ }
+}
+
+/* The wakeup operation for DR port, it will clear the wakeup irq status
+ * and re-enable the wakeup
+ */
+static void usbotg_wakeup_event_clear(void)
+{
+ int wakeup_req = USBC1_CTRL & UCTRL_OWIR;
+
+ if (wakeup_req != 0) {
+ pr_debug("Unknown wakeup.(OTGSC 0x%x)\n", UOG2_OTGSC);
+ /* Disable OWIE to clear OWIR, wait 3 clock
+ * cycles of standly clock(32KHz)
+ */
+ USBC1_CTRL &= ~UCTRL_OWIE;
+ udelay(100);
+ USBC1_CTRL |= UCTRL_OWIE;
+ }
+}
+
/* End of Common operation for DR port */
#ifdef CONFIG_USB_EHCI_ARC_OTG
@@ -215,21 +331,84 @@ static void _host_platform_resume(struct fsl_usb2_platform_data *pdata)
__raw_writel(tmp, phy_reg + HW_USBPHY_PWD_CLR);
}
+static void _host_phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata,
+ bool enable)
+{
+ __phy_lowpower_suspend(pdata, enable, ENABLED_BY_HOST);
+}
+
+static void _host_wakeup_enable(struct fsl_usb2_platform_data *pdata,
+ bool enable)
+{
+ __wakeup_irq_enable(pdata, enable, ENABLED_BY_HOST);
+
+ if (enable)
+ device_wakeup_enable(&pdata->pdev->dev);
+ else
+ device_wakeup_disable(&pdata->pdev->dev);
+}
+
+static enum usb_wakeup_event
+_is_host_wakeup(struct fsl_usb2_platform_data *pdata)
+{
+ u32 wakeup_req = USBC1_CTRL & UCTRL_OWIR;
+ u32 otgsc = UOG2_OTGSC;
+
+ if (wakeup_req) {
+ pr_debug("the otgsc is 0x%x, usbsts is 0x%x,"
+ " portsc is 0x%x, wakeup_irq is 0x%x\n",
+ UOG2_OTGSC, UOG2_USBSTS, UOG2_PORTSC1, wakeup_req);
+ }
+ /* if ID change sts, it is a host wakeup event */
+ if (wakeup_req && (otgsc & OTGSC_IS_USB_ID)) {
+ pr_debug("otg host ID wakeup\n");
+ /* if host ID wakeup, we must clear the b session change sts */
+ otgsc &= (~OTGSC_IS_USB_ID);
+ return WAKEUP_EVENT_ID;
+ }
+ if (wakeup_req && (!(otgsc & OTGSC_STS_USB_ID))) {
+ pr_debug("otg host Remote wakeup\n");
+ return WAKEUP_EVENT_DPDM;
+ }
+ return WAKEUP_EVENT_INVALID;
+}
+
+static void host_wakeup_handler(struct fsl_usb2_platform_data *pdata)
+{
+ _host_phy_lowpower_suspend(pdata, false);
+ _host_wakeup_enable(pdata, false);
+}
+
/* End of host related operation for DR port */
#endif /* CONFIG_USB_EHCI_ARC_OTG */
void __init mvf_usb_dr2_init(void)
{
- struct platform_device *pdev;
+ struct platform_device *pdev, *pdev_wakeup;
u32 reg;
#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.phy_lowpower_suspend = _host_phy_lowpower_suspend;
+ dr_utmi_config.is_wakeup_event = _is_host_wakeup;
+ dr_utmi_config.wakeup_pdata = &dr_wakeup_config;
+ dr_utmi_config.wakeup_handler = host_wakeup_handler;
+
+ pdev = mvf_add_fsl_ehci_otg(1, &dr_utmi_config);
+
+ dr_wakeup_config.usb_pdata[1] = pdev->dev.platform_data;
- pdev = mvf_add_fsl_ehci_otg(&dr_utmi_config);
+ /* register wakeup device */
+ pdev_wakeup = mvf_add_fsl_usb2_ehci_otg_wakeup(1, &dr_wakeup_config);
+ if (pdev != NULL)
+ ((struct fsl_usb2_platform_data *)
+ (pdev->dev.platform_data))->wakeup_pdata =
+ (struct fsl_usb2_wakeup_platform_data *)
+ (pdev_wakeup->dev.platform_data);
__raw_writel(0x0220C802, USBPHY2_CTRL);
udelay(20);
@@ -267,7 +446,7 @@ void __init mvf_usb_dr2_init(void)
}
/* USB HIGH_SPEED disconnect detect on/off */
-void fsl_platform_set_usb_phy_dis(struct fsl_usb2_platform_data *pdata,
+void fsl_platform_set_usb1_phy_dis(struct fsl_usb2_platform_data *pdata,
bool enable)
{
u32 reg;