summaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/pseries
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/pseries')
-rw-r--r--arch/powerpc/platforms/pseries/Makefile1
-rw-r--r--arch/powerpc/platforms/pseries/eeh.c5
-rw-r--r--arch/powerpc/platforms/pseries/lpar.c25
-rw-r--r--arch/powerpc/platforms/pseries/pci_dlpar.c9
-rw-r--r--arch/powerpc/platforms/pseries/phyp_dump.c507
-rw-r--r--arch/powerpc/platforms/pseries/pseries.h2
-rw-r--r--arch/powerpc/platforms/pseries/reconfig.c12
-rw-r--r--arch/powerpc/platforms/pseries/scanlog.c37
-rw-r--r--arch/powerpc/platforms/pseries/setup.c105
-rw-r--r--arch/powerpc/platforms/pseries/xics.c87
-rw-r--r--arch/powerpc/platforms/pseries/xics.h3
11 files changed, 630 insertions, 163 deletions
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 992ba6753cf..bdae04bb7a0 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -18,3 +18,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..550b2f7d2cc 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -945,7 +945,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 +958,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/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 9a455d46379..9235c469449 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -40,6 +40,7 @@
#include <asm/smp.h>
#include "plpar_wrappers.h"
+#include "pseries.h"
#ifdef DEBUG_LOW
#define DBG_LOW(fmt...) do { udbg_printf(fmt); } while(0)
@@ -203,7 +204,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 +219,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 +232,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);
@@ -520,6 +516,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 +607,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/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/scanlog.c b/arch/powerpc/platforms/pseries/scanlog.c
index 8e1ef168e2d..e5b0ea87016 100644
--- a/arch/powerpc/platforms/pseries/scanlog.c
+++ b/arch/powerpc/platforms/pseries/scanlog.c
@@ -195,31 +195,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..f66aa9c3b13 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -76,9 +76,6 @@
#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 */
static void pseries_shared_idle_sleep(void);
@@ -127,14 +124,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 +210,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 +248,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;
@@ -393,6 +406,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 +414,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 +447,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/xics.c b/arch/powerpc/platforms/pseries/xics.c
index a977f200db8..43df53c30aa 100644
--- a/arch/powerpc/platforms/pseries/xics.c
+++ b/arch/powerpc/platforms/pseries/xics.c
@@ -516,6 +516,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 +528,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 +553,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 +640,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 +672,6 @@ void __init xics_init_IRQ(void)
xics_setup_cpu();
- xics_setup_8259_cascade();
-
ppc64_boot_msg(0x21, "XICS Done");
}
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 */