]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/musb/omap2430.c
USB: MUSB: Better sysconf reg settings on PM point of view
[linux-2.6-omap-h63xx.git] / drivers / usb / musb / omap2430.c
index e62bb760a45136648eeeecc380c5414a4c792506..36f17397d80ebf96ea9a673e600bdb60e504e578 100644 (file)
@@ -1,5 +1,10 @@
 /*
- * Copyright (C) 2005-2006 by Texas Instruments
+ * Copyright (C) 2005-2007 by Texas Instruments
+ * Some code has been taken from tusb6010.c
+ * Copyrights for that are attributable to:
+ * Copyright (C) 2006 Nokia Corporation
+ * Jarkko Nikula <jarkko.nikula@nokia.com>
+ * Tony Lindgren <tony@atomide.com>
  *
  * This file is part of the Inventra Controller Driver for Linux.
  *
 #include <linux/init.h>
 #include <linux/list.h>
 #include <linux/clk.h>
+#include <linux/io.h>
 
-#include <asm/io.h>
 #include <asm/mach-types.h>
 #include <asm/arch/hardware.h>
 #include <asm/arch/mux.h>
 
-#include "musbdefs.h"
+#include "musb_core.h"
 #include "omap2430.h"
 
 #ifdef CONFIG_ARCH_OMAP3430
 #define        get_cpu_rev()   2
 #endif
 
+#define MUSB_TIMEOUT_A_WAIT_BCON       1100
+
+static struct timer_list musb_idle_timer;
+
+static void musb_do_idle(unsigned long _musb)
+{
+       struct musb     *musb = (void *)_musb;
+       unsigned long   flags;
+       u8      power;
+       u8      devctl;
+
+       devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+       spin_lock_irqsave(&musb->lock, flags);
+
+       switch (musb->xceiv.state) {
+       case OTG_STATE_A_WAIT_BCON:
+               devctl &= ~MUSB_DEVCTL_SESSION;
+               musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+               devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+               if (devctl & MUSB_DEVCTL_BDEVICE) {
+                       musb->xceiv.state = OTG_STATE_B_IDLE;
+                       MUSB_DEV_MODE(musb);
+               } else {
+                       musb->xceiv.state = OTG_STATE_A_IDLE;
+                       MUSB_HST_MODE(musb);
+               }
+               break;
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+       case OTG_STATE_A_SUSPEND:
+               /* finish RESUME signaling? */
+               if (musb->port1_status & MUSB_PORT_STAT_RESUME) {
+                       power = musb_readb(musb->mregs, MUSB_POWER);
+                       power &= ~MUSB_POWER_RESUME;
+                       DBG(1, "root port resume stopped, power %02x\n", power);
+                       musb_writeb(musb->mregs, MUSB_POWER, power);
+                       musb->is_active = 1;
+                       musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
+                                               | MUSB_PORT_STAT_RESUME);
+                       musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
+                       usb_hcd_poll_rh_status(musb_to_hcd(musb));
+                       /* NOTE: it might really be A_WAIT_BCON ... */
+                       musb->xceiv.state = OTG_STATE_A_HOST;
+               }
+               break;
+#endif
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+       case OTG_STATE_A_HOST:
+               devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+               if (devctl &  MUSB_DEVCTL_BDEVICE)
+                       musb->xceiv.state = OTG_STATE_B_IDLE;
+               else
+                       musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
+#endif
+       default:
+               break;
+       }
+       spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+
+void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
+{
+       unsigned long           default_timeout = jiffies + msecs_to_jiffies(3);
+       static unsigned long    last_timer;
+
+       if (timeout == 0)
+               timeout = default_timeout;
+
+       /* Never idle if active, or when VBUS timeout is not set as host */
+       if (musb->is_active || ((musb->a_wait_bcon == 0)
+                       && (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) {
+               DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
+               del_timer(&musb_idle_timer);
+               last_timer = jiffies;
+               return;
+       }
+
+       if (time_after(last_timer, timeout)) {
+               if (!timer_pending(&musb_idle_timer))
+                       last_timer = timeout;
+               else {
+                       DBG(4, "Longer idle timer already pending, ignoring\n");
+                       return;
+               }
+       }
+       last_timer = timeout;
+
+       DBG(4, "%s inactive, for idle timer for %lu ms\n",
+               otg_state_string(musb),
+               (unsigned long)jiffies_to_msecs(timeout - jiffies));
+       mod_timer(&musb_idle_timer, timeout);
+}
 
 void musb_platform_enable(struct musb *musb)
 {
@@ -65,7 +164,7 @@ static void omap_set_vbus(struct musb *musb, int is_on)
                musb->is_active = 1;
                musb->xceiv.default_a = 1;
                musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
-               devctl |= MGC_M_DEVCTL_SESSION;
+               devctl |= MUSB_DEVCTL_SESSION;
 
                MUSB_HST_MODE(musb);
        } else {
@@ -77,7 +176,7 @@ static void omap_set_vbus(struct musb *musb, int is_on)
 
                musb->xceiv.default_a = 0;
                musb->xceiv.state = OTG_STATE_B_IDLE;
-               devctl &= ~MGC_M_DEVCTL_SESSION;
+               devctl &= ~MUSB_DEVCTL_SESSION;
 
                MUSB_DEV_MODE(musb);
        }
@@ -95,20 +194,44 @@ static int omap_set_power(struct otg_transceiver *x, unsigned mA)
 
 int musb_platform_resume(struct musb *musb);
 
+void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
+{
+       u8      devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+       devctl |= MUSB_DEVCTL_SESSION;
+       musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+       switch (musb_mode) {
+       case MUSB_HOST:
+               otg_set_host(&musb->xceiv, musb->xceiv.host);
+               break;
+       case MUSB_PERIPHERAL:
+               otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget);
+               break;
+       case MUSB_OTG:
+               break;
+       }
+}
+
 int __init musb_platform_init(struct musb *musb)
 {
+       struct otg_transceiver *xceiv = otg_get_transceiver();
+
 #if defined(CONFIG_ARCH_OMAP2430)
        omap_cfg_reg(AE5_2430_USB0HS_STP);
-       /* get the clock */
-       musb->clock = clk_get((struct device *)musb->controller, "usbhs_ick");
-#else
-       musb->clock = clk_get((struct device *)musb->controller, "hsusb_ick");
 #endif
-       if(IS_ERR(musb->clock))
-               return PTR_ERR(musb->clock);
 
+       musb->xceiv = *xceiv;
        musb_platform_resume(musb);
 
+       OTG_SYSCONFIG_REG &= ~ENABLEWAKEUP;     /* disable wakeup */
+       OTG_SYSCONFIG_REG &= ~NOSTDBY;          /* remove possible nostdby */
+       OTG_SYSCONFIG_REG |= SMARTSTDBY;        /* enable smart standby */
+       OTG_SYSCONFIG_REG &= ~AUTOIDLE;         /* disable auto idle */
+       OTG_SYSCONFIG_REG &= ~NOIDLE;           /* remove possible noidle */
+       OTG_SYSCONFIG_REG |= SMARTIDLE;         /* enable smart idle */
+       OTG_SYSCONFIG_REG |= AUTOIDLE;          /* enable auto idle */
+
        OTG_INTERFSEL_REG |= ULPI_12PIN;
 
        pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
@@ -118,38 +241,52 @@ int __init musb_platform_init(struct musb *musb)
 
        omap_vbus_power(musb, musb->board_mode == MUSB_HOST, 1);
 
-
        if (is_host_enabled(musb))
                musb->board_set_vbus = omap_set_vbus;
        if (is_peripheral_enabled(musb))
                musb->xceiv.set_power = omap_set_power;
+       musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON;
+
+       setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
 
        return 0;
 }
 
 int musb_platform_suspend(struct musb *musb)
 {
+       if (!musb->clock)
+               return 0;
+
        /* in any role */
-       OTG_FORCESTDBY_REG &= ~ENABLEFORCE; /* disable MSTANDBY */
-       OTG_SYSCONFIG_REG &= FORCESTDBY;        /* enable force standby */
-       OTG_SYSCONFIG_REG &= ~AUTOIDLE;         /* disable auto idle */
-       OTG_SYSCONFIG_REG |= SMARTIDLE;         /* enable smart idle */
-       OTG_FORCESTDBY_REG |= ENABLEFORCE; /* enable MSTANDBY */
-       OTG_SYSCONFIG_REG |= AUTOIDLE;          /* enable auto idle */
+       OTG_FORCESTDBY_REG |= ENABLEFORCE;      /* enable MSTANDBY */
+       OTG_SYSCONFIG_REG |= ENABLEWAKEUP;      /* enable wakeup */
+
+       if (musb->xceiv.set_suspend)
+               musb->xceiv.set_suspend(&musb->xceiv, 1);
+
+       if (musb->set_clock)
+               musb->set_clock(musb->clock, 0);
+       else
+               clk_disable(musb->clock);
 
-       clk_disable(musb->clock);
        return 0;
 }
 
 int musb_platform_resume(struct musb *musb)
 {
-       clk_enable(musb->clock);
+       if (!musb->clock)
+               return 0;
 
-       OTG_FORCESTDBY_REG &= ~ENABLEFORCE; /* disable MSTANDBY */
-       OTG_SYSCONFIG_REG |= SMARTSTDBY;        /* enable smart standby */
-       OTG_SYSCONFIG_REG &= ~AUTOIDLE;         /* disable auto idle */
-       OTG_SYSCONFIG_REG |= SMARTIDLE;         /* enable smart idle */
-       OTG_SYSCONFIG_REG |= AUTOIDLE;          /* enable auto idle */
+       if (musb->xceiv.set_suspend)
+               musb->xceiv.set_suspend(&musb->xceiv, 0);
+
+       if (musb->set_clock)
+               musb->set_clock(musb->clock, 1);
+       else
+               clk_enable(musb->clock);
+
+       OTG_SYSCONFIG_REG &= ~ENABLEWAKEUP;     /* disable wakeup */
+       OTG_FORCESTDBY_REG &= ~ENABLEFORCE;     /* disable MSTANDBY */
 
        return 0;
 }