xref: /linux/drivers/usb/host/fhci-hub.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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/consumer.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 
fhci_gpio_set_value(struct fhci_hcd * fhci,int gpio_nr,bool on)39 static void fhci_gpio_set_value(struct fhci_hcd *fhci, int gpio_nr, bool on)
40 {
41 	struct gpio_desc *gpiod = fhci->gpiods[gpio_nr];
42 
43 	if (!gpiod)
44 		return;
45 
46 	gpiod_set_value(gpiod, on);
47 	mdelay(5);
48 }
49 
fhci_config_transceiver(struct fhci_hcd * fhci,enum fhci_port_status status)50 void fhci_config_transceiver(struct fhci_hcd *fhci,
51 			     enum fhci_port_status status)
52 {
53 	fhci_dbg(fhci, "-> %s: %d\n", __func__, status);
54 
55 	switch (status) {
56 	case FHCI_PORT_POWER_OFF:
57 		fhci_gpio_set_value(fhci, GPIO_POWER, false);
58 		break;
59 	case FHCI_PORT_DISABLED:
60 	case FHCI_PORT_WAITING:
61 		fhci_gpio_set_value(fhci, GPIO_POWER, true);
62 		break;
63 	case FHCI_PORT_LOW:
64 		fhci_gpio_set_value(fhci, GPIO_SPEED, false);
65 		break;
66 	case FHCI_PORT_FULL:
67 		fhci_gpio_set_value(fhci, GPIO_SPEED, true);
68 		break;
69 	default:
70 		WARN_ON(1);
71 		break;
72 	}
73 
74 	fhci_dbg(fhci, "<- %s: %d\n", __func__, status);
75 }
76 
77 /* disable the USB port by clearing the EN bit in the USBMOD register */
fhci_port_disable(struct fhci_hcd * fhci)78 void fhci_port_disable(struct fhci_hcd *fhci)
79 {
80 	struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld;
81 	enum fhci_port_status port_status;
82 
83 	fhci_dbg(fhci, "-> %s\n", __func__);
84 
85 	fhci_stop_sof_timer(fhci);
86 
87 	fhci_flush_all_transmissions(usb);
88 
89 	fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld);
90 	port_status = usb->port_status;
91 	usb->port_status = FHCI_PORT_DISABLED;
92 
93 	/* Enable IDLE since we want to know if something comes along */
94 	usb->saved_msk |= USB_E_IDLE_MASK;
95 	out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
96 
97 	/* check if during the disconnection process attached new device */
98 	if (port_status == FHCI_PORT_WAITING)
99 		fhci_device_connected_interrupt(fhci);
100 	usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE;
101 	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
102 	fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld);
103 
104 	fhci_dbg(fhci, "<- %s\n", __func__);
105 }
106 
107 /* enable the USB port by setting the EN bit in the USBMOD register */
fhci_port_enable(void * lld)108 void fhci_port_enable(void *lld)
109 {
110 	struct fhci_usb *usb = (struct fhci_usb *)lld;
111 	struct fhci_hcd *fhci = usb->fhci;
112 
113 	fhci_dbg(fhci, "-> %s\n", __func__);
114 
115 	fhci_config_transceiver(fhci, usb->port_status);
116 
117 	if ((usb->port_status != FHCI_PORT_FULL) &&
118 			(usb->port_status != FHCI_PORT_LOW))
119 		fhci_start_sof_timer(fhci);
120 
121 	usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
122 	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
123 
124 	fhci_dbg(fhci, "<- %s\n", __func__);
125 }
126 
fhci_io_port_generate_reset(struct fhci_hcd * fhci)127 void fhci_io_port_generate_reset(struct fhci_hcd *fhci)
128 {
129 	fhci_dbg(fhci, "-> %s\n", __func__);
130 
131 	gpiod_direction_output(fhci->gpiods[GPIO_USBOE], 0);
132 	gpiod_direction_output(fhci->gpiods[GPIO_USBTP], 0);
133 	gpiod_direction_output(fhci->gpiods[GPIO_USBTN], 0);
134 
135 	mdelay(5);
136 
137 	qe_pin_set_dedicated(fhci->pins[PIN_USBOE]);
138 	qe_pin_set_dedicated(fhci->pins[PIN_USBTP]);
139 	qe_pin_set_dedicated(fhci->pins[PIN_USBTN]);
140 
141 	fhci_dbg(fhci, "<- %s\n", __func__);
142 }
143 
144 /* generate the RESET condition on the bus */
fhci_port_reset(void * lld)145 void fhci_port_reset(void *lld)
146 {
147 	struct fhci_usb *usb = (struct fhci_usb *)lld;
148 	struct fhci_hcd *fhci = usb->fhci;
149 	u8 mode;
150 	u16 mask;
151 
152 	fhci_dbg(fhci, "-> %s\n", __func__);
153 
154 	fhci_stop_sof_timer(fhci);
155 	/* disable the USB controller */
156 	mode = in_8(&fhci->regs->usb_usmod);
157 	out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN));
158 
159 	/* disable idle interrupts */
160 	mask = in_be16(&fhci->regs->usb_usbmr);
161 	out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK));
162 
163 	fhci_io_port_generate_reset(fhci);
164 
165 	/* enable interrupt on this endpoint */
166 	out_be16(&fhci->regs->usb_usbmr, mask);
167 
168 	/* enable the USB controller */
169 	mode = in_8(&fhci->regs->usb_usmod);
170 	out_8(&fhci->regs->usb_usmod, mode | USB_MODE_EN);
171 	fhci_start_sof_timer(fhci);
172 
173 	fhci_dbg(fhci, "<- %s\n", __func__);
174 }
175 
fhci_hub_status_data(struct usb_hcd * hcd,char * buf)176 int fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
177 {
178 	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
179 	int ret = 0;
180 	unsigned long flags;
181 
182 	fhci_dbg(fhci, "-> %s\n", __func__);
183 
184 	spin_lock_irqsave(&fhci->lock, flags);
185 
186 	if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION |
187 			USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND |
188 			USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) {
189 		*buf = 1 << 1;
190 		ret = 1;
191 		fhci_dbg(fhci, "-- %s\n", __func__);
192 	}
193 
194 	spin_unlock_irqrestore(&fhci->lock, flags);
195 
196 	fhci_dbg(fhci, "<- %s\n", __func__);
197 
198 	return ret;
199 }
200 
fhci_hub_control(struct usb_hcd * hcd,u16 typeReq,u16 wValue,u16 wIndex,char * buf,u16 wLength)201 int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
202 			    u16 wIndex, char *buf, u16 wLength)
203 {
204 	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
205 	int retval = 0;
206 	struct usb_hub_status *hub_status;
207 	struct usb_port_status *port_status;
208 	unsigned long flags;
209 
210 	spin_lock_irqsave(&fhci->lock, flags);
211 
212 	fhci_dbg(fhci, "-> %s\n", __func__);
213 
214 	switch (typeReq) {
215 	case ClearHubFeature:
216 		switch (wValue) {
217 		case C_HUB_LOCAL_POWER:
218 		case C_HUB_OVER_CURRENT:
219 			break;
220 		default:
221 			goto error;
222 		}
223 		break;
224 	case ClearPortFeature:
225 		fhci->vroot_hub->feature &= (1 << wValue);
226 
227 		switch (wValue) {
228 		case USB_PORT_FEAT_ENABLE:
229 			fhci->vroot_hub->port.wPortStatus &=
230 			    ~USB_PORT_STAT_ENABLE;
231 			fhci_port_disable(fhci);
232 			break;
233 		case USB_PORT_FEAT_C_ENABLE:
234 			fhci->vroot_hub->port.wPortChange &=
235 			    ~USB_PORT_STAT_C_ENABLE;
236 			break;
237 		case USB_PORT_FEAT_SUSPEND:
238 			fhci->vroot_hub->port.wPortStatus &=
239 			    ~USB_PORT_STAT_SUSPEND;
240 			fhci_stop_sof_timer(fhci);
241 			break;
242 		case USB_PORT_FEAT_C_SUSPEND:
243 			fhci->vroot_hub->port.wPortChange &=
244 			    ~USB_PORT_STAT_C_SUSPEND;
245 			break;
246 		case USB_PORT_FEAT_POWER:
247 			fhci->vroot_hub->port.wPortStatus &=
248 			    ~USB_PORT_STAT_POWER;
249 			fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
250 			break;
251 		case USB_PORT_FEAT_C_CONNECTION:
252 			fhci->vroot_hub->port.wPortChange &=
253 			    ~USB_PORT_STAT_C_CONNECTION;
254 			break;
255 		case USB_PORT_FEAT_C_OVER_CURRENT:
256 			fhci->vroot_hub->port.wPortChange &=
257 			    ~USB_PORT_STAT_C_OVERCURRENT;
258 			break;
259 		case USB_PORT_FEAT_C_RESET:
260 			fhci->vroot_hub->port.wPortChange &=
261 			    ~USB_PORT_STAT_C_RESET;
262 			break;
263 		default:
264 			goto error;
265 		}
266 		break;
267 	case GetHubDescriptor:
268 		memcpy(buf, root_hub_des, sizeof(root_hub_des));
269 		break;
270 	case GetHubStatus:
271 		hub_status = (struct usb_hub_status *)buf;
272 		hub_status->wHubStatus =
273 		    cpu_to_le16(fhci->vroot_hub->hub.wHubStatus);
274 		hub_status->wHubChange =
275 		    cpu_to_le16(fhci->vroot_hub->hub.wHubChange);
276 		break;
277 	case GetPortStatus:
278 		port_status = (struct usb_port_status *)buf;
279 		port_status->wPortStatus =
280 		    cpu_to_le16(fhci->vroot_hub->port.wPortStatus);
281 		port_status->wPortChange =
282 		    cpu_to_le16(fhci->vroot_hub->port.wPortChange);
283 		break;
284 	case SetHubFeature:
285 		switch (wValue) {
286 		case C_HUB_OVER_CURRENT:
287 		case C_HUB_LOCAL_POWER:
288 			break;
289 		default:
290 			goto error;
291 		}
292 		break;
293 	case SetPortFeature:
294 		fhci->vroot_hub->feature |= (1 << wValue);
295 
296 		switch (wValue) {
297 		case USB_PORT_FEAT_ENABLE:
298 			fhci->vroot_hub->port.wPortStatus |=
299 			    USB_PORT_STAT_ENABLE;
300 			fhci_port_enable(fhci->usb_lld);
301 			break;
302 		case USB_PORT_FEAT_SUSPEND:
303 			fhci->vroot_hub->port.wPortStatus |=
304 			    USB_PORT_STAT_SUSPEND;
305 			fhci_stop_sof_timer(fhci);
306 			break;
307 		case USB_PORT_FEAT_RESET:
308 			fhci->vroot_hub->port.wPortStatus |=
309 			    USB_PORT_STAT_RESET;
310 			fhci_port_reset(fhci->usb_lld);
311 			fhci->vroot_hub->port.wPortStatus |=
312 			    USB_PORT_STAT_ENABLE;
313 			fhci->vroot_hub->port.wPortStatus &=
314 			    ~USB_PORT_STAT_RESET;
315 			break;
316 		case USB_PORT_FEAT_POWER:
317 			fhci->vroot_hub->port.wPortStatus |=
318 			    USB_PORT_STAT_POWER;
319 			fhci_config_transceiver(fhci, FHCI_PORT_WAITING);
320 			break;
321 		default:
322 			goto error;
323 		}
324 		break;
325 	default:
326 error:
327 		retval = -EPIPE;
328 	}
329 
330 	fhci_dbg(fhci, "<- %s\n", __func__);
331 
332 	spin_unlock_irqrestore(&fhci->lock, flags);
333 
334 	return retval;
335 }
336