diff options
Diffstat (limited to 'drivers/usb/host/ohci-pxa27x.c')
-rw-r--r-- | drivers/usb/host/ohci-pxa27x.c | 258 |
1 files changed, 141 insertions, 117 deletions
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 9b7435f..93371a2 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -19,27 +19,15 @@ * This file is licenced under the GPL. */ -#include <linux/clk.h> #include <linux/device.h> -#include <linux/dma-mapping.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> +#include <linux/signal.h> +#include <linux/platform_device.h> +#include <linux/clk.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> +#include <mach/hardware.h> #include <linux/platform_data/usb-ohci-pxa27x.h> #include <linux/platform_data/usb-pxa3xx-ulpi.h> -#include <linux/platform_device.h> -#include <linux/signal.h> -#include <linux/usb.h> -#include <linux/usb/hcd.h> -#include <linux/usb/otg.h> - -#include <mach/hardware.h> - -#include "ohci.h" - -#define DRIVER_DESC "OHCI PXA27x/PXA3x driver" /* * UHC: USB Host Controller (OHCI-like) register definitions @@ -113,16 +101,16 @@ #define PXA_UHC_MAX_PORTNUM 3 -static const char hcd_name[] = "ohci-pxa27x"; - -static struct hc_driver __read_mostly ohci_pxa27x_hc_driver; - struct pxa27x_ohci { + /* must be 1st member here for hcd_to_ohci() to work */ + struct ohci_hcd ohci; + + struct device *dev; struct clk *clk; void __iomem *mmio_base; }; -#define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)(hcd_to_ohci(hcd)->priv) +#define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)hcd_to_ohci(hcd) /* PMM_NPS_MODE -- PMM Non-power switching mode @@ -134,10 +122,10 @@ struct pxa27x_ohci { PMM_PERPORT_MODE -- PMM per port switching mode Ports are powered individually. */ -static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode) +static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *ohci, int mode) { - uint32_t uhcrhda = __raw_readl(pxa_ohci->mmio_base + UHCRHDA); - uint32_t uhcrhdb = __raw_readl(pxa_ohci->mmio_base + UHCRHDB); + uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA); + uint32_t uhcrhdb = __raw_readl(ohci->mmio_base + UHCRHDB); switch (mode) { case PMM_NPS_MODE: @@ -161,18 +149,20 @@ static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode) uhcrhda |= RH_A_NPS; } - __raw_writel(uhcrhda, pxa_ohci->mmio_base + UHCRHDA); - __raw_writel(uhcrhdb, pxa_ohci->mmio_base + UHCRHDB); + __raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA); + __raw_writel(uhcrhdb, ohci->mmio_base + UHCRHDB); return 0; } +extern int usb_disabled(void); + /*-------------------------------------------------------------------------*/ -static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci, +static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci, struct pxaohci_platform_data *inf) { - uint32_t uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR); - uint32_t uhcrhda = __raw_readl(pxa_ohci->mmio_base + UHCRHDA); + uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR); + uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA); if (inf->flags & ENABLE_PORT1) uhchr &= ~UHCHR_SSEP1; @@ -204,17 +194,17 @@ static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci, uhcrhda |= UHCRHDA_POTPGT(inf->power_on_delay / 2); } - __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR); - __raw_writel(uhcrhda, pxa_ohci->mmio_base + UHCRHDA); + __raw_writel(uhchr, ohci->mmio_base + UHCHR); + __raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA); } -static inline void pxa27x_reset_hc(struct pxa27x_ohci *pxa_ohci) +static inline void pxa27x_reset_hc(struct pxa27x_ohci *ohci) { - uint32_t uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR); + uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR); - __raw_writel(uhchr | UHCHR_FHR, pxa_ohci->mmio_base + UHCHR); + __raw_writel(uhchr | UHCHR_FHR, ohci->mmio_base + UHCHR); udelay(11); - __raw_writel(uhchr & ~UHCHR_FHR, pxa_ohci->mmio_base + UHCHR); + __raw_writel(uhchr & ~UHCHR_FHR, ohci->mmio_base + UHCHR); } #ifdef CONFIG_PXA27x @@ -223,26 +213,25 @@ extern void pxa27x_clear_otgph(void); #define pxa27x_clear_otgph() do {} while (0) #endif -static int pxa27x_start_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev) +static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev) { int retval = 0; struct pxaohci_platform_data *inf; uint32_t uhchr; - struct usb_hcd *hcd = dev_get_drvdata(dev); inf = dev_get_platdata(dev); - clk_prepare_enable(pxa_ohci->clk); + clk_prepare_enable(ohci->clk); - pxa27x_reset_hc(pxa_ohci); + pxa27x_reset_hc(ohci); - uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR) | UHCHR_FSBIR; - __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR); + uhchr = __raw_readl(ohci->mmio_base + UHCHR) | UHCHR_FSBIR; + __raw_writel(uhchr, ohci->mmio_base + UHCHR); - while (__raw_readl(pxa_ohci->mmio_base + UHCHR) & UHCHR_FSBIR) + while (__raw_readl(ohci->mmio_base + UHCHR) & UHCHR_FSBIR) cpu_relax(); - pxa27x_setup_hc(pxa_ohci, inf); + pxa27x_setup_hc(ohci, inf); if (inf->init) retval = inf->init(dev); @@ -251,39 +240,38 @@ static int pxa27x_start_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev) return retval; if (cpu_is_pxa3xx()) - pxa3xx_u2d_start_hc(&hcd->self); + pxa3xx_u2d_start_hc(&ohci_to_hcd(&ohci->ohci)->self); - uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR) & ~UHCHR_SSE; - __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR); - __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, pxa_ohci->mmio_base + UHCHIE); + uhchr = __raw_readl(ohci->mmio_base + UHCHR) & ~UHCHR_SSE; + __raw_writel(uhchr, ohci->mmio_base + UHCHR); + __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, ohci->mmio_base + UHCHIE); /* Clear any OTG Pin Hold */ pxa27x_clear_otgph(); return 0; } -static void pxa27x_stop_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev) +static void pxa27x_stop_hc(struct pxa27x_ohci *ohci, struct device *dev) { struct pxaohci_platform_data *inf; - struct usb_hcd *hcd = dev_get_drvdata(dev); uint32_t uhccoms; inf = dev_get_platdata(dev); if (cpu_is_pxa3xx()) - pxa3xx_u2d_stop_hc(&hcd->self); + pxa3xx_u2d_stop_hc(&ohci_to_hcd(&ohci->ohci)->self); if (inf->exit) inf->exit(dev); - pxa27x_reset_hc(pxa_ohci); + pxa27x_reset_hc(ohci); /* Host Controller Reset */ - uhccoms = __raw_readl(pxa_ohci->mmio_base + UHCCOMS) | 0x01; - __raw_writel(uhccoms, pxa_ohci->mmio_base + UHCCOMS); + uhccoms = __raw_readl(ohci->mmio_base + UHCCOMS) | 0x01; + __raw_writel(uhccoms, ohci->mmio_base + UHCCOMS); udelay(10); - clk_disable_unprepare(pxa_ohci->clk); + clk_disable_unprepare(ohci->clk); } #ifdef CONFIG_OF @@ -299,7 +287,6 @@ static int ohci_pxa_of_init(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct pxaohci_platform_data *pdata; u32 tmp; - int ret; if (!np) return 0; @@ -308,9 +295,10 @@ static int ohci_pxa_of_init(struct platform_device *pdev) * Since shared usb code relies on it, set it here for now. * Once we have dma capability bindings this can go away. */ - ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -368,8 +356,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device int retval, irq; struct usb_hcd *hcd; struct pxaohci_platform_data *inf; - struct pxa27x_ohci *pxa_ohci; - struct ohci_hcd *ohci; + struct pxa27x_ohci *ohci; struct resource *r; struct clk *usb_clk; @@ -422,31 +409,29 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device } /* initialize "struct pxa27x_ohci" */ - pxa_ohci = to_pxa27x_ohci(hcd); - pxa_ohci->clk = usb_clk; - pxa_ohci->mmio_base = (void __iomem *)hcd->regs; + ohci = (struct pxa27x_ohci *)hcd_to_ohci(hcd); + ohci->dev = &pdev->dev; + ohci->clk = usb_clk; + ohci->mmio_base = (void __iomem *)hcd->regs; - retval = pxa27x_start_hc(pxa_ohci, &pdev->dev); - if (retval < 0) { + if ((retval = pxa27x_start_hc(ohci, &pdev->dev)) < 0) { pr_debug("pxa27x_start_hc failed"); goto err3; } /* Select Power Management Mode */ - pxa27x_ohci_select_pmm(pxa_ohci, inf->port_mode); + pxa27x_ohci_select_pmm(ohci, inf->port_mode); if (inf->power_budget) hcd->power_budget = inf->power_budget; - /* The value of NDP in roothub_a is incorrect on this hardware */ - ohci = hcd_to_ohci(hcd); - ohci->num_ports = 3; + ohci_hcd_init(hcd_to_ohci(hcd)); retval = usb_add_hcd(hcd, irq, 0); if (retval == 0) return retval; - pxa27x_stop_hc(pxa_ohci, &pdev->dev); + pxa27x_stop_hc(ohci, &pdev->dev); err3: iounmap(hcd->regs); err2: @@ -474,18 +459,88 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device */ void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev) { - struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd); + struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); usb_remove_hcd(hcd); - pxa27x_stop_hc(pxa_ohci, &pdev->dev); + pxa27x_stop_hc(ohci, &pdev->dev); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - clk_put(pxa_ohci->clk); usb_put_hcd(hcd); + clk_put(ohci->clk); } /*-------------------------------------------------------------------------*/ +static int +ohci_pxa27x_start (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + + ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci); + + /* The value of NDP in roothub_a is incorrect on this hardware */ + ohci->num_ports = 3; + + if ((ret = ohci_init(ohci)) < 0) + return ret; + + if ((ret = ohci_run (ohci)) < 0) { + dev_err(hcd->self.controller, "can't start %s", + hcd->self.bus_name); + ohci_stop (hcd); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ohci_pxa27x_hc_driver = { + .description = hcd_name, + .product_desc = "PXA27x OHCI", + .hcd_priv_size = sizeof(struct pxa27x_ohci), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .start = ohci_pxa27x_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + /* + * 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_pxa27x_drv_probe(struct platform_device *pdev) { pr_debug ("In ohci_hcd_pxa27x_drv_probe"); @@ -508,42 +563,32 @@ static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev) static int ohci_hcd_pxa27x_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - bool do_wakeup = device_may_wakeup(dev); - int ret; - + struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); - if (time_before(jiffies, ohci->next_statechange)) + if (time_before(jiffies, ohci->ohci.next_statechange)) msleep(5); - ohci->next_statechange = jiffies; + ohci->ohci.next_statechange = jiffies; - ret = ohci_suspend(hcd, do_wakeup); - if (ret) - return ret; - - pxa27x_stop_hc(pxa_ohci, dev); - return ret; + pxa27x_stop_hc(ohci, dev); + return 0; } static int ohci_hcd_pxa27x_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd); + struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); struct pxaohci_platform_data *inf = dev_get_platdata(dev); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); int status; - if (time_before(jiffies, ohci->next_statechange)) + if (time_before(jiffies, ohci->ohci.next_statechange)) msleep(5); - ohci->next_statechange = jiffies; + ohci->ohci.next_statechange = jiffies; - status = pxa27x_start_hc(pxa_ohci, dev); - if (status < 0) + if ((status = pxa27x_start_hc(ohci, dev)) < 0) return status; /* Select Power Management Mode */ - pxa27x_ohci_select_pmm(pxa_ohci, inf->port_mode); + pxa27x_ohci_select_pmm(ohci, inf->port_mode); ohci_resume(hcd, false); return 0; @@ -555,6 +600,9 @@ static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = { }; #endif +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:pxa27x-ohci"); + static struct platform_driver ohci_hcd_pxa27x_driver = { .probe = ohci_hcd_pxa27x_drv_probe, .remove = ohci_hcd_pxa27x_drv_remove, @@ -569,27 +617,3 @@ static struct platform_driver ohci_hcd_pxa27x_driver = { }, }; -static const struct ohci_driver_overrides pxa27x_overrides __initconst = { - .extra_priv_size = sizeof(struct pxa27x_ohci), -}; - -static int __init ohci_pxa27x_init(void) -{ - if (usb_disabled()) - return -ENODEV; - - pr_info("%s: " DRIVER_DESC "\n", hcd_name); - ohci_init_driver(&ohci_pxa27x_hc_driver, &pxa27x_overrides); - return platform_driver_register(&ohci_hcd_pxa27x_driver); -} -module_init(ohci_pxa27x_init); - -static void __exit ohci_pxa27x_cleanup(void) -{ - platform_driver_unregister(&ohci_hcd_pxa27x_driver); -} -module_exit(ohci_pxa27x_cleanup); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pxa27x-ohci"); |