aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/ehci-tegra.c90
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
26struct tegra_ehci_hcd { 28struct 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
388struct temp_buffer {
389 void *kmalloc_ptr;
390 void *old_xfer_buffer;
391 u8 data[0];
392};
393
394static 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
416static 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
452static 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
468static 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
386static const struct hc_driver tegra_ehci_hc_driver = { 474static 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,