summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorJun Li <r65092@freescale.com>2009-12-01 21:16:33 +0800
committerJun Li <r65092@freescale.com>2009-12-03 18:18:36 +0800
commit898a2a3fc5527276e96468058c371bb7604930c0 (patch)
tree3b1ae8eb3061e7f3b278f7dc9f17b16d4d33f001 /drivers/usb
parent9a3dd2536ccf1bb0aa5610b18a897d687d2ef3ac (diff)
ENGR00117147-2 USB clock gating and PHY low power mode.
1. If there is no usb devices connectted or all connectted usb devices are in suspend state, usb host can suspend its whole bus, then put the PHY into low power mode and close all usb clocks. 2. close all usb clocks for usb device low power mode. (The patch is splitted 2 patches, this is FSL specific driver part.) Signed-off-by: Li Jun <r65092@freescale.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/gadget/arcotg_udc.c77
-rw-r--r--drivers/usb/host/ehci-arc.c103
2 files changed, 107 insertions, 73 deletions
diff --git a/drivers/usb/gadget/arcotg_udc.c b/drivers/usb/gadget/arcotg_udc.c
index acc1b9508da9..7772c39a1044 100644
--- a/drivers/usb/gadget/arcotg_udc.c
+++ b/drivers/usb/gadget/arcotg_udc.c
@@ -109,7 +109,7 @@ 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 (device_may_wakeup(udc_controller->gadget.dev.parent)) {
if (pdata->wake_up_enable)
pdata->wake_up_enable(pdata, enable);
}
@@ -272,19 +272,21 @@ static void dr_phy_low_power_mode(struct fsl_udc *udc, bool enable)
{
u32 temp;
- if (!device_can_wakeup(udc_controller->gadget.dev.parent))
+ if (!device_may_wakeup(udc_controller->gadget.dev.parent))
return;
- temp = fsl_readl(&dr_regs->portsc1);
- if ((enable) && !(temp & PORTSCX_PHY_LOW_POWER_SPD)) {
+
+ if (enable) {
+ temp = fsl_readl(&dr_regs->portsc1);
temp |= PORTSCX_PHY_LOW_POWER_SPD;
fsl_writel(temp, &dr_regs->portsc1);
if (udc_controller->pdata->usb_clock_for_pm)
udc_controller->pdata->usb_clock_for_pm(false);
- } else if ((!enable) && (temp & PORTSCX_PHY_LOW_POWER_SPD)) {
+ } else {
if (udc_controller->pdata->usb_clock_for_pm)
udc_controller->pdata->usb_clock_for_pm(true);
+ temp = fsl_readl(&dr_regs->portsc1);
temp &= ~PORTSCX_PHY_LOW_POWER_SPD;
fsl_writel(temp, &dr_regs->portsc1);
}
@@ -411,11 +413,7 @@ static void dr_controller_run(struct fsl_udc *udc)
fsl_writel(temp, &dr_regs->usbintr);
- /* If PHY clock is disabled, enable it */
- if (udc_controller->pdata->usb_clock_for_pm)
- udc_controller->pdata->usb_clock_for_pm(1);
-
- if (device_can_wakeup(udc_controller->gadget.dev.parent)) {
+ if (device_may_wakeup(udc_controller->gadget.dev.parent)) {
/* enable BSV irq */
temp = fsl_readl(&dr_regs->otgsc);
temp |= OTGSC_B_SESSION_VALID_IRQ_EN;
@@ -424,7 +422,7 @@ static void dr_controller_run(struct fsl_udc *udc)
/* 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)) {
+ && device_may_wakeup(udc_controller->gadget.dev.parent)) {
/* enable wake up */
dr_wake_up_enable(udc, true);
/* Set stopped before low power mode */
@@ -1939,8 +1937,7 @@ static void wake_up_irq(struct fsl_udc *udc)
/* disable wake up irq */
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;
}
@@ -2030,16 +2027,21 @@ bool try_wake_up_udc(struct fsl_udc *udc)
/* when udc is stopped, only handle wake up irq */
if (udc->stopped) {
- if (!device_can_wakeup(&(udc->pdata->pdev->dev)))
+ if (!device_may_wakeup(&(udc->pdata->pdev->dev)))
return false;
+
+ dr_phy_low_power_mode(udc_controller, 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);
+ } else {
+ dr_phy_low_power_mode(udc_controller, true);
}
}
- if (!device_can_wakeup(udc_controller->gadget.dev.parent))
+ if (!device_may_wakeup(udc_controller->gadget.dev.parent))
return true;
/* check if Vbus change irq */
@@ -2151,6 +2153,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
int retval = -ENODEV;
unsigned long flags = 0;
+ u32 portsc;
if (!udc_controller)
return -ENODEV;
@@ -2173,7 +2176,13 @@ 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);
+ portsc = fsl_readl(&dr_regs->portsc1);
+ portsc &= ~PORTSCX_PHY_LOW_POWER_SPD;
+ fsl_writel(portsc, &dr_regs->portsc1);
+
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(true);
+
/* bind udc driver to gadget driver */
retval = driver->bind(&udc_controller->gadget);
if (retval) {
@@ -2227,6 +2236,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct fsl_ep *loop_ep;
unsigned long flags;
+ u32 portsc;
if (!udc_controller)
return -ENODEV;
@@ -2237,11 +2247,12 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
if (udc_controller->transceiver)
(void)otg_set_peripheral(udc_controller->transceiver, 0);
- /* stop DR, disable intr */
- dr_controller_stop(udc_controller);
/* open phy clock for following operation */
dr_phy_low_power_mode(udc_controller, false);
+ /* stop DR, disable intr */
+ dr_controller_stop(udc_controller);
+
/* in fact, no needed */
udc_controller->usb_state = USB_STATE_ATTACHED;
udc_controller->ep0_dir = 0;
@@ -2264,7 +2275,13 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
udc_controller->driver = 0;
dr_wake_up_enable(udc_controller, false);
- dr_phy_low_power_mode(udc_controller, true);
+
+ portsc = fsl_readl(&dr_regs->portsc1);
+ portsc |= PORTSCX_PHY_LOW_POWER_SPD;
+ fsl_writel(portsc, &dr_regs->portsc1);
+
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(false);
printk(KERN_INFO "unregistered gadget driver '%s'\r\n",
driver->driver.name);
@@ -2662,7 +2679,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
int ret = -ENODEV;
unsigned int i;
- u32 dccparams;
+ u32 dccparams, portsc;
if (strcmp(pdev->name, driver_name)) {
VDBG("Wrong device\n");
@@ -2840,7 +2857,13 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
dr_wake_up_enable(udc_controller, false);
udc_controller->stopped = 1;
- dr_phy_low_power_mode(udc_controller, true);
+
+ portsc = fsl_readl(&dr_regs->portsc1);
+ portsc |= PORTSCX_PHY_LOW_POWER_SPD;
+ fsl_writel(portsc, &dr_regs->portsc1);
+
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(false);
create_proc_file();
return 0;
@@ -2920,6 +2943,10 @@ static int udc_suspend(struct fsl_udc *udc)
{
u32 mode, usbcmd;
+ /* open clock for register access */
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(true);
+
mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK;
usbcmd = fsl_readl(&dr_regs->usbcmd);
@@ -2933,12 +2960,12 @@ static int udc_suspend(struct fsl_udc *udc)
if (udc->stopped) {
pr_debug("gadget already stopped, leaving early\n");
udc->already_stopped = 1;
- return 0;
+ goto out;
}
if (mode != USB_MODE_CTRL_MODE_DEVICE) {
pr_debug("gadget not in device mode, leaving early\n");
- return 0;
+ goto out;
}
udc->stopped = 1;
@@ -2954,7 +2981,9 @@ static int udc_suspend(struct fsl_udc *udc)
fsl_writel(usbcmd, &dr_regs->usbcmd);
printk(KERN_INFO "USB Gadget suspended\n");
-
+out:
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(false);
return 0;
}
diff --git a/drivers/usb/host/ehci-arc.c b/drivers/usb/host/ehci-arc.c
index 57aa1a4fd64d..f05e97a57d58 100644
--- a/drivers/usb/host/ehci-arc.c
+++ b/drivers/usb/host/ehci-arc.c
@@ -269,6 +269,23 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ u32 tmp;
+
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ /* Need open clock for register access */
+ if (pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(true);
+
+ tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ if (tmp & PORT_PHCD) {
+ tmp &= ~PORT_PHCD;
+ ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
+ msleep(100);
+
+ if (pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(false);
+ }
+ }
/* DDD shouldn't we turn off the power here? */
fsl_platform_set_vbus_power(pdata, 0);
@@ -446,6 +463,12 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev,
u32 tmp, port_status;
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ if (device_may_wakeup(&(pdev->dev))) {
+ /* Need open clock for register access */
+ if (pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(true);
+ }
+
#ifdef DEBUG
u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE);
mode &= USBMODE_CM_MASK;
@@ -464,7 +487,7 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev,
if (pdata->suspended) {
pr_debug("%s: already suspended, leaving early\n", __func__);
pdata->already_suspended = 1;
- return 0;
+ goto err1;
}
pr_debug("%s: suspending...\n", __func__);
@@ -472,17 +495,11 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev,
printk(KERN_INFO "USB Host suspended\n");
port_status = ehci_readl(ehci, &ehci->regs->port_status[0]);
- hcd->state = HC_STATE_SUSPENDED;
pdev->dev.power.power_state = PMSG_SUSPEND;
/* ignore non-host interrupts */
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- /* stop the controller */
- tmp = ehci_readl(ehci, &ehci->regs->command);
- tmp &= ~CMD_RUN;
- ehci_writel(ehci, tmp, &ehci->regs->command);
-
/* save EHCI registers */
pdata->pm_command = ehci_readl(ehci, &ehci->regs->command);
pdata->pm_command &= ~CMD_RUN;
@@ -499,6 +516,9 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev,
/* clear the W1C bits */
pdata->pm_portsc &= cpu_to_hc32(ehci, ~PORT_RWC_BITS);
+ /* clear PHCD bit */
+ pdata->pm_portsc &= ~PORT_PHCD;
+
pdata->suspended = 1;
if (!device_may_wakeup(&(pdev->dev))) {
@@ -506,45 +526,18 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev,
tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
tmp &= ~PORT_POWER;
ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
- return 0;
- }
-
- /* device_may_wakeup */
- if (!((ehci->transceiver) &&
- (readl(hcd->regs + 0x1A4) & (1 << 8)))) {
- /* enable remote wake up irq */
- if (pdata->wake_up_enable)
- pdata->wake_up_enable(pdata, true);
-
- /* We CAN NOT enable wake up by connetion and disconnection
- * concurrently */
- tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
- /* if there is no usb device connectted */
- if (port_status & PORT_CONNECT) {
- /* enable wake up by usb device disconnection */
- tmp |= PORT_WKDISC_E;
- tmp &= ~(PORT_WKOC_E | PORT_WKCONN_E);
- } else {
- /* enable wake up by usb device insertion */
- tmp |= PORT_WKCONN_E;
- tmp &= ~(PORT_WKOC_E | PORT_WKDISC_E);
+ goto err1;
}
- ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
-
- /* Set the port into suspend */
- tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
- tmp |= PORT_SUSPEND;
- ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
- /* Disable PHY clock */
tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
- tmp |= (1 << 23);
- ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
- }
if (pdata->platform_suspend)
pdata->platform_suspend(pdata);
-
+err1:
+ if (device_may_wakeup(&(pdev->dev))) {
+ if (pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(false);
+ }
return 0;
}
@@ -573,15 +566,16 @@ static int ehci_fsl_drv_resume(struct platform_device *pdev)
return 0;
}
+ /* If hcd is resumed by non-usb wakeup events,
+ * then usb clocks are still not open when come here */
if (device_may_wakeup(&(pdev->dev))) {
- tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
- if (tmp & (1 << 23)) {
- tmp &= ~(1 << 23);
- ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
- msleep(10);
- }
+ /* Need open clock for register access */
+ if (pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(true);
}
+ tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
+
pdata->suspended = 0;
pr_debug("%s resuming...\n", __func__);
@@ -593,6 +587,7 @@ static int ehci_fsl_drv_resume(struct platform_device *pdev)
pdata->platform_resume(pdata);
/* restore EHCI registers */
+ ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]);
ehci_writel(ehci, pdata->pm_command, &ehci->regs->command);
ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable);
ehci_writel(ehci, pdata->pm_frame_index, &ehci->regs->frame_index);
@@ -601,10 +596,14 @@ static int ehci_fsl_drv_resume(struct platform_device *pdev)
ehci_writel(ehci, pdata->pm_async_next, &ehci->regs->async_next);
ehci_writel(ehci, pdata->pm_configured_flag,
&ehci->regs->configured_flag);
- ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]);
- set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- hcd->state = HC_STATE_RUNNING;
+ /* set bit should be done by wakeup irq routine if may wakeup */
+ if (!device_may_wakeup(&(pdev->dev)))
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ else
+ while (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ msleep(1);
+
pdev->dev.power.power_state = PMSG_ON;
tmp = ehci_readl(ehci, &ehci->regs->command);
@@ -614,6 +613,12 @@ static int ehci_fsl_drv_resume(struct platform_device *pdev)
usb_hcd_resume_root_hub(hcd);
printk(KERN_INFO "USB Host resumed\n");
+
+ if (device_may_wakeup(&(pdev->dev))) {
+ if (pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(false);
+ }
+
return 0;
}
#endif /* CONFIG_USB_OTG */