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