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
# Next, the SA1111 stuff.
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
obj-$(CONFIG_SA1100_ADSBITSY) += adsbitsy.o
......
......@@ -20,8 +20,6 @@
#include <asm/page.h>
#include "pcipool.h"
/*
* Pool allocator ... wraps the pci_alloc_consistent page allocator, so
* small blocks are easily used by drivers for bus mastering controllers.
......@@ -33,7 +31,6 @@ struct pci_pool { /* the pool */
spinlock_t lock;
size_t blocks_per_page;
size_t size;
int flags;
struct pci_dev *dev;
size_t allocation;
char name [32];
......@@ -52,6 +49,19 @@ struct pci_page { /* cacheable header for 'allocation' bytes */
// #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.
......@@ -60,7 +70,7 @@ struct pci_page { /* cacheable header for 'allocation' bytes */
* @size: size of the blocks in this pool.
* @align: alignment requirement for blocks; must be a power of two
* @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
* 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 */
*/
struct pci_pool *
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;
......@@ -100,13 +110,9 @@ pci_pool_create (const char *name, struct pci_dev *pdev,
} else if (allocation < size)
return 0;
if (!(retval = kmalloc (sizeof *retval, flags)))
if (!(retval = kmalloc (sizeof *retval, mem_flags)))
return retval;
#ifdef CONFIG_PCIPOOL_DEBUG
flags |= SLAB_POISON;
#endif
strncpy (retval->name, name, sizeof retval->name);
retval->name [sizeof retval->name - 1] = 0;
......@@ -114,14 +120,13 @@ pci_pool_create (const char *name, struct pci_dev *pdev,
INIT_LIST_HEAD (&retval->page_list);
spin_lock_init (&retval->lock);
retval->size = size;
retval->flags = flags;
retval->allocation = allocation;
retval->blocks_per_page = allocation / size;
init_waitqueue_head (&retval->waitq);
#ifdef CONFIG_PCIPOOL_DEBUG
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);
#endif
......@@ -143,11 +148,13 @@ pool_alloc_page (struct pci_pool *pool, int mem_flags)
if (!page)
return 0;
page->vaddr = pci_alloc_consistent (pool->dev,
pool->allocation, &page->dma);
pool->allocation,
&page->dma);
if (page->vaddr) {
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);
#endif
list_add (&page->page_list, &pool->page_list);
} else {
kfree (page);
......@@ -173,8 +180,9 @@ pool_free_page (struct pci_pool *pool, struct pci_page *page)
{
dma_addr_t dma = page->dma;
if (pool->flags & SLAB_POISON)
#ifdef CONFIG_DEBUG_SLAB
memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
#endif
pci_free_consistent (pool->dev, pool->allocation, page->vaddr, dma);
list_del (&page->page_list);
kfree (page);
......@@ -195,8 +203,7 @@ pci_pool_destroy (struct pci_pool *pool)
#ifdef CONFIG_PCIPOOL_DEBUG
printk (KERN_DEBUG "pcipool destroy %s/%s\n",
pool->dev ? pool->dev->slot_name : NULL,
pool->name);
slot_name(pool), pool->name);
#endif
spin_lock_irqsave (&pool->lock, flags);
......@@ -206,8 +213,7 @@ pci_pool_destroy (struct pci_pool *pool)
struct pci_page, page_list);
if (is_page_busy (pool->blocks_per_page, page->bitmap)) {
printk (KERN_ERR "pci_pool_destroy %s/%s, %p busy\n",
pool->dev ? pool->dev->slot_name : NULL,
pool->name, page->vaddr);
slot_name(pool), pool->name, page->vaddr);
/* leak the still-in-use consistent memory */
list_del (&page->page_list);
kfree (page);
......@@ -327,35 +333,32 @@ pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma)
int map, block;
if ((page = pool_find_page (pool, dma)) == 0) {
printk (KERN_ERR "pci_pool_free %s/%s, %p/%x (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",
printk (KERN_ERR "pci_pool_free %s/%s, %p/%lx (bad dma)\n",
pool->dev ? pool->dev->slot_name : NULL,
pool->name, vaddr, dma);
pool->name, vaddr, (unsigned long) dma);
return;
}
#endif
block = dma - page->dma;
block /= pool->size;
map = 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)) {
printk (KERN_ERR "pci_pool_free %s/%s, dma %x already free\n",
pool->dev ? pool->dev->slot_name : NULL,
pool->name, dma);
return;
}
#endif
if (pool->flags & SLAB_POISON)
memset (vaddr, POOL_POISON_BYTE, pool->size);
#endif
spin_lock_irqsave (&pool->lock, flags);
set_bit (block, &page->bitmap [map]);
......@@ -369,9 +372,19 @@ pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma)
spin_unlock_irqrestore (&pool->lock, flags);
}
EXPORT_SYMBOL (pci_pool_create);
EXPORT_SYMBOL (pci_pool_destroy);
EXPORT_SYMBOL (pci_pool_alloc);
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
{
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;
return consistent_alloc(gfp, size, handle);
......
......@@ -611,10 +611,13 @@ struct sa1111_device {
extern struct sa1111_device *sa1111;
int sa1111_check_dma_bug(dma_addr_t addr);
/*
* These frob the SKPCR register.
*/
void sa1111_enable_device(unsigned int mask);
void sa1111_disable_device(unsigned int mask);
#endif /* _ASM_ARCH_SA1111 */
......@@ -12,6 +12,34 @@
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)
{
/* No special bus mastering setup handling */
......@@ -61,17 +89,9 @@ pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr,
static inline dma_addr_t
pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
{
#ifdef CONFIG_SA1111
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)
if (dev_is_sa1111(hwdev))
return sa1111_map_single(hwdev, ptr, size, direction);
#endif
consistent_sync(ptr, size, direction);
return virt_to_bus(ptr);
}
......@@ -86,12 +106,9 @@ pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
static inline void
pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int direction)
{
#ifdef CONFIG_SA1111
extern void sa1111_unmap_single(struct pci_dev *, dma_addr_t, size_t, int);
if (hwdev != NULL)
if (dev_is_sa1111(hwdev))
sa1111_unmap_single(hwdev, dma_addr, size, direction);
#endif
/* nothing to do */
}
......@@ -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
* 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_LEN(LEN_NAME) __u32 LEN_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
{
int i;
if (dev_is_sa1111(hwdev))
return sa1111_map_sg(hwdev, sg, nents, direction);
for (i = 0; i < nents; i++, sg++) {
char *virt;
......@@ -153,6 +173,11 @@ pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int directi
static inline void
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 */
}
......@@ -168,6 +193,11 @@ pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direc
static inline void
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);
}
......@@ -182,6 +212,11 @@ pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int d
{
int i;
if (dev_is_sa1111(hwdev)) {
sa1111_dma_sync_sg(hwdev, sg, nelems, direction);
return;
}
for (i = 0; i < nelems; i++, sg++) {
char *virt = page_address(sg->page) + sg->offset;
consistent_sync(virt, sg->length, direction);
......@@ -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. */
#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
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