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