aboutsummaryrefslogtreecommitdiffstats
path: root/arch/v850/kernel/rte_mb_a_pci.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/v850/kernel/rte_mb_a_pci.c
downloadlinux-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz
linux-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/v850/kernel/rte_mb_a_pci.c')
-rw-r--r--arch/v850/kernel/rte_mb_a_pci.c796
1 files changed, 796 insertions, 0 deletions
diff --git a/arch/v850/kernel/rte_mb_a_pci.c b/arch/v850/kernel/rte_mb_a_pci.c
new file mode 100644
index 000000000000..074b50abc89d
--- /dev/null
+++ b/arch/v850/kernel/rte_mb_a_pci.c
@@ -0,0 +1,796 @@
+/*
+ * arch/v850/kernel/mb_a_pci.c -- PCI support for Midas lab RTE-MOTHER-A board
+ *
+ * Copyright (C) 2001,02,03 NEC Electronics Corporation
+ * Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ *
+ * Written by Miles Bader <miles@gnu.org>
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+
+#include <asm/machdep.h>
+
+/* __nomods_init is like __devinit, but is a no-op when modules are enabled.
+ This is used by some routines that can be called either during boot
+ or by a module. */
+#ifdef CONFIG_MODULES
+#define __nomods_init /*nothing*/
+#else
+#define __nomods_init __devinit
+#endif
+
+/* PCI devices on the Mother-A board can only do DMA to/from the MB SRAM
+ (the RTE-V850E/MA1-CB cpu board doesn't support PCI access to
+ CPU-board memory), and since linux DMA buffers are allocated in
+ normal kernel memory, we basically have to copy DMA blocks around
+ (this is like a `bounce buffer'). When a DMA block is `mapped', we
+ allocate an identically sized block in MB SRAM, and if we're doing
+ output to the device, copy the CPU-memory block to the MB-SRAM block.
+ When an active block is `unmapped', we will copy the block back to
+ CPU memory if necessary, and then deallocate the MB SRAM block.
+ Ack. */
+
+/* Where the motherboard SRAM is in the PCI-bus address space (the
+ first 512K of it is also mapped at PCI address 0). */
+#define PCI_MB_SRAM_ADDR 0x800000
+
+/* Convert CPU-view MB SRAM address to/from PCI-view addresses of the
+ same memory. */
+#define MB_SRAM_TO_PCI(mb_sram_addr) \
+ ((dma_addr_t)mb_sram_addr - MB_A_SRAM_ADDR + PCI_MB_SRAM_ADDR)
+#define PCI_TO_MB_SRAM(pci_addr) \
+ (void *)(pci_addr - PCI_MB_SRAM_ADDR + MB_A_SRAM_ADDR)
+
+static void pcibios_assign_resources (void);
+
+struct mb_pci_dev_irq {
+ unsigned dev; /* PCI device number */
+ unsigned irq_base; /* First IRQ */
+ unsigned query_pin; /* True if we should read the device's
+ Interrupt Pin info, and allocate
+ interrupt IRQ_BASE + PIN. */
+};
+
+/* PCI interrupts are mapped statically to GBUS interrupts. */
+static struct mb_pci_dev_irq mb_pci_dev_irqs[] = {
+ /* Motherboard SB82558 ethernet controller */
+ { 10, IRQ_MB_A_LAN, 0 },
+ /* PCI slot 1 */
+ { 8, IRQ_MB_A_PCI1(0), 1 },
+ /* PCI slot 2 */
+ { 9, IRQ_MB_A_PCI2(0), 1 }
+};
+#define NUM_MB_PCI_DEV_IRQS \
+ (sizeof mb_pci_dev_irqs / sizeof mb_pci_dev_irqs[0])
+
+
+/* PCI configuration primitives. */
+
+#define CONFIG_DMCFGA(bus, devfn, offs) \
+ (0x80000000 \
+ | ((offs) & ~0x3) \
+ | ((devfn) << 8) \
+ | ((bus)->number << 16))
+
+static int
+mb_pci_read (struct pci_bus *bus, unsigned devfn, int offs, int size, u32 *rval)
+{
+ u32 addr;
+ int flags;
+
+ local_irq_save (flags);
+
+ MB_A_PCI_PCICR = 0x7;
+ MB_A_PCI_DMCFGA = CONFIG_DMCFGA (bus, devfn, offs);
+
+ addr = MB_A_PCI_IO_ADDR + (offs & 0x3);
+
+ switch (size) {
+ case 1: *rval = *(volatile u8 *)addr; break;
+ case 2: *rval = *(volatile u16 *)addr; break;
+ case 4: *rval = *(volatile u32 *)addr; break;
+ }
+
+ if (MB_A_PCI_PCISR & 0x2000) {
+ MB_A_PCI_PCISR = 0x2000;
+ *rval = ~0;
+ }
+
+ MB_A_PCI_DMCFGA = 0;
+
+ local_irq_restore (flags);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+mb_pci_write (struct pci_bus *bus, unsigned devfn, int offs, int size, u32 val)
+{
+ u32 addr;
+ int flags;
+
+ local_irq_save (flags);
+
+ MB_A_PCI_PCICR = 0x7;
+ MB_A_PCI_DMCFGA = CONFIG_DMCFGA (bus, devfn, offs);
+
+ addr = MB_A_PCI_IO_ADDR + (offs & 0x3);
+
+ switch (size) {
+ case 1: *(volatile u8 *)addr = val; break;
+ case 2: *(volatile u16 *)addr = val; break;
+ case 4: *(volatile u32 *)addr = val; break;
+ }
+
+ if (MB_A_PCI_PCISR & 0x2000)
+ MB_A_PCI_PCISR = 0x2000;
+
+ MB_A_PCI_DMCFGA = 0;
+
+ local_irq_restore (flags);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops mb_pci_config_ops = {
+ .read = mb_pci_read,
+ .write = mb_pci_write,
+};
+
+
+/* PCI Initialization. */
+
+static struct pci_bus *mb_pci_bus = 0;
+
+/* Do initial PCI setup. */
+static int __devinit pcibios_init (void)
+{
+ u32 id = MB_A_PCI_PCIHIDR;
+ u16 vendor = id & 0xFFFF;
+ u16 device = (id >> 16) & 0xFFFF;
+
+ if (vendor == PCI_VENDOR_ID_PLX && device == PCI_DEVICE_ID_PLX_9080) {
+ printk (KERN_INFO
+ "PCI: PLX Technology PCI9080 HOST/PCI bridge\n");
+
+ MB_A_PCI_PCICR = 0x147;
+
+ MB_A_PCI_PCIBAR0 = 0x007FFF00;
+ MB_A_PCI_PCIBAR1 = 0x0000FF00;
+ MB_A_PCI_PCIBAR2 = 0x00800000;
+
+ MB_A_PCI_PCILTR = 0x20;
+
+ MB_A_PCI_PCIPBAM |= 0x3;
+
+ MB_A_PCI_PCISR = ~0; /* Clear errors. */
+
+ /* Reprogram the motherboard's IO/config address space,
+ as we don't support the GCS7 address space that the
+ default uses. */
+
+ /* Significant address bits used for decoding PCI GCS5 space
+ accessess. */
+ MB_A_PCI_DMRR = ~(MB_A_PCI_MEM_SIZE - 1);
+
+ /* I don't understand this, but the SolutionGear example code
+ uses such an offset, and it doesn't work without it. XXX */
+#if GCS5_SIZE == 0x00800000
+#define GCS5_CFG_OFFS 0x00800000
+#else
+#define GCS5_CFG_OFFS 0
+#endif
+
+ /* Address bit values for matching. Note that we have to give
+ the address from the motherboard's point of view, which is
+ different than the CPU's. */
+ /* PCI memory space. */
+ MB_A_PCI_DMLBAM = GCS5_CFG_OFFS + 0x0;
+ /* PCI I/O space. */
+ MB_A_PCI_DMLBAI =
+ GCS5_CFG_OFFS + (MB_A_PCI_IO_ADDR - GCS5_ADDR);
+
+ mb_pci_bus = pci_scan_bus (0, &mb_pci_config_ops, 0);
+
+ pcibios_assign_resources ();
+ } else
+ printk (KERN_ERR "PCI: HOST/PCI bridge not found\n");
+
+ return 0;
+}
+
+subsys_initcall (pcibios_init);
+
+char __devinit *pcibios_setup (char *option)
+{
+ /* Don't handle any options. */
+ return option;
+}
+
+
+int __nomods_init pcibios_enable_device (struct pci_dev *dev, int mask)
+{
+ u16 cmd, old_cmd;
+ int idx;
+ struct resource *r;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
+ for (idx = 0; idx < 6; idx++) {
+ r = &dev->resource[idx];
+ if (!r->start && r->end) {
+ printk(KERN_ERR "PCI: Device %s not available because "
+ "of resource collisions\n", pci_name(dev));
+ return -EINVAL;
+ }
+ if (r->flags & IORESOURCE_IO)
+ cmd |= PCI_COMMAND_IO;
+ if (r->flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+ if (cmd != old_cmd) {
+ printk("PCI: Enabling device %s (%04x -> %04x)\n",
+ pci_name(dev), old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ return 0;
+}
+
+
+/* Resource allocation. */
+static void __devinit pcibios_assign_resources (void)
+{
+ struct pci_dev *dev = NULL;
+ struct resource *r;
+
+ for_each_pci_dev(dev) {
+ unsigned di_num;
+ unsigned class = dev->class >> 8;
+
+ if (class && class != PCI_CLASS_BRIDGE_HOST) {
+ unsigned r_num;
+ for(r_num = 0; r_num < 6; r_num++) {
+ r = &dev->resource[r_num];
+ if (!r->start && r->end)
+ pci_assign_resource (dev, r_num);
+ }
+ }
+
+ /* Assign interrupts. */
+ for (di_num = 0; di_num < NUM_MB_PCI_DEV_IRQS; di_num++) {
+ struct mb_pci_dev_irq *di = &mb_pci_dev_irqs[di_num];
+
+ if (di->dev == PCI_SLOT (dev->devfn)) {
+ unsigned irq = di->irq_base;
+
+ if (di->query_pin) {
+ /* Find out which interrupt pin
+ this device uses (each PCI
+ slot has 4). */
+ u8 irq_pin;
+
+ pci_read_config_byte (dev,
+ PCI_INTERRUPT_PIN,
+ &irq_pin);
+
+ if (irq_pin == 0)
+ /* Doesn't use interrupts. */
+ continue;
+ else
+ irq += irq_pin - 1;
+ }
+
+ pcibios_update_irq (dev, irq);
+ }
+ }
+ }
+}
+
+void __devinit pcibios_update_irq (struct pci_dev *dev, int irq)
+{
+ dev->irq = irq;
+ pci_write_config_byte (dev, PCI_INTERRUPT_LINE, irq);
+}
+
+void __devinit
+pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
+ struct resource *res)
+{
+ unsigned long offset = 0;
+
+ if (res->flags & IORESOURCE_IO) {
+ offset = MB_A_PCI_IO_ADDR;
+ } else if (res->flags & IORESOURCE_MEM) {
+ offset = MB_A_PCI_MEM_ADDR;
+ }
+
+ region->start = res->start - offset;
+ region->end = res->end - offset;
+}
+
+
+/* Stubs for things we don't use. */
+
+/* Called after each bus is probed, but before its children are examined. */
+void pcibios_fixup_bus(struct pci_bus *b)
+{
+}
+
+void
+pcibios_align_resource (void *data, struct resource *res,
+ unsigned long size, unsigned long align)
+{
+}
+
+void pcibios_set_master (struct pci_dev *dev)
+{
+}
+
+
+/* Mother-A SRAM memory allocation. This is a simple first-fit allocator. */
+
+/* A memory free-list node. */
+struct mb_sram_free_area {
+ void *mem;
+ unsigned long size;
+ struct mb_sram_free_area *next;
+};
+
+/* The tail of the free-list, which starts out containing all the SRAM. */
+static struct mb_sram_free_area mb_sram_free_tail = {
+ (void *)MB_A_SRAM_ADDR, MB_A_SRAM_SIZE, 0
+};
+
+/* The free-list. */
+static struct mb_sram_free_area *mb_sram_free_areas = &mb_sram_free_tail;
+
+/* The free-list of free free-list nodes. (:-) */
+static struct mb_sram_free_area *mb_sram_free_free_areas = 0;
+
+/* Spinlock protecting the above globals. */
+static DEFINE_SPINLOCK(mb_sram_lock);
+
+/* Allocate a memory block at least SIZE bytes long in the Mother-A SRAM
+ space. */
+static void *alloc_mb_sram (size_t size)
+{
+ struct mb_sram_free_area *prev, *fa;
+ int flags;
+ void *mem = 0;
+
+ spin_lock_irqsave (mb_sram_lock, flags);
+
+ /* Look for a free area that can contain SIZE bytes. */
+ for (prev = 0, fa = mb_sram_free_areas; fa; prev = fa, fa = fa->next)
+ if (fa->size >= size) {
+ /* Found one! */
+ mem = fa->mem;
+
+ if (fa->size == size) {
+ /* In fact, it fits exactly, so remove
+ this node from the free-list. */
+ if (prev)
+ prev->next = fa->next;
+ else
+ mb_sram_free_areas = fa->next;
+ /* Put it on the free-list-entry-free-list. */
+ fa->next = mb_sram_free_free_areas;
+ mb_sram_free_free_areas = fa;
+ } else {
+ /* FA is bigger than SIZE, so just
+ reduce its size to account for this
+ allocation. */
+ fa->mem += size;
+ fa->size -= size;
+ }
+
+ break;
+ }
+
+ spin_unlock_irqrestore (mb_sram_lock, flags);
+
+ return mem;
+}
+
+/* Return the memory area MEM of size SIZE to the MB SRAM free pool. */
+static void free_mb_sram (void *mem, size_t size)
+{
+ struct mb_sram_free_area *prev, *fa, *new_fa;
+ int flags;
+ void *end = mem + size;
+
+ spin_lock_irqsave (mb_sram_lock, flags);
+
+ retry:
+ /* Find an adjacent free-list entry. */
+ for (prev = 0, fa = mb_sram_free_areas; fa; prev = fa, fa = fa->next)
+ if (fa->mem == end) {
+ /* FA is just after MEM, grow down to encompass it. */
+ fa->mem = mem;
+ fa->size += size;
+ goto done;
+ } else if (fa->mem + fa->size == mem) {
+ struct mb_sram_free_area *next_fa = fa->next;
+
+ /* FA is just before MEM, expand to encompass it. */
+ fa->size += size;
+
+ /* See if FA can now be merged with its successor. */
+ if (next_fa && fa->mem + fa->size == next_fa->mem) {
+ /* Yup; merge NEXT_FA's info into FA. */
+ fa->size += next_fa->size;
+ fa->next = next_fa->next;
+ /* Free NEXT_FA. */
+ next_fa->next = mb_sram_free_free_areas;
+ mb_sram_free_free_areas = next_fa;
+ }
+ goto done;
+ } else if (fa->mem > mem)
+ /* We've reached the right spot in the free-list
+ without finding an adjacent free-area, so add
+ a new free area to hold mem. */
+ break;
+
+ /* Make a new free-list entry. */
+
+ /* First, get a free-list entry. */
+ if (! mb_sram_free_free_areas) {
+ /* There are none, so make some. */
+ void *block;
+ size_t block_size = sizeof (struct mb_sram_free_area) * 8;
+
+ /* Don't hold the lock while calling kmalloc (I'm not
+ sure whether it would be a problem, since we use
+ GFP_ATOMIC, but it makes me nervous). */
+ spin_unlock_irqrestore (mb_sram_lock, flags);
+
+ block = kmalloc (block_size, GFP_ATOMIC);
+ if (! block)
+ panic ("free_mb_sram: can't allocate free-list entry");
+
+ /* Now get the lock back. */
+ spin_lock_irqsave (mb_sram_lock, flags);
+
+ /* Add the new free free-list entries. */
+ while (block_size > 0) {
+ struct mb_sram_free_area *nfa = block;
+ nfa->next = mb_sram_free_free_areas;
+ mb_sram_free_free_areas = nfa;
+ block += sizeof *nfa;
+ block_size -= sizeof *nfa;
+ }
+
+ /* Since we dropped the lock to call kmalloc, the
+ free-list could have changed, so retry from the
+ beginning. */
+ goto retry;
+ }
+
+ /* Remove NEW_FA from the free-list of free-list entries. */
+ new_fa = mb_sram_free_free_areas;
+ mb_sram_free_free_areas = new_fa->next;
+
+ /* NEW_FA initially holds only MEM. */
+ new_fa->mem = mem;
+ new_fa->size = size;
+
+ /* Insert NEW_FA in the free-list between PREV and FA. */
+ new_fa->next = fa;
+ if (prev)
+ prev->next = new_fa;
+ else
+ mb_sram_free_areas = new_fa;
+
+ done:
+ spin_unlock_irqrestore (mb_sram_lock, flags);
+}
+
+
+/* Maintainence of CPU -> Mother-A DMA mappings. */
+
+struct dma_mapping {
+ void *cpu_addr;
+ void *mb_sram_addr;
+ size_t size;
+ struct dma_mapping *next;
+};
+
+/* A list of mappings from CPU addresses to MB SRAM addresses for active
+ DMA blocks (that have been `granted' to the PCI device). */
+static struct dma_mapping *active_dma_mappings = 0;
+
+/* A list of free mapping objects. */
+static struct dma_mapping *free_dma_mappings = 0;
+
+/* Spinlock protecting the above globals. */
+static DEFINE_SPINLOCK(dma_mappings_lock);
+
+static struct dma_mapping *new_dma_mapping (size_t size)
+{
+ int flags;
+ struct dma_mapping *mapping;
+ void *mb_sram_block = alloc_mb_sram (size);
+
+ if (! mb_sram_block)
+ return 0;
+
+ spin_lock_irqsave (dma_mappings_lock, flags);
+
+ if (! free_dma_mappings) {
+ /* We're out of mapping structures, make more. */
+ void *mblock;
+ size_t mblock_size = sizeof (struct dma_mapping) * 8;
+
+ /* Don't hold the lock while calling kmalloc (I'm not
+ sure whether it would be a problem, since we use
+ GFP_ATOMIC, but it makes me nervous). */
+ spin_unlock_irqrestore (dma_mappings_lock, flags);
+
+ mblock = kmalloc (mblock_size, GFP_ATOMIC);
+ if (! mblock) {
+ free_mb_sram (mb_sram_block, size);
+ return 0;
+ }
+
+ /* Get the lock back. */
+ spin_lock_irqsave (dma_mappings_lock, flags);
+
+ /* Add the new mapping structures to the free-list. */
+ while (mblock_size > 0) {
+ struct dma_mapping *fm = mblock;
+ fm->next = free_dma_mappings;
+ free_dma_mappings = fm;
+ mblock += sizeof *fm;
+ mblock_size -= sizeof *fm;
+ }
+ }
+
+ /* Get a mapping struct from the freelist. */
+ mapping = free_dma_mappings;
+ free_dma_mappings = mapping->next;
+
+ /* Initialize the mapping. Other fields should be filled in by
+ caller. */
+ mapping->mb_sram_addr = mb_sram_block;
+ mapping->size = size;
+
+ /* Add it to the list of active mappings. */
+ mapping->next = active_dma_mappings;
+ active_dma_mappings = mapping;
+
+ spin_unlock_irqrestore (dma_mappings_lock, flags);
+
+ return mapping;
+}
+
+static struct dma_mapping *find_dma_mapping (void *mb_sram_addr)
+{
+ int flags;
+ struct dma_mapping *mapping;
+
+ spin_lock_irqsave (dma_mappings_lock, flags);
+
+ for (mapping = active_dma_mappings; mapping; mapping = mapping->next)
+ if (mapping->mb_sram_addr == mb_sram_addr) {
+ spin_unlock_irqrestore (dma_mappings_lock, flags);
+ return mapping;
+ }
+
+ panic ("find_dma_mapping: unmapped PCI DMA addr 0x%x",
+ MB_SRAM_TO_PCI (mb_sram_addr));
+}
+
+static struct dma_mapping *deactivate_dma_mapping (void *mb_sram_addr)
+{
+ int flags;
+ struct dma_mapping *mapping, *prev;
+
+ spin_lock_irqsave (dma_mappings_lock, flags);
+
+ for (prev = 0, mapping = active_dma_mappings;
+ mapping;
+ prev = mapping, mapping = mapping->next)
+ {
+ if (mapping->mb_sram_addr == mb_sram_addr) {
+ /* This is the MAPPING; deactivate it. */
+ if (prev)
+ prev->next = mapping->next;
+ else
+ active_dma_mappings = mapping->next;
+
+ spin_unlock_irqrestore (dma_mappings_lock, flags);
+
+ return mapping;
+ }
+ }
+
+ panic ("deactivate_dma_mapping: unmapped PCI DMA addr 0x%x",
+ MB_SRAM_TO_PCI (mb_sram_addr));
+}
+
+/* Return MAPPING to the freelist. */
+static inline void
+free_dma_mapping (struct dma_mapping *mapping)
+{
+ int flags;
+
+ free_mb_sram (mapping->mb_sram_addr, mapping->size);
+
+ spin_lock_irqsave (dma_mappings_lock, flags);
+
+ mapping->next = free_dma_mappings;
+ free_dma_mappings = mapping;
+
+ spin_unlock_irqrestore (dma_mappings_lock, flags);
+}
+
+
+/* Single PCI DMA mappings. */
+
+/* `Grant' to PDEV the memory block at CPU_ADDR, for doing DMA. The
+ 32-bit PCI bus mastering address to use is returned. the device owns
+ this memory until either pci_unmap_single or pci_dma_sync_single is
+ performed. */
+dma_addr_t
+pci_map_single (struct pci_dev *pdev, void *cpu_addr, size_t size, int dir)
+{
+ struct dma_mapping *mapping = new_dma_mapping (size);
+
+ if (! mapping)
+ return 0;
+
+ mapping->cpu_addr = cpu_addr;
+
+ if (dir == PCI_DMA_BIDIRECTIONAL || dir == PCI_DMA_TODEVICE)
+ memcpy (mapping->mb_sram_addr, cpu_addr, size);
+
+ return MB_SRAM_TO_PCI (mapping->mb_sram_addr);
+}
+
+/* Return to the CPU the PCI DMA memory block previously `granted' to
+ PDEV, at DMA_ADDR. */
+void pci_unmap_single (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size,
+ int dir)
+{
+ void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr);
+ struct dma_mapping *mapping = deactivate_dma_mapping (mb_sram_addr);
+
+ if (size != mapping->size)
+ panic ("pci_unmap_single: size (%d) doesn't match"
+ " size of mapping at PCI DMA addr 0x%x (%d)\n",
+ size, dma_addr, mapping->size);
+
+ /* Copy back the DMA'd contents if necessary. */
+ if (dir == PCI_DMA_BIDIRECTIONAL || dir == PCI_DMA_FROMDEVICE)
+ memcpy (mapping->cpu_addr, mb_sram_addr, size);
+
+ /* Return mapping to the freelist. */
+ free_dma_mapping (mapping);
+}
+
+/* Make physical memory consistent for a single streaming mode DMA
+ translation after a transfer.
+
+ If you perform a pci_map_single() but wish to interrogate the
+ buffer using the cpu, yet do not wish to teardown the PCI dma
+ mapping, you must call this function before doing so. At the next
+ point you give the PCI dma address back to the card, you must first
+ perform a pci_dma_sync_for_device, and then the device again owns
+ the buffer. */
+void
+pci_dma_sync_single_for_cpu (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size,
+ int dir)
+{
+ void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr);
+ struct dma_mapping *mapping = find_dma_mapping (mb_sram_addr);
+
+ /* Synchronize the DMA buffer with the CPU buffer if necessary. */
+ if (dir == PCI_DMA_FROMDEVICE)
+ memcpy (mapping->cpu_addr, mb_sram_addr, size);
+ else if (dir == PCI_DMA_TODEVICE)
+ ; /* nothing to do */
+ else
+ panic("pci_dma_sync_single: unsupported sync dir: %d", dir);
+}
+
+void
+pci_dma_sync_single_for_device (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size,
+ int dir)
+{
+ void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr);
+ struct dma_mapping *mapping = find_dma_mapping (mb_sram_addr);
+
+ /* Synchronize the DMA buffer with the CPU buffer if necessary. */
+ if (dir == PCI_DMA_FROMDEVICE)
+ ; /* nothing to do */
+ else if (dir == PCI_DMA_TODEVICE)
+ memcpy (mb_sram_addr, mapping->cpu_addr, size);
+ else
+ panic("pci_dma_sync_single: unsupported sync dir: %d", dir);
+}
+
+
+/* Scatter-gather PCI DMA mappings. */
+
+/* Do multiple DMA mappings at once. */
+int
+pci_map_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len, int dir)
+{
+ BUG ();
+ return 0;
+}
+
+/* Unmap multiple DMA mappings at once. */
+void
+pci_unmap_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len,int dir)
+{
+ BUG ();
+}
+
+/* Make physical memory consistent for a set of streaming mode DMA
+ translations after a transfer. The same as pci_dma_sync_single_* but
+ for a scatter-gather list, same rules and usage. */
+
+void
+pci_dma_sync_sg_for_cpu (struct pci_dev *dev, struct scatterlist *sg, int sg_len,
+ int dir)
+{
+ BUG ();
+}
+
+void
+pci_dma_sync_sg_for_device (struct pci_dev *dev, struct scatterlist *sg, int sg_len,
+ int dir)
+{
+ BUG ();
+}
+
+
+/* PCI mem mapping. */
+
+/* Allocate and map kernel buffer using consistent mode DMA for PCI
+ device. Returns non-NULL cpu-view pointer to the buffer if
+ successful and sets *DMA_ADDR to the pci side dma address as well,
+ else DMA_ADDR is undefined. */
+void *
+pci_alloc_consistent (struct pci_dev *pdev, size_t size, dma_addr_t *dma_addr)
+{
+ void *mb_sram_mem = alloc_mb_sram (size);
+ if (mb_sram_mem)
+ *dma_addr = MB_SRAM_TO_PCI (mb_sram_mem);
+ return mb_sram_mem;
+}
+
+/* Free and unmap a consistent DMA buffer. CPU_ADDR and DMA_ADDR must
+ be values that were returned from pci_alloc_consistent. SIZE must be
+ the same as what as passed into pci_alloc_consistent. References to
+ the memory and mappings assosciated with CPU_ADDR or DMA_ADDR past
+ this call are illegal. */
+void
+pci_free_consistent (struct pci_dev *pdev, size_t size, void *cpu_addr,
+ dma_addr_t dma_addr)
+{
+ void *mb_sram_mem = PCI_TO_MB_SRAM (dma_addr);
+ free_mb_sram (mb_sram_mem, size);
+}
+
+
+/* symbol exports (for modules) */
+
+EXPORT_SYMBOL (pci_map_single);
+EXPORT_SYMBOL (pci_unmap_single);
+EXPORT_SYMBOL (pci_alloc_consistent);
+EXPORT_SYMBOL (pci_free_consistent);
+EXPORT_SYMBOL (pci_dma_sync_single_for_cpu);
+EXPORT_SYMBOL (pci_dma_sync_single_for_device);