162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cpuidle-powernv - idle state cpuidle driver. 462306a36Sopenharmony_ci * Adapted from drivers/cpuidle/cpuidle-pseries 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/moduleparam.h> 1262306a36Sopenharmony_ci#include <linux/cpuidle.h> 1362306a36Sopenharmony_ci#include <linux/cpu.h> 1462306a36Sopenharmony_ci#include <linux/notifier.h> 1562306a36Sopenharmony_ci#include <linux/clockchips.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/machdep.h> 2062306a36Sopenharmony_ci#include <asm/firmware.h> 2162306a36Sopenharmony_ci#include <asm/opal.h> 2262306a36Sopenharmony_ci#include <asm/runlatch.h> 2362306a36Sopenharmony_ci#include <asm/cpuidle.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * Expose only those Hardware idle states via the cpuidle framework 2762306a36Sopenharmony_ci * that have latency value below POWERNV_THRESHOLD_LATENCY_NS. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#define POWERNV_THRESHOLD_LATENCY_NS 200000 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic struct cpuidle_driver powernv_idle_driver = { 3262306a36Sopenharmony_ci .name = "powernv_idle", 3362306a36Sopenharmony_ci .owner = THIS_MODULE, 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int max_idle_state __read_mostly; 3762306a36Sopenharmony_cistatic struct cpuidle_state *cpuidle_state_table __read_mostly; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct stop_psscr_table { 4062306a36Sopenharmony_ci u64 val; 4162306a36Sopenharmony_ci u64 mask; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic struct stop_psscr_table stop_psscr_table[CPUIDLE_STATE_MAX] __read_mostly; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic u64 default_snooze_timeout __read_mostly; 4762306a36Sopenharmony_cistatic bool snooze_timeout_en __read_mostly; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic u64 get_snooze_timeout(struct cpuidle_device *dev, 5062306a36Sopenharmony_ci struct cpuidle_driver *drv, 5162306a36Sopenharmony_ci int index) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci int i; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (unlikely(!snooze_timeout_en)) 5662306a36Sopenharmony_ci return default_snooze_timeout; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci for (i = index + 1; i < drv->state_count; i++) { 5962306a36Sopenharmony_ci if (dev->states_usage[i].disable) 6062306a36Sopenharmony_ci continue; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return drv->states[i].target_residency * tb_ticks_per_usec; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return default_snooze_timeout; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int snooze_loop(struct cpuidle_device *dev, 6962306a36Sopenharmony_ci struct cpuidle_driver *drv, 7062306a36Sopenharmony_ci int index) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci u64 snooze_exit_time; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci set_thread_flag(TIF_POLLING_NRFLAG); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci local_irq_enable(); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci snooze_exit_time = get_tb() + get_snooze_timeout(dev, drv, index); 7962306a36Sopenharmony_ci dev->poll_time_limit = false; 8062306a36Sopenharmony_ci ppc64_runlatch_off(); 8162306a36Sopenharmony_ci HMT_very_low(); 8262306a36Sopenharmony_ci while (!need_resched()) { 8362306a36Sopenharmony_ci if (likely(snooze_timeout_en) && get_tb() > snooze_exit_time) { 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * Task has not woken up but we are exiting the polling 8662306a36Sopenharmony_ci * loop anyway. Require a barrier after polling is 8762306a36Sopenharmony_ci * cleared to order subsequent test of need_resched(). 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci clear_thread_flag(TIF_POLLING_NRFLAG); 9062306a36Sopenharmony_ci dev->poll_time_limit = true; 9162306a36Sopenharmony_ci smp_mb(); 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci HMT_medium(); 9762306a36Sopenharmony_ci ppc64_runlatch_on(); 9862306a36Sopenharmony_ci clear_thread_flag(TIF_POLLING_NRFLAG); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci local_irq_disable(); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return index; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int nap_loop(struct cpuidle_device *dev, 10662306a36Sopenharmony_ci struct cpuidle_driver *drv, 10762306a36Sopenharmony_ci int index) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci power7_idle_type(PNV_THREAD_NAP); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return index; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* Register for fastsleep only in oneshot mode of broadcast */ 11562306a36Sopenharmony_ci#ifdef CONFIG_TICK_ONESHOT 11662306a36Sopenharmony_cistatic int fastsleep_loop(struct cpuidle_device *dev, 11762306a36Sopenharmony_ci struct cpuidle_driver *drv, 11862306a36Sopenharmony_ci int index) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci unsigned long old_lpcr = mfspr(SPRN_LPCR); 12162306a36Sopenharmony_ci unsigned long new_lpcr; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (unlikely(system_state < SYSTEM_RUNNING)) 12462306a36Sopenharmony_ci return index; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci new_lpcr = old_lpcr; 12762306a36Sopenharmony_ci /* Do not exit powersave upon decrementer as we've setup the timer 12862306a36Sopenharmony_ci * offload. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci new_lpcr &= ~LPCR_PECE1; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci mtspr(SPRN_LPCR, new_lpcr); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci power7_idle_type(PNV_THREAD_SLEEP); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci mtspr(SPRN_LPCR, old_lpcr); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return index; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci#endif 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int stop_loop(struct cpuidle_device *dev, 14362306a36Sopenharmony_ci struct cpuidle_driver *drv, 14462306a36Sopenharmony_ci int index) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci arch300_idle_type(stop_psscr_table[index].val, 14762306a36Sopenharmony_ci stop_psscr_table[index].mask); 14862306a36Sopenharmony_ci return index; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* 15262306a36Sopenharmony_ci * States for dedicated partition case. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic struct cpuidle_state powernv_states[CPUIDLE_STATE_MAX] = { 15562306a36Sopenharmony_ci { /* Snooze */ 15662306a36Sopenharmony_ci .name = "snooze", 15762306a36Sopenharmony_ci .desc = "snooze", 15862306a36Sopenharmony_ci .exit_latency = 0, 15962306a36Sopenharmony_ci .target_residency = 0, 16062306a36Sopenharmony_ci .enter = snooze_loop, 16162306a36Sopenharmony_ci .flags = CPUIDLE_FLAG_POLLING }, 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int powernv_cpuidle_cpu_online(unsigned int cpu) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (dev && cpuidle_get_driver()) { 16962306a36Sopenharmony_ci cpuidle_pause_and_lock(); 17062306a36Sopenharmony_ci cpuidle_enable_device(dev); 17162306a36Sopenharmony_ci cpuidle_resume_and_unlock(); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int powernv_cpuidle_cpu_dead(unsigned int cpu) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (dev && cpuidle_get_driver()) { 18162306a36Sopenharmony_ci cpuidle_pause_and_lock(); 18262306a36Sopenharmony_ci cpuidle_disable_device(dev); 18362306a36Sopenharmony_ci cpuidle_resume_and_unlock(); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* 18962306a36Sopenharmony_ci * powernv_cpuidle_driver_init() 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistatic int powernv_cpuidle_driver_init(void) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci int idle_state; 19462306a36Sopenharmony_ci struct cpuidle_driver *drv = &powernv_idle_driver; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci drv->state_count = 0; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci for (idle_state = 0; idle_state < max_idle_state; ++idle_state) { 19962306a36Sopenharmony_ci /* Is the state not enabled? */ 20062306a36Sopenharmony_ci if (cpuidle_state_table[idle_state].enter == NULL) 20162306a36Sopenharmony_ci continue; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci drv->states[drv->state_count] = /* structure copy */ 20462306a36Sopenharmony_ci cpuidle_state_table[idle_state]; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci drv->state_count += 1; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * On the PowerNV platform cpu_present may be less than cpu_possible in 21162306a36Sopenharmony_ci * cases when firmware detects the CPU, but it is not available to the 21262306a36Sopenharmony_ci * OS. If CONFIG_HOTPLUG_CPU=n, then such CPUs are not hotplugable at 21362306a36Sopenharmony_ci * run time and hence cpu_devices are not created for those CPUs by the 21462306a36Sopenharmony_ci * generic topology_init(). 21562306a36Sopenharmony_ci * 21662306a36Sopenharmony_ci * drv->cpumask defaults to cpu_possible_mask in 21762306a36Sopenharmony_ci * __cpuidle_driver_init(). This breaks cpuidle on PowerNV where 21862306a36Sopenharmony_ci * cpu_devices are not created for CPUs in cpu_possible_mask that 21962306a36Sopenharmony_ci * cannot be hot-added later at run time. 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * Trying cpuidle_register_device() on a CPU without a cpu_device is 22262306a36Sopenharmony_ci * incorrect, so pass a correct CPU mask to the generic cpuidle driver. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci drv->cpumask = (struct cpumask *)cpu_present_mask; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic inline void add_powernv_state(int index, const char *name, 23162306a36Sopenharmony_ci unsigned int flags, 23262306a36Sopenharmony_ci int (*idle_fn)(struct cpuidle_device *, 23362306a36Sopenharmony_ci struct cpuidle_driver *, 23462306a36Sopenharmony_ci int), 23562306a36Sopenharmony_ci unsigned int target_residency, 23662306a36Sopenharmony_ci unsigned int exit_latency, 23762306a36Sopenharmony_ci u64 psscr_val, u64 psscr_mask) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci strscpy(powernv_states[index].name, name, CPUIDLE_NAME_LEN); 24062306a36Sopenharmony_ci strscpy(powernv_states[index].desc, name, CPUIDLE_NAME_LEN); 24162306a36Sopenharmony_ci powernv_states[index].flags = flags; 24262306a36Sopenharmony_ci powernv_states[index].target_residency = target_residency; 24362306a36Sopenharmony_ci powernv_states[index].exit_latency = exit_latency; 24462306a36Sopenharmony_ci powernv_states[index].enter = idle_fn; 24562306a36Sopenharmony_ci /* For power8 and below psscr_* will be 0 */ 24662306a36Sopenharmony_ci stop_psscr_table[index].val = psscr_val; 24762306a36Sopenharmony_ci stop_psscr_table[index].mask = psscr_mask; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciextern u32 pnv_get_supported_cpuidle_states(void); 25162306a36Sopenharmony_cistatic int powernv_add_idle_states(void) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int nr_idle_states = 1; /* Snooze */ 25462306a36Sopenharmony_ci int dt_idle_states; 25562306a36Sopenharmony_ci u32 has_stop_states = 0; 25662306a36Sopenharmony_ci int i; 25762306a36Sopenharmony_ci u32 supported_flags = pnv_get_supported_cpuidle_states(); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Currently we have snooze statically defined */ 26162306a36Sopenharmony_ci if (nr_pnv_idle_states <= 0) { 26262306a36Sopenharmony_ci pr_warn("cpuidle-powernv : Only Snooze is available\n"); 26362306a36Sopenharmony_ci goto out; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* TODO: Count only states which are eligible for cpuidle */ 26762306a36Sopenharmony_ci dt_idle_states = nr_pnv_idle_states; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * Since snooze is used as first idle state, max idle states allowed is 27162306a36Sopenharmony_ci * CPUIDLE_STATE_MAX -1 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci if (nr_pnv_idle_states > CPUIDLE_STATE_MAX - 1) { 27462306a36Sopenharmony_ci pr_warn("cpuidle-powernv: discovered idle states more than allowed"); 27562306a36Sopenharmony_ci dt_idle_states = CPUIDLE_STATE_MAX - 1; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* 27962306a36Sopenharmony_ci * If the idle states use stop instruction, probe for psscr values 28062306a36Sopenharmony_ci * and psscr mask which are necessary to specify required stop level. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci has_stop_states = (pnv_idle_states[0].flags & 28362306a36Sopenharmony_ci (OPAL_PM_STOP_INST_FAST | OPAL_PM_STOP_INST_DEEP)); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci for (i = 0; i < dt_idle_states; i++) { 28662306a36Sopenharmony_ci unsigned int exit_latency, target_residency; 28762306a36Sopenharmony_ci bool stops_timebase = false; 28862306a36Sopenharmony_ci struct pnv_idle_states_t *state = &pnv_idle_states[i]; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * Skip the platform idle state whose flag isn't in 29262306a36Sopenharmony_ci * the supported_cpuidle_states flag mask. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci if ((state->flags & supported_flags) != state->flags) 29562306a36Sopenharmony_ci continue; 29662306a36Sopenharmony_ci /* 29762306a36Sopenharmony_ci * If an idle state has exit latency beyond 29862306a36Sopenharmony_ci * POWERNV_THRESHOLD_LATENCY_NS then don't use it 29962306a36Sopenharmony_ci * in cpu-idle. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci if (state->latency_ns > POWERNV_THRESHOLD_LATENCY_NS) 30262306a36Sopenharmony_ci continue; 30362306a36Sopenharmony_ci /* 30462306a36Sopenharmony_ci * Firmware passes residency and latency values in ns. 30562306a36Sopenharmony_ci * cpuidle expects it in us. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ci exit_latency = DIV_ROUND_UP(state->latency_ns, 1000); 30862306a36Sopenharmony_ci target_residency = DIV_ROUND_UP(state->residency_ns, 1000); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (has_stop_states && !(state->valid)) 31162306a36Sopenharmony_ci continue; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (state->flags & OPAL_PM_TIMEBASE_STOP) 31462306a36Sopenharmony_ci stops_timebase = true; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (state->flags & OPAL_PM_NAP_ENABLED) { 31762306a36Sopenharmony_ci /* Add NAP state */ 31862306a36Sopenharmony_ci add_powernv_state(nr_idle_states, "Nap", 31962306a36Sopenharmony_ci CPUIDLE_FLAG_NONE, nap_loop, 32062306a36Sopenharmony_ci target_residency, exit_latency, 0, 0); 32162306a36Sopenharmony_ci } else if (has_stop_states && !stops_timebase) { 32262306a36Sopenharmony_ci add_powernv_state(nr_idle_states, state->name, 32362306a36Sopenharmony_ci CPUIDLE_FLAG_NONE, stop_loop, 32462306a36Sopenharmony_ci target_residency, exit_latency, 32562306a36Sopenharmony_ci state->psscr_val, 32662306a36Sopenharmony_ci state->psscr_mask); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* 33062306a36Sopenharmony_ci * All cpuidle states with CPUIDLE_FLAG_TIMER_STOP set must come 33162306a36Sopenharmony_ci * within this config dependency check. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci#ifdef CONFIG_TICK_ONESHOT 33462306a36Sopenharmony_ci else if (state->flags & OPAL_PM_SLEEP_ENABLED || 33562306a36Sopenharmony_ci state->flags & OPAL_PM_SLEEP_ENABLED_ER1) { 33662306a36Sopenharmony_ci /* Add FASTSLEEP state */ 33762306a36Sopenharmony_ci add_powernv_state(nr_idle_states, "FastSleep", 33862306a36Sopenharmony_ci CPUIDLE_FLAG_TIMER_STOP, 33962306a36Sopenharmony_ci fastsleep_loop, 34062306a36Sopenharmony_ci target_residency, exit_latency, 0, 0); 34162306a36Sopenharmony_ci } else if (has_stop_states && stops_timebase) { 34262306a36Sopenharmony_ci add_powernv_state(nr_idle_states, state->name, 34362306a36Sopenharmony_ci CPUIDLE_FLAG_TIMER_STOP, stop_loop, 34462306a36Sopenharmony_ci target_residency, exit_latency, 34562306a36Sopenharmony_ci state->psscr_val, 34662306a36Sopenharmony_ci state->psscr_mask); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci#endif 34962306a36Sopenharmony_ci else 35062306a36Sopenharmony_ci continue; 35162306a36Sopenharmony_ci nr_idle_states++; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ciout: 35462306a36Sopenharmony_ci return nr_idle_states; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/* 35862306a36Sopenharmony_ci * powernv_idle_probe() 35962306a36Sopenharmony_ci * Choose state table for shared versus dedicated partition 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_cistatic int powernv_idle_probe(void) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci if (cpuidle_disable != IDLE_NO_OVERRIDE) 36462306a36Sopenharmony_ci return -ENODEV; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (firmware_has_feature(FW_FEATURE_OPAL)) { 36762306a36Sopenharmony_ci cpuidle_state_table = powernv_states; 36862306a36Sopenharmony_ci /* Device tree can indicate more idle states */ 36962306a36Sopenharmony_ci max_idle_state = powernv_add_idle_states(); 37062306a36Sopenharmony_ci default_snooze_timeout = TICK_USEC * tb_ticks_per_usec; 37162306a36Sopenharmony_ci if (max_idle_state > 1) 37262306a36Sopenharmony_ci snooze_timeout_en = true; 37362306a36Sopenharmony_ci } else 37462306a36Sopenharmony_ci return -ENODEV; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int __init powernv_processor_idle_init(void) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci int retval; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci retval = powernv_idle_probe(); 38462306a36Sopenharmony_ci if (retval) 38562306a36Sopenharmony_ci return retval; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci powernv_cpuidle_driver_init(); 38862306a36Sopenharmony_ci retval = cpuidle_register(&powernv_idle_driver, NULL); 38962306a36Sopenharmony_ci if (retval) { 39062306a36Sopenharmony_ci printk(KERN_DEBUG "Registration of powernv driver failed.\n"); 39162306a36Sopenharmony_ci return retval; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci retval = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, 39562306a36Sopenharmony_ci "cpuidle/powernv:online", 39662306a36Sopenharmony_ci powernv_cpuidle_cpu_online, NULL); 39762306a36Sopenharmony_ci WARN_ON(retval < 0); 39862306a36Sopenharmony_ci retval = cpuhp_setup_state_nocalls(CPUHP_CPUIDLE_DEAD, 39962306a36Sopenharmony_ci "cpuidle/powernv:dead", NULL, 40062306a36Sopenharmony_ci powernv_cpuidle_cpu_dead); 40162306a36Sopenharmony_ci WARN_ON(retval < 0); 40262306a36Sopenharmony_ci printk(KERN_DEBUG "powernv_idle_driver registered\n"); 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cidevice_initcall(powernv_processor_idle_init); 407