📄 rte_mb_a_pci.c
字号:
/* * arch/v850/kernel/mb_a_pci.c -- PCI support for Midas lab RTE-MOTHER-A board * * Copyright (C) 2001,2002 NEC Corporation * Copyright (C) 2001,2002 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(dev, offs) \ (0x80000000 \ | ((offs) & ~0x3) \ | ((dev)->devfn << 8) \ | ((dev)->bus->number << 16))#define DEF_MB_PCI_READ_CONFIG_OP(pci_type, c_type) \static int \mb_pci_read_config_ ## pci_type (struct pci_dev *dev, int offs, c_type *rval) \{ \ int flags; \ save_flags_cli (flags); \ \ MB_A_PCI_PCICR = 0x7; \ MB_A_PCI_DMCFGA = CONFIG_DMCFGA (dev, offs); \ \ *rval = *(volatile c_type *)(MB_A_PCI_IO_ADDR + (offs & 0x3)); \ \ if (MB_A_PCI_PCISR & 0x2000) { \ MB_A_PCI_PCISR = 0x2000; \ *rval = ~0; \ } \ \ MB_A_PCI_DMCFGA = 0; \ \ restore_flags (flags); \ \ return PCIBIOS_SUCCESSFUL; \}#define DEF_MB_PCI_WRITE_CONFIG_OP(pci_type, c_type) \static int \mb_pci_write_config_ ## pci_type (struct pci_dev *dev, int offs, c_type val) \{ \ int flags; \ save_flags_cli (flags); \ \ MB_A_PCI_PCICR = 0x7; \ MB_A_PCI_DMCFGA = CONFIG_DMCFGA (dev, offs); \ \ *(volatile c_type *)(MB_A_PCI_IO_ADDR + (offs & 0x3)) = val; \ \ if (MB_A_PCI_PCISR & 0x2000) \ MB_A_PCI_PCISR = 0x2000; \ \ MB_A_PCI_DMCFGA = 0; \ \ restore_flags (flags); \ \ return PCIBIOS_SUCCESSFUL; \}DEF_MB_PCI_READ_CONFIG_OP (byte, u8);DEF_MB_PCI_WRITE_CONFIG_OP (byte, u8);DEF_MB_PCI_READ_CONFIG_OP (word, u16);DEF_MB_PCI_WRITE_CONFIG_OP (word, u16);DEF_MB_PCI_READ_CONFIG_OP (dword, u32);DEF_MB_PCI_WRITE_CONFIG_OP (dword, u32);static struct pci_ops mb_pci_config_ops = { mb_pci_read_config_byte, mb_pci_read_config_word, mb_pci_read_config_dword, mb_pci_write_config_byte, mb_pci_write_config_word, mb_pci_write_config_dword};/* PCI Initialization. */static struct pci_bus *mb_pci_bus = 0;/* Do initial PCI setup. */void __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_DMLBAM = 0x0; 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. Note that we have to give the address from the motherboard's point of view, which is different than the CPU's. */ MB_A_PCI_DMLBAI = MB_A_PCI_IO_ADDR - GCS5_ADDR; MB_A_PCI_DMRR = ~(MB_A_PCI_MEM_SIZE - 1); 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");}char __devinit *pcibios_setup (char *option){ /* Don't handle any options. */ return option;}int __nomods_init pcibios_enable_device(struct pci_dev *dev){ 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", dev->slot_name); 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", dev->slot_name, 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; struct resource *r; pci_for_each_dev (dev) { int 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 __nomods_initpcibios_update_resource (struct pci_dev *dev, struct resource *root, struct resource *r, int resource){ u32 new, check; int reg; if (r->flags & IORESOURCE_IO) new = (((r->start - MB_A_PCI_IO_ADDR) & PCI_BASE_ADDRESS_IO_MASK) | PCI_BASE_ADDRESS_SPACE_IO); else if (r->flags & IORESOURCE_MEM) new = (((r->start - MB_A_PCI_MEM_ADDR) & PCI_BASE_ADDRESS_MEM_MASK) | PCI_BASE_ADDRESS_MEM_TYPE_32 | ((r->flags & IORESOURCE_PREFETCH) ? PCI_BASE_ADDRESS_MEM_PREFETCH : 0) | PCI_BASE_ADDRESS_SPACE_MEMORY); else panic ("pcibios_update_resource: unknown resource type"); if (resource < 6) reg = PCI_BASE_ADDRESS_0 + 4*resource; else if (resource == PCI_ROM_RESOURCE) { r->flags |= PCI_ROM_ADDRESS_ENABLE; new |= PCI_ROM_ADDRESS_ENABLE; reg = dev->rom_base_reg; } else return; pci_write_config_dword(dev, reg, new); pci_read_config_dword(dev, reg, &check); if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { printk (KERN_ERR "PCI: Error while updating region " "%s/%d (%08x != %08x)\n", dev->slot_name, resource, new, check); }}/* Stubs for things we don't use. */struct pci_fixup pcibios_fixups[] = { { 0 } };/* 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, unsigned long size){}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 spinlock_t mb_sram_lock = SPIN_LOCK_UNLOCKED;/* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -