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