aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/fhci-hub.c
diff options
context:
space:
mode:
authorAnton Vorontsov <avorontsov@ru.mvista.com>2009-01-09 21:03:21 -0500
committerGreg Kroah-Hartman <gregkh@kvm.kroah.org>2009-01-27 19:15:38 -0500
commit236dd4d18f293e3c9798f35c08272196826a980d (patch)
tree5e8f7dc48318e82c34758c1a807d034034b96221 /drivers/usb/host/fhci-hub.c
parentfc91be2ad03e0d243418414a854665274d560ca2 (diff)
USB: Driver for Freescale QUICC Engine USB Host Controller
This patch adds support for the FHCI USB controller, as found in the Freescale MPC836x and MPC832x processors. It can support Full or Low speed modes. Quite a lot the hardware is doing by itself (SOF generation, CRC generation and checking), though scheduling and retransmission is on software's shoulders. This controller does not integrate the root hub, so this driver also fakes one-port hub. External hub is required to support more than one device. Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/fhci-hub.c')
-rw-r--r--drivers/usb/host/fhci-hub.c345
1 files changed, 345 insertions, 0 deletions
diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c
new file mode 100644
index 000000000000..0cfaedc3e124
--- /dev/null
+++ b/drivers/usb/host/fhci-hub.c
@@ -0,0 +1,345 @@
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/gpio.h>
26#include <asm/qe.h>
27#include "../core/hcd.h"
28#include "fhci.h"
29
30/* virtual root hub specific descriptor */
31static 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
43static 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
55void 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 */
83void 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 */
113void 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
132void 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 */
150void 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
181int 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
206int 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:
336error:
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}