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