xref: /linux/drivers/usb/dwc3/host.c (revision 1b78070aaef63512688aebfbc82365ef9d6660f1)
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/irq.h>
11 #include <linux/of.h>
12 #include <linux/platform_device.h>
13 #include <linux/usb.h>
14 #include <linux/usb/hcd.h>
15 #include <linux/bitfield.h>
16 
17 #include "../host/xhci-port.h"
18 #include "../host/xhci-ext-caps.h"
19 #include "../host/xhci-caps.h"
20 #include "../host/xhci-plat.h"
21 #include "core.h"
22 
23 #define XHCI_HCSPARAMS1		0x4
24 #define XHCI_PORTSC_BASE	0x400
25 
26 /**
27  * dwc3_power_off_all_roothub_ports - Power off all Root hub ports
28  * @dwc: Pointer to our controller context structure
29  */
30 static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
31 {
32 	void __iomem *xhci_regs;
33 	u32 op_regs_base;
34 	int port_num;
35 	u32 offset;
36 	u32 reg;
37 	int i;
38 
39 	/* xhci regs are not mapped yet, do it temporarily here */
40 	if (dwc->xhci_resources[0].start) {
41 		if (dwc->xhci_resources[0].flags & IORESOURCE_MEM_NONPOSTED)
42 			xhci_regs = ioremap_np(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END);
43 		else
44 			xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END);
45 		if (!xhci_regs) {
46 			dev_err(dwc->dev, "Failed to ioremap xhci_regs\n");
47 			return;
48 		}
49 
50 		op_regs_base = FIELD_GET(HC_LENGTH, readl(xhci_regs));
51 		reg = readl(xhci_regs + XHCI_HCSPARAMS1);
52 		port_num = FIELD_GET(HCS_MAX_PORTS, reg);
53 
54 		for (i = 1; i <= port_num; i++) {
55 			offset = op_regs_base + XHCI_PORTSC_BASE + 0x10 * (i - 1);
56 			reg = readl(xhci_regs + offset);
57 			reg &= ~PORT_POWER;
58 			writel(reg, xhci_regs + offset);
59 		}
60 
61 		iounmap(xhci_regs);
62 	} else {
63 		dev_err(dwc->dev, "xhci base reg invalid\n");
64 	}
65 }
66 
67 static void dwc3_xhci_plat_start(struct usb_hcd *hcd)
68 {
69 	struct platform_device *pdev;
70 	struct dwc3 *dwc;
71 
72 	if (!usb_hcd_is_primary_hcd(hcd))
73 		return;
74 
75 	pdev = to_platform_device(hcd->self.controller);
76 	dwc = dev_get_drvdata(pdev->dev.parent);
77 
78 	dwc3_enable_susphy(dwc, true);
79 }
80 
81 static const struct xhci_plat_priv dwc3_xhci_plat_quirk = {
82 	.plat_start = dwc3_xhci_plat_start,
83 };
84 
85 static void dwc3_host_fill_xhci_irq_res(struct dwc3 *dwc,
86 					int irq, char *name)
87 {
88 	struct platform_device *pdev = to_platform_device(dwc->dev);
89 	struct device_node *np = dev_of_node(&pdev->dev);
90 
91 	dwc->xhci_resources[1].start = irq;
92 	dwc->xhci_resources[1].end = irq;
93 	dwc->xhci_resources[1].flags = IORESOURCE_IRQ | irq_get_trigger_type(irq);
94 	if (!name && np)
95 		dwc->xhci_resources[1].name = of_node_full_name(pdev->dev.of_node);
96 	else
97 		dwc->xhci_resources[1].name = name;
98 }
99 
100 static int dwc3_host_get_irq(struct dwc3 *dwc)
101 {
102 	struct platform_device	*dwc3_pdev = to_platform_device(dwc->dev);
103 	int irq;
104 
105 	irq = platform_get_irq_byname_optional(dwc3_pdev, "host");
106 	if (irq > 0) {
107 		dwc3_host_fill_xhci_irq_res(dwc, irq, "host");
108 		goto out;
109 	}
110 
111 	if (irq == -EPROBE_DEFER)
112 		goto out;
113 
114 	irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3");
115 	if (irq > 0) {
116 		dwc3_host_fill_xhci_irq_res(dwc, irq, "dwc_usb3");
117 		goto out;
118 	}
119 
120 	if (irq == -EPROBE_DEFER)
121 		goto out;
122 
123 	irq = platform_get_irq(dwc3_pdev, 0);
124 	if (irq > 0)
125 		dwc3_host_fill_xhci_irq_res(dwc, irq, NULL);
126 
127 out:
128 	return irq;
129 }
130 
131 int dwc3_host_init(struct dwc3 *dwc)
132 {
133 	struct property_entry	props[6];
134 	struct platform_device	*xhci;
135 	int			ret, irq;
136 	int			prop_idx = 0;
137 
138 	/*
139 	 * Some platforms need to power off all Root hub ports immediately after DWC3 set to host
140 	 * mode to avoid VBUS glitch happen when xhci get reset later.
141 	 */
142 	dwc3_power_off_all_roothub_ports(dwc);
143 
144 	irq = dwc3_host_get_irq(dwc);
145 	if (irq < 0)
146 		return irq;
147 
148 	xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
149 	if (!xhci) {
150 		dev_err(dwc->dev, "couldn't allocate xHCI device\n");
151 		return -ENOMEM;
152 	}
153 
154 	xhci->dev.parent	= dwc->dev;
155 
156 	dwc->xhci = xhci;
157 
158 	ret = platform_device_add_resources(xhci, dwc->xhci_resources,
159 						DWC3_XHCI_RESOURCES_NUM);
160 	if (ret) {
161 		dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
162 		goto err;
163 	}
164 
165 	memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
166 
167 	props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-sg-trb-cache-size-quirk");
168 
169 	props[prop_idx++] = PROPERTY_ENTRY_BOOL("write-64-hi-lo-quirk");
170 
171 	if (dwc->usb3_lpm_capable)
172 		props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");
173 
174 	if (dwc->usb2_lpm_disable)
175 		props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb2-lpm-disable");
176 
177 	/**
178 	 * WORKAROUND: dwc3 revisions <=3.00a have a limitation
179 	 * where Port Disable command doesn't work.
180 	 *
181 	 * The suggested workaround is that we avoid Port Disable
182 	 * completely.
183 	 *
184 	 * This following flag tells XHCI to do just that.
185 	 */
186 	if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
187 		props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
188 
189 	props[prop_idx++] = PROPERTY_ENTRY_U16("num-hc-interrupters",
190 					       dwc->num_hc_interrupters);
191 
192 	if (prop_idx) {
193 		ret = device_create_managed_software_node(&xhci->dev, props, NULL);
194 		if (ret) {
195 			dev_err(dwc->dev, "failed to add properties to xHCI\n");
196 			goto err;
197 		}
198 	}
199 
200 	ret = platform_device_add_data(xhci, &dwc3_xhci_plat_quirk,
201 				       sizeof(struct xhci_plat_priv));
202 	if (ret)
203 		goto err;
204 
205 	ret = platform_device_add(xhci);
206 	if (ret) {
207 		dev_err(dwc->dev, "failed to register xHCI device\n");
208 		goto err;
209 	}
210 
211 	if (dwc->sys_wakeup) {
212 		/* Restore wakeup setting if switched from device */
213 		device_wakeup_enable(dwc->sysdev);
214 
215 		/* Pass on wakeup setting to the new xhci platform device */
216 		device_init_wakeup(&xhci->dev, true);
217 	}
218 
219 	return 0;
220 err:
221 	platform_device_put(xhci);
222 	return ret;
223 }
224 EXPORT_SYMBOL_GPL(dwc3_host_init);
225 
226 void dwc3_host_exit(struct dwc3 *dwc)
227 {
228 	if (dwc->sys_wakeup)
229 		device_init_wakeup(&dwc->xhci->dev, false);
230 
231 	dwc3_enable_susphy(dwc, true);
232 	platform_device_unregister(dwc->xhci);
233 	dwc->xhci = NULL;
234 }
235 EXPORT_SYMBOL_GPL(dwc3_host_exit);
236