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/signal.h>
  15#include <linux/platform_device.h>
  16#include <linux/clk.h>
  17#include <linux/of.h>
  18
  19struct spear_ohci {
  20        struct ohci_hcd ohci;
  21        struct clk *clk;
  22};
  23
  24#define to_spear_ohci(hcd)      (struct spear_ohci *)hcd_to_ohci(hcd)
  25
  26static void spear_start_ohci(struct spear_ohci *ohci)
  27{
  28        clk_prepare_enable(ohci->clk);
  29}
  30
  31static void spear_stop_ohci(struct spear_ohci *ohci)
  32{
  33        clk_disable_unprepare(ohci->clk);
  34}
  35
  36static int ohci_spear_start(struct usb_hcd *hcd)
  37{
  38        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
  39        int ret;
  40
  41        ret = ohci_init(ohci);
  42        if (ret < 0)
  43                return ret;
  44        ohci->regs = hcd->regs;
  45
  46        ret = ohci_run(ohci);
  47        if (ret < 0) {
  48                dev_err(hcd->self.controller, "can't start\n");
  49                ohci_stop(hcd);
  50                return ret;
  51        }
  52
  53        create_debug_files(ohci);
  54
  55#ifdef DEBUG
  56        ohci_dump(ohci, 1);
  57#endif
  58        return 0;
  59}
  60
  61static const struct hc_driver ohci_spear_hc_driver = {
  62        .description            = hcd_name,
  63        .product_desc           = "SPEAr OHCI",
  64        .hcd_priv_size          = sizeof(struct spear_ohci),
  65
  66        /* generic hardware linkage */
  67        .irq                    = ohci_irq,
  68        .flags                  = HCD_USB11 | HCD_MEMORY,
  69
  70        /* basic lifecycle operations */
  71        .start                  = ohci_spear_start,
  72        .stop                   = ohci_stop,
  73        .shutdown               = ohci_shutdown,
  74#ifdef  CONFIG_PM
  75        .bus_suspend            = ohci_bus_suspend,
  76        .bus_resume             = ohci_bus_resume,
  77#endif
  78
  79        /* managing i/o requests and associated device resources */
  80        .urb_enqueue            = ohci_urb_enqueue,
  81        .urb_dequeue            = ohci_urb_dequeue,
  82        .endpoint_disable       = ohci_endpoint_disable,
  83
  84        /* scheduling support */
  85        .get_frame_number       = ohci_get_frame,
  86
  87        /* root hub support */
  88        .hub_status_data        = ohci_hub_status_data,
  89        .hub_control            = ohci_hub_control,
  90
  91        .start_port_reset       = ohci_start_port_reset,
  92};
  93
  94static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
  95{
  96        const struct hc_driver *driver = &ohci_spear_hc_driver;
  97        struct usb_hcd *hcd = NULL;
  98        struct clk *usbh_clk;
  99        struct spear_ohci *ohci_p;
 100        struct resource *res;
 101        int retval, irq;
 102
 103        irq = platform_get_irq(pdev, 0);
 104        if (irq < 0) {
 105                retval = irq;
 106                goto fail;
 107        }
 108
 109        /*
 110         * Right now device-tree probed devices don't get dma_mask set.
 111         * Since shared usb code relies on it, set it here for now.
 112         * Once we have dma capability bindings this can go away.
 113         */
 114        if (!pdev->dev.dma_mask)
 115                pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
 116        if (!pdev->dev.coherent_dma_mask)
 117                pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
 118
 119        usbh_clk = devm_clk_get(&pdev->dev, NULL);
 120        if (IS_ERR(usbh_clk)) {
 121                dev_err(&pdev->dev, "Error getting interface clock\n");
 122                retval = PTR_ERR(usbh_clk);
 123                goto fail;
 124        }
 125
 126        hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
 127        if (!hcd) {
 128                retval = -ENOMEM;
 129                goto fail;
 130        }
 131
 132        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 133        if (!res) {
 134                retval = -ENODEV;
 135                goto err_put_hcd;
 136        }
 137
 138        hcd->rsrc_start = pdev->resource[0].start;
 139        hcd->rsrc_len = resource_size(res);
 140        if (!devm_request_mem_region(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len,
 141                                hcd_name)) {
 142                dev_dbg(&pdev->dev, "request_mem_region failed\n");
 143                retval = -EBUSY;
 144                goto err_put_hcd;
 145        }
 146
 147        hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len);
 148        if (!hcd->regs) {
 149                dev_dbg(&pdev->dev, "ioremap failed\n");
 150                retval = -ENOMEM;
 151                goto err_put_hcd;
 152        }
 153
 154        ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd);
 155        ohci_p->clk = usbh_clk;
 156        spear_start_ohci(ohci_p);
 157        ohci_hcd_init(hcd_to_ohci(hcd));
 158
 159        retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0);
 160        if (retval == 0)
 161                return retval;
 162
 163        spear_stop_ohci(ohci_p);
 164err_put_hcd:
 165        usb_put_hcd(hcd);
 166fail:
 167        dev_err(&pdev->dev, "init fail, %d\n", retval);
 168
 169        return retval;
 170}
 171
 172static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
 173{
 174        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 175        struct spear_ohci *ohci_p = to_spear_ohci(hcd);
 176
 177        usb_remove_hcd(hcd);
 178        if (ohci_p->clk)
 179                spear_stop_ohci(ohci_p);
 180
 181        usb_put_hcd(hcd);
 182        return 0;
 183}
 184
 185#if defined(CONFIG_PM)
 186static int spear_ohci_hcd_drv_suspend(struct platform_device *dev,
 187                pm_message_t message)
 188{
 189        struct usb_hcd *hcd = platform_get_drvdata(dev);
 190        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
 191        struct spear_ohci *ohci_p = to_spear_ohci(hcd);
 192
 193        if (time_before(jiffies, ohci->next_statechange))
 194                msleep(5);
 195        ohci->next_statechange = jiffies;
 196
 197        spear_stop_ohci(ohci_p);
 198        return 0;
 199}
 200
 201static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
 202{
 203        struct usb_hcd *hcd = platform_get_drvdata(dev);
 204        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
 205        struct spear_ohci *ohci_p = to_spear_ohci(hcd);
 206
 207        if (time_before(jiffies, ohci->next_statechange))
 208                msleep(5);
 209        ohci->next_statechange = jiffies;
 210
 211        spear_start_ohci(ohci_p);
 212        ohci_resume(hcd, false);
 213        return 0;
 214}
 215#endif
 216
 217static struct of_device_id spear_ohci_id_table[] = {
 218        { .compatible = "st,spear600-ohci", },
 219        { },
 220};
 221
 222/* Driver definition to register with the platform bus */
 223static struct platform_driver spear_ohci_hcd_driver = {
 224        .probe =        spear_ohci_hcd_drv_probe,
 225        .remove =       spear_ohci_hcd_drv_remove,
 226#ifdef CONFIG_PM
 227        .suspend =      spear_ohci_hcd_drv_suspend,
 228        .resume =       spear_ohci_hcd_drv_resume,
 229#endif
 230        .driver = {
 231                .owner = THIS_MODULE,
 232                .name = "spear-ohci",
 233                .of_match_table = spear_ohci_id_table,
 234        },
 235};
 236
 237MODULE_ALIAS("platform:spear-ohci");
 238