From 141891a65384b6d0b899344c74938eb3e443785f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 3 Jan 2006 09:39:24 -0800 Subject: [PATCH] [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). --- drivers/usb/gadget/omap_udc.c | 75 ++++++++++++++++++++++++++++++++++- drivers/usb/gadget/omap_udc.h | 3 ++ drivers/usb/host/ohci-omap.c | 70 ++++++++++++++++++++++++++++++-- 3 files changed, 144 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 16136388ff0..7a6ba6ad7ac 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -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); diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index 652ee462734..1dc398bb9ab 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -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; }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index ae97252eeac..a0ad788e000 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -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); -- 2.41.1