xref: /linux/drivers/usb/dwc3/host.c (revision 5fd54ace4721fc5ce2bb5aef6318fcf17f421460)
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  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2  of
11  * the License as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18 
19 #include <linux/platform_device.h>
20 
21 #include "core.h"
22 
23 static int dwc3_host_get_irq(struct dwc3 *dwc)
24 {
25 	struct platform_device	*dwc3_pdev = to_platform_device(dwc->dev);
26 	int irq;
27 
28 	irq = platform_get_irq_byname(dwc3_pdev, "host");
29 	if (irq > 0)
30 		goto out;
31 
32 	if (irq == -EPROBE_DEFER)
33 		goto out;
34 
35 	irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
36 	if (irq > 0)
37 		goto out;
38 
39 	if (irq == -EPROBE_DEFER)
40 		goto out;
41 
42 	irq = platform_get_irq(dwc3_pdev, 0);
43 	if (irq > 0)
44 		goto out;
45 
46 	if (irq != -EPROBE_DEFER)
47 		dev_err(dwc->dev, "missing host IRQ\n");
48 
49 	if (!irq)
50 		irq = -EINVAL;
51 
52 out:
53 	return irq;
54 }
55 
56 int dwc3_host_init(struct dwc3 *dwc)
57 {
58 	struct property_entry	props[3];
59 	struct platform_device	*xhci;
60 	int			ret, irq;
61 	struct resource		*res;
62 	struct platform_device	*dwc3_pdev = to_platform_device(dwc->dev);
63 	int			prop_idx = 0;
64 
65 	irq = dwc3_host_get_irq(dwc);
66 	if (irq < 0)
67 		return irq;
68 
69 	res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, "host");
70 	if (!res)
71 		res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ,
72 				"dwc_usb3");
73 	if (!res)
74 		res = platform_get_resource(dwc3_pdev, IORESOURCE_IRQ, 0);
75 	if (!res)
76 		return -ENOMEM;
77 
78 	dwc->xhci_resources[1].start = irq;
79 	dwc->xhci_resources[1].end = irq;
80 	dwc->xhci_resources[1].flags = res->flags;
81 	dwc->xhci_resources[1].name = res->name;
82 
83 	xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
84 	if (!xhci) {
85 		dev_err(dwc->dev, "couldn't allocate xHCI device\n");
86 		return -ENOMEM;
87 	}
88 
89 	xhci->dev.parent	= dwc->dev;
90 
91 	dwc->xhci = xhci;
92 
93 	ret = platform_device_add_resources(xhci, dwc->xhci_resources,
94 						DWC3_XHCI_RESOURCES_NUM);
95 	if (ret) {
96 		dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
97 		goto err1;
98 	}
99 
100 	memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
101 
102 	if (dwc->usb3_lpm_capable)
103 		props[prop_idx++].name = "usb3-lpm-capable";
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 (dwc->revision <= DWC3_REVISION_300A)
115 		props[prop_idx++].name = "quirk-broken-port-ped";
116 
117 	if (prop_idx) {
118 		ret = platform_device_add_properties(xhci, props);
119 		if (ret) {
120 			dev_err(dwc->dev, "failed to add properties to xHCI\n");
121 			goto err1;
122 		}
123 	}
124 
125 	phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
126 			  dev_name(dwc->dev));
127 	phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
128 			  dev_name(dwc->dev));
129 
130 	ret = platform_device_add(xhci);
131 	if (ret) {
132 		dev_err(dwc->dev, "failed to register xHCI device\n");
133 		goto err2;
134 	}
135 
136 	return 0;
137 err2:
138 	phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
139 			  dev_name(dwc->dev));
140 	phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
141 			  dev_name(dwc->dev));
142 err1:
143 	platform_device_put(xhci);
144 	return ret;
145 }
146 
147 void dwc3_host_exit(struct dwc3 *dwc)
148 {
149 	phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
150 			  dev_name(dwc->dev));
151 	phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
152 			  dev_name(dwc->dev));
153 	platform_device_unregister(dwc->xhci);
154 }
155