/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 int rtas_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 : ""); */ 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 __init find_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; } break; } } PPCDBG(PPCDBG_PHBINIT, "\tphb->io_base_phys = 0x%lx\n", phb->io_base_phys); PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_mem_offset = 0x%lx\n", phb->pci_mem_offset); if (naca->interrupt_controller == IC_OPEN_PIC) { int addr = root_addr_size_words * (index + 2) - 1; openpic_setup_ISU(index, opprop[addr]); } index++; } pci_devs_phb_init(); return 0; /*Success */ } /****************************************************************** * * Allocate and partially initialize a structure to represent a PHB. * ******************************************************************/ struct pci_controller * alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) { struct pci_controller *phb; unsigned int *ui_ptr = NULL, len; struct reg_property64 reg_struct; struct property *of_prop; int *bus_range; int *buid_vals; PPCDBG(PPCDBG_PHBINIT, "alloc_phb: %s\n", dev->full_name); PPCDBG(PPCDBG_PHBINIT, "\tdev = 0x%lx\n", dev); PPCDBG(PPCDBG_PHBINIT, "\tmodel = 0x%lx\n", model); PPCDBG(PPCDBG_PHBINIT, "\taddr_size_words = 0x%lx\n", addr_size_words); /* Found a PHB, now figure out where his registers are mapped. */ ui_ptr = (unsigned int *) get_property(dev, "reg", &len); if (ui_ptr == NULL) { PPCDBG(PPCDBG_PHBINIT, "\tget reg failed.\n"); return(NULL); } if (addr_size_words == 1) { reg_struct.address = ((struct reg_property32 *)ui_ptr)->address; reg_struct.size = ((struct reg_property32 *)ui_ptr)->size; } else { reg_struct = *((struct reg_property64 *)ui_ptr); } PPCDBG(PPCDBG_PHBINIT, "\treg_struct.address = 0x%lx\n", reg_struct.address); PPCDBG(PPCDBG_PHBINIT, "\treg_struct.size = 0x%lx\n", reg_struct.size); /*************************************************************** * Set chip specific data in the phb, including types & * register pointers. ***************************************************************/ /**************************************************************** * Python ***************************************************************/ if (strstr(model, "Python")) { PPCDBG(PPCDBG_PHBINIT, "\tCreate python\n"); phb = pci_alloc_pci_controller("PHB PY",phb_type_python); if (phb == NULL) return NULL; phb->cfg_addr = (volatile unsigned long *) ioremap(reg_struct.address + 0xf8000, PAGE_SIZE); PPCDBG(PPCDBG_PHBINIT, "\tcfg_addr_r = 0x%lx\n", reg_struct.address + 0xf8000); PPCDBG(PPCDBG_PHBINIT, "\tcfg_addr_v = 0x%lx\n", phb->cfg_addr); phb->cfg_data = (char*)(phb->cfg_addr + 0x02); phb->phb_regs = (volatile unsigned long *) ioremap(reg_struct.address + 0xf7000, PAGE_SIZE); /* Python's register file is 1 MB in size. */ phb->chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000); /* * Firmware doesn't always clear this bit which is critical * for good performance - Anton */ { volatile u32 *tmp, i; #define PRG_CL_RESET_VALID 0x00010000 tmp = (u32 *)((unsigned long)phb->chip_regs + 0xf6030); if (*tmp & PRG_CL_RESET_VALID) { printk("Python workaround: "); *tmp &= ~PRG_CL_RESET_VALID; /* * We must read it back for changes to * take effect */ i = *tmp; printk("reg0: %x\n", i); } } /*************************************************************** * Speedwagon * include Winnipeg as well for the time being. ***************************************************************/ } else if ((strstr(model, "Speedwagon")) || (strstr(model, "Winnipeg"))) { PPCDBG(PPCDBG_PHBINIT, "\tCreate speedwagon\n"); phb = pci_alloc_pci_controller("PHB SW",phb_type_speedwagon); if (phb == NULL) return NULL; if (systemcfg->platform == PLATFORM_PSERIES) { phb->cfg_addr = (volatile unsigned long *) ioremap(reg_struct.address + 0x140, PAGE_SIZE); phb->cfg_data = (char*)(phb->cfg_addr - 0x02); /* minus is correct */ phb->phb_regs = (volatile unsigned long *) ioremap(reg_struct.address, PAGE_SIZE); /* Speedwagon's register file is 1 MB in size. */ phb->chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000); PPCDBG(PPCDBG_PHBINIT, "\tmapping chip_regs from 0x%lx -> 0x%lx\n", reg_struct.address & 0xfffff, phb->chip_regs); } else { phb->cfg_addr = NULL; phb->cfg_data = NULL; phb->phb_regs = NULL; phb->chip_regs = NULL; } phb->local_number = ((reg_struct.address >> 12) & 0xf) - 0x8; } else { PPCDBG(PPCDBG_PHBINIT, "\tUnknown PHB Type!\n"); if (systemcfg->platform == PLATFORM_PSERIES_LPAR) { phb=pci_alloc_pci_controller("PHB UK",phb_type_unknown); if (phb == NULL) return NULL; phb->cfg_addr = NULL; phb->cfg_data = NULL; phb->phb_regs = NULL; phb->chip_regs = NULL; } else { printk("PCI: Unknown Phb Type!\n"); return NULL; } } /* Add a linux,phbnum property to the device tree so user code * can translate bus numbers. */ of_prop = (struct property *) alloc_bootmem(sizeof(struct property) + sizeof(phb->global_number)); if (of_prop) { memset(of_prop, 0, sizeof(struct property)); of_prop->name = "linux,phbnum"; of_prop->length = sizeof(phb->global_number); of_prop->value = (unsigned char *)&of_prop[1]; memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number)); prom_add_property(dev, of_prop); } bus_range = (int *) get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { PPCDBG(PPCDBG_PHBINIT, "Can't get bus-range for %s\n", dev->full_name); kfree(phb); return(NULL); } /*************************************************************** * Finished with the initialization ***************************************************************/ phb->first_busno = bus_range[0]; phb->last_busno = bus_range[1]; phb->arch_data = dev; phb->ops = &rtas_pci_ops; buid_vals = (int *) get_property(dev, "ibm,fw-phb-id", &len); if (buid_vals == NULL) { phb->buid = 0; } else { struct pci_bus check; if (sizeof(check.number) == 1 || sizeof(check.primary) == 1 || sizeof(check.secondary) == 1 || sizeof(check.subordinate) == 1) { udbg_printf("pSeries_pci: this system has large bus numbers and the kernel was not\n" "built with the patch that fixes include/linux/pci.h struct pci_bus so\n" "number, primary, secondary and subordinate are ints.\n"); panic("pSeries_pci: this system has large bus numbers and the kernel was not\n" "built with the patch that fixes include/linux/pci.h struct pci_bus so\n" "number, primary, secondary and subordinate are ints.\n"); } if (len < 2 * sizeof(int)) phb->buid = (unsigned long)buid_vals[0]; // Support for new OF that only has 1 integer for buid. else phb->buid = (((unsigned long)buid_vals[0]) << 32UL) | (((unsigned long)buid_vals[1]) & 0xffffffff); phb->first_busno += (phb->global_number << 8); phb->last_busno += (phb->global_number << 8); } /* Dump PHB information for Debug */ PPCDBGCALL(PPCDBG_PHBINIT,dumpPci_Controller(phb) ); return phb; } void fixup_resources(struct pci_dev *dev) { int i; struct pci_controller *phb = PCI_GET_PHB_PTR(dev); struct device_node *dn; /* Add IBM loc code (slot) as a prefix to the device names for service */ dn = pci_device_to_OF_node(dev); if (dn) { char *loc_code = get_property(dn, "ibm,loc-code", 0); if (loc_code) { int loc_len = strlen(loc_code); if (loc_len < sizeof(dev->name)) { memmove(dev->name+loc_len+1, dev->name, sizeof(dev->name)-loc_len-1); memcpy(dev->name, loc_code, loc_len); dev->name[loc_len] = ' '; dev->name[sizeof(dev->name)-1] = '\0'; } } } PPCDBG(PPCDBG_PHBINIT, "fixup_resources:\n"); PPCDBG(PPCDBG_PHBINIT, "\tphb = 0x%016LX\n", phb); PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_io_offset = 0x%016LX\n", phb->pci_io_offset); PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_mem_offset = 0x%016LX\n", phb->pci_mem_offset); PPCDBG(PPCDBG_PHBINIT, "\tdev->name = %s\n", dev->name); PPCDBG(PPCDBG_PHBINIT, "\tdev->vendor:device = 0x%04X : 0x%04X\n", dev->vendor, dev->device); if (phb == NULL) return; for (i = 0; i < DEVICE_COUNT_RESOURCE; ++i) { PPCDBG(PPCDBG_PHBINIT, "\tdevice %x.%x[%d] (flags %x) [%lx..%lx]\n", dev->bus->number, dev->devfn, i, dev->resource[i].flags, dev->resource[i].start, dev->resource[i].end); if ((dev->resource[i].start == 0) && (dev->resource[i].end == 0)) { continue; } if (dev->resource[i].start > dev->resource[i].end) { /* Bogus resource. Just clear it out. */ dev->resource[i].start = dev->resource[i].end = 0; continue; } if (dev->resource[i].flags & IORESOURCE_IO) { unsigned long offset = (unsigned long)phb->io_base_virt - pci_io_base; dev->resource[i].start += offset; dev->resource[i].end += offset; PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx .. %lx]\n", dev->resource[i].start, dev->resource[i].end); } else if (dev->resource[i].flags & IORESOURCE_MEM) { if (dev->resource[i].start == 0) { /* Bogus. Probably an unused bridge. */ dev->resource[i].end = 0; } else { dev->resource[i].start += phb->pci_mem_offset; dev->resource[i].end += phb->pci_mem_offset; } PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx..%lx]\n", dev->resource[i].start, dev->resource[i].end); } else { continue; } /* zap the 2nd function of the winbond chip */ if (dev->resource[i].flags & IORESOURCE_IO && dev->bus->number == 0 && dev->devfn == 0x81) dev->resource[i].flags &= ~IORESOURCE_IO; } } static void check_s7a(void) { struct device_node *root; char *model; root = find_path_device("/"); if (root) { model = get_property(root, "model", NULL); if (model && !strcmp(model, "IBM,7013-S7A")) s7a_workaround = 1; } } void __init pSeries_pcibios_fixup(void) { struct pci_dev *dev; PPCDBG(PPCDBG_PHBINIT, "pSeries_pcibios_fixup: start\n"); pci_assign_all_busses = 0; check_s7a(); pci_for_each_dev(dev) { pci_read_irq_line(dev); PPCDBGCALL(PPCDBG_PHBINIT, dumpPci_Dev(dev) ); } } /*********************************************************************** * pci_find_hose_for_OF_device * * This function finds the PHB that matching device_node in the * OpenFirmware by scanning all the pci_controllers. * ***********************************************************************/ struct pci_controller* pci_find_hose_for_OF_device(struct device_node *node) { while (node) { struct pci_controller *hose; for (hose=hose_head;hose;hose=hose->next) if (hose->arch_data == node) return hose; node=node->parent; } return NULL; } /*********************************************************************** * ppc64_pcibios_init * * Chance to initialize and structures or variable before PCI Bus walk. * ***********************************************************************/ void pSeries_pcibios_init(void) { PPCDBG(PPCDBG_PHBINIT, "\tppc64_pcibios_init Entry.\n"); if (get_property(find_path_device("/rtas"),"ibm,fw-phb-id",NULL) != NULL) { PPCDBG(PPCDBG_PHBINIT, "\tFound: ibm,fw-phb-id\n"); Pci_Large_Bus_System = 1; } } /* * This is called very early before the page table is setup. */ void pSeries_pcibios_init_early(void) { ppc_md.pcibios_read_config_byte = rtas_read_config_byte; ppc_md.pcibios_read_config_word = rtas_read_config_word; ppc_md.pcibios_read_config_dword = rtas_read_config_dword; ppc_md.pcibios_write_config_byte = rtas_write_config_byte; ppc_md.pcibios_write_config_word = rtas_write_config_word; ppc_md.pcibios_write_config_dword = rtas_write_config_dword; } /************************************************************************/ /* Get a char* of the device physical location(U0.3-P1-I8) */ /* See the Product Topology in the RS/6000 Architecture. */ /************************************************************************/ int device_Location(struct pci_dev *PciDev, char *BufPtr) { struct device_node *DevNode = (struct device_node *)PciDev->sysdata; return sprintf(BufPtr,"PCI: Bus%3d, Device%3d, Vendor %04X, Location %-12s", PciDev->bus->number, PCI_SLOT(PciDev->devfn), PciDev->vendor, (char*)get_property(DevNode,"ibm,loc-code",0)); } /************************************************************************/ /* Set the slot reset line to the state passed in. */ /* This is the platform specific for code for the pci_reset_device */ /* function. */ /************************************************************************/ int pci_set_reset(struct pci_dev *PciDev, int state) { return -1; }