linux/drivers/usb/host/ehci-octeon.c
<<
>>
Prefs
   1/*
   2 * EHCI HCD glue for Cavium Octeon II SOCs.
   3 *
   4 * Loosely based on ehci-au1xxx.c
   5 *
   6 * This file is subject to the terms and conditions of the GNU General Public
   7 * License.  See the file "COPYING" in the main directory of this archive
   8 * for more details.
   9 *
  10 * Copyright (C) 2010 Cavium Networks
  11 *
  12 */
  13
  14#include <linux/platform_device.h>
  15
  16#include <asm/octeon/octeon.h>
  17#include <asm/octeon/cvmx-uctlx-defs.h>
  18
  19#define OCTEON_EHCI_HCD_NAME "octeon-ehci"
  20
  21/* Common clock init code.  */
  22void octeon2_usb_clocks_start(void);
  23void octeon2_usb_clocks_stop(void);
  24
  25static void ehci_octeon_start(void)
  26{
  27        union cvmx_uctlx_ehci_ctl ehci_ctl;
  28
  29        octeon2_usb_clocks_start();
  30
  31        ehci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_EHCI_CTL(0));
  32        /* Use 64-bit addressing. */
  33        ehci_ctl.s.ehci_64b_addr_en = 1;
  34        ehci_ctl.s.l2c_addr_msb = 0;
  35        ehci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */
  36        ehci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */
  37        cvmx_write_csr(CVMX_UCTLX_EHCI_CTL(0), ehci_ctl.u64);
  38}
  39
  40static void ehci_octeon_stop(void)
  41{
  42        octeon2_usb_clocks_stop();
  43}
  44
  45static const struct hc_driver ehci_octeon_hc_driver = {
  46        .description            = hcd_name,
  47        .product_desc           = "Octeon EHCI",
  48        .hcd_priv_size          = sizeof(struct ehci_hcd),
  49
  50        /*
  51         * generic hardware linkage
  52         */
  53        .irq                    = ehci_irq,
  54        .flags                  = HCD_MEMORY | HCD_USB2,
  55
  56        /*
  57         * basic lifecycle operations
  58         */
  59        .reset                  = ehci_setup,
  60        .start                  = ehci_run,
  61        .stop                   = ehci_stop,
  62        .shutdown               = ehci_shutdown,
  63
  64        /*
  65         * managing i/o requests and associated device resources
  66         */
  67        .urb_enqueue            = ehci_urb_enqueue,
  68        .urb_dequeue            = ehci_urb_dequeue,
  69        .endpoint_disable       = ehci_endpoint_disable,
  70        .endpoint_reset         = ehci_endpoint_reset,
  71
  72        /*
  73         * scheduling support
  74         */
  75        .get_frame_number       = ehci_get_frame,
  76
  77        /*
  78         * root hub support
  79         */
  80        .hub_status_data        = ehci_hub_status_data,
  81        .hub_control            = ehci_hub_control,
  82        .bus_suspend            = ehci_bus_suspend,
  83        .bus_resume             = ehci_bus_resume,
  84        .relinquish_port        = ehci_relinquish_port,
  85        .port_handed_over       = ehci_port_handed_over,
  86
  87        .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
  88};
  89
  90static u64 ehci_octeon_dma_mask = DMA_BIT_MASK(64);
  91
  92static int ehci_octeon_drv_probe(struct platform_device *pdev)
  93{
  94        struct usb_hcd *hcd;
  95        struct ehci_hcd *ehci;
  96        struct resource *res_mem;
  97        int irq;
  98        int ret;
  99
 100        if (usb_disabled())
 101                return -ENODEV;
 102
 103        irq = platform_get_irq(pdev, 0);
 104        if (irq < 0) {
 105                dev_err(&pdev->dev, "No irq assigned\n");
 106                return -ENODEV;
 107        }
 108
 109        res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 110        if (res_mem == NULL) {
 111                dev_err(&pdev->dev, "No register space assigned\n");
 112                return -ENODEV;
 113        }
 114
 115        /*
 116         * We can DMA from anywhere. But the descriptors must be in
 117         * the lower 4GB.
 118         */
 119        pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
 120        pdev->dev.dma_mask = &ehci_octeon_dma_mask;
 121
 122        hcd = usb_create_hcd(&ehci_octeon_hc_driver, &pdev->dev, "octeon");
 123        if (!hcd)
 124                return -ENOMEM;
 125
 126        hcd->rsrc_start = res_mem->start;
 127        hcd->rsrc_len = resource_size(res_mem);
 128
 129        if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
 130                                OCTEON_EHCI_HCD_NAME)) {
 131                dev_err(&pdev->dev, "request_mem_region failed\n");
 132                ret = -EBUSY;
 133                goto err1;
 134        }
 135
 136        hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
 137        if (!hcd->regs) {
 138                dev_err(&pdev->dev, "ioremap failed\n");
 139                ret = -ENOMEM;
 140                goto err2;
 141        }
 142
 143        ehci_octeon_start();
 144
 145        ehci = hcd_to_ehci(hcd);
 146
 147        /* Octeon EHCI matches CPU endianness. */
 148#ifdef __BIG_ENDIAN
 149        ehci->big_endian_mmio = 1;
 150#endif
 151
 152        ehci->caps = hcd->regs;
 153
 154        ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
 155        if (ret) {
 156                dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret);
 157                goto err3;
 158        }
 159
 160        platform_set_drvdata(pdev, hcd);
 161
 162        /* root ports should always stay powered */
 163        ehci_port_power(ehci, 1);
 164
 165        return 0;
 166err3:
 167        ehci_octeon_stop();
 168
 169        iounmap(hcd->regs);
 170err2:
 171        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 172err1:
 173        usb_put_hcd(hcd);
 174        return ret;
 175}
 176
 177static int ehci_octeon_drv_remove(struct platform_device *pdev)
 178{
 179        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 180
 181        usb_remove_hcd(hcd);
 182
 183        ehci_octeon_stop();
 184        iounmap(hcd->regs);
 185        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 186        usb_put_hcd(hcd);
 187
 188        platform_set_drvdata(pdev, NULL);
 189
 190        return 0;
 191}
 192
 193static struct platform_driver ehci_octeon_driver = {
 194        .probe          = ehci_octeon_drv_probe,
 195        .remove         = ehci_octeon_drv_remove,
 196        .shutdown       = usb_hcd_platform_shutdown,
 197        .driver = {
 198                .name   = OCTEON_EHCI_HCD_NAME,
 199                .owner  = THIS_MODULE,
 200        }
 201};
 202
 203MODULE_ALIAS("platform:" OCTEON_EHCI_HCD_NAME);
 204