Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
c67x00-hcd.c
Go to the documentation of this file.
1 /*
2  * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver
3  *
4  * Copyright (C) 2006-2008 Barco N.V.
5  * Derived from the Cypress cy7c67200/300 ezusb linux driver and
6  * based on multiple host controller drivers inside the linux kernel.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  * MA 02110-1301 USA.
22  */
23 
24 #include <linux/device.h>
25 #include <linux/platform_device.h>
26 #include <linux/usb.h>
27 
28 #include "c67x00.h"
29 #include "c67x00-hcd.h"
30 
31 /* --------------------------------------------------------------------------
32  * Root Hub Support
33  */
34 
35 static __u8 c67x00_hub_des[] = {
36  0x09, /* __u8 bLength; */
37  0x29, /* __u8 bDescriptorType; Hub-descriptor */
38  0x02, /* __u8 bNbrPorts; */
39  0x00, /* __u16 wHubCharacteristics; */
40  0x00, /* (per-port OC, no power switching) */
41  0x32, /* __u8 bPwrOn2pwrGood; 2ms */
42  0x00, /* __u8 bHubContrCurrent; 0 mA */
43  0x00, /* __u8 DeviceRemovable; ** 7 Ports max ** */
44  0xff, /* __u8 PortPwrCtrlMask; ** 7 ports max ** */
45 };
46 
47 static void c67x00_hub_reset_host_port(struct c67x00_sie *sie, int port)
48 {
49  struct c67x00_hcd *c67x00 = sie->private_data;
50  unsigned long flags;
51 
52  c67x00_ll_husb_reset(sie, port);
53 
54  spin_lock_irqsave(&c67x00->lock, flags);
55  c67x00_ll_husb_reset_port(sie, port);
56  spin_unlock_irqrestore(&c67x00->lock, flags);
57 
59 }
60 
61 static int c67x00_hub_status_data(struct usb_hcd *hcd, char *buf)
62 {
63  struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
64  struct c67x00_sie *sie = c67x00->sie;
65  u16 status;
66  int i;
67 
68  *buf = 0;
69  status = c67x00_ll_usb_get_status(sie);
70  for (i = 0; i < C67X00_PORTS; i++)
71  if (status & PORT_CONNECT_CHANGE(i))
72  *buf |= (1 << i);
73 
74  /* bit 0 denotes hub change, b1..n port change */
75  *buf <<= 1;
76 
77  return !!*buf;
78 }
79 
80 static int c67x00_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
81  u16 wIndex, char *buf, u16 wLength)
82 {
83  struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
84  struct c67x00_sie *sie = c67x00->sie;
85  u16 status, usb_status;
86  int len = 0;
87  unsigned int port = wIndex-1;
89 
90  switch (typeReq) {
91 
92  case GetHubStatus:
93  *(__le32 *) buf = cpu_to_le32(0);
94  len = 4; /* hub power */
95  break;
96 
97  case GetPortStatus:
98  if (wIndex > C67X00_PORTS)
99  return -EPIPE;
100 
101  status = c67x00_ll_usb_get_status(sie);
102  usb_status = c67x00_ll_get_usb_ctl(sie);
103 
104  wPortChange = 0;
105  if (status & PORT_CONNECT_CHANGE(port))
106  wPortChange |= USB_PORT_STAT_C_CONNECTION;
107 
108  wPortStatus = USB_PORT_STAT_POWER;
109  if (!(status & PORT_SE0_STATUS(port)))
110  wPortStatus |= USB_PORT_STAT_CONNECTION;
111  if (usb_status & LOW_SPEED_PORT(port)) {
112  wPortStatus |= USB_PORT_STAT_LOW_SPEED;
113  c67x00->low_speed_ports |= (1 << port);
114  } else
115  c67x00->low_speed_ports &= ~(1 << port);
116 
117  if (usb_status & SOF_EOP_EN(port))
118  wPortStatus |= USB_PORT_STAT_ENABLE;
119 
120  *(__le16 *) buf = cpu_to_le16(wPortStatus);
121  *(__le16 *) (buf + 2) = cpu_to_le16(wPortChange);
122  len = 4;
123  break;
124 
125  case SetHubFeature: /* We don't implement these */
126  case ClearHubFeature:
127  switch (wValue) {
128  case C_HUB_OVER_CURRENT:
129  case C_HUB_LOCAL_POWER:
130  len = 0;
131  break;
132 
133  default:
134  return -EPIPE;
135  }
136  break;
137 
138  case SetPortFeature:
139  if (wIndex > C67X00_PORTS)
140  return -EPIPE;
141 
142  switch (wValue) {
144  dev_dbg(c67x00_hcd_dev(c67x00),
145  "SetPortFeature %d (SUSPEND)\n", port);
146  len = 0;
147  break;
148 
149  case USB_PORT_FEAT_RESET:
150  c67x00_hub_reset_host_port(sie, port);
151  len = 0;
152  break;
153 
154  case USB_PORT_FEAT_POWER:
155  /* Power always enabled */
156  len = 0;
157  break;
158 
159  default:
160  dev_dbg(c67x00_hcd_dev(c67x00),
161  "%s: SetPortFeature %d (0x%04x) Error!\n",
162  __func__, port, wValue);
163  return -EPIPE;
164  }
165  break;
166 
167  case ClearPortFeature:
168  if (wIndex > C67X00_PORTS)
169  return -EPIPE;
170 
171  switch (wValue) {
173  /* Reset the port so that the c67x00 also notices the
174  * disconnect */
175  c67x00_hub_reset_host_port(sie, port);
176  len = 0;
177  break;
178 
180  dev_dbg(c67x00_hcd_dev(c67x00),
181  "ClearPortFeature (%d): C_ENABLE\n", port);
182  len = 0;
183  break;
184 
186  dev_dbg(c67x00_hcd_dev(c67x00),
187  "ClearPortFeature (%d): SUSPEND\n", port);
188  len = 0;
189  break;
190 
192  dev_dbg(c67x00_hcd_dev(c67x00),
193  "ClearPortFeature (%d): C_SUSPEND\n", port);
194  len = 0;
195  break;
196 
197  case USB_PORT_FEAT_POWER:
198  dev_dbg(c67x00_hcd_dev(c67x00),
199  "ClearPortFeature (%d): POWER\n", port);
200  return -EPIPE;
201 
204  PORT_CONNECT_CHANGE(port));
205  len = 0;
206  break;
207 
209  dev_dbg(c67x00_hcd_dev(c67x00),
210  "ClearPortFeature (%d): OVER_CURRENT\n", port);
211  len = 0;
212  break;
213 
215  dev_dbg(c67x00_hcd_dev(c67x00),
216  "ClearPortFeature (%d): C_RESET\n", port);
217  len = 0;
218  break;
219 
220  default:
221  dev_dbg(c67x00_hcd_dev(c67x00),
222  "%s: ClearPortFeature %d (0x%04x) Error!\n",
223  __func__, port, wValue);
224  return -EPIPE;
225  }
226  break;
227 
228  case GetHubDescriptor:
229  len = min_t(unsigned int, sizeof(c67x00_hub_des), wLength);
230  memcpy(buf, c67x00_hub_des, len);
231  break;
232 
233  default:
234  dev_dbg(c67x00_hcd_dev(c67x00), "%s: unknown\n", __func__);
235  return -EPIPE;
236  }
237 
238  return 0;
239 }
240 
241 /* ---------------------------------------------------------------------
242  * Main part of host controller driver
243  */
244 
250 static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg)
251 {
252  struct c67x00_hcd *c67x00 = sie->private_data;
253  struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
254 
255  /* Handle sie message flags */
256  if (msg) {
257  if (msg & HUSB_TDListDone)
258  c67x00_sched_kick(c67x00);
259  else
260  dev_warn(c67x00_hcd_dev(c67x00),
261  "Unknown SIE msg flag(s): 0x%04x\n", msg);
262  }
263 
264  if (unlikely(hcd->state == HC_STATE_HALT))
265  return;
266 
267  if (!HCD_HW_ACCESSIBLE(hcd))
268  return;
269 
270  /* Handle Start of frame events */
271  if (int_status & SOFEOP_FLG(sie->sie_num)) {
273  c67x00_sched_kick(c67x00);
274  }
275 }
276 
280 static int c67x00_hcd_start(struct usb_hcd *hcd)
281 {
282  hcd->uses_new_polling = 1;
283  hcd->state = HC_STATE_RUNNING;
284  set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
285 
286  return 0;
287 }
288 
292 static void c67x00_hcd_stop(struct usb_hcd *hcd)
293 {
294  /* Nothing to do */
295 }
296 
297 static int c67x00_hcd_get_frame(struct usb_hcd *hcd)
298 {
299  struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
300  u16 temp_val;
301 
302  dev_dbg(c67x00_hcd_dev(c67x00), "%s\n", __func__);
303  temp_val = c67x00_ll_husb_get_frame(c67x00->sie);
304  temp_val &= HOST_FRAME_MASK;
305  return temp_val ? (temp_val - 1) : HOST_FRAME_MASK;
306 }
307 
308 static struct hc_driver c67x00_hc_driver = {
309  .description = "c67x00-hcd",
310  .product_desc = "Cypress C67X00 Host Controller",
311  .hcd_priv_size = sizeof(struct c67x00_hcd),
312  .flags = HCD_USB11 | HCD_MEMORY,
313 
314  /*
315  * basic lifecycle operations
316  */
317  .start = c67x00_hcd_start,
318  .stop = c67x00_hcd_stop,
319 
320  /*
321  * managing i/o requests and associated device resources
322  */
323  .urb_enqueue = c67x00_urb_enqueue,
324  .urb_dequeue = c67x00_urb_dequeue,
325  .endpoint_disable = c67x00_endpoint_disable,
326 
327  /*
328  * scheduling support
329  */
330  .get_frame_number = c67x00_hcd_get_frame,
331 
332  /*
333  * root hub support
334  */
335  .hub_status_data = c67x00_hub_status_data,
336  .hub_control = c67x00_hub_control,
337 };
338 
339 /* ---------------------------------------------------------------------
340  * Setup/Teardown routines
341  */
342 
343 int c67x00_hcd_probe(struct c67x00_sie *sie)
344 {
345  struct c67x00_hcd *c67x00;
346  struct usb_hcd *hcd;
347  unsigned long flags;
348  int retval;
349 
350  if (usb_disabled())
351  return -ENODEV;
352 
353  hcd = usb_create_hcd(&c67x00_hc_driver, sie_dev(sie), "c67x00_sie");
354  if (!hcd) {
355  retval = -ENOMEM;
356  goto err0;
357  }
358  c67x00 = hcd_to_c67x00_hcd(hcd);
359 
360  spin_lock_init(&c67x00->lock);
361  c67x00->sie = sie;
362 
363  INIT_LIST_HEAD(&c67x00->list[PIPE_ISOCHRONOUS]);
364  INIT_LIST_HEAD(&c67x00->list[PIPE_INTERRUPT]);
365  INIT_LIST_HEAD(&c67x00->list[PIPE_CONTROL]);
366  INIT_LIST_HEAD(&c67x00->list[PIPE_BULK]);
367  c67x00->urb_count = 0;
368  INIT_LIST_HEAD(&c67x00->td_list);
371  c67x00->max_frame_bw = MAX_FRAME_BW_STD;
372 
374 
375  init_completion(&c67x00->endpoint_disable);
376  retval = c67x00_sched_start_scheduler(c67x00);
377  if (retval)
378  goto err1;
379 
380  retval = usb_add_hcd(hcd, 0, 0);
381  if (retval) {
382  dev_dbg(sie_dev(sie), "%s: usb_add_hcd returned %d\n",
383  __func__, retval);
384  goto err2;
385  }
386 
387  spin_lock_irqsave(&sie->lock, flags);
388  sie->private_data = c67x00;
389  sie->irq = c67x00_hcd_irq;
390  spin_unlock_irqrestore(&sie->lock, flags);
391 
392  return retval;
393 
394  err2:
396  err1:
397  usb_put_hcd(hcd);
398  err0:
399  return retval;
400 }
401 
402 /* may be called with controller, bus, and devices active */
403 void c67x00_hcd_remove(struct c67x00_sie *sie)
404 {
405  struct c67x00_hcd *c67x00 = sie->private_data;
406  struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
407 
409  usb_remove_hcd(hcd);
410  usb_put_hcd(hcd);
411 }