From a12b8db02035673153bbf19bb3641a08bed9e4b8 Mon Sep 17 00:00:00 2001
From: David Brownell <david-b@pacbell.net>
Date: Sun, 22 Jul 2007 15:13:13 -0700
Subject: [PATCH] USB: fix scatterlist PIO case (IOMMU)

Update the scatterlist logic so that PIO options are also disabled
when an IOMMU may have coalesced pages during dma_map_sg() ... it's
not just HIGHMEM that can make trouble supporting both PIO and DMA
based host controller drivers.

There also seems to be a cross-arch issue here, with 64bit powerpc
not using an IOMMU define ... and its IOMMU_VMERGE config can always
be overridden on the kernel command line.  So this is better, but
still imperfect.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/core/message.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 25f63f1096b..ad4b956380d 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -411,15 +411,22 @@ int usb_sg_init (
 		 * Some systems need to revert to PIO when DMA is temporarily
 		 * unavailable.  For their sakes, both transfer_buffer and
 		 * transfer_dma are set when possible.  However this can only
-		 * work on systems without HIGHMEM, since DMA buffers located
-		 * in high memory are not directly addressable by the CPU for
-		 * PIO ... so when HIGHMEM is in use, transfer_buffer is NULL
+		 * work on systems without:
+		 *
+		 *  - HIGHMEM, since DMA buffers located in high memory are
+		 *    not directly addressable by the CPU for PIO;
+		 *
+		 *  - IOMMU, since dma_map_sg() is allowed to use an IOMMU to
+		 *    make virtually discontiguous buffers be "dma-contiguous"
+		 *    so that PIO and DMA need diferent numbers of URBs.
+		 *
+		 * So when HIGHMEM or IOMMU are in use, transfer_buffer is NULL
 		 * to prevent stale pointers and to help spot bugs.
 		 */
 		if (dma) {
 			io->urbs [i]->transfer_dma = sg_dma_address (sg + i);
 			len = sg_dma_len (sg + i);
-#ifdef CONFIG_HIGHMEM
+#if defined(CONFIG_HIGHMEM) || defined(CONFIG_IOMMU)
 			io->urbs[i]->transfer_buffer = NULL;
 #else
 			io->urbs[i]->transfer_buffer =
-- 
2.41.3