diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries')
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,  |