xref: /openbmc/linux/arch/arm64/kernel/psci.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e790f1deSWill Deacon /*
3e790f1deSWill Deacon  *
4e790f1deSWill Deacon  * Copyright (C) 2013 ARM Limited
5e790f1deSWill Deacon  *
6e790f1deSWill Deacon  * Author: Will Deacon <will.deacon@arm.com>
7e790f1deSWill Deacon  */
8e790f1deSWill Deacon 
9e790f1deSWill Deacon #define pr_fmt(fmt) "psci: " fmt
10e790f1deSWill Deacon 
11e790f1deSWill Deacon #include <linux/init.h>
12e790f1deSWill Deacon #include <linux/of.h>
1300ef54bbSMark Rutland #include <linux/smp.h>
14c814ca02SAshwin Chaugule #include <linux/delay.h>
15bff60792SMark Rutland #include <linux/psci.h>
162077be67SLaura Abbott #include <linux/mm.h>
17bff60792SMark Rutland 
18e71246a2SAshwin Chaugule #include <uapi/linux/psci.h>
19e790f1deSWill Deacon 
20cd1aebf5SMark Rutland #include <asm/cpu_ops.h>
21e790f1deSWill Deacon #include <asm/errno.h>
2200ef54bbSMark Rutland #include <asm/smp_plat.h>
2318910ab0SLorenzo Pieralisi 
cpu_psci_cpu_init(unsigned int cpu)24819a8826SLorenzo Pieralisi static int __init cpu_psci_cpu_init(unsigned int cpu)
2500ef54bbSMark Rutland {
2600ef54bbSMark Rutland 	return 0;
2700ef54bbSMark Rutland }
2800ef54bbSMark Rutland 
cpu_psci_cpu_prepare(unsigned int cpu)29cd1aebf5SMark Rutland static int __init cpu_psci_cpu_prepare(unsigned int cpu)
3000ef54bbSMark Rutland {
3100ef54bbSMark Rutland 	if (!psci_ops.cpu_on) {
3200ef54bbSMark Rutland 		pr_err("no cpu_on method, not booting CPU%d\n", cpu);
3300ef54bbSMark Rutland 		return -ENODEV;
3400ef54bbSMark Rutland 	}
3500ef54bbSMark Rutland 
36652af899SMark Rutland 	return 0;
3700ef54bbSMark Rutland }
3800ef54bbSMark Rutland 
cpu_psci_cpu_boot(unsigned int cpu)39652af899SMark Rutland static int cpu_psci_cpu_boot(unsigned int cpu)
40652af899SMark Rutland {
41*607289a7SSami Tolvanen 	phys_addr_t pa_secondary_entry = __pa_symbol(secondary_entry);
42bde33977SSami Tolvanen 	int err = psci_ops.cpu_on(cpu_logical_map(cpu), pa_secondary_entry);
43652af899SMark Rutland 	if (err)
44288ac26cSVladimir Murzin 		pr_err("failed to boot CPU%d (%d)\n", cpu, err);
45652af899SMark Rutland 
46652af899SMark Rutland 	return err;
4700ef54bbSMark Rutland }
4800ef54bbSMark Rutland 
49831ccf79SMark Rutland #ifdef CONFIG_HOTPLUG_CPU
cpu_psci_cpu_can_disable(unsigned int cpu)50d55c5f28SSudeep Holla static bool cpu_psci_cpu_can_disable(unsigned int cpu)
51d55c5f28SSudeep Holla {
52d55c5f28SSudeep Holla 	return !psci_tos_resident_on(cpu);
53d55c5f28SSudeep Holla }
54d55c5f28SSudeep Holla 
cpu_psci_cpu_disable(unsigned int cpu)55831ccf79SMark Rutland static int cpu_psci_cpu_disable(unsigned int cpu)
56831ccf79SMark Rutland {
57831ccf79SMark Rutland 	/* Fail early if we don't have CPU_OFF support */
58831ccf79SMark Rutland 	if (!psci_ops.cpu_off)
59831ccf79SMark Rutland 		return -EOPNOTSUPP;
60ff3010e6SMark Rutland 
61ff3010e6SMark Rutland 	/* Trusted OS will deny CPU_OFF */
62ff3010e6SMark Rutland 	if (psci_tos_resident_on(cpu))
63ff3010e6SMark Rutland 		return -EPERM;
64ff3010e6SMark Rutland 
65831ccf79SMark Rutland 	return 0;
66831ccf79SMark Rutland }
67831ccf79SMark Rutland 
cpu_psci_cpu_die(unsigned int cpu)68831ccf79SMark Rutland static void cpu_psci_cpu_die(unsigned int cpu)
69831ccf79SMark Rutland {
70831ccf79SMark Rutland 	/*
71831ccf79SMark Rutland 	 * There are no known implementations of PSCI actually using the
72831ccf79SMark Rutland 	 * power state field, pass a sensible default for now.
73831ccf79SMark Rutland 	 */
74c8cc4273SMark Rutland 	u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN <<
75c8cc4273SMark Rutland 		    PSCI_0_2_POWER_STATE_TYPE_SHIFT;
76831ccf79SMark Rutland 
77891deb87SWill Deacon 	psci_ops.cpu_off(state);
78831ccf79SMark Rutland }
79c814ca02SAshwin Chaugule 
cpu_psci_cpu_kill(unsigned int cpu)80c814ca02SAshwin Chaugule static int cpu_psci_cpu_kill(unsigned int cpu)
81c814ca02SAshwin Chaugule {
82bfcef4abSYunfeng Ye 	int err;
83bfcef4abSYunfeng Ye 	unsigned long start, end;
84c814ca02SAshwin Chaugule 
85c814ca02SAshwin Chaugule 	if (!psci_ops.affinity_info)
866b99c68cSMark Rutland 		return 0;
87c814ca02SAshwin Chaugule 	/*
88c814ca02SAshwin Chaugule 	 * cpu_kill could race with cpu_die and we can
89c814ca02SAshwin Chaugule 	 * potentially end up declaring this cpu undead
90c814ca02SAshwin Chaugule 	 * while it is dying. So, try again a few times.
91c814ca02SAshwin Chaugule 	 */
92c814ca02SAshwin Chaugule 
93bfcef4abSYunfeng Ye 	start = jiffies;
94bfcef4abSYunfeng Ye 	end = start + msecs_to_jiffies(100);
95bfcef4abSYunfeng Ye 	do {
96c814ca02SAshwin Chaugule 		err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
97c814ca02SAshwin Chaugule 		if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
98bfcef4abSYunfeng Ye 			pr_info("CPU%d killed (polled %d ms)\n", cpu,
99bfcef4abSYunfeng Ye 				jiffies_to_msecs(jiffies - start));
1006b99c68cSMark Rutland 			return 0;
101c814ca02SAshwin Chaugule 		}
102c814ca02SAshwin Chaugule 
103bfcef4abSYunfeng Ye 		usleep_range(100, 1000);
104bfcef4abSYunfeng Ye 	} while (time_before(jiffies, end));
105c814ca02SAshwin Chaugule 
106c814ca02SAshwin Chaugule 	pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
107c814ca02SAshwin Chaugule 			cpu, err);
1086b99c68cSMark Rutland 	return -ETIMEDOUT;
109c814ca02SAshwin Chaugule }
110831ccf79SMark Rutland #endif
111831ccf79SMark Rutland 
112cd1aebf5SMark Rutland const struct cpu_operations cpu_psci_ops = {
11300ef54bbSMark Rutland 	.name		= "psci",
114cd1aebf5SMark Rutland 	.cpu_init	= cpu_psci_cpu_init,
115cd1aebf5SMark Rutland 	.cpu_prepare	= cpu_psci_cpu_prepare,
116652af899SMark Rutland 	.cpu_boot	= cpu_psci_cpu_boot,
117831ccf79SMark Rutland #ifdef CONFIG_HOTPLUG_CPU
118d55c5f28SSudeep Holla 	.cpu_can_disable = cpu_psci_cpu_can_disable,
119831ccf79SMark Rutland 	.cpu_disable	= cpu_psci_cpu_disable,
120831ccf79SMark Rutland 	.cpu_die	= cpu_psci_cpu_die,
121c814ca02SAshwin Chaugule 	.cpu_kill	= cpu_psci_cpu_kill,
122831ccf79SMark Rutland #endif
12300ef54bbSMark Rutland };
12400ef54bbSMark Rutland 
125