summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/gadget/arcotg_udc.c370
-rw-r--r--drivers/usb/gadget/arcotg_udc.h13
-rw-r--r--drivers/usb/host/ehci-arc.c381
-rw-r--r--drivers/usb/otg/fsl_otg.c199
-rw-r--r--include/linux/fsl_devices.h1
5 files changed, 575 insertions, 389 deletions
diff --git a/drivers/usb/gadget/arcotg_udc.c b/drivers/usb/gadget/arcotg_udc.c
index 161557f4b2a9..0eee633d9323 100644
--- a/drivers/usb/gadget/arcotg_udc.c
+++ b/drivers/usb/gadget/arcotg_udc.c
@@ -28,6 +28,7 @@
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
+#include <linux/jiffies.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/usb/ch9.h>
@@ -38,6 +39,7 @@
#include <linux/fsl_devices.h>
#include <linux/dmapool.h>
+#include <asm/processor.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -67,7 +69,7 @@
#endif
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
-
+extern void usb_debounce_id_pin(void);
static const char driver_name[] = "fsl-usb2-udc";
static const char driver_desc[] = DRIVER_DESC;
@@ -106,16 +108,6 @@ 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 (pdata->wake_up_enable)
- pdata->wake_up_enable(pdata, enable);
-}
-
#ifdef CONFIG_WORKAROUND_ARCUSB_REG_RW
static void safe_writel(u32 val32, void *addr)
{
@@ -280,9 +272,12 @@ static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
static void nuke(struct fsl_ep *ep, int status)
{
ep->stopped = 1;
-
- /* Flush fifo */
- fsl_ep_fifo_flush(&ep->ep);
+ /*
+ * At udc stop mode, the clock is already off
+ * So flush fifo, should be done at clock on mode.
+ */
+ if (!ep->udc->stopped)
+ fsl_ep_fifo_flush(&ep->ep);
/* Whether this eq has request linked */
while (!list_empty(&ep->queue)) {
@@ -297,28 +292,85 @@ static void nuke(struct fsl_ep *ep, int status)
/*------------------------------------------------------------------
Internal Hardware related function
------------------------------------------------------------------*/
+static inline void
+dr_wake_up_enable(struct fsl_udc *udc, bool enable)
+{
+ struct fsl_usb2_platform_data *pdata;
+ pdata = udc->pdata;
+
+ if (pdata && pdata->wake_up_enable)
+ pdata->wake_up_enable(pdata, enable);
+}
+
+static bool clk_stopped;
+static inline void dr_clk_gate(bool on)
+{
+ struct fsl_usb2_platform_data *pdata = udc_controller->pdata;
+
+ if (!pdata || !pdata->usb_clock_for_pm)
+ return;
+ if (on && clk_stopped) {
+ pdata->usb_clock_for_pm(true);
+ clk_stopped = false;
+ }
+ if (!on && !clk_stopped) {
+ pdata->usb_clock_for_pm(false);
+ clk_stopped = true;
+ }
+ if (on)
+ reset_phy();
+}
static void dr_phy_low_power_mode(struct fsl_udc *udc, bool enable)
{
- u32 temp;
- if (enable) {
- temp = fsl_readl(&dr_regs->portsc1);
- temp |= PORTSCX_PHY_LOW_POWER_SPD;
- fsl_writel(temp, &dr_regs->portsc1);
+ struct fsl_usb2_platform_data *pdata = udc->pdata;
+ u32 portsc;
- if (udc_controller->pdata->usb_clock_for_pm)
- udc_controller->pdata->usb_clock_for_pm(false);
+ if (pdata && pdata->phy_lowpower_suspend) {
+ pdata->phy_lowpower_suspend(enable);
} else {
- if (udc_controller->pdata->usb_clock_for_pm)
- udc_controller->pdata->usb_clock_for_pm(true);
+ if (enable) {
+ portsc = fsl_readl(&dr_regs->portsc1);
+ portsc |= PORTSCX_PHY_LOW_POWER_SPD;
+ fsl_writel(portsc, &dr_regs->portsc1);
+ } else {
+ portsc = fsl_readl(&dr_regs->portsc1);
+ portsc &= ~PORTSCX_PHY_LOW_POWER_SPD;
+ fsl_writel(portsc, &dr_regs->portsc1);
+ }
+ }
+}
- /* Due to mx35/mx25's phy's bug */
- reset_phy();
- temp = fsl_readl(&dr_regs->portsc1);
- temp &= ~PORTSCX_PHY_LOW_POWER_SPD;
- fsl_writel(temp, &dr_regs->portsc1);
- }
+/* workaroud for some boards, maybe there is a large capacitor between the ground and the Vbus
+ * that will cause the vbus dropping very slowly when device is detached,
+ * may cost 2-3 seconds to below 0.8V */
+static void udc_wait_b_session_low(void)
+{
+ u32 temp;
+ u32 wait = 5000/jiffies_to_msecs(1); /* max wait time is 5000 ms */
+ /* if we are in host mode, don't need to care the B session */
+ if ((fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID) == 0)
+ return;
+ /* if the udc is dettached , there will be a suspend irq */
+ if (udc_controller->usb_state != USB_STATE_SUSPENDED)
+ return;
+ temp = fsl_readl(&dr_regs->otgsc);
+ temp &= ~OTGSC_B_SESSION_VALID_IRQ_EN;
+ fsl_writel(temp, &dr_regs->otgsc);
+
+ do {
+ if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_B_SESSION_VALID))
+ break;
+ msleep(jiffies_to_msecs(1));
+ wait -= 1;
+ } while (wait);
+ if (!wait)
+ printk(KERN_ERR "ERROR!!!!!: the vbus can not be lower \
+ then 0.8V for 5 seconds, Pls Check your HW design\n");
+ temp = fsl_readl(&dr_regs->otgsc);
+ temp |= OTGSC_B_SESSION_VALID_IRQ_EN;
+ fsl_writel(temp, &dr_regs->otgsc);
}
static int dr_controller_setup(struct fsl_udc *udc)
@@ -367,7 +419,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
fsl_platform_set_device_mode(pdata);
/* Clear the setup status */
- fsl_writel(0, &dr_regs->usbsts);
+ fsl_writel(0xffffffff, &dr_regs->usbsts);
tmp = udc->ep_qh_dma;
tmp &= USB_EP_LIST_ADDRESS_MASK;
@@ -453,9 +505,9 @@ static void dr_controller_run(struct fsl_udc *udc)
udc->stopped = 1;
/* enable wake up */
dr_wake_up_enable(udc, true);
- /* close PHY clock */
+ /* enter lower power mode */
dr_phy_low_power_mode(udc, true);
- printk(KERN_INFO "%s: udc enter low power mode \n", __func__);
+ printk(KERN_DEBUG "%s: udc enter low power mode \n", __func__);
} else {
#ifdef CONFIG_ARCH_MX37
/*
@@ -472,6 +524,7 @@ static void dr_controller_run(struct fsl_udc *udc)
temp = fsl_readl(&dr_regs->usbcmd);
temp |= USB_CMD_RUN_STOP;
fsl_writel(temp, &dr_regs->usbcmd);
+ printk(KERN_DEBUG "%s: udc out low power mode\n", __func__);
}
return;
@@ -1967,16 +2020,33 @@ static void suspend_irq(struct fsl_udc *udc)
udc->driver->suspend(&udc->gadget);
}
-/* Process Wake up interrupt */
-static void wake_up_irq(struct fsl_udc *udc)
-{
- pr_debug("%s\n", __func__);
-
- /* disable wake up irq */
- dr_wake_up_enable(udc_controller, false);
-
- udc->stopped = 0;
-}
+/* Process Wake up interrupt
+ * Be careful that some boards will use ID pin to control the VBUS on/off
+ * in these case, after the device enter the lowpower mode(clk off,
+ * phy lowpower mode, wakeup enable), then an udisk is attaced to the otg port,
+ * there will be an Vbus wakeup event and then an ID change wakeup, But the Vbus
+ * event is not expected, so there is an workaround that will detect the ID, if ID=0
+ * we just need the ID event so we can not disable the wakeup
+ *
+ * false: host wakeup event
+ * true: device wakeup event
+ */
+ static bool wake_up_irq(struct fsl_udc *udc)
+ {
+ usb_debounce_id_pin();
+ /* if the ID=0, let arc host process the wakeup */
+ if (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID) {
+ dr_wake_up_enable(udc_controller, false);
+ dr_phy_low_power_mode(udc, false);
+ pr_debug("at %s: device wake up event\n", __func__);
+ return true;
+ } else {/* wakeup is vbus wake event, but not for device so we need to clear b session */
+ int irq_src = fsl_readl(&dr_regs->otgsc) & (~OTGSC_ID_CHANGE_IRQ_STS);
+ fsl_writel(irq_src, &dr_regs->otgsc);
+ pr_debug("at %s: he host wakeup event, should be handled by host\n", __func__);
+ return false;
+ }
+ }
static void bus_resume(struct fsl_udc *udc)
{
@@ -2044,17 +2114,16 @@ static void reset_irq(struct fsl_udc *udc)
bool try_wake_up_udc(struct fsl_udc *udc)
{
u32 irq_src;
+ bool b_device;
/* when udc is stopped, only handle wake up irq */
if (udc->stopped) {
- 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 (wake_up_irq(udc) == false) {
+ return false; /* host wakeup event */
+ }
}
}
@@ -2062,21 +2131,29 @@ bool try_wake_up_udc(struct fsl_udc *udc)
irq_src = fsl_readl(&dr_regs->otgsc);
if (irq_src & OTGSC_B_SESSION_VALID_IRQ_STS) {
u32 tmp;
+ usb_debounce_id_pin();
+ b_device = (irq_src & OTGSC_STS_USB_ID) ? true : false;
fsl_writel(irq_src, &dr_regs->otgsc);
+ if (!b_device)
+ return false;
tmp = fsl_readl(&dr_regs->usbcmd);
/* check BSV bit to see if fall or rise */
if (irq_src & OTGSC_B_SESSION_VALID) {
+ if (udc->suspended) /*let the system pm resume the udc */
+ return true;
udc->stopped = 0;
fsl_writel(tmp | USB_CMD_RUN_STOP, &dr_regs->usbcmd);
- printk(KERN_INFO "%s: udc out low power mode\n", __func__);
+ printk(KERN_DEBUG "%s: udc out low power mode\n", __func__);
} else {
- printk(KERN_INFO "%s: udc enter low power mode \n", __func__);
+ if (udc->driver)
+ udc->driver->disconnect(&udc->gadget);
fsl_writel(tmp & ~USB_CMD_RUN_STOP, &dr_regs->usbcmd);
udc->stopped = 1;
/* enable wake up */
dr_wake_up_enable(udc, true);
/* close USB PHY clock */
dr_phy_low_power_mode(udc, true);
+ printk(KERN_DEBUG "%s: udc enter low power mode \n", __func__);
return false;
}
}
@@ -2093,15 +2170,24 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
irqreturn_t status = IRQ_NONE;
unsigned long flags;
+ spin_lock_irqsave(&udc->lock, flags);
+ if (udc->stopped)
+ dr_clk_gate(true);
+
+ if (try_wake_up_udc(udc) == false) {
+ goto irq_end;
+ }
+#ifdef CONFIG_USB_OTG
+ /* if no gadget register in this driver, we need do noting */
+ if (udc->transceiver->gadget == NULL)
+ goto irq_end;
+
/* only handle device interrupt event */
if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) {
- return IRQ_NONE;
+ goto irq_end;
}
+#endif
- 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 */
fsl_writel(irq_src, &dr_regs->usbsts);
@@ -2160,6 +2246,11 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
VDBG("Error IRQ %x ", irq_src);
}
+irq_end:
+ if (udc->stopped) {
+ dr_clk_gate(false);
+ }
+
spin_unlock_irqrestore(&udc->lock, flags);
return status;
}
@@ -2189,11 +2280,12 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
spin_lock_irqsave(&udc_controller->lock, flags);
driver->driver.bus = 0;
+ udc_controller->pdata->port_enables = 1;
/* hook up the driver */
udc_controller->driver = driver;
udc_controller->gadget.dev.driver = &driver->driver;
spin_unlock_irqrestore(&udc_controller->lock, flags);
-
+ dr_clk_gate(true);
/* It doesn't need to switch usb from low power mode to normal mode
* at otg mode
*/
@@ -2211,30 +2303,29 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
if (udc_controller->transceiver) {
/* Suspend the controller until OTG enable it */
- udc_controller->stopped = 1;
- printk(KERN_INFO "Suspend udc for OTG auto detect\n");
+ udc_controller->suspended = 1;/* let the otg resume it */
+ printk(KERN_DEBUG "Suspend udc for OTG auto detect\n");
dr_wake_up_enable(udc_controller, true);
- dr_phy_low_power_mode(udc_controller, true);
/* export udc suspend/resume call to OTG */
udc_controller->gadget.dev.driver->suspend = (dev_sus)fsl_udc_suspend;
udc_controller->gadget.dev.driver->resume = (dev_res)fsl_udc_resume;
/* connect to bus through transceiver */
- if (udc_controller->transceiver) {
- retval = otg_set_peripheral(udc_controller->transceiver,
- &udc_controller->gadget);
- if (retval < 0) {
- ERR("can't bind to transceiver\n");
- driver->unbind(&udc_controller->gadget);
- udc_controller->gadget.dev.driver = 0;
- udc_controller->driver = 0;
- return retval;
- }
+ retval = otg_set_peripheral(udc_controller->transceiver,
+ &udc_controller->gadget);
+ if (retval < 0) {
+ ERR("can't bind to transceiver\n");
+ driver->unbind(&udc_controller->gadget);
+ udc_controller->gadget.dev.driver = 0;
+ udc_controller->driver = 0;
+ return retval;
}
} else {
/* Enable DR IRQ reg and Set usbcmd reg Run bit */
dr_controller_run(udc_controller);
+ if (udc_controller->stopped)
+ dr_clk_gate(false);
udc_controller->usb_state = USB_STATE_ATTACHED;
udc_controller->ep0_dir = 0;
}
@@ -2242,8 +2333,10 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
udc_controller->gadget.name, driver->driver.name);
out:
- if (retval)
+ if (retval) {
printk(KERN_DEBUG "retval %d \n", retval);
+ udc_controller->pdata->port_enables = 0;
+ }
return retval;
}
EXPORT_SYMBOL(usb_gadget_register_driver);
@@ -2260,15 +2353,16 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
if (!driver || driver != udc_controller->driver || !driver->unbind)
return -EINVAL;
+ if (udc_controller->stopped)
+ dr_clk_gate(true);
+
if (udc_controller->transceiver)
(void)otg_set_peripheral(udc_controller->transceiver, 0);
- /* open phy clock for following operation */
- dr_phy_low_power_mode(udc_controller, false);
-
/* stop DR, disable intr */
dr_controller_stop(udc_controller);
+ udc_controller->pdata->port_enables = 0;
/* in fact, no needed */
udc_controller->usb_state = USB_STATE_ATTACHED;
udc_controller->ep0_dir = 0;
@@ -2290,7 +2384,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);
+ if (udc_controller->gadget.is_otg) {
+ dr_wake_up_enable(udc_controller, true);
+ }
dr_phy_low_power_mode(udc_controller, true);
@@ -2707,6 +2803,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
ret = -ENODEV;
goto err1a;
}
+ udc_controller->gadget.is_otg = 1;
#endif
if ((pdev->dev.parent) &&
@@ -2807,12 +2904,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
if (ret < 0)
goto err3;
- 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);
@@ -2855,24 +2946,29 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
#ifdef POSTPONE_FREE_LAST_DTD
last_free_td = NULL;
#endif
-#ifndef CONFIG_USB_OTG
+
/* disable all INTR */
+#ifndef CONFIG_USB_OTG
fsl_writel(0, &dr_regs->usbintr);
-
dr_wake_up_enable(udc_controller, false);
- udc_controller->stopped = 1;
+#else
+ dr_wake_up_enable(udc_controller, true);
+#endif
+/*
+ * As mx25/mx35 does not implement clk_gate, should not let phy to low
+ * power mode due to IC bug
+ */
#if !(defined CONFIG_ARCH_MX35 || defined CONFIG_ARCH_MX25)
{
- u32 portsc;
- portsc = fsl_readl(&dr_regs->portsc1);
- portsc |= PORTSCX_PHY_LOW_POWER_SPD;
- fsl_writel(portsc, &dr_regs->portsc1);
+ dr_phy_low_power_mode(udc_controller, true);
}
#endif
- if (udc_controller->pdata->usb_clock_for_pm)
- udc_controller->pdata->usb_clock_for_pm(false);
-#endif
+ udc_controller->stopped = 1;
+
+ /* let the gadget register function open the clk */
+ dr_clk_gate(false);
+
create_proc_file();
return 0;
@@ -2907,7 +3003,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
return -ENODEV;
udc_controller->done = &done;
/* open USB PHY clock */
- dr_phy_low_power_mode(udc_controller, false);
+ if (udc_controller->stopped)
+ dr_clk_gate(true);
/* DR has been stopped in usb_gadget_unregister_driver() */
remove_proc_file();
@@ -2948,6 +3045,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
if (pdata->platform_uninit)
pdata->platform_uninit(pdata);
+ if (udc_controller->stopped)
+ dr_clk_gate(false);
return 0;
}
@@ -2955,43 +3054,41 @@ 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);
-
/*
* When it is the PM suspend routine and the device has no
* abilities to wakeup system, it should not set wakeup enable.
* Otherwise, the system will wakeup even the user only wants to
* charge using usb
*/
- if (!device_may_wakeup(udc_controller->gadget.dev.parent) &&
- udc_controller->gadget.dev.parent->power.status
- == DPM_SUSPENDING){
- dr_wake_up_enable(udc, false);
+ printk(KERN_DEBUG "udc suspend begins\n");
+ if (udc_controller->gadget.dev.parent->power.status
+ == DPM_SUSPENDING) {
+ if (!device_may_wakeup(udc_controller->gadget.dev.parent))
+ dr_wake_up_enable(udc, false);
+ else
+ dr_wake_up_enable(udc, true);
}
-
mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK;
usbcmd = fsl_readl(&dr_regs->usbcmd);
- pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped);
-
/*
* If the controller is already stopped, then this must be a
* PM suspend. Remember this fact, so that we will leave the
* controller stopped at PM resume time.
*/
- if (udc->stopped) {
- pr_debug("gadget already stopped, leaving early\n");
- udc->already_stopped = 1;
+ if (udc->suspended) {
+ printk(KERN_DEBUG "gadget already suspended, leaving early\n");
goto out;
}
if (mode != USB_MODE_CTRL_MODE_DEVICE) {
- pr_debug("gadget not in device mode, leaving early\n");
+ printk(KERN_DEBUG "gadget not in device mode, leaving early\n");
goto out;
}
+ /* For some buggy hardware designs, see comment of this function for detail */
+ udc_wait_b_session_low();
+
udc->stopped = 1;
/* stop the controller */
@@ -3004,13 +3101,15 @@ static int udc_suspend(struct fsl_udc *udc)
if (device_may_wakeup(udc_controller->gadget.dev.parent)) {
dr_wake_up_enable(udc, true);
}
- dr_phy_low_power_mode(udc, true);
}
- printk(KERN_INFO "USB Gadget suspended\n");
+ dr_phy_low_power_mode(udc, true);
+ printk(KERN_DEBUG "USB Gadget suspend ends\n");
out:
- if (udc_controller->pdata->usb_clock_for_pm)
- udc_controller->pdata->usb_clock_for_pm(false);
+ udc->suspended++;
+ if (udc->suspended > 2)
+ printk(KERN_ERR "ERROR: suspended times > 2\n");
+
return 0;
}
@@ -3020,13 +3119,23 @@ out:
-----------------------------------------------------------------*/
static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state)
{
+ int ret;
+#ifdef CONFIG_USB_OTG
+ if (udc_controller->transceiver->gadget == NULL)
+ return 0;
+#endif
+ if (udc_controller->stopped)
+ dr_clk_gate(true);
if (((!(udc_controller->gadget.is_otg)) ||
- (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) &&
+ (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) &&
(udc_controller->usb_state > USB_STATE_POWERED) &&
- (udc_controller->usb_state < USB_STATE_SUSPENDED))
- return -EBUSY;
+ (udc_controller->usb_state < USB_STATE_SUSPENDED)) {
+ return -EBUSY;/* keep the clk on */
+ } else
+ ret = udc_suspend(udc_controller);
+ dr_clk_gate(false);
- return udc_suspend(udc_controller);
+ return ret;
}
/*-----------------------------------------------------------------
@@ -3035,14 +3144,18 @@ static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state)
*-----------------------------------------------------------------*/
static int fsl_udc_resume(struct platform_device *pdev)
{
- pr_debug("%s(): stopped %d already_stopped %d\n", __func__,
- udc_controller->stopped, udc_controller->already_stopped);
+ printk(KERN_DEBUG "USB Gadget resume begins\n");
+ pr_debug("%s(): stopped %d suspended %d\n", __func__,
+ udc_controller->stopped, udc_controller->suspended);
+#ifdef CONFIG_USB_OTG
+ if (udc_controller->transceiver->gadget == NULL)
+ return 0;
+#endif
/*
* If the controller was stopped at suspend time, then
* don't resume it now.
*/
- if (udc_controller->already_stopped) {
/*
* If it is PM resume routine, the udc is at low power mode,
* and the udc has no abilities to wakeup system, it should
@@ -3055,19 +3168,25 @@ static int fsl_udc_resume(struct platform_device *pdev)
if (udc_controller->pdata->usb_clock_for_pm)
udc_controller->pdata->usb_clock_for_pm(true);
dr_wake_up_enable(udc_controller, true);
- if (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID) {
- if (udc_controller->pdata->usb_clock_for_pm)
- udc_controller->pdata->usb_clock_for_pm(false);
- }
+
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(false);
+
}
- udc_controller->already_stopped = 0;
- pr_debug("gadget was already stopped, leaving early\n");
+ if (--udc_controller->suspended) {
+ printk(KERN_DEBUG "gadget was already stopped, leaving early\n");
return 0;
}
/* Enable DR irq reg and set controller Run */
if (udc_controller->stopped) {
+ dr_clk_gate(true);
+ /* if in host mode, we need to do nothing */
+ if ((fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID) == 0) {
+ dr_clk_gate(false);
+ return 0;
+ }
dr_wake_up_enable(udc_controller, false);
dr_phy_low_power_mode(udc_controller, false);
mdelay(1);
@@ -3078,7 +3197,12 @@ static int fsl_udc_resume(struct platform_device *pdev)
udc_controller->usb_state = USB_STATE_ATTACHED;
udc_controller->ep0_dir = 0;
- printk(KERN_INFO "USB Gadget resumed\n");
+ /* if udc is resume by otg id change and no device
+ * connecting to the otg, otg will enter low power mode*/
+ if (udc_controller->stopped)
+ dr_clk_gate(false);
+
+ printk(KERN_DEBUG "USB Gadget resume ends\n");
return 0;
}
diff --git a/drivers/usb/gadget/arcotg_udc.h b/drivers/usb/gadget/arcotg_udc.h
index 466a4447d3dd..6f0282b90938 100644
--- a/drivers/usb/gadget/arcotg_udc.h
+++ b/drivers/usb/gadget/arcotg_udc.h
@@ -261,6 +261,7 @@ struct usb_sys_interface {
#define PORTSCX_SPEED_BIT_POS (26)
/* OTGSC Register Bit Masks */
+#define OTGSC_ID_CHANGE_IRQ_STS (1 << 16)
#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)
@@ -588,9 +589,15 @@ struct fsl_udc {
struct otg_transceiver *transceiver;
unsigned softconnect:1;
unsigned vbus_active:1;
- unsigned stopped:1;
unsigned remote_wakeup:1;
- unsigned already_stopped:1;
+ /* we must distinguish the stopped and suspended state,
+ * stopped means the udc enter lowpower mode, suspended
+ * means the udc is suspended by system pm or by otg
+ * switching to host mode.if the udc in suspended state
+ * it also in the stopped state, while if the udc in
+ * stopped state,it may not be in the suspended state*/
+ unsigned stopped:1;
+ int suspended;
struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */
struct fsl_req *status_req; /* ep0 status request */
@@ -696,7 +703,7 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length)
#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_STMP3XXX) || \
defined(CONFIG_ARCH_MXS)
#include <mach/fsl_usb_gadget.h>
-#elif CONFIG_PPC32
+#elif defined (CONFIG_PPC32)
#include <asm/fsl_usb_gadget.h>
#endif
diff --git a/drivers/usb/host/ehci-arc.c b/drivers/usb/host/ehci-arc.c
index fe7989586cee..b4bea17b9e03 100644
--- a/drivers/usb/host/ehci-arc.c
+++ b/drivers/usb/host/ehci-arc.c
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2005 MontaVista Software
* Copyright (C) 2010 Freescale Semiconductor
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -23,10 +23,30 @@
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
#include <linux/usb/otg.h>
+#include <linux/usb/hcd.h>
+#include "../core/usb.h"
#include "ehci-fsl.h"
#include <mach/fsl_usb.h>
+extern int usb_host_wakeup_irq(struct device *wkup_dev);
+extern void usb_host_set_wakeup(struct device *wkup_dev, bool para);
+static void fsl_usb_lowpower_mode(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ if (enable) {
+ if (pdata->phy_lowpower_suspend)
+ pdata->phy_lowpower_suspend(true);
+ } else {
+ if (pdata->phy_lowpower_suspend)
+ pdata->phy_lowpower_suspend(false);
+ }
+}
+
+static void fsl_usb_clk_gate(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ if (pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(enable);
+}
#undef EHCI_PROC_PTC
#ifdef EHCI_PROC_PTC /* /proc PORTSC:PTC support */
/*
@@ -91,8 +111,34 @@ static int ehci_testmode_init(struct ehci_hcd *ehci)
#endif /* /proc PORTSC:PTC support */
-/* configure so an HC device and id are always provided */
-/* always called with process context; sleeping is OK */
+/**
+ * This irq is used to open the hw access and let usb_hcd_irq process the usb event
+ * ehci_fsl_pre_irq will be called before usb_hcd_irq
+ */
+static irqreturn_t ehci_fsl_pre_irq(int irq, void *dev)
+{
+ struct platform_device *pdev = (struct platform_device *)dev;
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct fsl_usb2_platform_data *pdata;
+
+ pdata = hcd->self.controller->platform_data;
+
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ /* Need to open clk for accessing the register */
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, true);
+ /* if receive a remote wakeup interrrupt after suspend */
+ if (usb_host_wakeup_irq(hcd->self.controller)) {
+ pr_debug("host wakeup happens\n");
+ /* disable remote wake up irq */
+ usb_host_set_wakeup(hcd->self.controller, false);
+ fsl_usb_lowpower_mode(pdata, false);
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ } else {
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, false);
+ }
+ }
+ return IRQ_NONE;
+}
/**
* usb_hcd_fsl_probe - initialize FSL-based HCDs
@@ -183,10 +229,19 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
fsl_platform_set_host_mode(hcd);
hcd->power_budget = pdata->power_budget;
- retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ /*
+ * The ehci_fsl_pre_irq must be registered before usb_hcd_irq, in that case
+ * it can be called before usb_hcd_irq when irq occurs
+ */
+ retval = request_irq(irq, ehci_fsl_pre_irq, IRQF_SHARED,
+ "fsl ehci pre interrupt", (void *)pdev);
if (retval != 0)
goto err4;
+ retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ if (retval != 0)
+ goto err5;
+
fsl_platform_set_vbus_power(pdata, 1);
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
@@ -200,7 +255,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
if (!ehci->transceiver) {
printk(KERN_ERR "can't find transceiver\n");
retval = -ENODEV;
- goto err4;
+ goto err5;
}
retval = otg_set_host(ehci->transceiver, &ehci_to_hcd(ehci)->self);
@@ -217,7 +272,8 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
fsl_platform_set_ahb_burst(hcd);
ehci_testmode_init(hcd_to_ehci(hcd));
return retval;
-
+err5:
+ free_irq(irq, (void *)pdev);
err4:
iounmap(hcd->regs);
err3:
@@ -232,9 +288,6 @@ err1:
return retval;
}
-/* may be called without controller electrically present */
-/* may be called with controller, bus, and devices active */
-
/**
* usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs
* @dev: USB Host Controller being removed
@@ -325,6 +378,68 @@ static int ehci_fsl_reinit(struct ehci_hcd *ehci)
return 0;
}
+static int ehci_fsl_bus_suspend(struct usb_hcd *hcd)
+{
+ int ret = 0;
+ struct fsl_usb2_platform_data *pdata;
+ pdata = hcd->self.controller->platform_data;
+ printk(KERN_DEBUG "%s, %s\n", __func__, pdata->name);
+
+
+ /* the host is already at low power mode */
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ return 0;
+ }
+
+ pr_debug("%s, it is the host mode, %s\n", __func__, pdata->name);
+
+ ehci_bus_suspend(hcd);
+
+ if (pdata->platform_suspend)
+ pdata->platform_suspend(pdata);
+
+ usb_host_set_wakeup(hcd->self.controller, true);
+ fsl_usb_lowpower_mode(pdata, true);
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, false);
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+ return ret;
+}
+
+static int ehci_fsl_bus_resume(struct usb_hcd *hcd)
+{
+ int ret = 0;
+ struct fsl_usb2_platform_data *pdata;
+
+ pdata = hcd->self.controller->platform_data;
+ printk(KERN_DEBUG "%s, %s\n", __func__, pdata->name);
+
+ /*
+ * At otg mode, it should not call host resume for usb gadget device
+ * Otherwise, this usb device can't be recognized as a gadget
+ */
+ if (hcd->self.is_b_host) {
+ return -ESHUTDOWN;
+ }
+
+ /* if it is a remote wakeup, it will open clock and clear PHCD automatically */
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, true);
+ usb_host_set_wakeup(hcd->self.controller, false);
+ fsl_usb_lowpower_mode(pdata, false);
+ }
+
+ if (pdata->platform_resume)
+ pdata->platform_resume(pdata);
+ ret = ehci_bus_resume(hcd);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+
/* called during probe() after chip reset completes */
static int ehci_fsl_setup(struct usb_hcd *hcd)
{
@@ -360,97 +475,6 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
return retval;
}
-static int ehci_fsl_bus_suspend(struct usb_hcd *hcd)
-{
- u32 temp;
- int rc;
- struct fsl_usb2_platform_data *pdata = hcd->self.controller->platform_data;
-
- if ((pdata->operating_mode == FSL_USB2_DR_OTG) ||
- (pdata->operating_mode == FSL_USB2_DR_DEVICE))
- return -EBUSY;
-
- rc = ehci_bus_suspend(hcd);
-
- if (!rc && device_may_wakeup(hcd->self.controller)) {
- clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- /* enable remote wake up irq */
- if (pdata->wake_up_enable)
- pdata->wake_up_enable(pdata, true);
-
- /* Put PHY into low power mode */
- temp = readl(hcd->regs + 0x184);
- writel(temp | (1 << 23), (hcd->regs + 0x184));
-
- if (pdata->usb_clock_for_pm)
- pdata->usb_clock_for_pm(false);
- }
- return rc;
-}
-
-static int ehci_fsl_bus_resume(struct usb_hcd *hcd)
-{
- u32 temp;
-
- if (device_may_wakeup(hcd->self.controller)) {
- temp = readl(hcd->regs + 0x184);
- writel(temp & (~(1 << 23)), (hcd->regs + 0x184));
- }
- return ehci_bus_resume(hcd);
-}
-
-int usb_host_wakeup_irq(struct device *wkup_dev);
-
-static irqreturn_t ehci_fsl_irq(struct usb_hcd *hcd)
-{
- struct fsl_usb2_platform_data *pdata = hcd->self.controller->platform_data;
-
- if (pdata->usb_clock_for_pm)
- pdata->usb_clock_for_pm(true);
-
- /* if receive a remote wakeup interrrupt after suspend */
- if (usb_host_wakeup_irq(hcd->self.controller)) {
- /* disable remote wake up irq */
- if (pdata->wake_up_enable)
- pdata->wake_up_enable(pdata, false);
-
- set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- }
-
- return ehci_irq(hcd);
-}
-
-#ifdef CONFIG_USB_OTG
-static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port)
-{
- struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- u32 status;
-
- if (!port)
- return -EINVAL;
- port--;
-
- /* start port reset before HNP protocol time out */
- status = readl(&ehci->regs->port_status[port]);
- if (!(status & PORT_CONNECT))
- return -ENODEV;
-
- /* khubd will finish the reset later */
- if (ehci_is_TDI(ehci))
- writel(PORT_RESET | (status & ~(PORT_CSC | PORT_PEC
- | PORT_OCC)), &ehci->regs->port_status[port]);
- else
- writel(PORT_RESET, &ehci->regs->port_status[port]);
-
- return 0;
-}
-#else
-static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port)
-{
- return 0;
-}
-#endif /* CONFIG_USB_OTG */
-
static const struct hc_driver ehci_fsl_hc_driver = {
.description = hcd_name,
.product_desc = "Freescale On-Chip EHCI Host Controller",
@@ -459,7 +483,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
/*
* generic hardware linkage
*/
- .irq = ehci_fsl_irq,
+ .irq = ehci_irq,
.flags = HCD_USB2,
/*
@@ -490,7 +514,6 @@ static const struct hc_driver ehci_fsl_hc_driver = {
.hub_control = ehci_hub_control,
.bus_suspend = ehci_fsl_bus_suspend,
.bus_resume = ehci_fsl_bus_resume,
- .start_port_reset = ehci_start_port_reset,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
@@ -530,13 +553,43 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev,
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- u32 tmp, port_status;
+ struct usb_device *roothub = hcd->self.root_hub;
+ u32 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);
+ printk(KERN_DEBUG "USB Host suspend begins\n");
+ /* Only handles OTG mode switch event, system suspend event will be done in bus suspend */
+ if (pdev->dev.power.status == DPM_SUSPENDING) {
+ printk(KERN_DEBUG "%s, pm event \n", __func__);
+ if (!device_may_wakeup(&(pdev->dev))) {
+ int mask;
+ /* Need open clock for register access */
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, true);
+
+ mask = ehci_readl(ehci, &ehci->regs->intr_enable);
+ mask &= ~STS_PCD;
+ ehci_writel(ehci, mask, &ehci->regs->intr_enable);
+
+ usb_host_set_wakeup(hcd->self.controller, false);
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, false);
+ }
+ return 0;
+ }
+ /* only the otg host can go here */
+ /* wait for all usb device on the hcd dettached */
+ while (roothub->children[0] != NULL)
+ msleep(1);
+ if ((pdata->operating_mode != FSL_USB2_MPH_HOST) && (!(hcd->state & HC_STATE_SUSPENDED))) {
+ printk(KERN_DEBUG "will suspend roothub and its children\n");
+ usb_lock_device(roothub);
+ usb_suspend(&roothub->dev, PMSG_USER_SUSPEND);
+ usb_unlock_device(roothub);
+ }
+
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, true);
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
}
#ifdef DEBUG
@@ -549,24 +602,10 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev,
pdata->suspended, pdata->already_suspended, mode, tmp);
#endif
- /*
- * If the controller is already suspended, then this must be a
- * PM suspend. Remember this fact, so that we will leave the
- * controller suspended at PM resume time.
- */
- if (pdata->suspended) {
- pr_debug("%s: already suspended, leaving early\n", __func__);
- pdata->already_suspended = 1;
- goto err1;
- }
-
pr_debug("%s: suspending...\n", __func__);
- printk(KERN_INFO "USB Host suspended\n");
port_status = ehci_readl(ehci, &ehci->regs->port_status[0]);
- pdev->dev.power.power_state = PMSG_SUSPEND;
-
/* save EHCI registers */
pdata->pm_command = ehci_readl(ehci, &ehci->regs->command);
pdata->pm_command &= ~CMD_RUN;
@@ -585,29 +624,11 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev,
/* clear PHCD bit */
pdata->pm_portsc &= ~PORT_PTS_PHCD;
-
- pdata->suspended = 1;
-
- if (!device_may_wakeup(&(pdev->dev))) {
- /* ignore non-host interrupts */
+ if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
- /* clear PP to cut power to the port */
- tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
- tmp &= ~PORT_POWER;
- ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
- goto err1;
- }
-
- tmp = ehci_readl(ehci, &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);
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, false);
}
+ printk(KERN_DEBUG "host suspend ends\n");
return 0;
}
@@ -615,47 +636,35 @@ static int ehci_fsl_drv_resume(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct usb_device *roothub = hcd->self.root_hub;
u32 tmp;
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
-
- pr_debug("%s('%s'): suspend=%d already_suspended=%d\n", __func__,
- pdata->name, pdata->suspended, pdata->already_suspended);
-
- /*
- * If the controller was already suspended at suspend time,
- * then don't resume it now.
- */
- if (pdata->already_suspended) {
- pr_debug("already suspended, leaving early\n");
- pdata->already_suspended = 0;
- return 0;
- }
-
- if (!pdata->suspended) {
- pr_debug("not suspended, leaving early\n");
+ /* Only handles OTG mode switch event */
+ printk(KERN_DEBUG "ehci fsl drv resume begins: %s\n", pdata->name);
+ if (pdev->dev.power.status == DPM_RESUMING) {
+ printk(KERN_DEBUG "%s, pm event \n", __func__);
+ if (!device_may_wakeup(&(pdev->dev))) {
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, true);
+ }
+ usb_host_set_wakeup(hcd->self.controller, true);
+
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, false);
+ }
+ }
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))) {
- /* Need open clock for register access */
- if (pdata->usb_clock_for_pm)
- pdata->usb_clock_for_pm(true);
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ fsl_usb_clk_gate(hcd->self.controller->platform_data, true);
+ usb_host_set_wakeup(hcd->self.controller, false);
+ fsl_usb_lowpower_mode(pdata, false);
}
- tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
-
- pdata->suspended = 0;
-
- pr_debug("%s resuming...\n", __func__);
-
/* set host mode */
fsl_platform_set_host_mode(hcd);
- if (pdata->platform_resume)
- 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);
@@ -667,32 +676,22 @@ static int ehci_fsl_drv_resume(struct platform_device *pdev)
ehci_writel(ehci, pdata->pm_configured_flag,
&ehci->regs->configured_flag);
- /* 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);
tmp |= CMD_RUN;
ehci_writel(ehci, tmp, &ehci->regs->command);
- 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);
+ if ((hcd->state & HC_STATE_SUSPENDED)) {
+ printk(KERN_DEBUG "will resume roothub and its children\n");
+ usb_lock_device(roothub);
+ usb_resume(&roothub->dev, PMSG_USER_RESUME);
+ usb_unlock_device(roothub);
}
+ printk(KERN_DEBUG "ehci fsl drv resume ends: %s\n", pdata->name);
return 0;
}
-#endif /* CONFIG_USB_OTG */
-
+#endif
MODULE_ALIAS("platform:fsl-ehci");
static struct platform_driver ehci_fsl_driver = {
diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c
index 6cf451589c09..88d7d4b39f20 100644
--- a/drivers/usb/otg/fsl_otg.c
+++ b/drivers/usb/otg/fsl_otg.c
@@ -34,6 +34,7 @@
#include <linux/init.h>
#include <linux/reboot.h>
#include <linux/timer.h>
+#include <linux/jiffies.h>
#include <linux/list.h>
#include <linux/usb.h>
#include <linux/device.h>
@@ -61,8 +62,10 @@
#define DRIVER_DESC "Freescale USB OTG Driver"
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+#define TIMER_FREQ 1000 /* 1000 ms */
+#define IDLE_TIME 5000 /* 5000 ms */
-MODULE_DESCRIPTION("Freescale USB OTG Transceiver Driver");
+MODULE_DESCRIPTION("Freescale USB OTG Driver");
static const char driver_name[] = "fsl-usb2-otg";
@@ -90,6 +93,13 @@ static struct fsl_otg_config fsl_otg_initdata = {
.otg_port = 1,
};
+/* the timer is used to monitor the otg loading, if idle for some times
+ * we will close the otg clk
+ */
+static unsigned long last_busy;
+static bool clk_stopped;
+static struct timer_list monitor_timer;
+
int write_ulpi(u8 addr, u8 data)
{
u32 temp;
@@ -139,15 +149,6 @@ void fsl_otg_dischrg_vbus(int on)
/* A-device driver vbus, controlled through PP bit in PORTSC */
void fsl_otg_drv_vbus(struct fsl_usb2_platform_data *pdata, int on)
{
-/* if (on)
- usb_dr_regs->portsc =
- cpu_to_le32((le32_to_cpu(usb_dr_regs->portsc) &
- ~PORTSC_W1C_BITS) | PORTSC_PORT_POWER);
- else
- usb_dr_regs->portsc =
- cpu_to_le32(le32_to_cpu(usb_dr_regs->portsc) &
- ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER);
-*/
if (pdata->xcvr_ops && pdata->xcvr_ops->set_vbus_power)
pdata->xcvr_ops->set_vbus_power(pdata->xcvr_ops, pdata, on);
}
@@ -398,6 +399,60 @@ int fsl_otg_tick_timer(void)
return expired;
}
+static void fsl_otg_clk_gate(bool on)
+{
+ struct device *dev = fsl_otg_dev->otg.dev;
+ struct fsl_usb2_platform_data *pdata;
+
+ if (dev) {
+ pdata = dev->platform_data;
+ if (pdata && pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(on);
+ }
+}
+
+static void fsl_otg_clk_ctl(void)
+{
+ if (clk_stopped) {
+ fsl_otg_clk_gate(true);
+ clk_stopped = false;
+ }
+ last_busy = jiffies;
+}
+
+static void fsl_otg_loading_monitor(unsigned long data)
+{
+ unsigned long now = jiffies;
+ if (!clk_stopped) {
+ if (time_after(now, last_busy + msecs_to_jiffies(IDLE_TIME))) {
+ clk_stopped = true;
+ fsl_otg_clk_gate(false);
+ }
+ }
+ mod_timer(&monitor_timer, jiffies + msecs_to_jiffies(TIMER_FREQ));
+}
+
+/**
+ * Enable vbus interrupt
+ * The otg cares USB_ID interrupt
+ * The device cares B Session Valid
+ */
+static void b_session_irq_enable(bool enable)
+{
+ int osc = le32_to_cpu(usb_dr_regs->otgsc);
+ printk(KERN_DEBUG "%s:enable is %d\n", __func__, enable);
+ /* The other interrupts' status should not be cleared */
+ osc &= ~(OTGSC_INTSTS_USB_ID | OTGSC_INTSTS_A_VBUS_VALID
+ | OTGSC_INTSTS_A_SESSION_VALID | OTGSC_INTSTS_B_SESSION_VALID);
+ osc |= OTGSC_INTSTS_B_SESSION_VALID;
+
+ if (enable)
+ osc |= OTGSC_INTR_B_SESSION_VALID_EN;
+ else
+ osc &= ~OTGSC_INTR_B_SESSION_VALID_EN;
+ usb_dr_regs->otgsc = cpu_to_le32(osc);
+}
+
/* Reset controller, not reset the bus */
void otg_reset_controller(void)
{
@@ -485,7 +540,6 @@ int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
struct device *dev;
struct platform_driver *gadget_pdrv;
struct platform_device *gadget_pdev;
-
if (!xceiv->gadget || !xceiv->gadget->dev.parent)
return -ENODEV;
@@ -521,7 +575,6 @@ static int fsl_otg_set_host(struct otg_transceiver *otg_p, struct usb_bus *host)
if (host) {
VDBG("host off......\n");
-
otg_p->host->otg_port = fsl_otg_initdata.otg_port;
otg_p->host->is_b_host = otg_dev->fsm.id;
/* must leave time for khubd to finish its thing
@@ -638,11 +691,28 @@ static void fsl_otg_event(struct work_struct *work)
{
struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work);
struct otg_fsm *fsm = &og->fsm;
+ struct otg_transceiver *otg = &og->otg;
+
+ otg->default_a = (fsm->id == 0);
+ /* clear conn information */
+ if (fsm->id)
+ fsm->b_conn = 0;
+ else
+ fsm->a_conn = 0;
+
+ if (otg->host)
+ otg->host->is_b_host = fsm->id;
+ if (otg->gadget)
+ otg->gadget->is_a_peripheral = !fsm->id;
if (fsm->id) { /* switch to gadget */
fsl_otg_start_host(fsm, 0);
otg_drv_vbus(fsm, 0);
fsl_otg_start_gadget(fsm, 1);
+ } else { /* switch to host */
+ fsl_otg_start_gadget(fsm, 0);
+ otg_drv_vbus(fsm, 1);
+ fsl_otg_start_host(fsm, 1);
}
}
@@ -682,12 +752,13 @@ irqreturn_t fsl_otg_isr_gpio(int irq, void *dev_id)
struct otg_fsm *fsm;
struct fsl_usb2_platform_data *pdata =
(struct fsl_usb2_platform_data *)dev_id;
- struct fsl_otg *p_otg;
+ struct fsl_otg *f_otg;
struct otg_transceiver *otg_trans = otg_get_transceiver();
int value;
- p_otg = container_of(otg_trans, struct fsl_otg, otg);
- fsm = &p_otg->fsm;
+ f_otg = container_of(otg_trans, struct fsl_otg, otg);
+ fsm = &f_otg->fsm;
+ fsl_otg_clk_ctl();
if (pdata->id_gpio == 0)
return IRQ_NONE;
@@ -699,35 +770,23 @@ irqreturn_t fsl_otg_isr_gpio(int irq, void *dev_id)
set_irq_type(gpio_to_irq(pdata->id_gpio), IRQ_TYPE_LEVEL_HIGH);
- if (value == p_otg->fsm.id)
+ if (value == f_otg->fsm.id)
return IRQ_HANDLED;
- p_otg->fsm.id = value;
-
- otg_trans->default_a = (fsm->id == 0);
- /* clear conn information */
- if (fsm->id)
- fsm->b_conn = 0;
- else
- fsm->a_conn = 0;
+ f_otg->fsm.id = value;
- if (otg_trans->host)
- otg_trans->host->is_b_host = fsm->id;
- if (otg_trans->gadget)
- otg_trans->gadget->is_a_peripheral = !fsm->id;
-
- VDBG("ID int (ID is %d)\n", fsm->id);
- if (fsm->id) { /* switch to gadget */
- schedule_delayed_work(&p_otg->otg_event, 100);
-
- } else { /* switch to host */
- cancel_delayed_work(&p_otg->otg_event);
- fsl_otg_start_gadget(fsm, 0);
- otg_drv_vbus(fsm, 1);
- fsl_otg_start_host(fsm, 1);
+ cancel_delayed_work(&f_otg->otg_event);
+ schedule_delayed_work(&f_otg->otg_event, msecs_to_jiffies(10));
+ /* if host mode, we should clear B_SESSION_VLD event and disable
+ * B_SESSION_VLD irq
+ */
+ if (!f_otg->fsm.id) {
+ b_session_irq_enable(false);
}
+
return IRQ_HANDLED;
}
+
/* Interrupt handler. OTG/host/peripheral share the same int line.
* OTG driver clears OTGSC interrupts and leaves USB interrupts
* intact. It needs to have knowledge of some USB interrupts
@@ -735,55 +794,44 @@ irqreturn_t fsl_otg_isr_gpio(int irq, void *dev_id)
*/
irqreturn_t fsl_otg_isr(int irq, void *dev_id)
{
- struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm;
- struct otg_transceiver *otg = &((struct fsl_otg *)dev_id)->otg;
+ struct fsl_otg *fotg = (struct fsl_otg *)dev_id;
+ struct otg_transceiver *otg = &fotg->otg;
u32 otg_int_src, otg_sc;
+ irqreturn_t ret = IRQ_NONE;
+ fsl_otg_clk_ctl();
otg_sc = le32_to_cpu(usb_dr_regs->otgsc);
otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8);
- /* Only clear otg interrupts */
- usb_dr_regs->otgsc |= cpu_to_le32(otg_sc & OTGSC_INTSTS_MASK);
+ /* Only clear otg interrupts, expect B_SESSION_VALID,
+ * Leave it to be handled by arcotg_udc */
+ usb_dr_regs->otgsc = ((usb_dr_regs->otgsc | cpu_to_le32(otg_sc & OTGSC_INTSTS_MASK))&
+ (~OTGSC_INTSTS_B_SESSION_VALID));
/*FIXME: ID change not generate when init to 0 */
- fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
- otg->default_a = (fsm->id == 0);
+ fotg->fsm.id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
+ otg->default_a = (fotg->fsm.id == 0);
/* process OTG interrupts */
if (otg_int_src) {
if (otg_int_src & OTGSC_INTSTS_USB_ID) {
- fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
- otg->default_a = (fsm->id == 0);
- /* clear conn information */
- if (fsm->id)
- fsm->b_conn = 0;
- else
- fsm->a_conn = 0;
-
- if (otg->host)
- otg->host->is_b_host = fsm->id;
- if (otg->gadget)
- otg->gadget->is_a_peripheral = !fsm->id;
- VDBG("ID int (ID is %d)\n", fsm->id);
-
- if (fsm->id) { /* switch to gadget */
- schedule_delayed_work(&((struct fsl_otg *)
- dev_id)->otg_event,
- 100);
- } else { /* switch to host */
- cancel_delayed_work(&
- ((struct fsl_otg *)dev_id)->
- otg_event);
- fsl_otg_start_gadget(fsm, 0);
- otg_drv_vbus(fsm, 1);
- fsl_otg_start_host(fsm, 1);
- }
+ fotg->fsm.id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
+
+ printk(KERN_DEBUG "ID int (ID is %d)\n", fotg->fsm.id);
- return IRQ_HANDLED;
+ cancel_delayed_work(&fotg->otg_event);
+ schedule_delayed_work(&fotg->otg_event, msecs_to_jiffies(10));
+ /* if host mode, we should clear B_SESSION_VLD event and disable
+ * B_SESSION_VLD irq
+ */
+ if (!fotg->fsm.id) {
+ b_session_irq_enable(false);
+ }
+ ret = IRQ_HANDLED;
}
}
- return IRQ_NONE;
+ return ret;
}
static void fsl_otg_fsm_drv_vbus(int on)
@@ -859,6 +907,7 @@ static int fsl_otg_conf(struct platform_device *pdev)
fsl_otg_tc->otg.set_power = fsl_otg_set_power;
fsl_otg_tc->otg.start_hnp = fsl_otg_start_hnp;
fsl_otg_tc->otg.start_srp = fsl_otg_start_srp;
+ fsl_otg_tc->otg.dev = &pdev->dev;
fsl_otg_dev = fsl_otg_tc;
@@ -925,6 +974,8 @@ int usb_otg_start(struct platform_device *pdev)
if (pdata->platform_init && pdata->platform_init(pdev) != 0)
return -EINVAL;
+ clk_stopped = false; /* platform_init will open the otg clk */
+
/* stop the controller */
temp = readl(&p_otg->dr_mem_map->usbcmd);
temp &= ~USB_CMD_RUN_STOP;
@@ -1252,6 +1303,10 @@ static int __init fsl_otg_probe(struct platform_device *pdev)
return -EIO;
}
+ last_busy = jiffies;
+ setup_timer(&monitor_timer, fsl_otg_loading_monitor, (unsigned long)pdev);
+ mod_timer(&monitor_timer, jiffies + msecs_to_jiffies(TIMER_FREQ));
+
create_proc_file();
return status;
}
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 67398a699792..93d02d3f3d09 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -91,6 +91,7 @@ struct fsl_usb2_platform_data {
void (*platform_suspend)(struct fsl_usb2_platform_data *);
void (*platform_resume)(struct fsl_usb2_platform_data *);
void (*wake_up_enable)(struct fsl_usb2_platform_data *pdata, bool on);
+ void (*phy_lowpower_suspend)(bool);
void (*platform_driver_vbus)(bool on); /* platform special function for vbus shutdown/open */
unsigned big_endian_mmio:1;
unsigned big_endian_desc:1;