📄 pseries_pci.c
字号:
/* * pSeries_pci.c * * Copyright (C) 2001 Dave Engebretsen, IBM Corporation * * pSeries specific routines for PCI. * * Based on code from pci.c and chrp_pci.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <linux/kernel.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/init.h>#include <linux/bootmem.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/irq.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/init.h>#include <asm/pci-bridge.h>#include <asm/ppcdebug.h>#include <asm/naca.h>#include <asm/pci_dma.h>#include "xics.h"#include "open_pic.h"#include "pci.h"extern struct device_node *allnodes;/******************************************************************* * Forward declares of prototypes. *******************************************************************/unsigned long find_and_init_phbs(void);struct pci_controller* alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) ;void pSeries_pcibios_fixup(void);static int rtas_fake_read(struct device_node *dn, int offset, int nbytes, unsigned long *returnval);/* RTAS tokens */static int read_pci_config;static int write_pci_config;static int ibm_read_pci_config;static int ibm_write_pci_config;static int s7a_workaround;/****************************************************************************** * * pSeries I/O Operations to access the PCI configuration space. * *****************************************************************************/#define RTAS_PCI_READ_OP(size, type, nbytes) \int __chrp \rtas_read_config_##size(struct device_node *dn, int offset, type val) { \ unsigned long returnval = ~0L; \ unsigned long buid; \ unsigned int addr; \ int ret; \ \ if (dn == NULL) { \ ret = -2; \ } else if (dn->status) { \ ret = -1; \ } else { \ addr = (dn->busno << 16) | (dn->devfn << 8) | offset; \ buid = dn->phb->buid; \ if (buid) { \ ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval, addr, buid >> 32, buid & 0xffffffff, nbytes); \ if (ret < 0 || (returnval == 0xffffffff)) \ ret = rtas_fake_read(dn, offset, nbytes, &returnval); \ } else { \ ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, nbytes); \ } \ } \ *val = returnval; \ return ret; \} \int __chrp \rtas_pci_read_config_##size(struct pci_dev *dev, int offset, type val) { \ struct device_node *dn = pci_device_to_OF_node(dev); \ int ret = rtas_read_config_##size(dn, offset, val); \ /* udbg_printf("read bus=%x, devfn=%x, ret=%d phb=%lx, dn=%lx\n", dev->bus->number, dev->devfn, ret, dn ? dn->phb : 0, dn); */ \ return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; \}#define RTAS_PCI_WRITE_OP(size, type, nbytes) \int __chrp \rtas_write_config_##size(struct device_node *dn, int offset, type val) { \ unsigned long buid; \ unsigned int addr; \ int ret; \ \ if (dn == NULL) { \ ret = -2; \ } else if (dn->status) { \ ret = -1; \ } else { \ buid = dn->phb->buid; \ addr = (dn->busno << 16) | (dn->devfn << 8) | offset; \ if (buid) { \ ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, nbytes, (ulong) val); \ } else { \ ret = rtas_call(write_pci_config, 3, 1, NULL, addr, nbytes, (ulong)val); \ } \ } \ return ret; \} \int __chrp \rtas_pci_write_config_##size(struct pci_dev *dev, int offset, type val) { \ struct device_node* dn = pci_device_to_OF_node(dev); \ int ret = rtas_write_config_##size(dn, offset, val); \ /* udbg_printf("write bus=%x, devfn=%x, ret=%d phb=%lx, dn=%lx\n", dev->bus->number, dev->devfn, ret, dn ? dn->phb : 0, dn); */ \ return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; \}RTAS_PCI_READ_OP(byte, u8 *, 1)RTAS_PCI_READ_OP(word, u16 *, 2)RTAS_PCI_READ_OP(dword, u32 *, 4)RTAS_PCI_WRITE_OP(byte, u8, 1)RTAS_PCI_WRITE_OP(word, u16, 2)RTAS_PCI_WRITE_OP(dword, u32, 4)struct pci_ops rtas_pci_ops = { rtas_pci_read_config_byte, rtas_pci_read_config_word, rtas_pci_read_config_dword, rtas_pci_write_config_byte, rtas_pci_write_config_word, rtas_pci_write_config_dword,};/* * Handle the case where rtas refuses to do a pci config read. * This currently only happens with some PHBs in which case we totally fake * out the values (and call it a speedwagaon -- something we could look up * in the device tree). */static intrtas_fake_read(struct device_node *dn, int offset, int nbytes, unsigned long *returnval){ char *device_type = (char *)get_property(dn, "device_type", 0); u32 *class_code = (u32 *)get_property(dn, "class-code", 0); *returnval = ~0; /* float by default */ /* udbg_printf("rtas_fake_read dn=%p, offset=0x%02x, nbytes=%d, device_type=%s\n", dn, offset, nbytes, device_type ? device_type : "<none>"); */ if (device_type && strcmp(device_type, "pci") != 0) return -3; /* Not a phb or bridge */ /* NOTE: class_code != NULL => EADS pci bridge. Else a PHB */ if (nbytes == 1) { if (offset == PCI_HEADER_TYPE) *returnval = 0x80; /* multifunction */ else if (offset == PCI_INTERRUPT_PIN || offset == PCI_INTERRUPT_LINE) *returnval = 0; } else if (nbytes == 2) { if (offset == PCI_SUBSYSTEM_VENDOR_ID || offset == PCI_SUBSYSTEM_ID) *returnval = 0; else if (offset == PCI_COMMAND) *returnval = PCI_COMMAND_PARITY|PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY; } else if (nbytes == 4) { if (offset == PCI_VENDOR_ID) *returnval = 0x1014 | ((class_code ? 0x8b : 0x102) << 16); /* a phb */ else if (offset == PCI_REVISION_ID) *returnval = (class_code ? PCI_CLASS_BRIDGE_PCI : PCI_CLASS_BRIDGE_HOST) << 16; /* revs are zero */ else if ((offset >= PCI_BASE_ADDRESS_0 && offset <= PCI_BASE_ADDRESS_5) || offset == PCI_ROM_ADDRESS) *returnval = 0; } /* printk("fake: %s nbytes=%d, offset=%lx ret=%lx\n", class_code ? "EADS" : "PHB", nbytes, offset, *returnval); */ return 0;}/****************************************************************** * pci_read_irq_line * * Reads the Interrupt Pin to determine if interrupt is use by card. * If the interrupt is used, then gets the interrupt line from the * openfirmware and sets it in the pci_dev and pci_config line. * ******************************************************************/int pci_read_irq_line(struct pci_dev *Pci_Dev){ u8 InterruptPin; struct device_node *Node; pci_read_config_byte(Pci_Dev, PCI_INTERRUPT_PIN, &InterruptPin); if (InterruptPin == 0) { PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Interrupt used by device.\n",Pci_Dev->slot_name); return 0; } Node = pci_device_to_OF_node(Pci_Dev); if ( Node == NULL) { PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s Device Node not found.\n",Pci_Dev->slot_name); return -1; } if (Node->n_intrs == 0) { PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Device OF interrupts defined.\n",Pci_Dev->slot_name); return -1; } Pci_Dev->irq = Node->intrs[0].line; if (s7a_workaround) { if (Pci_Dev->irq > 16) Pci_Dev->irq -= 3; } pci_write_config_byte(Pci_Dev, PCI_INTERRUPT_LINE, Pci_Dev->irq); PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s pci_dev->irq = 0x%02X\n",Pci_Dev->slot_name,Pci_Dev->irq); return 0;}/****************************************************************** * Find all PHBs in the system and initialize a set of data * structures to represent them. ******************************************************************/unsigned long __initfind_and_init_phbs(void){ struct device_node *Pci_Node; struct pci_controller *phb; unsigned int root_addr_size_words = 0, this_addr_size_words = 0; unsigned int this_addr_count = 0, range_stride; unsigned int *ui_ptr = NULL, *ranges; char *model; struct pci_range64 range; struct resource *res; unsigned int memno, rlen, i, index; unsigned int *opprop; int has_isa = 0; PPCDBG(PPCDBG_PHBINIT, "find_and_init_phbs\n"); read_pci_config = rtas_token("read-pci-config"); write_pci_config = rtas_token("write-pci-config"); ibm_read_pci_config = rtas_token("ibm,read-pci-config"); ibm_write_pci_config = rtas_token("ibm,write-pci-config"); if (naca->interrupt_controller == IC_OPEN_PIC) { opprop = (unsigned int *)get_property(find_path_device("/"), "platform-open-pic", NULL); } /* Get the root address word size. */ ui_ptr = (unsigned int *) get_property(find_path_device("/"), "#size-cells", NULL); if (ui_ptr) { root_addr_size_words = *ui_ptr; } else { PPCDBG(PPCDBG_PHBINIT, "\tget #size-cells failed.\n"); return(-1); } if (find_type_devices("isa")) { has_isa = 1; PPCDBG(PPCDBG_PHBINIT, "\tFound an ISA bus.\n"); } index = 0; /****************************************************************** * Find all PHB devices and create an object for them. ******************************************************************/ for (Pci_Node = find_devices("pci"); Pci_Node != NULL; Pci_Node = Pci_Node->next) { model = (char *) get_property(Pci_Node, "model", NULL); if (model != NULL) { phb = alloc_phb(Pci_Node, model, root_addr_size_words); if (phb == NULL) return(-1); } else { continue; } /* Get this node's address word size. */ ui_ptr = (unsigned int *) get_property(Pci_Node, "#size-cells", NULL); if (ui_ptr) this_addr_size_words = *ui_ptr; else this_addr_size_words = 1; /* Get this node's address word count. */ ui_ptr = (unsigned int *) get_property(Pci_Node, "#address-cells", NULL); if (ui_ptr) this_addr_count = *ui_ptr; else this_addr_count = 3; range_stride = this_addr_count + root_addr_size_words + this_addr_size_words; memno = 0; phb->io_base_phys = 0; ranges = (unsigned int *) get_property(Pci_Node, "ranges", &rlen); PPCDBG(PPCDBG_PHBINIT, "\trange_stride = 0x%lx, rlen = 0x%x\n", range_stride, rlen); for (i = 0; i < (rlen/sizeof(*ranges)); i+=range_stride) { /* Put the PCI addr part of the current element into a * '64' struct. */ range = *((struct pci_range64 *)(ranges + i)); /* If this is a '32' element, map into a 64 struct. */ if ((range_stride * sizeof(int)) == sizeof(struct pci_range32)) { range.parent_addr = (unsigned long)(*(ranges + i + 3)); range.size = (((unsigned long)(*(ranges + i + 4)))<<32) | (*(ranges + i + 5)); } else { range.parent_addr = (((unsigned long)(*(ranges + i + 3)))<<32) | (*(ranges + i + 4)); range.size = (((unsigned long)(*(ranges + i + 5)))<<32) | (*(ranges + i + 6)); } PPCDBG(PPCDBG_PHBINIT, "\trange.parent_addr = 0x%lx\n", range.parent_addr); PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.hi = 0x%lx\n", range.child_addr.a_hi); PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.mid = 0x%lx\n", range.child_addr.a_mid); PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.lo = 0x%lx\n", range.child_addr.a_lo); PPCDBG(PPCDBG_PHBINIT, "\trange.size = 0x%lx\n", range.size); res = NULL; switch ((range.child_addr.a_hi >> 24) & 0x3) { case 1: /* I/O space */ PPCDBG(PPCDBG_PHBINIT, "\tIO Space\n"); phb->io_base_phys = range.parent_addr; res = &phb->io_resource; res->name = Pci_Node->full_name; res->flags = IORESOURCE_IO; phb->io_base_virt = __ioremap(phb->io_base_phys, range.size, _PAGE_NO_CACHE); if (!pci_io_base) { pci_io_base = (unsigned long)phb->io_base_virt; if (has_isa) isa_io_base = pci_io_base; } res->start = ((((unsigned long) range.child_addr.a_mid) << 32) | (range.child_addr.a_lo)); res->start += (unsigned long)phb->io_base_virt - pci_io_base; res->end = res->start + range.size - 1; res->parent = NULL; res->sibling = NULL; res->child = NULL; phb->pci_io_offset = range.parent_addr - ((((unsigned long) range.child_addr.a_mid) << 32) | (range.child_addr.a_lo)); PPCDBG(PPCDBG_PHBINIT, "\tpci_io_offset = 0x%lx\n", phb->pci_io_offset); break; case 2: /* mem space */ PPCDBG(PPCDBG_PHBINIT, "\tMem Space\n"); phb->pci_mem_offset = range.parent_addr - ((((unsigned long) range.child_addr.a_mid) << 32) | (range.child_addr.a_lo)); PPCDBG(PPCDBG_PHBINIT, "\tpci_mem_offset = 0x%lx\n", phb->pci_mem_offset); if (memno < sizeof(phb->mem_resources)/sizeof(phb->mem_resources[0])) { res = &(phb->mem_resources[memno]); ++memno; res->name = Pci_Node->full_name; res->flags = IORESOURCE_MEM; res->start = range.parent_addr; res->end = range.parent_addr + range.size - 1; res->parent = NULL; res->sibling = NULL; res->child = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -