diff options
Diffstat (limited to 'drivers/usb/host/ohci-omap.c')
| -rw-r--r-- | drivers/usb/host/ohci-omap.c | 319 |
1 files changed, 151 insertions, 168 deletions
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index c4c4babd476..c923cafcaca 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -4,7 +4,7 @@ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> * (C) Copyright 2000-2005 David Brownell * (C) Copyright 2002 Hewlett-Packard Company - * + * * OMAP Bus Glue * * Modified for OMAP by Tony Lindgren <tony@atomide.com> @@ -14,20 +14,30 @@ * This file is licenced under the GPL. */ -#include <linux/signal.h> /* IRQF_DISABLED */ +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/io.h> #include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb/otg.h> #include <linux/platform_device.h> -#include <linux/clk.h> +#include <linux/signal.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "ohci.h" -#include <asm/hardware.h> #include <asm/io.h> #include <asm/mach-types.h> -#include <asm/arch/mux.h> -#include <asm/arch/irqs.h> -#include <asm/arch/gpio.h> -#include <asm/arch/fpga.h> -#include <asm/arch/usb.h> +#include <mach/mux.h> + +#include <mach/hardware.h> +#include <mach/irqs.h> +#include <mach/usb.h> /* OMAP-1510 OHCI has its own MMU for DMA */ @@ -41,13 +51,10 @@ #define OMAP1510_LB_MMU_RAM_H 0xfffec234 #define OMAP1510_LB_MMU_RAM_L 0xfffec238 - -#ifndef CONFIG_ARCH_OMAP -#error "This file is OMAP bus glue. CONFIG_OMAP must be defined." -#endif +#define DRIVER_DESC "OHCI OMAP driver" #ifdef CONFIG_TPS65010 -#include <asm/arch/tps65010.h> +#include <linux/i2c/tps65010.h> #else #define LOW 0 @@ -66,15 +73,21 @@ extern int usb_disabled(void); extern int ocpi_enable(void); static struct clk *usb_host_ck; +static struct clk *usb_dc_ck; + +static const char hcd_name[] = "ohci-omap"; +static struct hc_driver __read_mostly ohci_omap_hc_driver; 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); } } @@ -86,15 +99,15 @@ 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*/)), + __raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL) + | ((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*/)), + __raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL) + & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), INNOVATOR_FPGA_CAM_USB_CONTROL); else if (machine_is_omap_osk()) tps65010_set_gpio_out_value(GPIO1, HIGH); @@ -103,6 +116,7 @@ static int omap_ohci_transceiver_power(int on) return 0; } +#ifdef CONFIG_ARCH_OMAP15XX /* * OMAP-1510 specific Local Bus clock on/off */ @@ -121,8 +135,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) @@ -130,7 +144,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 */ @@ -138,7 +152,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); @@ -152,20 +166,28 @@ 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 static void start_hnp(struct ohci_hcd *ohci) { - const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1; + struct usb_hcd *hcd = ohci_to_hcd(ohci); + const unsigned port = hcd->self.otg_port - 1; unsigned long flags; + u32 l; - otg_start_hnp(ohci->transceiver); + otg_start_hnp(hcd->phy->otg); local_irq_save(flags); - ohci->transceiver->state = OTG_STATE_A_SUSPEND; + hcd->phy->state = OTG_STATE_A_SUSPEND; writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]); - OTG_CTRL_REG &= ~OTG_A_BUSREQ; + l = omap_readl(OTG_CTRL); + l &= ~OTG_A_BUSREQ; + omap_writel(l, OTG_CTRL); local_irq_restore(flags); } @@ -173,57 +195,65 @@ 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_reset(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 = dev_get_platdata(hcd->self.controller); 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; + hcd->self.otg_port = config->otg; /* default/minimum OTG power budget: 8 mA */ - ohci_to_hcd(ohci)->power_budget = 8; + hcd->power_budget = 8; } /* boards can use OTG transceivers in non-OTG modes */ need_transceiver = need_transceiver || machine_is_omap_h2() || machine_is_omap_h3(); - if (cpu_is_omap16xx()) - ocpi_enable(); + /* XXX OMAP16xx only */ + if (config->ocpi_enable) + config->ocpi_enable(); -#ifdef CONFIG_ARCH_OMAP_OTG +#ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = otg_get_transceiver(); - if (ohci->transceiver) { - int status = otg_set_host(ohci->transceiver, + hcd->phy = usb_get_phy(USB_PHY_TYPE_USB2); + if (!IS_ERR_OR_NULL(hcd->phy)) { + int status = otg_set_host(hcd->phy->otg, &ohci_to_hcd(ohci)->self); - dev_dbg(&pdev->dev, "init %s transceiver, status %d\n", - ohci->transceiver->label, status); + dev_dbg(hcd->self.controller, "init %s phy, status %d\n", + hcd->phy->label, status); if (status) { - if (ohci->transceiver) - put_device(ohci->transceiver->dev); + usb_put_phy(hcd->phy); return status; } } else { - dev_err(&pdev->dev, "can't find transceiver\n"); + dev_err(hcd->self.controller, "can't find phy\n"); return -ENODEV; } + ohci->start_hnp = start_hnp; } #endif omap_ohci_clock_power(1); - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { omap_1510_local_bus_power(1); omap_1510_local_bus_init(); } - if ((ret = ohci_init(ohci)) < 0) + ret = ohci_setup(hcd); + if (ret < 0) return ret; + if (config->otg || config->rwc) { + ohci->hc_control = OHCI_CTRL_RWC; + writel(OHCI_CTRL_RWC, &ohci->regs->control); + } + /* board-specific power switching and overcurrent support */ if (machine_is_omap_osk() || machine_is_omap_innovator()) { u32 rh = roothub_a (ohci); @@ -239,14 +269,18 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) /* gpio9 for overcurrent detction */ omap_cfg_reg(W8_1610_GPIO9); - omap_request_gpio(9); - omap_set_gpio_direction(9, 1 /* IN */); + gpio_request(9, "OHCI overcurrent"); + gpio_direction_input(9); /* for paranoia's sake: disable USB.PUEN */ omap_cfg_reg(W4_USB_HIGHZ); } ohci_writel(ohci, rh, &ohci->regs->roothub.a); - distrust_firmware = 0; + ohci->flags &= ~OHCI_QUIRK_HUB_POWER; + } 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 */ @@ -260,21 +294,8 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) return 0; } -static void omap_stop_hc(struct platform_device *pdev) -{ - dev_dbg(&pdev->dev, "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() @@ -283,30 +304,40 @@ 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, 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", + dev_err(&pdev->dev, "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"); + dev_err(&pdev->dev, "invalid resource type\n"); return -ENODEV; } - usb_host_ck = clk_get(0, "usb_hhc_ck"); + usb_host_ck = clk_get(&pdev->dev, "usb_hhc_ck"); if (IS_ERR(usb_host_ck)) return PTR_ERR(usb_host_ck); - hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id); + if (!cpu_is_omap15xx()) + usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck"); + else + usb_dc_ck = clk_get(&pdev->dev, "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, dev_name(&pdev->dev)); if (!hcd) { retval = -ENOMEM; goto err0; @@ -320,30 +351,32 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, goto err1; } - hcd->regs = (void __iomem *) (int) IO_ADDRESS(hcd->rsrc_start); - - ohci = hcd_to_ohci(hcd); - ohci_hcd_init(ohci); - - retval = omap_start_hc(ohci, pdev); - if (retval < 0) + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_err(&pdev->dev, "can't ioremap OHCI HCD\n"); + retval = -ENOMEM; goto err2; + } irq = platform_get_irq(pdev, 0); if (irq < 0) { retval = -ENXIO; - goto err2; + goto err3; } - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); - if (retval == 0) - return retval; + retval = usb_add_hcd(hcd, irq, 0); + if (retval) + goto err3; - omap_stop_hc(pdev); + device_wakeup_enable(hcd->self.controller); + return 0; +err3: + iounmap(hcd->regs); 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; } @@ -359,85 +392,28 @@ 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) { + dev_dbg(hcd->self.controller, "stopping USB Controller\n"); usb_remove_hcd(hcd); + omap_ohci_clock_power(0); + if (!IS_ERR_OR_NULL(hcd->phy)) { + (void) otg_set_host(hcd->phy->otg, 0); + usb_put_phy(hcd->phy); + } if (machine_is_omap_osk()) - omap_free_gpio(9); - omap_stop_hc(pdev); + gpio_free(9); + iounmap(hcd->regs); 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 -ohci_omap_start (struct usb_hcd *hcd) -{ - struct omap_usb_config *config; - struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ret; - - config = hcd->self.controller->platform_data; - if (config->otg || config->rwc) - writel(OHCI_CTRL_RWC, &ohci->regs->control); - - if ((ret = ohci_run (ohci)) < 0) { - dev_err(hcd->self.controller, "can't start\n"); - ohci_stop (hcd); - return ret; - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static const struct hc_driver ohci_omap_hc_driver = { - .description = hcd_name, - .product_desc = "OMAP OHCI", - .hcd_priv_size = sizeof(struct ohci_hcd), - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .start = ohci_omap_start, - .stop = ohci_stop, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ohci_urb_enqueue, - .urb_dequeue = ohci_urb_dequeue, - .endpoint_disable = ohci_endpoint_disable, - - /* - * scheduling support - */ - .get_frame_number = ohci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ohci_hub_status_data, - .hub_control = ohci_hub_control, -#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 platform_device *dev) { return usb_hcd_omap_probe(&ohci_omap_hc_driver, dev); @@ -446,14 +422,8 @@ static int ohci_hcd_omap_drv_probe(struct platform_device *dev) static int ohci_hcd_omap_drv_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); - struct ohci_hcd *ohci = hcd_to_ohci (hcd); usb_hcd_omap_remove(hcd, dev); - if (ohci->transceiver) { - (void) otg_set_host(ohci->transceiver, 0); - put_device(ohci->transceiver->dev); - } - platform_set_drvdata(dev, NULL); return 0; } @@ -462,31 +432,36 @@ static int ohci_hcd_omap_drv_remove(struct platform_device *dev) #ifdef CONFIG_PM -static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message) +static int ohci_omap_suspend(struct platform_device *pdev, pm_message_t message) { - struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev)); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + bool do_wakeup = device_may_wakeup(&pdev->dev); + int ret; if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; + omap_ohci_clock_power(0); - ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; - dev->power.power_state = PMSG_SUSPEND; - return 0; + return ret; } static int ohci_omap_resume(struct platform_device *dev) { - struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev)); + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; omap_ohci_clock_power(1); - dev->power.power_state = PMSG_ON; - usb_hcd_resume_root_hub(dev_get_drvdata(dev)); + ohci_resume(hcd, false); return 0; } @@ -500,6 +475,7 @@ static int ohci_omap_resume(struct platform_device *dev) 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, @@ -510,22 +486,29 @@ static struct platform_driver ohci_hcd_omap_driver = { }, }; -static int __init ohci_hcd_omap_init (void) +static const struct ohci_driver_overrides omap_overrides __initconst = { + .product_desc = "OMAP OHCI", + .reset = ohci_omap_reset +}; + +static int __init ohci_omap_init(void) { - printk (KERN_DEBUG "%s: " DRIVER_INFO " (OMAP)\n", hcd_name); if (usb_disabled()) return -ENODEV; - pr_debug("%s: block sizes: ed %Zd td %Zd\n", hcd_name, - sizeof (struct ed), sizeof (struct td)); + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + ohci_init_driver(&ohci_omap_hc_driver, &omap_overrides); return platform_driver_register(&ohci_hcd_omap_driver); } +module_init(ohci_omap_init); -static void __exit ohci_hcd_omap_cleanup (void) +static void __exit ohci_omap_cleanup(void) { platform_driver_unregister(&ohci_hcd_omap_driver); } +module_exit(ohci_omap_cleanup); -module_init (ohci_hcd_omap_init); -module_exit (ohci_hcd_omap_cleanup); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_ALIAS("platform:ohci"); +MODULE_LICENSE("GPL"); |
