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