aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-tegra.c
diff options
context:
space:
mode:
authorRobert Morell <rmorell@nvidia.com>2011-03-09 19:28:57 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2011-03-11 17:22:12 -0500
commitfbf9865c6d96f4a131092d2018056e86113e5cea (patch)
treeee9c5f63d8709bf50817a730360464031761fa27 /drivers/usb/host/ehci-tegra.c
parent79ad3b5add4a683af02d1b51ccb699d1b01f1fbf (diff)
USB: ehci: tegra: Align DMA transfers to 32 bytes
The Tegra2 USB controller doesn't properly deal with misaligned DMA buffers, causing corruption. This is especially prevalent with USB network adapters, where skbuff alignment is often in the middle of a 4-byte dword. To avoid this, allocate a temporary buffer for the DMA if the provided buffer isn't sufficiently aligned. Signed-off-by: Robert Morell <rmorell@nvidia.com> Signed-off-by: Benoit Goby <benoit@android.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ehci-tegra.c')
-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,