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