Subject: [PATCH 2.5] PCI DMA fixes for non-coherent arch

From: Vivien Chappelier <[email-masked]>
Date: Sun, 26 Jan 2003 03:10:46 +0100 (CET)

Hi,

	Here's a patch to fix various bugs in the PCI DMA routines on
non-coherent archs:
	- when unmapped, a buffer should be flushed if PCI_DMA_FROMDEVICE
is set, which is not the opposite of PCI_DMA_TODEVICE (both can be set)
	- on non-coherent archs, DECLARE_PCI_UNMAP_ADDR and friends are
needed, since pci_unmap_{page,single} is not a nop
	- the scatter buffer offset was not added in pci_dma_sync_sg

Vivien.

--- include/asm-mips64/pci.h	2002-10-09 23:12:58.000000000 +0200
+++ include/asm-mips64/pci.h	2002-12-08 13:54:03.000000000 +0100
@@ -24,6 +24,8 @@
 #define PCIBIOS_MIN_IO		0x1000
 #define PCIBIOS_MIN_MEM		0x10000000
 
+struct pci_dev;
+
 static inline void pcibios_set_master(struct pci_dev *dev)
 {
 	/* No special bus mastering setup handling */
@@ -44,6 +46,7 @@
 #include <asm/scatterlist.h>
 #include <linux/string.h>
 #include <asm/io.h>
+#include <linux/pci.h>
 
 #if defined(CONFIG_DDB5074) || defined(CONFIG_DDB5476)
 #undef PCIBIOS_MIN_IO
@@ -52,8 +55,6 @@
 #define PCIBIOS_MIN_MEM		0x1000000
 #endif
 
-struct pci_dev;
-
 /*
  * The PCI address space does equal the physical memory address space.  The
  * networking and block device layers use this boolean for bounce buffer
@@ -141,17 +142,33 @@
 static inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
 				    size_t size, int direction)
 {
+	unsigned long addr;
+
 	if (direction == PCI_DMA_NONE)
 		BUG();
 
-	if (direction != PCI_DMA_TODEVICE) {
-		unsigned long addr;
+	if (direction == PCI_DMA_TODEVICE)
+		return; /* nothing to do */
 
-		addr = baddr_to_bus(hwdev->bus, dma_addr) + PAGE_OFFSET;
-		dma_cache_wback_inv(addr, size);
-	}
+	addr = baddr_to_bus(hwdev->bus, dma_addr) + PAGE_OFFSET;
+	dma_cache_wback_inv(addr, size);
 }
 
+#ifdef CONFIG_NONCOHERENT_IO
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+#else
 /* pci_unmap_{page,single} is a nop so... */
 #define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
 #define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
@@ -159,6 +176,7 @@
 #define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	do { } while (0)
 #define pci_unmap_len(PTR, LEN_NAME)		(0)
 #define pci_unmap_len_set(PTR, LEN_NAME, VAL)	do { } while (0)
+#endif
 
 /*
  * pci_{map,unmap}_single_page maps a kernel page to a dma_addr_t. identical
@@ -182,15 +200,16 @@
 static inline void pci_unmap_page(struct pci_dev *hwdev, dma_addr_t dma_address,
 				  size_t size, int direction)
 {
+	unsigned long addr;
+
 	if (direction == PCI_DMA_NONE)
 		BUG();
 
-	if (direction != PCI_DMA_TODEVICE) {
-		unsigned long addr;
+	if (direction == PCI_DMA_TODEVICE)
+		return; /* nothing to do */
 
-		addr = baddr_to_bus(hwdev->bus, dma_address) + PAGE_OFFSET;
-		dma_cache_wback_inv(addr, size);
-	}
+	addr = baddr_to_bus(hwdev->bus, dma_address) + PAGE_OFFSET;
+	dma_cache_wback_inv(addr, size);
 }
 
 /*
@@ -301,9 +320,12 @@
 
 	/* Make sure that gcc doesn't leave the empty loop body.  */
 #ifdef CONFIG_NONCOHERENT_IO
