aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRuslan Bilovol <ruslan.bilovol@ti.com>2013-03-29 13:15:21 -0400
committerFelipe Balbi <balbi@ti.com>2013-04-02 04:42:49 -0400
commit8408fd1d83e39bf856d31a36b70bcc53527702fd (patch)
treeaede13a3701adefcb50f1587682630e4f682c9d2
parent2f1d57069338b14fcf4765ae2c25fc377da45b1f (diff)
usb: musb: implement (un)map_urb_for_dma hooks
MUSB controller cannot work in DMA mode with misaligned buffers, switching in PIO mode. HCD core has hooks that allow to override the default DMA mapping and unmapping routines for host controllers that have special DMA requirements, such as alignment constraints. It is observed that work in PIO mode is slow and it's better to align buffers properly before passing them to MUSB This increased throughput 80->120 MBits/s over musb@omap4 with USB Gigabit Ethernet adapter attached. Some ideas are taken from ehci-tegra.c Signed-off-by: Ruslan Bilovol <ruslan.bilovol@ti.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r--drivers/usb/musb/musb_host.c117
1 files changed, 117 insertions, 0 deletions
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 51e9e8a38444..8914dec49f01 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2465,6 +2465,118 @@ static int musb_bus_resume(struct usb_hcd *hcd)
2465 return 0; 2465 return 0;
2466} 2466}
2467 2467
2468
2469#ifndef CONFIG_MUSB_PIO_ONLY
2470
2471#define MUSB_USB_DMA_ALIGN 4
2472
2473struct musb_temp_buffer {
2474 void *kmalloc_ptr;
2475 void *old_xfer_buffer;
2476 u8 data[0];
2477};
2478
2479static void musb_free_temp_buffer(struct urb *urb)
2480{
2481 enum dma_data_direction dir;
2482 struct musb_temp_buffer *temp;
2483
2484 if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
2485 return;
2486
2487 dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
2488
2489 temp = container_of(urb->transfer_buffer, struct musb_temp_buffer,
2490 data);
2491
2492 if (dir == DMA_FROM_DEVICE) {
2493 memcpy(temp->old_xfer_buffer, temp->data,
2494 urb->transfer_buffer_length);
2495 }
2496 urb->transfer_buffer = temp->old_xfer_buffer;
2497 kfree(temp->kmalloc_ptr);
2498
2499 urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
2500}
2501
2502static int musb_alloc_temp_buffer(struct urb *urb, gfp_t mem_flags)
2503{
2504 enum dma_data_direction dir;
2505 struct musb_temp_buffer *temp;
2506 void *kmalloc_ptr;
2507 size_t kmalloc_size;
2508
2509 if (urb->num_sgs || urb->sg ||
2510 urb->transfer_buffer_length == 0 ||
2511 !((uintptr_t)urb->transfer_buffer & (MUSB_USB_DMA_ALIGN - 1)))
2512 return 0;
2513
2514 dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
2515
2516 /* Allocate a buffer with enough padding for alignment */
2517 kmalloc_size = urb->transfer_buffer_length +
2518 sizeof(struct musb_temp_buffer) + MUSB_USB_DMA_ALIGN - 1;
2519
2520 kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
2521 if (!kmalloc_ptr)
2522 return -ENOMEM;
2523
2524 /* Position our struct temp_buffer such that data is aligned */
2525 temp = PTR_ALIGN(kmalloc_ptr, MUSB_USB_DMA_ALIGN);
2526
2527
2528 temp->kmalloc_ptr = kmalloc_ptr;
2529 temp->old_xfer_buffer = urb->transfer_buffer;
2530 if (dir == DMA_TO_DEVICE)
2531 memcpy(temp->data, urb->transfer_buffer,
2532 urb->transfer_buffer_length);
2533 urb->transfer_buffer = temp->data;
2534
2535 urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
2536
2537 return 0;
2538}
2539
2540static int musb_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
2541 gfp_t mem_flags)
2542{
2543 struct musb *musb = hcd_to_musb(hcd);
2544 int ret;
2545
2546 /*
2547 * The DMA engine in RTL1.8 and above cannot handle
2548 * DMA addresses that are not aligned to a 4 byte boundary.
2549 * For such engine implemented (un)map_urb_for_dma hooks.
2550 * Do not use these hooks for RTL<1.8
2551 */
2552 if (musb->hwvers < MUSB_HWVERS_1800)
2553 return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
2554
2555 ret = musb_alloc_temp_buffer(urb, mem_flags);
2556 if (ret)
2557 return ret;
2558
2559 ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
2560 if (ret)
2561 musb_free_temp_buffer(urb);
2562
2563 return ret;
2564}
2565
2566static void musb_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
2567{
2568 struct musb *musb = hcd_to_musb(hcd);
2569
2570 usb_hcd_unmap_urb_for_dma(hcd, urb);
2571
2572 /* Do not use this hook for RTL<1.8 (see description above) */
2573 if (musb->hwvers < MUSB_HWVERS_1800)
2574 return;
2575
2576 musb_free_temp_buffer(urb);
2577}
2578#endif /* !CONFIG_MUSB_PIO_ONLY */
2579
2468const struct hc_driver musb_hc_driver = { 2580const struct hc_driver musb_hc_driver = {
2469 .description = "musb-hcd", 2581 .description = "musb-hcd",
2470 .product_desc = "MUSB HDRC host driver", 2582 .product_desc = "MUSB HDRC host driver",
@@ -2484,6 +2596,11 @@ const struct hc_driver musb_hc_driver = {
2484 .urb_dequeue = musb_urb_dequeue, 2596 .urb_dequeue = musb_urb_dequeue,
2485 .endpoint_disable = musb_h_disable, 2597 .endpoint_disable = musb_h_disable,
2486 2598
2599#ifndef CONFIG_MUSB_PIO_ONLY
2600 .map_urb_for_dma = musb_map_urb_for_dma,
2601 .unmap_urb_for_dma = musb_unmap_urb_for_dma,
2602#endif
2603
2487 .hub_status_data = musb_hub_status_data, 2604 .hub_status_data = musb_hub_status_data,
2488 .hub_control = musb_hub_control, 2605 .hub_control = musb_hub_control,
2489 .bus_suspend = musb_bus_suspend, 2606 .bus_suspend = musb_bus_suspend,