diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries')
-rw-r--r-- | arch/powerpc/platforms/pseries/Kconfig | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/cmm.c | 29 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh.c | 44 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-cpu.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/pci_dlpar.c | 163 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/phyp_dump.c | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/xics.c | 43 |
7 files changed, 174 insertions, 114 deletions
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 97619fd51e3..ddc2a307cd5 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -54,7 +54,7 @@ config PPC_SMLPAR config CMM tristate "Collaborative memory management" - depends on PPC_SMLPAR + depends on PPC_SMLPAR && !CRASH_DUMP default y help Select this option, if you want to enable the kernel interface diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index 5cd4d276162..6567439fe78 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -28,6 +28,7 @@ #include <linux/kthread.h> #include <linux/module.h> #include <linux/oom.h> +#include <linux/reboot.h> #include <linux/sched.h> #include <linux/stringify.h> #include <linux/swap.h> @@ -384,6 +385,26 @@ static void cmm_unregister_sysfs(struct sys_device *sysdev) } /** + * cmm_reboot_notifier - Make sure pages are not still marked as "loaned" + * + **/ +static int cmm_reboot_notifier(struct notifier_block *nb, + unsigned long action, void *unused) +{ + if (action == SYS_RESTART) { + if (cmm_thread_ptr) + kthread_stop(cmm_thread_ptr); + cmm_thread_ptr = NULL; + cmm_free_pages(loaned_pages); + } + return NOTIFY_DONE; +} + +static struct notifier_block cmm_reboot_nb = { + .notifier_call = cmm_reboot_notifier, +}; + +/** * cmm_init - Module initialization * * Return value: @@ -399,9 +420,12 @@ static int cmm_init(void) if ((rc = register_oom_notifier(&cmm_oom_nb)) < 0) return rc; - if ((rc = cmm_sysfs_register(&cmm_sysdev))) + if ((rc = register_reboot_notifier(&cmm_reboot_nb))) goto out_oom_notifier; + if ((rc = cmm_sysfs_register(&cmm_sysdev))) + goto out_reboot_notifier; + if (cmm_disabled) return rc; @@ -415,6 +439,8 @@ static int cmm_init(void) out_unregister_sysfs: cmm_unregister_sysfs(&cmm_sysdev); +out_reboot_notifier: + unregister_reboot_notifier(&cmm_reboot_nb); out_oom_notifier: unregister_oom_notifier(&cmm_oom_nb); return rc; @@ -431,6 +457,7 @@ static void cmm_exit(void) if (cmm_thread_ptr) kthread_stop(cmm_thread_ptr); unregister_oom_notifier(&cmm_oom_nb); + unregister_reboot_notifier(&cmm_reboot_nb); cmm_free_pages(loaned_pages); cmm_unregister_sysfs(&cmm_sysdev); } diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 54816d75b57..989d6462c15 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -21,6 +21,8 @@ * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com> */ +#undef DEBUG + #include <linux/delay.h> #include <linux/init.h> #include <linux/list.h> @@ -488,10 +490,8 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) || pdn->eeh_mode & EEH_MODE_NOCHECK) { ignored_check++; -#ifdef DEBUG - printk ("EEH:ignored check (%x) for %s %s\n", - pdn->eeh_mode, pci_name (dev), dn->full_name); -#endif + pr_debug("EEH: Ignored check (%x) for %s %s\n", + pdn->eeh_mode, pci_name (dev), dn->full_name); return 0; } @@ -1014,10 +1014,9 @@ static void *early_enable_eeh(struct device_node *dn, void *data) eeh_subsystem_enabled = 1; pdn->eeh_mode |= EEH_MODE_SUPPORTED; -#ifdef DEBUG - printk(KERN_DEBUG "EEH: %s: eeh enabled, config=%x pe_config=%x\n", - dn->full_name, pdn->eeh_config_addr, pdn->eeh_pe_config_addr); -#endif + pr_debug("EEH: %s: eeh enabled, config=%x pe_config=%x\n", + dn->full_name, pdn->eeh_config_addr, + pdn->eeh_pe_config_addr); } else { /* This device doesn't support EEH, but it may have an @@ -1161,13 +1160,17 @@ static void eeh_add_device_late(struct pci_dev *dev) if (!dev || !eeh_subsystem_enabled) return; -#ifdef DEBUG - printk(KERN_DEBUG "EEH: adding device %s\n", pci_name(dev)); -#endif + pr_debug("EEH: Adding device %s\n", pci_name(dev)); - pci_dev_get (dev); dn = pci_device_to_OF_node(dev); pdn = PCI_DN(dn); + if (pdn->pcidev == dev) { + pr_debug("EEH: Already referenced !\n"); + return; + } + WARN_ON(pdn->pcidev); + + pci_dev_get (dev); pdn->pcidev = dev; pci_addr_cache_insert_device(dev); @@ -1206,17 +1209,18 @@ static void eeh_remove_device(struct pci_dev *dev) return; /* Unregister the device with the EEH/PCI address search system */ -#ifdef DEBUG - printk(KERN_DEBUG "EEH: remove device %s\n", pci_name(dev)); -#endif - pci_addr_cache_remove_device(dev); - eeh_sysfs_remove_device(dev); + pr_debug("EEH: Removing device %s\n", pci_name(dev)); dn = pci_device_to_OF_node(dev); - if (PCI_DN(dn)->pcidev) { - PCI_DN(dn)->pcidev = NULL; - pci_dev_put (dev); + if (PCI_DN(dn)->pcidev == NULL) { + pr_debug("EEH: Not referenced !\n"); + return; } + PCI_DN(dn)->pcidev = NULL; + pci_dev_put (dev); + + pci_addr_cache_remove_device(dev); + eeh_sysfs_remove_device(dev); } void eeh_remove_bus_device(struct pci_dev *dev) diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 1f032483c02..a20ead87153 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -116,7 +116,7 @@ static void pseries_cpu_die(unsigned int cpu) cpu_status = query_cpu_stopped(pcpu); if (cpu_status == 0 || cpu_status == -1) break; - msleep(200); + cpu_relax(); } if (cpu_status != 0) { printk("Querying DEAD? cpu %i (%i) shows %i\n", diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 7190493e9bd..5e1ed3d60ee 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -25,6 +25,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#undef DEBUG + #include <linux/pci.h> #include <asm/pci-bridge.h> #include <asm/ppc-pci.h> @@ -69,74 +71,25 @@ EXPORT_SYMBOL_GPL(pcibios_find_pci_bus); * Remove all of the PCI devices under this bus both from the * linux pci device tree, and from the powerpc EEH address cache. */ -void -pcibios_remove_pci_devices(struct pci_bus *bus) +void pcibios_remove_pci_devices(struct pci_bus *bus) { - struct pci_dev *dev, *tmp; + struct pci_dev *dev, *tmp; + struct pci_bus *child_bus; + + /* First go down child busses */ + list_for_each_entry(child_bus, &bus->children, node) + pcibios_remove_pci_devices(child_bus); + pr_debug("PCI: Removing devices on bus %04x:%02x\n", + pci_domain_nr(bus), bus->number); list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { + pr_debug(" * Removing %s...\n", pci_name(dev)); eeh_remove_bus_device(dev); - pci_remove_bus_device(dev); - } + pci_remove_bus_device(dev); + } } EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); -/* Must be called before pci_bus_add_devices */ -void -pcibios_fixup_new_pci_devices(struct pci_bus *bus) -{ - struct pci_dev *dev; - - list_for_each_entry(dev, &bus->devices, bus_list) { - /* Skip already-added devices */ - if (!dev->is_added) { - int i; - - /* Fill device archdata and setup iommu table */ - pcibios_setup_new_device(dev); - - pci_read_irq_line(dev); - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r = &dev->resource[i]; - - if (r->parent || !r->start || !r->flags) - continue; - pci_claim_resource(dev, i); - } - } - } -} -EXPORT_SYMBOL_GPL(pcibios_fixup_new_pci_devices); - -static int -pcibios_pci_config_bridge(struct pci_dev *dev) -{ - u8 sec_busno; - struct pci_bus *child_bus; - - /* Get busno of downstream bus */ - pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno); - - /* Add to children of PCI bridge dev->bus */ - child_bus = pci_add_new_bus(dev->bus, dev, sec_busno); - if (!child_bus) { - printk (KERN_ERR "%s: could not add second bus\n", __func__); - return -EIO; - } - sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number); - - pci_scan_child_bus(child_bus); - - /* Fixup new pci devices */ - pcibios_fixup_new_pci_devices(child_bus); - - /* Make the discovered devices available */ - pci_bus_add_devices(child_bus); - - eeh_add_device_tree_late(child_bus); - return 0; -} - /** * pcibios_add_pci_devices - adds new pci devices to bus * @@ -147,10 +100,9 @@ pcibios_pci_config_bridge(struct pci_dev *dev) * is how this routine differs from other, similar pcibios * routines.) */ -void -pcibios_add_pci_devices(struct pci_bus * bus) +void pcibios_add_pci_devices(struct pci_bus * bus) { - int slotno, num, mode; + int slotno, num, mode, pass, max; struct pci_dev *dev; struct device_node *dn = pci_bus_to_OF_node(bus); @@ -162,26 +114,23 @@ pcibios_add_pci_devices(struct pci_bus * bus) if (mode == PCI_PROBE_DEVTREE) { /* use ofdt-based probe */ - of_scan_bus(dn, bus); - if (!list_empty(&bus->devices)) { - pcibios_fixup_new_pci_devices(bus); - pci_bus_add_devices(bus); - eeh_add_device_tree_late(bus); - } + of_rescan_bus(dn, bus); } else if (mode == PCI_PROBE_NORMAL) { /* use legacy probe */ slotno = PCI_SLOT(PCI_DN(dn->child)->devfn); num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); - if (num) { - pcibios_fixup_new_pci_devices(bus); - pci_bus_add_devices(bus); - eeh_add_device_tree_late(bus); + if (!num) + return; + pcibios_setup_bus_devices(bus); + max = bus->secondary; + for (pass=0; pass < 2; pass++) + list_for_each_entry(dev, &bus->devices, bus_list) { + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || + dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) + max = pci_scan_bridge(bus, dev, max, pass); } - - list_for_each_entry(dev, &bus->devices, bus_list) - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) - pcibios_pci_config_bridge(dev); } + pcibios_finish_adding_to_bus(bus); } EXPORT_SYMBOL_GPL(pcibios_add_pci_devices); @@ -190,6 +139,8 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) struct pci_controller *phb; int primary; + pr_debug("PCI: Initializing new hotplug PHB %s\n", dn->full_name); + primary = list_empty(&hose_list); phb = pcibios_alloc_controller(dn); if (!phb) @@ -203,11 +154,59 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) eeh_add_device_tree_early(dn); scan_phb(phb); - pcibios_allocate_bus_resources(phb->bus); - pcibios_fixup_new_pci_devices(phb->bus); - pci_bus_add_devices(phb->bus); - eeh_add_device_tree_late(phb->bus); + pcibios_finish_adding_to_bus(phb->bus); return phb; } EXPORT_SYMBOL_GPL(init_phb_dynamic); + +/* RPA-specific bits for removing PHBs */ +int remove_phb_dynamic(struct pci_controller *phb) +{ + struct pci_bus *b = phb->bus; + struct resource *res; + int rc, i; + + pr_debug("PCI: Removing PHB %04x:%02x... \n", + pci_domain_nr(b), b->number); + + /* We cannot to remove a root bus that has children */ + if (!(list_empty(&b->children) && list_empty(&b->devices))) + return -EBUSY; + + /* We -know- there aren't any child devices anymore at this stage + * and thus, we can safely unmap the IO space as it's not in use + */ + res = &phb->io_resource; + if (res->flags & IORESOURCE_IO) { + rc = pcibios_unmap_io_space(b); + if (rc) { + printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", + __func__, b->name); + return 1; + } + } + + /* Unregister the bridge device from sysfs and remove the PCI bus */ + device_unregister(b->bridge); + phb->bus = NULL; + pci_remove_bus(b); + + /* Now release the IO resource */ + if (res->flags & IORESOURCE_IO) + release_resource(res); + + /* Release memory resources */ + for (i = 0; i < 3; ++i) { + res = &phb->mem_resources[i]; + if (!(res->flags & IORESOURCE_MEM)) + continue; + release_resource(res); + } + + /* Free pci_controller data structure */ + pcibios_free_controller(phb); + + return 0; +} +EXPORT_SYMBOL_GPL(remove_phb_dynamic); diff --git a/arch/powerpc/platforms/pseries/phyp_dump.c b/arch/powerpc/platforms/pseries/phyp_dump.c index edbc012c2eb..6cf35cd8d0b 100644 --- a/arch/powerpc/platforms/pseries/phyp_dump.c +++ b/arch/powerpc/platforms/pseries/phyp_dump.c @@ -130,6 +130,9 @@ static unsigned long init_dump_header(struct phyp_dump_header *ph) static void print_dump_header(const struct phyp_dump_header *ph) { #ifdef DEBUG + if (ph == NULL) + return; + printk(KERN_INFO "dump header:\n"); /* setup some ph->sections required */ printk(KERN_INFO "version = %d\n", ph->version); @@ -411,6 +414,8 @@ static int __init phyp_dump_setup(void) of_node_put(rtas); } + ibm_configure_kernel_dump = rtas_token("ibm,configure-kernel-dump"); + print_dump_header(dump_header); dump_area_length = init_dump_header(&phdr); /* align down */ diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 424b335a71c..84e058f1e1c 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -579,7 +579,7 @@ static void xics_update_irq_servers(void) int i, j; struct device_node *np; u32 ilen; - const u32 *ireg, *isize; + const u32 *ireg; u32 hcpuid; /* Find the server numbers for the boot cpu. */ @@ -607,11 +607,6 @@ static void xics_update_irq_servers(void) } } - /* get the bit size of server numbers */ - isize = of_get_property(np, "ibm,interrupt-server#-size", NULL); - if (isize) - interrupt_server_size = *isize; - of_node_put(np); } @@ -682,6 +677,7 @@ void __init xics_init_IRQ(void) struct device_node *np; u32 indx = 0; int found = 0; + const u32 *isize; ppc64_boot_msg(0x20, "XICS Init"); @@ -701,6 +697,26 @@ void __init xics_init_IRQ(void) if (found == 0) return; + /* get the bit size of server numbers */ + found = 0; + + for_each_compatible_node(np, NULL, "ibm,ppc-xics") { + isize = of_get_property(np, "ibm,interrupt-server#-size", NULL); + + if (!isize) + continue; + + if (!found) { + interrupt_server_size = *isize; + found = 1; + } else if (*isize != interrupt_server_size) { + printk(KERN_WARNING "XICS: " + "mismatched ibm,interrupt-server#-size\n"); + interrupt_server_size = max(*isize, + interrupt_server_size); + } + } + xics_update_irq_servers(); xics_init_host(); @@ -728,9 +744,18 @@ static void xics_set_cpu_priority(unsigned char cppr) /* Have the calling processor join or leave the specified global queue */ static void xics_set_cpu_giq(unsigned int gserver, unsigned int join) { - int status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE, - (1UL << interrupt_server_size) - 1 - gserver, join); - WARN_ON(status < 0); + int index; + int status; + + if (!rtas_indicator_present(GLOBAL_INTERRUPT_QUEUE, NULL)) + return; + + index = (1UL << interrupt_server_size) - 1 - gserver; + + status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE, index, join); + + WARN(status < 0, "set-indicator(%d, %d, %u) returned %d\n", + GLOBAL_INTERRUPT_QUEUE, index, join, status); } void xics_setup_cpu(void) |