diff options
Diffstat (limited to 'arch/powerpc/platforms/powernv/setup.c')
| -rw-r--r-- | arch/powerpc/platforms/powernv/setup.c | 124 | 
1 files changed, 118 insertions, 6 deletions
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index e239dcfa224..d9b88fa7c5a 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -23,8 +23,11 @@  #include <linux/irq.h>  #include <linux/seq_file.h>  #include <linux/of.h> +#include <linux/of_fdt.h>  #include <linux/interrupt.h>  #include <linux/bug.h> +#include <linux/pci.h> +#include <linux/cpufreq.h>  #include <asm/machdep.h>  #include <asm/firmware.h> @@ -32,11 +35,14 @@  #include <asm/rtas.h>  #include <asm/opal.h>  #include <asm/kexec.h> +#include <asm/smp.h>  #include "powernv.h"  static void __init pnv_setup_arch(void)  { +	set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT); +  	/* Initialize SMP */  	pnv_smp_init(); @@ -96,11 +102,32 @@ static void pnv_show_cpuinfo(struct seq_file *m)  	of_node_put(root);  } +static void pnv_prepare_going_down(void) +{ +	/* +	 * Disable all notifiers from OPAL, we can't +	 * service interrupts anymore anyway +	 */ +	opal_notifier_disable(); + +	/* Soft disable interrupts */ +	local_irq_disable(); + +	/* +	 * Return secondary CPUs to firwmare if a flash update +	 * is pending otherwise we will get all sort of error +	 * messages about CPU being stuck etc.. This will also +	 * have the side effect of hard disabling interrupts so +	 * past this point, the kernel is effectively dead. +	 */ +	opal_flash_term_callback(); +} +  static void  __noreturn pnv_restart(char *cmd)  {  	long rc = OPAL_BUSY; -	opal_notifier_disable(); +	pnv_prepare_going_down();  	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {  		rc = opal_cec_reboot(); @@ -117,7 +144,7 @@ static void __noreturn pnv_power_off(void)  {  	long rc = OPAL_BUSY; -	opal_notifier_disable(); +	pnv_prepare_going_down();  	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {  		rc = opal_cec_power_down(0); @@ -139,34 +166,94 @@ static void pnv_progress(char *s, unsigned short hex)  {  } +static int pnv_dma_set_mask(struct device *dev, u64 dma_mask) +{ +	if (dev_is_pci(dev)) +		return pnv_pci_dma_set_mask(to_pci_dev(dev), dma_mask); +	return __dma_set_mask(dev, dma_mask); +} +  static void pnv_shutdown(void)  {  	/* Let the PCI code clear up IODA tables */  	pnv_pci_shutdown(); -	/* And unregister all OPAL interrupts so they don't fire -	 * up while we kexec +	/* +	 * Stop OPAL activity: Unregister all OPAL interrupts so they +	 * don't fire up while we kexec and make sure all potentially +	 * DMA'ing ops are complete (such as dump retrieval).  	 */  	opal_shutdown();  }  #ifdef CONFIG_KEXEC +static void pnv_kexec_wait_secondaries_down(void) +{ +	int my_cpu, i, notified = -1; + +	my_cpu = get_cpu(); + +	for_each_online_cpu(i) { +		uint8_t status; +		int64_t rc; + +		if (i == my_cpu) +			continue; + +		for (;;) { +			rc = opal_query_cpu_status(get_hard_smp_processor_id(i), +						   &status); +			if (rc != OPAL_SUCCESS || status != OPAL_THREAD_STARTED) +				break; +			barrier(); +			if (i != notified) { +				printk(KERN_INFO "kexec: waiting for cpu %d " +				       "(physical %d) to enter OPAL\n", +				       i, paca[i].hw_cpu_id); +				notified = i; +			} +		} +	} +} +  static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)  {  	xics_kexec_teardown_cpu(secondary); -	/* Return secondary CPUs to firmware on OPAL v3 */ -	if (firmware_has_feature(FW_FEATURE_OPALv3) && secondary) { +	/* On OPAL v3, we return all CPUs to firmware */ + +	if (!firmware_has_feature(FW_FEATURE_OPALv3)) +		return; + +	if (secondary) { +		/* Return secondary CPUs to firmware on OPAL v3 */  		mb();  		get_paca()->kexec_state = KEXEC_STATE_REAL_MODE;  		mb();  		/* Return the CPU to OPAL */  		opal_return_cpu(); +	} else if (crash_shutdown) { +		/* +		 * On crash, we don't wait for secondaries to go +		 * down as they might be unreachable or hung, so +		 * instead we just wait a bit and move on. +		 */ +		mdelay(1); +	} else { +		/* Primary waits for the secondaries to have reached OPAL */ +		pnv_kexec_wait_secondaries_down();  	}  }  #endif /* CONFIG_KEXEC */ +#ifdef CONFIG_MEMORY_HOTPLUG_SPARSE +static unsigned long pnv_memory_block_size(void) +{ +	return 256UL * 1024 * 1024; +} +#endif +  static void __init pnv_setup_machdep_opal(void)  {  	ppc_md.get_boot_time = opal_get_boot_time; @@ -176,6 +263,7 @@ static void __init pnv_setup_machdep_opal(void)  	ppc_md.power_off = pnv_power_off;  	ppc_md.halt = pnv_halt;  	ppc_md.machine_check_exception = opal_machine_check; +	ppc_md.mce_check_early_recovery = opal_mce_check_early_recovery;  }  #ifdef CONFIG_PPC_POWERNV_RTAS @@ -213,6 +301,25 @@ static int __init pnv_probe(void)  	return 1;  } +/* + * Returns the cpu frequency for 'cpu' in Hz. This is used by + * /proc/cpuinfo + */ +unsigned long pnv_get_proc_freq(unsigned int cpu) +{ +	unsigned long ret_freq; + +	ret_freq = cpufreq_quick_get(cpu) * 1000ul; + +	/* +	 * If the backend cpufreq driver does not exist, +         * then fallback to old way of reporting the clockrate. +	 */ +	if (!ret_freq) +		ret_freq = ppc_proc_freq; +	return ret_freq; +} +  define_machine(powernv) {  	.name			= "PowerNV",  	.probe			= pnv_probe, @@ -220,11 +327,16 @@ define_machine(powernv) {  	.setup_arch		= pnv_setup_arch,  	.init_IRQ		= pnv_init_IRQ,  	.show_cpuinfo		= pnv_show_cpuinfo, +	.get_proc_freq          = pnv_get_proc_freq,  	.progress		= pnv_progress,  	.machine_shutdown	= pnv_shutdown,  	.power_save             = power7_idle,  	.calibrate_decr		= generic_calibrate_decr, +	.dma_set_mask		= pnv_dma_set_mask,  #ifdef CONFIG_KEXEC  	.kexec_cpu_down		= pnv_kexec_cpu_down,  #endif +#ifdef CONFIG_MEMORY_HOTPLUG_SPARSE +	.memory_block_size	= pnv_memory_block_size, +#endif  };  | 
