]>
Commit | Line | Data |
---|---|---|
eb81955b | 1 | #include <common.h> |
24b852a7 | 2 | #include <console.h> |
7a342785 | 3 | #include <watchdog.h> |
246e3b87 | 4 | #ifdef CONFIG_ARCH_SUNXI |
2aacc423 | 5 | #include <asm/arch/usb_phy.h> |
246e3b87 | 6 | #endif |
eb81955b IY |
7 | #include <asm/errno.h> |
8 | #include <linux/usb/ch9.h> | |
9 | #include <linux/usb/gadget.h> | |
10 | ||
eb81955b IY |
11 | #include <usb.h> |
12 | #include "linux-compat.h" | |
13 | #include "usb-compat.h" | |
14 | #include "musb_core.h" | |
15 | #include "musb_host.h" | |
16 | #include "musb_gadget.h" | |
fc85d39e | 17 | #include "musb_uboot.h" |
eb81955b | 18 | |
95de1e2f | 19 | #ifdef CONFIG_USB_MUSB_HOST |
904f2a83 HG |
20 | struct int_queue { |
21 | struct usb_host_endpoint hep; | |
22 | struct urb urb; | |
23 | }; | |
24 | ||
09e7ea47 | 25 | #ifndef CONFIG_DM_USB |
fc85d39e | 26 | struct musb_host_data musb_host; |
09e7ea47 | 27 | #endif |
eb81955b IY |
28 | |
29 | static void musb_host_complete_urb(struct urb *urb) | |
30 | { | |
31 | urb->dev->status &= ~USB_ST_NOT_PROC; | |
32 | urb->dev->act_len = urb->actual_length; | |
33 | } | |
34 | ||
accf04c2 HG |
35 | static void construct_urb(struct urb *urb, struct usb_host_endpoint *hep, |
36 | struct usb_device *dev, int endpoint_type, | |
37 | unsigned long pipe, void *buffer, int len, | |
38 | struct devrequest *setup, int interval) | |
eb81955b IY |
39 | { |
40 | int epnum = usb_pipeendpoint(pipe); | |
41 | int is_in = usb_pipein(pipe); | |
42 | ||
accf04c2 HG |
43 | memset(urb, 0, sizeof(struct urb)); |
44 | memset(hep, 0, sizeof(struct usb_host_endpoint)); | |
45 | INIT_LIST_HEAD(&hep->urb_list); | |
46 | INIT_LIST_HEAD(&urb->urb_list); | |
47 | urb->ep = hep; | |
48 | urb->complete = musb_host_complete_urb; | |
49 | urb->status = -EINPROGRESS; | |
50 | urb->dev = dev; | |
51 | urb->pipe = pipe; | |
52 | urb->transfer_buffer = buffer; | |
53 | urb->transfer_dma = (unsigned long)buffer; | |
54 | urb->transfer_buffer_length = len; | |
55 | urb->setup_packet = (unsigned char *)setup; | |
56 | ||
57 | urb->ep->desc.wMaxPacketSize = | |
eb81955b IY |
58 | __cpu_to_le16(is_in ? dev->epmaxpacketin[epnum] : |
59 | dev->epmaxpacketout[epnum]); | |
accf04c2 HG |
60 | urb->ep->desc.bmAttributes = endpoint_type; |
61 | urb->ep->desc.bEndpointAddress = | |
eb81955b | 62 | (is_in ? USB_DIR_IN : USB_DIR_OUT) | epnum; |
accf04c2 | 63 | urb->ep->desc.bInterval = interval; |
eb81955b IY |
64 | } |
65 | ||
eb81955b IY |
66 | static int submit_urb(struct usb_hcd *hcd, struct urb *urb) |
67 | { | |
68 | struct musb *host = hcd->hcd_priv; | |
69 | int ret; | |
dc9a3912 | 70 | unsigned long timeout; |
eb81955b IY |
71 | |
72 | ret = musb_urb_enqueue(hcd, urb, 0); | |
73 | if (ret < 0) { | |
74 | printf("Failed to enqueue URB to controller\n"); | |
75 | return ret; | |
76 | } | |
77 | ||
dc9a3912 | 78 | timeout = get_timer(0) + USB_TIMEOUT_MS(urb->pipe); |
eb81955b IY |
79 | do { |
80 | if (ctrlc()) | |
81 | return -EIO; | |
82 | host->isr(0, host); | |
e8672e3f | 83 | } while (urb->status == -EINPROGRESS && |
dc9a3912 | 84 | get_timer(0) < timeout); |
eb81955b | 85 | |
b918a0c6 HG |
86 | if (urb->status == -EINPROGRESS) |
87 | musb_urb_dequeue(hcd, urb, -ETIME); | |
88 | ||
eb81955b IY |
89 | return urb->status; |
90 | } | |
91 | ||
fc85d39e HG |
92 | static int _musb_submit_control_msg(struct musb_host_data *host, |
93 | struct usb_device *dev, unsigned long pipe, | |
94 | void *buffer, int len, struct devrequest *setup) | |
eb81955b | 95 | { |
fc85d39e HG |
96 | construct_urb(&host->urb, &host->hep, dev, USB_ENDPOINT_XFER_CONTROL, |
97 | pipe, buffer, len, setup, 0); | |
eb81955b IY |
98 | |
99 | /* Fix speed for non hub-attached devices */ | |
e740ca3c | 100 | if (!usb_dev_get_parent(dev)) |
fc85d39e | 101 | dev->speed = host->host_speed; |
eb81955b | 102 | |
fc85d39e | 103 | return submit_urb(&host->hcd, &host->urb); |
eb81955b IY |
104 | } |
105 | ||
fc85d39e HG |
106 | static int _musb_submit_bulk_msg(struct musb_host_data *host, |
107 | struct usb_device *dev, unsigned long pipe, void *buffer, int len) | |
eb81955b | 108 | { |
fc85d39e HG |
109 | construct_urb(&host->urb, &host->hep, dev, USB_ENDPOINT_XFER_BULK, |
110 | pipe, buffer, len, NULL, 0); | |
111 | return submit_urb(&host->hcd, &host->urb); | |
eb81955b IY |
112 | } |
113 | ||
fc85d39e HG |
114 | static int _musb_submit_int_msg(struct musb_host_data *host, |
115 | struct usb_device *dev, unsigned long pipe, | |
116 | void *buffer, int len, int interval) | |
eb81955b | 117 | { |
fc85d39e | 118 | construct_urb(&host->urb, &host->hep, dev, USB_ENDPOINT_XFER_INT, pipe, |
accf04c2 | 119 | buffer, len, NULL, interval); |
fc85d39e | 120 | return submit_urb(&host->hcd, &host->urb); |
eb81955b IY |
121 | } |
122 | ||
fc85d39e HG |
123 | static struct int_queue *_musb_create_int_queue(struct musb_host_data *host, |
124 | struct usb_device *dev, unsigned long pipe, int queuesize, | |
125 | int elementsize, void *buffer, int interval) | |
904f2a83 HG |
126 | { |
127 | struct int_queue *queue; | |
128 | int ret, index = usb_pipein(pipe) * 16 + usb_pipeendpoint(pipe); | |
129 | ||
130 | if (queuesize != 1) { | |
131 | printf("ERROR musb int-queues only support queuesize 1\n"); | |
132 | return NULL; | |
133 | } | |
134 | ||
135 | if (dev->int_pending & (1 << index)) { | |
136 | printf("ERROR int-urb is already pending on pipe %lx\n", pipe); | |
137 | return NULL; | |
138 | } | |
139 | ||
140 | queue = malloc(sizeof(*queue)); | |
141 | if (!queue) | |
142 | return NULL; | |
143 | ||
144 | construct_urb(&queue->urb, &queue->hep, dev, USB_ENDPOINT_XFER_INT, | |
145 | pipe, buffer, elementsize, NULL, interval); | |
146 | ||
fc85d39e | 147 | ret = musb_urb_enqueue(&host->hcd, &queue->urb, 0); |
904f2a83 HG |
148 | if (ret < 0) { |
149 | printf("Failed to enqueue URB to controller\n"); | |
150 | free(queue); | |
151 | return NULL; | |
152 | } | |
153 | ||
154 | dev->int_pending |= 1 << index; | |
155 | return queue; | |
156 | } | |
157 | ||
fc85d39e HG |
158 | static int _musb_destroy_int_queue(struct musb_host_data *host, |
159 | struct usb_device *dev, struct int_queue *queue) | |
904f2a83 HG |
160 | { |
161 | int index = usb_pipein(queue->urb.pipe) * 16 + | |
162 | usb_pipeendpoint(queue->urb.pipe); | |
163 | ||
164 | if (queue->urb.status == -EINPROGRESS) | |
fc85d39e | 165 | musb_urb_dequeue(&host->hcd, &queue->urb, -ETIME); |
904f2a83 HG |
166 | |
167 | dev->int_pending &= ~(1 << index); | |
168 | free(queue); | |
169 | return 0; | |
170 | } | |
171 | ||
fc85d39e HG |
172 | static void *_musb_poll_int_queue(struct musb_host_data *host, |
173 | struct usb_device *dev, struct int_queue *queue) | |
904f2a83 HG |
174 | { |
175 | if (queue->urb.status != -EINPROGRESS) | |
176 | return NULL; /* URB has already completed in a prev. poll */ | |
177 | ||
fc85d39e | 178 | host->host->isr(0, host->host); |
904f2a83 HG |
179 | |
180 | if (queue->urb.status != -EINPROGRESS) | |
181 | return queue->urb.transfer_buffer; /* Done */ | |
182 | ||
183 | return NULL; /* URB still pending */ | |
184 | } | |
185 | ||
fc85d39e HG |
186 | static int _musb_reset_root_port(struct musb_host_data *host, |
187 | struct usb_device *dev) | |
eb81955b | 188 | { |
fc85d39e | 189 | void *mbase = host->host->mregs; |
eb81955b | 190 | u8 power; |
90cdc103 HG |
191 | |
192 | power = musb_readb(mbase, MUSB_POWER); | |
193 | power &= 0xf0; | |
194 | musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power); | |
195 | mdelay(50); | |
246e3b87 HG |
196 | #ifdef CONFIG_ARCH_SUNXI |
197 | /* | |
198 | * sunxi phy has a bug and it will wrongly detect high speed squelch | |
199 | * when clearing reset on low-speed devices, temporary disable | |
200 | * squelch detection to work around this. | |
201 | */ | |
7b798658 | 202 | sunxi_usb_phy_enable_squelch_detect(0, 0); |
246e3b87 | 203 | #endif |
90cdc103 HG |
204 | power = musb_readb(mbase, MUSB_POWER); |
205 | musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power); | |
246e3b87 | 206 | #ifdef CONFIG_ARCH_SUNXI |
7b798658 | 207 | sunxi_usb_phy_enable_squelch_detect(0, 1); |
246e3b87 | 208 | #endif |
fc85d39e HG |
209 | host->host->isr(0, host->host); |
210 | host->host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? | |
90cdc103 HG |
211 | USB_SPEED_HIGH : |
212 | (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? | |
213 | USB_SPEED_FULL : USB_SPEED_LOW; | |
fc85d39e | 214 | mdelay((host->host_speed == USB_SPEED_LOW) ? 200 : 50); |
de31213f SG |
215 | |
216 | return 0; | |
90cdc103 HG |
217 | } |
218 | ||
fc85d39e | 219 | int musb_lowlevel_init(struct musb_host_data *host) |
90cdc103 | 220 | { |
eb81955b | 221 | void *mbase; |
dc9a3912 HG |
222 | /* USB spec says it may take up to 1 second for a device to connect */ |
223 | unsigned long timeout = get_timer(0) + 1000; | |
15837236 | 224 | int ret; |
eb81955b | 225 | |
fc85d39e | 226 | if (!host->host) { |
eb81955b IY |
227 | printf("MUSB host is not registered\n"); |
228 | return -ENODEV; | |
229 | } | |
230 | ||
fc85d39e | 231 | ret = musb_start(host->host); |
15837236 HG |
232 | if (ret) |
233 | return ret; | |
234 | ||
fc85d39e | 235 | mbase = host->host->mregs; |
eb81955b IY |
236 | do { |
237 | if (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_HM) | |
238 | break; | |
dc9a3912 | 239 | } while (get_timer(0) < timeout); |
bf313230 HG |
240 | if (get_timer(0) >= timeout) { |
241 | musb_stop(host->host); | |
eb81955b | 242 | return -ENODEV; |
bf313230 | 243 | } |
eb81955b | 244 | |
fc85d39e HG |
245 | _musb_reset_root_port(host, NULL); |
246 | host->host->is_active = 1; | |
247 | host->hcd.hcd_priv = host->host; | |
eb81955b IY |
248 | |
249 | return 0; | |
250 | } | |
251 | ||
09e7ea47 | 252 | #ifndef CONFIG_DM_USB |
eb81955b IY |
253 | int usb_lowlevel_stop(int index) |
254 | { | |
fc85d39e | 255 | if (!musb_host.host) { |
eb81955b IY |
256 | printf("MUSB host is not registered\n"); |
257 | return -ENODEV; | |
258 | } | |
259 | ||
fc85d39e | 260 | musb_stop(musb_host.host); |
eb81955b IY |
261 | return 0; |
262 | } | |
1398252a HG |
263 | |
264 | int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, | |
265 | void *buffer, int length) | |
266 | { | |
fc85d39e | 267 | return _musb_submit_bulk_msg(&musb_host, dev, pipe, buffer, length); |
1398252a HG |
268 | } |
269 | ||
270 | int submit_control_msg(struct usb_device *dev, unsigned long pipe, | |
271 | void *buffer, int length, struct devrequest *setup) | |
272 | { | |
fc85d39e | 273 | return _musb_submit_control_msg(&musb_host, dev, pipe, buffer, length, setup); |
1398252a HG |
274 | } |
275 | ||
276 | int submit_int_msg(struct usb_device *dev, unsigned long pipe, | |
277 | void *buffer, int length, int interval) | |
278 | { | |
fc85d39e | 279 | return _musb_submit_int_msg(&musb_host, dev, pipe, buffer, length, interval); |
1398252a HG |
280 | } |
281 | ||
282 | struct int_queue *create_int_queue(struct usb_device *dev, | |
283 | unsigned long pipe, int queuesize, int elementsize, | |
284 | void *buffer, int interval) | |
285 | { | |
fc85d39e | 286 | return _musb_create_int_queue(&musb_host, dev, pipe, queuesize, elementsize, |
1398252a HG |
287 | buffer, interval); |
288 | } | |
289 | ||
290 | void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) | |
291 | { | |
fc85d39e | 292 | return _musb_poll_int_queue(&musb_host, dev, queue); |
1398252a HG |
293 | } |
294 | ||
295 | int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) | |
296 | { | |
fc85d39e | 297 | return _musb_destroy_int_queue(&musb_host, dev, queue); |
1398252a HG |
298 | } |
299 | ||
300 | int usb_reset_root_port(struct usb_device *dev) | |
301 | { | |
fc85d39e | 302 | return _musb_reset_root_port(&musb_host, dev); |
1398252a HG |
303 | } |
304 | ||
305 | int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) | |
306 | { | |
fc85d39e | 307 | return musb_lowlevel_init(&musb_host); |
1398252a | 308 | } |
09e7ea47 HG |
309 | #endif /* !CONFIG_DM_USB */ |
310 | ||
311 | #ifdef CONFIG_DM_USB | |
312 | static int musb_submit_control_msg(struct udevice *dev, struct usb_device *udev, | |
313 | unsigned long pipe, void *buffer, int length, | |
314 | struct devrequest *setup) | |
315 | { | |
316 | struct musb_host_data *host = dev_get_priv(dev); | |
317 | return _musb_submit_control_msg(host, udev, pipe, buffer, length, setup); | |
318 | } | |
319 | ||
320 | static int musb_submit_bulk_msg(struct udevice *dev, struct usb_device *udev, | |
321 | unsigned long pipe, void *buffer, int length) | |
322 | { | |
323 | struct musb_host_data *host = dev_get_priv(dev); | |
324 | return _musb_submit_bulk_msg(host, udev, pipe, buffer, length); | |
325 | } | |
326 | ||
327 | static int musb_submit_int_msg(struct udevice *dev, struct usb_device *udev, | |
328 | unsigned long pipe, void *buffer, int length, | |
329 | int interval) | |
330 | { | |
331 | struct musb_host_data *host = dev_get_priv(dev); | |
332 | return _musb_submit_int_msg(host, udev, pipe, buffer, length, interval); | |
333 | } | |
334 | ||
335 | static struct int_queue *musb_create_int_queue(struct udevice *dev, | |
336 | struct usb_device *udev, unsigned long pipe, int queuesize, | |
337 | int elementsize, void *buffer, int interval) | |
338 | { | |
339 | struct musb_host_data *host = dev_get_priv(dev); | |
340 | return _musb_create_int_queue(host, udev, pipe, queuesize, elementsize, | |
341 | buffer, interval); | |
342 | } | |
343 | ||
344 | static void *musb_poll_int_queue(struct udevice *dev, struct usb_device *udev, | |
345 | struct int_queue *queue) | |
346 | { | |
347 | struct musb_host_data *host = dev_get_priv(dev); | |
348 | return _musb_poll_int_queue(host, udev, queue); | |
349 | } | |
350 | ||
351 | static int musb_destroy_int_queue(struct udevice *dev, struct usb_device *udev, | |
352 | struct int_queue *queue) | |
353 | { | |
354 | struct musb_host_data *host = dev_get_priv(dev); | |
355 | return _musb_destroy_int_queue(host, udev, queue); | |
356 | } | |
357 | ||
358 | static int musb_reset_root_port(struct udevice *dev, struct usb_device *udev) | |
359 | { | |
360 | struct musb_host_data *host = dev_get_priv(dev); | |
361 | return _musb_reset_root_port(host, udev); | |
362 | } | |
363 | ||
364 | struct dm_usb_ops musb_usb_ops = { | |
365 | .control = musb_submit_control_msg, | |
366 | .bulk = musb_submit_bulk_msg, | |
367 | .interrupt = musb_submit_int_msg, | |
368 | .create_int_queue = musb_create_int_queue, | |
369 | .poll_int_queue = musb_poll_int_queue, | |
370 | .destroy_int_queue = musb_destroy_int_queue, | |
371 | .reset_root_port = musb_reset_root_port, | |
372 | }; | |
373 | #endif /* CONFIG_DM_USB */ | |
95de1e2f | 374 | #endif /* CONFIG_USB_MUSB_HOST */ |
eb81955b | 375 | |
95de1e2f | 376 | #ifdef CONFIG_USB_MUSB_GADGET |
eb81955b IY |
377 | static struct musb *gadget; |
378 | ||
2d48aa69 | 379 | int usb_gadget_handle_interrupts(int index) |
eb81955b | 380 | { |
7a342785 | 381 | WATCHDOG_RESET(); |
eb81955b IY |
382 | if (!gadget || !gadget->isr) |
383 | return -EINVAL; | |
384 | ||
385 | return gadget->isr(0, gadget); | |
386 | } | |
387 | ||
388 | int usb_gadget_register_driver(struct usb_gadget_driver *driver) | |
389 | { | |
390 | int ret; | |
391 | ||
76b09b85 | 392 | if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind || |
eb81955b IY |
393 | !driver->setup) { |
394 | printf("bad parameter.\n"); | |
395 | return -EINVAL; | |
396 | } | |
397 | ||
398 | if (!gadget) { | |
399 | printf("Controller uninitialized\n"); | |
400 | return -ENXIO; | |
401 | } | |
402 | ||
403 | ret = musb_gadget_start(&gadget->g, driver); | |
404 | if (ret < 0) { | |
405 | printf("gadget_start failed with %d\n", ret); | |
406 | return ret; | |
407 | } | |
408 | ||
409 | ret = driver->bind(&gadget->g); | |
410 | if (ret < 0) { | |
411 | printf("bind failed with %d\n", ret); | |
412 | return ret; | |
413 | } | |
414 | ||
415 | return 0; | |
416 | } | |
417 | ||
418 | int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) | |
419 | { | |
078d7302 RH |
420 | if (driver->disconnect) |
421 | driver->disconnect(&gadget->g); | |
422 | if (driver->unbind) | |
423 | driver->unbind(&gadget->g); | |
eb81955b IY |
424 | return 0; |
425 | } | |
95de1e2f | 426 | #endif /* CONFIG_USB_MUSB_GADGET */ |
eb81955b IY |
427 | |
428 | int musb_register(struct musb_hdrc_platform_data *plat, void *bdata, | |
429 | void *ctl_regs) | |
430 | { | |
431 | struct musb **musbp; | |
432 | ||
433 | switch (plat->mode) { | |
95de1e2f | 434 | #if defined(CONFIG_USB_MUSB_HOST) && !defined(CONFIG_DM_USB) |
eb81955b | 435 | case MUSB_HOST: |
fc85d39e | 436 | musbp = &musb_host.host; |
eb81955b IY |
437 | break; |
438 | #endif | |
95de1e2f | 439 | #ifdef CONFIG_USB_MUSB_GADGET |
eb81955b IY |
440 | case MUSB_PERIPHERAL: |
441 | musbp = &gadget; | |
442 | break; | |
443 | #endif | |
444 | default: | |
445 | return -EINVAL; | |
446 | } | |
447 | ||
448 | *musbp = musb_init_controller(plat, (struct device *)bdata, ctl_regs); | |
449 | if (!musbp) { | |
450 | printf("Failed to init the controller\n"); | |
451 | return -EIO; | |
452 | } | |
453 | ||
454 | return 0; | |
455 | } |