Commit 969d67a8 authored by Christopher Hoover's avatar Christopher Hoover Committed by Russell King

[ARM PATCH] 1255/1: [PATCH] SA-1111 PCI support for USB

Fixes several oopsen in the SA-1111 "fake" PCI support

Complete re-write of the SA-1111 DMA bug "bounce buffer" workaround.

Merge latest drivers/pci/pool.c into mach-sa1100/pcipool.c (pool
allocation debugging follows CONFIG_DEBUG_SLAB a la drivers/pci/pool.c)

/arch/arm/mach-sa1100/pcipool.h can be deleted
(unrelated: /arch/arm/mach-sa1100/sa1111-ohci.c can be deleted)

Applies to 2.5.30-rmk1.  Should be back ported to 2.4 as the existing
SA-1111 bounce buffer code is broken.

(This patch is *required* for the OHCI HCD driver that is part of 2.5.21+.)
parent 50a06edf
...@@ -27,7 +27,7 @@ endif ...@@ -27,7 +27,7 @@ endif
# Next, the SA1111 stuff. # Next, the SA1111 stuff.
obj-$(CONFIG_SA1111) += sa1111.o obj-$(CONFIG_SA1111) += sa1111.o
obj-$(CONFIG_USB_OHCI_SA1111) += sa1111-pcibuf.o pcipool.o obj-$(CONFIG_USB_OHCI_HCD) += sa1111-pcibuf.o pcipool.o
# Specific board support # Specific board support
obj-$(CONFIG_SA1100_ADSBITSY) += adsbitsy.o obj-$(CONFIG_SA1100_ADSBITSY) += adsbitsy.o
......
...@@ -20,8 +20,6 @@ ...@@ -20,8 +20,6 @@
#include <asm/page.h> #include <asm/page.h>
#include "pcipool.h"
/* /*
* Pool allocator ... wraps the pci_alloc_consistent page allocator, so * Pool allocator ... wraps the pci_alloc_consistent page allocator, so
* small blocks are easily used by drivers for bus mastering controllers. * small blocks are easily used by drivers for bus mastering controllers.
...@@ -33,7 +31,6 @@ struct pci_pool { /* the pool */ ...@@ -33,7 +31,6 @@ struct pci_pool { /* the pool */
spinlock_t lock; spinlock_t lock;
size_t blocks_per_page; size_t blocks_per_page;
size_t size; size_t size;
int flags;
struct pci_dev *dev; struct pci_dev *dev;
size_t allocation; size_t allocation;
char name [32]; char name [32];
...@@ -52,6 +49,19 @@ struct pci_page { /* cacheable header for 'allocation' bytes */ ...@@ -52,6 +49,19 @@ struct pci_page { /* cacheable header for 'allocation' bytes */
// #define CONFIG_PCIPOOL_DEBUG // #define CONFIG_PCIPOOL_DEBUG
static inline const char *slot_name(const struct pci_pool *pool)
{
const struct pci_dev *pdev = pool->dev;
if (pdev == 0)
return "[0]";
else if (dev_is_sa1111(pdev))
return "[SA-1111]";
else
return pdev->slot_name;
}
/** /**
* pci_pool_create - Creates a pool of pci consistent memory blocks, for dma. * pci_pool_create - Creates a pool of pci consistent memory blocks, for dma.
...@@ -60,7 +70,7 @@ struct pci_page { /* cacheable header for 'allocation' bytes */ ...@@ -60,7 +70,7 @@ struct pci_page { /* cacheable header for 'allocation' bytes */
* @size: size of the blocks in this pool. * @size: size of the blocks in this pool.
* @align: alignment requirement for blocks; must be a power of two * @align: alignment requirement for blocks; must be a power of two
* @allocation: returned blocks won't cross this boundary (or zero) * @allocation: returned blocks won't cross this boundary (or zero)
* @flags: SLAB_* flags (not all are supported). * @mem_flags: SLAB_* flags.
* *
* Returns a pci allocation pool with the requested characteristics, or * Returns a pci allocation pool with the requested characteristics, or
* null if one can't be created. Given one of these pools, pci_pool_alloc() * null if one can't be created. Given one of these pools, pci_pool_alloc()
...@@ -76,7 +86,7 @@ struct pci_page { /* cacheable header for 'allocation' bytes */ ...@@ -76,7 +86,7 @@ struct pci_page { /* cacheable header for 'allocation' bytes */
*/ */
struct pci_pool * struct pci_pool *
pci_pool_create (const char *name, struct pci_dev *pdev, pci_pool_create (const char *name, struct pci_dev *pdev,
size_t size, size_t align, size_t allocation, int flags) size_t size, size_t align, size_t allocation, int mem_flags)
{ {
struct pci_pool *retval; struct pci_pool *retval;
...@@ -100,13 +110,9 @@ pci_pool_create (const char *name, struct pci_dev *pdev, ...@@ -100,13 +110,9 @@ pci_pool_create (const char *name, struct pci_dev *pdev,
} else if (allocation < size) } else if (allocation < size)
return 0; return 0;
if (!(retval = kmalloc (sizeof *retval, flags))) if (!(retval = kmalloc (sizeof *retval, mem_flags)))
return retval; return retval;
#ifdef CONFIG_PCIPOOL_DEBUG
flags |= SLAB_POISON;
#endif
strncpy (retval->name, name, sizeof retval->name); strncpy (retval->name, name, sizeof retval->name);
retval->name [sizeof retval->name - 1] = 0; retval->name [sizeof retval->name - 1] = 0;
...@@ -114,14 +120,13 @@ pci_pool_create (const char *name, struct pci_dev *pdev, ...@@ -114,14 +120,13 @@ pci_pool_create (const char *name, struct pci_dev *pdev,
INIT_LIST_HEAD (&retval->page_list); INIT_LIST_HEAD (&retval->page_list);
spin_lock_init (&retval->lock); spin_lock_init (&retval->lock);
retval->size = size; retval->size = size;
retval->flags = flags;
retval->allocation = allocation; retval->allocation = allocation;
retval->blocks_per_page = allocation / size; retval->blocks_per_page = allocation / size;
init_waitqueue_head (&retval->waitq); init_waitqueue_head (&retval->waitq);
#ifdef CONFIG_PCIPOOL_DEBUG #ifdef CONFIG_PCIPOOL_DEBUG
printk (KERN_DEBUG "pcipool create %s/%s size %d, %d/page (%d alloc)\n", printk (KERN_DEBUG "pcipool create %s/%s size %d, %d/page (%d alloc)\n",
pdev ? pdev->slot_name : NULL, retval->name, size, slot_name(retval), retval->name, size,
retval->blocks_per_page, allocation); retval->blocks_per_page, allocation);
#endif #endif
...@@ -143,11 +148,13 @@ pool_alloc_page (struct pci_pool *pool, int mem_flags) ...@@ -143,11 +148,13 @@ pool_alloc_page (struct pci_pool *pool, int mem_flags)
if (!page) if (!page)
return 0; return 0;
page->vaddr = pci_alloc_consistent (pool->dev, page->vaddr = pci_alloc_consistent (pool->dev,
pool->allocation, &page->dma); pool->allocation,
&page->dma);
if (page->vaddr) { if (page->vaddr) {
memset (page->bitmap, 0xff, mapsize); // bit set == free memset (page->bitmap, 0xff, mapsize); // bit set == free
if (pool->flags & SLAB_POISON) #ifdef CONFIG_DEBUG_SLAB
memset (page->vaddr, POOL_POISON_BYTE, pool->allocation); memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
#endif
list_add (&page->page_list, &pool->page_list); list_add (&page->page_list, &pool->page_list);
} else { } else {
kfree (page); kfree (page);
...@@ -173,8 +180,9 @@ pool_free_page (struct pci_pool *pool, struct pci_page *page) ...@@ -173,8 +180,9 @@ pool_free_page (struct pci_pool *pool, struct pci_page *page)
{ {
dma_addr_t dma = page->dma; dma_addr_t dma = page->dma;
if (pool->flags & SLAB_POISON) #ifdef CONFIG_DEBUG_SLAB
memset (page->vaddr, POOL_POISON_BYTE, pool->allocation); memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
#endif
pci_free_consistent (pool->dev, pool->allocation, page->vaddr, dma); pci_free_consistent (pool->dev, pool->allocation, page->vaddr, dma);
list_del (&page->page_list); list_del (&page->page_list);
kfree (page); kfree (page);
...@@ -195,8 +203,7 @@ pci_pool_destroy (struct pci_pool *pool) ...@@ -195,8 +203,7 @@ pci_pool_destroy (struct pci_pool *pool)
#ifdef CONFIG_PCIPOOL_DEBUG #ifdef CONFIG_PCIPOOL_DEBUG
printk (KERN_DEBUG "pcipool destroy %s/%s\n", printk (KERN_DEBUG "pcipool destroy %s/%s\n",
pool->dev ? pool->dev->slot_name : NULL, slot_name(pool), pool->name);
pool->name);
#endif #endif
spin_lock_irqsave (&pool->lock, flags); spin_lock_irqsave (&pool->lock, flags);
...@@ -206,8 +213,7 @@ pci_pool_destroy (struct pci_pool *pool) ...@@ -206,8 +213,7 @@ pci_pool_destroy (struct pci_pool *pool)
struct pci_page, page_list); struct pci_page, page_list);
if (is_page_busy (pool->blocks_per_page, page->bitmap)) { if (is_page_busy (pool->blocks_per_page, page->bitmap)) {
printk (KERN_ERR "pci_pool_destroy %s/%s, %p busy\n", printk (KERN_ERR "pci_pool_destroy %s/%s, %p busy\n",
pool->dev ? pool->dev->slot_name : NULL, slot_name(pool), pool->name, page->vaddr);
pool->name, page->vaddr);
/* leak the still-in-use consistent memory */ /* leak the still-in-use consistent memory */
list_del (&page->page_list); list_del (&page->page_list);
kfree (page); kfree (page);
...@@ -327,35 +333,32 @@ pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma) ...@@ -327,35 +333,32 @@ pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma)
int map, block; int map, block;
if ((page = pool_find_page (pool, dma)) == 0) { if ((page = pool_find_page (pool, dma)) == 0) {
printk (KERN_ERR "pci_pool_free %s/%s, %p/%x (bad dma)\n", printk (KERN_ERR "pci_pool_free %s/%s, %p/%lx (bad dma)\n",
pool->dev ? pool->dev->slot_name : NULL,
pool->name, vaddr, dma);
return;
}
#ifdef CONFIG_PCIPOOL_DEBUG
if (((dma - page->dma) + (void *)page->vaddr) != vaddr) {
printk (KERN_ERR "pci_pool_free %s/%s, %p (bad vaddr)/%x\n",
pool->dev ? pool->dev->slot_name : NULL, pool->dev ? pool->dev->slot_name : NULL,
pool->name, vaddr, dma); pool->name, vaddr, (unsigned long) dma);
return; return;
} }
#endif
block = dma - page->dma; block = dma - page->dma;
block /= pool->size; block /= pool->size;
map = block / BITS_PER_LONG; map = block / BITS_PER_LONG;
block %= BITS_PER_LONG; block %= BITS_PER_LONG;
#ifdef CONFIG_PCIPOOL_DEBUG #ifdef CONFIG_DEBUG_SLAB
if (((dma - page->dma) + (void *)page->vaddr) != vaddr) {
printk (KERN_ERR "pci_pool_free %s/%s, %p (bad vaddr)/%lx\n",
pool->dev ? pool->dev->slot_name : NULL,
pool->name, vaddr, (unsigned long) dma);
return;
}
if (page->bitmap [map] & (1UL << block)) { if (page->bitmap [map] & (1UL << block)) {
printk (KERN_ERR "pci_pool_free %s/%s, dma %x already free\n", printk (KERN_ERR "pci_pool_free %s/%s, dma %x already free\n",
pool->dev ? pool->dev->slot_name : NULL, pool->dev ? pool->dev->slot_name : NULL,
pool->name, dma); pool->name, dma);
return; return;
} }
#endif
if (pool->flags & SLAB_POISON)
memset (vaddr, POOL_POISON_BYTE, pool->size); memset (vaddr, POOL_POISON_BYTE, pool->size);
#endif
spin_lock_irqsave (&pool->lock, flags); spin_lock_irqsave (&pool->lock, flags);
set_bit (block, &page->bitmap [map]); set_bit (block, &page->bitmap [map]);
...@@ -369,9 +372,19 @@ pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma) ...@@ -369,9 +372,19 @@ pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma)
spin_unlock_irqrestore (&pool->lock, flags); spin_unlock_irqrestore (&pool->lock, flags);
} }
EXPORT_SYMBOL (pci_pool_create); EXPORT_SYMBOL (pci_pool_create);
EXPORT_SYMBOL (pci_pool_destroy); EXPORT_SYMBOL (pci_pool_destroy);
EXPORT_SYMBOL (pci_pool_alloc); EXPORT_SYMBOL (pci_pool_alloc);
EXPORT_SYMBOL (pci_pool_free); EXPORT_SYMBOL (pci_pool_free);
/* **************************************** */
static int __init pcipool_init(void)
{
MOD_INC_USE_COUNT; /* never unload */
return 0;
}
module_init(pcipool_init);
MODULE_LICENSE("GPL");
struct pci_pool *pci_pool_create (const char *name, struct pci_dev *dev,
size_t size, size_t align, size_t allocation, int flags);
void pci_pool_destroy (struct pci_pool *pool);
void *pci_pool_alloc (struct pci_pool *pool, int flags, dma_addr_t *handle);
void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr);
This diff is collapsed.
...@@ -98,7 +98,8 @@ void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *handl ...@@ -98,7 +98,8 @@ void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *handl
{ {
int gfp = GFP_KERNEL; int gfp = GFP_KERNEL;
if (hwdev == NULL || hwdev->dma_mask != 0xffffffff) if (hwdev == NULL || dev_is_sa1111(hwdev) ||
hwdev->dma_mask != 0xffffffff)
gfp |= GFP_DMA; gfp |= GFP_DMA;
return consistent_alloc(gfp, size, handle); return consistent_alloc(gfp, size, handle);
......
...@@ -611,10 +611,13 @@ struct sa1111_device { ...@@ -611,10 +611,13 @@ struct sa1111_device {
extern struct sa1111_device *sa1111; extern struct sa1111_device *sa1111;
int sa1111_check_dma_bug(dma_addr_t addr);
/* /*
* These frob the SKPCR register. * These frob the SKPCR register.
*/ */
void sa1111_enable_device(unsigned int mask); void sa1111_enable_device(unsigned int mask);
void sa1111_disable_device(unsigned int mask); void sa1111_disable_device(unsigned int mask);
#endif /* _ASM_ARCH_SA1111 */ #endif /* _ASM_ARCH_SA1111 */
...@@ -12,6 +12,34 @@ ...@@ -12,6 +12,34 @@
struct pci_dev; struct pci_dev;
/*
* For SA-1111 these functions are "magic" and utilize bounce
* buffers as need to workaround SA-1111 DMA bugs. They are called in
* place of their pci_* counterparts when dev_is_sa1111() returns true.
*/
dma_addr_t sa1111_map_single(struct pci_dev *, void *, size_t, int);
void sa1111_unmap_single(struct pci_dev *, dma_addr_t, size_t, int);
int sa1111_map_sg(struct pci_dev *, struct scatterlist *, int, int);
void sa1111_unmap_sg(struct pci_dev *, struct scatterlist *, int, int);
void sa1111_dma_sync_single(struct pci_dev *, dma_addr_t, size_t, int);
void sa1111_dma_sync_sg(struct pci_dev *, struct scatterlist *, int, int);
#ifdef CONFIG_SA1111
#define SA1111_FAKE_PCIDEV ((struct pci_dev *) 1111)
static inline int dev_is_sa1111(const struct pci_dev *dev)
{
return (dev == SA1111_FAKE_PCIDEV);
}
#else
static inline int dev_is_sa1111(const struct pci_dev *dev) { return 0; }
#endif
static inline void pcibios_set_master(struct pci_dev *dev) static inline void pcibios_set_master(struct pci_dev *dev)
{ {
/* No special bus mastering setup handling */ /* No special bus mastering setup handling */
...@@ -61,17 +89,9 @@ pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, ...@@ -61,17 +89,9 @@ pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr,
static inline dma_addr_t static inline dma_addr_t
pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
{ {
#ifdef CONFIG_SA1111 if (dev_is_sa1111(hwdev))
extern dma_addr_t sa1111_map_single(struct pci_dev *, void *, size_t, int);
/*
* for SA1111 these functions are "magic" and relocate buffers. We
* only need to do these if hwdev is non-null; otherwise we expect
* the buffer to already be suitable for DMA.
*/
if (hwdev != NULL)
return sa1111_map_single(hwdev, ptr, size, direction); return sa1111_map_single(hwdev, ptr, size, direction);
#endif
consistent_sync(ptr, size, direction); consistent_sync(ptr, size, direction);
return virt_to_bus(ptr); return virt_to_bus(ptr);
} }
...@@ -86,12 +106,9 @@ pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) ...@@ -86,12 +106,9 @@ pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
static inline void static inline void
pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int direction) pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int direction)
{ {
#ifdef CONFIG_SA1111 if (dev_is_sa1111(hwdev))
extern void sa1111_unmap_single(struct pci_dev *, dma_addr_t, size_t, int);
if (hwdev != NULL)
sa1111_unmap_single(hwdev, dma_addr, size, direction); sa1111_unmap_single(hwdev, dma_addr, size, direction);
#endif
/* nothing to do */ /* nothing to do */
} }
...@@ -99,7 +116,7 @@ pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int di ...@@ -99,7 +116,7 @@ pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int di
* Whether pci_unmap_{single,page} is a nop depends upon the * Whether pci_unmap_{single,page} is a nop depends upon the
* configuration. * configuration.
*/ */
#ifdef CONFIG_PCI #if defined(CONFIG_PCI) || defined(CONFIG_SA1111)
#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) dma_addr_t ADDR_NAME; #define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) dma_addr_t ADDR_NAME;
#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) __u32 LEN_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(PTR, ADDR_NAME) ((PTR)->ADDR_NAME)
...@@ -135,6 +152,9 @@ pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int directi ...@@ -135,6 +152,9 @@ pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int directi
{ {
int i; int i;
if (dev_is_sa1111(hwdev))
return sa1111_map_sg(hwdev, sg, nents, direction);
for (i = 0; i < nents; i++, sg++) { for (i = 0; i < nents; i++, sg++) {
char *virt; char *virt;
...@@ -153,6 +173,11 @@ pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int directi ...@@ -153,6 +173,11 @@ pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int directi
static inline void static inline void
pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction) pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction)
{ {
if (dev_is_sa1111(hwdev)) {
sa1111_unmap_sg(hwdev, sg, nents, direction);
return;
}
/* nothing to do */ /* nothing to do */
} }
...@@ -168,6 +193,11 @@ pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direc ...@@ -168,6 +193,11 @@ pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direc
static inline void static inline void
pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction) pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction)
{ {
if (dev_is_sa1111(hwdev)) {
sa1111_dma_sync_single(hwdev, dma_handle, size, direction);
return;
}
consistent_sync(bus_to_virt(dma_handle), size, direction); consistent_sync(bus_to_virt(dma_handle), size, direction);
} }
...@@ -182,6 +212,11 @@ pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int d ...@@ -182,6 +212,11 @@ pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int d
{ {
int i; int i;
if (dev_is_sa1111(hwdev)) {
sa1111_dma_sync_sg(hwdev, sg, nelems, direction);
return;
}
for (i = 0; i < nelems; i++, sg++) { for (i = 0; i < nelems; i++, sg++) {
char *virt = page_address(sg->page) + sg->offset; char *virt = page_address(sg->page) + sg->offset;
consistent_sync(virt, sg->length, direction); consistent_sync(virt, sg->length, direction);
...@@ -204,6 +239,19 @@ static inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) ...@@ -204,6 +239,19 @@ static inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask)
/* Return the index of the PCI controller for device PDEV. */ /* Return the index of the PCI controller for device PDEV. */
#define pci_controller_num(PDEV) (0) #define pci_controller_num(PDEV) (0)
#if defined(CONFIG_SA1111) && !defined(CONFIG_PCI)
/* SA-1111 needs these prototypes even when !defined(CONFIG_PCI) */
/* kmem_cache style wrapper around pci_alloc_consistent() */
struct pci_pool *pci_pool_create (const char *name, struct pci_dev *dev,
size_t size, size_t align, size_t allocation, int flags);
void pci_pool_destroy (struct pci_pool *pool);
void *pci_pool_alloc (struct pci_pool *pool, int flags, dma_addr_t *handle);
void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr);
#endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment