#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/mach-types.h>
+#include <asm/hardware/clock.h>
#include <asm/arch/dma.h>
#include <asm/arch/usb.h>
UDC_SYSCON1_REG &= ~UDC_PULLUP_EN;
}
+static struct omap_udc *udc;
+
+static void omap_udc_enable_clock(int enable)
+{
+ if (udc == NULL || udc->dc_clk == NULL || udc->hhc_clk == NULL)
+ return;
+
+ if (enable) {
+ clk_use(udc->dc_clk);
+ clk_use(udc->hhc_clk);
+ udelay(100);
+ } else {
+ clk_unuse(udc->hhc_clk);
+ clk_unuse(udc->dc_clk);
+ }
+}
+
/*
* Called by whatever detects VBUS sessions: external transceiver
* driver, or maybe GPIO0 VBUS IRQ. May request 48 MHz clock.
else
FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510;
}
+ if (udc->dc_clk != NULL && is_active) {
+ if (!udc->clk_requested) {
+ omap_udc_enable_clock(1);
+ udc->clk_requested = 1;
+ }
+ }
if (can_pullup(udc))
pullup_enable(udc);
else
pullup_disable(udc);
+ if (udc->dc_clk != NULL && !is_active) {
+ if (udc->clk_requested) {
+ omap_udc_enable_clock(0);
+ udc->clk_requested = 0;
+ }
+ }
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
/*-------------------------------------------------------------------------*/
-static struct omap_udc *udc;
int usb_gadget_register_driver (struct usb_gadget_driver *driver)
{
udc->gadget.dev.driver = &driver->driver;
spin_unlock_irqrestore(&udc->lock, flags);
+ if (udc->dc_clk != NULL)
+ omap_udc_enable_clock(1);
+
status = driver->bind (&udc->gadget);
if (status) {
DBG("bind to %s --> %d\n", driver->driver.name, status);
omap_vbus_session(&udc->gadget, 1);
done:
+ if (udc->dc_clk != NULL)
+ omap_udc_enable_clock(0);
return status;
}
EXPORT_SYMBOL(usb_gadget_register_driver);
if (!driver || driver != udc->driver)
return -EINVAL;
+ if (udc->dc_clk != NULL)
+ omap_udc_enable_clock(1);
+
if (machine_is_omap_innovator() || machine_is_omap_osk())
omap_vbus_session(&udc->gadget, 0);
udc->gadget.dev.driver = NULL;
udc->driver = NULL;
+ if (udc->dc_clk != NULL)
+ omap_udc_enable_clock(0);
DBG("unregistered driver '%s'\n", driver->driver.name);
return status;
}
struct otg_transceiver *xceiv = NULL;
const char *type = NULL;
struct omap_usb_config *config = pdev->dev.platform_data;
+ struct clk *dc_clk;
+ struct clk *hhc_clk;
/* NOTE: "knows" the order of the resources! */
if (!request_mem_region(pdev->resource[0].start,
return -EBUSY;
}
+ if (cpu_is_omap16xx()) {
+ dc_clk = clk_get(dev, "usb_dc_ck");
+ hhc_clk = clk_get(dev, "usb_hhc_ck");
+ BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk));
+ /* can't use omap_udc_enable_clock yet */
+ clk_use(dc_clk);
+ clk_use(hhc_clk);
+ udelay(100);
+ }
+
INFO("OMAP UDC rev %d.%d%s\n",
UDC_REV_REG >> 4, UDC_REV_REG & 0xf,
config->otg ? ", Mini-AB" : "");
goto cleanup3;
}
#endif
+ if (cpu_is_omap16xx()) {
+ udc->dc_clk = dc_clk;
+ udc->hhc_clk = hhc_clk;
+ clk_unuse(hhc_clk);
+ clk_unuse(dc_clk);
+ }
create_proc_file();
device_add(&udc->gadget.dev);
cleanup0:
if (xceiv)
put_device(xceiv->dev);
+
+ if (cpu_is_omap16xx()) {
+ clk_unuse(hhc_clk);
+ clk_unuse(dc_clk);
+ clk_put(hhc_clk);
+ clk_put(dc_clk);
+ }
+
release_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1);
+
return status;
}
free_irq(pdev->resource[2].start, udc);
free_irq(pdev->resource[1].start, udc);
+ if (udc->dc_clk) {
+ if (udc->clk_requested)
+ omap_udc_enable_clock(0);
+ clk_put(udc->hhc_clk);
+ clk_put(udc->dc_clk);
+ }
+
release_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1);
extern int ocpi_enable(void);
static struct clk *usb_host_ck;
+static struct clk *usb_dc_ck;
+static int host_enabled;
+static int host_initialized;
static void omap_ohci_clock_power(int on)
{
if (on) {
+ clk_use(usb_dc_ck);
clk_use(usb_host_ck);
/* guesstimate for T5 == 1x 32K clock + APLL lock time */
udelay(100);
} else {
clk_unuse(usb_host_ck);
+ clk_unuse(usb_dc_ck);
}
}
/* always called with process context; sleeping is OK */
+int ohci_omap_host_enable(struct usb_bus *host, int enable)
+{
+ struct usb_hcd *hcd;
+ struct ohci_hcd *ohci;
+ int retval;
+
+ if (host_enabled == enable)
+ return 0;
+
+ host_enabled = enable;
+
+ if (!host_initialized)
+ return 0;
+
+ hcd = (struct usb_hcd *)host->hcpriv;
+ ohci = hcd_to_ohci(hcd);
+ if (enable) {
+ omap_ohci_clock_power(1);
+ if ((retval = ohci_init(ohci)) < 0) {
+ dev_err(hcd->self.controller, "init error %d\n",
+ retval);
+ return retval;
+ }
+ if ((retval = hcd->driver->start(hcd)) < 0) {
+ dev_err(hcd->self.controller, "startup error %d\n",
+ retval);
+ return retval;
+ }
+ } else {
+ usb_disconnect(&hcd->self.root_hub);
+ hcd->driver->stop(hcd);
+ omap_ohci_clock_power(0);
+ }
+
+ return 0;
+}
+
/**
* usb_hcd_omap_probe - initialize OMAP-based HCDs
* Context: !in_interrupt()
if (IS_ERR(usb_host_ck))
return PTR_ERR(usb_host_ck);
+ usb_dc_ck = clk_get(0, "usb_dc_ck");
+ if (IS_ERR(usb_dc_ck)) {
+ clk_put(usb_host_ck);
+ return PTR_ERR(usb_dc_ck);
+ }
+
+
hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
if (!hcd) {
retval = -ENOMEM;
ohci = hcd_to_ohci(hcd);
ohci_hcd_init(ohci);
+ host_initialized = 0;
+ host_enabled = 1;
+
retval = omap_start_hc(ohci, pdev);
if (retval < 0)
goto err2;
retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), SA_INTERRUPT);
- if (retval == 0)
- return retval;
+ if (retval)
+ goto err3;
+
+ host_initialized = 1;
+
+ if (!host_enabled)
+ omap_ohci_clock_power(0);
+
+ return 0;
+err3:
omap_stop_hc(pdev);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
err0:
+ clk_put(usb_dc_ck);
clk_put(usb_host_ck);
return retval;
}
omap_stop_hc(pdev);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
+ clk_put(usb_dc_ck);
clk_put(usb_host_ck);
}
/*-------------------------------------------------------------------------*/
-static int __devinit
+static int
ohci_omap_start (struct usb_hcd *hcd)
{
struct omap_usb_config *config;
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
+ if (!host_enabled)
+ return 0;
config = hcd->self.controller->platform_data;
if (config->otg || config->rwc)
writel(OHCI_CTRL_RWC, &ohci->regs->control);