diff options
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 6202102a6d75..a516af28c29b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/usb/otg.h> | 23 | #include <linux/usb/otg.h> |
24 | #include <mach/usb_phy.h> | 24 | #include <mach/usb_phy.h> |
25 | 25 | ||
26 | #define TEGRA_USB_DMA_ALIGN 32 | ||
27 | |||
26 | struct tegra_ehci_hcd { | 28 | struct tegra_ehci_hcd { |
27 | struct ehci_hcd *ehci; | 29 | struct ehci_hcd *ehci; |
28 | struct tegra_usb_phy *phy; | 30 | struct tegra_usb_phy *phy; |
@@ -383,6 +385,92 @@ static int tegra_ehci_bus_resume(struct usb_hcd *hcd) | |||
383 | } | 385 | } |
384 | #endif | 386 | #endif |
385 | 387 | ||
388 | struct temp_buffer { | ||
389 | void *kmalloc_ptr; | ||
390 | void *old_xfer_buffer; | ||
391 | u8 data[0]; | ||
392 | }; | ||
393 | |||
394 | static void free_temp_buffer(struct urb *urb) | ||
395 | { | ||
396 | enum dma_data_direction dir; | ||
397 | struct temp_buffer *temp; | ||
398 | |||
399 | if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) | ||
400 | return; | ||
401 | |||
402 | dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; | ||
403 | |||
404 | temp = container_of(urb->transfer_buffer, struct temp_buffer, | ||
405 | data); | ||
406 | |||
407 | if (dir == DMA_FROM_DEVICE) | ||
408 | memcpy(temp->old_xfer_buffer, temp->data, | ||
409 | urb->transfer_buffer_length); | ||
410 | urb->transfer_buffer = temp->old_xfer_buffer; | ||
411 | kfree(temp->kmalloc_ptr); | ||
412 | |||
413 | urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; | ||
414 | } | ||
415 | |||
416 | static int alloc_temp_buffer(struct urb *urb, gfp_t mem_flags) | ||
417 | { | ||
418 | enum dma_data_direction dir; | ||
419 | struct temp_buffer *temp, *kmalloc_ptr; | ||
420 | size_t kmalloc_size; | ||
421 | |||
422 | if (urb->num_sgs || urb->sg || | ||
423 | urb->transfer_buffer_length == 0 || | ||
424 | !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1))) | ||
425 | return 0; | ||
426 | |||
427 | dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; | ||
428 | |||
429 | /* Allocate a buffer with enough padding for alignment */ | ||
430 | kmalloc_size = urb->transfer_buffer_length + | ||
431 | sizeof(struct temp_buffer) + TEGRA_USB_DMA_ALIGN - 1; | ||
432 | |||
433 | kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); | ||
434 | if (!kmalloc_ptr) | ||
435 | return -ENOMEM; | ||
436 | |||
437 | /* Position our struct temp_buffer such that data is aligned */ | ||
438 | temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1; | ||
439 | |||
440 | temp->kmalloc_ptr = kmalloc_ptr; | ||
441 | temp->old_xfer_buffer = urb->transfer_buffer; | ||
442 | if (dir == DMA_TO_DEVICE) | ||
443 | memcpy(temp->data, urb->transfer_buffer, | ||
444 | urb->transfer_buffer_length); | ||
445 | urb->transfer_buffer = temp->data; | ||
446 | |||
447 | urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; | ||
448 | |||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, | ||
453 | gfp_t mem_flags) | ||
454 | { | ||
455 | int ret; | ||
456 | |||
457 | ret = alloc_temp_buffer(urb, mem_flags); | ||
458 | if (ret) | ||
459 | return ret; | ||
460 | |||
461 | ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); | ||
462 | if (ret) | ||
463 | free_temp_buffer(urb); | ||
464 | |||
465 | return ret; | ||
466 | } | ||
467 | |||
468 | static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) | ||
469 | { | ||
470 | usb_hcd_unmap_urb_for_dma(hcd, urb); | ||
471 | free_temp_buffer(urb); | ||
472 | } | ||
473 | |||
386 | static const struct hc_driver tegra_ehci_hc_driver = { | 474 | static const struct hc_driver tegra_ehci_hc_driver = { |
387 | .description = hcd_name, | 475 | .description = hcd_name, |
388 | .product_desc = "Tegra EHCI Host Controller", | 476 | .product_desc = "Tegra EHCI Host Controller", |
@@ -398,6 +486,8 @@ static const struct hc_driver tegra_ehci_hc_driver = { | |||
398 | .shutdown = tegra_ehci_shutdown, | 486 | .shutdown = tegra_ehci_shutdown, |
399 | .urb_enqueue = ehci_urb_enqueue, | 487 | .urb_enqueue = ehci_urb_enqueue, |
400 | .urb_dequeue = ehci_urb_dequeue, | 488 | .urb_dequeue = ehci_urb_dequeue, |
489 | .map_urb_for_dma = tegra_ehci_map_urb_for_dma, | ||
490 | .unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma, | ||
401 | .endpoint_disable = ehci_endpoint_disable, | 491 | .endpoint_disable = ehci_endpoint_disable, |
402 | .endpoint_reset = ehci_endpoint_reset, | 492 | .endpoint_reset = ehci_endpoint_reset, |
403 | .get_frame_number = ehci_get_frame, | 493 | .get_frame_number = ehci_get_frame, |