Commit | Line | Data |
---|---|---|
b164935b AK |
1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | |
3 | * License. See the file "COPYING" in the main directory of this archive | |
4 | * for more details. | |
5 | * | |
6 | * Copyright (C) 2008 Cavium Networks | |
7 | */ | |
8 | #include <linux/kernel.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/init.h> | |
11 | #include <linux/pci.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/usb.h> | |
16 | ||
17 | #include <asm/time.h> | |
18 | #include <asm/delay.h> | |
19 | ||
20 | #include <asm/octeon/cvmx.h> | |
21 | #include "cvmx-usb.h" | |
22 | #include <asm/octeon/cvmx-iob-defs.h> | |
23 | ||
24 | #include <linux/usb/hcd.h> | |
25 | ||
26 | //#define DEBUG_CALL(format, ...) printk(format, ##__VA_ARGS__) | |
27 | #define DEBUG_CALL(format, ...) do {} while (0) | |
28 | //#define DEBUG_SUBMIT(format, ...) printk(format, ##__VA_ARGS__) | |
29 | #define DEBUG_SUBMIT(format, ...) do {} while (0) | |
30 | //#define DEBUG_ROOT_HUB(format, ...) printk(format, ##__VA_ARGS__) | |
31 | #define DEBUG_ROOT_HUB(format, ...) do {} while (0) | |
32 | //#define DEBUG_ERROR(format, ...) printk(format, ##__VA_ARGS__) | |
33 | #define DEBUG_ERROR(format, ...) do {} while (0) | |
34 | #define DEBUG_FATAL(format, ...) printk(format, ##__VA_ARGS__) | |
35 | ||
36 | struct octeon_hcd { | |
37 | spinlock_t lock; | |
38 | cvmx_usb_state_t usb; | |
39 | struct tasklet_struct dequeue_tasklet; | |
40 | struct list_head dequeue_list; | |
41 | }; | |
42 | ||
43 | /* convert between an HCD pointer and the corresponding struct octeon_hcd */ | |
44 | static inline struct octeon_hcd *hcd_to_octeon(struct usb_hcd *hcd) | |
45 | { | |
46 | return (struct octeon_hcd *)(hcd->hcd_priv); | |
47 | } | |
48 | ||
49 | static inline struct usb_hcd *octeon_to_hcd(struct octeon_hcd *p) | |
50 | { | |
51 | return container_of((void *)p, struct usb_hcd, hcd_priv); | |
52 | } | |
53 | ||
54 | static inline struct octeon_hcd *cvmx_usb_to_octeon(cvmx_usb_state_t *p) | |
55 | { | |
56 | return container_of(p, struct octeon_hcd, usb); | |
57 | } | |
58 | ||
59 | static irqreturn_t octeon_usb_irq(struct usb_hcd *hcd) | |
60 | { | |
61 | struct octeon_hcd *priv = hcd_to_octeon(hcd); | |
62 | unsigned long flags; | |
63 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
64 | spin_lock_irqsave(&priv->lock, flags); | |
65 | cvmx_usb_poll(&priv->usb); | |
66 | spin_unlock_irqrestore(&priv->lock, flags); | |
67 | return IRQ_HANDLED; | |
68 | } | |
69 | ||
70 | static void octeon_usb_port_callback(cvmx_usb_state_t *usb, | |
71 | cvmx_usb_callback_t reason, | |
72 | cvmx_usb_complete_t status, | |
73 | int pipe_handle, | |
74 | int submit_handle, | |
75 | int bytes_transferred, | |
76 | void *user_data) | |
77 | { | |
78 | struct octeon_hcd *priv = cvmx_usb_to_octeon(usb); | |
79 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
80 | spin_unlock(&priv->lock); | |
81 | usb_hcd_poll_rh_status(octeon_to_hcd(priv)); | |
82 | spin_lock(&priv->lock); | |
83 | } | |
84 | ||
85 | static int octeon_usb_start(struct usb_hcd *hcd) | |
86 | { | |
87 | struct octeon_hcd *priv = hcd_to_octeon(hcd); | |
88 | unsigned long flags; | |
89 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
90 | hcd->state = HC_STATE_RUNNING; | |
91 | spin_lock_irqsave(&priv->lock, flags); | |
92 | cvmx_usb_register_callback(&priv->usb, CVMX_USB_CALLBACK_PORT_CHANGED, | |
93 | octeon_usb_port_callback, NULL); | |
94 | spin_unlock_irqrestore(&priv->lock, flags); | |
95 | return 0; | |
96 | } | |
97 | ||
98 | static void octeon_usb_stop(struct usb_hcd *hcd) | |
99 | { | |
100 | struct octeon_hcd *priv = hcd_to_octeon(hcd); | |
101 | unsigned long flags; | |
102 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
103 | spin_lock_irqsave(&priv->lock, flags); | |
104 | cvmx_usb_register_callback(&priv->usb, CVMX_USB_CALLBACK_PORT_CHANGED, | |
105 | NULL, NULL); | |
106 | spin_unlock_irqrestore(&priv->lock, flags); | |
107 | hcd->state = HC_STATE_HALT; | |
108 | } | |
109 | ||
110 | static int octeon_usb_get_frame_number(struct usb_hcd *hcd) | |
111 | { | |
112 | struct octeon_hcd *priv = hcd_to_octeon(hcd); | |
113 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
114 | return cvmx_usb_get_frame_number(&priv->usb); | |
115 | } | |
116 | ||
117 | static void octeon_usb_urb_complete_callback(cvmx_usb_state_t *usb, | |
118 | cvmx_usb_callback_t reason, | |
119 | cvmx_usb_complete_t status, | |
120 | int pipe_handle, | |
121 | int submit_handle, | |
122 | int bytes_transferred, | |
123 | void *user_data) | |
124 | { | |
125 | struct octeon_hcd *priv = cvmx_usb_to_octeon(usb); | |
126 | struct urb *urb = user_data; | |
127 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
128 | urb->actual_length = bytes_transferred; | |
129 | urb->hcpriv = NULL; | |
130 | ||
131 | if (!list_empty(&urb->urb_list)) { | |
132 | /* | |
133 | * It is on the dequeue_list, but we are going to call | |
134 | * usb_hcd_giveback_urb(), so we must clear it from | |
135 | * the list. We got to it before the | |
136 | * octeon_usb_urb_dequeue_work() tasklet did. | |
137 | */ | |
138 | list_del(&urb->urb_list); | |
139 | /* No longer on the dequeue_list. */ | |
140 | INIT_LIST_HEAD(&urb->urb_list); | |
141 | } | |
142 | ||
143 | /* For Isochronous transactions we need to update the URB packet status | |
144 | list from data in our private copy */ | |
145 | if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) | |
146 | { | |
147 | int i; | |
148 | /* The pointer to the private list is stored in the setup_packet field */ | |
149 | cvmx_usb_iso_packet_t *iso_packet = (cvmx_usb_iso_packet_t *)urb->setup_packet; | |
150 | /* Recalculate the transfer size by adding up each packet */ | |
151 | urb->actual_length = 0; | |
152 | for (i=0; i<urb->number_of_packets; i++) | |
153 | { | |
154 | if (iso_packet[i].status == CVMX_USB_COMPLETE_SUCCESS) | |
155 | { | |
156 | urb->iso_frame_desc[i].status = 0; | |
157 | urb->iso_frame_desc[i].actual_length = iso_packet[i].length; | |
158 | urb->actual_length += urb->iso_frame_desc[i].actual_length; | |
159 | } | |
160 | else | |
161 | { | |
162 | DEBUG_ERROR("%s: ISOCHRONOUS packet=%d of %d status=%d pipe=%d submit=%d size=%d\n", | |
163 | __FUNCTION__, i, urb->number_of_packets, | |
164 | iso_packet[i].status, pipe_handle, | |
165 | submit_handle, iso_packet[i].length); | |
166 | urb->iso_frame_desc[i].status = -EREMOTEIO; | |
167 | } | |
168 | } | |
169 | /* Free the private list now that we don't need it anymore */ | |
170 | kfree(iso_packet); | |
171 | urb->setup_packet = NULL; | |
172 | } | |
173 | ||
174 | switch (status) | |
175 | { | |
176 | case CVMX_USB_COMPLETE_SUCCESS: | |
177 | urb->status = 0; | |
178 | break; | |
179 | case CVMX_USB_COMPLETE_CANCEL: | |
180 | if (urb->status == 0) | |
181 | urb->status = -ENOENT; | |
182 | break; | |
183 | case CVMX_USB_COMPLETE_STALL: | |
184 | DEBUG_ERROR("%s: status=stall pipe=%d submit=%d size=%d\n", __FUNCTION__, pipe_handle, submit_handle, bytes_transferred); | |
185 | urb->status = -EPIPE; | |
186 | break; | |
187 | case CVMX_USB_COMPLETE_BABBLEERR: | |
188 | DEBUG_ERROR("%s: status=babble pipe=%d submit=%d size=%d\n", __FUNCTION__, pipe_handle, submit_handle, bytes_transferred); | |
189 | urb->status = -EPIPE; | |
190 | break; | |
191 | case CVMX_USB_COMPLETE_SHORT: | |
192 | DEBUG_ERROR("%s: status=short pipe=%d submit=%d size=%d\n", __FUNCTION__, pipe_handle, submit_handle, bytes_transferred); | |
193 | urb->status = -EREMOTEIO; | |
194 | break; | |
195 | case CVMX_USB_COMPLETE_ERROR: | |
196 | case CVMX_USB_COMPLETE_XACTERR: | |
197 | case CVMX_USB_COMPLETE_DATATGLERR: | |
198 | case CVMX_USB_COMPLETE_FRAMEERR: | |
199 | DEBUG_ERROR("%s: status=%d pipe=%d submit=%d size=%d\n", __FUNCTION__, status, pipe_handle, submit_handle, bytes_transferred); | |
200 | urb->status = -EPROTO; | |
201 | break; | |
202 | } | |
203 | spin_unlock(&priv->lock); | |
204 | usb_hcd_giveback_urb(octeon_to_hcd(priv), urb, urb->status); | |
205 | spin_lock(&priv->lock); | |
206 | } | |
207 | ||
208 | static int octeon_usb_urb_enqueue(struct usb_hcd *hcd, | |
209 | struct urb *urb, | |
210 | gfp_t mem_flags) | |
211 | { | |
212 | struct octeon_hcd *priv = hcd_to_octeon(hcd); | |
213 | int submit_handle = -1; | |
214 | int pipe_handle; | |
215 | unsigned long flags; | |
216 | cvmx_usb_iso_packet_t *iso_packet; | |
217 | struct usb_host_endpoint *ep = urb->ep; | |
218 | ||
219 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
220 | ||
221 | urb->status = 0; | |
222 | INIT_LIST_HEAD(&urb->urb_list); /* not enqueued on dequeue_list */ | |
223 | spin_lock_irqsave(&priv->lock, flags); | |
224 | ||
225 | if (!ep->hcpriv) | |
226 | { | |
227 | cvmx_usb_transfer_t transfer_type; | |
228 | cvmx_usb_speed_t speed; | |
229 | int split_device = 0; | |
230 | int split_port = 0; | |
231 | switch (usb_pipetype(urb->pipe)) | |
232 | { | |
233 | case PIPE_ISOCHRONOUS: | |
234 | transfer_type = CVMX_USB_TRANSFER_ISOCHRONOUS; | |
235 | break; | |
236 | case PIPE_INTERRUPT: | |
237 | transfer_type = CVMX_USB_TRANSFER_INTERRUPT; | |
238 | break; | |
239 | case PIPE_CONTROL: | |
240 | transfer_type = CVMX_USB_TRANSFER_CONTROL; | |
241 | break; | |
242 | default: | |
243 | transfer_type = CVMX_USB_TRANSFER_BULK; | |
244 | break; | |
245 | } | |
246 | switch (urb->dev->speed) | |
247 | { | |
248 | case USB_SPEED_LOW: | |
249 | speed = CVMX_USB_SPEED_LOW; | |
250 | break; | |
251 | case USB_SPEED_FULL: | |
252 | speed = CVMX_USB_SPEED_FULL; | |
253 | break; | |
254 | default: | |
255 | speed = CVMX_USB_SPEED_HIGH; | |
256 | break; | |
257 | } | |
258 | /* For slow devices on high speed ports we need to find the hub that | |
259 | does the speed translation so we know where to send the split | |
260 | transactions */ | |
261 | if (speed != CVMX_USB_SPEED_HIGH) | |
262 | { | |
263 | /* Start at this device and work our way up the usb tree */ | |
264 | struct usb_device *dev = urb->dev; | |
265 | while (dev->parent) | |
266 | { | |
267 | /* If our parent is high speed then he'll receive the splits */ | |
268 | if (dev->parent->speed == USB_SPEED_HIGH) | |
269 | { | |
270 | split_device = dev->parent->devnum; | |
271 | split_port = dev->portnum; | |
272 | break; | |
273 | } | |
274 | /* Move up the tree one level. If we make it all the way up the | |
275 | tree, then the port must not be in high speed mode and we | |
276 | don't need a split */ | |
277 | dev = dev->parent; | |
278 | } | |
279 | } | |
280 | pipe_handle = cvmx_usb_open_pipe(&priv->usb, | |
281 | 0, | |
282 | usb_pipedevice(urb->pipe), | |
283 | usb_pipeendpoint(urb->pipe), | |
284 | speed, | |
285 | le16_to_cpu(ep->desc.wMaxPacketSize) & 0x7ff, | |
286 | transfer_type, | |
287 | usb_pipein(urb->pipe) ? CVMX_USB_DIRECTION_IN : CVMX_USB_DIRECTION_OUT, | |
288 | urb->interval, | |
289 | (le16_to_cpu(ep->desc.wMaxPacketSize)>>11) & 0x3, | |
290 | split_device, | |
291 | split_port); | |
292 | if (pipe_handle < 0) | |
293 | { | |
294 | spin_unlock_irqrestore(&priv->lock, flags); | |
295 | DEBUG_ERROR("OcteonUSB: %s failed to create pipe\n", __FUNCTION__); | |
296 | return -ENOMEM; | |
297 | } | |
298 | ep->hcpriv = (void*)(0x10000L + pipe_handle); | |
299 | } | |
300 | else | |
301 | pipe_handle = 0xffff & (long)ep->hcpriv; | |
302 | ||
303 | switch (usb_pipetype(urb->pipe)) | |
304 | { | |
305 | case PIPE_ISOCHRONOUS: | |
306 | DEBUG_SUBMIT("OcteonUSB: %s submit isochronous to %d.%d\n", __FUNCTION__, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe)); | |
307 | /* Allocate a structure to use for our private list of isochronous | |
308 | packets */ | |
309 | iso_packet = kmalloc(urb->number_of_packets * sizeof(cvmx_usb_iso_packet_t), GFP_ATOMIC); | |
310 | if (iso_packet) | |
311 | { | |
312 | int i; | |
313 | /* Fill the list with the data from the URB */ | |
314 | for (i=0; i<urb->number_of_packets; i++) | |
315 | { | |
316 | iso_packet[i].offset = urb->iso_frame_desc[i].offset; | |
317 | iso_packet[i].length = urb->iso_frame_desc[i].length; | |
318 | iso_packet[i].status = CVMX_USB_COMPLETE_ERROR; | |
319 | } | |
320 | /* Store a pointer to the list in uthe URB setup_pakcet field. | |
321 | We know this currently isn't being used and this saves us | |
322 | a bunch of logic */ | |
323 | urb->setup_packet = (char*)iso_packet; | |
324 | submit_handle = cvmx_usb_submit_isochronous(&priv->usb, pipe_handle, | |
325 | urb->start_frame, | |
326 | 0 /* flags */, | |
327 | urb->number_of_packets, | |
328 | iso_packet, | |
329 | urb->transfer_dma, | |
330 | urb->transfer_buffer_length, | |
331 | octeon_usb_urb_complete_callback, | |
332 | urb); | |
333 | /* If submit failed we need to free our private packet list */ | |
334 | if (submit_handle < 0) | |
335 | { | |
336 | urb->setup_packet = NULL; | |
337 | kfree(iso_packet); | |
338 | } | |
339 | } | |
340 | break; | |
341 | case PIPE_INTERRUPT: | |
342 | DEBUG_SUBMIT("OcteonUSB: %s submit interrupt to %d.%d\n", __FUNCTION__, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe)); | |
343 | submit_handle = cvmx_usb_submit_interrupt(&priv->usb, pipe_handle, | |
344 | urb->transfer_dma, | |
345 | urb->transfer_buffer_length, | |
346 | octeon_usb_urb_complete_callback, | |
347 | urb); | |
348 | break; | |
349 | case PIPE_CONTROL: | |
350 | DEBUG_SUBMIT("OcteonUSB: %s submit control to %d.%d\n", __FUNCTION__, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe)); | |
351 | submit_handle = cvmx_usb_submit_control(&priv->usb, pipe_handle, | |
352 | urb->setup_dma, | |
353 | urb->transfer_dma, | |
354 | urb->transfer_buffer_length, | |
355 | octeon_usb_urb_complete_callback, | |
356 | urb); | |
357 | break; | |
358 | case PIPE_BULK: | |
359 | DEBUG_SUBMIT("OcteonUSB: %s submit bulk to %d.%d\n", __FUNCTION__, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe)); | |
360 | submit_handle = cvmx_usb_submit_bulk(&priv->usb, pipe_handle, | |
361 | urb->transfer_dma, | |
362 | urb->transfer_buffer_length, | |
363 | octeon_usb_urb_complete_callback, | |
364 | urb); | |
365 | break; | |
366 | } | |
367 | if (submit_handle < 0) | |
368 | { | |
369 | spin_unlock_irqrestore(&priv->lock, flags); | |
370 | DEBUG_ERROR("OcteonUSB: %s failed to submit\n", __FUNCTION__); | |
371 | return -ENOMEM; | |
372 | } | |
373 | urb->hcpriv = (void*)(long)(((submit_handle & 0xffff) << 16) | pipe_handle); | |
374 | spin_unlock_irqrestore(&priv->lock, flags); | |
375 | return 0; | |
376 | } | |
377 | ||
378 | static void octeon_usb_urb_dequeue_work(unsigned long arg) | |
379 | { | |
380 | unsigned long flags; | |
381 | struct octeon_hcd *priv = (struct octeon_hcd *)arg; | |
382 | ||
383 | spin_lock_irqsave(&priv->lock, flags); | |
384 | ||
385 | while (!list_empty(&priv->dequeue_list)) { | |
386 | int pipe_handle; | |
387 | int submit_handle; | |
388 | struct urb *urb = container_of(priv->dequeue_list.next, struct urb, urb_list); | |
389 | list_del(&urb->urb_list); | |
390 | /* not enqueued on dequeue_list */ | |
391 | INIT_LIST_HEAD(&urb->urb_list); | |
392 | pipe_handle = 0xffff & (long)urb->hcpriv; | |
393 | submit_handle = ((long)urb->hcpriv) >> 16; | |
394 | cvmx_usb_cancel(&priv->usb, pipe_handle, submit_handle); | |
395 | } | |
396 | ||
397 | spin_unlock_irqrestore(&priv->lock, flags); | |
398 | } | |
399 | ||
400 | static int octeon_usb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) | |
401 | { | |
402 | struct octeon_hcd *priv = hcd_to_octeon(hcd); | |
403 | unsigned long flags; | |
404 | ||
405 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
406 | ||
407 | if (!urb->dev) | |
408 | return -EINVAL; | |
409 | ||
410 | spin_lock_irqsave(&priv->lock, flags); | |
411 | ||
412 | urb->status = status; | |
413 | list_add_tail(&urb->urb_list, &priv->dequeue_list); | |
414 | ||
415 | spin_unlock_irqrestore(&priv->lock, flags); | |
416 | ||
417 | tasklet_schedule(&priv->dequeue_tasklet); | |
418 | ||
419 | return 0; | |
420 | } | |
421 | ||
422 | static void octeon_usb_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) | |
423 | { | |
424 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
425 | if (ep->hcpriv) | |
426 | { | |
427 | struct octeon_hcd *priv = hcd_to_octeon(hcd); | |
428 | int pipe_handle = 0xffff & (long)ep->hcpriv; | |
429 | unsigned long flags; | |
430 | spin_lock_irqsave(&priv->lock, flags); | |
431 | cvmx_usb_cancel_all(&priv->usb, pipe_handle); | |
432 | if (cvmx_usb_close_pipe(&priv->usb, pipe_handle)) | |
433 | DEBUG_ERROR("OcteonUSB: Closing pipe %d failed\n", pipe_handle); | |
434 | spin_unlock_irqrestore(&priv->lock, flags); | |
435 | ep->hcpriv = NULL; | |
436 | } | |
437 | } | |
438 | ||
439 | static int octeon_usb_hub_status_data(struct usb_hcd *hcd, char *buf) | |
440 | { | |
441 | struct octeon_hcd *priv = hcd_to_octeon(hcd); | |
442 | cvmx_usb_port_status_t port_status; | |
443 | unsigned long flags; | |
444 | ||
445 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
446 | ||
447 | spin_lock_irqsave(&priv->lock, flags); | |
448 | port_status = cvmx_usb_get_status(&priv->usb); | |
449 | spin_unlock_irqrestore(&priv->lock, flags); | |
450 | buf[0] = 0; | |
451 | buf[0] = port_status.connect_change << 1; | |
452 | ||
453 | return(buf[0] != 0); | |
454 | } | |
455 | ||
456 | static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) | |
457 | { | |
458 | struct octeon_hcd *priv = hcd_to_octeon(hcd); | |
459 | cvmx_usb_port_status_t usb_port_status; | |
460 | int port_status; | |
461 | struct usb_hub_descriptor *desc; | |
462 | unsigned long flags; | |
463 | ||
464 | switch (typeReq) | |
465 | { | |
466 | case ClearHubFeature: | |
467 | DEBUG_ROOT_HUB("OcteonUSB: ClearHubFeature\n"); | |
468 | switch (wValue) | |
469 | { | |
470 | case C_HUB_LOCAL_POWER: | |
471 | case C_HUB_OVER_CURRENT: | |
472 | /* Nothing required here */ | |
473 | break; | |
474 | default: | |
475 | return -EINVAL; | |
476 | } | |
477 | break; | |
478 | case ClearPortFeature: | |
479 | DEBUG_ROOT_HUB("OcteonUSB: ClearPortFeature"); | |
480 | if (wIndex != 1) | |
481 | { | |
482 | DEBUG_ROOT_HUB(" INVALID\n"); | |
483 | return -EINVAL; | |
484 | } | |
485 | ||
486 | switch (wValue) | |
487 | { | |
488 | case USB_PORT_FEAT_ENABLE: | |
489 | DEBUG_ROOT_HUB(" ENABLE"); | |
490 | spin_lock_irqsave(&priv->lock, flags); | |
491 | cvmx_usb_disable(&priv->usb); | |
492 | spin_unlock_irqrestore(&priv->lock, flags); | |
493 | break; | |
494 | case USB_PORT_FEAT_SUSPEND: | |
495 | DEBUG_ROOT_HUB(" SUSPEND"); | |
496 | /* Not supported on Octeon */ | |
497 | break; | |
498 | case USB_PORT_FEAT_POWER: | |
499 | DEBUG_ROOT_HUB(" POWER"); | |
500 | /* Not supported on Octeon */ | |
501 | break; | |
502 | case USB_PORT_FEAT_INDICATOR: | |
503 | DEBUG_ROOT_HUB(" INDICATOR"); | |
504 | /* Port inidicator not supported */ | |
505 | break; | |
506 | case USB_PORT_FEAT_C_CONNECTION: | |
507 | DEBUG_ROOT_HUB(" C_CONNECTION"); | |
508 | /* Clears drivers internal connect status change flag */ | |
509 | spin_lock_irqsave(&priv->lock, flags); | |
510 | cvmx_usb_set_status(&priv->usb, cvmx_usb_get_status(&priv->usb)); | |
511 | spin_unlock_irqrestore(&priv->lock, flags); | |
512 | break; | |
513 | case USB_PORT_FEAT_C_RESET: | |
514 | DEBUG_ROOT_HUB(" C_RESET"); | |
515 | /* Clears the driver's internal Port Reset Change flag */ | |
516 | spin_lock_irqsave(&priv->lock, flags); | |
517 | cvmx_usb_set_status(&priv->usb, cvmx_usb_get_status(&priv->usb)); | |
518 | spin_unlock_irqrestore(&priv->lock, flags); | |
519 | break; | |
520 | case USB_PORT_FEAT_C_ENABLE: | |
521 | DEBUG_ROOT_HUB(" C_ENABLE"); | |
522 | /* Clears the driver's internal Port Enable/Disable Change flag */ | |
523 | spin_lock_irqsave(&priv->lock, flags); | |
524 | cvmx_usb_set_status(&priv->usb, cvmx_usb_get_status(&priv->usb)); | |
525 | spin_unlock_irqrestore(&priv->lock, flags); | |
526 | break; | |
527 | case USB_PORT_FEAT_C_SUSPEND: | |
528 | DEBUG_ROOT_HUB(" C_SUSPEND"); | |
529 | /* Clears the driver's internal Port Suspend Change flag, | |
530 | which is set when resume signaling on the host port is | |
531 | complete */ | |
532 | break; | |
533 | case USB_PORT_FEAT_C_OVER_CURRENT: | |
534 | DEBUG_ROOT_HUB(" C_OVER_CURRENT"); | |
535 | /* Clears the driver's overcurrent Change flag */ | |
536 | spin_lock_irqsave(&priv->lock, flags); | |
537 | cvmx_usb_set_status(&priv->usb, cvmx_usb_get_status(&priv->usb)); | |
538 | spin_unlock_irqrestore(&priv->lock, flags); | |
539 | break; | |
540 | default: | |
541 | DEBUG_ROOT_HUB(" UNKNOWN\n"); | |
542 | return -EINVAL; | |
543 | } | |
544 | DEBUG_ROOT_HUB("\n"); | |
545 | break; | |
546 | case GetHubDescriptor: | |
547 | DEBUG_ROOT_HUB("OcteonUSB: GetHubDescriptor\n"); | |
548 | desc = (struct usb_hub_descriptor *)buf; | |
549 | desc->bDescLength = 9; | |
550 | desc->bDescriptorType = 0x29; | |
551 | desc->bNbrPorts = 1; | |
552 | desc->wHubCharacteristics = 0x08; | |
553 | desc->bPwrOn2PwrGood = 1; | |
554 | desc->bHubContrCurrent = 0; | |
555 | desc->u.hs.DeviceRemovable[0] = 0; | |
556 | desc->u.hs.DeviceRemovable[1] = 0xff; | |
557 | break; | |
558 | case GetHubStatus: | |
559 | DEBUG_ROOT_HUB("OcteonUSB: GetHubStatus\n"); | |
560 | *(__le32 *)buf = 0; | |
561 | break; | |
562 | case GetPortStatus: | |
563 | DEBUG_ROOT_HUB("OcteonUSB: GetPortStatus"); | |
564 | if (wIndex != 1) | |
565 | { | |
566 | DEBUG_ROOT_HUB(" INVALID\n"); | |
567 | return -EINVAL; | |
568 | } | |
569 | ||
570 | spin_lock_irqsave(&priv->lock, flags); | |
571 | usb_port_status = cvmx_usb_get_status(&priv->usb); | |
572 | spin_unlock_irqrestore(&priv->lock, flags); | |
573 | port_status = 0; | |
574 | ||
575 | if (usb_port_status.connect_change) | |
576 | { | |
577 | port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); | |
578 | DEBUG_ROOT_HUB(" C_CONNECTION"); | |
579 | } | |
580 | ||
581 | if (usb_port_status.port_enabled) | |
582 | { | |
583 | port_status |= (1 << USB_PORT_FEAT_C_ENABLE); | |
584 | DEBUG_ROOT_HUB(" C_ENABLE"); | |
585 | } | |
586 | ||
587 | if (usb_port_status.connected) | |
588 | { | |
589 | port_status |= (1 << USB_PORT_FEAT_CONNECTION); | |
590 | DEBUG_ROOT_HUB(" CONNECTION"); | |
591 | } | |
592 | ||
593 | if (usb_port_status.port_enabled) | |
594 | { | |
595 | port_status |= (1 << USB_PORT_FEAT_ENABLE); | |
596 | DEBUG_ROOT_HUB(" ENABLE"); | |
597 | } | |
598 | ||
599 | if (usb_port_status.port_over_current) | |
600 | { | |
601 | port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT); | |
602 | DEBUG_ROOT_HUB(" OVER_CURRENT"); | |
603 | } | |
604 | ||
605 | if (usb_port_status.port_powered) | |
606 | { | |
607 | port_status |= (1 << USB_PORT_FEAT_POWER); | |
608 | DEBUG_ROOT_HUB(" POWER"); | |
609 | } | |
610 | ||
611 | if (usb_port_status.port_speed == CVMX_USB_SPEED_HIGH) | |
612 | { | |
613 | port_status |= USB_PORT_STAT_HIGH_SPEED; | |
614 | DEBUG_ROOT_HUB(" HIGHSPEED"); | |
615 | } | |
616 | else if (usb_port_status.port_speed == CVMX_USB_SPEED_LOW) | |
617 | { | |
618 | port_status |= (1 << USB_PORT_FEAT_LOWSPEED); | |
619 | DEBUG_ROOT_HUB(" LOWSPEED"); | |
620 | } | |
621 | ||
622 | *((__le32 *)buf) = cpu_to_le32(port_status); | |
623 | DEBUG_ROOT_HUB("\n"); | |
624 | break; | |
625 | case SetHubFeature: | |
626 | DEBUG_ROOT_HUB("OcteonUSB: SetHubFeature\n"); | |
627 | /* No HUB features supported */ | |
628 | break; | |
629 | case SetPortFeature: | |
630 | DEBUG_ROOT_HUB("OcteonUSB: SetPortFeature"); | |
631 | if (wIndex != 1) | |
632 | { | |
633 | DEBUG_ROOT_HUB(" INVALID\n"); | |
634 | return -EINVAL; | |
635 | } | |
636 | ||
637 | switch (wValue) | |
638 | { | |
639 | case USB_PORT_FEAT_SUSPEND: | |
640 | DEBUG_ROOT_HUB(" SUSPEND\n"); | |
641 | return -EINVAL; | |
642 | case USB_PORT_FEAT_POWER: | |
643 | DEBUG_ROOT_HUB(" POWER\n"); | |
644 | return -EINVAL; | |
645 | case USB_PORT_FEAT_RESET: | |
646 | DEBUG_ROOT_HUB(" RESET\n"); | |
647 | spin_lock_irqsave(&priv->lock, flags); | |
648 | cvmx_usb_disable(&priv->usb); | |
649 | if (cvmx_usb_enable(&priv->usb)) | |
650 | DEBUG_ERROR("Failed to enable the port\n"); | |
651 | spin_unlock_irqrestore(&priv->lock, flags); | |
652 | return 0; | |
653 | case USB_PORT_FEAT_INDICATOR: | |
654 | DEBUG_ROOT_HUB(" INDICATOR\n"); | |
655 | /* Not supported */ | |
656 | break; | |
657 | default: | |
658 | DEBUG_ROOT_HUB(" UNKNOWN\n"); | |
659 | return -EINVAL; | |
660 | } | |
661 | break; | |
662 | default: | |
663 | DEBUG_ROOT_HUB("OcteonUSB: Unknown root hub request\n"); | |
664 | return -EINVAL; | |
665 | } | |
666 | return 0; | |
667 | } | |
668 | ||
669 | ||
670 | static const struct hc_driver octeon_hc_driver = { | |
671 | .description = "Octeon USB", | |
672 | .product_desc = "Octeon Host Controller", | |
673 | .hcd_priv_size = sizeof(struct octeon_hcd), | |
674 | .irq = octeon_usb_irq, | |
675 | .flags = HCD_MEMORY | HCD_USB2, | |
676 | .start = octeon_usb_start, | |
677 | .stop = octeon_usb_stop, | |
678 | .urb_enqueue = octeon_usb_urb_enqueue, | |
679 | .urb_dequeue = octeon_usb_urb_dequeue, | |
680 | .endpoint_disable = octeon_usb_endpoint_disable, | |
681 | .get_frame_number = octeon_usb_get_frame_number, | |
682 | .hub_status_data = octeon_usb_hub_status_data, | |
683 | .hub_control = octeon_usb_hub_control, | |
684 | }; | |
685 | ||
686 | ||
687 | static int octeon_usb_driver_probe(struct device *dev) | |
688 | { | |
689 | int status; | |
690 | int usb_num = to_platform_device(dev)->id; | |
691 | int irq = platform_get_irq(to_platform_device(dev), 0); | |
692 | struct octeon_hcd *priv; | |
693 | struct usb_hcd *hcd; | |
694 | unsigned long flags; | |
695 | ||
696 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
697 | ||
698 | /* Set the DMA mask to 64bits so we get buffers already translated for | |
699 | DMA */ | |
700 | dev->coherent_dma_mask = ~0; | |
701 | dev->dma_mask = &dev->coherent_dma_mask; | |
702 | ||
703 | hcd = usb_create_hcd(&octeon_hc_driver, dev, dev_name(dev)); | |
704 | if (!hcd) | |
705 | { | |
706 | DEBUG_FATAL("OcteonUSB: Failed to allocate memory for HCD\n"); | |
707 | return -1; | |
708 | } | |
709 | hcd->uses_new_polling = 1; | |
710 | priv = (struct octeon_hcd *)hcd->hcd_priv; | |
711 | ||
712 | spin_lock_init(&priv->lock); | |
713 | ||
714 | tasklet_init(&priv->dequeue_tasklet, octeon_usb_urb_dequeue_work, (unsigned long)priv); | |
715 | INIT_LIST_HEAD(&priv->dequeue_list); | |
716 | ||
717 | //status = cvmx_usb_initialize(&priv->usb, usb_num, CVMX_USB_INITIALIZE_FLAGS_CLOCK_AUTO | CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO | CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS | CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS); | |
718 | status = cvmx_usb_initialize(&priv->usb, usb_num, CVMX_USB_INITIALIZE_FLAGS_CLOCK_AUTO); | |
719 | if (status) | |
720 | { | |
721 | DEBUG_FATAL("OcteonUSB: USB initialization failed with %d\n", status); | |
722 | kfree(hcd); | |
723 | return -1; | |
724 | } | |
725 | ||
726 | /* This delay is needed for CN3010, but I don't know why... */ | |
727 | mdelay(10); | |
728 | ||
729 | spin_lock_irqsave(&priv->lock, flags); | |
730 | cvmx_usb_poll(&priv->usb); | |
731 | spin_unlock_irqrestore(&priv->lock, flags); | |
732 | ||
733 | status = usb_add_hcd(hcd, irq, IRQF_SHARED); | |
734 | if (status) | |
735 | { | |
736 | DEBUG_FATAL("OcteonUSB: USB add HCD failed with %d\n", status); | |
737 | kfree(hcd); | |
738 | return -1; | |
739 | } | |
740 | ||
741 | printk("OcteonUSB: Registered HCD for port %d on irq %d\n", usb_num, irq); | |
742 | ||
743 | return 0; | |
744 | } | |
745 | ||
746 | static int octeon_usb_driver_remove(struct device *dev) | |
747 | { | |
748 | int status; | |
749 | struct usb_hcd *hcd = dev_get_drvdata(dev); | |
750 | struct octeon_hcd *priv = hcd_to_octeon(hcd); | |
751 | unsigned long flags; | |
752 | ||
753 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
754 | ||
755 | usb_remove_hcd(hcd); | |
756 | tasklet_kill(&priv->dequeue_tasklet); | |
757 | spin_lock_irqsave(&priv->lock, flags); | |
758 | status = cvmx_usb_shutdown(&priv->usb); | |
759 | spin_unlock_irqrestore(&priv->lock, flags); | |
760 | if (status) | |
761 | DEBUG_FATAL("OcteonUSB: USB shutdown failed with %d\n", status); | |
762 | ||
763 | kfree(hcd); | |
764 | ||
765 | return 0; | |
766 | } | |
767 | ||
768 | static struct device_driver octeon_usb_driver = { | |
769 | .name = "OcteonUSB", | |
770 | .bus = &platform_bus_type, | |
771 | .probe = octeon_usb_driver_probe, | |
772 | .remove = octeon_usb_driver_remove, | |
773 | }; | |
774 | ||
775 | ||
776 | #define MAX_USB_PORTS 10 | |
777 | struct platform_device *pdev_glob[MAX_USB_PORTS]; | |
778 | static int octeon_usb_registered; | |
779 | static int __init octeon_usb_module_init(void) | |
780 | { | |
781 | int num_devices = cvmx_usb_get_num_ports(); | |
782 | int device; | |
783 | ||
784 | if (usb_disabled() || num_devices == 0) | |
785 | return -ENODEV; | |
786 | ||
787 | if (driver_register(&octeon_usb_driver)) | |
788 | { | |
789 | DEBUG_FATAL("OcteonUSB: Failed to register driver\n"); | |
790 | return -ENOMEM; | |
791 | } | |
792 | octeon_usb_registered = 1; | |
793 | printk("OcteonUSB: Detected %d ports\n", num_devices); | |
794 | ||
795 | /* | |
796 | * Only cn52XX and cn56XX have DWC_OTG USB hardware and the | |
797 | * IOB priority registers. Under heavy network load USB | |
798 | * hardware can be starved by the IOB causing a crash. Give | |
799 | * it a priority boost if it has been waiting more than 400 | |
800 | * cycles to avoid this situation. | |
801 | * | |
802 | * Testing indicates that a cnt_val of 8192 is not sufficient, | |
803 | * but no failures are seen with 4096. We choose a value of | |
804 | * 400 to give a safety factor of 10. | |
805 | */ | |
806 | if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN56XX)) { | |
807 | union cvmx_iob_n2c_l2c_pri_cnt pri_cnt; | |
808 | ||
809 | pri_cnt.u64 = 0; | |
810 | pri_cnt.s.cnt_enb = 1; | |
811 | pri_cnt.s.cnt_val = 400; | |
812 | cvmx_write_csr(CVMX_IOB_N2C_L2C_PRI_CNT, pri_cnt.u64); | |
813 | } | |
814 | ||
815 | for (device = 0; device < num_devices; device++) | |
816 | { | |
817 | struct resource irq_resource; | |
818 | struct platform_device *pdev; | |
819 | memset(&irq_resource, 0, sizeof(irq_resource)); | |
820 | irq_resource.start = (device==0) ? OCTEON_IRQ_USB0 : OCTEON_IRQ_USB1; | |
821 | irq_resource.end = irq_resource.start; | |
822 | irq_resource.flags = IORESOURCE_IRQ; | |
823 | pdev = platform_device_register_simple((char*)octeon_usb_driver.name, device, &irq_resource, 1); | |
824 | if (!pdev) | |
825 | { | |
826 | DEBUG_FATAL("OcteonUSB: Failed to allocate platform device for USB%d\n", device); | |
827 | return -ENOMEM; | |
828 | } | |
829 | if (device < MAX_USB_PORTS) | |
830 | pdev_glob[device] = pdev; | |
831 | ||
832 | } | |
833 | return 0; | |
834 | } | |
835 | ||
836 | static void __exit octeon_usb_module_cleanup(void) | |
837 | { | |
838 | int i; | |
839 | DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__); | |
840 | for (i = 0; i <MAX_USB_PORTS; i++) | |
841 | if (pdev_glob[i]) | |
842 | { | |
843 | platform_device_unregister(pdev_glob[i]); | |
844 | pdev_glob[i] = NULL; | |
845 | } | |
846 | if (octeon_usb_registered) | |
847 | driver_unregister(&octeon_usb_driver); | |
848 | } | |
849 | ||
850 | MODULE_LICENSE("GPL"); | |
851 | MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>"); | |
852 | MODULE_DESCRIPTION("Cavium Networks Octeon USB Host driver."); | |
853 | module_init(octeon_usb_module_init); | |
854 | module_exit(octeon_usb_module_cleanup); |