]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
musb_hdrc: Add board specific external clock handling
authorTony Lindgren <tony@atomide.com>
Thu, 1 Feb 2007 23:38:35 +0000 (15:38 -0800)
committerTony Lindgren <tony@atomide.com>
Fri, 30 Mar 2007 18:41:07 +0000 (14:41 -0400)
On N800, if osc_ck is disabled by PM, USB will stop working.

Signed-off-by: Tony Lindgren <tony@atomide.com>
Updated so some of this code can be shared with other HDRC implementations;
pretty much everything except the TUSB 6010 EVM needs a clock.  This will
need more work in the future.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
arch/arm/mach-omap2/board-n800-usb.c
drivers/usb/musb/musbdefs.h
drivers/usb/musb/plat_uds.c
drivers/usb/musb/tusb6010.c
include/linux/usb/musb.h

index e43690ddb53ea13913e1a6b882686025f9e9bbb6..f6cc15cb793cf297055517b18d8b762dcf818183 100644 (file)
  */
 
 #include <linux/types.h>
-#include <linux/errno.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/usb/musb.h>
 #include <asm/arch/gpmc.h>
 #include <asm/arch/gpio.h>
+#include <asm/arch/pm.h>
 
 #define TUSB_ASYNC_CS          1
 #define TUSB_SYNC_CS           4
@@ -23,6 +25,7 @@
 #define GPIO_TUSB_ENABLE       0
 
 static int tusb_set_power(int state);
+static int tusb_set_clock(struct clk *osc_ck, int state);
 
 #if    defined(CONFIG_USB_MUSB_OTG)
 #      define BOARD_MODE       MUSB_OTG
@@ -36,7 +39,9 @@ static struct musb_hdrc_platform_data tusb_data = {
        .mode           = BOARD_MODE,
        .multipoint     = 1,
        .set_power      = tusb_set_power,
+       .set_clock      = tusb_set_clock,
        .min_power      = 25,   /* x2 = 50 mA drawn from VBUS as peripheral */
+       .clock          = "osc_ck",
 };
 
 /*
@@ -71,6 +76,29 @@ static int tusb_set_power(int state)
        return retval;
 }
 
+static int             osc_ck_on;
+
+static int tusb_set_clock(struct clk *osc_ck, int state)
+{
+       if (state) {
+               if (osc_ck_on > 0)
+                       return -ENODEV;
+
+               omap2_block_sleep();
+               clk_enable(osc_ck);
+               osc_ck_on = 1;
+       } else {
+               if (osc_ck_on == 0)
+                       return -ENODEV;
+
+               clk_disable(osc_ck);
+               osc_ck_on = 0;
+               omap2_allow_sleep();
+       }
+
+       return 0;
+}
+
 void __init n800_usb_init(void)
 {
        int ret = 0;
index d059be34ff0a6d5bca9ea8f5add3414f18e3c5a4..b39c719121c76959c4fa31d315ce4bae0ac9d0a8 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/interrupt.h>
 #include <linux/smp_lock.h>
 #include <linux/errno.h>
+#include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb_gadget.h>
@@ -402,6 +403,8 @@ struct musb {
        u8 board_mode;          /* enum musb_mode */
        int                     (*board_set_power)(int state);
 
+       int                     (*set_clock)(struct clk *clk, int is_active);
+
        u8                      min_power;      /* vbus for periph, in mA/2 */
 
        /* active means connected and not suspended */
index e29e825d7d3f7de719f5cdcf2b64dc56d7a067ef..909511509d56437f255db8d3ee07cd895955642e 100644 (file)
@@ -97,7 +97,6 @@
 #include <linux/list.h>
 #include <linux/kobject.h>
 #include <linux/platform_device.h>
-#include <linux/clk.h>
 
 #include <asm/io.h>
 
@@ -792,6 +791,10 @@ static void musb_shutdown(struct platform_device *pdev)
        spin_lock_irqsave(&musb->Lock, flags);
        musb_platform_disable(musb);
        musb_generic_disable(musb);
+       if (musb->clock) {
+               clk_put(musb->clock);
+               musb->clock = NULL;
+       }
        spin_unlock_irqrestore(&musb->Lock, flags);
 
        /* FIXME power down */
@@ -1712,8 +1715,23 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
        spin_lock_init(&pThis->Lock);
        pThis->board_mode = plat->mode;
        pThis->board_set_power = plat->set_power;
+       pThis->set_clock = plat->set_clock;
        pThis->min_power = plat->min_power;
 
+       /* Clock usage is chip-specific ... functional clock (DaVinci,
+        * OMAP2430), or PHY ref (some TUSB6010 boards).  All this core
+        * code does is make sure a clock handle is available; platform
+        * code manages it during start/stop and suspend/resume.
+        */
+       if (plat->clock) {
+               pThis->clock = clk_get(dev, plat->clock);
+               if (IS_ERR(pThis->clock)) {
+                       status = PTR_ERR(pThis->clock);
+                       pThis->clock = NULL;
+                       goto fail;
+               }
+       }
+
        /* assume vbus is off */
 
        /* platform adjusts pThis->pRegs and pThis->isr if needed,
@@ -1825,6 +1843,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
                musb_debug_create("driver/musb_hdrc", pThis);
        else {
 fail:
+               if (pThis->clock)
+                       clk_put(pThis->clock);
                device_init_wakeup(dev, 0);
                musb_free(pThis);
                return status;
index e5ec6e6e15d38f918afe3093925bb1ddb4a404c2..eeaeb164c41d0caef3b161b5890eec75acdfe9fa 100644 (file)
@@ -159,6 +159,9 @@ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA)
        /* tps65030 seems to consume max 100mA, with maybe 60mA available
         * (measured on one board) for things other than tps and tusb.
         *
+        * Boards sharing the CPU clock with CLKIN will need to prevent
+        * certain idle sleep states while the USB link is active.
+        *
         * REVISIT we could use VBUS to supply only _one_ of { 1.5V, 3.3V }.
         * The actual current usage would be very board-specific.  For now,
         * it's simpler to just use an aggregate (also board-specific).
@@ -168,11 +171,15 @@ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA)
 
        reg = musb_readl(base, TUSB_PRCM_MNGMT);
        if (mA) {
+               if (musb->set_clock)
+                       musb->set_clock(musb->clock, 1);
                musb->is_bus_powered = 1;
                reg |= TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN;
        } else {
                musb->is_bus_powered = 0;
                reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
+               if (musb->set_clock)
+                       musb->set_clock(musb->clock, 0);
        }
        musb_writel(base, TUSB_PRCM_MNGMT, reg);
 
index e1902df003578113c47239c834a8fa740805f61c..da34a6199e2df052196eb11504a26d1a3206e538 100644 (file)
@@ -17,10 +17,15 @@ enum musb_mode {
        MUSB_OTG                /* Mini-AB connector */
 };
 
+struct clk;
+
 struct musb_hdrc_platform_data {
        /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */
        u8              mode;
 
+       /* for clk_get() */
+       const char      *clock;
+
        /* (HOST or OTG) switch VBUS on/off */
        int             (*set_vbus)(struct device *dev, int is_on);
 
@@ -40,6 +45,9 @@ struct musb_hdrc_platform_data {
 
        /* Power the device on or off */
        int             (*set_power)(int state);
+
+       /* Turn device clock on or off */
+       int             (*set_clock)(struct clk *clock, int is_on);
 };