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 */
|