Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/shli/md
[sfrench/cifs-2.6.git] / drivers / usb / host / fhci-hub.c
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 }