diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries')
-rw-r--r-- | arch/powerpc/platforms/pseries/Kconfig | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/Makefile | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh.c | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_cache.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/firmware.c | 10 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/iommu.c | 39 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/lpar.c | 61 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/pci_dlpar.c | 9 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/phyp_dump.c | 507 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/pseries.h | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/ras.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/reconfig.c | 12 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/rtasd.c | 14 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/scanlog.c | 60 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/setup.c | 122 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/smp.c | 11 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/xics.c | 90 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/xics.h | 3 |
18 files changed, 699 insertions, 262 deletions
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 306a9d07491..07fe5b69b9e 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -34,3 +34,8 @@ config LPARCFG help Provide system capacity information via human readable <key word>=<value> pairs through a /proc/ppc64/lparcfg interface. + +config PPC_PSERIES_DEBUG + depends on PPC_PSERIES && PPC_EARLY_DEBUG + bool "Enable extra debug logging in platforms/pseries" + default y diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 992ba6753cf..bd2593ed28d 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -2,6 +2,10 @@ ifeq ($(CONFIG_PPC64),y) EXTRA_CFLAGS += -mno-minimal-toc endif +ifeq ($(CONFIG_PPC_PSERIES_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + obj-y := lpar.o hvCall.o nvram.o reconfig.o \ setup.o iommu.o ras.o rtasd.o \ firmware.o power.o @@ -18,3 +22,4 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o obj-$(CONFIG_HVCS) += hvcserver.o obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o +obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 9eb539ee5f9..a3fd56b186e 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -39,7 +39,6 @@ #include <asm/ppc-pci.h> #include <asm/rtas.h> -#undef DEBUG /** Overview: * EEH, or "Extended Error Handling" is a PCI bridge technology for @@ -945,7 +944,6 @@ static void *early_enable_eeh(struct device_node *dn, void *data) unsigned int rets[3]; struct eeh_early_enable_info *info = data; int ret; - const char *status = of_get_property(dn, "status", NULL); const u32 *class_code = of_get_property(dn, "class-code", NULL); const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL); const u32 *device_id = of_get_property(dn, "device-id", NULL); @@ -959,8 +957,8 @@ static void *early_enable_eeh(struct device_node *dn, void *data) pdn->eeh_freeze_count = 0; pdn->eeh_false_positives = 0; - if (status && strncmp(status, "ok", 2) != 0) - return NULL; /* ignore devices with bad status */ + if (!of_device_is_available(dn)) + return NULL; /* Ignore bad nodes. */ if (!class_code || !vendor_id || !device_id) diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c index 1e83fcd0df3..ce37040af87 100644 --- a/arch/powerpc/platforms/pseries/eeh_cache.c +++ b/arch/powerpc/platforms/pseries/eeh_cache.c @@ -28,7 +28,6 @@ #include <asm/pci-bridge.h> #include <asm/ppc-pci.h> -#undef DEBUG /** * The pci address cache subsystem. This subsystem places diff --git a/arch/powerpc/platforms/pseries/firmware.c b/arch/powerpc/platforms/pseries/firmware.c index b765b7c77b6..9d3a40f4597 100644 --- a/arch/powerpc/platforms/pseries/firmware.c +++ b/arch/powerpc/platforms/pseries/firmware.c @@ -21,17 +21,11 @@ * 2 of the License, or (at your option) any later version. */ -#undef DEBUG #include <asm/firmware.h> #include <asm/prom.h> #include <asm/udbg.h> -#ifdef DEBUG -#define DBG(fmt...) udbg_printf(fmt) -#else -#define DBG(fmt...) -#endif typedef struct { unsigned long val; @@ -72,7 +66,7 @@ void __init fw_feature_init(const char *hypertas, unsigned long len) const char *s; int i; - DBG(" -> fw_feature_init()\n"); + pr_debug(" -> fw_feature_init()\n"); for (s = hypertas; s < hypertas + len; s += strlen(s) + 1) { for (i = 0; i < FIRMWARE_MAX_FEATURES; i++) { @@ -88,5 +82,5 @@ void __init fw_feature_init(const char *hypertas, unsigned long len) } } - DBG(" <- fw_feature_init()\n"); + pr_debug(" <- fw_feature_init()\n"); } diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index a65c7630820..176f1f39d2d 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -47,7 +47,6 @@ #include "plpar_wrappers.h" -#define DBG(fmt...) static void tce_build_pSeries(struct iommu_table *tbl, long index, long npages, unsigned long uaddr, @@ -322,7 +321,7 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus) dn = pci_bus_to_OF_node(bus); - DBG("pci_dma_bus_setup_pSeries: setting up bus %s\n", dn->full_name); + pr_debug("pci_dma_bus_setup_pSeries: setting up bus %s\n", dn->full_name); if (bus->self) { /* This is not a root bus, any setup will be done for the @@ -347,7 +346,7 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus) for (children = 0, tmp = dn->child; tmp; tmp = tmp->sibling) children++; - DBG("Children: %d\n", children); + pr_debug("Children: %d\n", children); /* Calculate amount of DMA window per slot. Each window must be * a power of two (due to pci_alloc_consistent requirements). @@ -361,8 +360,8 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus) while (pci->phb->dma_window_size * children > 0x80000000ul) pci->phb->dma_window_size >>= 1; - DBG("No ISA/IDE, window size is 0x%lx\n", - pci->phb->dma_window_size); + pr_debug("No ISA/IDE, window size is 0x%lx\n", + pci->phb->dma_window_size); pci->phb->dma_window_base_cur = 0; return; @@ -387,8 +386,7 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus) while (pci->phb->dma_window_size * children > 0x70000000ul) pci->phb->dma_window_size >>= 1; - DBG("ISA/IDE, window size is 0x%lx\n", pci->phb->dma_window_size); - + pr_debug("ISA/IDE, window size is 0x%lx\n", pci->phb->dma_window_size); } @@ -401,7 +399,8 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus) dn = pci_bus_to_OF_node(bus); - DBG("pci_dma_bus_setup_pSeriesLP: setting up bus %s\n", dn->full_name); + pr_debug("pci_dma_bus_setup_pSeriesLP: setting up bus %s\n", + dn->full_name); /* Find nearest ibm,dma-window, walking up the device tree */ for (pdn = dn; pdn != NULL; pdn = pdn->parent) { @@ -411,14 +410,14 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus) } if (dma_window == NULL) { - DBG(" no ibm,dma-window property !\n"); + pr_debug(" no ibm,dma-window property !\n"); return; } ppci = PCI_DN(pdn); - DBG(" parent is %s, iommu_table: 0x%p\n", - pdn->full_name, ppci->iommu_table); + pr_debug(" parent is %s, iommu_table: 0x%p\n", + pdn->full_name, ppci->iommu_table); if (!ppci->iommu_table) { tbl = kmalloc_node(sizeof(struct iommu_table), GFP_KERNEL, @@ -426,7 +425,7 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus) iommu_table_setparms_lpar(ppci->phb, pdn, tbl, dma_window, bus->number); ppci->iommu_table = iommu_init_table(tbl, ppci->phb->node); - DBG(" created table: %p\n", ppci->iommu_table); + pr_debug(" created table: %p\n", ppci->iommu_table); } if (pdn != dn) @@ -439,7 +438,7 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev) struct device_node *dn; struct iommu_table *tbl; - DBG("pci_dma_dev_setup_pSeries: %s\n", pci_name(dev)); + pr_debug("pci_dma_dev_setup_pSeries: %s\n", pci_name(dev)); dn = dev->dev.archdata.of_node; @@ -450,7 +449,7 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev) if (!dev->bus->self) { struct pci_controller *phb = PCI_DN(dn)->phb; - DBG(" --> first child, no bridge. Allocating iommu table.\n"); + pr_debug(" --> first child, no bridge. Allocating iommu table.\n"); tbl = kmalloc_node(sizeof(struct iommu_table), GFP_KERNEL, phb->node); iommu_table_setparms(phb, dn, tbl); @@ -480,7 +479,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) const void *dma_window = NULL; struct pci_dn *pci; - DBG("pci_dma_dev_setup_pSeriesLP: %s\n", pci_name(dev)); + pr_debug("pci_dma_dev_setup_pSeriesLP: %s\n", pci_name(dev)); /* dev setup for LPAR is a little tricky, since the device tree might * contain the dma-window properties per-device and not neccesarily @@ -489,7 +488,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) * already allocated. */ dn = pci_device_to_OF_node(dev); - DBG(" node is %s\n", dn->full_name); + pr_debug(" node is %s\n", dn->full_name); for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table; pdn = pdn->parent) { @@ -504,13 +503,13 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) pci_name(dev), dn? dn->full_name : "<null>"); return; } - DBG(" parent is %s\n", pdn->full_name); + pr_debug(" parent is %s\n", pdn->full_name); /* Check for parent == NULL so we don't try to setup the empty EADS * slots on POWER4 machines. */ if (dma_window == NULL || pdn->parent == NULL) { - DBG(" no dma window for device, linking to parent\n"); + pr_debug(" no dma window for device, linking to parent\n"); dev->dev.archdata.dma_data = PCI_DN(pdn)->iommu_table; return; } @@ -522,9 +521,9 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window, pci->phb->bus->number); pci->iommu_table = iommu_init_table(tbl, pci->phb->node); - DBG(" created table: %p\n", pci->iommu_table); + pr_debug(" created table: %p\n", pci->iommu_table); } else { - DBG(" found DMA window, table: %p\n", pci->iommu_table); + pr_debug(" found DMA window, table: %p\n", pci->iommu_table); } dev->dev.archdata.dma_data = pci->iommu_table; diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 9a455d46379..2cbaedb17f3 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -19,7 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#undef DEBUG_LOW +/* Enables debugging of low-level hash table routines - careful! */ +#undef DEBUG #include <linux/kernel.h> #include <linux/dma-mapping.h> @@ -40,12 +41,8 @@ #include <asm/smp.h> #include "plpar_wrappers.h" +#include "pseries.h" -#ifdef DEBUG_LOW -#define DBG_LOW(fmt...) do { udbg_printf(fmt); } while(0) -#else -#define DBG_LOW(fmt...) do { } while(0) -#endif /* in hvCall.S */ EXPORT_SYMBOL(plpar_hcall); @@ -195,6 +192,8 @@ void __init udbg_init_debug_lpar(void) udbg_putc = udbg_putcLP; udbg_getc = udbg_getcLP; udbg_getc_poll = udbg_getc_pollLP; + + register_early_udbg_console(); } /* returns 0 if couldn't find or use /chosen/stdout as console */ @@ -203,7 +202,6 @@ void __init find_udbg_vterm(void) struct device_node *stdout_node; const u32 *termno; const char *name; - int add_console; /* find the boot console from /chosen/stdout */ if (!of_chosen) @@ -219,8 +217,6 @@ void __init find_udbg_vterm(void) printk(KERN_WARNING "stdout node missing 'name' property!\n"); goto out; } - /* The user has requested a console so this is already set up. */ - add_console = !strstr(cmd_line, "console="); /* Check if it's a virtual terminal */ if (strncmp(name, "vty", 3) != 0) @@ -234,15 +230,13 @@ void __init find_udbg_vterm(void) udbg_putc = udbg_putcLP; udbg_getc = udbg_getcLP; udbg_getc_poll = udbg_getc_pollLP; - if (add_console) - add_preferred_console("hvc", termno[0] & 0xff, NULL); + add_preferred_console("hvc", termno[0] & 0xff, NULL); } else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { vtermno = termno[0]; udbg_putc = udbg_hvsi_putc; udbg_getc = udbg_hvsi_getc; udbg_getc_poll = udbg_hvsi_getc_poll; - if (add_console) - add_preferred_console("hvsi", termno[0] & 0xff, NULL); + add_preferred_console("hvsi", termno[0] & 0xff, NULL); } out: of_node_put(stdout_node); @@ -292,15 +286,15 @@ static long pSeries_lpar_hpte_insert(unsigned long hpte_group, unsigned long hpte_v, hpte_r; if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW("hpte_insert(group=%lx, va=%016lx, pa=%016lx, " - "rflags=%lx, vflags=%lx, psize=%d)\n", - hpte_group, va, pa, rflags, vflags, psize); + pr_debug("hpte_insert(group=%lx, va=%016lx, pa=%016lx, " + "rflags=%lx, vflags=%lx, psize=%d)\n", + hpte_group, va, pa, rflags, vflags, psize); hpte_v = hpte_encode_v(va, psize, ssize) | vflags | HPTE_V_VALID; hpte_r = hpte_encode_r(pa, psize) | rflags; if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r); + pr_debug(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r); /* Now fill in the actual HPTE */ /* Set CEC cookie to 0 */ @@ -317,7 +311,7 @@ static long pSeries_lpar_hpte_insert(unsigned long hpte_group, lpar_rc = plpar_pte_enter(flags, hpte_group, hpte_v, hpte_r, &slot); if (unlikely(lpar_rc == H_PTEG_FULL)) { if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" full\n"); + pr_debug(" full\n"); return -1; } @@ -328,11 +322,11 @@ static long pSeries_lpar_hpte_insert(unsigned long hpte_group, */ if (unlikely(lpar_rc != H_SUCCESS)) { if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" lpar err %d\n", lpar_rc); + pr_debug(" lpar err %lu\n", lpar_rc); return -2; } if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" -> slot: %d\n", slot & 7); + pr_debug(" -> slot: %lu\n", slot & 7); /* Because of iSeries, we have to pass down the secondary * bucket bit here as well @@ -424,17 +418,17 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot, want_v = hpte_encode_avpn(va, psize, ssize); - DBG_LOW(" update: avpnv=%016lx, hash=%016lx, f=%x, psize: %d ... ", - want_v, slot, flags, psize); + pr_debug(" update: avpnv=%016lx, hash=%016lx, f=%lx, psize: %d ...", + want_v, slot, flags, psize); lpar_rc = plpar_pte_protect(flags, slot, want_v); if (lpar_rc == H_NOT_FOUND) { - DBG_LOW("not found !\n"); + pr_debug("not found !\n"); return -1; } - DBG_LOW("ok\n"); + pr_debug("ok\n"); BUG_ON(lpar_rc != H_SUCCESS); @@ -509,8 +503,8 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, unsigned long lpar_rc; unsigned long dummy1, dummy2; - DBG_LOW(" inval : slot=%lx, va=%016lx, psize: %d, local: %d", - slot, va, psize, local); + pr_debug(" inval : slot=%lx, va=%016lx, psize: %d, local: %d\n", + slot, va, psize, local); want_v = hpte_encode_avpn(va, psize, ssize); lpar_rc = plpar_pte_remove(H_AVPN, slot, want_v, &dummy1, &dummy2); @@ -520,6 +514,20 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, BUG_ON(lpar_rc != H_SUCCESS); } +static void pSeries_lpar_hpte_removebolted(unsigned long ea, + int psize, int ssize) +{ + unsigned long slot, vsid, va; + + vsid = get_kernel_vsid(ea, ssize); + va = hpt_va(ea, vsid, ssize); + + slot = pSeries_lpar_hpte_find(va, psize, ssize); + BUG_ON(slot == -1); + + pSeries_lpar_hpte_invalidate(slot, va, psize, ssize, 0); +} + /* Flag bits for H_BULK_REMOVE */ #define HBR_REQUEST 0x4000000000000000UL #define HBR_RESPONSE 0x8000000000000000UL @@ -597,6 +605,7 @@ void __init hpte_init_lpar(void) ppc_md.hpte_updateboltedpp = pSeries_lpar_hpte_updateboltedpp; ppc_md.hpte_insert = pSeries_lpar_hpte_insert; ppc_md.hpte_remove = pSeries_lpar_hpte_remove; + ppc_md.hpte_removebolted = pSeries_lpar_hpte_removebolted; ppc_md.flush_hash_range = pSeries_lpar_flush_hash_range; ppc_md.hpte_clear_all = pSeries_lpar_hptab_clear; } diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 5a5a19e40bb..21a6d55418f 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -88,11 +88,8 @@ pcibios_fixup_new_pci_devices(struct pci_bus *bus) struct pci_dev *dev; list_for_each_entry(dev, &bus->devices, bus_list) { - /* - * Skip already-present devices (which are on the - * global device list.) - */ - if (list_empty(&dev->global_list)) { + /* Skip already-added devices */ + if (!dev->is_added) { int i; /* Fill device archdata and setup iommu table */ @@ -123,7 +120,7 @@ pcibios_pci_config_bridge(struct pci_dev *dev) /* 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", __FUNCTION__); + printk (KERN_ERR "%s: could not add second bus\n", __func__); return -EIO; } sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number); diff --git a/arch/powerpc/platforms/pseries/phyp_dump.c b/arch/powerpc/platforms/pseries/phyp_dump.c new file mode 100644 index 00000000000..edbc012c2eb --- /dev/null +++ b/arch/powerpc/platforms/pseries/phyp_dump.c @@ -0,0 +1,507 @@ +/* + * Hypervisor-assisted dump + * + * Linas Vepstas, Manish Ahuja 2008 + * Copyright 2008 IBM Corp. + * + * 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. + * + */ + +#include <linux/init.h> +#include <linux/kobject.h> +#include <linux/mm.h> +#include <linux/of.h> +#include <linux/pfn.h> +#include <linux/swap.h> +#include <linux/sysfs.h> + +#include <asm/page.h> +#include <asm/phyp_dump.h> +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/rtas.h> + +/* Variables, used to communicate data between early boot and late boot */ +static struct phyp_dump phyp_dump_vars; +struct phyp_dump *phyp_dump_info = &phyp_dump_vars; + +static int ibm_configure_kernel_dump; +/* ------------------------------------------------- */ +/* RTAS interfaces to declare the dump regions */ + +struct dump_section { + u32 dump_flags; + u16 source_type; + u16 error_flags; + u64 source_address; + u64 source_length; + u64 length_copied; + u64 destination_address; +}; + +struct phyp_dump_header { + u32 version; + u16 num_of_sections; + u16 status; + + u32 first_offset_section; + u32 dump_disk_section; + u64 block_num_dd; + u64 num_of_blocks_dd; + u32 offset_dd; + u32 maxtime_to_auto; + /* No dump disk path string used */ + + struct dump_section cpu_data; + struct dump_section hpte_data; + struct dump_section kernel_data; +}; + +/* The dump header *must be* in low memory, so .bss it */ +static struct phyp_dump_header phdr; + +#define NUM_DUMP_SECTIONS 3 +#define DUMP_HEADER_VERSION 0x1 +#define DUMP_REQUEST_FLAG 0x1 +#define DUMP_SOURCE_CPU 0x0001 +#define DUMP_SOURCE_HPTE 0x0002 +#define DUMP_SOURCE_RMO 0x0011 +#define DUMP_ERROR_FLAG 0x2000 +#define DUMP_TRIGGERED 0x4000 +#define DUMP_PERFORMED 0x8000 + + +/** + * init_dump_header() - initialize the header declaring a dump + * Returns: length of dump save area. + * + * When the hypervisor saves crashed state, it needs to put + * it somewhere. The dump header tells the hypervisor where + * the data can be saved. + */ +static unsigned long init_dump_header(struct phyp_dump_header *ph) +{ + unsigned long addr_offset = 0; + + /* Set up the dump header */ + ph->version = DUMP_HEADER_VERSION; + ph->num_of_sections = NUM_DUMP_SECTIONS; + ph->status = 0; + + ph->first_offset_section = + (u32)offsetof(struct phyp_dump_header, cpu_data); + ph->dump_disk_section = 0; + ph->block_num_dd = 0; + ph->num_of_blocks_dd = 0; + ph->offset_dd = 0; + + ph->maxtime_to_auto = 0; /* disabled */ + + /* The first two sections are mandatory */ + ph->cpu_data.dump_flags = DUMP_REQUEST_FLAG; + ph->cpu_data.source_type = DUMP_SOURCE_CPU; + ph->cpu_data.source_address = 0; + ph->cpu_data.source_length = phyp_dump_info->cpu_state_size; + ph->cpu_data.destination_address = addr_offset; + addr_offset += phyp_dump_info->cpu_state_size; + + ph->hpte_data.dump_flags = DUMP_REQUEST_FLAG; + ph->hpte_data.source_type = DUMP_SOURCE_HPTE; + ph->hpte_data.source_address = 0; + ph->hpte_data.source_length = phyp_dump_info->hpte_region_size; + ph->hpte_data.destination_address = addr_offset; + addr_offset += phyp_dump_info->hpte_region_size; + + /* This section describes the low kernel region */ + ph->kernel_data.dump_flags = DUMP_REQUEST_FLAG; + ph->kernel_data.source_type = DUMP_SOURCE_RMO; + ph->kernel_data.source_address = PHYP_DUMP_RMR_START; + ph->kernel_data.source_length = PHYP_DUMP_RMR_END; + ph->kernel_data.destination_address = addr_offset; + addr_offset += ph->kernel_data.source_length; + + return addr_offset; +} + +static void print_dump_header(const struct phyp_dump_header *ph) +{ +#ifdef DEBUG + printk(KERN_INFO "dump header:\n"); + /* setup some ph->sections required */ + printk(KERN_INFO "version = %d\n", ph->version); + printk(KERN_INFO "Sections = %d\n", ph->num_of_sections); + printk(KERN_INFO "Status = 0x%x\n", ph->status); + + /* No ph->disk, so all should be set to 0 */ + printk(KERN_INFO "Offset to first section 0x%x\n", + ph->first_offset_section); + printk(KERN_INFO "dump disk sections should be zero\n"); + printk(KERN_INFO "dump disk section = %d\n", ph->dump_disk_section); + printk(KERN_INFO "block num = %ld\n", ph->block_num_dd); + printk(KERN_INFO "number of blocks = %ld\n", ph->num_of_blocks_dd); + printk(KERN_INFO "dump disk offset = %d\n", ph->offset_dd); + printk(KERN_INFO "Max auto time= %d\n", ph->maxtime_to_auto); + + /*set cpu state and hpte states as well scratch pad area */ + printk(KERN_INFO " CPU AREA \n"); + printk(KERN_INFO "cpu dump_flags =%d\n", ph->cpu_data.dump_flags); + printk(KERN_INFO "cpu source_type =%d\n", ph->cpu_data.source_type); + printk(KERN_INFO "cpu error_flags =%d\n", ph->cpu_data.error_flags); + printk(KERN_INFO "cpu source_address =%lx\n", + ph->cpu_data.source_address); + printk(KERN_INFO "cpu source_length =%lx\n", + ph->cpu_data.source_length); + printk(KERN_INFO "cpu length_copied =%lx\n", + ph->cpu_data.length_copied); + + printk(KERN_INFO " HPTE AREA \n"); + printk(KERN_INFO "HPTE dump_flags =%d\n", ph->hpte_data.dump_flags); + printk(KERN_INFO "HPTE source_type =%d\n", ph->hpte_data.source_type); + printk(KERN_INFO "HPTE error_flags =%d\n", ph->hpte_data.error_flags); + printk(KERN_INFO "HPTE source_address =%lx\n", + ph->hpte_data.source_address); + printk(KERN_INFO "HPTE source_length =%lx\n", + ph->hpte_data.source_length); + printk(KERN_INFO "HPTE length_copied =%lx\n", + ph->hpte_data.length_copied); + + printk(KERN_INFO " SRSD AREA \n"); + printk(KERN_INFO "SRSD dump_flags =%d\n", ph->kernel_data.dump_flags); + printk(KERN_INFO "SRSD source_type =%d\n", ph->kernel_data.source_type); + printk(KERN_INFO "SRSD error_flags =%d\n", ph->kernel_data.error_flags); + printk(KERN_INFO "SRSD source_address =%lx\n", + ph->kernel_data.source_address); + printk(KERN_INFO "SRSD source_length =%lx\n", + ph->kernel_data.source_length); + printk(KERN_INFO "SRSD length_copied =%lx\n", + ph->kernel_data.length_copied); +#endif +} + +static ssize_t show_phyp_dump_active(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + + /* create filesystem entry so kdump is phyp-dump aware */ + return sprintf(buf, "%lx\n", phyp_dump_info->phyp_dump_at_boot); +} + +static struct kobj_attribute pdl = __ATTR(phyp_dump_active, 0600, + show_phyp_dump_active, + NULL); + +static void register_dump_area(struct phyp_dump_header *ph, unsigned long addr) +{ + int rc; + + /* Add addr value if not initialized before */ + if (ph->cpu_data.destination_address == 0) { + ph->cpu_data.destination_address += addr; + ph->hpte_data.destination_address += addr; + ph->kernel_data.destination_address += addr; + } + + /* ToDo Invalidate kdump and free memory range. */ + + do { + rc = rtas_call(ibm_configure_kernel_dump, 3, 1, NULL, + 1, ph, sizeof(struct phyp_dump_header)); + } while (rtas_busy_delay(rc)); + + if (rc) { + printk(KERN_ERR "phyp-dump: unexpected error (%d) on " + "register\n", rc); + print_dump_header(ph); + return; + } + + rc = sysfs_create_file(kernel_kobj, &pdl.attr); + if (rc) + printk(KERN_ERR "phyp-dump: unable to create sysfs" + " file (%d)\n", rc); +} + +static +void invalidate_last_dump(struct phyp_dump_header *ph, unsigned long addr) +{ + int rc; + + /* Add addr value if not initialized before */ + if (ph->cpu_data.destination_address == 0) { + ph->cpu_data.destination_address += addr; + ph->hpte_data.destination_address += addr; + ph->kernel_data.destination_address += addr; + } + + do { + rc = rtas_call(ibm_configure_kernel_dump, 3, 1, NULL, + 2, ph, sizeof(struct phyp_dump_header)); + } while (rtas_busy_delay(rc)); + + if (rc) { + printk(KERN_ERR "phyp-dump: unexpected error (%d) " + "on invalidate\n", rc); + print_dump_header(ph); + } +} + +/* ------------------------------------------------- */ +/** + * release_memory_range -- release memory previously lmb_reserved + * @start_pfn: starting physical frame number + * @nr_pages: number of pages to free. + * + * This routine will release memory that had been previously + * lmb_reserved in early boot. The released memory becomes + * available for genreal use. + */ +static void release_memory_range(unsigned long start_pfn, + unsigned long nr_pages) +{ + struct page *rpage; + unsigned long end_pfn; + long i; + + end_pfn = start_pfn + nr_pages; + + for (i = start_pfn; i <= end_pfn; i++) { + rpage = pfn_to_page(i); + if (PageReserved(rpage)) { + ClearPageReserved(rpage); + init_page_count(rpage); + __free_page(rpage); + totalram_pages++; + } + } +} + +/** + * track_freed_range -- Counts the range being freed. + * Once the counter goes to zero, it re-registers dump for + * future use. + */ +static void +track_freed_range(unsigned long addr, unsigned long length) +{ + static unsigned long scratch_area_size, reserved_area_size; + + if (addr < phyp_dump_info->init_reserve_start) + return; + + if ((addr >= phyp_dump_info->init_reserve_start) && + (addr <= phyp_dump_info->init_reserve_start + + phyp_dump_info->init_reserve_size)) + reserved_area_size += length; + + if ((addr >= phyp_dump_info->reserved_scratch_addr) && + (addr <= phyp_dump_info->reserved_scratch_addr + + phyp_dump_info->reserved_scratch_size)) + scratch_area_size += length; + + if ((reserved_area_size == phyp_dump_info->init_reserve_size) && + (scratch_area_size == phyp_dump_info->reserved_scratch_size)) { + + invalidate_last_dump(&phdr, + phyp_dump_info->reserved_scratch_addr); + register_dump_area(&phdr, + phyp_dump_info->reserved_scratch_addr); + } +} + +/* ------------------------------------------------- */ +/** + * sysfs_release_region -- sysfs interface to release memory range. + * + * Usage: + * "echo <start addr> <length> > /sys/kernel/release_region" + * + * Example: + * "echo 0x40000000 0x10000000 > /sys/kernel/release_region" + * + * will release 256MB starting at 1GB. + */ +static ssize_t store_release_region(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned long start_addr, length, end_addr; + unsigned long start_pfn, nr_pages; + ssize_t ret; + + ret = sscanf(buf, "%lx %lx", &start_addr, &length); + if (ret != 2) + return -EINVAL; + + track_freed_range(start_addr, length); + + /* Range-check - don't free any reserved memory that + * wasn't reserved for phyp-dump */ + if (start_addr < phyp_dump_info->init_reserve_start) + start_addr = phyp_dump_info->init_reserve_start; + + end_addr = phyp_dump_info->init_reserve_start + + phyp_dump_info->init_reserve_size; + if (start_addr+length > end_addr) + length = end_addr - start_addr; + + /* Release the region of memory assed in by user */ + start_pfn = PFN_DOWN(start_addr); + nr_pages = PFN_DOWN(length); + release_memory_range(start_pfn, nr_pages); + + return count; +} + +static ssize_t show_release_region(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + u64 second_addr_range; + + /* total reserved size - start of scratch area */ + second_addr_range = phyp_dump_info->init_reserve_size - + phyp_dump_info->reserved_scratch_size; + return sprintf(buf, "CPU:0x%lx-0x%lx: HPTE:0x%lx-0x%lx:" + " DUMP:0x%lx-0x%lx, 0x%lx-0x%lx:\n", + phdr.cpu_data.destination_address, + phdr.cpu_data.length_copied, + phdr.hpte_data.destination_address, + phdr.hpte_data.length_copied, + phdr.kernel_data.destination_address, + phdr.kernel_data.length_copied, + phyp_dump_info->init_reserve_start, + second_addr_range); +} + +static struct kobj_attribute rr = __ATTR(release_region, 0600, + show_release_region, + store_release_region); + +static int __init phyp_dump_setup(void) +{ + struct device_node *rtas; + const struct phyp_dump_header *dump_header = NULL; + unsigned long dump_area_start; + unsigned long dump_area_length; + int header_len = 0; + int rc; + + /* If no memory was reserved in early boot, there is nothing to do */ + if (phyp_dump_info->init_reserve_size == 0) + return 0; + + /* Return if phyp dump not supported */ + if (!phyp_dump_info->phyp_dump_configured) + return -ENOSYS; + + /* Is there dump data waiting for us? If there isn't, + * then register a new dump area, and release all of + * the rest of the reserved ram. + * + * The /rtas/ibm,kernel-dump rtas node is present only + * if there is dump data waiting for us. + */ + rtas = of_find_node_by_path("/rtas"); + if (rtas) { + dump_header = of_get_property(rtas, "ibm,kernel-dump", + &header_len); + of_node_put(rtas); + } + + print_dump_header(dump_header); + dump_area_length = init_dump_header(&phdr); + /* align down */ + dump_area_start = phyp_dump_info->init_reserve_start & PAGE_MASK; + + if (dump_header == NULL) { + register_dump_area(&phdr, dump_area_start); + return 0; + } + + /* re-register the dump area, if old dump was invalid */ + if ((dump_header) && (dump_header->status & DUMP_ERROR_FLAG)) { + invalidate_last_dump(&phdr, dump_area_start); + register_dump_area(&phdr, dump_area_start); + return 0; + } + + if (dump_header) { + phyp_dump_info->reserved_scratch_addr = + dump_header->cpu_data.destination_address; + phyp_dump_info->reserved_scratch_size = + dump_header->cpu_data.source_length + + dump_header->hpte_data.source_length + + dump_header->kernel_data.source_length; + } + + /* Should we create a dump_subsys, analogous to s390/ipl.c ? */ + rc = sysfs_create_file(kernel_kobj, &rr.attr); + if (rc) + printk(KERN_ERR "phyp-dump: unable to create sysfs file (%d)\n", + rc); + + /* ToDo: re-register the dump area, for next time. */ + return 0; +} +machine_subsys_initcall(pseries, phyp_dump_setup); + +int __init early_init_dt_scan_phyp_dump(unsigned long node, + const char *uname, int depth, void *data) +{ + const unsigned int *sizes; + + phyp_dump_info->phyp_dump_configured = 0; + phyp_dump_info->phyp_dump_is_active = 0; + + if (depth != 1 || strcmp(uname, "rtas") != 0) + return 0; + + if (of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL)) + phyp_dump_info->phyp_dump_configured++; + + if (of_get_flat_dt_prop(node, "ibm,dump-kernel", NULL)) + phyp_dump_info->phyp_dump_is_active++; + + sizes = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes", + NULL); + if (!sizes) + return 0; + + if (sizes[0] == 1) + phyp_dump_info->cpu_state_size = *((unsigned long *)&sizes[1]); + + if (sizes[3] == 2) + phyp_dump_info->hpte_region_size = + *((unsigned long *)&sizes[4]); + return 1; +} + +/* Look for phyp_dump= cmdline option */ +static int __init early_phyp_dump_enabled(char *p) +{ + phyp_dump_info->phyp_dump_at_boot = 1; + + if (!p) + return 0; + + if (strncmp(p, "1", 1) == 0) + phyp_dump_info->phyp_dump_at_boot = 1; + else if (strncmp(p, "0", 1) == 0) + phyp_dump_info->phyp_dump_at_boot = 0; + + return 0; +} +early_param("phyp_dump", early_phyp_dump_enabled); + +/* Look for phyp_dump_reserve_size= cmdline option */ +static int __init early_phyp_dump_reserve_size(char *p) +{ + if (p) + phyp_dump_info->reserve_bootvar = memparse(p, &p); + + return 0; +} +early_param("phyp_dump_reserve_size", early_phyp_dump_reserve_size); diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h index 61136d01955..9e17c0d2a0c 100644 --- a/arch/powerpc/platforms/pseries/pseries.h +++ b/arch/powerpc/platforms/pseries/pseries.h @@ -38,4 +38,6 @@ extern void pSeries_final_fixup(void); /* Poweron flag used for enabling auto ups restart */ extern unsigned long rtas_poweron_auto; +extern void find_udbg_vterm(void); + #endif /* _PSERIES_PSERIES_H */ diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index a1ab25c7082..2b548afd100 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c @@ -67,8 +67,6 @@ static int ras_check_exception_token; static irqreturn_t ras_epow_interrupt(int irq, void *dev_id); static irqreturn_t ras_error_interrupt(int irq, void *dev_id); -/* #define DEBUG */ - static void request_ras_irqs(struct device_node *np, irq_handler_t handler, @@ -237,7 +235,7 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id) printk(KERN_EMERG "Error: Fatal hardware error <0x%lx 0x%x>\n", *((unsigned long *)&ras_log_buf), status); -#ifndef DEBUG +#ifndef DEBUG_RTAS_POWER_OFF /* Don't actually power off when debugging so we can test * without actually failing while injecting errors. * Error data will not be logged to syslog. diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 2800fced8c7..ac75c10de27 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -222,14 +222,14 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length tmp = strchr(buf, ' '); if (!tmp) { printk(KERN_ERR "property parse failed in %s at line %d\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return NULL; } *tmp = '\0'; if (++tmp >= end) { printk(KERN_ERR "property parse failed in %s at line %d\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return NULL; } @@ -238,12 +238,12 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length *length = simple_strtoul(tmp, &tmp, 10); if (*length == -1) { printk(KERN_ERR "property parse failed in %s at line %d\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return NULL; } if (*tmp != ' ' || ++tmp >= end) { printk(KERN_ERR "property parse failed in %s at line %d\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return NULL; } @@ -252,12 +252,12 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length tmp += *length; if (tmp > end) { printk(KERN_ERR "property parse failed in %s at line %d\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return NULL; } else if (tmp < end && *tmp != ' ' && *tmp != '\0') { printk(KERN_ERR "property parse failed in %s at line %d\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return NULL; } tmp++; diff --git a/arch/powerpc/platforms/pseries/rtasd.c b/arch/powerpc/platforms/pseries/rtasd.c index e3078ce4151..befadd4f952 100644 --- a/arch/powerpc/platforms/pseries/rtasd.c +++ b/arch/powerpc/platforms/pseries/rtasd.c @@ -29,11 +29,6 @@ #include <asm/atomic.h> #include <asm/machdep.h> -#if 0 -#define DEBUG(A...) printk(KERN_ERR A) -#else -#define DEBUG(A...) -#endif static DEFINE_SPINLOCK(rtasd_log_lock); @@ -198,7 +193,7 @@ void pSeries_log_error(char *buf, unsigned int err_type, int fatal) unsigned long s; int len = 0; - DEBUG("logging event\n"); + pr_debug("rtasd: logging event\n"); if (buf == NULL) return; @@ -409,7 +404,8 @@ static int rtasd(void *unused) daemonize("rtasd"); printk(KERN_DEBUG "RTAS daemon started\n"); - DEBUG("will sleep for %d milliseconds\n", (30000/rtas_event_scan_rate)); + pr_debug("rtasd: will sleep for %d milliseconds\n", + (30000 / rtas_event_scan_rate)); /* See if we have any error stored in NVRAM */ memset(logdata, 0, rtas_error_log_max); @@ -428,9 +424,9 @@ static int rtasd(void *unused) do_event_scan_all_cpus(1000); if (surveillance_timeout != -1) { - DEBUG("enabling surveillance\n"); + pr_debug("rtasd: enabling surveillance\n"); enable_surveillance(surveillance_timeout); - DEBUG("surveillance enabled\n"); + pr_debug("rtasd: surveillance enabled\n"); } /* Delay should be at least one second since some diff --git a/arch/powerpc/platforms/pseries/scanlog.c b/arch/powerpc/platforms/pseries/scanlog.c index 8e1ef168e2d..bec3803f061 100644 --- a/arch/powerpc/platforms/pseries/scanlog.c +++ b/arch/powerpc/platforms/pseries/scanlog.c @@ -38,9 +38,7 @@ #define SCANLOG_HWERROR -1 #define SCANLOG_CONTINUE 1 -#define DEBUG(A...) do { if (scanlog_debug) printk(KERN_ERR "scanlog: " A); } while (0) -static int scanlog_debug; static unsigned int ibm_scan_log_dump; /* RTAS token */ static struct proc_dir_entry *proc_ppc64_scan_log_dump; /* The proc file */ @@ -86,14 +84,14 @@ static ssize_t scanlog_read(struct file *file, char __user *buf, memcpy(data, rtas_data_buf, RTAS_DATA_BUF_SIZE); spin_unlock(&rtas_data_buf_lock); - DEBUG("status=%d, data[0]=%x, data[1]=%x, data[2]=%x\n", - status, data[0], data[1], data[2]); + pr_debug("scanlog: status=%d, data[0]=%x, data[1]=%x, " \ + "data[2]=%x\n", status, data[0], data[1], data[2]); switch (status) { case SCANLOG_COMPLETE: - DEBUG("hit eof\n"); + pr_debug("scanlog: hit eof\n"); return 0; case SCANLOG_HWERROR: - DEBUG("hardware error reading scan log data\n"); + pr_debug("scanlog: hardware error reading data\n"); return -EIO; case SCANLOG_CONTINUE: /* We may or may not have data yet */ @@ -110,7 +108,8 @@ static ssize_t scanlog_read(struct file *file, char __user *buf, /* Assume extended busy */ wait_time = rtas_busy_delay_time(status); if (!wait_time) { - printk(KERN_ERR "scanlog: unknown error from rtas: %d\n", status); + printk(KERN_ERR "scanlog: unknown error " \ + "from rtas: %d\n", status); return -EIO; } } @@ -134,15 +133,9 @@ static ssize_t scanlog_write(struct file * file, const char __user * buf, if (buf) { if (strncmp(stkbuf, "reset", 5) == 0) { - DEBUG("reset scanlog\n"); + pr_debug("scanlog: reset scanlog\n"); status = rtas_call(ibm_scan_log_dump, 2, 1, NULL, 0, 0); - DEBUG("rtas returns %d\n", status); - } else if (strncmp(stkbuf, "debugon", 7) == 0) { - printk(KERN_ERR "scanlog: debug on\n"); - scanlog_debug = 1; - } else if (strncmp(stkbuf, "debugoff", 8) == 0) { - printk(KERN_ERR "scanlog: debug off\n"); - scanlog_debug = 0; + pr_debug("scanlog: rtas returns %d\n", status); } } return count; @@ -195,31 +188,30 @@ const struct file_operations scanlog_fops = { static int __init scanlog_init(void) { struct proc_dir_entry *ent; + void *data; + int err = -ENOMEM; ibm_scan_log_dump = rtas_token("ibm,scan-log-dump"); - if (ibm_scan_log_dump == RTAS_UNKNOWN_SERVICE) { - printk(KERN_ERR "scan-log-dump not implemented on this system\n"); - return -EIO; - } + if (ibm_scan_log_dump == RTAS_UNKNOWN_SERVICE) + return -ENODEV; - ent = create_proc_entry("ppc64/rtas/scan-log-dump", S_IRUSR, NULL); - if (ent) { - ent->proc_fops = &scanlog_fops; - /* Ideally we could allocate a buffer < 4G */ - ent->data = kmalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); - if (!ent->data) { - printk(KERN_ERR "Failed to allocate a buffer\n"); - remove_proc_entry("scan-log-dump", ent->parent); - return -ENOMEM; - } - ((unsigned int *)ent->data)[0] = 0; - } else { - printk(KERN_ERR "Failed to create ppc64/scan-log-dump proc entry\n"); - return -EIO; - } + /* Ideally we could allocate a buffer < 4G */ + data = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); + if (!data) + goto err; + + ent = proc_create("ppc64/rtas/scan-log-dump", S_IRUSR, NULL, + &scanlog_fops); + if (!ent) + goto err; + + ent->data = data; proc_ppc64_scan_log_dump = ent; return 0; +err: + kfree(data); + return err; } static void __exit scanlog_cleanup(void) diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index fdb9b1c8f97..f5d29f5b13c 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -16,8 +16,6 @@ * bootup setup stuff.. */ -#undef DEBUG - #include <linux/cpu.h> #include <linux/errno.h> #include <linux/sched.h> @@ -70,14 +68,6 @@ #include "plpar_wrappers.h" #include "pseries.h" -#ifdef DEBUG -#define DBG(fmt...) udbg_printf(fmt) -#else -#define DBG(fmt...) -#endif - -/* move those away to a .h */ -extern void find_udbg_vterm(void); int fwnmi_active; /* TRUE if an FWNMI handler is present */ @@ -127,14 +117,60 @@ void pseries_8259_cascade(unsigned int irq, struct irq_desc *desc) desc->chip->eoi(irq); } -static void __init pseries_mpic_init_IRQ(void) +static void __init pseries_setup_i8259_cascade(void) { - struct device_node *np, *old, *cascade = NULL; - const unsigned int *addrp; + struct device_node *np, *old, *found = NULL; + unsigned int cascade; + const u32 *addrp; unsigned long intack = 0; + int naddr; + + for_each_node_by_type(np, "interrupt-controller") { + if (of_device_is_compatible(np, "chrp,iic")) { + found = np; + break; + } + } + + if (found == NULL) { + printk(KERN_DEBUG "pic: no ISA interrupt controller\n"); + return; + } + + cascade = irq_of_parse_and_map(found, 0); + if (cascade == NO_IRQ) { + printk(KERN_ERR "pic: failed to map cascade interrupt"); + return; + } + pr_debug("pic: cascade mapped to irq %d\n", cascade); + + for (old = of_node_get(found); old != NULL ; old = np) { + np = of_get_parent(old); + of_node_put(old); + if (np == NULL) + break; + if (strcmp(np->name, "pci") != 0) + continue; + addrp = of_get_property(np, "8259-interrupt-acknowledge", NULL); + if (addrp == NULL) + continue; + naddr = of_n_addr_cells(np); + intack = addrp[naddr-1]; + if (naddr > 1) + intack |= ((unsigned long)addrp[naddr-2]) << 32; + } + if (intack) + printk(KERN_DEBUG "pic: PCI 8259 intack at 0x%016lx\n", intack); + i8259_init(found, intack); + of_node_put(found); + set_irq_chained_handler(cascade, pseries_8259_cascade); +} + +static void __init pseries_mpic_init_IRQ(void) +{ + struct device_node *np; const unsigned int *opprop; unsigned long openpic_addr = 0; - unsigned int cascade_irq; int naddr, n, i, opplen; struct mpic *mpic; @@ -167,43 +203,13 @@ static void __init pseries_mpic_init_IRQ(void) mpic_init(mpic); /* Look for cascade */ - for_each_node_by_type(np, "interrupt-controller") - if (of_device_is_compatible(np, "chrp,iic")) { - cascade = np; - break; - } - if (cascade == NULL) - return; - - cascade_irq = irq_of_parse_and_map(cascade, 0); - if (cascade_irq == NO_IRQ) { - printk(KERN_ERR "mpic: failed to map cascade interrupt"); - return; - } + pseries_setup_i8259_cascade(); +} - /* Check ACK type */ - for (old = of_node_get(cascade); old != NULL ; old = np) { - np = of_get_parent(old); - of_node_put(old); - if (np == NULL) - break; - if (strcmp(np->name, "pci") != 0) - continue; - addrp = of_get_property(np, "8259-interrupt-acknowledge", - NULL); - if (addrp == NULL) - continue; - naddr = of_n_addr_cells(np); - intack = addrp[naddr-1]; - if (naddr > 1) - intack |= ((unsigned long)addrp[naddr-2]) << 32; - } - if (intack) - printk(KERN_DEBUG "mpic: PCI 8259 intack at 0x%016lx\n", - intack); - i8259_init(cascade, intack); - of_node_put(cascade); - set_irq_chained_handler(cascade_irq, pseries_8259_cascade); +static void __init pseries_xics_init_IRQ(void) +{ + xics_init_IRQ(); + pseries_setup_i8259_cascade(); } static void pseries_lpar_enable_pmcs(void) @@ -235,7 +241,7 @@ static void __init pseries_discover_pic(void) smp_init_pseries_mpic(); return; } else if (strstr(typep, "ppc-xicp")) { - ppc_md.init_IRQ = xics_init_IRQ; + ppc_md.init_IRQ = pseries_xics_init_IRQ; setup_kexec_cpu_down_xics(); smp_init_pseries_xics(); return; @@ -313,7 +319,7 @@ static int pseries_set_xdabr(unsigned long dabr) */ static void __init pSeries_init_early(void) { - DBG(" -> pSeries_init_early()\n"); + pr_debug(" -> pSeries_init_early()\n"); if (firmware_has_feature(FW_FEATURE_LPAR)) find_udbg_vterm(); @@ -325,7 +331,7 @@ static void __init pSeries_init_early(void) iommu_init_early_pSeries(); - DBG(" <- pSeries_init_early()\n"); + pr_debug(" <- pSeries_init_early()\n"); } /* @@ -370,7 +376,7 @@ static int __init pSeries_probe(void) of_flat_dt_is_compatible(root, "IBM,CBEA")) return 0; - DBG("pSeries detected, looking for LPAR capability...\n"); + pr_debug("pSeries detected, looking for LPAR capability...\n"); /* Now try to figure out if we are running on LPAR */ of_scan_flat_dt(pSeries_probe_hypertas, NULL); @@ -380,8 +386,8 @@ static int __init pSeries_probe(void) else hpte_init_native(); - DBG("Machine is%s LPAR !\n", - (powerpc_firmware_features & FW_FEATURE_LPAR) ? "" : " not"); + pr_debug("Machine is%s LPAR !\n", + (powerpc_firmware_features & FW_FEATURE_LPAR) ? "" : " not"); return 1; } @@ -393,6 +399,7 @@ static void pseries_dedicated_idle_sleep(void) { unsigned int cpu = smp_processor_id(); unsigned long start_snooze; + unsigned long in_purr, out_purr; /* * Indicate to the HV that we are idle. Now would be @@ -400,6 +407,7 @@ static void pseries_dedicated_idle_sleep(void) */ get_lppaca()->idle = 1; get_lppaca()->donate_dedicated_cpu = 1; + in_purr = mfspr(SPRN_PURR); /* * We come in with interrupts disabled, and need_resched() @@ -432,6 +440,8 @@ static void pseries_dedicated_idle_sleep(void) out: HMT_medium(); + out_purr = mfspr(SPRN_PURR); + get_lppaca()->wait_state_cycles += out_purr - in_purr; get_lppaca()->donate_dedicated_cpu = 0; get_lppaca()->idle = 0; } diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index ea4c65917a6..9d8f8c84ab8 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -12,7 +12,6 @@ * 2 of the License, or (at your option) any later version. */ -#undef DEBUG #include <linux/kernel.h> #include <linux/module.h> @@ -51,12 +50,6 @@ #include "plpar_wrappers.h" #include "pseries.h" -#ifdef DEBUG -#include <asm/udbg.h> -#define DBG(fmt...) udbg_printf(fmt) -#else -#define DBG(fmt...) -#endif /* * The primary thread of each non-boot processor is recorded here before @@ -231,7 +224,7 @@ static void __init smp_init_pseries(void) { int i; - DBG(" -> smp_init_pSeries()\n"); + pr_debug(" -> smp_init_pSeries()\n"); /* Mark threads which are still spinning in hold loops. */ if (cpu_has_feature(CPU_FTR_SMT)) { @@ -255,7 +248,7 @@ static void __init smp_init_pseries(void) smp_ops->take_timebase = pSeries_take_timebase; } - DBG(" <- smp_init_pSeries()\n"); + pr_debug(" <- smp_init_pSeries()\n"); } #ifdef CONFIG_MPIC diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index ca52b587166..ebebc28fe89 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -9,7 +9,6 @@ * 2 of the License, or (at your option) any later version. */ -#undef DEBUG #include <linux/types.h> #include <linux/threads.h> @@ -516,6 +515,8 @@ static struct irq_chip xics_pic_lpar = { .set_affinity = xics_set_affinity }; +/* Points to the irq_chip we're actually using */ +static struct irq_chip *xics_irq_chip; static int xics_host_match(struct irq_host *h, struct device_node *node) { @@ -526,23 +527,13 @@ static int xics_host_match(struct irq_host *h, struct device_node *node) return !of_device_is_compatible(node, "chrp,iic"); } -static int xics_host_map_direct(struct irq_host *h, unsigned int virq, - irq_hw_number_t hw) +static int xics_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw) { - pr_debug("xics: map_direct virq %d, hwirq 0x%lx\n", virq, hw); + pr_debug("xics: map virq %d, hwirq 0x%lx\n", virq, hw); get_irq_desc(virq)->status |= IRQ_LEVEL; - set_irq_chip_and_handler(virq, &xics_pic_direct, handle_fasteoi_irq); - return 0; -} - -static int xics_host_map_lpar(struct irq_host *h, unsigned int virq, - irq_hw_number_t hw) -{ - pr_debug("xics: map_direct virq %d, hwirq 0x%lx\n", virq, hw); - - get_irq_desc(virq)->status |= IRQ_LEVEL; - set_irq_chip_and_handler(virq, &xics_pic_lpar, handle_fasteoi_irq); + set_irq_chip_and_handler(virq, xics_irq_chip, handle_fasteoi_irq); return 0; } @@ -561,27 +552,20 @@ static int xics_host_xlate(struct irq_host *h, struct device_node *ct, return 0; } -static struct irq_host_ops xics_host_direct_ops = { +static struct irq_host_ops xics_host_ops = { .match = xics_host_match, - .map = xics_host_map_direct, - .xlate = xics_host_xlate, -}; - -static struct irq_host_ops xics_host_lpar_ops = { - .match = xics_host_match, - .map = xics_host_map_lpar, + .map = xics_host_map, .xlate = xics_host_xlate, }; static void __init xics_init_host(void) { - struct irq_host_ops *ops; - if (firmware_has_feature(FW_FEATURE_LPAR)) - ops = &xics_host_lpar_ops; + xics_irq_chip = &xics_pic_lpar; else - ops = &xics_host_direct_ops; - xics_host = irq_alloc_host(NULL, IRQ_HOST_MAP_TREE, 0, ops, + xics_irq_chip = &xics_pic_direct; + + xics_host = irq_alloc_host(NULL, IRQ_HOST_MAP_TREE, 0, &xics_host_ops, XICS_IRQ_SPURIOUS); BUG_ON(xics_host == NULL); irq_set_default_host(xics_host); @@ -655,52 +639,6 @@ static void __init xics_init_one_node(struct device_node *np, } } - -static void __init xics_setup_8259_cascade(void) -{ - struct device_node *np, *old, *found = NULL; - int cascade, naddr; - const u32 *addrp; - unsigned long intack = 0; - - for_each_node_by_type(np, "interrupt-controller") - if (of_device_is_compatible(np, "chrp,iic")) { - found = np; - break; - } - if (found == NULL) { - printk(KERN_DEBUG "xics: no ISA interrupt controller\n"); - return; - } - cascade = irq_of_parse_and_map(found, 0); - if (cascade == NO_IRQ) { - printk(KERN_ERR "xics: failed to map cascade interrupt"); - return; - } - pr_debug("xics: cascade mapped to irq %d\n", cascade); - - for (old = of_node_get(found); old != NULL ; old = np) { - np = of_get_parent(old); - of_node_put(old); - if (np == NULL) - break; - if (strcmp(np->name, "pci") != 0) - continue; - addrp = of_get_property(np, "8259-interrupt-acknowledge", NULL); - if (addrp == NULL) - continue; - naddr = of_n_addr_cells(np); - intack = addrp[naddr-1]; - if (naddr > 1) - intack |= ((unsigned long)addrp[naddr-2]) << 32; - } - if (intack) - printk(KERN_DEBUG "xics: PCI 8259 intack at 0x%016lx\n", intack); - i8259_init(found, intack); - of_node_put(found); - set_irq_chained_handler(cascade, pseries_8259_cascade); -} - void __init xics_init_IRQ(void) { struct device_node *np; @@ -733,8 +671,6 @@ void __init xics_init_IRQ(void) xics_setup_cpu(); - xics_setup_8259_cascade(); - ppc64_boot_msg(0x21, "XICS Done"); } @@ -763,7 +699,7 @@ void xics_request_IPIs(void) } #endif /* CONFIG_SMP */ -void xics_teardown_cpu() +void xics_teardown_cpu(void) { int cpu = smp_processor_id(); diff --git a/arch/powerpc/platforms/pseries/xics.h b/arch/powerpc/platforms/pseries/xics.h index c26bcff47b6..1c5321ae8f2 100644 --- a/arch/powerpc/platforms/pseries/xics.h +++ b/arch/powerpc/platforms/pseries/xics.h @@ -28,7 +28,4 @@ struct xics_ipi_struct { extern struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned; -struct irq_desc; -extern void pseries_8259_cascade(unsigned int irq, struct irq_desc *desc); - #endif /* _POWERPC_KERNEL_XICS_H */ |