18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PowerNV cpuidle code
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2015 IBM Corp.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/types.h>
98c2ecf20Sopenharmony_ci#include <linux/mm.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/device.h>
138c2ecf20Sopenharmony_ci#include <linux/cpu.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <asm/asm-prototypes.h>
168c2ecf20Sopenharmony_ci#include <asm/firmware.h>
178c2ecf20Sopenharmony_ci#include <asm/machdep.h>
188c2ecf20Sopenharmony_ci#include <asm/opal.h>
198c2ecf20Sopenharmony_ci#include <asm/cputhreads.h>
208c2ecf20Sopenharmony_ci#include <asm/cpuidle.h>
218c2ecf20Sopenharmony_ci#include <asm/code-patching.h>
228c2ecf20Sopenharmony_ci#include <asm/smp.h>
238c2ecf20Sopenharmony_ci#include <asm/runlatch.h>
248c2ecf20Sopenharmony_ci#include <asm/dbell.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "powernv.h"
278c2ecf20Sopenharmony_ci#include "subcore.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* Power ISA 3.0 allows for stop states 0x0 - 0xF */
308c2ecf20Sopenharmony_ci#define MAX_STOP_STATE	0xF
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define P9_STOP_SPR_MSR 2000
338c2ecf20Sopenharmony_ci#define P9_STOP_SPR_PSSCR      855
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic u32 supported_cpuidle_states;
368c2ecf20Sopenharmony_cistruct pnv_idle_states_t *pnv_idle_states;
378c2ecf20Sopenharmony_ciint nr_pnv_idle_states;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/*
408c2ecf20Sopenharmony_ci * The default stop state that will be used by ppc_md.power_save
418c2ecf20Sopenharmony_ci * function on platforms that support stop instruction.
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_cistatic u64 pnv_default_stop_val;
448c2ecf20Sopenharmony_cistatic u64 pnv_default_stop_mask;
458c2ecf20Sopenharmony_cistatic bool default_stop_found;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * First stop state levels when SPR and TB loss can occur.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_cistatic u64 pnv_first_tb_loss_level = MAX_STOP_STATE + 1;
518c2ecf20Sopenharmony_cistatic u64 deep_spr_loss_state = MAX_STOP_STATE + 1;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci * psscr value and mask of the deepest stop idle state.
558c2ecf20Sopenharmony_ci * Used when a cpu is offlined.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistatic u64 pnv_deepest_stop_psscr_val;
588c2ecf20Sopenharmony_cistatic u64 pnv_deepest_stop_psscr_mask;
598c2ecf20Sopenharmony_cistatic u64 pnv_deepest_stop_flag;
608c2ecf20Sopenharmony_cistatic bool deepest_stop_found;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic unsigned long power7_offline_type;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int pnv_save_sprs_for_deep_states(void)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	int cpu;
678c2ecf20Sopenharmony_ci	int rc;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/*
708c2ecf20Sopenharmony_ci	 * hid0, hid1, hid4, hid5, hmeer and lpcr values are symmetric across
718c2ecf20Sopenharmony_ci	 * all cpus at boot. Get these reg values of current cpu and use the
728c2ecf20Sopenharmony_ci	 * same across all cpus.
738c2ecf20Sopenharmony_ci	 */
748c2ecf20Sopenharmony_ci	uint64_t lpcr_val	= mfspr(SPRN_LPCR);
758c2ecf20Sopenharmony_ci	uint64_t hid0_val	= mfspr(SPRN_HID0);
768c2ecf20Sopenharmony_ci	uint64_t hmeer_val	= mfspr(SPRN_HMEER);
778c2ecf20Sopenharmony_ci	uint64_t msr_val = MSR_IDLE;
788c2ecf20Sopenharmony_ci	uint64_t psscr_val = pnv_deepest_stop_psscr_val;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	for_each_present_cpu(cpu) {
818c2ecf20Sopenharmony_ci		uint64_t pir = get_hard_smp_processor_id(cpu);
828c2ecf20Sopenharmony_ci		uint64_t hsprg0_val = (uint64_t)paca_ptrs[cpu];
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		rc = opal_slw_set_reg(pir, SPRN_HSPRG0, hsprg0_val);
858c2ecf20Sopenharmony_ci		if (rc != 0)
868c2ecf20Sopenharmony_ci			return rc;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci		rc = opal_slw_set_reg(pir, SPRN_LPCR, lpcr_val);
898c2ecf20Sopenharmony_ci		if (rc != 0)
908c2ecf20Sopenharmony_ci			return rc;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci		if (cpu_has_feature(CPU_FTR_ARCH_300)) {
938c2ecf20Sopenharmony_ci			rc = opal_slw_set_reg(pir, P9_STOP_SPR_MSR, msr_val);
948c2ecf20Sopenharmony_ci			if (rc)
958c2ecf20Sopenharmony_ci				return rc;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci			rc = opal_slw_set_reg(pir,
988c2ecf20Sopenharmony_ci					      P9_STOP_SPR_PSSCR, psscr_val);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci			if (rc)
1018c2ecf20Sopenharmony_ci				return rc;
1028c2ecf20Sopenharmony_ci		}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		/* HIDs are per core registers */
1058c2ecf20Sopenharmony_ci		if (cpu_thread_in_core(cpu) == 0) {
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci			rc = opal_slw_set_reg(pir, SPRN_HMEER, hmeer_val);
1088c2ecf20Sopenharmony_ci			if (rc != 0)
1098c2ecf20Sopenharmony_ci				return rc;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci			rc = opal_slw_set_reg(pir, SPRN_HID0, hid0_val);
1128c2ecf20Sopenharmony_ci			if (rc != 0)
1138c2ecf20Sopenharmony_ci				return rc;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci			/* Only p8 needs to set extra HID regiters */
1168c2ecf20Sopenharmony_ci			if (!cpu_has_feature(CPU_FTR_ARCH_300)) {
1178c2ecf20Sopenharmony_ci				uint64_t hid1_val = mfspr(SPRN_HID1);
1188c2ecf20Sopenharmony_ci				uint64_t hid4_val = mfspr(SPRN_HID4);
1198c2ecf20Sopenharmony_ci				uint64_t hid5_val = mfspr(SPRN_HID5);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci				rc = opal_slw_set_reg(pir, SPRN_HID1, hid1_val);
1228c2ecf20Sopenharmony_ci				if (rc != 0)
1238c2ecf20Sopenharmony_ci					return rc;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci				rc = opal_slw_set_reg(pir, SPRN_HID4, hid4_val);
1268c2ecf20Sopenharmony_ci				if (rc != 0)
1278c2ecf20Sopenharmony_ci					return rc;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci				rc = opal_slw_set_reg(pir, SPRN_HID5, hid5_val);
1308c2ecf20Sopenharmony_ci				if (rc != 0)
1318c2ecf20Sopenharmony_ci					return rc;
1328c2ecf20Sopenharmony_ci			}
1338c2ecf20Sopenharmony_ci		}
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return 0;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ciu32 pnv_get_supported_cpuidle_states(void)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	return supported_cpuidle_states;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pnv_get_supported_cpuidle_states);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void pnv_fastsleep_workaround_apply(void *info)
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int rc;
1498c2ecf20Sopenharmony_ci	int *err = info;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	rc = opal_config_cpu_idle_state(OPAL_CONFIG_IDLE_FASTSLEEP,
1528c2ecf20Sopenharmony_ci					OPAL_CONFIG_IDLE_APPLY);
1538c2ecf20Sopenharmony_ci	if (rc)
1548c2ecf20Sopenharmony_ci		*err = 1;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic bool power7_fastsleep_workaround_entry = true;
1588c2ecf20Sopenharmony_cistatic bool power7_fastsleep_workaround_exit = true;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/*
1618c2ecf20Sopenharmony_ci * Used to store fastsleep workaround state
1628c2ecf20Sopenharmony_ci * 0 - Workaround applied/undone at fastsleep entry/exit path (Default)
1638c2ecf20Sopenharmony_ci * 1 - Workaround applied once, never undone.
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic u8 fastsleep_workaround_applyonce;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic ssize_t show_fastsleep_workaround_applyonce(struct device *dev,
1688c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", fastsleep_workaround_applyonce);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic ssize_t store_fastsleep_workaround_applyonce(struct device *dev,
1748c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf,
1758c2ecf20Sopenharmony_ci		size_t count)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	cpumask_t primary_thread_mask;
1788c2ecf20Sopenharmony_ci	int err;
1798c2ecf20Sopenharmony_ci	u8 val;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (kstrtou8(buf, 0, &val) || val != 1)
1828c2ecf20Sopenharmony_ci		return -EINVAL;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (fastsleep_workaround_applyonce == 1)
1858c2ecf20Sopenharmony_ci		return count;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/*
1888c2ecf20Sopenharmony_ci	 * fastsleep_workaround_applyonce = 1 implies
1898c2ecf20Sopenharmony_ci	 * fastsleep workaround needs to be left in 'applied' state on all
1908c2ecf20Sopenharmony_ci	 * the cores. Do this by-
1918c2ecf20Sopenharmony_ci	 * 1. Disable the 'undo' workaround in fastsleep exit path
1928c2ecf20Sopenharmony_ci	 * 2. Sendi IPIs to all the cores which have at least one online thread
1938c2ecf20Sopenharmony_ci	 * 3. Disable the 'apply' workaround in fastsleep entry path
1948c2ecf20Sopenharmony_ci	 *
1958c2ecf20Sopenharmony_ci	 * There is no need to send ipi to cores which have all threads
1968c2ecf20Sopenharmony_ci	 * offlined, as last thread of the core entering fastsleep or deeper
1978c2ecf20Sopenharmony_ci	 * state would have applied workaround.
1988c2ecf20Sopenharmony_ci	 */
1998c2ecf20Sopenharmony_ci	power7_fastsleep_workaround_exit = false;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	get_online_cpus();
2028c2ecf20Sopenharmony_ci	primary_thread_mask = cpu_online_cores_map();
2038c2ecf20Sopenharmony_ci	on_each_cpu_mask(&primary_thread_mask,
2048c2ecf20Sopenharmony_ci				pnv_fastsleep_workaround_apply,
2058c2ecf20Sopenharmony_ci				&err, 1);
2068c2ecf20Sopenharmony_ci	put_online_cpus();
2078c2ecf20Sopenharmony_ci	if (err) {
2088c2ecf20Sopenharmony_ci		pr_err("fastsleep_workaround_applyonce change failed while running pnv_fastsleep_workaround_apply");
2098c2ecf20Sopenharmony_ci		goto fail;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	power7_fastsleep_workaround_entry = false;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	fastsleep_workaround_applyonce = 1;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return count;
2178c2ecf20Sopenharmony_cifail:
2188c2ecf20Sopenharmony_ci	return -EIO;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic DEVICE_ATTR(fastsleep_workaround_applyonce, 0600,
2228c2ecf20Sopenharmony_ci			show_fastsleep_workaround_applyonce,
2238c2ecf20Sopenharmony_ci			store_fastsleep_workaround_applyonce);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic inline void atomic_start_thread_idle(void)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
2288c2ecf20Sopenharmony_ci	int first = cpu_first_thread_sibling(cpu);
2298c2ecf20Sopenharmony_ci	int thread_nr = cpu_thread_in_core(cpu);
2308c2ecf20Sopenharmony_ci	unsigned long *state = &paca_ptrs[first]->idle_state;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	clear_bit(thread_nr, state);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic inline void atomic_stop_thread_idle(void)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
2388c2ecf20Sopenharmony_ci	int first = cpu_first_thread_sibling(cpu);
2398c2ecf20Sopenharmony_ci	int thread_nr = cpu_thread_in_core(cpu);
2408c2ecf20Sopenharmony_ci	unsigned long *state = &paca_ptrs[first]->idle_state;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	set_bit(thread_nr, state);
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic inline void atomic_lock_thread_idle(void)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
2488c2ecf20Sopenharmony_ci	int first = cpu_first_thread_sibling(cpu);
2498c2ecf20Sopenharmony_ci	unsigned long *state = &paca_ptrs[first]->idle_state;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	while (unlikely(test_and_set_bit_lock(NR_PNV_CORE_IDLE_LOCK_BIT, state)))
2528c2ecf20Sopenharmony_ci		barrier();
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic inline void atomic_unlock_and_stop_thread_idle(void)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
2588c2ecf20Sopenharmony_ci	int first = cpu_first_thread_sibling(cpu);
2598c2ecf20Sopenharmony_ci	unsigned long thread = 1UL << cpu_thread_in_core(cpu);
2608c2ecf20Sopenharmony_ci	unsigned long *state = &paca_ptrs[first]->idle_state;
2618c2ecf20Sopenharmony_ci	u64 s = READ_ONCE(*state);
2628c2ecf20Sopenharmony_ci	u64 new, tmp;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	BUG_ON(!(s & PNV_CORE_IDLE_LOCK_BIT));
2658c2ecf20Sopenharmony_ci	BUG_ON(s & thread);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ciagain:
2688c2ecf20Sopenharmony_ci	new = (s | thread) & ~PNV_CORE_IDLE_LOCK_BIT;
2698c2ecf20Sopenharmony_ci	tmp = cmpxchg(state, s, new);
2708c2ecf20Sopenharmony_ci	if (unlikely(tmp != s)) {
2718c2ecf20Sopenharmony_ci		s = tmp;
2728c2ecf20Sopenharmony_ci		goto again;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic inline void atomic_unlock_thread_idle(void)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
2798c2ecf20Sopenharmony_ci	int first = cpu_first_thread_sibling(cpu);
2808c2ecf20Sopenharmony_ci	unsigned long *state = &paca_ptrs[first]->idle_state;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	BUG_ON(!test_bit(NR_PNV_CORE_IDLE_LOCK_BIT, state));
2838c2ecf20Sopenharmony_ci	clear_bit_unlock(NR_PNV_CORE_IDLE_LOCK_BIT, state);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci/* P7 and P8 */
2878c2ecf20Sopenharmony_cistruct p7_sprs {
2888c2ecf20Sopenharmony_ci	/* per core */
2898c2ecf20Sopenharmony_ci	u64 tscr;
2908c2ecf20Sopenharmony_ci	u64 worc;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/* per subcore */
2938c2ecf20Sopenharmony_ci	u64 sdr1;
2948c2ecf20Sopenharmony_ci	u64 rpr;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* per thread */
2978c2ecf20Sopenharmony_ci	u64 lpcr;
2988c2ecf20Sopenharmony_ci	u64 hfscr;
2998c2ecf20Sopenharmony_ci	u64 fscr;
3008c2ecf20Sopenharmony_ci	u64 purr;
3018c2ecf20Sopenharmony_ci	u64 spurr;
3028c2ecf20Sopenharmony_ci	u64 dscr;
3038c2ecf20Sopenharmony_ci	u64 wort;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* per thread SPRs that get lost in shallow states */
3068c2ecf20Sopenharmony_ci	u64 amr;
3078c2ecf20Sopenharmony_ci	u64 iamr;
3088c2ecf20Sopenharmony_ci	u64 amor;
3098c2ecf20Sopenharmony_ci	u64 uamor;
3108c2ecf20Sopenharmony_ci};
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic unsigned long power7_idle_insn(unsigned long type)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
3158c2ecf20Sopenharmony_ci	int first = cpu_first_thread_sibling(cpu);
3168c2ecf20Sopenharmony_ci	unsigned long *state = &paca_ptrs[first]->idle_state;
3178c2ecf20Sopenharmony_ci	unsigned long thread = 1UL << cpu_thread_in_core(cpu);
3188c2ecf20Sopenharmony_ci	unsigned long core_thread_mask = (1UL << threads_per_core) - 1;
3198c2ecf20Sopenharmony_ci	unsigned long srr1;
3208c2ecf20Sopenharmony_ci	bool full_winkle;
3218c2ecf20Sopenharmony_ci	struct p7_sprs sprs = {}; /* avoid false use-uninitialised */
3228c2ecf20Sopenharmony_ci	bool sprs_saved = false;
3238c2ecf20Sopenharmony_ci	int rc;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (unlikely(type != PNV_THREAD_NAP)) {
3268c2ecf20Sopenharmony_ci		atomic_lock_thread_idle();
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		BUG_ON(!(*state & thread));
3298c2ecf20Sopenharmony_ci		*state &= ~thread;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		if (power7_fastsleep_workaround_entry) {
3328c2ecf20Sopenharmony_ci			if ((*state & core_thread_mask) == 0) {
3338c2ecf20Sopenharmony_ci				rc = opal_config_cpu_idle_state(
3348c2ecf20Sopenharmony_ci						OPAL_CONFIG_IDLE_FASTSLEEP,
3358c2ecf20Sopenharmony_ci						OPAL_CONFIG_IDLE_APPLY);
3368c2ecf20Sopenharmony_ci				BUG_ON(rc);
3378c2ecf20Sopenharmony_ci			}
3388c2ecf20Sopenharmony_ci		}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		if (type == PNV_THREAD_WINKLE) {
3418c2ecf20Sopenharmony_ci			sprs.tscr	= mfspr(SPRN_TSCR);
3428c2ecf20Sopenharmony_ci			sprs.worc	= mfspr(SPRN_WORC);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci			sprs.sdr1	= mfspr(SPRN_SDR1);
3458c2ecf20Sopenharmony_ci			sprs.rpr	= mfspr(SPRN_RPR);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci			sprs.lpcr	= mfspr(SPRN_LPCR);
3488c2ecf20Sopenharmony_ci			if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
3498c2ecf20Sopenharmony_ci				sprs.hfscr	= mfspr(SPRN_HFSCR);
3508c2ecf20Sopenharmony_ci				sprs.fscr	= mfspr(SPRN_FSCR);
3518c2ecf20Sopenharmony_ci			}
3528c2ecf20Sopenharmony_ci			sprs.purr	= mfspr(SPRN_PURR);
3538c2ecf20Sopenharmony_ci			sprs.spurr	= mfspr(SPRN_SPURR);
3548c2ecf20Sopenharmony_ci			sprs.dscr	= mfspr(SPRN_DSCR);
3558c2ecf20Sopenharmony_ci			sprs.wort	= mfspr(SPRN_WORT);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci			sprs_saved = true;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci			/*
3608c2ecf20Sopenharmony_ci			 * Increment winkle counter and set all winkle bits if
3618c2ecf20Sopenharmony_ci			 * all threads are winkling. This allows wakeup side to
3628c2ecf20Sopenharmony_ci			 * distinguish between fast sleep and winkle state
3638c2ecf20Sopenharmony_ci			 * loss. Fast sleep still has to resync the timebase so
3648c2ecf20Sopenharmony_ci			 * this may not be a really big win.
3658c2ecf20Sopenharmony_ci			 */
3668c2ecf20Sopenharmony_ci			*state += 1 << PNV_CORE_IDLE_WINKLE_COUNT_SHIFT;
3678c2ecf20Sopenharmony_ci			if ((*state & PNV_CORE_IDLE_WINKLE_COUNT_BITS)
3688c2ecf20Sopenharmony_ci					>> PNV_CORE_IDLE_WINKLE_COUNT_SHIFT
3698c2ecf20Sopenharmony_ci					== threads_per_core)
3708c2ecf20Sopenharmony_ci				*state |= PNV_CORE_IDLE_THREAD_WINKLE_BITS;
3718c2ecf20Sopenharmony_ci			WARN_ON((*state & PNV_CORE_IDLE_WINKLE_COUNT_BITS) == 0);
3728c2ecf20Sopenharmony_ci		}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci		atomic_unlock_thread_idle();
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
3788c2ecf20Sopenharmony_ci		sprs.amr	= mfspr(SPRN_AMR);
3798c2ecf20Sopenharmony_ci		sprs.iamr	= mfspr(SPRN_IAMR);
3808c2ecf20Sopenharmony_ci		sprs.amor	= mfspr(SPRN_AMOR);
3818c2ecf20Sopenharmony_ci		sprs.uamor	= mfspr(SPRN_UAMOR);
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	local_paca->thread_idle_state = type;
3858c2ecf20Sopenharmony_ci	srr1 = isa206_idle_insn_mayloss(type);		/* go idle */
3868c2ecf20Sopenharmony_ci	local_paca->thread_idle_state = PNV_THREAD_RUNNING;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!srr1);
3898c2ecf20Sopenharmony_ci	WARN_ON_ONCE(mfmsr() & (MSR_IR|MSR_DR));
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
3928c2ecf20Sopenharmony_ci		if ((srr1 & SRR1_WAKESTATE) != SRR1_WS_NOLOSS) {
3938c2ecf20Sopenharmony_ci			/*
3948c2ecf20Sopenharmony_ci			 * We don't need an isync after the mtsprs here because
3958c2ecf20Sopenharmony_ci			 * the upcoming mtmsrd is execution synchronizing.
3968c2ecf20Sopenharmony_ci			 */
3978c2ecf20Sopenharmony_ci			mtspr(SPRN_AMR,		sprs.amr);
3988c2ecf20Sopenharmony_ci			mtspr(SPRN_IAMR,	sprs.iamr);
3998c2ecf20Sopenharmony_ci			mtspr(SPRN_AMOR,	sprs.amor);
4008c2ecf20Sopenharmony_ci			mtspr(SPRN_UAMOR,	sprs.uamor);
4018c2ecf20Sopenharmony_ci		}
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (unlikely((srr1 & SRR1_WAKEMASK_P8) == SRR1_WAKEHMI))
4058c2ecf20Sopenharmony_ci		hmi_exception_realmode(NULL);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (likely((srr1 & SRR1_WAKESTATE) != SRR1_WS_HVLOSS)) {
4088c2ecf20Sopenharmony_ci		if (unlikely(type != PNV_THREAD_NAP)) {
4098c2ecf20Sopenharmony_ci			atomic_lock_thread_idle();
4108c2ecf20Sopenharmony_ci			if (type == PNV_THREAD_WINKLE) {
4118c2ecf20Sopenharmony_ci				WARN_ON((*state & PNV_CORE_IDLE_WINKLE_COUNT_BITS) == 0);
4128c2ecf20Sopenharmony_ci				*state -= 1 << PNV_CORE_IDLE_WINKLE_COUNT_SHIFT;
4138c2ecf20Sopenharmony_ci				*state &= ~(thread << PNV_CORE_IDLE_THREAD_WINKLE_BITS_SHIFT);
4148c2ecf20Sopenharmony_ci			}
4158c2ecf20Sopenharmony_ci			atomic_unlock_and_stop_thread_idle();
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci		return srr1;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* HV state loss */
4218c2ecf20Sopenharmony_ci	BUG_ON(type == PNV_THREAD_NAP);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	atomic_lock_thread_idle();
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	full_winkle = false;
4268c2ecf20Sopenharmony_ci	if (type == PNV_THREAD_WINKLE) {
4278c2ecf20Sopenharmony_ci		WARN_ON((*state & PNV_CORE_IDLE_WINKLE_COUNT_BITS) == 0);
4288c2ecf20Sopenharmony_ci		*state -= 1 << PNV_CORE_IDLE_WINKLE_COUNT_SHIFT;
4298c2ecf20Sopenharmony_ci		if (*state & (thread << PNV_CORE_IDLE_THREAD_WINKLE_BITS_SHIFT)) {
4308c2ecf20Sopenharmony_ci			*state &= ~(thread << PNV_CORE_IDLE_THREAD_WINKLE_BITS_SHIFT);
4318c2ecf20Sopenharmony_ci			full_winkle = true;
4328c2ecf20Sopenharmony_ci			BUG_ON(!sprs_saved);
4338c2ecf20Sopenharmony_ci		}
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	WARN_ON(*state & thread);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if ((*state & core_thread_mask) != 0)
4398c2ecf20Sopenharmony_ci		goto core_woken;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* Per-core SPRs */
4428c2ecf20Sopenharmony_ci	if (full_winkle) {
4438c2ecf20Sopenharmony_ci		mtspr(SPRN_TSCR,	sprs.tscr);
4448c2ecf20Sopenharmony_ci		mtspr(SPRN_WORC,	sprs.worc);
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (power7_fastsleep_workaround_exit) {
4488c2ecf20Sopenharmony_ci		rc = opal_config_cpu_idle_state(OPAL_CONFIG_IDLE_FASTSLEEP,
4498c2ecf20Sopenharmony_ci						OPAL_CONFIG_IDLE_UNDO);
4508c2ecf20Sopenharmony_ci		BUG_ON(rc);
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* TB */
4548c2ecf20Sopenharmony_ci	if (opal_resync_timebase() != OPAL_SUCCESS)
4558c2ecf20Sopenharmony_ci		BUG();
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cicore_woken:
4588c2ecf20Sopenharmony_ci	if (!full_winkle)
4598c2ecf20Sopenharmony_ci		goto subcore_woken;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if ((*state & local_paca->subcore_sibling_mask) != 0)
4628c2ecf20Sopenharmony_ci		goto subcore_woken;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* Per-subcore SPRs */
4658c2ecf20Sopenharmony_ci	mtspr(SPRN_SDR1,	sprs.sdr1);
4668c2ecf20Sopenharmony_ci	mtspr(SPRN_RPR,		sprs.rpr);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cisubcore_woken:
4698c2ecf20Sopenharmony_ci	/*
4708c2ecf20Sopenharmony_ci	 * isync after restoring shared SPRs and before unlocking. Unlock
4718c2ecf20Sopenharmony_ci	 * only contains hwsync which does not necessarily do the right
4728c2ecf20Sopenharmony_ci	 * thing for SPRs.
4738c2ecf20Sopenharmony_ci	 */
4748c2ecf20Sopenharmony_ci	isync();
4758c2ecf20Sopenharmony_ci	atomic_unlock_and_stop_thread_idle();
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	/* Fast sleep does not lose SPRs */
4788c2ecf20Sopenharmony_ci	if (!full_winkle)
4798c2ecf20Sopenharmony_ci		return srr1;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* Per-thread SPRs */
4828c2ecf20Sopenharmony_ci	mtspr(SPRN_LPCR,	sprs.lpcr);
4838c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
4848c2ecf20Sopenharmony_ci		mtspr(SPRN_HFSCR,	sprs.hfscr);
4858c2ecf20Sopenharmony_ci		mtspr(SPRN_FSCR,	sprs.fscr);
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci	mtspr(SPRN_PURR,	sprs.purr);
4888c2ecf20Sopenharmony_ci	mtspr(SPRN_SPURR,	sprs.spurr);
4898c2ecf20Sopenharmony_ci	mtspr(SPRN_DSCR,	sprs.dscr);
4908c2ecf20Sopenharmony_ci	mtspr(SPRN_WORT,	sprs.wort);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	mtspr(SPRN_SPRG3,	local_paca->sprg_vdso);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/*
4958c2ecf20Sopenharmony_ci	 * The SLB has to be restored here, but it sometimes still
4968c2ecf20Sopenharmony_ci	 * contains entries, so the __ variant must be used to prevent
4978c2ecf20Sopenharmony_ci	 * multi hits.
4988c2ecf20Sopenharmony_ci	 */
4998c2ecf20Sopenharmony_ci	__slb_restore_bolted_realmode();
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return srr1;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ciextern unsigned long idle_kvm_start_guest(unsigned long srr1);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
5078c2ecf20Sopenharmony_cistatic unsigned long power7_offline(void)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	unsigned long srr1;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	mtmsr(MSR_IDLE);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
5148c2ecf20Sopenharmony_ci	/* Tell KVM we're entering idle. */
5158c2ecf20Sopenharmony_ci	/******************************************************/
5168c2ecf20Sopenharmony_ci	/*  N O T E   W E L L    ! ! !    N O T E   W E L L   */
5178c2ecf20Sopenharmony_ci	/* The following store to HSTATE_HWTHREAD_STATE(r13)  */
5188c2ecf20Sopenharmony_ci	/* MUST occur in real mode, i.e. with the MMU off,    */
5198c2ecf20Sopenharmony_ci	/* and the MMU must stay off until we clear this flag */
5208c2ecf20Sopenharmony_ci	/* and test HSTATE_HWTHREAD_REQ(r13) in               */
5218c2ecf20Sopenharmony_ci	/* pnv_powersave_wakeup in this file.                 */
5228c2ecf20Sopenharmony_ci	/* The reason is that another thread can switch the   */
5238c2ecf20Sopenharmony_ci	/* MMU to a guest context whenever this flag is set   */
5248c2ecf20Sopenharmony_ci	/* to KVM_HWTHREAD_IN_IDLE, and if the MMU was on,    */
5258c2ecf20Sopenharmony_ci	/* that would potentially cause this thread to start  */
5268c2ecf20Sopenharmony_ci	/* executing instructions from guest memory in        */
5278c2ecf20Sopenharmony_ci	/* hypervisor mode, leading to a host crash or data   */
5288c2ecf20Sopenharmony_ci	/* corruption, or worse.                              */
5298c2ecf20Sopenharmony_ci	/******************************************************/
5308c2ecf20Sopenharmony_ci	local_paca->kvm_hstate.hwthread_state = KVM_HWTHREAD_IN_IDLE;
5318c2ecf20Sopenharmony_ci#endif
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	__ppc64_runlatch_off();
5348c2ecf20Sopenharmony_ci	srr1 = power7_idle_insn(power7_offline_type);
5358c2ecf20Sopenharmony_ci	__ppc64_runlatch_on();
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
5388c2ecf20Sopenharmony_ci	local_paca->kvm_hstate.hwthread_state = KVM_HWTHREAD_IN_KERNEL;
5398c2ecf20Sopenharmony_ci	/* Order setting hwthread_state vs. testing hwthread_req */
5408c2ecf20Sopenharmony_ci	smp_mb();
5418c2ecf20Sopenharmony_ci	if (local_paca->kvm_hstate.hwthread_req)
5428c2ecf20Sopenharmony_ci		srr1 = idle_kvm_start_guest(srr1);
5438c2ecf20Sopenharmony_ci#endif
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	mtmsr(MSR_KERNEL);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	return srr1;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci#endif
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_civoid power7_idle_type(unsigned long type)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	unsigned long srr1;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (!prep_irq_for_idle_irqsoff())
5568c2ecf20Sopenharmony_ci		return;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	mtmsr(MSR_IDLE);
5598c2ecf20Sopenharmony_ci	__ppc64_runlatch_off();
5608c2ecf20Sopenharmony_ci	srr1 = power7_idle_insn(type);
5618c2ecf20Sopenharmony_ci	__ppc64_runlatch_on();
5628c2ecf20Sopenharmony_ci	mtmsr(MSR_KERNEL);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	fini_irq_for_idle_irqsoff();
5658c2ecf20Sopenharmony_ci	irq_set_pending_from_srr1(srr1);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic void power7_idle(void)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	if (!powersave_nap)
5718c2ecf20Sopenharmony_ci		return;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	power7_idle_type(PNV_THREAD_NAP);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistruct p9_sprs {
5778c2ecf20Sopenharmony_ci	/* per core */
5788c2ecf20Sopenharmony_ci	u64 ptcr;
5798c2ecf20Sopenharmony_ci	u64 rpr;
5808c2ecf20Sopenharmony_ci	u64 tscr;
5818c2ecf20Sopenharmony_ci	u64 ldbar;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* per thread */
5848c2ecf20Sopenharmony_ci	u64 lpcr;
5858c2ecf20Sopenharmony_ci	u64 hfscr;
5868c2ecf20Sopenharmony_ci	u64 fscr;
5878c2ecf20Sopenharmony_ci	u64 pid;
5888c2ecf20Sopenharmony_ci	u64 purr;
5898c2ecf20Sopenharmony_ci	u64 spurr;
5908c2ecf20Sopenharmony_ci	u64 dscr;
5918c2ecf20Sopenharmony_ci	u64 wort;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	u64 mmcra;
5948c2ecf20Sopenharmony_ci	u32 mmcr0;
5958c2ecf20Sopenharmony_ci	u32 mmcr1;
5968c2ecf20Sopenharmony_ci	u64 mmcr2;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	/* per thread SPRs that get lost in shallow states */
5998c2ecf20Sopenharmony_ci	u64 amr;
6008c2ecf20Sopenharmony_ci	u64 iamr;
6018c2ecf20Sopenharmony_ci	u64 amor;
6028c2ecf20Sopenharmony_ci	u64 uamor;
6038c2ecf20Sopenharmony_ci};
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
6088c2ecf20Sopenharmony_ci	int first = cpu_first_thread_sibling(cpu);
6098c2ecf20Sopenharmony_ci	unsigned long *state = &paca_ptrs[first]->idle_state;
6108c2ecf20Sopenharmony_ci	unsigned long core_thread_mask = (1UL << threads_per_core) - 1;
6118c2ecf20Sopenharmony_ci	unsigned long srr1;
6128c2ecf20Sopenharmony_ci	unsigned long pls;
6138c2ecf20Sopenharmony_ci	unsigned long mmcr0 = 0;
6148c2ecf20Sopenharmony_ci	unsigned long mmcra = 0;
6158c2ecf20Sopenharmony_ci	struct p9_sprs sprs = {}; /* avoid false used-uninitialised */
6168c2ecf20Sopenharmony_ci	bool sprs_saved = false;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	if (!(psscr & (PSSCR_EC|PSSCR_ESL))) {
6198c2ecf20Sopenharmony_ci		/* EC=ESL=0 case */
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci		BUG_ON(!mmu_on);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci		/*
6248c2ecf20Sopenharmony_ci		 * Wake synchronously. SRESET via xscom may still cause
6258c2ecf20Sopenharmony_ci		 * a 0x100 powersave wakeup with SRR1 reason!
6268c2ecf20Sopenharmony_ci		 */
6278c2ecf20Sopenharmony_ci		srr1 = isa300_idle_stop_noloss(psscr);		/* go idle */
6288c2ecf20Sopenharmony_ci		if (likely(!srr1))
6298c2ecf20Sopenharmony_ci			return 0;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		/*
6328c2ecf20Sopenharmony_ci		 * Registers not saved, can't recover!
6338c2ecf20Sopenharmony_ci		 * This would be a hardware bug
6348c2ecf20Sopenharmony_ci		 */
6358c2ecf20Sopenharmony_ci		BUG_ON((srr1 & SRR1_WAKESTATE) != SRR1_WS_NOLOSS);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci		goto out;
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	/* EC=ESL=1 case */
6418c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
6428c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TM_XER_SO_BUG)) {
6438c2ecf20Sopenharmony_ci		local_paca->requested_psscr = psscr;
6448c2ecf20Sopenharmony_ci		/* order setting requested_psscr vs testing dont_stop */
6458c2ecf20Sopenharmony_ci		smp_mb();
6468c2ecf20Sopenharmony_ci		if (atomic_read(&local_paca->dont_stop)) {
6478c2ecf20Sopenharmony_ci			local_paca->requested_psscr = 0;
6488c2ecf20Sopenharmony_ci			return 0;
6498c2ecf20Sopenharmony_ci		}
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci#endif
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (!cpu_has_feature(CPU_FTR_POWER9_DD2_1)) {
6548c2ecf20Sopenharmony_ci		 /*
6558c2ecf20Sopenharmony_ci		  * POWER9 DD2 can incorrectly set PMAO when waking up
6568c2ecf20Sopenharmony_ci		  * after a state-loss idle. Saving and restoring MMCR0
6578c2ecf20Sopenharmony_ci		  * over idle is a workaround.
6588c2ecf20Sopenharmony_ci		  */
6598c2ecf20Sopenharmony_ci		mmcr0		= mfspr(SPRN_MMCR0);
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	if ((psscr & PSSCR_RL_MASK) >= deep_spr_loss_state) {
6638c2ecf20Sopenharmony_ci		sprs.lpcr	= mfspr(SPRN_LPCR);
6648c2ecf20Sopenharmony_ci		sprs.hfscr	= mfspr(SPRN_HFSCR);
6658c2ecf20Sopenharmony_ci		sprs.fscr	= mfspr(SPRN_FSCR);
6668c2ecf20Sopenharmony_ci		sprs.pid	= mfspr(SPRN_PID);
6678c2ecf20Sopenharmony_ci		sprs.purr	= mfspr(SPRN_PURR);
6688c2ecf20Sopenharmony_ci		sprs.spurr	= mfspr(SPRN_SPURR);
6698c2ecf20Sopenharmony_ci		sprs.dscr	= mfspr(SPRN_DSCR);
6708c2ecf20Sopenharmony_ci		sprs.wort	= mfspr(SPRN_WORT);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		sprs.mmcra	= mfspr(SPRN_MMCRA);
6738c2ecf20Sopenharmony_ci		sprs.mmcr0	= mfspr(SPRN_MMCR0);
6748c2ecf20Sopenharmony_ci		sprs.mmcr1	= mfspr(SPRN_MMCR1);
6758c2ecf20Sopenharmony_ci		sprs.mmcr2	= mfspr(SPRN_MMCR2);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		sprs.ptcr	= mfspr(SPRN_PTCR);
6788c2ecf20Sopenharmony_ci		sprs.rpr	= mfspr(SPRN_RPR);
6798c2ecf20Sopenharmony_ci		sprs.tscr	= mfspr(SPRN_TSCR);
6808c2ecf20Sopenharmony_ci		if (!firmware_has_feature(FW_FEATURE_ULTRAVISOR))
6818c2ecf20Sopenharmony_ci			sprs.ldbar = mfspr(SPRN_LDBAR);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci		sprs_saved = true;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci		atomic_start_thread_idle();
6868c2ecf20Sopenharmony_ci	}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	sprs.amr	= mfspr(SPRN_AMR);
6898c2ecf20Sopenharmony_ci	sprs.iamr	= mfspr(SPRN_IAMR);
6908c2ecf20Sopenharmony_ci	sprs.amor	= mfspr(SPRN_AMOR);
6918c2ecf20Sopenharmony_ci	sprs.uamor	= mfspr(SPRN_UAMOR);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	srr1 = isa300_idle_stop_mayloss(psscr);		/* go idle */
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
6968c2ecf20Sopenharmony_ci	local_paca->requested_psscr = 0;
6978c2ecf20Sopenharmony_ci#endif
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	psscr = mfspr(SPRN_PSSCR);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!srr1);
7028c2ecf20Sopenharmony_ci	WARN_ON_ONCE(mfmsr() & (MSR_IR|MSR_DR));
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if ((srr1 & SRR1_WAKESTATE) != SRR1_WS_NOLOSS) {
7058c2ecf20Sopenharmony_ci		/*
7068c2ecf20Sopenharmony_ci		 * We don't need an isync after the mtsprs here because the
7078c2ecf20Sopenharmony_ci		 * upcoming mtmsrd is execution synchronizing.
7088c2ecf20Sopenharmony_ci		 */
7098c2ecf20Sopenharmony_ci		mtspr(SPRN_AMR,		sprs.amr);
7108c2ecf20Sopenharmony_ci		mtspr(SPRN_IAMR,	sprs.iamr);
7118c2ecf20Sopenharmony_ci		mtspr(SPRN_AMOR,	sprs.amor);
7128c2ecf20Sopenharmony_ci		mtspr(SPRN_UAMOR,	sprs.uamor);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci		/*
7158c2ecf20Sopenharmony_ci		 * Workaround for POWER9 DD2.0, if we lost resources, the ERAT
7168c2ecf20Sopenharmony_ci		 * might have been corrupted and needs flushing. We also need
7178c2ecf20Sopenharmony_ci		 * to reload MMCR0 (see mmcr0 comment above).
7188c2ecf20Sopenharmony_ci		 */
7198c2ecf20Sopenharmony_ci		if (!cpu_has_feature(CPU_FTR_POWER9_DD2_1)) {
7208c2ecf20Sopenharmony_ci			asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT);
7218c2ecf20Sopenharmony_ci			mtspr(SPRN_MMCR0, mmcr0);
7228c2ecf20Sopenharmony_ci		}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci		/*
7258c2ecf20Sopenharmony_ci		 * DD2.2 and earlier need to set then clear bit 60 in MMCRA
7268c2ecf20Sopenharmony_ci		 * to ensure the PMU starts running.
7278c2ecf20Sopenharmony_ci		 */
7288c2ecf20Sopenharmony_ci		mmcra = mfspr(SPRN_MMCRA);
7298c2ecf20Sopenharmony_ci		mmcra |= PPC_BIT(60);
7308c2ecf20Sopenharmony_ci		mtspr(SPRN_MMCRA, mmcra);
7318c2ecf20Sopenharmony_ci		mmcra &= ~PPC_BIT(60);
7328c2ecf20Sopenharmony_ci		mtspr(SPRN_MMCRA, mmcra);
7338c2ecf20Sopenharmony_ci	}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	if (unlikely((srr1 & SRR1_WAKEMASK_P8) == SRR1_WAKEHMI))
7368c2ecf20Sopenharmony_ci		hmi_exception_realmode(NULL);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/*
7398c2ecf20Sopenharmony_ci	 * On POWER9, SRR1 bits do not match exactly as expected.
7408c2ecf20Sopenharmony_ci	 * SRR1_WS_GPRLOSS (10b) can also result in SPR loss, so
7418c2ecf20Sopenharmony_ci	 * just always test PSSCR for SPR/TB state loss.
7428c2ecf20Sopenharmony_ci	 */
7438c2ecf20Sopenharmony_ci	pls = (psscr & PSSCR_PLS) >> PSSCR_PLS_SHIFT;
7448c2ecf20Sopenharmony_ci	if (likely(pls < deep_spr_loss_state)) {
7458c2ecf20Sopenharmony_ci		if (sprs_saved)
7468c2ecf20Sopenharmony_ci			atomic_stop_thread_idle();
7478c2ecf20Sopenharmony_ci		goto out;
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	/* HV state loss */
7518c2ecf20Sopenharmony_ci	BUG_ON(!sprs_saved);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	atomic_lock_thread_idle();
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if ((*state & core_thread_mask) != 0)
7568c2ecf20Sopenharmony_ci		goto core_woken;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	/* Per-core SPRs */
7598c2ecf20Sopenharmony_ci	mtspr(SPRN_PTCR,	sprs.ptcr);
7608c2ecf20Sopenharmony_ci	mtspr(SPRN_RPR,		sprs.rpr);
7618c2ecf20Sopenharmony_ci	mtspr(SPRN_TSCR,	sprs.tscr);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (pls >= pnv_first_tb_loss_level) {
7648c2ecf20Sopenharmony_ci		/* TB loss */
7658c2ecf20Sopenharmony_ci		if (opal_resync_timebase() != OPAL_SUCCESS)
7668c2ecf20Sopenharmony_ci			BUG();
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	/*
7708c2ecf20Sopenharmony_ci	 * isync after restoring shared SPRs and before unlocking. Unlock
7718c2ecf20Sopenharmony_ci	 * only contains hwsync which does not necessarily do the right
7728c2ecf20Sopenharmony_ci	 * thing for SPRs.
7738c2ecf20Sopenharmony_ci	 */
7748c2ecf20Sopenharmony_ci	isync();
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cicore_woken:
7778c2ecf20Sopenharmony_ci	atomic_unlock_and_stop_thread_idle();
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	/* Per-thread SPRs */
7808c2ecf20Sopenharmony_ci	mtspr(SPRN_LPCR,	sprs.lpcr);
7818c2ecf20Sopenharmony_ci	mtspr(SPRN_HFSCR,	sprs.hfscr);
7828c2ecf20Sopenharmony_ci	mtspr(SPRN_FSCR,	sprs.fscr);
7838c2ecf20Sopenharmony_ci	mtspr(SPRN_PID,		sprs.pid);
7848c2ecf20Sopenharmony_ci	mtspr(SPRN_PURR,	sprs.purr);
7858c2ecf20Sopenharmony_ci	mtspr(SPRN_SPURR,	sprs.spurr);
7868c2ecf20Sopenharmony_ci	mtspr(SPRN_DSCR,	sprs.dscr);
7878c2ecf20Sopenharmony_ci	mtspr(SPRN_WORT,	sprs.wort);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	mtspr(SPRN_MMCRA,	sprs.mmcra);
7908c2ecf20Sopenharmony_ci	mtspr(SPRN_MMCR0,	sprs.mmcr0);
7918c2ecf20Sopenharmony_ci	mtspr(SPRN_MMCR1,	sprs.mmcr1);
7928c2ecf20Sopenharmony_ci	mtspr(SPRN_MMCR2,	sprs.mmcr2);
7938c2ecf20Sopenharmony_ci	if (!firmware_has_feature(FW_FEATURE_ULTRAVISOR))
7948c2ecf20Sopenharmony_ci		mtspr(SPRN_LDBAR, sprs.ldbar);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	mtspr(SPRN_SPRG3,	local_paca->sprg_vdso);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	if (!radix_enabled())
7998c2ecf20Sopenharmony_ci		__slb_restore_bolted_realmode();
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ciout:
8028c2ecf20Sopenharmony_ci	if (mmu_on)
8038c2ecf20Sopenharmony_ci		mtmsr(MSR_KERNEL);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return srr1;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
8098c2ecf20Sopenharmony_ci/*
8108c2ecf20Sopenharmony_ci * This is used in working around bugs in thread reconfiguration
8118c2ecf20Sopenharmony_ci * on POWER9 (at least up to Nimbus DD2.2) relating to transactional
8128c2ecf20Sopenharmony_ci * memory and the way that XER[SO] is checkpointed.
8138c2ecf20Sopenharmony_ci * This function forces the core into SMT4 in order by asking
8148c2ecf20Sopenharmony_ci * all other threads not to stop, and sending a message to any
8158c2ecf20Sopenharmony_ci * that are in a stop state.
8168c2ecf20Sopenharmony_ci * Must be called with preemption disabled.
8178c2ecf20Sopenharmony_ci */
8188c2ecf20Sopenharmony_civoid pnv_power9_force_smt4_catch(void)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	int cpu, cpu0, thr;
8218c2ecf20Sopenharmony_ci	int awake_threads = 1;		/* this thread is awake */
8228c2ecf20Sopenharmony_ci	int poke_threads = 0;
8238c2ecf20Sopenharmony_ci	int need_awake = threads_per_core;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	cpu = smp_processor_id();
8268c2ecf20Sopenharmony_ci	cpu0 = cpu & ~(threads_per_core - 1);
8278c2ecf20Sopenharmony_ci	for (thr = 0; thr < threads_per_core; ++thr) {
8288c2ecf20Sopenharmony_ci		if (cpu != cpu0 + thr)
8298c2ecf20Sopenharmony_ci			atomic_inc(&paca_ptrs[cpu0+thr]->dont_stop);
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci	/* order setting dont_stop vs testing requested_psscr */
8328c2ecf20Sopenharmony_ci	smp_mb();
8338c2ecf20Sopenharmony_ci	for (thr = 0; thr < threads_per_core; ++thr) {
8348c2ecf20Sopenharmony_ci		if (!paca_ptrs[cpu0+thr]->requested_psscr)
8358c2ecf20Sopenharmony_ci			++awake_threads;
8368c2ecf20Sopenharmony_ci		else
8378c2ecf20Sopenharmony_ci			poke_threads |= (1 << thr);
8388c2ecf20Sopenharmony_ci	}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	/* If at least 3 threads are awake, the core is in SMT4 already */
8418c2ecf20Sopenharmony_ci	if (awake_threads < need_awake) {
8428c2ecf20Sopenharmony_ci		/* We have to wake some threads; we'll use msgsnd */
8438c2ecf20Sopenharmony_ci		for (thr = 0; thr < threads_per_core; ++thr) {
8448c2ecf20Sopenharmony_ci			if (poke_threads & (1 << thr)) {
8458c2ecf20Sopenharmony_ci				ppc_msgsnd_sync();
8468c2ecf20Sopenharmony_ci				ppc_msgsnd(PPC_DBELL_MSGTYPE, 0,
8478c2ecf20Sopenharmony_ci					   paca_ptrs[cpu0+thr]->hw_cpu_id);
8488c2ecf20Sopenharmony_ci			}
8498c2ecf20Sopenharmony_ci		}
8508c2ecf20Sopenharmony_ci		/* now spin until at least 3 threads are awake */
8518c2ecf20Sopenharmony_ci		do {
8528c2ecf20Sopenharmony_ci			for (thr = 0; thr < threads_per_core; ++thr) {
8538c2ecf20Sopenharmony_ci				if ((poke_threads & (1 << thr)) &&
8548c2ecf20Sopenharmony_ci				    !paca_ptrs[cpu0+thr]->requested_psscr) {
8558c2ecf20Sopenharmony_ci					++awake_threads;
8568c2ecf20Sopenharmony_ci					poke_threads &= ~(1 << thr);
8578c2ecf20Sopenharmony_ci				}
8588c2ecf20Sopenharmony_ci			}
8598c2ecf20Sopenharmony_ci		} while (awake_threads < need_awake);
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci}
8628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pnv_power9_force_smt4_catch);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_civoid pnv_power9_force_smt4_release(void)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	int cpu, cpu0, thr;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	cpu = smp_processor_id();
8698c2ecf20Sopenharmony_ci	cpu0 = cpu & ~(threads_per_core - 1);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	/* clear all the dont_stop flags */
8728c2ecf20Sopenharmony_ci	for (thr = 0; thr < threads_per_core; ++thr) {
8738c2ecf20Sopenharmony_ci		if (cpu != cpu0 + thr)
8748c2ecf20Sopenharmony_ci			atomic_dec(&paca_ptrs[cpu0+thr]->dont_stop);
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci}
8778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pnv_power9_force_smt4_release);
8788c2ecf20Sopenharmony_ci#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_cistruct p10_sprs {
8818c2ecf20Sopenharmony_ci	/*
8828c2ecf20Sopenharmony_ci	 * SPRs that get lost in shallow states:
8838c2ecf20Sopenharmony_ci	 *
8848c2ecf20Sopenharmony_ci	 * P10 loses CR, LR, CTR, FPSCR, VSCR, XER, TAR, SPRG2, and HSPRG1
8858c2ecf20Sopenharmony_ci	 * isa300 idle routines restore CR, LR.
8868c2ecf20Sopenharmony_ci	 * CTR is volatile
8878c2ecf20Sopenharmony_ci	 * idle thread doesn't use FP or VEC
8888c2ecf20Sopenharmony_ci	 * kernel doesn't use TAR
8898c2ecf20Sopenharmony_ci	 * HSPRG1 is only live in HV interrupt entry
8908c2ecf20Sopenharmony_ci	 * SPRG2 is only live in KVM guests, KVM handles it.
8918c2ecf20Sopenharmony_ci	 */
8928c2ecf20Sopenharmony_ci};
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_cistatic unsigned long power10_idle_stop(unsigned long psscr, bool mmu_on)
8958c2ecf20Sopenharmony_ci{
8968c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
8978c2ecf20Sopenharmony_ci	int first = cpu_first_thread_sibling(cpu);
8988c2ecf20Sopenharmony_ci	unsigned long *state = &paca_ptrs[first]->idle_state;
8998c2ecf20Sopenharmony_ci	unsigned long core_thread_mask = (1UL << threads_per_core) - 1;
9008c2ecf20Sopenharmony_ci	unsigned long srr1;
9018c2ecf20Sopenharmony_ci	unsigned long pls;
9028c2ecf20Sopenharmony_ci//	struct p10_sprs sprs = {}; /* avoid false used-uninitialised */
9038c2ecf20Sopenharmony_ci	bool sprs_saved = false;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	if (!(psscr & (PSSCR_EC|PSSCR_ESL))) {
9068c2ecf20Sopenharmony_ci		/* EC=ESL=0 case */
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci		BUG_ON(!mmu_on);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci		/*
9118c2ecf20Sopenharmony_ci		 * Wake synchronously. SRESET via xscom may still cause
9128c2ecf20Sopenharmony_ci		 * a 0x100 powersave wakeup with SRR1 reason!
9138c2ecf20Sopenharmony_ci		 */
9148c2ecf20Sopenharmony_ci		srr1 = isa300_idle_stop_noloss(psscr);		/* go idle */
9158c2ecf20Sopenharmony_ci		if (likely(!srr1))
9168c2ecf20Sopenharmony_ci			return 0;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci		/*
9198c2ecf20Sopenharmony_ci		 * Registers not saved, can't recover!
9208c2ecf20Sopenharmony_ci		 * This would be a hardware bug
9218c2ecf20Sopenharmony_ci		 */
9228c2ecf20Sopenharmony_ci		BUG_ON((srr1 & SRR1_WAKESTATE) != SRR1_WS_NOLOSS);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci		goto out;
9258c2ecf20Sopenharmony_ci	}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	/* EC=ESL=1 case */
9288c2ecf20Sopenharmony_ci	if ((psscr & PSSCR_RL_MASK) >= deep_spr_loss_state) {
9298c2ecf20Sopenharmony_ci		/* XXX: save SPRs for deep state loss here. */
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci		sprs_saved = true;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci		atomic_start_thread_idle();
9348c2ecf20Sopenharmony_ci	}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	srr1 = isa300_idle_stop_mayloss(psscr);		/* go idle */
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	psscr = mfspr(SPRN_PSSCR);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!srr1);
9418c2ecf20Sopenharmony_ci	WARN_ON_ONCE(mfmsr() & (MSR_IR|MSR_DR));
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	if (unlikely((srr1 & SRR1_WAKEMASK_P8) == SRR1_WAKEHMI))
9448c2ecf20Sopenharmony_ci		hmi_exception_realmode(NULL);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	/*
9478c2ecf20Sopenharmony_ci	 * On POWER10, SRR1 bits do not match exactly as expected.
9488c2ecf20Sopenharmony_ci	 * SRR1_WS_GPRLOSS (10b) can also result in SPR loss, so
9498c2ecf20Sopenharmony_ci	 * just always test PSSCR for SPR/TB state loss.
9508c2ecf20Sopenharmony_ci	 */
9518c2ecf20Sopenharmony_ci	pls = (psscr & PSSCR_PLS) >> PSSCR_PLS_SHIFT;
9528c2ecf20Sopenharmony_ci	if (likely(pls < deep_spr_loss_state)) {
9538c2ecf20Sopenharmony_ci		if (sprs_saved)
9548c2ecf20Sopenharmony_ci			atomic_stop_thread_idle();
9558c2ecf20Sopenharmony_ci		goto out;
9568c2ecf20Sopenharmony_ci	}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	/* HV state loss */
9598c2ecf20Sopenharmony_ci	BUG_ON(!sprs_saved);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	atomic_lock_thread_idle();
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	if ((*state & core_thread_mask) != 0)
9648c2ecf20Sopenharmony_ci		goto core_woken;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	/* XXX: restore per-core SPRs here */
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	if (pls >= pnv_first_tb_loss_level) {
9698c2ecf20Sopenharmony_ci		/* TB loss */
9708c2ecf20Sopenharmony_ci		if (opal_resync_timebase() != OPAL_SUCCESS)
9718c2ecf20Sopenharmony_ci			BUG();
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	/*
9758c2ecf20Sopenharmony_ci	 * isync after restoring shared SPRs and before unlocking. Unlock
9768c2ecf20Sopenharmony_ci	 * only contains hwsync which does not necessarily do the right
9778c2ecf20Sopenharmony_ci	 * thing for SPRs.
9788c2ecf20Sopenharmony_ci	 */
9798c2ecf20Sopenharmony_ci	isync();
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_cicore_woken:
9828c2ecf20Sopenharmony_ci	atomic_unlock_and_stop_thread_idle();
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	/* XXX: restore per-thread SPRs here */
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	if (!radix_enabled())
9878c2ecf20Sopenharmony_ci		__slb_restore_bolted_realmode();
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ciout:
9908c2ecf20Sopenharmony_ci	if (mmu_on)
9918c2ecf20Sopenharmony_ci		mtmsr(MSR_KERNEL);
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	return srr1;
9948c2ecf20Sopenharmony_ci}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
9978c2ecf20Sopenharmony_cistatic unsigned long arch300_offline_stop(unsigned long psscr)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	unsigned long srr1;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci#ifndef CONFIG_KVM_BOOK3S_HV_POSSIBLE
10028c2ecf20Sopenharmony_ci	__ppc64_runlatch_off();
10038c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_31))
10048c2ecf20Sopenharmony_ci		srr1 = power10_idle_stop(psscr, true);
10058c2ecf20Sopenharmony_ci	else
10068c2ecf20Sopenharmony_ci		srr1 = power9_idle_stop(psscr, true);
10078c2ecf20Sopenharmony_ci	__ppc64_runlatch_on();
10088c2ecf20Sopenharmony_ci#else
10098c2ecf20Sopenharmony_ci	/*
10108c2ecf20Sopenharmony_ci	 * Tell KVM we're entering idle.
10118c2ecf20Sopenharmony_ci	 * This does not have to be done in real mode because the P9 MMU
10128c2ecf20Sopenharmony_ci	 * is independent per-thread. Some steppings share radix/hash mode
10138c2ecf20Sopenharmony_ci	 * between threads, but in that case KVM has a barrier sync in real
10148c2ecf20Sopenharmony_ci	 * mode before and after switching between radix and hash.
10158c2ecf20Sopenharmony_ci	 *
10168c2ecf20Sopenharmony_ci	 * kvm_start_guest must still be called in real mode though, hence
10178c2ecf20Sopenharmony_ci	 * the false argument.
10188c2ecf20Sopenharmony_ci	 */
10198c2ecf20Sopenharmony_ci	local_paca->kvm_hstate.hwthread_state = KVM_HWTHREAD_IN_IDLE;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	__ppc64_runlatch_off();
10228c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_31))
10238c2ecf20Sopenharmony_ci		srr1 = power10_idle_stop(psscr, false);
10248c2ecf20Sopenharmony_ci	else
10258c2ecf20Sopenharmony_ci		srr1 = power9_idle_stop(psscr, false);
10268c2ecf20Sopenharmony_ci	__ppc64_runlatch_on();
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	local_paca->kvm_hstate.hwthread_state = KVM_HWTHREAD_IN_KERNEL;
10298c2ecf20Sopenharmony_ci	/* Order setting hwthread_state vs. testing hwthread_req */
10308c2ecf20Sopenharmony_ci	smp_mb();
10318c2ecf20Sopenharmony_ci	if (local_paca->kvm_hstate.hwthread_req)
10328c2ecf20Sopenharmony_ci		srr1 = idle_kvm_start_guest(srr1);
10338c2ecf20Sopenharmony_ci	mtmsr(MSR_KERNEL);
10348c2ecf20Sopenharmony_ci#endif
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	return srr1;
10378c2ecf20Sopenharmony_ci}
10388c2ecf20Sopenharmony_ci#endif
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_civoid arch300_idle_type(unsigned long stop_psscr_val,
10418c2ecf20Sopenharmony_ci				      unsigned long stop_psscr_mask)
10428c2ecf20Sopenharmony_ci{
10438c2ecf20Sopenharmony_ci	unsigned long psscr;
10448c2ecf20Sopenharmony_ci	unsigned long srr1;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	if (!prep_irq_for_idle_irqsoff())
10478c2ecf20Sopenharmony_ci		return;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	psscr = mfspr(SPRN_PSSCR);
10508c2ecf20Sopenharmony_ci	psscr = (psscr & ~stop_psscr_mask) | stop_psscr_val;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	__ppc64_runlatch_off();
10538c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_31))
10548c2ecf20Sopenharmony_ci		srr1 = power10_idle_stop(psscr, true);
10558c2ecf20Sopenharmony_ci	else
10568c2ecf20Sopenharmony_ci		srr1 = power9_idle_stop(psscr, true);
10578c2ecf20Sopenharmony_ci	__ppc64_runlatch_on();
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	fini_irq_for_idle_irqsoff();
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	irq_set_pending_from_srr1(srr1);
10628c2ecf20Sopenharmony_ci}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci/*
10658c2ecf20Sopenharmony_ci * Used for ppc_md.power_save which needs a function with no parameters
10668c2ecf20Sopenharmony_ci */
10678c2ecf20Sopenharmony_cistatic void arch300_idle(void)
10688c2ecf20Sopenharmony_ci{
10698c2ecf20Sopenharmony_ci	arch300_idle_type(pnv_default_stop_val, pnv_default_stop_mask);
10708c2ecf20Sopenharmony_ci}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_civoid pnv_program_cpu_hotplug_lpcr(unsigned int cpu, u64 lpcr_val)
10758c2ecf20Sopenharmony_ci{
10768c2ecf20Sopenharmony_ci	u64 pir = get_hard_smp_processor_id(cpu);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	mtspr(SPRN_LPCR, lpcr_val);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	/*
10818c2ecf20Sopenharmony_ci	 * Program the LPCR via stop-api only if the deepest stop state
10828c2ecf20Sopenharmony_ci	 * can lose hypervisor context.
10838c2ecf20Sopenharmony_ci	 */
10848c2ecf20Sopenharmony_ci	if (supported_cpuidle_states & OPAL_PM_LOSE_FULL_CONTEXT)
10858c2ecf20Sopenharmony_ci		opal_slw_set_reg(pir, SPRN_LPCR, lpcr_val);
10868c2ecf20Sopenharmony_ci}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci/*
10898c2ecf20Sopenharmony_ci * pnv_cpu_offline: A function that puts the CPU into the deepest
10908c2ecf20Sopenharmony_ci * available platform idle state on a CPU-Offline.
10918c2ecf20Sopenharmony_ci * interrupts hard disabled and no lazy irq pending.
10928c2ecf20Sopenharmony_ci */
10938c2ecf20Sopenharmony_ciunsigned long pnv_cpu_offline(unsigned int cpu)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	unsigned long srr1;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	__ppc64_runlatch_off();
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_300) && deepest_stop_found) {
11008c2ecf20Sopenharmony_ci		unsigned long psscr;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci		psscr = mfspr(SPRN_PSSCR);
11038c2ecf20Sopenharmony_ci		psscr = (psscr & ~pnv_deepest_stop_psscr_mask) |
11048c2ecf20Sopenharmony_ci						pnv_deepest_stop_psscr_val;
11058c2ecf20Sopenharmony_ci		srr1 = arch300_offline_stop(psscr);
11068c2ecf20Sopenharmony_ci	} else if (cpu_has_feature(CPU_FTR_ARCH_206) && power7_offline_type) {
11078c2ecf20Sopenharmony_ci		srr1 = power7_offline();
11088c2ecf20Sopenharmony_ci	} else {
11098c2ecf20Sopenharmony_ci		/* This is the fallback method. We emulate snooze */
11108c2ecf20Sopenharmony_ci		while (!generic_check_cpu_restart(cpu)) {
11118c2ecf20Sopenharmony_ci			HMT_low();
11128c2ecf20Sopenharmony_ci			HMT_very_low();
11138c2ecf20Sopenharmony_ci		}
11148c2ecf20Sopenharmony_ci		srr1 = 0;
11158c2ecf20Sopenharmony_ci		HMT_medium();
11168c2ecf20Sopenharmony_ci	}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	__ppc64_runlatch_on();
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	return srr1;
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci#endif
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci/*
11258c2ecf20Sopenharmony_ci * Power ISA 3.0 idle initialization.
11268c2ecf20Sopenharmony_ci *
11278c2ecf20Sopenharmony_ci * POWER ISA 3.0 defines a new SPR Processor stop Status and Control
11288c2ecf20Sopenharmony_ci * Register (PSSCR) to control idle behavior.
11298c2ecf20Sopenharmony_ci *
11308c2ecf20Sopenharmony_ci * PSSCR layout:
11318c2ecf20Sopenharmony_ci * ----------------------------------------------------------
11328c2ecf20Sopenharmony_ci * | PLS | /// | SD | ESL | EC | PSLL | /// | TR | MTL | RL |
11338c2ecf20Sopenharmony_ci * ----------------------------------------------------------
11348c2ecf20Sopenharmony_ci * 0      4     41   42    43   44     48    54   56    60
11358c2ecf20Sopenharmony_ci *
11368c2ecf20Sopenharmony_ci * PSSCR key fields:
11378c2ecf20Sopenharmony_ci *	Bits 0:3  - Power-Saving Level Status (PLS). This field indicates the
11388c2ecf20Sopenharmony_ci *	lowest power-saving state the thread entered since stop instruction was
11398c2ecf20Sopenharmony_ci *	last executed.
11408c2ecf20Sopenharmony_ci *
11418c2ecf20Sopenharmony_ci *	Bit 41 - Status Disable(SD)
11428c2ecf20Sopenharmony_ci *	0 - Shows PLS entries
11438c2ecf20Sopenharmony_ci *	1 - PLS entries are all 0
11448c2ecf20Sopenharmony_ci *
11458c2ecf20Sopenharmony_ci *	Bit 42 - Enable State Loss
11468c2ecf20Sopenharmony_ci *	0 - No state is lost irrespective of other fields
11478c2ecf20Sopenharmony_ci *	1 - Allows state loss
11488c2ecf20Sopenharmony_ci *
11498c2ecf20Sopenharmony_ci *	Bit 43 - Exit Criterion
11508c2ecf20Sopenharmony_ci *	0 - Exit from power-save mode on any interrupt
11518c2ecf20Sopenharmony_ci *	1 - Exit from power-save mode controlled by LPCR's PECE bits
11528c2ecf20Sopenharmony_ci *
11538c2ecf20Sopenharmony_ci *	Bits 44:47 - Power-Saving Level Limit
11548c2ecf20Sopenharmony_ci *	This limits the power-saving level that can be entered into.
11558c2ecf20Sopenharmony_ci *
11568c2ecf20Sopenharmony_ci *	Bits 60:63 - Requested Level
11578c2ecf20Sopenharmony_ci *	Used to specify which power-saving level must be entered on executing
11588c2ecf20Sopenharmony_ci *	stop instruction
11598c2ecf20Sopenharmony_ci */
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ciint validate_psscr_val_mask(u64 *psscr_val, u64 *psscr_mask, u32 flags)
11628c2ecf20Sopenharmony_ci{
11638c2ecf20Sopenharmony_ci	int err = 0;
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	/*
11668c2ecf20Sopenharmony_ci	 * psscr_mask == 0xf indicates an older firmware.
11678c2ecf20Sopenharmony_ci	 * Set remaining fields of psscr to the default values.
11688c2ecf20Sopenharmony_ci	 * See NOTE above definition of PSSCR_HV_DEFAULT_VAL
11698c2ecf20Sopenharmony_ci	 */
11708c2ecf20Sopenharmony_ci	if (*psscr_mask == 0xf) {
11718c2ecf20Sopenharmony_ci		*psscr_val = *psscr_val | PSSCR_HV_DEFAULT_VAL;
11728c2ecf20Sopenharmony_ci		*psscr_mask = PSSCR_HV_DEFAULT_MASK;
11738c2ecf20Sopenharmony_ci		return err;
11748c2ecf20Sopenharmony_ci	}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	/*
11778c2ecf20Sopenharmony_ci	 * New firmware is expected to set the psscr_val bits correctly.
11788c2ecf20Sopenharmony_ci	 * Validate that the following invariants are correctly maintained by
11798c2ecf20Sopenharmony_ci	 * the new firmware.
11808c2ecf20Sopenharmony_ci	 * - ESL bit value matches the EC bit value.
11818c2ecf20Sopenharmony_ci	 * - ESL bit is set for all the deep stop states.
11828c2ecf20Sopenharmony_ci	 */
11838c2ecf20Sopenharmony_ci	if (GET_PSSCR_ESL(*psscr_val) != GET_PSSCR_EC(*psscr_val)) {
11848c2ecf20Sopenharmony_ci		err = ERR_EC_ESL_MISMATCH;
11858c2ecf20Sopenharmony_ci	} else if ((flags & OPAL_PM_LOSE_FULL_CONTEXT) &&
11868c2ecf20Sopenharmony_ci		GET_PSSCR_ESL(*psscr_val) == 0) {
11878c2ecf20Sopenharmony_ci		err = ERR_DEEP_STATE_ESL_MISMATCH;
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	return err;
11918c2ecf20Sopenharmony_ci}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci/*
11948c2ecf20Sopenharmony_ci * pnv_arch300_idle_init: Initializes the default idle state, first
11958c2ecf20Sopenharmony_ci *                        deep idle state and deepest idle state on
11968c2ecf20Sopenharmony_ci *                        ISA 3.0 CPUs.
11978c2ecf20Sopenharmony_ci *
11988c2ecf20Sopenharmony_ci * @np: /ibm,opal/power-mgt device node
11998c2ecf20Sopenharmony_ci * @flags: cpu-idle-state-flags array
12008c2ecf20Sopenharmony_ci * @dt_idle_states: Number of idle state entries
12018c2ecf20Sopenharmony_ci * Returns 0 on success
12028c2ecf20Sopenharmony_ci */
12038c2ecf20Sopenharmony_cistatic void __init pnv_arch300_idle_init(void)
12048c2ecf20Sopenharmony_ci{
12058c2ecf20Sopenharmony_ci	u64 max_residency_ns = 0;
12068c2ecf20Sopenharmony_ci	int i;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	/* stop is not really architected, we only have p9,p10 drivers */
12098c2ecf20Sopenharmony_ci	if (!pvr_version_is(PVR_POWER10) && !pvr_version_is(PVR_POWER9))
12108c2ecf20Sopenharmony_ci		return;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	/*
12138c2ecf20Sopenharmony_ci	 * pnv_deepest_stop_{val,mask} should be set to values corresponding to
12148c2ecf20Sopenharmony_ci	 * the deepest stop state.
12158c2ecf20Sopenharmony_ci	 *
12168c2ecf20Sopenharmony_ci	 * pnv_default_stop_{val,mask} should be set to values corresponding to
12178c2ecf20Sopenharmony_ci	 * the deepest loss-less (OPAL_PM_STOP_INST_FAST) stop state.
12188c2ecf20Sopenharmony_ci	 */
12198c2ecf20Sopenharmony_ci	pnv_first_tb_loss_level = MAX_STOP_STATE + 1;
12208c2ecf20Sopenharmony_ci	deep_spr_loss_state = MAX_STOP_STATE + 1;
12218c2ecf20Sopenharmony_ci	for (i = 0; i < nr_pnv_idle_states; i++) {
12228c2ecf20Sopenharmony_ci		int err;
12238c2ecf20Sopenharmony_ci		struct pnv_idle_states_t *state = &pnv_idle_states[i];
12248c2ecf20Sopenharmony_ci		u64 psscr_rl = state->psscr_val & PSSCR_RL_MASK;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci		/* No deep loss driver implemented for POWER10 yet */
12278c2ecf20Sopenharmony_ci		if (pvr_version_is(PVR_POWER10) &&
12288c2ecf20Sopenharmony_ci				state->flags & (OPAL_PM_TIMEBASE_STOP|OPAL_PM_LOSE_FULL_CONTEXT))
12298c2ecf20Sopenharmony_ci			continue;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci		if ((state->flags & OPAL_PM_TIMEBASE_STOP) &&
12328c2ecf20Sopenharmony_ci		     (pnv_first_tb_loss_level > psscr_rl))
12338c2ecf20Sopenharmony_ci			pnv_first_tb_loss_level = psscr_rl;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci		if ((state->flags & OPAL_PM_LOSE_FULL_CONTEXT) &&
12368c2ecf20Sopenharmony_ci		     (deep_spr_loss_state > psscr_rl))
12378c2ecf20Sopenharmony_ci			deep_spr_loss_state = psscr_rl;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci		/*
12408c2ecf20Sopenharmony_ci		 * The idle code does not deal with TB loss occurring
12418c2ecf20Sopenharmony_ci		 * in a shallower state than SPR loss, so force it to
12428c2ecf20Sopenharmony_ci		 * behave like SPRs are lost if TB is lost. POWER9 would
12438c2ecf20Sopenharmony_ci		 * never encouter this, but a POWER8 core would if it
12448c2ecf20Sopenharmony_ci		 * implemented the stop instruction. So this is for forward
12458c2ecf20Sopenharmony_ci		 * compatibility.
12468c2ecf20Sopenharmony_ci		 */
12478c2ecf20Sopenharmony_ci		if ((state->flags & OPAL_PM_TIMEBASE_STOP) &&
12488c2ecf20Sopenharmony_ci		     (deep_spr_loss_state > psscr_rl))
12498c2ecf20Sopenharmony_ci			deep_spr_loss_state = psscr_rl;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci		err = validate_psscr_val_mask(&state->psscr_val,
12528c2ecf20Sopenharmony_ci					      &state->psscr_mask,
12538c2ecf20Sopenharmony_ci					      state->flags);
12548c2ecf20Sopenharmony_ci		if (err) {
12558c2ecf20Sopenharmony_ci			report_invalid_psscr_val(state->psscr_val, err);
12568c2ecf20Sopenharmony_ci			continue;
12578c2ecf20Sopenharmony_ci		}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci		state->valid = true;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci		if (max_residency_ns < state->residency_ns) {
12628c2ecf20Sopenharmony_ci			max_residency_ns = state->residency_ns;
12638c2ecf20Sopenharmony_ci			pnv_deepest_stop_psscr_val = state->psscr_val;
12648c2ecf20Sopenharmony_ci			pnv_deepest_stop_psscr_mask = state->psscr_mask;
12658c2ecf20Sopenharmony_ci			pnv_deepest_stop_flag = state->flags;
12668c2ecf20Sopenharmony_ci			deepest_stop_found = true;
12678c2ecf20Sopenharmony_ci		}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci		if (!default_stop_found &&
12708c2ecf20Sopenharmony_ci		    (state->flags & OPAL_PM_STOP_INST_FAST)) {
12718c2ecf20Sopenharmony_ci			pnv_default_stop_val = state->psscr_val;
12728c2ecf20Sopenharmony_ci			pnv_default_stop_mask = state->psscr_mask;
12738c2ecf20Sopenharmony_ci			default_stop_found = true;
12748c2ecf20Sopenharmony_ci			WARN_ON(state->flags & OPAL_PM_LOSE_FULL_CONTEXT);
12758c2ecf20Sopenharmony_ci		}
12768c2ecf20Sopenharmony_ci	}
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	if (unlikely(!default_stop_found)) {
12798c2ecf20Sopenharmony_ci		pr_warn("cpuidle-powernv: No suitable default stop state found. Disabling platform idle.\n");
12808c2ecf20Sopenharmony_ci	} else {
12818c2ecf20Sopenharmony_ci		ppc_md.power_save = arch300_idle;
12828c2ecf20Sopenharmony_ci		pr_info("cpuidle-powernv: Default stop: psscr = 0x%016llx,mask=0x%016llx\n",
12838c2ecf20Sopenharmony_ci			pnv_default_stop_val, pnv_default_stop_mask);
12848c2ecf20Sopenharmony_ci	}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	if (unlikely(!deepest_stop_found)) {
12878c2ecf20Sopenharmony_ci		pr_warn("cpuidle-powernv: No suitable stop state for CPU-Hotplug. Offlined CPUs will busy wait");
12888c2ecf20Sopenharmony_ci	} else {
12898c2ecf20Sopenharmony_ci		pr_info("cpuidle-powernv: Deepest stop: psscr = 0x%016llx,mask=0x%016llx\n",
12908c2ecf20Sopenharmony_ci			pnv_deepest_stop_psscr_val,
12918c2ecf20Sopenharmony_ci			pnv_deepest_stop_psscr_mask);
12928c2ecf20Sopenharmony_ci	}
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	pr_info("cpuidle-powernv: First stop level that may lose SPRs = 0x%llx\n",
12958c2ecf20Sopenharmony_ci		deep_spr_loss_state);
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	pr_info("cpuidle-powernv: First stop level that may lose timebase = 0x%llx\n",
12988c2ecf20Sopenharmony_ci		pnv_first_tb_loss_level);
12998c2ecf20Sopenharmony_ci}
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_cistatic void __init pnv_disable_deep_states(void)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	/*
13048c2ecf20Sopenharmony_ci	 * The stop-api is unable to restore hypervisor
13058c2ecf20Sopenharmony_ci	 * resources on wakeup from platform idle states which
13068c2ecf20Sopenharmony_ci	 * lose full context. So disable such states.
13078c2ecf20Sopenharmony_ci	 */
13088c2ecf20Sopenharmony_ci	supported_cpuidle_states &= ~OPAL_PM_LOSE_FULL_CONTEXT;
13098c2ecf20Sopenharmony_ci	pr_warn("cpuidle-powernv: Disabling idle states that lose full context\n");
13108c2ecf20Sopenharmony_ci	pr_warn("cpuidle-powernv: Idle power-savings, CPU-Hotplug affected\n");
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_300) &&
13138c2ecf20Sopenharmony_ci	    (pnv_deepest_stop_flag & OPAL_PM_LOSE_FULL_CONTEXT)) {
13148c2ecf20Sopenharmony_ci		/*
13158c2ecf20Sopenharmony_ci		 * Use the default stop state for CPU-Hotplug
13168c2ecf20Sopenharmony_ci		 * if available.
13178c2ecf20Sopenharmony_ci		 */
13188c2ecf20Sopenharmony_ci		if (default_stop_found) {
13198c2ecf20Sopenharmony_ci			pnv_deepest_stop_psscr_val = pnv_default_stop_val;
13208c2ecf20Sopenharmony_ci			pnv_deepest_stop_psscr_mask = pnv_default_stop_mask;
13218c2ecf20Sopenharmony_ci			pr_warn("cpuidle-powernv: Offlined CPUs will stop with psscr = 0x%016llx\n",
13228c2ecf20Sopenharmony_ci				pnv_deepest_stop_psscr_val);
13238c2ecf20Sopenharmony_ci		} else { /* Fallback to snooze loop for CPU-Hotplug */
13248c2ecf20Sopenharmony_ci			deepest_stop_found = false;
13258c2ecf20Sopenharmony_ci			pr_warn("cpuidle-powernv: Offlined CPUs will busy wait\n");
13268c2ecf20Sopenharmony_ci		}
13278c2ecf20Sopenharmony_ci	}
13288c2ecf20Sopenharmony_ci}
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci/*
13318c2ecf20Sopenharmony_ci * Probe device tree for supported idle states
13328c2ecf20Sopenharmony_ci */
13338c2ecf20Sopenharmony_cistatic void __init pnv_probe_idle_states(void)
13348c2ecf20Sopenharmony_ci{
13358c2ecf20Sopenharmony_ci	int i;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	if (nr_pnv_idle_states < 0) {
13388c2ecf20Sopenharmony_ci		pr_warn("cpuidle-powernv: no idle states found in the DT\n");
13398c2ecf20Sopenharmony_ci		return;
13408c2ecf20Sopenharmony_ci	}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_300))
13438c2ecf20Sopenharmony_ci		pnv_arch300_idle_init();
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	for (i = 0; i < nr_pnv_idle_states; i++)
13468c2ecf20Sopenharmony_ci		supported_cpuidle_states |= pnv_idle_states[i].flags;
13478c2ecf20Sopenharmony_ci}
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci/*
13508c2ecf20Sopenharmony_ci * This function parses device-tree and populates all the information
13518c2ecf20Sopenharmony_ci * into pnv_idle_states structure. It also sets up nr_pnv_idle_states
13528c2ecf20Sopenharmony_ci * which is the number of cpuidle states discovered through device-tree.
13538c2ecf20Sopenharmony_ci */
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_cistatic int pnv_parse_cpuidle_dt(void)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	struct device_node *np;
13588c2ecf20Sopenharmony_ci	int nr_idle_states, i;
13598c2ecf20Sopenharmony_ci	int rc = 0;
13608c2ecf20Sopenharmony_ci	u32 *temp_u32;
13618c2ecf20Sopenharmony_ci	u64 *temp_u64;
13628c2ecf20Sopenharmony_ci	const char **temp_string;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	np = of_find_node_by_path("/ibm,opal/power-mgt");
13658c2ecf20Sopenharmony_ci	if (!np) {
13668c2ecf20Sopenharmony_ci		pr_warn("opal: PowerMgmt Node not found\n");
13678c2ecf20Sopenharmony_ci		return -ENODEV;
13688c2ecf20Sopenharmony_ci	}
13698c2ecf20Sopenharmony_ci	nr_idle_states = of_property_count_u32_elems(np,
13708c2ecf20Sopenharmony_ci						"ibm,cpu-idle-state-flags");
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	pnv_idle_states = kcalloc(nr_idle_states, sizeof(*pnv_idle_states),
13738c2ecf20Sopenharmony_ci				  GFP_KERNEL);
13748c2ecf20Sopenharmony_ci	temp_u32 = kcalloc(nr_idle_states, sizeof(u32),  GFP_KERNEL);
13758c2ecf20Sopenharmony_ci	temp_u64 = kcalloc(nr_idle_states, sizeof(u64),  GFP_KERNEL);
13768c2ecf20Sopenharmony_ci	temp_string = kcalloc(nr_idle_states, sizeof(char *),  GFP_KERNEL);
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	if (!(pnv_idle_states && temp_u32 && temp_u64 && temp_string)) {
13798c2ecf20Sopenharmony_ci		pr_err("Could not allocate memory for dt parsing\n");
13808c2ecf20Sopenharmony_ci		rc = -ENOMEM;
13818c2ecf20Sopenharmony_ci		goto out;
13828c2ecf20Sopenharmony_ci	}
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	/* Read flags */
13858c2ecf20Sopenharmony_ci	if (of_property_read_u32_array(np, "ibm,cpu-idle-state-flags",
13868c2ecf20Sopenharmony_ci				       temp_u32, nr_idle_states)) {
13878c2ecf20Sopenharmony_ci		pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-flags in DT\n");
13888c2ecf20Sopenharmony_ci		rc = -EINVAL;
13898c2ecf20Sopenharmony_ci		goto out;
13908c2ecf20Sopenharmony_ci	}
13918c2ecf20Sopenharmony_ci	for (i = 0; i < nr_idle_states; i++)
13928c2ecf20Sopenharmony_ci		pnv_idle_states[i].flags = temp_u32[i];
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	/* Read latencies */
13958c2ecf20Sopenharmony_ci	if (of_property_read_u32_array(np, "ibm,cpu-idle-state-latencies-ns",
13968c2ecf20Sopenharmony_ci				       temp_u32, nr_idle_states)) {
13978c2ecf20Sopenharmony_ci		pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-latencies-ns in DT\n");
13988c2ecf20Sopenharmony_ci		rc = -EINVAL;
13998c2ecf20Sopenharmony_ci		goto out;
14008c2ecf20Sopenharmony_ci	}
14018c2ecf20Sopenharmony_ci	for (i = 0; i < nr_idle_states; i++)
14028c2ecf20Sopenharmony_ci		pnv_idle_states[i].latency_ns = temp_u32[i];
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	/* Read residencies */
14058c2ecf20Sopenharmony_ci	if (of_property_read_u32_array(np, "ibm,cpu-idle-state-residency-ns",
14068c2ecf20Sopenharmony_ci				       temp_u32, nr_idle_states)) {
14078c2ecf20Sopenharmony_ci		pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-residency-ns in DT\n");
14088c2ecf20Sopenharmony_ci		rc = -EINVAL;
14098c2ecf20Sopenharmony_ci		goto out;
14108c2ecf20Sopenharmony_ci	}
14118c2ecf20Sopenharmony_ci	for (i = 0; i < nr_idle_states; i++)
14128c2ecf20Sopenharmony_ci		pnv_idle_states[i].residency_ns = temp_u32[i];
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	/* For power9 and later */
14158c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_300)) {
14168c2ecf20Sopenharmony_ci		/* Read pm_crtl_val */
14178c2ecf20Sopenharmony_ci		if (of_property_read_u64_array(np, "ibm,cpu-idle-state-psscr",
14188c2ecf20Sopenharmony_ci					       temp_u64, nr_idle_states)) {
14198c2ecf20Sopenharmony_ci			pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr in DT\n");
14208c2ecf20Sopenharmony_ci			rc = -EINVAL;
14218c2ecf20Sopenharmony_ci			goto out;
14228c2ecf20Sopenharmony_ci		}
14238c2ecf20Sopenharmony_ci		for (i = 0; i < nr_idle_states; i++)
14248c2ecf20Sopenharmony_ci			pnv_idle_states[i].psscr_val = temp_u64[i];
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci		/* Read pm_crtl_mask */
14278c2ecf20Sopenharmony_ci		if (of_property_read_u64_array(np, "ibm,cpu-idle-state-psscr-mask",
14288c2ecf20Sopenharmony_ci					       temp_u64, nr_idle_states)) {
14298c2ecf20Sopenharmony_ci			pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr-mask in DT\n");
14308c2ecf20Sopenharmony_ci			rc = -EINVAL;
14318c2ecf20Sopenharmony_ci			goto out;
14328c2ecf20Sopenharmony_ci		}
14338c2ecf20Sopenharmony_ci		for (i = 0; i < nr_idle_states; i++)
14348c2ecf20Sopenharmony_ci			pnv_idle_states[i].psscr_mask = temp_u64[i];
14358c2ecf20Sopenharmony_ci	}
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	/*
14388c2ecf20Sopenharmony_ci	 * power8 specific properties ibm,cpu-idle-state-pmicr-mask and
14398c2ecf20Sopenharmony_ci	 * ibm,cpu-idle-state-pmicr-val were never used and there is no
14408c2ecf20Sopenharmony_ci	 * plan to use it in near future. Hence, not parsing these properties
14418c2ecf20Sopenharmony_ci	 */
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	if (of_property_read_string_array(np, "ibm,cpu-idle-state-names",
14448c2ecf20Sopenharmony_ci					  temp_string, nr_idle_states) < 0) {
14458c2ecf20Sopenharmony_ci		pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-names in DT\n");
14468c2ecf20Sopenharmony_ci		rc = -EINVAL;
14478c2ecf20Sopenharmony_ci		goto out;
14488c2ecf20Sopenharmony_ci	}
14498c2ecf20Sopenharmony_ci	for (i = 0; i < nr_idle_states; i++)
14508c2ecf20Sopenharmony_ci		strlcpy(pnv_idle_states[i].name, temp_string[i],
14518c2ecf20Sopenharmony_ci			PNV_IDLE_NAME_LEN);
14528c2ecf20Sopenharmony_ci	nr_pnv_idle_states = nr_idle_states;
14538c2ecf20Sopenharmony_ci	rc = 0;
14548c2ecf20Sopenharmony_ciout:
14558c2ecf20Sopenharmony_ci	kfree(temp_u32);
14568c2ecf20Sopenharmony_ci	kfree(temp_u64);
14578c2ecf20Sopenharmony_ci	kfree(temp_string);
14588c2ecf20Sopenharmony_ci	return rc;
14598c2ecf20Sopenharmony_ci}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_cistatic int __init pnv_init_idle_states(void)
14628c2ecf20Sopenharmony_ci{
14638c2ecf20Sopenharmony_ci	int cpu;
14648c2ecf20Sopenharmony_ci	int rc = 0;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	/* Set up PACA fields */
14678c2ecf20Sopenharmony_ci	for_each_present_cpu(cpu) {
14688c2ecf20Sopenharmony_ci		struct paca_struct *p = paca_ptrs[cpu];
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci		p->idle_state = 0;
14718c2ecf20Sopenharmony_ci		if (cpu == cpu_first_thread_sibling(cpu))
14728c2ecf20Sopenharmony_ci			p->idle_state = (1 << threads_per_core) - 1;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci		if (!cpu_has_feature(CPU_FTR_ARCH_300)) {
14758c2ecf20Sopenharmony_ci			/* P7/P8 nap */
14768c2ecf20Sopenharmony_ci			p->thread_idle_state = PNV_THREAD_RUNNING;
14778c2ecf20Sopenharmony_ci		} else if (pvr_version_is(PVR_POWER9)) {
14788c2ecf20Sopenharmony_ci			/* P9 stop workarounds */
14798c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
14808c2ecf20Sopenharmony_ci			p->requested_psscr = 0;
14818c2ecf20Sopenharmony_ci			atomic_set(&p->dont_stop, 0);
14828c2ecf20Sopenharmony_ci#endif
14838c2ecf20Sopenharmony_ci		}
14848c2ecf20Sopenharmony_ci	}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	/* In case we error out nr_pnv_idle_states will be zero */
14878c2ecf20Sopenharmony_ci	nr_pnv_idle_states = 0;
14888c2ecf20Sopenharmony_ci	supported_cpuidle_states = 0;
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	if (cpuidle_disable != IDLE_NO_OVERRIDE)
14918c2ecf20Sopenharmony_ci		goto out;
14928c2ecf20Sopenharmony_ci	rc = pnv_parse_cpuidle_dt();
14938c2ecf20Sopenharmony_ci	if (rc)
14948c2ecf20Sopenharmony_ci		return rc;
14958c2ecf20Sopenharmony_ci	pnv_probe_idle_states();
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	if (!cpu_has_feature(CPU_FTR_ARCH_300)) {
14988c2ecf20Sopenharmony_ci		if (!(supported_cpuidle_states & OPAL_PM_SLEEP_ENABLED_ER1)) {
14998c2ecf20Sopenharmony_ci			power7_fastsleep_workaround_entry = false;
15008c2ecf20Sopenharmony_ci			power7_fastsleep_workaround_exit = false;
15018c2ecf20Sopenharmony_ci		} else {
15028c2ecf20Sopenharmony_ci			/*
15038c2ecf20Sopenharmony_ci			 * OPAL_PM_SLEEP_ENABLED_ER1 is set. It indicates that
15048c2ecf20Sopenharmony_ci			 * workaround is needed to use fastsleep. Provide sysfs
15058c2ecf20Sopenharmony_ci			 * control to choose how this workaround has to be
15068c2ecf20Sopenharmony_ci			 * applied.
15078c2ecf20Sopenharmony_ci			 */
15088c2ecf20Sopenharmony_ci			device_create_file(cpu_subsys.dev_root,
15098c2ecf20Sopenharmony_ci				&dev_attr_fastsleep_workaround_applyonce);
15108c2ecf20Sopenharmony_ci		}
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci		update_subcore_sibling_mask();
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci		if (supported_cpuidle_states & OPAL_PM_NAP_ENABLED) {
15158c2ecf20Sopenharmony_ci			ppc_md.power_save = power7_idle;
15168c2ecf20Sopenharmony_ci			power7_offline_type = PNV_THREAD_NAP;
15178c2ecf20Sopenharmony_ci		}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci		if ((supported_cpuidle_states & OPAL_PM_WINKLE_ENABLED) &&
15208c2ecf20Sopenharmony_ci			   (supported_cpuidle_states & OPAL_PM_LOSE_FULL_CONTEXT))
15218c2ecf20Sopenharmony_ci			power7_offline_type = PNV_THREAD_WINKLE;
15228c2ecf20Sopenharmony_ci		else if ((supported_cpuidle_states & OPAL_PM_SLEEP_ENABLED) ||
15238c2ecf20Sopenharmony_ci			   (supported_cpuidle_states & OPAL_PM_SLEEP_ENABLED_ER1))
15248c2ecf20Sopenharmony_ci			power7_offline_type = PNV_THREAD_SLEEP;
15258c2ecf20Sopenharmony_ci	}
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	if (supported_cpuidle_states & OPAL_PM_LOSE_FULL_CONTEXT) {
15288c2ecf20Sopenharmony_ci		if (pnv_save_sprs_for_deep_states())
15298c2ecf20Sopenharmony_ci			pnv_disable_deep_states();
15308c2ecf20Sopenharmony_ci	}
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ciout:
15338c2ecf20Sopenharmony_ci	return 0;
15348c2ecf20Sopenharmony_ci}
15358c2ecf20Sopenharmony_cimachine_subsys_initcall(powernv, pnv_init_idle_states);
1536