162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cpuidle-pseries - idle state cpuidle driver. 462306a36Sopenharmony_ci * Adapted from drivers/idle/intel_idle.c and 562306a36Sopenharmony_ci * drivers/acpi/processor_idle.c 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/moduleparam.h> 1362306a36Sopenharmony_ci#include <linux/cpuidle.h> 1462306a36Sopenharmony_ci#include <linux/cpu.h> 1562306a36Sopenharmony_ci#include <linux/notifier.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/paca.h> 1862306a36Sopenharmony_ci#include <asm/reg.h> 1962306a36Sopenharmony_ci#include <asm/machdep.h> 2062306a36Sopenharmony_ci#include <asm/firmware.h> 2162306a36Sopenharmony_ci#include <asm/runlatch.h> 2262306a36Sopenharmony_ci#include <asm/idle.h> 2362306a36Sopenharmony_ci#include <asm/plpar_wrappers.h> 2462306a36Sopenharmony_ci#include <asm/rtas.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic struct cpuidle_driver pseries_idle_driver = { 2762306a36Sopenharmony_ci .name = "pseries_idle", 2862306a36Sopenharmony_ci .owner = THIS_MODULE, 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int max_idle_state __read_mostly; 3262306a36Sopenharmony_cistatic struct cpuidle_state *cpuidle_state_table __read_mostly; 3362306a36Sopenharmony_cistatic u64 snooze_timeout __read_mostly; 3462306a36Sopenharmony_cistatic bool snooze_timeout_en __read_mostly; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic __cpuidle 3762306a36Sopenharmony_ciint snooze_loop(struct cpuidle_device *dev, struct cpuidle_driver *drv, 3862306a36Sopenharmony_ci int index) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci u64 snooze_exit_time; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci set_thread_flag(TIF_POLLING_NRFLAG); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci pseries_idle_prolog(); 4562306a36Sopenharmony_ci raw_local_irq_enable(); 4662306a36Sopenharmony_ci snooze_exit_time = get_tb() + snooze_timeout; 4762306a36Sopenharmony_ci dev->poll_time_limit = false; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci while (!need_resched()) { 5062306a36Sopenharmony_ci HMT_low(); 5162306a36Sopenharmony_ci HMT_very_low(); 5262306a36Sopenharmony_ci if (likely(snooze_timeout_en) && get_tb() > snooze_exit_time) { 5362306a36Sopenharmony_ci /* 5462306a36Sopenharmony_ci * Task has not woken up but we are exiting the polling 5562306a36Sopenharmony_ci * loop anyway. Require a barrier after polling is 5662306a36Sopenharmony_ci * cleared to order subsequent test of need_resched(). 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci dev->poll_time_limit = true; 5962306a36Sopenharmony_ci clear_thread_flag(TIF_POLLING_NRFLAG); 6062306a36Sopenharmony_ci smp_mb(); 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci HMT_medium(); 6662306a36Sopenharmony_ci clear_thread_flag(TIF_POLLING_NRFLAG); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci raw_local_irq_disable(); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci pseries_idle_epilog(); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return index; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic __cpuidle void check_and_cede_processor(void) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * Ensure our interrupt state is properly tracked, 7962306a36Sopenharmony_ci * also checks if no interrupt has occurred while we 8062306a36Sopenharmony_ci * were soft-disabled 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci if (prep_irq_for_idle()) { 8362306a36Sopenharmony_ci cede_processor(); 8462306a36Sopenharmony_ci#ifdef CONFIG_TRACE_IRQFLAGS 8562306a36Sopenharmony_ci /* Ensure that H_CEDE returns with IRQs on */ 8662306a36Sopenharmony_ci if (WARN_ON(!(mfmsr() & MSR_EE))) 8762306a36Sopenharmony_ci __hard_irq_enable(); 8862306a36Sopenharmony_ci#endif 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * XCEDE: Extended CEDE states discovered through the 9462306a36Sopenharmony_ci * "ibm,get-systems-parameter" RTAS call with the token 9562306a36Sopenharmony_ci * CEDE_LATENCY_TOKEN 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * Section 7.3.16 System Parameters Option of PAPR version 2.8.1 has a 10062306a36Sopenharmony_ci * table with all the parameters to ibm,get-system-parameters. 10162306a36Sopenharmony_ci * CEDE_LATENCY_TOKEN corresponds to the token value for Cede Latency 10262306a36Sopenharmony_ci * Settings Information. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci#define CEDE_LATENCY_TOKEN 45 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * If the platform supports the cede latency settings information system 10862306a36Sopenharmony_ci * parameter it must provide the following information in the NULL terminated 10962306a36Sopenharmony_ci * parameter string: 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * a. The first byte is the length “N” of each cede latency setting record minus 11262306a36Sopenharmony_ci * one (zero indicates a length of 1 byte). 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * b. For each supported cede latency setting a cede latency setting record 11562306a36Sopenharmony_ci * consisting of the first “N” bytes as per the following table. 11662306a36Sopenharmony_ci * 11762306a36Sopenharmony_ci * ----------------------------- 11862306a36Sopenharmony_ci * | Field | Field | 11962306a36Sopenharmony_ci * | Name | Length | 12062306a36Sopenharmony_ci * ----------------------------- 12162306a36Sopenharmony_ci * | Cede Latency | 1 Byte | 12262306a36Sopenharmony_ci * | Specifier Value | | 12362306a36Sopenharmony_ci * ----------------------------- 12462306a36Sopenharmony_ci * | Maximum wakeup | | 12562306a36Sopenharmony_ci * | latency in | 8 Bytes | 12662306a36Sopenharmony_ci * | tb-ticks | | 12762306a36Sopenharmony_ci * ----------------------------- 12862306a36Sopenharmony_ci * | Responsive to | | 12962306a36Sopenharmony_ci * | external | 1 Byte | 13062306a36Sopenharmony_ci * | interrupts | | 13162306a36Sopenharmony_ci * ----------------------------- 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * This version has cede latency record size = 10. 13462306a36Sopenharmony_ci * 13562306a36Sopenharmony_ci * The structure xcede_latency_payload represents a) and b) with 13662306a36Sopenharmony_ci * xcede_latency_record representing the table in b). 13762306a36Sopenharmony_ci * 13862306a36Sopenharmony_ci * xcede_latency_parameter is what gets returned by 13962306a36Sopenharmony_ci * ibm,get-systems-parameter RTAS call when made with 14062306a36Sopenharmony_ci * CEDE_LATENCY_TOKEN. 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * These structures are only used to represent the data obtained by the RTAS 14362306a36Sopenharmony_ci * call. The data is in big-endian. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistruct xcede_latency_record { 14662306a36Sopenharmony_ci u8 hint; 14762306a36Sopenharmony_ci __be64 latency_ticks; 14862306a36Sopenharmony_ci u8 wake_on_irqs; 14962306a36Sopenharmony_ci} __packed; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci// Make space for 16 records, which "should be enough". 15262306a36Sopenharmony_cistruct xcede_latency_payload { 15362306a36Sopenharmony_ci u8 record_size; 15462306a36Sopenharmony_ci struct xcede_latency_record records[16]; 15562306a36Sopenharmony_ci} __packed; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistruct xcede_latency_parameter { 15862306a36Sopenharmony_ci __be16 payload_size; 15962306a36Sopenharmony_ci struct xcede_latency_payload payload; 16062306a36Sopenharmony_ci u8 null_char; 16162306a36Sopenharmony_ci} __packed; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic unsigned int nr_xcede_records; 16462306a36Sopenharmony_cistatic struct xcede_latency_parameter xcede_latency_parameter __initdata; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int __init parse_cede_parameters(void) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct xcede_latency_payload *payload; 16962306a36Sopenharmony_ci u32 total_xcede_records_size; 17062306a36Sopenharmony_ci u8 xcede_record_size; 17162306a36Sopenharmony_ci u16 payload_size; 17262306a36Sopenharmony_ci int ret, i; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ret = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1, 17562306a36Sopenharmony_ci NULL, CEDE_LATENCY_TOKEN, __pa(&xcede_latency_parameter), 17662306a36Sopenharmony_ci sizeof(xcede_latency_parameter)); 17762306a36Sopenharmony_ci if (ret) { 17862306a36Sopenharmony_ci pr_err("xcede: Error parsing CEDE_LATENCY_TOKEN\n"); 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci payload_size = be16_to_cpu(xcede_latency_parameter.payload_size); 18362306a36Sopenharmony_ci payload = &xcede_latency_parameter.payload; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci xcede_record_size = payload->record_size + 1; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (xcede_record_size != sizeof(struct xcede_latency_record)) { 18862306a36Sopenharmony_ci pr_err("xcede: Expected record-size %lu. Observed size %u.\n", 18962306a36Sopenharmony_ci sizeof(struct xcede_latency_record), xcede_record_size); 19062306a36Sopenharmony_ci return -EINVAL; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci pr_info("xcede: xcede_record_size = %d\n", xcede_record_size); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * Since the payload_size includes the last NULL byte and the 19762306a36Sopenharmony_ci * xcede_record_size, the remaining bytes correspond to array of all 19862306a36Sopenharmony_ci * cede_latency settings. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci total_xcede_records_size = payload_size - 2; 20162306a36Sopenharmony_ci nr_xcede_records = total_xcede_records_size / xcede_record_size; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci for (i = 0; i < nr_xcede_records; i++) { 20462306a36Sopenharmony_ci struct xcede_latency_record *record = &payload->records[i]; 20562306a36Sopenharmony_ci u64 latency_ticks = be64_to_cpu(record->latency_ticks); 20662306a36Sopenharmony_ci u8 wake_on_irqs = record->wake_on_irqs; 20762306a36Sopenharmony_ci u8 hint = record->hint; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci pr_info("xcede: Record %d : hint = %u, latency = 0x%llx tb ticks, Wake-on-irq = %u\n", 21062306a36Sopenharmony_ci i, hint, latency_ticks, wake_on_irqs); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci#define NR_DEDICATED_STATES 2 /* snooze, CEDE */ 21762306a36Sopenharmony_cistatic u8 cede_latency_hint[NR_DEDICATED_STATES]; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic __cpuidle 22062306a36Sopenharmony_ciint dedicated_cede_loop(struct cpuidle_device *dev, struct cpuidle_driver *drv, 22162306a36Sopenharmony_ci int index) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci u8 old_latency_hint; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci pseries_idle_prolog(); 22662306a36Sopenharmony_ci get_lppaca()->donate_dedicated_cpu = 1; 22762306a36Sopenharmony_ci old_latency_hint = get_lppaca()->cede_latency_hint; 22862306a36Sopenharmony_ci get_lppaca()->cede_latency_hint = cede_latency_hint[index]; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci HMT_medium(); 23162306a36Sopenharmony_ci check_and_cede_processor(); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci raw_local_irq_disable(); 23462306a36Sopenharmony_ci get_lppaca()->donate_dedicated_cpu = 0; 23562306a36Sopenharmony_ci get_lppaca()->cede_latency_hint = old_latency_hint; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci pseries_idle_epilog(); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return index; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic __cpuidle 24362306a36Sopenharmony_ciint shared_cede_loop(struct cpuidle_device *dev, struct cpuidle_driver *drv, 24462306a36Sopenharmony_ci int index) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci pseries_idle_prolog(); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* 25062306a36Sopenharmony_ci * Yield the processor to the hypervisor. We return if 25162306a36Sopenharmony_ci * an external interrupt occurs (which are driven prior 25262306a36Sopenharmony_ci * to returning here) or if a prod occurs from another 25362306a36Sopenharmony_ci * processor. When returning here, external interrupts 25462306a36Sopenharmony_ci * are enabled. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci check_and_cede_processor(); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci raw_local_irq_disable(); 25962306a36Sopenharmony_ci pseries_idle_epilog(); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return index; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/* 26562306a36Sopenharmony_ci * States for dedicated partition case. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_cistatic struct cpuidle_state dedicated_states[NR_DEDICATED_STATES] = { 26862306a36Sopenharmony_ci { /* Snooze */ 26962306a36Sopenharmony_ci .name = "snooze", 27062306a36Sopenharmony_ci .desc = "snooze", 27162306a36Sopenharmony_ci .exit_latency = 0, 27262306a36Sopenharmony_ci .target_residency = 0, 27362306a36Sopenharmony_ci .enter = &snooze_loop, 27462306a36Sopenharmony_ci .flags = CPUIDLE_FLAG_POLLING }, 27562306a36Sopenharmony_ci { /* CEDE */ 27662306a36Sopenharmony_ci .name = "CEDE", 27762306a36Sopenharmony_ci .desc = "CEDE", 27862306a36Sopenharmony_ci .exit_latency = 10, 27962306a36Sopenharmony_ci .target_residency = 100, 28062306a36Sopenharmony_ci .enter = &dedicated_cede_loop }, 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/* 28462306a36Sopenharmony_ci * States for shared partition case. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_cistatic struct cpuidle_state shared_states[] = { 28762306a36Sopenharmony_ci { /* Snooze */ 28862306a36Sopenharmony_ci .name = "snooze", 28962306a36Sopenharmony_ci .desc = "snooze", 29062306a36Sopenharmony_ci .exit_latency = 0, 29162306a36Sopenharmony_ci .target_residency = 0, 29262306a36Sopenharmony_ci .enter = &snooze_loop, 29362306a36Sopenharmony_ci .flags = CPUIDLE_FLAG_POLLING }, 29462306a36Sopenharmony_ci { /* Shared Cede */ 29562306a36Sopenharmony_ci .name = "Shared Cede", 29662306a36Sopenharmony_ci .desc = "Shared Cede", 29762306a36Sopenharmony_ci .exit_latency = 10, 29862306a36Sopenharmony_ci .target_residency = 100, 29962306a36Sopenharmony_ci .enter = &shared_cede_loop }, 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int pseries_cpuidle_cpu_online(unsigned int cpu) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (dev && cpuidle_get_driver()) { 30762306a36Sopenharmony_ci cpuidle_pause_and_lock(); 30862306a36Sopenharmony_ci cpuidle_enable_device(dev); 30962306a36Sopenharmony_ci cpuidle_resume_and_unlock(); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci return 0; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int pseries_cpuidle_cpu_dead(unsigned int cpu) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (dev && cpuidle_get_driver()) { 31962306a36Sopenharmony_ci cpuidle_pause_and_lock(); 32062306a36Sopenharmony_ci cpuidle_disable_device(dev); 32162306a36Sopenharmony_ci cpuidle_resume_and_unlock(); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/* 32762306a36Sopenharmony_ci * pseries_cpuidle_driver_init() 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_cistatic int pseries_cpuidle_driver_init(void) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci int idle_state; 33262306a36Sopenharmony_ci struct cpuidle_driver *drv = &pseries_idle_driver; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci drv->state_count = 0; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci for (idle_state = 0; idle_state < max_idle_state; ++idle_state) { 33762306a36Sopenharmony_ci /* Is the state not enabled? */ 33862306a36Sopenharmony_ci if (cpuidle_state_table[idle_state].enter == NULL) 33962306a36Sopenharmony_ci continue; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci drv->states[drv->state_count] = /* structure copy */ 34262306a36Sopenharmony_ci cpuidle_state_table[idle_state]; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci drv->state_count += 1; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void __init fixup_cede0_latency(void) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct xcede_latency_payload *payload; 35362306a36Sopenharmony_ci u64 min_xcede_latency_us = UINT_MAX; 35462306a36Sopenharmony_ci int i; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (parse_cede_parameters()) 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci pr_info("cpuidle: Skipping the %d Extended CEDE idle states\n", 36062306a36Sopenharmony_ci nr_xcede_records); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci payload = &xcede_latency_parameter.payload; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* 36562306a36Sopenharmony_ci * The CEDE idle state maps to CEDE(0). While the hypervisor 36662306a36Sopenharmony_ci * does not advertise CEDE(0) exit latency values, it does 36762306a36Sopenharmony_ci * advertise the latency values of the extended CEDE states. 36862306a36Sopenharmony_ci * We use the lowest advertised exit latency value as a proxy 36962306a36Sopenharmony_ci * for the exit latency of CEDE(0). 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci for (i = 0; i < nr_xcede_records; i++) { 37262306a36Sopenharmony_ci struct xcede_latency_record *record = &payload->records[i]; 37362306a36Sopenharmony_ci u8 hint = record->hint; 37462306a36Sopenharmony_ci u64 latency_tb = be64_to_cpu(record->latency_ticks); 37562306a36Sopenharmony_ci u64 latency_us = DIV_ROUND_UP_ULL(tb_to_ns(latency_tb), NSEC_PER_USEC); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* 37862306a36Sopenharmony_ci * We expect the exit latency of an extended CEDE 37962306a36Sopenharmony_ci * state to be non-zero, it to since it takes at least 38062306a36Sopenharmony_ci * a few nanoseconds to wakeup the idle CPU and 38162306a36Sopenharmony_ci * dispatch the virtual processor into the Linux 38262306a36Sopenharmony_ci * Guest. 38362306a36Sopenharmony_ci * 38462306a36Sopenharmony_ci * So we consider only non-zero value for performing 38562306a36Sopenharmony_ci * the fixup of CEDE(0) latency. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci if (latency_us == 0) { 38862306a36Sopenharmony_ci pr_warn("cpuidle: Skipping xcede record %d [hint=%d]. Exit latency = 0us\n", 38962306a36Sopenharmony_ci i, hint); 39062306a36Sopenharmony_ci continue; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (latency_us < min_xcede_latency_us) 39462306a36Sopenharmony_ci min_xcede_latency_us = latency_us; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (min_xcede_latency_us != UINT_MAX) { 39862306a36Sopenharmony_ci dedicated_states[1].exit_latency = min_xcede_latency_us; 39962306a36Sopenharmony_ci dedicated_states[1].target_residency = 10 * (min_xcede_latency_us); 40062306a36Sopenharmony_ci pr_info("cpuidle: Fixed up CEDE exit latency to %llu us\n", 40162306a36Sopenharmony_ci min_xcede_latency_us); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/* 40762306a36Sopenharmony_ci * pseries_idle_probe() 40862306a36Sopenharmony_ci * Choose state table for shared versus dedicated partition 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_cistatic int __init pseries_idle_probe(void) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (cpuidle_disable != IDLE_NO_OVERRIDE) 41462306a36Sopenharmony_ci return -ENODEV; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (firmware_has_feature(FW_FEATURE_SPLPAR)) { 41762306a36Sopenharmony_ci if (lppaca_shared_proc()) { 41862306a36Sopenharmony_ci cpuidle_state_table = shared_states; 41962306a36Sopenharmony_ci max_idle_state = ARRAY_SIZE(shared_states); 42062306a36Sopenharmony_ci } else { 42162306a36Sopenharmony_ci /* 42262306a36Sopenharmony_ci * Use firmware provided latency values 42362306a36Sopenharmony_ci * starting with POWER10 platforms. In the 42462306a36Sopenharmony_ci * case that we are running on a POWER10 42562306a36Sopenharmony_ci * platform but in an earlier compat mode, we 42662306a36Sopenharmony_ci * can still use the firmware provided values. 42762306a36Sopenharmony_ci * 42862306a36Sopenharmony_ci * However, on platforms prior to POWER10, we 42962306a36Sopenharmony_ci * cannot rely on the accuracy of the firmware 43062306a36Sopenharmony_ci * provided latency values. On such platforms, 43162306a36Sopenharmony_ci * go with the conservative default estimate 43262306a36Sopenharmony_ci * of 10us. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ARCH_31) || pvr_version_is(PVR_POWER10)) 43562306a36Sopenharmony_ci fixup_cede0_latency(); 43662306a36Sopenharmony_ci cpuidle_state_table = dedicated_states; 43762306a36Sopenharmony_ci max_idle_state = NR_DEDICATED_STATES; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci } else 44062306a36Sopenharmony_ci return -ENODEV; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (max_idle_state > 1) { 44362306a36Sopenharmony_ci snooze_timeout_en = true; 44462306a36Sopenharmony_ci snooze_timeout = cpuidle_state_table[1].target_residency * 44562306a36Sopenharmony_ci tb_ticks_per_usec; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int __init pseries_processor_idle_init(void) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci int retval; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci retval = pseries_idle_probe(); 45562306a36Sopenharmony_ci if (retval) 45662306a36Sopenharmony_ci return retval; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci pseries_cpuidle_driver_init(); 45962306a36Sopenharmony_ci retval = cpuidle_register(&pseries_idle_driver, NULL); 46062306a36Sopenharmony_ci if (retval) { 46162306a36Sopenharmony_ci printk(KERN_DEBUG "Registration of pseries driver failed.\n"); 46262306a36Sopenharmony_ci return retval; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci retval = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, 46662306a36Sopenharmony_ci "cpuidle/pseries:online", 46762306a36Sopenharmony_ci pseries_cpuidle_cpu_online, NULL); 46862306a36Sopenharmony_ci WARN_ON(retval < 0); 46962306a36Sopenharmony_ci retval = cpuhp_setup_state_nocalls(CPUHP_CPUIDLE_DEAD, 47062306a36Sopenharmony_ci "cpuidle/pseries:DEAD", NULL, 47162306a36Sopenharmony_ci pseries_cpuidle_cpu_dead); 47262306a36Sopenharmony_ci WARN_ON(retval < 0); 47362306a36Sopenharmony_ci printk(KERN_DEBUG "pseries_idle_driver registered\n"); 47462306a36Sopenharmony_ci return 0; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cidevice_initcall(pseries_processor_idle_init); 478