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