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