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>
*/
#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
#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
.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",
};
/*
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;
#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>
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 */
#include <linux/list.h>
#include <linux/kobject.h>
#include <linux/platform_device.h>
-#include <linux/clk.h>
#include <asm/io.h>
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 */
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,
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;
/* 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).
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);
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);
/* 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);
};