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/Kconfig1
-rw-r--r--arch/powerpc/platforms/pseries/Makefile2
-rw-r--r--arch/powerpc/platforms/pseries/dtl.c6
-rw-r--r--arch/powerpc/platforms/pseries/eeh.c24
-rw-r--r--arch/powerpc/platforms/pseries/firmware.c69
-rw-r--r--arch/powerpc/platforms/pseries/hotplug-cpu.c8
-rw-r--r--arch/powerpc/platforms/pseries/hotplug-memory.c12
-rw-r--r--arch/powerpc/platforms/pseries/hvCall_inst.c2
-rw-r--r--arch/powerpc/platforms/pseries/hvcserver.c5
-rw-r--r--arch/powerpc/platforms/pseries/iommu.c128
-rw-r--r--arch/powerpc/platforms/pseries/lpar.c16
-rw-r--r--arch/powerpc/platforms/pseries/mobility.c41
-rw-r--r--arch/powerpc/platforms/pseries/msi.c21
-rw-r--r--arch/powerpc/platforms/pseries/pci.c56
-rw-r--r--arch/powerpc/platforms/pseries/plpar_wrappers.h33
-rw-r--r--arch/powerpc/platforms/pseries/processor_idle.c34
-rw-r--r--arch/powerpc/platforms/pseries/pseries.h9
-rw-r--r--arch/powerpc/platforms/pseries/pseries_energy.c37
-rw-r--r--arch/powerpc/platforms/pseries/reconfig.c2
-rw-r--r--arch/powerpc/platforms/pseries/scanlog.c36
-rw-r--r--arch/powerpc/platforms/pseries/setup.c68
-rw-r--r--arch/powerpc/platforms/pseries/smp.c33
22 files changed, 441 insertions, 202 deletions
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 837cf49357e..9a0941bc4d3 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -17,6 +17,7 @@ config PPC_PSERIES
select PPC_NATIVE
select PPC_PCI_CHOICE if EXPERT
select ZLIB_DEFLATE
+ select PPC_DOORBELL
default y
config PPC_SPLPAR
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 890622b87c8..53866e537a9 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -1,4 +1,4 @@
-ccflags-$(CONFIG_PPC64) := -mno-minimal-toc
+ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
ccflags-$(CONFIG_PPC_PSERIES_DEBUG) += -DDEBUG
obj-y := lpar.o hvCall.o nvram.o reconfig.o \
diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
index a7648543c59..0cc0ac07a55 100644
--- a/arch/powerpc/platforms/pseries/dtl.c
+++ b/arch/powerpc/platforms/pseries/dtl.c
@@ -57,7 +57,7 @@ static u8 dtl_event_mask = 0x7;
*/
static int dtl_buf_entries = N_DISPATCH_LOG;
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
struct dtl_ring {
u64 write_index;
struct dtl_entry *write_ptr;
@@ -142,7 +142,7 @@ static u64 dtl_current_index(struct dtl *dtl)
return per_cpu(dtl_rings, dtl->cpu).write_index;
}
-#else /* CONFIG_VIRT_CPU_ACCOUNTING */
+#else /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
static int dtl_start(struct dtl *dtl)
{
@@ -188,7 +188,7 @@ static u64 dtl_current_index(struct dtl *dtl)
{
return lppaca_of(dtl->cpu).dtl_idx;
}
-#endif /* CONFIG_VIRT_CPU_ACCOUNTING */
+#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
static int dtl_enable(struct dtl *dtl)
{
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 9a04322b173..6b73d6c44f5 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -788,7 +788,6 @@ static void eeh_add_device_late(struct pci_dev *dev)
dev->dev.archdata.edev = edev;
eeh_addr_cache_insert_dev(dev);
- eeh_sysfs_add_device(dev);
}
/**
@@ -815,6 +814,29 @@ void eeh_add_device_tree_late(struct pci_bus *bus)
EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
/**
+ * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus
+ * @bus: PCI bus
+ *
+ * This routine must be used to add EEH sysfs files for PCI
+ * devices which are attached to the indicated PCI bus. The PCI bus
+ * is added after system boot through hotplug or dlpar.
+ */
+void eeh_add_sysfs_files(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ eeh_sysfs_add_device(dev);
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ struct pci_bus *subbus = dev->subordinate;
+ if (subbus)
+ eeh_add_sysfs_files(subbus);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
+
+/**
* eeh_remove_device - Undo EEH setup for the indicated pci device
* @dev: pci device to be removed
* @purge_pe: remove the PE or not
diff --git a/arch/powerpc/platforms/pseries/firmware.c b/arch/powerpc/platforms/pseries/firmware.c
index 7b56118f531..8c80588abac 100644
--- a/arch/powerpc/platforms/pseries/firmware.c
+++ b/arch/powerpc/platforms/pseries/firmware.c
@@ -28,13 +28,18 @@
#include "pseries.h"
-typedef struct {
+struct hypertas_fw_feature {
unsigned long val;
char * name;
-} firmware_feature_t;
+};
-static __initdata firmware_feature_t
-firmware_features_table[FIRMWARE_MAX_FEATURES] = {
+/*
+ * The names in this table match names in rtas/ibm,hypertas-functions. If the
+ * entry ends in a '*', only upto the '*' is matched. Otherwise the entire
+ * string must match.
+ */
+static __initdata struct hypertas_fw_feature
+hypertas_fw_features_table[] = {
{FW_FEATURE_PFT, "hcall-pft"},
{FW_FEATURE_TCE, "hcall-tce"},
{FW_FEATURE_SPRG0, "hcall-sprg0"},
@@ -57,32 +62,72 @@ firmware_features_table[FIRMWARE_MAX_FEATURES] = {
{FW_FEATURE_SPLPAR, "hcall-splpar"},
{FW_FEATURE_VPHN, "hcall-vphn"},
{FW_FEATURE_SET_MODE, "hcall-set-mode"},
+ {FW_FEATURE_BEST_ENERGY, "hcall-best-energy-1*"},
};
/* Build up the firmware features bitmask using the contents of
* device-tree/ibm,hypertas-functions. Ultimately this functionality may
* be moved into prom.c prom_init().
*/
-void __init fw_feature_init(const char *hypertas, unsigned long len)
+void __init fw_hypertas_feature_init(const char *hypertas, unsigned long len)
{
const char *s;
int i;
- pr_debug(" -> fw_feature_init()\n");
+ pr_debug(" -> fw_hypertas_feature_init()\n");
for (s = hypertas; s < hypertas + len; s += strlen(s) + 1) {
- for (i = 0; i < FIRMWARE_MAX_FEATURES; i++) {
- /* check value against table of strings */
- if (!firmware_features_table[i].name ||
- strcmp(firmware_features_table[i].name, s))
+ for (i = 0; i < ARRAY_SIZE(hypertas_fw_features_table); i++) {
+ const char *name = hypertas_fw_features_table[i].name;
+ size_t size;
+
+ /*
+ * If there is a '*' at the end of name, only check
+ * upto there
+ */
+ size = strlen(name);
+ if (size && name[size - 1] == '*') {
+ if (strncmp(name, s, size - 1))
+ continue;
+ } else if (strcmp(name, s))
continue;
/* we have a match */
powerpc_firmware_features |=
- firmware_features_table[i].val;
+ hypertas_fw_features_table[i].val;
break;
}
}
- pr_debug(" <- fw_feature_init()\n");
+ pr_debug(" <- fw_hypertas_feature_init()\n");
+}
+
+struct vec5_fw_feature {
+ unsigned long val;
+ unsigned int feature;
+};
+
+static __initdata struct vec5_fw_feature
+vec5_fw_features_table[] = {
+ {FW_FEATURE_TYPE1_AFFINITY, OV5_TYPE1_AFFINITY},
+ {FW_FEATURE_PRRN, OV5_PRRN},
+};
+
+void __init fw_vec5_feature_init(const char *vec5, unsigned long len)
+{
+ unsigned int index, feat;
+ int i;
+
+ pr_debug(" -> fw_vec5_feature_init()\n");
+
+ for (i = 0; i < ARRAY_SIZE(vec5_fw_features_table); i++) {
+ index = OV5_INDX(vec5_fw_features_table[i].feature);
+ feat = OV5_FEAT(vec5_fw_features_table[i].feature);
+
+ if (vec5[index] & feat)
+ powerpc_firmware_features |=
+ vec5_fw_features_table[i].val;
+ }
+
+ pr_debug(" <- fw_vec5_feature_init()\n");
}
diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
index a38956269fb..217ca5c75b2 100644
--- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -127,9 +127,16 @@ static void pseries_mach_cpu_die(void)
get_lppaca()->donate_dedicated_cpu = 1;
while (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) {
+ while (!prep_irq_for_idle()) {
+ local_irq_enable();
+ local_irq_disable();
+ }
+
extended_cede_processor(cede_latency_hint);
}
+ local_irq_disable();
+
if (!get_lppaca()->shared_proc)
get_lppaca()->donate_dedicated_cpu = 0;
get_lppaca()->idle = 0;
@@ -137,6 +144,7 @@ static void pseries_mach_cpu_die(void)
if (get_preferred_offline_state(cpu) == CPU_STATE_ONLINE) {
unregister_slb_shadow(hwcpu);
+ hard_irq_disable();
/*
* Call to start_secondary_resume() will not return.
* Kernel stack will be reset and start_secondary()
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c
index 2372c609fa2..9a432de363b 100644
--- a/arch/powerpc/platforms/pseries/hotplug-memory.c
+++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
@@ -72,6 +72,7 @@ unsigned long memory_block_size_bytes(void)
return get_memblock_size();
}
+#ifdef CONFIG_MEMORY_HOTREMOVE
static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size)
{
unsigned long start, start_pfn;
@@ -153,6 +154,17 @@ static int pseries_remove_memory(struct device_node *np)
ret = pseries_remove_memblock(base, lmb_size);
return ret;
}
+#else
+static inline int pseries_remove_memblock(unsigned long base,
+ unsigned int memblock_size)
+{
+ return -EOPNOTSUPP;
+}
+static inline int pseries_remove_memory(struct device_node *np)
+{
+ return -EOPNOTSUPP;
+}
+#endif /* CONFIG_MEMORY_HOTREMOVE */
static int pseries_add_memory(struct device_node *np)
{
diff --git a/arch/powerpc/platforms/pseries/hvCall_inst.c b/arch/powerpc/platforms/pseries/hvCall_inst.c
index c9311cfdfca..cf4e7736e4f 100644
--- a/arch/powerpc/platforms/pseries/hvCall_inst.c
+++ b/arch/powerpc/platforms/pseries/hvCall_inst.c
@@ -86,7 +86,7 @@ static int hcall_inst_seq_open(struct inode *inode, struct file *file)
rc = seq_open(file, &hcall_inst_seq_ops);
seq = file->private_data;
- seq->private = file->f_path.dentry->d_inode->i_private;
+ seq->private = file_inode(file)->i_private;
return rc;
}
diff --git a/arch/powerpc/platforms/pseries/hvcserver.c b/arch/powerpc/platforms/pseries/hvcserver.c
index fcf4b4cbeaf..4557e91626c 100644
--- a/arch/powerpc/platforms/pseries/hvcserver.c
+++ b/arch/powerpc/platforms/pseries/hvcserver.c
@@ -23,6 +23,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <asm/hvcall.h>
#include <asm/hvcserver.h>
@@ -188,9 +189,9 @@ int hvcs_get_partner_info(uint32_t unit_address, struct list_head *head,
= (unsigned int)last_p_partition_ID;
/* copy the Null-term char too */
- strncpy(&next_partner_info->location_code[0],
+ strlcpy(&next_partner_info->location_code[0],
(char *)&pi_buff[2],
- strlen((char *)&pi_buff[2]) + 1);
+ sizeof(next_partner_info->location_code));
list_add_tail(&(next_partner_info->node), head);
next_partner_info = NULL;
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index e2685badb5d..86ae364900d 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -382,6 +382,7 @@ static int tce_clearrange_multi_pSeriesLP(unsigned long start_pfn,
rc = plpar_tce_stuff((u64)be32_to_cpu(maprange->liobn),
dma_offset,
0, limit);
+ next += limit * tce_size;
num_tce -= limit;
} while (num_tce > 0 && !rc);
@@ -786,33 +787,68 @@ static u64 find_existing_ddw(struct device_node *pdn)
return dma_addr;
}
+static void __restore_default_window(struct eeh_dev *edev,
+ u32 ddw_restore_token)
+{
+ u32 cfg_addr;
+ u64 buid;
+ int ret;
+
+ /*
+ * Get the config address and phb buid of the PE window.
+ * Rely on eeh to retrieve this for us.
+ * Retrieve them from the pci device, not the node with the
+ * dma-window property
+ */
+ cfg_addr = edev->config_addr;
+ if (edev->pe_config_addr)
+ cfg_addr = edev->pe_config_addr;
+ buid = edev->phb->buid;
+
+ do {
+ ret = rtas_call(ddw_restore_token, 3, 1, NULL, cfg_addr,
+ BUID_HI(buid), BUID_LO(buid));
+ } while (rtas_busy_delay(ret));
+ pr_info("ibm,reset-pe-dma-windows(%x) %x %x %x returned %d\n",
+ ddw_restore_token, cfg_addr, BUID_HI(buid), BUID_LO(buid), ret);
+}
+
static int find_existing_ddw_windows(void)
{
- int len;
struct device_node *pdn;
- struct direct_window *window;
const struct dynamic_dma_window_prop *direct64;
+ const u32 *ddw_extensions;
if (!firmware_has_feature(FW_FEATURE_LPAR))
return 0;
for_each_node_with_property(pdn, DIRECT64_PROPNAME) {
- direct64 = of_get_property(pdn, DIRECT64_PROPNAME, &len);
+ direct64 = of_get_property(pdn, DIRECT64_PROPNAME, NULL);
if (!direct64)
continue;
- window = kzalloc(sizeof(*window), GFP_KERNEL);
- if (!window || len < sizeof(struct dynamic_dma_window_prop)) {
- kfree(window);
- remove_ddw(pdn);
- continue;
- }
+ /*
+ * We need to ensure the IOMMU table is active when we
+ * return from the IOMMU setup so that the common code
+ * can clear the table or find the holes. To that end,
+ * first, remove any existing DDW configuration.
+ */
+ remove_ddw(pdn);
- window->device = pdn;
- window->prop = direct64;
- spin_lock(&direct_window_list_lock);
- list_add(&window->list, &direct_window_list);
- spin_unlock(&direct_window_list_lock);
+ /*
+ * Second, if we are running on a new enough level of
+ * firmware where the restore API is present, use it to
+ * restore the 32-bit window, which was removed in
+ * create_ddw.
+ * If the API is not present, then create_ddw couldn't
+ * have removed the 32-bit window in the first place, so
+ * removing the DDW configuration should be sufficient.
+ */
+ ddw_extensions = of_get_property(pdn, "ibm,ddw-extensions",
+ NULL);
+ if (ddw_extensions && ddw_extensions[0] > 0)
+ __restore_default_window(of_node_to_eeh_dev(pdn),
+ ddw_extensions[1]);
}
return 0;
@@ -883,33 +919,17 @@ static int create_ddw(struct pci_dev *dev, const u32 *ddw_avail,
}
static void restore_default_window(struct pci_dev *dev,
- u32 ddw_restore_token, unsigned long liobn)
+ u32 ddw_restore_token)
{
- struct eeh_dev *edev;
- u32 cfg_addr;
- u64 buid;
- int ret;
+ __restore_default_window(pci_dev_to_eeh_dev(dev), ddw_restore_token);
+}
- /*
- * Get the config address and phb buid of the PE window.
- * Rely on eeh to retrieve this for us.
- * Retrieve them from the pci device, not the node with the
- * dma-window property
- */
- edev = pci_dev_to_eeh_dev(dev);
- cfg_addr = edev->config_addr;
- if (edev->pe_config_addr)
- cfg_addr = edev->pe_config_addr;
- buid = edev->phb->buid;
+struct failed_ddw_pdn {
+ struct device_node *pdn;
+ struct list_head list;
+};
- do {
- ret = rtas_call(ddw_restore_token, 3, 1, NULL, cfg_addr,
- BUID_HI(buid), BUID_LO(buid));
- } while (rtas_busy_delay(ret));
- dev_info(&dev->dev,
- "ibm,reset-pe-dma-windows(%x) %x %x %x returned %d\n",
- ddw_restore_token, cfg_addr, BUID_HI(buid), BUID_LO(buid), ret);
-}
+static LIST_HEAD(failed_ddw_pdn_list);
/*
* If the PE supports dynamic dma windows, and there is space for a table
@@ -938,6 +958,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
struct dynamic_dma_window_prop *ddwprop;
const void *dma_window = NULL;
unsigned long liobn, offset, size;
+ struct failed_ddw_pdn *fpdn;
mutex_lock(&direct_window_init_mutex);
@@ -946,6 +967,18 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
goto out_unlock;
/*
+ * If we already went through this for a previous function of
+ * the same device and failed, we don't want to muck with the
+ * DMA window again, as it will race with in-flight operations
+ * and can lead to EEHs. The above mutex protects access to the
+ * list.
+ */
+ list_for_each_entry(fpdn, &failed_ddw_pdn_list, list) {
+ if (!strcmp(fpdn->pdn->full_name, pdn->full_name))
+ goto out_unlock;
+ }
+
+ /*
* the ibm,ddw-applicable property holds the tokens for:
* ibm,query-pe-dma-window
* ibm,create-pe-dma-window
@@ -1099,7 +1132,13 @@ out_free_prop:
out_restore_window:
if (ddw_restore_token)
- restore_default_window(dev, ddw_restore_token, liobn);
+ restore_default_window(dev, ddw_restore_token);
+
+ fpdn = kzalloc(sizeof(*fpdn), GFP_KERNEL);
+ if (!fpdn)
+ goto out_unlock;
+ fpdn->pdn = pdn;
+ list_add(&fpdn->list, &failed_ddw_pdn_list);
out_unlock:
mutex_unlock(&direct_window_init_mutex);
@@ -1295,6 +1334,7 @@ static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long acti
switch (action) {
case OF_RECONFIG_DETACH_NODE:
+ remove_ddw(np);
if (pci && pci->iommu_table)
iommu_free_table(pci->iommu_table, np->full_name);
@@ -1307,16 +1347,6 @@ static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long acti
}
}
spin_unlock(&direct_window_list_lock);
-
- /*
- * Because the notifier runs after isolation of the
- * slot, we are guaranteed any DMA window has already
- * been revoked and the TCEs have been marked invalid,
- * so we don't need a call to remove_ddw(np). However,
- * if an additional notifier action is added before the
- * isolate call, we should update this code for
- * completeness with such a call.
- */
break;
default:
err = NOTIFY_DONE;
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 0da39fed355..6d62072a7d5 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -109,7 +109,7 @@ void vpa_init(int cpu)
static long pSeries_lpar_hpte_insert(unsigned long hpte_group,
unsigned long vpn, unsigned long pa,
unsigned long rflags, unsigned long vflags,
- int psize, int ssize)
+ int psize, int apsize, int ssize)
{
unsigned long lpar_rc;
unsigned long flags;
@@ -121,8 +121,8 @@ static long pSeries_lpar_hpte_insert(unsigned long hpte_group,
"pa=%016lx, rflags=%lx, vflags=%lx, psize=%d)\n",
hpte_group, vpn, pa, rflags, vflags, psize);
- hpte_v = hpte_encode_v(vpn, psize, ssize) | vflags | HPTE_V_VALID;
- hpte_r = hpte_encode_r(pa, psize) | rflags;
+ hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID;
+ hpte_r = hpte_encode_r(pa, psize, apsize) | rflags;
if (!(vflags & HPTE_V_BOLTED))
pr_devel(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r);
@@ -155,7 +155,7 @@ static long pSeries_lpar_hpte_insert(unsigned long hpte_group,
*/
if (unlikely(lpar_rc != H_SUCCESS)) {
if (!(vflags & HPTE_V_BOLTED))
- pr_devel(" lpar err %lu\n", lpar_rc);
+ pr_devel(" lpar err %ld\n", lpar_rc);
return -2;
}
if (!(vflags & HPTE_V_BOLTED))
@@ -186,7 +186,13 @@ static long pSeries_lpar_hpte_remove(unsigned long hpte_group)
(0x1UL << 4), &dummy1, &dummy2);
if (lpar_rc == H_SUCCESS)
return i;
- BUG_ON(lpar_rc != H_NOT_FOUND);
+
+ /*
+ * The test for adjunct partition is performed before the
+ * ANDCOND test. H_RESOURCE may be returned, so we need to
+ * check for that as well.
+ */
+ BUG_ON(lpar_rc != H_NOT_FOUND && lpar_rc != H_RESOURCE);
slot_offset++;
slot_offset &= 0x7;
diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c
index 6573808cc5f..3d01eee9ffb 100644
--- a/arch/powerpc/platforms/pseries/mobility.c
+++ b/arch/powerpc/platforms/pseries/mobility.c
@@ -37,14 +37,16 @@ struct update_props_workarea {
#define UPDATE_DT_NODE 0x02000000
#define ADD_DT_NODE 0x03000000
-static int mobility_rtas_call(int token, char *buf)
+#define MIGRATION_SCOPE (1)
+
+static int mobility_rtas_call(int token, char *buf, s32 scope)
{
int rc;
spin_lock(&rtas_data_buf_lock);
memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
- rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, 1);
+ rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope);
memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
spin_unlock(&rtas_data_buf_lock);
@@ -123,7 +125,7 @@ static int update_dt_property(struct device_node *dn, struct property **prop,
return 0;
}
-static int update_dt_node(u32 phandle)
+static int update_dt_node(u32 phandle, s32 scope)
{
struct update_props_workarea *upwa;
struct device_node *dn;
@@ -132,6 +134,7 @@ static int update_dt_node(u32 phandle)
char *prop_data;
char *rtas_buf;
int update_properties_token;
+ u32 vd;
update_properties_token = rtas_token("ibm,update-properties");
if (update_properties_token == RTAS_UNKNOWN_SERVICE)
@@ -151,19 +154,31 @@ static int update_dt_node(u32 phandle)
upwa->phandle = phandle;
do {
- rc = mobility_rtas_call(update_properties_token, rtas_buf);
+ rc = mobility_rtas_call(update_properties_token, rtas_buf,
+ scope);
if (rc < 0)
break;
prop_data = rtas_buf + sizeof(*upwa);
- for (i = 0; i < upwa->nprops; i++) {
+ /* The first element of the buffer is the path of the node
+ * being updated in the form of a 8 byte string length
+ * followed by the string. Skip past this to get to the
+ * properties being updated.
+ */
+ vd = *prop_data++;
+ prop_data += vd;
+
+ /* The path we skipped over is counted as one of the elements
+ * returned so start counting at one.
+ */
+ for (i = 1; i < upwa->nprops; i++) {
char *prop_name;
- u32 vd;
- prop_name = prop_data + 1;
+ prop_name = prop_data;
prop_data += strlen(prop_name) + 1;
- vd = *prop_data++;
+ vd = *(u32 *)prop_data;
+ prop_data += sizeof(vd);
switch (vd) {
case 0x00000000:
@@ -219,7 +234,7 @@ static int add_dt_node(u32 parent_phandle, u32 drc_index)
return rc;
}
-static int pseries_devicetree_update(void)
+int pseries_devicetree_update(s32 scope)
{
char *rtas_buf;
u32 *data;
@@ -235,7 +250,7 @@ static int pseries_devicetree_update(void)
return -ENOMEM;
do {
- rc = mobility_rtas_call(update_nodes_token, rtas_buf);
+ rc = mobility_rtas_call(update_nodes_token, rtas_buf, scope);
if (rc && rc != 1)
break;
@@ -256,7 +271,7 @@ static int pseries_devicetree_update(void)
delete_dt_node(phandle);
break;
case UPDATE_DT_NODE:
- update_dt_node(phandle);
+ update_dt_node(phandle, scope);
break;
case ADD_DT_NODE:
drc_index = *data++;
@@ -276,7 +291,7 @@ void post_mobility_fixup(void)
int rc;
int activate_fw_token;
- rc = pseries_devicetree_update();
+ rc = pseries_devicetree_update(MIGRATION_SCOPE);
if (rc) {
printk(KERN_ERR "Initial post-mobility device tree update "
"failed: %d\n", rc);
@@ -292,7 +307,7 @@ void post_mobility_fixup(void)
rc = rtas_call(activate_fw_token, 0, 1, NULL);
if (!rc) {
- rc = pseries_devicetree_update();
+ rc = pseries_devicetree_update(MIGRATION_SCOPE);
if (rc)
printk(KERN_ERR "Secondary post-mobility device tree "
"update failed: %d\n", rc);
diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c
index e5b08472313..420524e6f8c 100644
--- a/arch/powerpc/platforms/pseries/msi.c
+++ b/arch/powerpc/platforms/pseries/msi.c
@@ -24,6 +24,7 @@ static int query_token, change_token;
#define RTAS_RESET_FN 2
#define RTAS_CHANGE_MSI_FN 3
#define RTAS_CHANGE_MSIX_FN 4
+#define RTAS_CHANGE_32MSI_FN 5
static struct pci_dn *get_pdn(struct pci_dev *pdev)
{
@@ -58,7 +59,8 @@ static int rtas_change_msi(struct pci_dn *pdn, u32 func, u32 num_irqs)
seq_num = 1;
do {
- if (func == RTAS_CHANGE_MSI_FN || func == RTAS_CHANGE_MSIX_FN)
+ if (func == RTAS_CHANGE_MSI_FN || func == RTAS_CHANGE_MSIX_FN ||
+ func == RTAS_CHANGE_32MSI_FN)
rc = rtas_call(change_token, 6, 4, rtas_ret, addr,
BUID_HI(buid), BUID_LO(buid),
func, num_irqs, seq_num);
@@ -426,9 +428,12 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
*/
again:
if (type == PCI_CAP_ID_MSI) {
- rc = rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, nvec);
+ if (pdn->force_32bit_msi)
+ rc = rtas_change_msi(pdn, RTAS_CHANGE_32MSI_FN, nvec);
+ else
+ rc = rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, nvec);
- if (rc < 0) {
+ if (rc < 0 && !pdn->force_32bit_msi) {
pr_debug("rtas_msi: trying the old firmware call.\n");
rc = rtas_change_msi(pdn, RTAS_CHANGE_FN, nvec);
}
@@ -512,3 +517,13 @@ static int rtas_msi_init(void)
return 0;
}
arch_initcall(rtas_msi_init);
+
+static void quirk_radeon(struct pci_dev *dev)
+{
+ struct pci_dn *pdn = get_pdn(dev);
+
+ if (pdn)
+ pdn->force_32bit_msi = 1;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x68f2, quirk_radeon);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0xaa68, quirk_radeon);
diff --git a/arch/powerpc/platforms/pseries/pci.c b/arch/powerpc/platforms/pseries/pci.c
index 56b864d777e..5f93856cdf4 100644
--- a/arch/powerpc/platforms/pseries/pci.c
+++ b/arch/powerpc/platforms/pseries/pci.c
@@ -40,7 +40,8 @@ void pcibios_name_device(struct pci_dev *dev)
*/
dn = pci_device_to_OF_node(dev);
if (dn) {
- const char *loc_code = of_get_property(dn, "ibm,loc-code", 0);
+ const char *loc_code = of_get_property(dn, "ibm,loc-code",
+ NULL);
if (loc_code) {
int loc_len = strlen(loc_code);
if (loc_len < sizeof(dev->dev.name)) {
@@ -107,3 +108,56 @@ static void fixup_winbond_82c105(struct pci_dev* dev)
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105,
fixup_winbond_82c105);
+
+int pseries_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ struct device_node *dn, *pdn;
+ struct pci_bus *bus;
+ const uint32_t *pcie_link_speed_stats;
+
+ bus = bridge->bus;
+
+ dn = pcibios_get_phb_of_node(bus);
+ if (!dn)
+ return 0;
+
+ for (pdn = dn; pdn != NULL; pdn = of_get_next_parent(pdn)) {
+ pcie_link_speed_stats = (const uint32_t *) of_get_property(pdn,
+ "ibm,pcie-link-speed-stats", NULL);
+ if (pcie_link_speed_stats)
+ break;
+ }
+
+ of_node_put(pdn);
+
+ if (!pcie_link_speed_stats) {
+ pr_err("no ibm,pcie-link-speed-stats property\n");
+ return 0;
+ }
+
+ switch (pcie_link_speed_stats[0]) {
+ case 0x01:
+ bus->max_bus_speed = PCIE_SPEED_2_5GT;
+ break;
+ case 0x02:
+ bus->max_bus_speed = PCIE_SPEED_5_0GT;
+ break;
+ default:
+ bus->max_bus_speed = PCI_SPEED_UNKNOWN;
+ break;
+ }
+
+ switch (pcie_link_speed_stats[1]) {
+ case 0x01:
+ bus->cur_bus_speed = PCIE_SPEED_2_5GT;
+ break;
+ case 0x02:
+ bus->cur_bus_speed = PCIE_SPEED_5_0GT;
+ break;
+ default:
+ bus->cur_bus_speed = PCI_SPEED_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h
index e6cc34a6705..f35787b6a5e 100644
--- a/arch/powerpc/platforms/pseries/plpar_wrappers.h
+++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h
@@ -2,6 +2,7 @@
#define _PSERIES_PLPAR_WRAPPERS_H
#include <linux/string.h>
+#include <linux/irqflags.h>
#include <asm/hvcall.h>
#include <asm/paca.h>
@@ -41,7 +42,14 @@ static inline long extended_cede_processor(unsigned long latency_hint)
u8 old_latency_hint = get_cede_latency_hint();
set_cede_latency_hint(latency_hint);
+
rc = cede_processor();
+#ifdef CONFIG_TRACE_IRQFLAGS
+ /* Ensure that H_CEDE returns with IRQs on */
+ if (WARN_ON(!(mfmsr() & MSR_EE)))
+ __hard_irq_enable();
+#endif
+
set_cede_latency_hint(old_latency_hint);
return rc;
@@ -50,40 +58,39 @@ static inline long extended_cede_processor(unsigned long latency_hint)
static inline long vpa_call(unsigned long flags, unsigned long cpu,
unsigned long vpa)
{
- /* flags are in bits 16-18 (counting from most significant bit) */
- flags = flags << (63 - 18);
+ flags = flags << H_VPA_FUNC_SHIFT;
return plpar_hcall_norets(H_REGISTER_VPA, flags, cpu, vpa);
}
static inline long unregister_vpa(unsigned long cpu)
{
- return vpa_call(0x5, cpu, 0);
+ return vpa_call(H_VPA_DEREG_VPA, cpu, 0);
}
static inline long register_vpa(unsigned long cpu, unsigned long vpa)
{
- return vpa_call(0x1, cpu, vpa);
+ return vpa_call(H_VPA_REG_VPA, cpu, vpa);
}
static inline long unregister_slb_shadow(unsigned long cpu)
{
- return vpa_call(0x7, cpu, 0);
+ return vpa_call(H_VPA_DEREG_SLB, cpu, 0);
}
static inline long register_slb_shadow(unsigned long cpu, unsigned long vpa)
{
- return vpa_call(0x3, cpu, vpa);
+ return vpa_call(H_VPA_REG_SLB, cpu, vpa);
}
static inline long unregister_dtl(unsigned long cpu)
{
- return vpa_call(0x6, cpu, 0);
+ return vpa_call(H_VPA_DEREG_DTL, cpu, 0);
}
static inline long register_dtl(unsigned long cpu, unsigned long vpa)
{
- return vpa_call(0x2, cpu, vpa);
+ return vpa_call(H_VPA_REG_DTL, cpu, vpa);
}
static inline long plpar_page_set_loaned(unsigned long vpa)
@@ -304,4 +311,14 @@ static inline long disable_reloc_on_exceptions(void) {
return plpar_set_mode(0, 3, 0, 0);
}
+static inline long plapr_set_ciabr(unsigned long ciabr)
+{
+ return plpar_set_mode(0, 1, ciabr, 0);
+}
+
+static inline long plapr_set_watchpoint0(unsigned long dawr0, unsigned long dawrx0)
+{
+ return plpar_set_mode(0, 2, dawr0, dawrx0);
+}
+
#endif /* _PSERIES_PLPAR_WRAPPERS_H */
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index 4d806b41960..4644efa0694 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -23,8 +23,8 @@
#include "pseries.h"
struct cpuidle_driver pseries_idle_driver = {
- .name = "pseries_idle",
- .owner = THIS_MODULE,
+ .name = "pseries_idle",
+ .owner = THIS_MODULE,
};
#define MAX_IDLE_STATE_COUNT 2
@@ -33,10 +33,8 @@ static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
static struct cpuidle_device __percpu *pseries_cpuidle_devices;
static struct cpuidle_state *cpuidle_state_table;
-static inline void idle_loop_prolog(unsigned long *in_purr, ktime_t *kt_before)
+static inline void idle_loop_prolog(unsigned long *in_purr)
{
-
- *kt_before = ktime_get();
*in_purr = mfspr(SPRN_PURR);
/*
* Indicate to the HV that we are idle. Now would be
@@ -45,12 +43,10 @@ static inline void idle_loop_prolog(unsigned long *in_purr, ktime_t *kt_before)
get_lppaca()->idle = 1;
}
-static inline s64 idle_loop_epilog(unsigned long in_purr, ktime_t kt_before)
+static inline void idle_loop_epilog(unsigned long in_purr)
{
get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
get_lppaca()->idle = 0;
-
- return ktime_to_us(ktime_sub(ktime_get(), kt_before));
}
static int snooze_loop(struct cpuidle_device *dev,
@@ -58,10 +54,9 @@ static int snooze_loop(struct cpuidle_device *dev,
int index)
{
unsigned long in_purr;
- ktime_t kt_before;
int cpu = dev->cpu;
- idle_loop_prolog(&in_purr, &kt_before);
+ idle_loop_prolog(&in_purr);
local_irq_enable();
set_thread_flag(TIF_POLLING_NRFLAG);
@@ -75,8 +70,8 @@ static int snooze_loop(struct cpuidle_device *dev,
clear_thread_flag(TIF_POLLING_NRFLAG);
smp_mb();
- dev->last_residency =
- (int)idle_loop_epilog(in_purr, kt_before);
+ idle_loop_epilog(in_purr);
+
return index;
}
@@ -102,9 +97,8 @@ static int dedicated_cede_loop(struct cpuidle_device *dev,
int index)
{
unsigned long in_purr;
- ktime_t kt_before;
- idle_loop_prolog(&in_purr, &kt_before);
+ idle_loop_prolog(&in_purr);
get_lppaca()->donate_dedicated_cpu = 1;
ppc64_runlatch_off();
@@ -112,8 +106,9 @@ static int dedicated_cede_loop(struct cpuidle_device *dev,
check_and_cede_processor();
get_lppaca()->donate_dedicated_cpu = 0;
- dev->last_residency =
- (int)idle_loop_epilog(in_purr, kt_before);
+
+ idle_loop_epilog(in_purr);
+
return index;
}
@@ -122,9 +117,8 @@ static int shared_cede_loop(struct cpuidle_device *dev,
int index)
{
unsigned long in_purr;
- ktime_t kt_before;
- idle_loop_prolog(&in_purr, &kt_before);
+ idle_loop_prolog(&in_purr);
/*
* Yield the processor to the hypervisor. We return if
@@ -135,8 +129,8 @@ static int shared_cede_loop(struct cpuidle_device *dev,
*/
check_and_cede_processor();
- dev->last_residency =
- (int)idle_loop_epilog(in_purr, kt_before);
+ idle_loop_epilog(in_purr);
+
return index;
}
diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h
index 9a3dda07566..c2a3a258001 100644
--- a/arch/powerpc/platforms/pseries/pseries.h
+++ b/arch/powerpc/platforms/pseries/pseries.h
@@ -19,7 +19,10 @@ extern void request_event_sources_irqs(struct device_node *np,
#include <linux/of.h>
-extern void __init fw_feature_init(const char *hypertas, unsigned long len);
+extern void __init fw_hypertas_feature_init(const char *hypertas,
+ unsigned long len);
+extern void __init fw_vec5_feature_init(const char *hypertas,
+ unsigned long len);
struct pt_regs;
@@ -60,4 +63,8 @@ extern int dlpar_detach_node(struct device_node *);
/* Snooze Delay, pseries_idle */
DECLARE_PER_CPU(long, smt_snooze_delay);
+/* PCI root bridge prepare function override for pseries */
+struct pci_host_bridge;
+int pseries_root_bridge_prepare(struct pci_host_bridge *bridge);
+
#endif /* _PSERIES_PSERIES_H */
diff --git a/arch/powerpc/platforms/pseries/pseries_energy.c b/arch/powerpc/platforms/pseries/pseries_energy.c
index af281dce510..a91e6dadda2 100644
--- a/arch/powerpc/platforms/pseries/pseries_energy.c
+++ b/arch/powerpc/platforms/pseries/pseries_energy.c
@@ -21,6 +21,7 @@
#include <asm/cputhreads.h>
#include <asm/page.h>
#include <asm/hvcall.h>
+#include <asm/firmware.h>
#define MODULE_VERS "1.0"
@@ -32,40 +33,6 @@ static int sysfs_entries;
/* Helper routines */
-/*
- * Routine to detect firmware support for hcall
- * return 1 if H_BEST_ENERGY is supported
- * else return 0
- */
-
-static int check_for_h_best_energy(void)
-{
- struct device_node *rtas = NULL;
- const char *hypertas, *s;
- int length;
- int rc = 0;
-
- rtas = of_find_node_by_path("/rtas");
- if (!rtas)
- return 0;
-
- hypertas = of_get_property(rtas, "ibm,hypertas-functions", &length);
- if (!hypertas) {
- of_node_put(rtas);
- return 0;
- }
-
- /* hypertas will have list of strings with hcall names */
- for (s = hypertas; s < hypertas + length; s += strlen(s) + 1) {
- if (!strncmp("hcall-best-energy-1", s, 19)) {
- rc = 1; /* Found the string */
- break;
- }
- }
- of_node_put(rtas);
- return rc;
-}
-
/* Helper Routines to convert between drc_index to cpu numbers */
static u32 cpu_to_drc_index(int cpu)
@@ -262,7 +229,7 @@ static int __init pseries_energy_init(void)
int cpu, err;
struct device *cpu_dev;
- if (!check_for_h_best_energy()) {
+ if (!firmware_has_feature(FW_FEATURE_BEST_ENERGY)) {
printk(KERN_INFO "Hypercall H_BEST_ENERGY not supported\n");
return 0;
}
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c
index d6491bd481d..f93cdf55628 100644
--- a/arch/powerpc/platforms/pseries/reconfig.c
+++ b/arch/powerpc/platforms/pseries/reconfig.c
@@ -452,7 +452,7 @@ static int proc_ppc64_create_ofdt(void)
ent = proc_create("powerpc/ofdt", S_IWUSR, NULL, &ofdt_fops);
if (ent)
- ent->size = 0;
+ proc_set_size(ent, 0);
return 0;
}
diff --git a/arch/powerpc/platforms/pseries/scanlog.c b/arch/powerpc/platforms/pseries/scanlog.c
index 554457294a2..b502ab61aaf 100644
--- a/arch/powerpc/platforms/pseries/scanlog.c
+++ b/arch/powerpc/platforms/pseries/scanlog.c
@@ -41,21 +41,16 @@
static unsigned int ibm_scan_log_dump; /* RTAS token */
-static struct proc_dir_entry *proc_ppc64_scan_log_dump; /* The proc file */
+static unsigned int *scanlog_buffer; /* The data buffer */
static ssize_t scanlog_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- struct inode * inode = file->f_path.dentry->d_inode;
- struct proc_dir_entry *dp;
- unsigned int *data;
+ unsigned int *data = scanlog_buffer;
int status;
unsigned long len, off;
unsigned int wait_time;
- dp = PDE(inode);
- data = (unsigned int *)dp->data;
-
if (count > RTAS_DATA_BUF_SIZE)
count = RTAS_DATA_BUF_SIZE;
@@ -139,8 +134,7 @@ static ssize_t scanlog_write(struct file * file, const char __user * buf,
static int scanlog_open(struct inode * inode, struct file * file)
{
- struct proc_dir_entry *dp = PDE(inode);
- unsigned int *data = (unsigned int *)dp->data;
+ unsigned int *data = scanlog_buffer;
if (data[0] != 0) {
/* This imperfect test stops a second copy of the
@@ -156,11 +150,9 @@ static int scanlog_open(struct inode * inode, struct file * file)
static int scanlog_release(struct inode * inode, struct file * file)
{
- struct proc_dir_entry *dp = PDE(inode);
- unsigned int *data = (unsigned int *)dp->data;
+ unsigned int *data = scanlog_buffer;
data[0] = 0;
-
return 0;
}
@@ -176,7 +168,6 @@ 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");
@@ -184,29 +175,24 @@ static int __init scanlog_init(void)
return -ENODEV;
/* Ideally we could allocate a buffer < 4G */
- data = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
- if (!data)
+ scanlog_buffer = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
+ if (!scanlog_buffer)
goto err;
- ent = proc_create_data("powerpc/rtas/scan-log-dump", S_IRUSR, NULL,
- &scanlog_fops, data);
+ ent = proc_create("powerpc/rtas/scan-log-dump", S_IRUSR, NULL,
+ &scanlog_fops);
if (!ent)
goto err;
-
- proc_ppc64_scan_log_dump = ent;
-
return 0;
err:
- kfree(data);
+ kfree(scanlog_buffer);
return err;
}
static void __exit scanlog_cleanup(void)
{
- if (proc_ppc64_scan_log_dump) {
- kfree(proc_ppc64_scan_log_dump->data);
- remove_proc_entry("scan-log-dump", proc_ppc64_scan_log_dump->parent);
- }
+ remove_proc_entry("powerpc/rtas/scan-log-dump", NULL);
+ kfree(scanlog_buffer);
}
module_init(scanlog_init);
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index ca55882465d..c11c8238797 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -65,6 +65,7 @@
#include <asm/smp.h>
#include <asm/firmware.h>
#include <asm/eeh.h>
+#include <asm/reg.h>
#include "plpar_wrappers.h"
#include "pseries.h"
@@ -281,7 +282,7 @@ static struct notifier_block pci_dn_reconfig_nb = {
struct kmem_cache *dtl_cache;
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
/*
* Allocate space for the dispatch trace log for all possible cpus
* and register the buffers with the hypervisor. This is used for
@@ -332,12 +333,12 @@ static int alloc_dispatch_logs(void)
return 0;
}
-#else /* !CONFIG_VIRT_CPU_ACCOUNTING */
+#else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
static inline int alloc_dispatch_logs(void)
{
return 0;
}
-#endif /* CONFIG_VIRT_CPU_ACCOUNTING */
+#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
static int alloc_dispatch_log_kmem_cache(void)
{
@@ -375,7 +376,7 @@ static void pSeries_idle(void)
* to ever be a problem in practice we can move this into a kernel thread to
* finish off the process later in boot.
*/
-static int __init pSeries_enable_reloc_on_exc(void)
+long pSeries_enable_reloc_on_exc(void)
{
long rc;
unsigned int delay, total_delay = 0;
@@ -397,9 +398,9 @@ static int __init pSeries_enable_reloc_on_exc(void)
mdelay(delay);
}
}
+EXPORT_SYMBOL(pSeries_enable_reloc_on_exc);
-#ifdef CONFIG_KEXEC
-static long pSeries_disable_reloc_on_exc(void)
+long pSeries_disable_reloc_on_exc(void)
{
long rc;
@@ -410,7 +411,9 @@ static long pSeries_disable_reloc_on_exc(void)
mdelay(get_longbusy_msecs(rc));
}
}
+EXPORT_SYMBOL(pSeries_disable_reloc_on_exc);
+#ifdef CONFIG_KEXEC
static void pSeries_machine_kexec(struct kimage *image)
{
long rc;
@@ -463,6 +466,8 @@ static void __init pSeries_setup_arch(void)
else
ppc_md.enable_pmcs = power4_enable_pmcs;
+ ppc_md.pcibios_root_bridge_prepare = pseries_root_bridge_prepare;
+
if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
long rc;
if ((rc = pSeries_enable_reloc_on_exc()) != H_SUCCESS) {
@@ -498,6 +503,14 @@ static int pseries_set_xdabr(unsigned long dabr, unsigned long dabrx)
return plpar_hcall_norets(H_SET_XDABR, dabr, dabrx);
}
+static int pseries_set_dawr(unsigned long dawr, unsigned long dawrx)
+{
+ /* PAPR says we can't set HYP */
+ dawrx &= ~DAWRX_HYP;
+
+ return plapr_set_watchpoint0(dawr, dawrx);
+}
+
#define CMO_CHARACTERISTICS_TOKEN 44
#define CMO_MAXLENGTH 1026
@@ -604,6 +617,9 @@ static void __init pSeries_init_early(void)
else if (firmware_has_feature(FW_FEATURE_DABR))
ppc_md.set_dabr = pseries_set_dabr;
+ if (firmware_has_feature(FW_FEATURE_SET_MODE))
+ ppc_md.set_dawr = pseries_set_dawr;
+
pSeries_cmo_feature_init();
iommu_init_early_pSeries();
@@ -614,25 +630,39 @@ static void __init pSeries_init_early(void)
* Called very early, MMU is off, device-tree isn't unflattened
*/
-static int __init pSeries_probe_hypertas(unsigned long node,
- const char *uname, int depth,
- void *data)
+static int __init pseries_probe_fw_features(unsigned long node,
+ const char *uname, int depth,
+ void *data)
{
- const char *hypertas;
+ const char *prop;
unsigned long len;
+ static int hypertas_found;
+ static int vec5_found;
- if (depth != 1 ||
- (strcmp(uname, "rtas") != 0 && strcmp(uname, "rtas@0") != 0))
+ if (depth != 1)
return 0;
- hypertas = of_get_flat_dt_prop(node, "ibm,hypertas-functions", &len);
- if (!hypertas)
- return 1;
+ if (!strcmp(uname, "rtas") || !strcmp(uname, "rtas@0")) {
+ prop = of_get_flat_dt_prop(node, "ibm,hypertas-functions",
+ &len);
+ if (prop) {
+ powerpc_firmware_features |= FW_FEATURE_LPAR;
+ fw_hypertas_feature_init(prop, len);
+ }
- powerpc_firmware_features |= FW_FEATURE_LPAR;
- fw_feature_init(hypertas, len);
+ hypertas_found = 1;
+ }
- return 1;
+ if (!strcmp(uname, "chosen")) {
+ prop = of_get_flat_dt_prop(node, "ibm,architecture-vec-5",
+ &len);
+ if (prop)
+ fw_vec5_feature_init(prop, len);
+
+ vec5_found = 1;
+ }
+
+ return hypertas_found && vec5_found;
}
static int __init pSeries_probe(void)
@@ -655,7 +685,7 @@ static int __init pSeries_probe(void)
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);
+ of_scan_flat_dt(pseries_probe_fw_features, NULL);
if (firmware_has_feature(FW_FEATURE_LPAR))
hpte_init_lpar();
diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c
index 80cd0be71e0..12bc8c3663a 100644
--- a/arch/powerpc/platforms/pseries/smp.c
+++ b/arch/powerpc/platforms/pseries/smp.c
@@ -42,6 +42,7 @@
#include <asm/vdso_datapage.h>
#include <asm/cputhreads.h>
#include <asm/xics.h>
+#include <asm/dbell.h>
#include "plpar_wrappers.h"
#include "pseries.h"
@@ -54,6 +55,11 @@
*/
static cpumask_var_t of_spin_mask;
+/*
+ * If we multiplex IPI mechanisms, store the appropriate XICS IPI mechanism here
+ */
+static void (*xics_cause_ipi)(int cpu, unsigned long data);
+
/* Query where a cpu is now. Return codes #defined in plpar_wrappers.h */
int smp_query_cpu_stopped(unsigned int pcpu)
{
@@ -137,6 +143,8 @@ static void smp_xics_setup_cpu(int cpu)
{
if (cpu != boot_cpuid)
xics_setup_cpu();
+ if (cpu_has_feature(CPU_FTR_DBELL))
+ doorbell_setup_this_cpu();
if (firmware_has_feature(FW_FEATURE_SPLPAR))
vpa_init(cpu);
@@ -195,6 +203,27 @@ static int smp_pSeries_cpu_bootable(unsigned int nr)
return 1;
}
+/* Only used on systems that support multiple IPI mechanisms */
+static void pSeries_cause_ipi_mux(int cpu, unsigned long data)
+{
+ if (cpumask_test_cpu(cpu, cpu_sibling_mask(smp_processor_id())))
+ doorbell_cause_ipi(cpu, data);
+ else
+ xics_cause_ipi(cpu, data);
+}
+
+static __init int pSeries_smp_probe(void)
+{
+ int ret = xics_smp_probe();
+
+ if (cpu_has_feature(CPU_FTR_DBELL)) {
+ xics_cause_ipi = smp_ops->cause_ipi;
+ smp_ops->cause_ipi = pSeries_cause_ipi_mux;
+ }
+
+ return ret;
+}
+
static struct smp_ops_t pSeries_mpic_smp_ops = {
.message_pass = smp_mpic_message_pass,
.probe = smp_mpic_probe,
@@ -204,8 +233,8 @@ static struct smp_ops_t pSeries_mpic_smp_ops = {
static struct smp_ops_t pSeries_xics_smp_ops = {
.message_pass = NULL, /* Use smp_muxed_ipi_message_pass */
- .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */
- .probe = xics_smp_probe,
+ .cause_ipi = NULL, /* Filled at runtime by pSeries_smp_probe() */
+ .probe = pSeries_smp_probe,
.kick_cpu = smp_pSeries_kick_cpu,
.setup_cpu = smp_xics_setup_cpu,
.cpu_bootable = smp_pSeries_cpu_bootable,