linux/drivers/usb/host/ehci-grlib.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Driver for Aeroflex Gaisler GRLIB GRUSBHC EHCI host controller
   4 *
   5 * GRUSBHC is typically found on LEON/GRLIB SoCs
   6 *
   7 * (c) Jan Andersson <jan@gaisler.com>
   8 *
   9 * Based on ehci-ppc-of.c which is:
  10 * (c) Valentine Barshak <vbarshak@ru.mvista.com>
  11 * and in turn based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de>
  12 * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com>
  13 */
  14
  15#include <linux/err.h>
  16#include <linux/signal.h>
  17
  18#include <linux/of_irq.h>
  19#include <linux/of_address.h>
  20#include <linux/of_platform.h>
  21
  22#define GRUSBHC_HCIVERSION 0x0100 /* Known value of cap. reg. HCIVERSION */
  23
  24static const struct hc_driver ehci_grlib_hc_driver = {
  25        .description            = hcd_name,
  26        .product_desc           = "GRLIB GRUSBHC EHCI",
  27        .hcd_priv_size          = sizeof(struct ehci_hcd),
  28
  29        /*
  30         * generic hardware linkage
  31         */
  32        .irq                    = ehci_irq,
  33        .flags                  = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
  34
  35        /*
  36         * basic lifecycle operations
  37         */
  38        .reset                  = ehci_setup,
  39        .start                  = ehci_run,
  40        .stop                   = ehci_stop,
  41        .shutdown               = ehci_shutdown,
  42
  43        /*
  44         * managing i/o requests and associated device resources
  45         */
  46        .urb_enqueue            = ehci_urb_enqueue,
  47        .urb_dequeue            = ehci_urb_dequeue,
  48        .endpoint_disable       = ehci_endpoint_disable,
  49        .endpoint_reset         = ehci_endpoint_reset,
  50
  51        /*
  52         * scheduling support
  53         */
  54        .get_frame_number       = ehci_get_frame,
  55
  56        /*
  57         * root hub support
  58         */
  59        .hub_status_data        = ehci_hub_status_data,
  60        .hub_control            = ehci_hub_control,
  61#ifdef  CONFIG_PM
  62        .bus_suspend            = ehci_bus_suspend,
  63        .bus_resume             = ehci_bus_resume,
  64#endif
  65        .relinquish_port        = ehci_relinquish_port,
  66        .port_handed_over       = ehci_port_handed_over,
  67
  68        .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
  69};
  70
  71
  72static int ehci_hcd_grlib_probe(struct platform_device *op)
  73{
  74        struct device_node *dn = op->dev.of_node;
  75        struct usb_hcd *hcd;
  76        struct ehci_hcd *ehci = NULL;
  77        struct resource res;
  78        u32 hc_capbase;
  79        int irq;
  80        int rv;
  81
  82        if (usb_disabled())
  83                return -ENODEV;
  84
  85        dev_dbg(&op->dev, "initializing GRUSBHC EHCI USB Controller\n");
  86
  87        rv = of_address_to_resource(dn, 0, &res);
  88        if (rv)
  89                return rv;
  90
  91        /* usb_create_hcd requires dma_mask != NULL */
  92        op->dev.dma_mask = &op->dev.coherent_dma_mask;
  93        hcd = usb_create_hcd(&ehci_grlib_hc_driver, &op->dev,
  94                        "GRUSBHC EHCI USB");
  95        if (!hcd)
  96                return -ENOMEM;
  97
  98        hcd->rsrc_start = res.start;
  99        hcd->rsrc_len = resource_size(&res);
 100
 101        irq = irq_of_parse_and_map(dn, 0);
 102        if (irq == NO_IRQ) {
 103                dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n",
 104                        __FILE__);
 105                rv = -EBUSY;
 106                goto err_irq;
 107        }
 108
 109        hcd->regs = devm_ioremap_resource(&op->dev, &res);
 110        if (IS_ERR(hcd->regs)) {
 111                rv = PTR_ERR(hcd->regs);
 112                goto err_ioremap;
 113        }
 114
 115        ehci = hcd_to_ehci(hcd);
 116
 117        ehci->caps = hcd->regs;
 118
 119        /* determine endianness of this implementation */
 120        hc_capbase = ehci_readl(ehci, &ehci->caps->hc_capbase);
 121        if (HC_VERSION(ehci, hc_capbase) != GRUSBHC_HCIVERSION) {
 122                ehci->big_endian_mmio = 1;
 123                ehci->big_endian_desc = 1;
 124                ehci->big_endian_capbase = 1;
 125        }
 126
 127        rv = usb_add_hcd(hcd, irq, 0);
 128        if (rv)
 129                goto err_ioremap;
 130
 131        device_wakeup_enable(hcd->self.controller);
 132        return 0;
 133
 134err_ioremap:
 135        irq_dispose_mapping(irq);
 136err_irq:
 137        usb_put_hcd(hcd);
 138
 139        return rv;
 140}
 141
 142
 143static int ehci_hcd_grlib_remove(struct platform_device *op)
 144{
 145        struct usb_hcd *hcd = platform_get_drvdata(op);
 146
 147        dev_dbg(&op->dev, "stopping GRLIB GRUSBHC EHCI USB Controller\n");
 148
 149        usb_remove_hcd(hcd);
 150
 151        irq_dispose_mapping(hcd->irq);
 152
 153        usb_put_hcd(hcd);
 154
 155        return 0;
 156}
 157
 158
 159static const struct of_device_id ehci_hcd_grlib_of_match[] = {
 160        {
 161                .name = "GAISLER_EHCI",
 162         },
 163        {
 164                .name = "01_026",
 165         },
 166        {},
 167};
 168MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match);
 169
 170
 171static struct platform_driver ehci_grlib_driver = {
 172        .probe          = ehci_hcd_grlib_probe,
 173        .remove         = ehci_hcd_grlib_remove,
 174        .shutdown       = usb_hcd_platform_shutdown,
 175        .driver = {
 176                .name = "grlib-ehci",
 177                .of_match_table = ehci_hcd_grlib_of_match,
 178        },
 179};
 180