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        retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
  85        if (retval)
  86                goto fail;
  87
  88        usbh_clk = devm_clk_get(&pdev->dev, NULL);
  89        if (IS_ERR(usbh_clk)) {
  90                dev_err(&pdev->dev, "Error getting interface clock\n");
  91                retval = PTR_ERR(usbh_clk);
  92                goto fail;
  93        }
  94
  95        hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
  96        if (!hcd) {
  97                retval = -ENOMEM;
  98                goto fail;
  99        }
 100
 101        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 102        hcd->regs = devm_ioremap_resource(&pdev->dev, res);
 103        if (IS_ERR(hcd->regs)) {
 104                retval = PTR_ERR(hcd->regs);
 105                goto err_put_hcd;
 106        }
 107        hcd->rsrc_start = res->start;
 108        hcd->rsrc_len = resource_size(res);
 109
 110        sehci = to_spear_ehci(hcd);
 111        sehci->clk = usbh_clk;
 112
 113        /* registers start at offset 0x0 */
 114        hcd_to_ehci(hcd)->caps = hcd->regs;
 115
 116        clk_prepare_enable(sehci->clk);
 117        retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
 118        if (retval)
 119                goto err_stop_ehci;
 120
 121        device_wakeup_enable(hcd->self.controller);
 122        return retval;
 123
 124err_stop_ehci:
 125        clk_disable_unprepare(sehci->clk);
 126err_put_hcd:
 127        usb_put_hcd(hcd);
 128fail:
 129        dev_err(&pdev->dev, "init fail, %d\n", retval);
 130
 131        return retval ;
 132}
 133
 134static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
 135{
 136        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 137        struct spear_ehci *sehci = to_spear_ehci(hcd);
 138
 139        usb_remove_hcd(hcd);
 140
 141        if (sehci->clk)
 142                clk_disable_unprepare(sehci->clk);
 143        usb_put_hcd(hcd);
 144
 145        return 0;
 146}
 147
 148static const struct of_device_id spear_ehci_id_table[] = {
 149        { .compatible = "st,spear600-ehci", },
 150        { },
 151};
 152MODULE_DEVICE_TABLE(of, spear_ehci_id_table);
 153
 154static struct platform_driver spear_ehci_hcd_driver = {
 155        .probe          = spear_ehci_hcd_drv_probe,
 156        .remove         = spear_ehci_hcd_drv_remove,
 157        .shutdown       = usb_hcd_platform_shutdown,
 158        .driver         = {
 159                .name = "spear-ehci",
 160                .bus = &platform_bus_type,
 161                .pm = &ehci_spear_pm_ops,
 162                .of_match_table = spear_ehci_id_table,
 163        }
 164};
 165
 166static const struct ehci_driver_overrides spear_overrides __initconst = {
 167        .extra_priv_size = sizeof(struct spear_ehci),
 168};
 169
 170static int __init ehci_spear_init(void)
 171{
 172        if (usb_disabled())
 173                return -ENODEV;
 174
 175        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 176
 177        ehci_init_driver(&ehci_spear_hc_driver, &spear_overrides);
 178        return platform_driver_register(&spear_ehci_hcd_driver);
 179}
 180module_init(ehci_spear_init);
 181
 182static void __exit ehci_spear_cleanup(void)
 183{
 184        platform_driver_unregister(&spear_ehci_hcd_driver);
 185}
 186module_exit(ehci_spear_cleanup);
 187
 188MODULE_DESCRIPTION(DRIVER_DESC);
 189MODULE_ALIAS("platform:spear-ehci");
 190MODULE_AUTHOR("Deepak Sikri");
 191MODULE_LICENSE("GPL");
 192