Commit 4648dd52 authored by Tony Lindgren's avatar Tony Lindgren

musb_hdrc: Add board specific external clock handling

On N800, if osc_ck is disabled by PM, USB will stop working.
Signed-off-by: default avatarTony 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: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
parent 1ffb59bf
...@@ -10,12 +10,14 @@ ...@@ -10,12 +10,14 @@
*/ */
#include <linux/types.h> #include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/usb/musb.h> #include <linux/usb/musb.h>
#include <asm/arch/gpmc.h> #include <asm/arch/gpmc.h>
#include <asm/arch/gpio.h> #include <asm/arch/gpio.h>
#include <asm/arch/pm.h>
#define TUSB_ASYNC_CS 1 #define TUSB_ASYNC_CS 1
#define TUSB_SYNC_CS 4 #define TUSB_SYNC_CS 4
...@@ -23,6 +25,7 @@ ...@@ -23,6 +25,7 @@
#define GPIO_TUSB_ENABLE 0 #define GPIO_TUSB_ENABLE 0
static int tusb_set_power(int state); static int tusb_set_power(int state);
static int tusb_set_clock(struct clk *osc_ck, int state);
#if defined(CONFIG_USB_MUSB_OTG) #if defined(CONFIG_USB_MUSB_OTG)
# define BOARD_MODE MUSB_OTG # define BOARD_MODE MUSB_OTG
...@@ -36,7 +39,9 @@ static struct musb_hdrc_platform_data tusb_data = { ...@@ -36,7 +39,9 @@ static struct musb_hdrc_platform_data tusb_data = {
.mode = BOARD_MODE, .mode = BOARD_MODE,
.multipoint = 1, .multipoint = 1,
.set_power = tusb_set_power, .set_power = tusb_set_power,
.set_clock = tusb_set_clock,
.min_power = 25, /* x2 = 50 mA drawn from VBUS as peripheral */ .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) ...@@ -71,6 +76,29 @@ static int tusb_set_power(int state)
return retval; 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) void __init n800_usb_init(void)
{ {
int ret = 0; int ret = 0;
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/clk.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb_gadget.h> #include <linux/usb_gadget.h>
...@@ -402,6 +403,8 @@ struct musb { ...@@ -402,6 +403,8 @@ struct musb {
u8 board_mode; /* enum musb_mode */ u8 board_mode; /* enum musb_mode */
int (*board_set_power)(int state); int (*board_set_power)(int state);
int (*set_clock)(struct clk *clk, int is_active);
u8 min_power; /* vbus for periph, in mA/2 */ u8 min_power; /* vbus for periph, in mA/2 */
/* active means connected and not suspended */ /* active means connected and not suspended */
......
...@@ -97,7 +97,6 @@ ...@@ -97,7 +97,6 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -792,6 +791,10 @@ static void musb_shutdown(struct platform_device *pdev) ...@@ -792,6 +791,10 @@ static void musb_shutdown(struct platform_device *pdev)
spin_lock_irqsave(&musb->Lock, flags); spin_lock_irqsave(&musb->Lock, flags);
musb_platform_disable(musb); musb_platform_disable(musb);
musb_generic_disable(musb); musb_generic_disable(musb);
if (musb->clock) {
clk_put(musb->clock);
musb->clock = NULL;
}
spin_unlock_irqrestore(&musb->Lock, flags); spin_unlock_irqrestore(&musb->Lock, flags);
/* FIXME power down */ /* FIXME power down */
...@@ -1712,8 +1715,23 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) ...@@ -1712,8 +1715,23 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
spin_lock_init(&pThis->Lock); spin_lock_init(&pThis->Lock);
pThis->board_mode = plat->mode; pThis->board_mode = plat->mode;
pThis->board_set_power = plat->set_power; pThis->board_set_power = plat->set_power;
pThis->set_clock = plat->set_clock;
pThis->min_power = plat->min_power; 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 */ /* assume vbus is off */
/* platform adjusts pThis->pRegs and pThis->isr if needed, /* platform adjusts pThis->pRegs and pThis->isr if needed,
...@@ -1825,6 +1843,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) ...@@ -1825,6 +1843,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb_debug_create("driver/musb_hdrc", pThis); musb_debug_create("driver/musb_hdrc", pThis);
else { else {
fail: fail:
if (pThis->clock)
clk_put(pThis->clock);
device_init_wakeup(dev, 0); device_init_wakeup(dev, 0);
musb_free(pThis); musb_free(pThis);
return status; return status;
......
...@@ -159,6 +159,9 @@ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA) ...@@ -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 /* tps65030 seems to consume max 100mA, with maybe 60mA available
* (measured on one board) for things other than tps and tusb. * (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 }. * 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, * The actual current usage would be very board-specific. For now,
* it's simpler to just use an aggregate (also board-specific). * 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) ...@@ -168,11 +171,15 @@ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA)
reg = musb_readl(base, TUSB_PRCM_MNGMT); reg = musb_readl(base, TUSB_PRCM_MNGMT);
if (mA) { if (mA) {
if (musb->set_clock)
musb->set_clock(musb->clock, 1);
musb->is_bus_powered = 1; musb->is_bus_powered = 1;
reg |= TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN; reg |= TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN;
} else { } else {
musb->is_bus_powered = 0; musb->is_bus_powered = 0;
reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN); 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); musb_writel(base, TUSB_PRCM_MNGMT, reg);
......
...@@ -17,10 +17,15 @@ enum musb_mode { ...@@ -17,10 +17,15 @@ enum musb_mode {
MUSB_OTG /* Mini-AB connector */ MUSB_OTG /* Mini-AB connector */
}; };
struct clk;
struct musb_hdrc_platform_data { struct musb_hdrc_platform_data {
/* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */ /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */
u8 mode; u8 mode;
/* for clk_get() */
const char *clock;
/* (HOST or OTG) switch VBUS on/off */ /* (HOST or OTG) switch VBUS on/off */
int (*set_vbus)(struct device *dev, int is_on); int (*set_vbus)(struct device *dev, int is_on);
...@@ -40,6 +45,9 @@ struct musb_hdrc_platform_data { ...@@ -40,6 +45,9 @@ struct musb_hdrc_platform_data {
/* Power the device on or off */ /* Power the device on or off */
int (*set_power)(int state); int (*set_power)(int state);
/* Turn device clock on or off */
int (*set_clock)(struct clk *clock, int is_on);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment