linux/drivers/usb/host/ehci-spear.c
<<
>>
Prefs
   1/*
   2* Driver for EHCI HCD on SPEAr SOC
   3*
   4* Copyright (C) 2010 ST Micro Electronics,
   5* Deepak Sikri <deepak.sikri@st.com>
   6*
   7* Based on various ehci-*.c drivers
   8*
   9* This file is subject to the terms and conditions of the GNU General Public
  10* License. See the file COPYING in the main directory of this archive for
  11* more details.
  12*/
  13
  14#include <linux/clk.h>
  15#include <linux/dma-mapping.h>
  16#include <linux/io.h>
  17#include <linux/jiffies.h>
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20#include <linux/of.h>
  21#include <linux/platform_device.h>
  22#include <linux/pm.h>
  23#include <linux/usb.h>
  24#include <linux/usb/hcd.h>
  25
  26#include "ehci.h"
  27
  28#define DRIVER_DESC "EHCI SPEAr driver"
  29
  30static const char hcd_name[] = "SPEAr-ehci";
  31
  32struct spear_ehci {
  33        struct clk *clk;
  34};
  35
  36#define to_spear_ehci(hcd)      (struct spear_ehci *)(hcd_to_ehci(hcd)->priv)
  37
  38static struct hc_driver __read_mostly ehci_spear_hc_driver;
  39
  40#ifdef CONFIG_PM_SLEEP
  41static int ehci_spear_drv_suspend(struct device *dev)
  42{
  43        struct usb_hcd *hcd = dev_get_drvdata(dev);
  44        bool do_wakeup = device_may_wakeup(dev);
  45
  46        return ehci_suspend(hcd, do_wakeup);
  47}
  48
  49static int ehci_spear_drv_resume(struct device *dev)
  50{
  51        struct usb_hcd *hcd = dev_get_drvdata(dev);
  52
  53        ehci_resume(hcd, false);
  54        return 0;
  55}
  56#endif /* CONFIG_PM_SLEEP */
  57
  58static SIMPLE_DEV_PM_OPS(ehci_spear_pm_ops, ehci_spear_drv_suspend,
  59                ehci_spear_drv_resume);
  60
  61static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
  62{
  63        struct usb_hcd *hcd ;
  64        struct spear_ehci *sehci;
  65        struct resource *res;
  66        struct clk *usbh_clk;
  67        const struct hc_driver *driver = &ehci_spear_hc_driver;
  68        int irq, retval;
  69
  70        if (usb_disabled())
  71                return -ENODEV;
  72
  73        irq = platform_get_irq(pdev, 0);
  74        if (irq < 0) {
  75                retval = irq;
  76                goto fail;
  77        }
  78
  79        /*
  80         * Right now device-tree probed devices don't get dma_mask set.
  81         * Since shared usb code relies on it, set it here for now.
  82         * Once we have dma capability bindings this can go away.
  83         */
  84        if (!pdev->dev.dma_mask)
  85                pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
  86        if (!pdev->dev.coherent_dma_mask)
  87                pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
  88
  89        usbh_clk = devm_clk_get(&pdev->dev, NULL);
  90        if (IS_ERR(usbh_clk)) {
  91                dev_err(&pdev->dev, "Error getting interface clock\n");
  92                retval = PTR_ERR(usbh_clk);
  93                goto fail;
  94        }
  95
  96        hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
  97        if (!hcd) {
  98                retval = -ENOMEM;
  99                goto fail;
 100        }
 101
 102        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 103        if (!res) {
 104                retval = -ENODEV;
 105                goto err_put_hcd;
 106        }
 107
 108        hcd->rsrc_start = res->start;
 109        hcd->rsrc_len = resource_size(res);
 110        if (!devm_request_mem_region(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len,
 111                                driver->description)) {
 112                retval = -EBUSY;
 113                goto err_put_hcd;
 114        }
 115
 116        hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len);
 117        if (hcd->regs == NULL) {
 118                dev_dbg(&pdev->dev, "error mapping memory\n");
 119                retval = -ENOMEM;
 120                goto err_put_hcd;
 121        }
 122
 123        sehci = to_spear_ehci(hcd);
 124        sehci->clk = usbh_clk;
 125
 126        /* registers start at offset 0x0 */
 127        hcd_to_ehci(hcd)->caps = hcd->regs;
 128
 129        clk_prepare_enable(sehci->clk);
 130        retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
 131        if (retval)
 132                goto err_stop_ehci;
 133
 134        return retval;
 135
 136err_stop_ehci:
 137        clk_disable_unprepare(sehci->clk);
 138err_put_hcd:
 139        usb_put_hcd(hcd);
 140fail:
 141        dev_err(&pdev->dev, "init fail, %d\n", retval);
 142
 143        return retval ;
 144}
 145
 146static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
 147{
 148        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 149        struct spear_ehci *sehci = to_spear_ehci(hcd);
 150
 151        if (!hcd)
 152                return 0;
 153        if (in_interrupt())
 154                BUG();
 155        usb_remove_hcd(hcd);
 156
 157        if (sehci->clk)
 158                clk_disable_unprepare(sehci->clk);
 159        usb_put_hcd(hcd);
 160
 161        return 0;
 162}
 163
 164static struct of_device_id spear_ehci_id_table[] = {
 165        { .compatible = "st,spear600-ehci", },
 166        { },
 167};
 168
 169static struct platform_driver spear_ehci_hcd_driver = {
 170        .probe          = spear_ehci_hcd_drv_probe,
 171        .remove         = spear_ehci_hcd_drv_remove,
 172        .shutdown       = usb_hcd_platform_shutdown,
 173        .driver         = {
 174                .name = "spear-ehci",
 175                .bus = &platform_bus_type,
 176                .pm = &ehci_spear_pm_ops,
 177                .of_match_table = of_match_ptr(spear_ehci_id_table),
 178        }
 179};
 180
 181static const struct ehci_driver_overrides spear_overrides __initdata = {
 182        .extra_priv_size = sizeof(struct spear_ehci),
 183};
 184
 185static int __init ehci_spear_init(void)
 186{
 187        if (usb_disabled())
 188                return -ENODEV;
 189
 190        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 191
 192        ehci_init_driver(&ehci_spear_hc_driver, &spear_overrides);
 193        return platform_driver_register(&spear_ehci_hcd_driver);
 194}
 195module_init(ehci_spear_init);
 196
 197static void __exit ehci_spear_cleanup(void)
 198{
 199        platform_driver_unregister(&spear_ehci_hcd_driver);
 200}
 201module_exit(ehci_spear_cleanup);
 202
 203MODULE_DESCRIPTION(DRIVER_DESC);
 204MODULE_ALIAS("platform:spear-ehci");
 205MODULE_AUTHOR("Deepak Sikri");
 206MODULE_LICENSE("GPL");
 207