📄 rte_mb_a_pci.c
字号:
/* * arch/v850/kernel/mb_a_pci.c -- PCI support for Midas lab RTE-MOTHER-A board * * Copyright (C) 2001,02,03,05 NEC Electronics Corporation * Copyright (C) 2001,02,03,05 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/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 ARRAY_SIZE(mb_pci_dev_irqs)/* PCI configuration primitives. */#define CONFIG_DMCFGA(bus, devfn, offs) \ (0x80000000 \ | ((offs) & ~0x3) \ | ((devfn) << 8) \ | ((bus)->number << 16))static intmb_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 intmb_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 accesses. */ 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 __devinitpcibios_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){}voidpcibios_align_resource (void *data, struct resource *res, resource_size_t size, resource_size_t 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; unsigned long 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; unsigned long flags; void *end = mem + size;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -