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