linux/drivers/usb/host/ohci-spear.c
<<
>>
Prefs
   1/*
   2* OHCI HCD (Host Controller Driver) for USB.
   3*
   4* Copyright (C) 2010 ST Microelectronics.
   5* Deepak Sikri<deepak.sikri@st.com>
   6*
   7* Based on various ohci-*.c drivers
   8*
   9* This file is licensed under the terms of the GNU General Public
  10* License version 2. This program is licensed "as is" without any
  11* warranty of any kind, whether express or implied.
  12*/
  13
  14#include <linux/clk.h>
  15#include <linux/dma-mapping.h>
  16#include <linux/io.h>
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/of.h>
  20#include <linux/platform_device.h>
  21#include <linux/signal.h>
  22#include <linux/usb.h>
  23#include <linux/usb/hcd.h>
  24
  25#include "ohci.h"
  26
  27#define DRIVER_DESC "OHCI SPEAr driver"
  28
  29static const char hcd_name[] = "SPEAr-ohci";
  30struct spear_ohci {
  31        struct clk *clk;
  32};
  33
  34#define to_spear_ohci(hcd)     (struct spear_ohci *)(hcd_to_ohci(hcd)->priv)
  35
  36static struct hc_driver __read_mostly ohci_spear_hc_driver;
  37
  38static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
  39{
  40        const struct hc_driver *driver = &ohci_spear_hc_driver;
  41        struct ohci_hcd *ohci;
  42        struct usb_hcd *hcd = NULL;
  43        struct clk *usbh_clk;
  44        struct spear_ohci *sohci_p;
  45        struct resource *res;
  46        int retval, irq;
  47
  48        irq = platform_get_irq(pdev, 0);
  49        if (irq < 0) {
  50                retval = irq;
  51                goto fail;
  52        }
  53
  54        /*
  55         * Right now device-tree probed devices don't get dma_mask set.
  56         * Since shared usb code relies on it, set it here for now.
  57         * Once we have dma capability bindings this can go away.
  58         */
  59        retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
  60        if (retval)
  61                goto fail;
  62
  63        usbh_clk = devm_clk_get(&pdev->dev, NULL);
  64        if (IS_ERR(usbh_clk)) {
  65                dev_err(&pdev->dev, "Error getting interface clock\n");
  66                retval = PTR_ERR(usbh_clk);
  67                goto fail;
  68        }
  69
  70        hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
  71        if (!hcd) {
  72                retval = -ENOMEM;
  73                goto fail;
  74        }
  75
  76        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  77        hcd->regs = devm_ioremap_resource(&pdev->dev, res);
  78        if (IS_ERR(hcd->regs)) {
  79                retval = PTR_ERR(hcd->regs);
  80                goto err_put_hcd;
  81        }
  82
  83        hcd->rsrc_start = pdev->resource[0].start;
  84        hcd->rsrc_len = resource_size(res);
  85
  86        sohci_p = to_spear_ohci(hcd);
  87        sohci_p->clk = usbh_clk;
  88
  89        clk_prepare_enable(sohci_p->clk);
  90
  91        ohci = hcd_to_ohci(hcd);
  92
  93        retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0);
  94        if (retval == 0) {
  95                device_wakeup_enable(hcd->self.controller);
  96                return retval;
  97        }
  98
  99        clk_disable_unprepare(sohci_p->clk);
 100err_put_hcd:
 101        usb_put_hcd(hcd);
 102fail:
 103        dev_err(&pdev->dev, "init fail, %d\n", retval);
 104
 105        return retval;
 106}
 107
 108static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
 109{
 110        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 111        struct spear_ohci *sohci_p = to_spear_ohci(hcd);
 112
 113        usb_remove_hcd(hcd);
 114        if (sohci_p->clk)
 115                clk_disable_unprepare(sohci_p->clk);
 116
 117        usb_put_hcd(hcd);
 118        return 0;
 119}
 120
 121#if defined(CONFIG_PM)
 122static int spear_ohci_hcd_drv_suspend(struct platform_device *pdev,
 123                pm_message_t message)
 124{
 125        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 126        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
 127        struct spear_ohci *sohci_p = to_spear_ohci(hcd);
 128        bool do_wakeup = device_may_wakeup(&pdev->dev);
 129        int ret;
 130
 131        if (time_before(jiffies, ohci->next_statechange))
 132                msleep(5);
 133        ohci->next_statechange = jiffies;
 134
 135        ret = ohci_suspend(hcd, do_wakeup);
 136        if (ret)
 137                return ret;
 138
 139        clk_disable_unprepare(sohci_p->clk);
 140
 141        return ret;
 142}
 143
 144static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
 145{
 146        struct usb_hcd *hcd = platform_get_drvdata(dev);
 147        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
 148        struct spear_ohci *sohci_p = to_spear_ohci(hcd);
 149
 150        if (time_before(jiffies, ohci->next_statechange))
 151                msleep(5);
 152        ohci->next_statechange = jiffies;
 153
 154        clk_prepare_enable(sohci_p->clk);
 155        ohci_resume(hcd, false);
 156        return 0;
 157}
 158#endif
 159
 160static const struct of_device_id spear_ohci_id_table[] = {
 161        { .compatible = "st,spear600-ohci", },
 162        { },
 163};
 164MODULE_DEVICE_TABLE(of, spear_ohci_id_table);
 165
 166/* Driver definition to register with the platform bus */
 167static struct platform_driver spear_ohci_hcd_driver = {
 168        .probe =        spear_ohci_hcd_drv_probe,
 169        .remove =       spear_ohci_hcd_drv_remove,
 170#ifdef CONFIG_PM
 171        .suspend =      spear_ohci_hcd_drv_suspend,
 172        .resume =       spear_ohci_hcd_drv_resume,
 173#endif
 174        .driver = {
 175                .name = "spear-ohci",
 176                .of_match_table = spear_ohci_id_table,
 177        },
 178};
 179
 180static const struct ohci_driver_overrides spear_overrides __initconst = {
 181        .extra_priv_size = sizeof(struct spear_ohci),
 182};
 183static int __init ohci_spear_init(void)
 184{
 185        if (usb_disabled())
 186                return -ENODEV;
 187
 188        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 189
 190        ohci_init_driver(&ohci_spear_hc_driver, &spear_overrides);
 191        return platform_driver_register(&spear_ohci_hcd_driver);
 192}
 193module_init(ohci_spear_init);
 194
 195static void __exit ohci_spear_cleanup(void)
 196{
 197        platform_driver_unregister(&spear_ohci_hcd_driver);
 198}
 199module_exit(ohci_spear_cleanup);
 200
 201MODULE_DESCRIPTION(DRIVER_DESC);
 202MODULE_AUTHOR("Deepak Sikri");
 203MODULE_LICENSE("GPL v2");
 204MODULE_ALIAS("platform:spear-ohci");
 205