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 @@
*/
#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;
......
......@@ -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 */
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
};
......
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