linux/drivers/usb/dwc3/host.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/**
   3 * host.c - DesignWare USB3 DRD Controller Host Glue
   4 *
   5 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
   6 *
   7 * Authors: Felipe Balbi <balbi@ti.com>,
   8 */
   9
  10#include <linux/platform_device.h>
  11
  12#include "core.h"
  13
  14static int dwc3_host_get_irq(struct dwc3 *dwc)
  15{
  16        struct platform_device  *dwc3_pdev = to_platform_device(dwc->dev);
  17        int irq;
  18
  19        irq = platform_get_irq_byname_optional(dwc3_pdev, "host");
  20        if (irq > 0)
  21                goto out;
  22
  23        if (irq == -EPROBE_DEFER)
  24                goto out;
  25
  26        irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3");
  27        if (irq > 0)
  28                goto out;
  29
  30        if (irq == -EPROBE_DEFER)
  31                goto out;
  32
  33        irq = platform_get_irq(dwc3_pdev, 0);
  34        if (irq > 0)
  35                goto out;
  36
  37        if (!irq)
  38                irq = -EINVAL;
  39
  40out:
  41        return irq;
  42}
  43
  44int dwc3_host_init(struct dwc3 *dwc)
  45{
  46        struct property_entry   props[4];
  47        struct platform_device  *xhci;
  48        int                     ret, irq;
  49        struct resource         *res;
  50        struct platform_device  *dwc3_pdev = to_platform_device(dwc->dev);
  51        int                     prop_idx = 0;
  52
  53        irq = dwc3_host_get_irq(dwc);
  54        if (irq < 0)
  55                return irq;
  56
  57        res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, "host");
  58        if (!res)
  59                res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ,
  60                                "dwc_usb3");
  61        if (!res)
  62                res = platform_get_resource(dwc3_pdev, IORESOURCE_IRQ, 0);
  63        if (!res)
  64                return -ENOMEM;
  65
  66        dwc->xhci_resources[1].start = irq;
  67        dwc->xhci_resources[1].end = irq;
  68        dwc->xhci_resources[1].flags = res->flags;
  69        dwc->xhci_resources[1].name = res->name;
  70
  71        xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
  72        if (!xhci) {
  73                dev_err(dwc->dev, "couldn't allocate xHCI device\n");
  74                return -ENOMEM;
  75        }
  76
  77        xhci->dev.parent        = dwc->dev;
  78
  79        dwc->xhci = xhci;
  80
  81        ret = platform_device_add_resources(xhci, dwc->xhci_resources,
  82                                                DWC3_XHCI_RESOURCES_NUM);
  83        if (ret) {
  84                dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
  85                goto err;
  86        }
  87
  88        memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
  89
  90        if (dwc->usb3_lpm_capable)
  91                props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");
  92
  93        if (dwc->usb2_lpm_disable)
  94                props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb2-lpm-disable");
  95
  96        /**
  97         * WORKAROUND: dwc3 revisions <=3.00a have a limitation
  98         * where Port Disable command doesn't work.
  99         *
 100         * The suggested workaround is that we avoid Port Disable
 101         * completely.
 102         *
 103         * This following flag tells XHCI to do just that.
 104         */
 105        if (dwc->revision <= DWC3_REVISION_300A)
 106                props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
 107
 108        if (prop_idx) {
 109                ret = platform_device_add_properties(xhci, props);
 110                if (ret) {
 111                        dev_err(dwc->dev, "failed to add properties to xHCI\n");
 112                        goto err;
 113                }
 114        }
 115
 116        ret = platform_device_add(xhci);
 117        if (ret) {
 118                dev_err(dwc->dev, "failed to register xHCI device\n");
 119                goto err;
 120        }
 121
 122        return 0;
 123err:
 124        platform_device_put(xhci);
 125        return ret;
 126}
 127
 128void dwc3_host_exit(struct dwc3 *dwc)
 129{
 130        platform_device_unregister(dwc->xhci);
 131}
 132