summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAlbert Chen <R65187@freescale.com>2009-10-09 16:44:20 +0800
committerAlbert Chen <R65187@freescale.com>2009-10-10 09:44:14 +0800
commit8635334b08952f242aba2e7cf916a47b117f4169 (patch)
treedbba7f1ab2bfce6db5bedd8c7a68d9c5dfb04d26 /drivers
parente247e219bdc546d0b130b928570d1af8543e064e (diff)
ENGR00115875 add USB gadget low power mode feature.
USB will enter low power mode if no vbus power supply. Only finished this feature in Mx37. 1. When enter low power mode, driver will close usb related clocks as possible as could. set usb IP to stop. Set PHY to low power suspend by setting PHCD bit of portsc. 2. This patch doesn't support low power mode in OTG mode yet. Signed-off-by: Albert Chen <r65187@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/gadget/arcotg_udc.c184
-rw-r--r--drivers/usb/gadget/arcotg_udc.h5
2 files changed, 122 insertions, 67 deletions
diff --git a/drivers/usb/gadget/arcotg_udc.c b/drivers/usb/gadget/arcotg_udc.c
index 7c7af5dfcec0..4ae2475a319f 100644
--- a/drivers/usb/gadget/arcotg_udc.c
+++ b/drivers/usb/gadget/arcotg_udc.c
@@ -103,6 +103,18 @@ extern struct resource *otg_get_resources(void);
extern void fsl_platform_set_test_mode(struct fsl_usb2_platform_data *pdata, enum usb_test_mode mode);
+static inline void
+dr_wake_up_enable(struct fsl_udc *udc, bool enable)
+{
+ struct fsl_usb2_platform_data *pdata;
+ pdata = udc->pdata;
+
+ if (device_can_wakeup(udc_controller->gadget.dev.parent)) {
+ if (pdata->wake_up_enable)
+ pdata->wake_up_enable(pdata, enable);
+ }
+}
+
#ifdef CONFIG_PPC32
#define fsl_readl(addr) in_le32((addr))
#define fsl_writel(addr, val32) out_le32((val32), (addr))
@@ -260,9 +272,8 @@ static void dr_phy_low_power_mode(struct fsl_udc *udc, bool enable)
{
u32 temp;
- if (!device_may_wakeup(&(udc->pdata->pdev->dev)))
+ if (!device_can_wakeup(udc_controller->gadget.dev.parent))
return;
-
temp = fsl_readl(&dr_regs->portsc1);
if ((enable) && !(temp & PORTSCX_PHY_LOW_POWER_SPD)) {
temp |= PORTSCX_PHY_LOW_POWER_SPD;
@@ -279,44 +290,7 @@ static void dr_phy_low_power_mode(struct fsl_udc *udc, bool enable)
}
}
-static void dr_wake_up_enable(struct fsl_udc *udc, bool enable)
-{
- u32 temp;
-
- temp = fsl_readl(&dr_regs->usbctrl);
-
- if (!enable) {
- temp &= ~USB_CTRL_OTG_WUIE;
- fsl_writel(temp, &dr_regs->usbctrl);
-#if CONFIG_ARCH_MX51
- /* OTG vbus Wakeup disable */
- temp = fsl_readl(&dr_regs->uh2ctrl);
- temp &= ~USB_UH2_OVBWK_EN;
- fsl_writel(temp, &dr_regs->uh2ctrl);
-
- /* disable conf2 */
- temp = fsl_readl(&dr_regs->phyctrl0);
- temp &= ~PHY_CTRL0_CONF2;
- fsl_writel(temp, &dr_regs->phyctrl0);
-#endif
- } else if (device_may_wakeup(&(udc->pdata->pdev->dev))) {
- temp |= USB_CTRL_OTG_WUIE;
- fsl_writel(temp, &dr_regs->usbctrl);
-
-#if CONFIG_ARCH_MX51
- /* OTG vbus wakeup enable */
- temp = fsl_readl(&dr_regs->uh2ctrl);
- temp |= USB_UH2_OVBWK_EN;
- fsl_writel(temp, &dr_regs->uh2ctrl);
-
- /* enable conf2 */
- temp = fsl_readl(&dr_regs->phyctrl0);
- temp |= PHY_CTRL0_CONF2;
- fsl_writel(temp, &dr_regs->phyctrl0);
-#endif
- }
-}
static int dr_controller_setup(struct fsl_udc *udc)
{
@@ -439,14 +413,41 @@ static void dr_controller_run(struct fsl_udc *udc)
fsl_writel(temp, &dr_regs->usbintr);
- /* Clear stopped bit */
- udc->stopped = 0;
+ /* If PHY clock is disabled, enable it */
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(1);
- /* Set controller to Run */
- if (udc->driver) {
+ if (device_can_wakeup(udc_controller->gadget.dev.parent)) {
+ /* enable BSV irq */
+ temp = fsl_readl(&dr_regs->otgsc);
+ temp |= OTGSC_B_SESSION_VALID_IRQ_EN;
+ fsl_writel(temp, &dr_regs->otgsc);
+ }
+
+ /* If vbus not on and used low power mode */
+ if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_B_SESSION_VALID)
+ && device_can_wakeup(udc_controller->gadget.dev.parent)) {
+ /* enable wake up */
+ dr_wake_up_enable(udc, true);
+ /* Set stopped before low power mode */
+ udc->stopped = 1;
+ /* close PHY clock */
+ dr_phy_low_power_mode(udc, true);
+ printk(KERN_INFO "udc enter low power mode \n");
+ } else {
+ /*
+ add some delay for USB timing issue. USB may be
+ recognize as FS device
+ during USB gadget remote wake up function
+ */
+ mdelay(100);
+ /* Clear stopped bit */
+ udc->stopped = 0;
+ /* Set controller to Run */
temp = fsl_readl(&dr_regs->usbcmd);
temp |= USB_CMD_RUN_STOP;
fsl_writel(temp, &dr_regs->usbcmd);
+ printk(KERN_INFO "udc run \n");
}
return;
@@ -472,6 +473,13 @@ static void dr_controller_stop(struct fsl_udc *udc)
/* disable all INTR */
fsl_writel(0, &dr_regs->usbintr);
+ /* disable wake up */
+ dr_wake_up_enable(udc, false);
+ /* disable BSV irq */
+ tmp = fsl_readl(&dr_regs->otgsc);
+ tmp &= ~OTGSC_B_SESSION_VALID_IRQ_EN;
+ fsl_writel(tmp, &dr_regs->otgsc);
+
/* Set stopped bit for isr */
udc->stopped = 1;
@@ -1932,7 +1940,10 @@ static void wake_up_irq(struct fsl_udc *udc)
pr_debug("%s\n", __func__);
/* disable wake up irq */
- dr_wake_up_enable(udc, false);
+ dr_wake_up_enable(udc_controller, false);
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(true);
+ udc->stopped = 0;
}
static void bus_resume(struct fsl_udc *udc)
@@ -2014,41 +2025,64 @@ static void reset_irq(struct fsl_udc *udc)
}
}
-/*
- * USB device controller interrupt handler
- */
-static irqreturn_t fsl_udc_irq(int irq, void *_udc)
+/* if wakup udc, return true; else return false*/
+bool try_wake_up_udc(struct fsl_udc *udc)
{
- struct fsl_udc *udc = _udc;
u32 irq_src;
- irqreturn_t status = IRQ_NONE;
- unsigned long flags;
/* when udc is stopped, only handle wake up irq */
if (udc->stopped) {
- if (!device_may_wakeup(&(udc->pdata->pdev->dev)))
- return IRQ_NONE;
-
- spin_lock_irqsave(&udc->lock, flags);
+ if (!device_can_wakeup(&(udc->pdata->pdev->dev)))
+ return false;
/* check to see if wake up irq */
irq_src = fsl_readl(&dr_regs->usbctrl);
if (irq_src & USB_CTRL_OTG_WUIR) {
wake_up_irq(udc);
- irq_src = fsl_readl(&dr_regs->usbsts) &
- fsl_readl(&dr_regs->usbintr);
- spin_unlock_irqrestore(&udc->lock, flags);
- if (irq_src)
- /* Some udc irq to be handled */
- udc->stopped = 0;
- else
- return IRQ_HANDLED;
+ }
+ }
+
+ if (!device_can_wakeup(udc_controller->gadget.dev.parent))
+ return true;
+
+ /* check if Vbus change irq */
+ irq_src = fsl_readl(&dr_regs->otgsc);
+ if (irq_src & OTGSC_B_SESSION_VALID_IRQ_STS) {
+ u32 tmp;
+ fsl_writel(irq_src, &dr_regs->otgsc);
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ /* check BSV bit to see if fall or rise */
+ if (irq_src & OTGSC_B_SESSION_VALID) {
+ udc->stopped = 0;
+ fsl_writel(tmp | USB_CMD_RUN_STOP, &dr_regs->usbcmd);
+ printk(KERN_INFO "udc out low power mode\n");
} else {
- /* If udc is stopped and irq is not wake up */
- spin_unlock_irqrestore(&udc->lock, flags);
- return IRQ_NONE;
+ printk(KERN_INFO "udc enter low power mode \n");
+ fsl_writel(tmp & ~USB_CMD_RUN_STOP, &dr_regs->usbcmd);
+ /* enable wake up */
+ dr_wake_up_enable(udc, true);
+ udc->stopped = 1;
+ /* close USB PHY clock */
+ dr_phy_low_power_mode(udc, true);
+ return false;
}
}
+ return true;
+}
+
+/*
+ * USB device controller interrupt handler
+ */
+static irqreturn_t fsl_udc_irq(int irq, void *_udc)
+{
+ struct fsl_udc *udc = _udc;
+ u32 irq_src;
+ irqreturn_t status = IRQ_NONE;
+ unsigned long flags;
+
+ if (try_wake_up_udc(udc) == false)
+ return IRQ_NONE;
+
spin_lock_irqsave(&udc->lock, flags);
irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr);
/* Clear notification bits */
@@ -2141,6 +2175,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
udc_controller->gadget.dev.driver = &driver->driver;
spin_unlock_irqrestore(&udc_controller->lock, flags);
+ dr_phy_low_power_mode(udc_controller, false);
/* bind udc driver to gadget driver */
retval = driver->bind(&udc_controller->gadget);
if (retval) {
@@ -2206,6 +2241,8 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
/* stop DR, disable intr */
dr_controller_stop(udc_controller);
+ /* open phy clock for following operation */
+ dr_phy_low_power_mode(udc_controller, false);
/* in fact, no needed */
udc_controller->usb_state = USB_STATE_ATTACHED;
@@ -2228,6 +2265,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
udc_controller->gadget.dev.driver = 0;
udc_controller->driver = 0;
+ dr_wake_up_enable(udc_controller, false);
+ dr_phy_low_power_mode(udc_controller, true);
+
printk(KERN_INFO "unregistered gadget driver '%s'\r\n",
driver->driver.name);
return 0;
@@ -2758,8 +2798,11 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
if (ret < 0)
goto err3;
- if (udc_controller->transceiver)
+ if (udc_controller->transceiver) {
udc_controller->gadget.is_otg = 1;
+ /* now didn't support lpm in OTG mode*/
+ device_set_wakeup_capable(&pdev->dev, 0);
+ }
/* setup QH and epctrl for ep0 */
ep0_setup(udc_controller);
@@ -2802,6 +2845,11 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
#ifdef POSTPONE_FREE_LAST_DTD
last_free_td = NULL;
#endif
+
+ dr_wake_up_enable(udc_controller, false);
+ udc_controller->stopped = 1;
+ dr_phy_low_power_mode(udc_controller, true);
+
create_proc_file();
return 0;
@@ -2837,6 +2885,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
if (!udc_controller)
return -ENODEV;
udc_controller->done = &done;
+ /* open USB PHY clock */
+ dr_phy_low_power_mode(udc_controller, false);
/* DR has been stopped in usb_gadget_unregister_driver() */
remove_proc_file();
diff --git a/drivers/usb/gadget/arcotg_udc.h b/drivers/usb/gadget/arcotg_udc.h
index e7cca079e184..3d13b9af6155 100644
--- a/drivers/usb/gadget/arcotg_udc.h
+++ b/drivers/usb/gadget/arcotg_udc.h
@@ -262,6 +262,11 @@ struct usb_sys_interface {
#define PORTSCX_PORT_SPEED_UNDEF (0x0C000000)
#define PORTSCX_SPEED_BIT_POS (26)
+/* OTGSC Register Bit Masks */
+#define OTGSC_B_SESSION_VALID_IRQ_EN (1 << 27)
+#define OTGSC_B_SESSION_VALID_IRQ_STS (1 << 19)
+#define OTGSC_B_SESSION_VALID (1 << 11)
+
/* bit 28 is parallel transceiver width for UTMI interface */
#define PORTSCX_PTW (0x10000000)
#define PORTSCX_PTW_8BIT (0x00000000)