From: Tony Lindgren Date: Thu, 1 Feb 2007 23:38:35 +0000 (-0800) Subject: musb_hdrc: Add board specific external clock handling X-Git-Tag: v2.6.21-omap1~55 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=4648dd526b8f7b7ff18af5cc06c5b16636fa7e97;p=linux-2.6-omap-h63xx.git musb_hdrc: Add board specific external clock handling On N800, if osc_ck is disabled by PM, USB will stop working. Signed-off-by: Tony Lindgren 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 --- diff --git a/arch/arm/mach-omap2/board-n800-usb.c b/arch/arm/mach-omap2/board-n800-usb.c index e43690ddb53..f6cc15cb793 100644 --- a/arch/arm/mach-omap2/board-n800-usb.c +++ b/arch/arm/mach-omap2/board-n800-usb.c @@ -10,12 +10,14 @@ */ #include -#include #include #include +#include +#include #include #include #include +#include #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; diff --git a/drivers/usb/musb/musbdefs.h b/drivers/usb/musb/musbdefs.h index d059be34ff0..b39c719121c 100644 --- a/drivers/usb/musb/musbdefs.h +++ b/drivers/usb/musb/musbdefs.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -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 */ diff --git a/drivers/usb/musb/plat_uds.c b/drivers/usb/musb/plat_uds.c index e29e825d7d3..909511509d5 100644 --- a/drivers/usb/musb/plat_uds.c +++ b/drivers/usb/musb/plat_uds.c @@ -97,7 +97,6 @@ #include #include #include -#include #include @@ -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; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index e5ec6e6e15d..eeaeb164c41 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -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); diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index e1902df0035..da34a6199e2 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -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); };