diff options
Diffstat (limited to 'drivers/usb/host/ohci-platform.c')
-rw-r--r-- | drivers/usb/host/ohci-platform.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c new file mode 100644 index 00000000000..ec5c6791c8b --- /dev/null +++ b/drivers/usb/host/ohci-platform.c @@ -0,0 +1,194 @@ +/* + * Generic platform ohci driver + * + * Copyright 2007 Michael Buesch <m@bues.ch> + * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de> + * + * Derived from the OCHI-SSB driver + * Derived from the OHCI-PCI driver + * Copyright 1999 Roman Weissgaerber + * Copyright 2000-2002 David Brownell + * Copyright 1999 Linus Torvalds + * Copyright 1999 Gregory P. Smith + * + * Licensed under the GNU/GPL. See COPYING for details. + */ +#include <linux/platform_device.h> +#include <linux/usb/ohci_pdriver.h> + +static int ohci_platform_reset(struct usb_hcd *hcd) +{ + struct platform_device *pdev = to_platform_device(hcd->self.controller); + struct usb_ohci_pdata *pdata = pdev->dev.platform_data; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int err; + + if (pdata->big_endian_desc) + ohci->flags |= OHCI_QUIRK_BE_DESC; + if (pdata->big_endian_mmio) + ohci->flags |= OHCI_QUIRK_BE_MMIO; + if (pdata->no_big_frame_no) + ohci->flags |= OHCI_QUIRK_FRAME_NO; + + ohci_hcd_init(ohci); + err = ohci_init(ohci); + + return err; +} + +static int ohci_platform_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int err; + + err = ohci_run(ohci); + if (err < 0) { + ohci_err(ohci, "can't start\n"); + ohci_stop(hcd); + } + + return err; +} + +static const struct hc_driver ohci_platform_hc_driver = { + .description = hcd_name, + .product_desc = "Generic Platform OHCI Controller", + .hcd_priv_size = sizeof(struct ohci_hcd), + + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + + .reset = ohci_platform_reset, + .start = ohci_platform_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + .get_frame_number = ohci_get_frame, + + .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 __devinit ohci_platform_probe(struct platform_device *dev) +{ + struct usb_hcd *hcd; + struct resource *res_mem; + int irq; + int err = -ENOMEM; + + BUG_ON(!dev->dev.platform_data); + + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(dev, 0); + if (irq < 0) { + pr_err("no irq provieded"); + return irq; + } + + res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res_mem) { + pr_err("no memory recourse provieded"); + return -ENXIO; + } + + hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, + dev_name(&dev->dev)); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res_mem->start; + hcd->rsrc_len = resource_size(res_mem); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_err("controller already in use"); + err = -EBUSY; + goto err_put_hcd; + } + + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) + goto err_release_region; + err = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (err) + goto err_iounmap; + + platform_set_drvdata(dev, hcd); + + return err; + +err_iounmap: + iounmap(hcd->regs); +err_release_region: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_put_hcd: + usb_put_hcd(hcd); + return err; +} + +static int __devexit ohci_platform_remove(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + platform_set_drvdata(dev, NULL); + + return 0; +} + +#ifdef CONFIG_PM + +static int ohci_platform_suspend(struct device *dev) +{ + return 0; +} + +static int ohci_platform_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + + ohci_finish_controller_resume(hcd); + return 0; +} + +#else /* !CONFIG_PM */ +#define ohci_platform_suspend NULL +#define ohci_platform_resume NULL +#endif /* CONFIG_PM */ + +static const struct platform_device_id ohci_platform_table[] = { + { "ohci-platform", 0 }, + { } +}; +MODULE_DEVICE_TABLE(platform, ohci_platform_table); + +static const struct dev_pm_ops ohci_platform_pm_ops = { + .suspend = ohci_platform_suspend, + .resume = ohci_platform_resume, +}; + +static struct platform_driver ohci_platform_driver = { + .id_table = ohci_platform_table, + .probe = ohci_platform_probe, + .remove = __devexit_p(ohci_platform_remove), + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = "ohci-platform", + .pm = &ohci_platform_pm_ops, + } +}; |