diff options
Diffstat (limited to 'drivers/usb/host/ohci-omap3.c')
-rw-r--r-- | drivers/usb/host/ohci-omap3.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c new file mode 100644 index 000000000..ec15aebe8 --- /dev/null +++ b/drivers/usb/host/ohci-omap3.c @@ -0,0 +1,211 @@ +/* + * ohci-omap3.c - driver for OHCI on OMAP3 and later processors + * + * Bus Glue for OMAP3 USBHOST 3 port OHCI controller + * This controller is also used in later OMAPs and AM35x chips + * + * Copyright (C) 2007-2010 Texas Instruments, Inc. + * Author: Vikram Pandita <vikram.pandita@ti.com> + * Author: Anand Gadiyar <gadiyar@ti.com> + * Author: Keshava Munegowda <keshava_mgowda@ti.com> + * + * Based on ehci-omap.c and some other ohci glue layers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * TODO (last updated Feb 27, 2011): + * - add kernel-doc + */ + +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/usb/otg.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "ohci.h" + +#define DRIVER_DESC "OHCI OMAP3 driver" + +static const char hcd_name[] = "ohci-omap3"; +static struct hc_driver __read_mostly ohci_omap3_hc_driver; + +/* + * configure so an HC device and id are always provided + * always called with process context; sleeping is OK + */ + +/** + * ohci_hcd_omap3_probe - initialize OMAP-based HCDs + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +static int ohci_hcd_omap3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ohci_hcd *ohci; + struct usb_hcd *hcd = NULL; + void __iomem *regs = NULL; + struct resource *res; + int ret; + int irq; + + if (usb_disabled()) + return -ENODEV; + + if (!dev->parent) { + dev_err(dev, "Missing parent device\n"); + return -ENODEV; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "OHCI irq failed\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "UHH OHCI get resource failed\n"); + return -ENOMEM; + } + + regs = ioremap(res->start, resource_size(res)); + if (!regs) { + dev_err(dev, "UHH OHCI ioremap failed\n"); + return -ENOMEM; + } + + /* + * Right now device-tree probed devices don't get dma_mask set. + * 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(dev, DMA_BIT_MASK(32)); + if (ret) + goto err_io; + + ret = -ENODEV; + hcd = usb_create_hcd(&ohci_omap3_hc_driver, dev, + dev_name(dev)); + if (!hcd) { + dev_err(dev, "usb_create_hcd failed\n"); + goto err_io; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->regs = regs; + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + ohci = hcd_to_ohci(hcd); + /* + * RemoteWakeupConnected has to be set explicitly before + * calling ohci_run. The reset value of RWC is 0. + */ + ohci->hc_control = OHCI_CTRL_RWC; + + ret = usb_add_hcd(hcd, irq, 0); + if (ret) { + dev_dbg(dev, "failed to add hcd with err %d\n", ret); + goto err_add_hcd; + } + device_wakeup_enable(hcd->self.controller); + + return 0; + +err_add_hcd: + pm_runtime_put_sync(dev); + usb_put_hcd(hcd); + +err_io: + iounmap(regs); + + return ret; +} + +/* + * may be called without controller electrically present + * may be called with controller, bus, and devices active + */ + +/** + * ohci_hcd_omap3_remove - shutdown processing for OHCI HCDs + * @pdev: USB Host Controller being removed + * + * Reverses the effect of ohci_hcd_omap3_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + */ +static int ohci_hcd_omap3_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_hcd *hcd = dev_get_drvdata(dev); + + iounmap(hcd->regs); + usb_remove_hcd(hcd); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + usb_put_hcd(hcd); + return 0; +} + +static const struct of_device_id omap_ohci_dt_ids[] = { + { .compatible = "ti,ohci-omap3" }, + { } +}; + +MODULE_DEVICE_TABLE(of, omap_ohci_dt_ids); + +static struct platform_driver ohci_hcd_omap3_driver = { + .probe = ohci_hcd_omap3_probe, + .remove = ohci_hcd_omap3_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "ohci-omap3", + .of_match_table = omap_ohci_dt_ids, + }, +}; + +static int __init ohci_omap3_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ohci_init_driver(&ohci_omap3_hc_driver, NULL); + return platform_driver_register(&ohci_hcd_omap3_driver); +} +module_init(ohci_omap3_init); + +static void __exit ohci_omap3_cleanup(void) +{ + platform_driver_unregister(&ohci_hcd_omap3_driver); +} +module_exit(ohci_omap3_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_ALIAS("platform:ohci-omap3"); +MODULE_AUTHOR("Anand Gadiyar <gadiyar@ti.com>"); +MODULE_LICENSE("GPL"); |