Commit 141891a6 authored by Tony Lindgren's avatar Tony Lindgren

[PATCH] ARM: OMAP: USB clock changes from Juha

This patch from Juha Yrjola adds two missing USB clocks.

Please note that CONFIG_OMAP_RESET_CLOCKS may still need to be
disabled in .config for USB to work on some platforms (OSK).
parent 824711c6
......@@ -50,6 +50,7 @@
#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>
......@@ -1306,6 +1307,23 @@ static void pullup_disable(struct omap_udc *udc)
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.
......@@ -1326,10 +1344,22 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active)
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;
}
......@@ -2039,7 +2069,6 @@ omap_udc_iso_irq(int irq, void *_dev, struct pt_regs *r)
/*-------------------------------------------------------------------------*/
static struct omap_udc *udc;
int usb_gadget_register_driver (struct usb_gadget_driver *driver)
{
......@@ -2082,6 +2111,9 @@ 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);
......@@ -2117,6 +2149,8 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
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);
......@@ -2131,6 +2165,9 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *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);
......@@ -2147,6 +2184,8 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
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;
}
......@@ -2720,6 +2759,8 @@ static int __init omap_udc_probe(struct platform_device *pdev)
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,
......@@ -2729,6 +2770,16 @@ static int __init omap_udc_probe(struct platform_device *pdev)
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" : "");
......@@ -2851,6 +2902,12 @@ bad_on_1710:
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);
......@@ -2871,8 +2928,17 @@ cleanup1:
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;
}
......@@ -2900,6 +2966,13 @@ static int __exit omap_udc_remove(struct platform_device *pdev)
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);
......
......@@ -175,6 +175,9 @@ struct omap_udc {
unsigned ep0_reset_config:1;
unsigned ep0_setup:1;
struct completion *done;
struct clk *dc_clk;
struct clk *hhc_clk;
unsigned clk_requested:1;
};
/*-------------------------------------------------------------------------*/
......
......@@ -66,15 +66,20 @@ extern int usb_disabled(void);
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);
}
}
......@@ -280,6 +285,43 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
/* 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()
......@@ -311,6 +353,13 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
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;
......@@ -330,20 +379,32 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
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;
}
......@@ -369,18 +430,21 @@ void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
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);
......
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