Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
fhci-hub.c
Go to the documentation of this file.
1 /*
2  * Freescale QUICC Engine USB Host Controller Driver
3  *
4  * Copyright (c) Freescale Semicondutor, Inc. 2006.
5  * Shlomi Gridish <[email protected]>
6  * Jerry Huang <[email protected]>
7  * Copyright (c) Logic Product Development, Inc. 2007
8  * Peter Barada <[email protected]>
9  * Copyright (c) MontaVista Software, Inc. 2008.
10  * Anton Vorontsov <[email protected]>
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 
57 {
58  fhci_dbg(fhci, "-> %s: %d\n", __func__, status);
59 
60  switch (status) {
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;
87 
88  fhci_dbg(fhci, "-> %s\n", __func__);
89 
90  fhci_stop_sof_timer(fhci);
91 
93 
95  port_status = usb->port_status;
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_usbmr, usb->saved_msk);
101 
102  /* check if during the disconnection process attached new device */
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 
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 
133 {
134  fhci_dbg(fhci, "-> %s\n", __func__);
135 
139 
140  mdelay(5);
141 
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_usmod);
162  out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN));
163 
164  /* disable idle interrupts */
165  mask = in_be16(&fhci->regs->usb_usbmr);
166  out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK));
167 
169 
170  /* enable interrupt on this endpoint */
171  out_be16(&fhci->regs->usb_usbmr, mask);
172 
173  /* enable the USB controller */
174  mode = in_8(&fhci->regs->usb_usmod);
175  out_8(&fhci->regs->usb_usmod, 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 |
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;
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) {
235  fhci->vroot_hub->port.wPortStatus &=
237  fhci_port_disable(fhci);
238  break;
240  fhci->vroot_hub->port.wPortChange &=
242  break;
244  fhci->vroot_hub->port.wPortStatus &=
246  fhci_stop_sof_timer(fhci);
247  break;
249  fhci->vroot_hub->port.wPortChange &=
251  break;
252  case USB_PORT_FEAT_POWER:
253  fhci->vroot_hub->port.wPortStatus &=
256  break;
258  fhci->vroot_hub->port.wPortChange &=
260  break;
262  fhci->vroot_hub->port.wPortChange &=
264  break;
266  fhci->vroot_hub->port.wPortChange &=
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) {
308  fhci->vroot_hub->port.wPortStatus |=
310  fhci_port_enable(fhci->usb_lld);
311  break;
313  fhci->vroot_hub->port.wPortStatus |=
315  fhci_stop_sof_timer(fhci);
316  break;
317  case USB_PORT_FEAT_RESET:
318  fhci->vroot_hub->port.wPortStatus |=
320  fhci_port_reset(fhci->usb_lld);
321  fhci->vroot_hub->port.wPortStatus |=
323  fhci->vroot_hub->port.wPortStatus &=
325  break;
326  case USB_PORT_FEAT_POWER:
327  fhci->vroot_hub->port.wPortStatus |=
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 }