-	for (i = 0; i < nelems; i++, sg++)
-		dma_cache_wback_inv((unsigned long)page_address(sg->page),
-		                    sg->length);
+	for (i = 0; i < nelems; i++, sg++) {
+		unsigned long addr;
+
+		addr = (unsigned long) page_address(sg->page);
+		dma_cache_wback_inv(addr + sg->offset, sg->length);
+	}
 #endif
 }
 #endif /* CONFIG_MAPPED_PCI_IO  */
--- include/asm-mips/pci.h	2002-10-09 23:12:58.000000000 +0200
+++ include/asm-mips/pci.h	2002-12-08 13:54:03.000000000 +0100
@@ -24,6 +24,8 @@
 #define PCIBIOS_MIN_IO		0x1000
 #define PCIBIOS_MIN_MEM		0x10000000
 
+struct pci_dev;
+
 static inline void pcibios_set_master(struct pci_dev *dev)
 {
 	/* No special bus mastering setup handling */
@@ -44,6 +46,7 @@
 #include <asm/scatterlist.h>
 #include <linux/string.h>
 #include <asm/io.h>
+#include <linux/pci.h>
 
 #if defined(CONFIG_DDB5074) || defined(CONFIG_DDB5476)
 #undef PCIBIOS_MIN_IO
@@ -52,8 +55,6 @@
 #define PCIBIOS_MIN_MEM		0x1000000
 #endif
 
-struct pci_dev;
-
 /*
  * The PCI address space does equal the physical memory address space.  The
  * networking and block device layers use this boolean for bounce buffer
@@ -141,17 +142,33 @@
 static inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
 				    size_t size, int direction)
 {
+	unsigned long addr;
+
 	if (direction == PCI_DMA_NONE)
 		BUG();
 
-	if (direction != PCI_DMA_TODEVICE) {
-		unsigned long addr;
+	if (direction == PCI_DMA_TODEVICE)
+		return; /* nothing to do */
 
-		addr = baddr_to_bus(hwdev->bus, dma_addr) + PAGE_OFFSET;
-		dma_cache_wback_inv(addr, size);
-	}
+	addr = baddr_to_bus(hwdev->bus, dma_addr) + PAGE_OFFSET;
+	dma_cache_wback_inv(addr, size);
 }
 
+#ifdef CONFIG_NONCOHERENT_IO
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+#else
 /* pci_unmap_{page,single} is a nop so... */
 #define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
 #define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
@@ -159,6 +176,7 @@
 #define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	do { } while (0)
 #define pci_unmap_len(PTR, LEN_NAME)		(0)
 #define pci_unmap_len_set(PTR, LEN_NAME, VAL)	do { } while (0)
+#endif
 
 /*
  * pci_{map,unmap}_single_page maps a kernel page to a dma_addr_t. identical
@@ -182,15 +200,16 @@
 static inline void pci_unmap_page(struct pci_dev *hwdev, dma_addr_t dma_address,
 				  size_t size, int direction)
 {
+	unsigned long addr;
+
 	if (direction == PCI_DMA_NONE)
 		BUG();
 
-	if (direction != PCI_DMA_TODEVICE) {
-		unsigned long addr;
+	if (direction == PCI_DMA_TODEVICE)
+		return; /* nothing to do */
 
-		addr = baddr_to_bus(hwdev->bus, dma_address) + PAGE_OFFSET;
-		dma_cache_wback_inv(addr, size);
-	}
+	addr = baddr_to_bus(hwdev->bus, dma_address) + PAGE_OFFSET;
+	dma_cache_wback_inv(addr, size);
 }
 
 /*
@@ -301,9 +320,12 @@
 
 	/* Make sure that gcc doesn't leave the empty loop body.  */
 #ifdef CONFIG_NONCOHERENT_IO
-	for (i = 0; i < nelems; i++, sg++)
-		dma_cache_wback_inv((unsigned long)page_address(sg->page),
-		                    sg->length);
+	for (i = 0; i < nelems; i++, sg++) {
+		unsigned long addr;
+
+		addr = (unsigned long) page_address(sg->page);
+		dma_cache_wback_inv(addr + sg->offset, sg->length);
+	}
 #endif
 }
 #endif /* CONFIG_MAPPED_PCI_IO  */