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(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(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 != -EPROBE_DEFER)
  38                dev_err(dwc->dev, "missing host IRQ\n");
  39
  40        if (!irq)
  41                irq = -EINVAL;
  42
  43out:
  44        return irq;
  45}
  46
  47int dwc3_host_init(struct dwc3 *dwc)
  48{
  49        struct property_entry   props[4];
  50        struct platform_device  *xhci;
  51        int                     ret, irq;
  52        struct resource         *res;
  53        struct platform_device  *dwc3_pdev = to_platform_device(dwc->dev);
  54        int                     prop_idx = 0;
  55
  56        irq = dwc3_host_get_irq(dwc);
  57        if (irq < 0)
  58                return irq;
  59
  60        res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, "host");
  61        if (!res)
  62                res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ,
  63                                "dwc_usb3");
  64        if (!res)
  65                res = platform_get_resource(dwc3_pdev, IORESOURCE_IRQ, 0);
  66        if (!res)
  67                return -ENOMEM;
  68
  69        dwc->xhci_resources[1].start = irq;
  70        dwc->xhci_resources[1].end = irq;
  71        dwc->xhci_resources[1].flags = res->flags;
  72        dwc->xhci_resources[1].name = res->name;
  73
  74        xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
  75        if (!xhci) {
  76                dev_err(dwc->dev, "couldn't allocate xHCI device\n");
  77                return -ENOMEM;
  78        }
  79
  80        xhci->dev.parent        = dwc->dev;
  81
  82        dwc->xhci = xhci;
  83
  84        ret = platform_device_add_resources(xhci, dwc->xhci_resources,
  85                                                DWC3_XHCI_RESOURCES_NUM);
  86        if (ret) {
  87                dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
  88                goto err1;
  89        }
  90
  91        memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
  92
  93        if (dwc->usb3_lpm_capable)
  94                props[prop_idx++].name = "usb3-lpm-capable";
  95
  96        if (dwc->usb2_lpm_disable)
  97                props[prop_idx++].name = "usb2-lpm-disable";
  98
  99        /**
 100         * WORKAROUND: dwc3 revisions <=3.00a have a limitation
 101         * where Port Disable command doesn't work.
 102         *
 103         * The suggested workaround is that we avoid Port Disable
 104         * completely.
 105         *
 106         * This following flag tells XHCI to do just that.
 107         */
 108        if (dwc->revision <= DWC3_REVISION_300A)
 109                props[prop_idx++].name = "quirk-broken-port-ped";
 110
 111        if (prop_idx) {
 112                ret = platform_device_add_properties(xhci, props);
 113                if (ret) {
 114                        dev_err(dwc->dev, "failed to add properties to xHCI\n");
 115                        goto err1;
 116                }
 117        }
 118
 119        phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
 120                          dev_name(dwc->dev));
 121        phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
 122                          dev_name(dwc->dev));
 123
 124        ret = platform_device_add(xhci);
 125        if (ret) {
 126                dev_err(dwc->dev, "failed to register xHCI device\n");
 127                goto err2;
 128        }
 129
 130        return 0;
 131err2:
 132        phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
 133                          dev_name(dwc->dev));
 134        phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
 135                          dev_name(dwc->dev));
 136err1:
 137        platform_device_put(xhci);
 138        return ret;
 139}
 140
 141void dwc3_host_exit(struct dwc3 *dwc)
 142{
 143        phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
 144                          dev_name(dwc->dev));
 145        phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
 146                          dev_name(dwc->dev));
 147        platform_device_unregister(dwc->xhci);
 148}
 149