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        if (pdata->pre_setup) {
  52                retval = pdata->pre_setup(hcd);
  53                if (retval < 0)
  54                        return retval;
  55        }
  56
  57        ehci->caps = hcd->regs + pdata->caps_offset;
  58        retval = ehci_setup(hcd);
  59        if (retval)
  60                return retval;
  61
  62        if (pdata->no_io_watchdog)
  63                ehci->need_io_watchdog = 0;
  64        return 0;
  65}
  66
  67static struct hc_driver __read_mostly ehci_platform_hc_driver;
  68
  69static const struct ehci_driver_overrides platform_overrides __initconst = {
  70        .reset =        ehci_platform_reset,
  71};
  72
  73static struct usb_ehci_pdata ehci_platform_defaults;
  74
  75static int ehci_platform_probe(struct platform_device *dev)
  76{
  77        struct usb_hcd *hcd;
  78        struct resource *res_mem;
  79        struct usb_ehci_pdata *pdata;
  80        int irq;
  81        int err = -ENOMEM;
  82
  83        if (usb_disabled())
  84                return -ENODEV;
  85
  86        /*
  87         * use reasonable defaults so platforms don't have to provide these.
  88         * with DT probing on ARM, none of these are set.
  89         */
  90        if (!dev->dev.platform_data)
  91                dev->dev.platform_data = &ehci_platform_defaults;
  92        if (!dev->dev.dma_mask)
  93                dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
  94        if (!dev->dev.coherent_dma_mask)
  95                dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
  96
  97        pdata = dev->dev.platform_data;
  98
  99        irq = platform_get_irq(dev, 0);
 100        if (irq < 0) {
 101                dev_err(&dev->dev, "no irq provided");
 102                return irq;
 103        }
 104        res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
 105        if (!res_mem) {
 106                dev_err(&dev->dev, "no memory resource provided");
 107                return -ENXIO;
 108        }
 109
 110        if (pdata->power_on) {
 111                err = pdata->power_on(dev);
 112                if (err < 0)
 113                        return err;
 114        }
 115
 116        hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
 117                             dev_name(&dev->dev));
 118        if (!hcd) {
 119                err = -ENOMEM;
 120                goto err_power;
 121        }
 122
 123        hcd->rsrc_start = res_mem->start;
 124        hcd->rsrc_len = resource_size(res_mem);
 125
 126        hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
 127        if (IS_ERR(hcd->regs)) {
 128                err = PTR_ERR(hcd->regs);
 129                goto err_put_hcd;
 130        }
 131        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
 132        if (err)
 133                goto err_put_hcd;
 134
 135        platform_set_drvdata(dev, hcd);
 136
 137        return err;
 138
 139err_put_hcd:
 140        usb_put_hcd(hcd);
 141err_power:
 142        if (pdata->power_off)
 143                pdata->power_off(dev);
 144
 145        return err;
 146}
 147
 148static int ehci_platform_remove(struct platform_device *dev)
 149{
 150        struct usb_hcd *hcd = platform_get_drvdata(dev);
 151        struct usb_ehci_pdata *pdata = dev->dev.platform_data;
 152
 153        usb_remove_hcd(hcd);
 154        usb_put_hcd(hcd);
 155        platform_set_drvdata(dev, NULL);
 156
 157        if (pdata->power_off)
 158                pdata->power_off(dev);
 159
 160        if (pdata == &ehci_platform_defaults)
 161                dev->dev.platform_data = NULL;
 162
 163        return 0;
 164}
 165
 166#ifdef CONFIG_PM_SLEEP
 167static int ehci_platform_suspend(struct device *dev)
 168{
 169        struct usb_hcd *hcd = dev_get_drvdata(dev);
 170        struct usb_ehci_pdata *pdata = dev->platform_data;
 171        struct platform_device *pdev =
 172                container_of(dev, struct platform_device, dev);
 173        bool do_wakeup = device_may_wakeup(dev);
 174        int ret;
 175
 176        ret = ehci_suspend(hcd, do_wakeup);
 177        if (ret)
 178                return ret;
 179
 180        if (pdata->power_suspend)
 181                pdata->power_suspend(pdev);
 182
 183        return ret;
 184}
 185
 186static int ehci_platform_resume(struct device *dev)
 187{
 188        struct usb_hcd *hcd = dev_get_drvdata(dev);
 189        struct usb_ehci_pdata *pdata = dev->platform_data;
 190        struct platform_device *pdev =
 191                container_of(dev, struct platform_device, dev);
 192
 193        if (pdata->power_on) {
 194                int err = pdata->power_on(pdev);
 195                if (err < 0)
 196                        return err;
 197        }
 198
 199        ehci_resume(hcd, false);
 200        return 0;
 201}
 202#endif /* CONFIG_PM_SLEEP */
 203
 204static const struct of_device_id vt8500_ehci_ids[] = {
 205        { .compatible = "via,vt8500-ehci", },
 206        { .compatible = "wm,prizm-ehci", },
 207        {}
 208};
 209
 210static const struct platform_device_id ehci_platform_table[] = {
 211        { "ehci-platform", 0 },
 212        { }
 213};
 214MODULE_DEVICE_TABLE(platform, ehci_platform_table);
 215
 216static SIMPLE_DEV_PM_OPS(ehci_platform_pm_ops, ehci_platform_suspend,
 217        ehci_platform_resume);
 218
 219static struct platform_driver ehci_platform_driver = {
 220        .id_table       = ehci_platform_table,
 221        .probe          = ehci_platform_probe,
 222        .remove         = ehci_platform_remove,
 223        .shutdown       = usb_hcd_platform_shutdown,
 224        .driver         = {
 225                .owner  = THIS_MODULE,
 226                .name   = "ehci-platform",
 227                .pm     = &ehci_platform_pm_ops,
 228                .of_match_table = of_match_ptr(vt8500_ehci_ids),
 229        }
 230};
 231
 232static int __init ehci_platform_init(void)
 233{
 234        if (usb_disabled())
 235                return -ENODEV;
 236
 237        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 238
 239        ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);
 240        return platform_driver_register(&ehci_platform_driver);
 241}
 242module_init(ehci_platform_init);
 243
 244static void __exit ehci_platform_cleanup(void)
 245{
 246        platform_driver_unregister(&ehci_platform_driver);
 247}
 248module_exit(ehci_platform_cleanup);
 249
 250MODULE_DESCRIPTION(DRIVER_DESC);
 251MODULE_AUTHOR("Hauke Mehrtens");
 252MODULE_AUTHOR("Alan Stern");
 253MODULE_LICENSE("GPL");
 254