linux/drivers/usb/host/ehci-platform.c
<<
>>
Prefs
   1/*
   2 * Generic platform ehci driver
   3 *
   4 * Copyright 2007 Steven Brown <sbrown@cortland.com>
   5 * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de>
   6 *
   7 * Derived from the ohci-ssb driver
   8 * Copyright 2007 Michael Buesch <m@bues.ch>
   9 *
  10 * Derived from the EHCI-PCI driver
  11 * Copyright (c) 2000-2004 by David Brownell
  12 *
  13 * Derived from the ohci-pci driver
  14 * Copyright 1999 Roman Weissgaerber
  15 * Copyright 2000-2002 David Brownell
  16 * Copyright 1999 Linus Torvalds
  17 * Copyright 1999 Gregory P. Smith
  18 *
  19 * Licensed under the GNU/GPL. See COPYING for details.
  20 */
  21#include <linux/dma-mapping.h>
  22#include <linux/err.h>
  23#include <linux/kernel.h>
  24#include <linux/hrtimer.h>
  25#include <linux/io.h>
  26#include <linux/module.h>
  27#include <linux/of.h>
  28#include <linux/platform_device.h>
  29#include <linux/usb.h>
  30#include <linux/usb/hcd.h>
  31#include <linux/usb/ehci_pdriver.h>
  32
  33#include "ehci.h"
  34
  35#define DRIVER_DESC "EHCI generic platform driver"
  36
  37static const char hcd_name[] = "ehci-platform";
  38
  39static int ehci_platform_reset(struct usb_hcd *hcd)
  40{
  41        struct platform_device *pdev = to_platform_device(hcd->self.controller);
  42        struct usb_ehci_pdata *pdata = pdev->dev.platform_data;
  43        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
  44        int retval;
  45
  46        hcd->has_tt = pdata->has_tt;
  47        ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug;
  48        ehci->big_endian_desc = pdata->big_endian_desc;
  49        ehci->big_endian_mmio = pdata->big_endian_mmio;
  50
  51        ehci->caps = hcd->regs + pdata->caps_offset;
  52        retval = ehci_setup(hcd);
  53        if (retval)
  54                return retval;
  55
  56        if (pdata->no_io_watchdog)
  57                ehci->need_io_watchdog = 0;
  58        return 0;
  59}
  60
  61static struct hc_driver __read_mostly ehci_platform_hc_driver;
  62
  63static const struct ehci_driver_overrides platform_overrides __initconst = {
  64        .reset =        ehci_platform_reset,
  65};
  66
  67static struct usb_ehci_pdata ehci_platform_defaults;
  68
  69static int ehci_platform_probe(struct platform_device *dev)
  70{
  71        struct usb_hcd *hcd;
  72        struct resource *res_mem;
  73        struct usb_ehci_pdata *pdata;
  74        int irq;
  75        int err = -ENOMEM;
  76
  77        if (usb_disabled())
  78                return -ENODEV;
  79
  80        /*
  81         * use reasonable defaults so platforms don't have to provide these.
  82         * with DT probing on ARM, none of these are set.
  83         */
  84        if (!dev->dev.platform_data)
  85                dev->dev.platform_data = &ehci_platform_defaults;
  86        if (!dev->dev.dma_mask)
  87                dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
  88        if (!dev->dev.coherent_dma_mask)
  89                dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
  90
  91        pdata = dev->dev.platform_data;
  92
  93        irq = platform_get_irq(dev, 0);
  94        if (irq < 0) {
  95                dev_err(&dev->dev, "no irq provided");
  96                return irq;
  97        }
  98        res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
  99        if (!res_mem) {
 100                dev_err(&dev->dev, "no memory resource provided");
 101                return -ENXIO;
 102        }
 103
 104        if (pdata->power_on) {
 105                err = pdata->power_on(dev);
 106                if (err < 0)
 107                        return err;
 108        }
 109
 110        hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
 111                             dev_name(&dev->dev));
 112        if (!hcd) {
 113                err = -ENOMEM;
 114                goto err_power;
 115        }
 116
 117        hcd->rsrc_start = res_mem->start;
 118        hcd->rsrc_len = resource_size(res_mem);
 119
 120        hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
 121        if (IS_ERR(hcd->regs)) {
 122                err = PTR_ERR(hcd->regs);
 123                goto err_put_hcd;
 124        }
 125        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
 126        if (err)
 127                goto err_put_hcd;
 128
 129        platform_set_drvdata(dev, hcd);
 130
 131        return err;
 132
 133err_put_hcd:
 134        usb_put_hcd(hcd);
 135err_power:
 136        if (pdata->power_off)
 137                pdata->power_off(dev);
 138
 139        return err;
 140}
 141
 142static int ehci_platform_remove(struct platform_device *dev)
 143{
 144        struct usb_hcd *hcd = platform_get_drvdata(dev);
 145        struct usb_ehci_pdata *pdata = dev->dev.platform_data;
 146
 147        usb_remove_hcd(hcd);
 148        usb_put_hcd(hcd);
 149        platform_set_drvdata(dev, NULL);
 150
 151        if (pdata->power_off)
 152                pdata->power_off(dev);
 153
 154        if (pdata == &ehci_platform_defaults)
 155                dev->dev.platform_data = NULL;
 156
 157        return 0;
 158}
 159
 160#ifdef CONFIG_PM
 161
 162static int ehci_platform_suspend(struct device *dev)
 163{
 164        struct usb_hcd *hcd = dev_get_drvdata(dev);
 165        struct usb_ehci_pdata *pdata = dev->platform_data;
 166        struct platform_device *pdev =
 167                container_of(dev, struct platform_device, dev);
 168        bool do_wakeup = device_may_wakeup(dev);
 169        int ret;
 170
 171        ret = ehci_suspend(hcd, do_wakeup);
 172
 173        if (pdata->power_suspend)
 174                pdata->power_suspend(pdev);
 175
 176        return ret;
 177}
 178
 179static int ehci_platform_resume(struct device *dev)
 180{
 181        struct usb_hcd *hcd = dev_get_drvdata(dev);
 182        struct usb_ehci_pdata *pdata = dev->platform_data;
 183        struct platform_device *pdev =
 184                container_of(dev, struct platform_device, dev);
 185
 186        if (pdata->power_on) {
 187                int err = pdata->power_on(pdev);
 188                if (err < 0)
 189                        return err;
 190        }
 191
 192        ehci_resume(hcd, false);
 193        return 0;
 194}
 195
 196#else /* !CONFIG_PM */
 197#define ehci_platform_suspend   NULL
 198#define ehci_platform_resume    NULL
 199#endif /* CONFIG_PM */
 200
 201static const struct of_device_id vt8500_ehci_ids[] = {
 202        { .compatible = "via,vt8500-ehci", },
 203        { .compatible = "wm,prizm-ehci", },
 204        {}
 205};
 206
 207static const struct platform_device_id ehci_platform_table[] = {
 208        { "ehci-platform", 0 },
 209        { }
 210};
 211MODULE_DEVICE_TABLE(platform, ehci_platform_table);
 212
 213static const struct dev_pm_ops ehci_platform_pm_ops = {
 214        .suspend        = ehci_platform_suspend,
 215        .resume         = ehci_platform_resume,
 216};
 217
 218static struct platform_driver ehci_platform_driver = {
 219        .id_table       = ehci_platform_table,
 220        .probe          = ehci_platform_probe,
 221        .remove         = ehci_platform_remove,
 222        .shutdown       = usb_hcd_platform_shutdown,
 223        .driver         = {
 224                .owner  = THIS_MODULE,
 225                .name   = "ehci-platform",
 226                .pm     = &ehci_platform_pm_ops,
 227                .of_match_table = of_match_ptr(vt8500_ehci_ids),
 228        }
 229};
 230
 231static int __init ehci_platform_init(void)
 232{
 233        if (usb_disabled())
 234                return -ENODEV;
 235
 236        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 237
 238        ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);
 239        return platform_driver_register(&ehci_platform_driver);
 240}
 241module_init(ehci_platform_init);
 242
 243static void __exit ehci_platform_cleanup(void)
 244{
 245        platform_driver_unregister(&ehci_platform_driver);
 246}
 247module_exit(ehci_platform_cleanup);
 248
 249MODULE_DESCRIPTION(DRIVER_DESC);
 250MODULE_AUTHOR("Hauke Mehrtens");
 251MODULE_AUTHOR("Alan Stern");
 252MODULE_LICENSE("GPL");
 253