linux/drivers/usb/host/ehci-atmel.c
<<
>>
Prefs
   1/*
   2 * Driver for EHCI UHP on Atmel chips
   3 *
   4 *  Copyright (C) 2009 Atmel Corporation,
   5 *                     Nicolas Ferre <nicolas.ferre@atmel.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/platform_device.h>
  16
  17/* interface and function clocks */
  18static struct clk *iclk, *fclk;
  19static int clocked;
  20
  21/*-------------------------------------------------------------------------*/
  22
  23static void atmel_start_clock(void)
  24{
  25        clk_enable(iclk);
  26        clk_enable(fclk);
  27        clocked = 1;
  28}
  29
  30static void atmel_stop_clock(void)
  31{
  32        clk_disable(fclk);
  33        clk_disable(iclk);
  34        clocked = 0;
  35}
  36
  37static void atmel_start_ehci(struct platform_device *pdev)
  38{
  39        dev_dbg(&pdev->dev, "start\n");
  40        atmel_start_clock();
  41}
  42
  43static void atmel_stop_ehci(struct platform_device *pdev)
  44{
  45        dev_dbg(&pdev->dev, "stop\n");
  46        atmel_stop_clock();
  47}
  48
  49/*-------------------------------------------------------------------------*/
  50
  51static int ehci_atmel_setup(struct usb_hcd *hcd)
  52{
  53        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
  54        int retval = 0;
  55
  56        /* registers start at offset 0x0 */
  57        ehci->caps = hcd->regs;
  58        ehci->regs = hcd->regs +
  59                HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
  60        dbg_hcs_params(ehci, "reset");
  61        dbg_hcc_params(ehci, "reset");
  62
  63        /* cache this readonly data; minimize chip reads */
  64        ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
  65
  66        retval = ehci_halt(ehci);
  67        if (retval)
  68                return retval;
  69
  70        /* data structure init */
  71        retval = ehci_init(hcd);
  72        if (retval)
  73                return retval;
  74
  75        ehci->sbrn = 0x20;
  76
  77        ehci_reset(ehci);
  78        ehci_port_power(ehci, 0);
  79
  80        return retval;
  81}
  82
  83static const struct hc_driver ehci_atmel_hc_driver = {
  84        .description            = hcd_name,
  85        .product_desc           = "Atmel EHCI UHP HS",
  86        .hcd_priv_size          = sizeof(struct ehci_hcd),
  87
  88        /* generic hardware linkage */
  89        .irq                    = ehci_irq,
  90        .flags                  = HCD_MEMORY | HCD_USB2,
  91
  92        /* basic lifecycle operations */
  93        .reset                  = ehci_atmel_setup,
  94        .start                  = ehci_run,
  95        .stop                   = ehci_stop,
  96        .shutdown               = ehci_shutdown,
  97
  98        /* managing i/o requests and associated device resources */
  99        .urb_enqueue            = ehci_urb_enqueue,
 100        .urb_dequeue            = ehci_urb_dequeue,
 101        .endpoint_disable       = ehci_endpoint_disable,
 102        .endpoint_reset         = ehci_endpoint_reset,
 103
 104        /* scheduling support */
 105        .get_frame_number       = ehci_get_frame,
 106
 107        /* root hub support */
 108        .hub_status_data        = ehci_hub_status_data,
 109        .hub_control            = ehci_hub_control,
 110        .bus_suspend            = ehci_bus_suspend,
 111        .bus_resume             = ehci_bus_resume,
 112        .relinquish_port        = ehci_relinquish_port,
 113        .port_handed_over       = ehci_port_handed_over,
 114
 115        .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
 116};
 117
 118static int __init ehci_atmel_drv_probe(struct platform_device *pdev)
 119{
 120        struct usb_hcd *hcd;
 121        const struct hc_driver *driver = &ehci_atmel_hc_driver;
 122        struct resource *res;
 123        int irq;
 124        int retval;
 125
 126        if (usb_disabled())
 127                return -ENODEV;
 128
 129        pr_debug("Initializing Atmel-SoC USB Host Controller\n");
 130
 131        irq = platform_get_irq(pdev, 0);
 132        if (irq <= 0) {
 133                dev_err(&pdev->dev,
 134                        "Found HC with no IRQ. Check %s setup!\n",
 135                        dev_name(&pdev->dev));
 136                retval = -ENODEV;
 137                goto fail_create_hcd;
 138        }
 139
 140        hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
 141        if (!hcd) {
 142                retval = -ENOMEM;
 143                goto fail_create_hcd;
 144        }
 145
 146        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 147        if (!res) {
 148                dev_err(&pdev->dev,
 149                        "Found HC with no register addr. Check %s setup!\n",
 150                        dev_name(&pdev->dev));
 151                retval = -ENODEV;
 152                goto fail_request_resource;
 153        }
 154        hcd->rsrc_start = res->start;
 155        hcd->rsrc_len = resource_size(res);
 156
 157        if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
 158                                driver->description)) {
 159                dev_dbg(&pdev->dev, "controller already in use\n");
 160                retval = -EBUSY;
 161                goto fail_request_resource;
 162        }
 163
 164        hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
 165        if (hcd->regs == NULL) {
 166                dev_dbg(&pdev->dev, "error mapping memory\n");
 167                retval = -EFAULT;
 168                goto fail_ioremap;
 169        }
 170
 171        iclk = clk_get(&pdev->dev, "ehci_clk");
 172        if (IS_ERR(iclk)) {
 173                dev_err(&pdev->dev, "Error getting interface clock\n");
 174                retval = -ENOENT;
 175                goto fail_get_iclk;
 176        }
 177        fclk = clk_get(&pdev->dev, "uhpck");
 178        if (IS_ERR(fclk)) {
 179                dev_err(&pdev->dev, "Error getting function clock\n");
 180                retval = -ENOENT;
 181                goto fail_get_fclk;
 182        }
 183
 184        atmel_start_ehci(pdev);
 185
 186        retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
 187        if (retval)
 188                goto fail_add_hcd;
 189
 190        return retval;
 191
 192fail_add_hcd:
 193        atmel_stop_ehci(pdev);
 194        clk_put(fclk);
 195fail_get_fclk:
 196        clk_put(iclk);
 197fail_get_iclk:
 198        iounmap(hcd->regs);
 199fail_ioremap:
 200        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 201fail_request_resource:
 202        usb_put_hcd(hcd);
 203fail_create_hcd:
 204        dev_err(&pdev->dev, "init %s fail, %d\n",
 205                dev_name(&pdev->dev), retval);
 206
 207        return retval;
 208}
 209
 210static int __exit ehci_atmel_drv_remove(struct platform_device *pdev)
 211{
 212        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 213
 214        ehci_shutdown(hcd);
 215        usb_remove_hcd(hcd);
 216        iounmap(hcd->regs);
 217        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 218        usb_put_hcd(hcd);
 219
 220        atmel_stop_ehci(pdev);
 221        clk_put(fclk);
 222        clk_put(iclk);
 223        fclk = iclk = NULL;
 224
 225        return 0;
 226}
 227
 228static struct platform_driver ehci_atmel_driver = {
 229        .probe          = ehci_atmel_drv_probe,
 230        .remove         = __exit_p(ehci_atmel_drv_remove),
 231        .shutdown       = usb_hcd_platform_shutdown,
 232        .driver.name    = "atmel-ehci",
 233};
 234