xref: /openbmc/linux/drivers/usb/host/fhci-hub.c (revision 55fd7e02)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Freescale QUICC Engine USB Host Controller Driver
4  *
5  * Copyright (c) Freescale Semicondutor, Inc. 2006.
6  *               Shlomi Gridish <gridish@freescale.com>
7  *               Jerry Huang <Chang-Ming.Huang@freescale.com>
8  * Copyright (c) Logic Product Development, Inc. 2007
9  *               Peter Barada <peterb@logicpd.com>
10  * Copyright (c) MontaVista Software, Inc. 2008.
11  *               Anton Vorontsov <avorontsov@ru.mvista.com>
12  */
13 
14 #include <linux/kernel.h>
15 #include <linux/types.h>
16 #include <linux/spinlock.h>
17 #include <linux/delay.h>
18 #include <linux/errno.h>
19 #include <linux/io.h>
20 #include <linux/usb.h>
21 #include <linux/usb/hcd.h>
22 #include <linux/gpio.h>
23 #include <soc/fsl/qe/qe.h>
24 #include "fhci.h"
25 
26 /* virtual root hub specific descriptor */
27 static u8 root_hub_des[] = {
28 	0x09, /* blength */
29 	USB_DT_HUB, /* bDescriptorType;hub-descriptor */
30 	0x01, /* bNbrPorts */
31 	HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM, /* wHubCharacteristics */
32 	0x00, /* per-port power, no overcurrent */
33 	0x01, /* bPwrOn2pwrGood;2ms */
34 	0x00, /* bHubContrCurrent;0mA */
35 	0x00, /* DeviceRemoveable */
36 	0xff, /* PortPwrCtrlMask */
37 };
38 
39 static void fhci_gpio_set_value(struct fhci_hcd *fhci, int gpio_nr, bool on)
40 {
41 	int gpio = fhci->gpios[gpio_nr];
42 	bool alow = fhci->alow_gpios[gpio_nr];
43 
44 	if (!gpio_is_valid(gpio))
45 		return;
46 
47 	gpio_set_value(gpio, on ^ alow);
48 	mdelay(5);
49 }
50 
51 void fhci_config_transceiver(struct fhci_hcd *fhci,
52 			     enum fhci_port_status status)
53 {
54 	fhci_dbg(fhci, "-> %s: %d\n", __func__, status);
55 
56 	switch (status) {
57 	case FHCI_PORT_POWER_OFF:
58 		fhci_gpio_set_value(fhci, GPIO_POWER, false);
59 		break;
60 	case FHCI_PORT_DISABLED:
61 	case FHCI_PORT_WAITING:
62 		fhci_gpio_set_value(fhci, GPIO_POWER, true);
63 		break;
64 	case FHCI_PORT_LOW:
65 		fhci_gpio_set_value(fhci, GPIO_SPEED, false);
66 		break;
67 	case FHCI_PORT_FULL:
68 		fhci_gpio_set_value(fhci, GPIO_SPEED, true);
69 		break;
70 	default:
71 		WARN_ON(1);
72 		break;
73 	}
74 
75 	fhci_dbg(fhci, "<- %s: %d\n", __func__, status);
76 }
77 
78 /* disable the USB port by clearing the EN bit in the USBMOD register */
79 void fhci_port_disable(struct fhci_hcd *fhci)
80 {
81 	struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld;
82 	enum fhci_port_status port_status;
83 
84 	fhci_dbg(fhci, "-> %s\n", __func__);
85 
86 	fhci_stop_sof_timer(fhci);
87 
88 	fhci_flush_all_transmissions(usb);
89 
90 	fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld);
91 	port_status = usb->port_status;
92 	usb->port_status = FHCI_PORT_DISABLED;
93 
94 	/* Enable IDLE since we want to know if something comes along */
95 	usb->saved_msk |= USB_E_IDLE_MASK;
96 	out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
97 
98 	/* check if during the disconnection process attached new device */
99 	if (port_status == FHCI_PORT_WAITING)
100 		fhci_device_connected_interrupt(fhci);
101 	usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE;
102 	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
103 	fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld);
104 
105 	fhci_dbg(fhci, "<- %s\n", __func__);
106 }
107 
108 /* enable the USB port by setting the EN bit in the USBMOD register */
109 void fhci_port_enable(void *lld)
110 {
111 	struct fhci_usb *usb = (struct fhci_usb *)lld;
112 	struct fhci_hcd *fhci = usb->fhci;
113 
114 	fhci_dbg(fhci, "-> %s\n", __func__);
115 
116 	fhci_config_transceiver(fhci, usb->port_status);
117 
118 	if ((usb->port_status != FHCI_PORT_FULL) &&
119 			(usb->port_status != FHCI_PORT_LOW))
120 		fhci_start_sof_timer(fhci);
121 
122 	usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
123 	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
124 
125 	fhci_dbg(fhci, "<- %s\n", __func__);
126 }
127 
128 void fhci_io_port_generate_reset(struct fhci_hcd *fhci)
129 {
130 	fhci_dbg(fhci, "-> %s\n", __func__);
131 
132 	gpio_direction_output(fhci->gpios[GPIO_USBOE], 0);
133 	gpio_direction_output(fhci->gpios[GPIO_USBTP], 0);
134 	gpio_direction_output(fhci->gpios[GPIO_USBTN], 0);
135 
136 	mdelay(5);
137 
138 	qe_pin_set_dedicated(fhci->pins[PIN_USBOE]);
139 	qe_pin_set_dedicated(fhci->pins[PIN_USBTP]);
140 	qe_pin_set_dedicated(fhci->pins[PIN_USBTN]);
141 
142 	fhci_dbg(fhci, "<- %s\n", __func__);
143 }
144 
145 /* generate the RESET condition on the bus */
146 void fhci_port_reset(void *lld)
147 {
148 	struct fhci_usb *usb = (struct fhci_usb *)lld;
149 	struct fhci_hcd *fhci = usb->fhci;
150 	u8 mode;
151 	u16 mask;
152 
153 	fhci_dbg(fhci, "-> %s\n", __func__);
154 
155 	fhci_stop_sof_timer(fhci);
156 	/* disable the USB controller */
157 	mode = in_8(&fhci->regs->usb_usmod);
158 	out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN));
159 
160 	/* disable idle interrupts */
161 	mask = in_be16(&fhci->regs->usb_usbmr);
162 	out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK));
163 
164 	fhci_io_port_generate_reset(fhci);
165 
166 	/* enable interrupt on this endpoint */
167 	out_be16(&fhci->regs->usb_usbmr, mask);
168 
169 	/* enable the USB controller */
170 	mode = in_8(&fhci->regs->usb_usmod);
171 	out_8(&fhci->regs->usb_usmod, mode | USB_MODE_EN);
172 	fhci_start_sof_timer(fhci);
173 
174 	fhci_dbg(fhci, "<- %s\n", __func__);
175 }
176 
177 int fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
178 {
179 	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
180 	int ret = 0;
181 	unsigned long flags;
182 
183 	fhci_dbg(fhci, "-> %s\n", __func__);
184 
185 	spin_lock_irqsave(&fhci->lock, flags);
186 
187 	if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION |
188 			USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND |
189 			USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) {
190 		*buf = 1 << 1;
191 		ret = 1;
192 		fhci_dbg(fhci, "-- %s\n", __func__);
193 	}
194 
195 	spin_unlock_irqrestore(&fhci->lock, flags);
196 
197 	fhci_dbg(fhci, "<- %s\n", __func__);
198 
199 	return ret;
200 }
201 
202 int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
203 			    u16 wIndex, char *buf, u16 wLength)
204 {
205 	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
206 	int retval = 0;
207 	struct usb_hub_status *hub_status;
208 	struct usb_port_status *port_status;
209 	unsigned long flags;
210 
211 	spin_lock_irqsave(&fhci->lock, flags);
212 
213 	fhci_dbg(fhci, "-> %s\n", __func__);
214 
215 	switch (typeReq) {
216 	case ClearHubFeature:
217 		switch (wValue) {
218 		case C_HUB_LOCAL_POWER:
219 		case C_HUB_OVER_CURRENT:
220 			break;
221 		default:
222 			goto error;
223 		}
224 		break;
225 	case ClearPortFeature:
226 		fhci->vroot_hub->feature &= (1 << wValue);
227 
228 		switch (wValue) {
229 		case USB_PORT_FEAT_ENABLE:
230 			fhci->vroot_hub->port.wPortStatus &=
231 			    ~USB_PORT_STAT_ENABLE;
232 			fhci_port_disable(fhci);
233 			break;
234 		case USB_PORT_FEAT_C_ENABLE:
235 			fhci->vroot_hub->port.wPortChange &=
236 			    ~USB_PORT_STAT_C_ENABLE;
237 			break;
238 		case USB_PORT_FEAT_SUSPEND:
239 			fhci->vroot_hub->port.wPortStatus &=
240 			    ~USB_PORT_STAT_SUSPEND;
241 			fhci_stop_sof_timer(fhci);
242 			break;
243 		case USB_PORT_FEAT_C_SUSPEND:
244 			fhci->vroot_hub->port.wPortChange &=
245 			    ~USB_PORT_STAT_C_SUSPEND;
246 			break;
247 		case USB_PORT_FEAT_POWER:
248 			fhci->vroot_hub->port.wPortStatus &=
249 			    ~USB_PORT_STAT_POWER;
250 			fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
251 			break;
252 		case USB_PORT_FEAT_C_CONNECTION:
253 			fhci->vroot_hub->port.wPortChange &=
254 			    ~USB_PORT_STAT_C_CONNECTION;
255 			break;
256 		case USB_PORT_FEAT_C_OVER_CURRENT:
257 			fhci->vroot_hub->port.wPortChange &=
258 			    ~USB_PORT_STAT_C_OVERCURRENT;
259 			break;
260 		case USB_PORT_FEAT_C_RESET:
261 			fhci->vroot_hub->port.wPortChange &=
262 			    ~USB_PORT_STAT_C_RESET;
263 			break;
264 		default:
265 			goto error;
266 		}
267 		break;
268 	case GetHubDescriptor:
269 		memcpy(buf, root_hub_des, sizeof(root_hub_des));
270 		break;
271 	case GetHubStatus:
272 		hub_status = (struct usb_hub_status *)buf;
273 		hub_status->wHubStatus =
274 		    cpu_to_le16(fhci->vroot_hub->hub.wHubStatus);
275 		hub_status->wHubChange =
276 		    cpu_to_le16(fhci->vroot_hub->hub.wHubChange);
277 		break;
278 	case GetPortStatus:
279 		port_status = (struct usb_port_status *)buf;
280 		port_status->wPortStatus =
281 		    cpu_to_le16(fhci->vroot_hub->port.wPortStatus);
282 		port_status->wPortChange =
283 		    cpu_to_le16(fhci->vroot_hub->port.wPortChange);
284 		break;
285 	case SetHubFeature:
286 		switch (wValue) {
287 		case C_HUB_OVER_CURRENT:
288 		case C_HUB_LOCAL_POWER:
289 			break;
290 		default:
291 			goto error;
292 		}
293 		break;
294 	case SetPortFeature:
295 		fhci->vroot_hub->feature |= (1 << wValue);
296 
297 		switch (wValue) {
298 		case USB_PORT_FEAT_ENABLE:
299 			fhci->vroot_hub->port.wPortStatus |=
300 			    USB_PORT_STAT_ENABLE;
301 			fhci_port_enable(fhci->usb_lld);
302 			break;
303 		case USB_PORT_FEAT_SUSPEND:
304 			fhci->vroot_hub->port.wPortStatus |=
305 			    USB_PORT_STAT_SUSPEND;
306 			fhci_stop_sof_timer(fhci);
307 			break;
308 		case USB_PORT_FEAT_RESET:
309 			fhci->vroot_hub->port.wPortStatus |=
310 			    USB_PORT_STAT_RESET;
311 			fhci_port_reset(fhci->usb_lld);
312 			fhci->vroot_hub->port.wPortStatus |=
313 			    USB_PORT_STAT_ENABLE;
314 			fhci->vroot_hub->port.wPortStatus &=
315 			    ~USB_PORT_STAT_RESET;
316 			break;
317 		case USB_PORT_FEAT_POWER:
318 			fhci->vroot_hub->port.wPortStatus |=
319 			    USB_PORT_STAT_POWER;
320 			fhci_config_transceiver(fhci, FHCI_PORT_WAITING);
321 			break;
322 		default:
323 			goto error;
324 		}
325 		break;
326 	default:
327 error:
328 		retval = -EPIPE;
329 	}
330 
331 	fhci_dbg(fhci, "<- %s\n", __func__);
332 
333 	spin_unlock_irqrestore(&fhci->lock, flags);
334 
335 	return retval;
336 }
337