diff options
Diffstat (limited to 'arch/v850/kernel/rte_mb_a_pci.c')
-rw-r--r-- | arch/v850/kernel/rte_mb_a_pci.c | 796 |
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); |