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