X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fohci-omap.c;h=27be1f9368853925da47e2b64cd18fb3ec59088e;hb=refs%2Fheads%2Fvserver;hp=8aab5907afe9ad442012ec4c00aecd46dd88c50b;hpb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;p=linux-2.6.git diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 8aab5907a..27be1f936 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -4,7 +4,7 @@ * (C) Copyright 1999 Roman Weissgaerber * (C) Copyright 2000-2005 David Brownell * (C) Copyright 2002 Hewlett-Packard Company - * + * * OMAP Bus Glue * * Modified for OMAP by Tony Lindgren @@ -14,17 +14,20 @@ * This file is licenced under the GPL. */ +#include /* IRQF_DISABLED */ +#include +#include +#include + #include #include #include -#include #include #include #include #include #include -#include /* OMAP-1510 OHCI has its own MMU for DMA */ @@ -63,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_enable(usb_dc_ck); clk_enable(usb_host_ck); /* guesstimate for T5 == 1x 32K clock + APLL lock time */ udelay(100); } else { clk_disable(usb_host_ck); + clk_disable(usb_dc_ck); } } @@ -84,14 +92,14 @@ static int omap_ohci_transceiver_power(int on) if (on) { if (machine_is_omap_innovator() && cpu_is_omap1510()) fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) - | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), + | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), INNOVATOR_FPGA_CAM_USB_CONTROL); else if (machine_is_omap_osk()) tps65010_set_gpio_out_value(GPIO1, LOW); } else { if (machine_is_omap_innovator() && cpu_is_omap1510()) fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) - & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), + & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), INNOVATOR_FPGA_CAM_USB_CONTROL); else if (machine_is_omap_osk()) tps65010_set_gpio_out_value(GPIO1, HIGH); @@ -100,6 +108,7 @@ static int omap_ohci_transceiver_power(int on) return 0; } +#ifdef CONFIG_ARCH_OMAP15XX /* * OMAP-1510 specific Local Bus clock on/off */ @@ -118,8 +127,8 @@ static int omap_1510_local_bus_power(int on) /* * OMAP-1510 specific Local Bus initialization * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE. - * See also arch/mach-omap/memory.h for __virt_to_dma() and - * __dma_to_virt() which need to match with the physical + * See also arch/mach-omap/memory.h for __virt_to_dma() and + * __dma_to_virt() which need to match with the physical * Local Bus address below. */ static int omap_1510_local_bus_init(void) @@ -127,7 +136,7 @@ static int omap_1510_local_bus_init(void) unsigned int tlb; unsigned long lbaddr, physaddr; - omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4, + omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4, OMAP1510_LB_CLOCK_DIV); /* Configure the Local Bus MMU table */ @@ -135,7 +144,7 @@ static int omap_1510_local_bus_init(void) lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET; physaddr = tlb * 0x00100000 + PHYS_OFFSET; omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H); - omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, + omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, OMAP1510_LB_MMU_CAM_L); omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H); omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L); @@ -149,6 +158,10 @@ static int omap_1510_local_bus_init(void) return 0; } +#else +#define omap_1510_local_bus_power(x) {} +#define omap_1510_local_bus_init() {} +#endif #ifdef CONFIG_USB_OTG @@ -170,18 +183,19 @@ static void start_hnp(struct ohci_hcd *ohci) /*-------------------------------------------------------------------------*/ -static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) +static int ohci_omap_init(struct usb_hcd *hcd) { - struct omap_usb_config *config = pdev->dev.platform_data; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct omap_usb_config *config = hcd->self.controller->platform_data; int need_transceiver = (config->otg != 0); int ret; - dev_dbg(&pdev->dev, "starting USB Controller\n"); + dev_dbg(hcd->self.controller, "starting USB Controller\n"); if (config->otg) { ohci_to_hcd(ohci)->self.otg_port = config->otg; /* default/minimum OTG power budget: 8 mA */ - ohci->power_budget = 8; + ohci_to_hcd(ohci)->power_budget = 8; } /* boards can use OTG transceivers in non-OTG modes */ @@ -197,7 +211,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) if (ohci->transceiver) { int status = otg_set_host(ohci->transceiver, &ohci_to_hcd(ohci)->self); - dev_dbg(&pdev->dev, "init %s transceiver, status %d\n", + dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", ohci->transceiver->label, status); if (status) { if (ohci->transceiver) @@ -205,7 +219,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) return status; } } else { - dev_err(&pdev->dev, "can't find transceiver\n"); + dev_err(hcd->self.controller, "can't find transceiver\n"); return -ENODEV; } } @@ -230,7 +244,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) /* TPS2045 switch for internal transceiver (port 1) */ if (machine_is_omap_osk()) { - ohci->power_budget = 250; + ohci_to_hcd(ohci)->power_budget = 250; rh &= ~RH_A_NOCP; @@ -244,6 +258,10 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) } ohci_writel(ohci, rh, &ohci->regs->roothub.a); distrust_firmware = 0; + } else if (machine_is_nokia770()) { + /* We require a self-powered hub, which should have + * plenty of power. */ + ohci_to_hcd(ohci)->power_budget = 0; } /* FIXME khubd hub requests should manage power switching */ @@ -257,21 +275,15 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) return 0; } -static void omap_stop_hc(struct platform_device *pdev) +static void ohci_omap_stop(struct usb_hcd *hcd) { - dev_dbg(&pdev->dev, "stopping USB Controller\n"); + dev_dbg(hcd->self.controller, "stopping USB Controller\n"); omap_ohci_clock_power(0); } /*-------------------------------------------------------------------------*/ -void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *); - -/* configure so an HC device and id are always provided */ -/* always called with process context; sleeping is OK */ - - /** * usb_hcd_omap_probe - initialize OMAP-based HCDs * Context: !in_interrupt() @@ -280,20 +292,20 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *); * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. */ -int usb_hcd_omap_probe (const struct hc_driver *driver, +static int usb_hcd_omap_probe (const struct hc_driver *driver, struct platform_device *pdev) { - int retval; + int retval, irq; struct usb_hcd *hcd = 0; struct ohci_hcd *ohci; if (pdev->num_resources != 2) { - printk(KERN_ERR "hcd probe: invalid num_resources: %i\n", + printk(KERN_ERR "hcd probe: invalid num_resources: %i\n", pdev->num_resources); return -ENODEV; } - if (pdev->resource[0].flags != IORESOURCE_MEM + if (pdev->resource[0].flags != IORESOURCE_MEM || pdev->resource[1].flags != IORESOURCE_IRQ) { printk(KERN_ERR "hcd probe: invalid resource type\n"); return -ENODEV; @@ -303,6 +315,17 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, if (IS_ERR(usb_host_ck)) return PTR_ERR(usb_host_ck); + if (!cpu_is_omap1510()) + usb_dc_ck = clk_get(0, "usb_dc_ck"); + else + usb_dc_ck = clk_get(0, "lb_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; @@ -322,20 +345,30 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, ohci = hcd_to_ohci(hcd); ohci_hcd_init(ohci); - retval = omap_start_hc(ohci, pdev); - if (retval < 0) + host_initialized = 0; + host_enabled = 1; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + retval = -ENXIO; goto err2; + } + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + if (retval) + goto err2; + + host_initialized = 1; - retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), SA_INTERRUPT); - if (retval == 0) - return retval; + if (!host_enabled) + omap_ohci_clock_power(0); - omap_stop_hc(pdev); + return 0; 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; } @@ -351,31 +384,41 @@ err0: * Reverses the effect of usb_hcd_omap_probe(), first invoking * the HCD's stop() method. It is always called from a thread * context, normally "rmmod", "apmd", or something similar. - * */ -void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) +static inline void +usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) { + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + usb_remove_hcd(hcd); + if (ohci->transceiver) { + (void) otg_set_host(ohci->transceiver, 0); + put_device(ohci->transceiver->dev); + } if (machine_is_omap_osk()) omap_free_gpio(9); - 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) + if (config->otg || config->rwc) { + ohci->hc_control = OHCI_CTRL_RWC; writel(OHCI_CTRL_RWC, &ohci->regs->control); + } if ((ret = ohci_run (ohci)) < 0) { dev_err(hcd->self.controller, "can't start\n"); @@ -401,8 +444,10 @@ static const struct hc_driver ohci_omap_hc_driver = { /* * basic lifecycle operations */ + .reset = ohci_omap_init, .start = ohci_omap_start, - .stop = ohci_stop, + .stop = ohci_omap_stop, + .shutdown = ohci_shutdown, /* * managing i/o requests and associated device resources @@ -421,33 +466,27 @@ static const struct hc_driver ohci_omap_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, -#ifdef CONFIG_USB_SUSPEND - .hub_suspend = ohci_hub_suspend, - .hub_resume = ohci_hub_resume, + .hub_irq_enable = ohci_rhsc_enable, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, #endif .start_port_reset = ohci_start_port_reset, }; /*-------------------------------------------------------------------------*/ -static int ohci_hcd_omap_drv_probe(struct device *dev) +static int ohci_hcd_omap_drv_probe(struct platform_device *dev) { - return usb_hcd_omap_probe(&ohci_omap_hc_driver, - to_platform_device(dev)); + return usb_hcd_omap_probe(&ohci_omap_hc_driver, dev); } -static int ohci_hcd_omap_drv_remove(struct device *dev) +static int ohci_hcd_omap_drv_remove(struct platform_device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct usb_hcd *hcd = platform_get_drvdata(dev); - usb_hcd_omap_remove(hcd, pdev); - if (ohci->transceiver) { - (void) otg_set_host(ohci->transceiver, 0); - put_device(ohci->transceiver->dev); - } - dev_set_drvdata(dev, NULL); + usb_hcd_omap_remove(hcd, dev); + platform_set_drvdata(dev, NULL); return 0; } @@ -456,71 +495,32 @@ static int ohci_hcd_omap_drv_remove(struct device *dev) #ifdef CONFIG_PM -/* states match PCI usage, always suspending the root hub except that - * 4 ~= D3cold (ACPI D3) with clock off (resume sees reset). - * - * FIXME: above comment is not right, and code is wrong, too :-(. - */ - -static int ohci_omap_suspend(struct device *dev, pm_message_t state, u32 level) +static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message) { - struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); - int status = -EINVAL; + struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev)); - if (level != SUSPEND_POWER_DOWN) - return 0; - if (state <= dev->power.power_state) - return 0; + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; - dev_dbg(dev, "suspend to %d\n", state); - down(&ohci_to_hcd(ohci)->self.root_hub->serialize); - status = ohci_hub_suspend(ohci_to_hcd(ohci)); - if (status == 0) { - if (state >= 4) { - omap_ohci_clock_power(0); - ohci_to_hcd(ohci)->self.root_hub->state = - USB_STATE_SUSPENDED; - state = 4; - } - ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; - dev->power.power_state = state; - } - up(&ohci_to_hcd(ohci)->self.root_hub->serialize); - return status; + omap_ohci_clock_power(0); + ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; + dev->dev.power.power_state = PMSG_SUSPEND; + return 0; } -static int ohci_omap_resume(struct device *dev, u32 level) +static int ohci_omap_resume(struct platform_device *dev) { - struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); - int status = 0; + struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev)); - if (level != RESUME_POWER_ON) - return 0; + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; - switch (dev->power.power_state) { - case 0: - break; - case 4: - if (time_before(jiffies, ohci->next_statechange)) - msleep(5); - ohci->next_statechange = jiffies; - omap_ohci_clock_power(1); - /* FALLTHROUGH */ - default: - dev_dbg(dev, "resume from %d\n", dev->power.power_state); -#ifdef CONFIG_USB_SUSPEND - /* get extra cleanup even if remote wakeup isn't in use */ - status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub); -#else - down(&ohci_to_hcd(ohci)->self.root_hub->serialize); - status = ohci_hub_resume(ohci_to_hcd(ohci)); - up(&ohci_to_hcd(ohci)->self.root_hub->serialize); -#endif - if (status == 0) - dev->power.power_state = 0; - break; - } - return status; + omap_ohci_clock_power(1); + dev->dev.power.power_state = PMSG_ON; + usb_hcd_resume_root_hub(platform_get_drvdata(dev)); + return 0; } #endif @@ -530,15 +530,18 @@ static int ohci_omap_resume(struct device *dev, u32 level) /* * Driver definition to register with the OMAP bus */ -static struct device_driver ohci_hcd_omap_driver = { - .name = "ohci", - .bus = &platform_bus_type, +static struct platform_driver ohci_hcd_omap_driver = { .probe = ohci_hcd_omap_drv_probe, .remove = ohci_hcd_omap_drv_remove, + .shutdown = usb_hcd_platform_shutdown, #ifdef CONFIG_PM .suspend = ohci_omap_suspend, .resume = ohci_omap_resume, #endif + .driver = { + .owner = THIS_MODULE, + .name = "ohci", + }, }; static int __init ohci_hcd_omap_init (void) @@ -550,12 +553,12 @@ static int __init ohci_hcd_omap_init (void) pr_debug("%s: block sizes: ed %Zd td %Zd\n", hcd_name, sizeof (struct ed), sizeof (struct td)); - return driver_register(&ohci_hcd_omap_driver); + return platform_driver_register(&ohci_hcd_omap_driver); } static void __exit ohci_hcd_omap_cleanup (void) { - driver_unregister(&ohci_hcd_omap_driver); + platform_driver_unregister(&ohci_hcd_omap_driver); } module_init (ohci_hcd_omap_init);