diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries/processor_idle.c')
| -rw-r--r-- | arch/powerpc/platforms/pseries/processor_idle.c | 53 | 
1 files changed, 41 insertions, 12 deletions
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c index 41a34bc4a9a2..455760b1fe6e 100644 --- a/arch/powerpc/platforms/pseries/processor_idle.c +++ b/arch/powerpc/platforms/pseries/processor_idle.c @@ -11,6 +11,7 @@  #include <linux/moduleparam.h>  #include <linux/cpuidle.h>  #include <linux/cpu.h> +#include <linux/notifier.h>  #include <asm/paca.h>  #include <asm/reg.h> @@ -99,15 +100,18 @@ out:  static void check_and_cede_processor(void)  {  	/* -	 * Interrupts are soft-disabled at this point, -	 * but not hard disabled. So an interrupt might have -	 * occurred before entering NAP, and would be potentially -	 * lost (edge events, decrementer events, etc...) unless -	 * we first hard disable then check. +	 * Ensure our interrupt state is properly tracked, +	 * also checks if no interrupt has occurred while we +	 * were soft-disabled  	 */ -	hard_irq_disable(); -	if (get_paca()->irq_happened == 0) +	if (prep_irq_for_idle()) {  		cede_processor(); +#ifdef CONFIG_TRACE_IRQFLAGS +		/* Ensure that H_CEDE returns with IRQs on */ +		if (WARN_ON(!(mfmsr() & MSR_EE))) +			__hard_irq_enable(); +#endif +	}  }  static int dedicated_cede_loop(struct cpuidle_device *dev, @@ -186,17 +190,40 @@ static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {  		.enter = &shared_cede_loop },  }; -int pseries_notify_cpuidle_add_cpu(int cpu) +static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n, +			unsigned long action, void *hcpu)  { +	int hotcpu = (unsigned long)hcpu;  	struct cpuidle_device *dev = -			per_cpu_ptr(pseries_cpuidle_devices, cpu); +			per_cpu_ptr(pseries_cpuidle_devices, hotcpu); +  	if (dev && cpuidle_get_driver()) { -		cpuidle_disable_device(dev); -		cpuidle_enable_device(dev); +		switch (action) { +		case CPU_ONLINE: +		case CPU_ONLINE_FROZEN: +			cpuidle_pause_and_lock(); +			cpuidle_enable_device(dev); +			cpuidle_resume_and_unlock(); +			break; + +		case CPU_DEAD: +		case CPU_DEAD_FROZEN: +			cpuidle_pause_and_lock(); +			cpuidle_disable_device(dev); +			cpuidle_resume_and_unlock(); +			break; + +		default: +			return NOTIFY_DONE; +		}  	} -	return 0; +	return NOTIFY_OK;  } +static struct notifier_block setup_hotplug_notifier = { +	.notifier_call = pseries_cpuidle_add_cpu_notifier, +}; +  /*   * pseries_cpuidle_driver_init()   */ @@ -321,6 +348,7 @@ static int __init pseries_processor_idle_init(void)  		return retval;  	} +	register_cpu_notifier(&setup_hotplug_notifier);  	printk(KERN_DEBUG "pseries_idle_driver registered\n");  	return 0; @@ -329,6 +357,7 @@ static int __init pseries_processor_idle_init(void)  static void __exit pseries_processor_idle_exit(void)  { +	unregister_cpu_notifier(&setup_hotplug_notifier);  	pseries_idle_devices_uninit();  	cpuidle_unregister_driver(&pseries_idle_driver);  | 
