Commit | Line | Data |
---|---|---|
05774088 SS |
1 | /* |
2 | * This program is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License version 2 as | |
4 | * published by the Free Software Foundation. | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * Copyright (C) 2012 ARM Limited | |
12 | * | |
13 | * Author: Will Deacon <will.deacon@arm.com> | |
14 | */ | |
15 | ||
16 | #include <linux/init.h> | |
05774088 SS |
17 | #include <linux/smp.h> |
18 | #include <linux/of.h> | |
c814ca02 | 19 | #include <linux/delay.h> |
be120397 MR |
20 | #include <linux/psci.h> |
21 | ||
c814ca02 | 22 | #include <uapi/linux/psci.h> |
05774088 SS |
23 | |
24 | #include <asm/psci.h> | |
25 | #include <asm/smp_plat.h> | |
26 | ||
27 | /* | |
28 | * psci_smp assumes that the following is true about PSCI: | |
29 | * | |
30 | * cpu_suspend Suspend the execution on a CPU | |
31 | * @state we don't currently describe affinity levels, so just pass 0. | |
32 | * @entry_point the first instruction to be executed on return | |
33 | * returns 0 success, < 0 on failure | |
34 | * | |
35 | * cpu_off Power down a CPU | |
36 | * @state we don't currently describe affinity levels, so just pass 0. | |
37 | * no return on successful call | |
38 | * | |
39 | * cpu_on Power up a CPU | |
40 | * @cpuid cpuid of target CPU, as from MPIDR | |
41 | * @entry_point the first instruction to be executed on return | |
42 | * returns 0 success, < 0 on failure | |
43 | * | |
44 | * migrate Migrate the context to a different CPU | |
45 | * @cpuid cpuid of target CPU, as from MPIDR | |
46 | * returns 0 success, < 0 on failure | |
47 | * | |
48 | */ | |
49 | ||
50 | extern void secondary_startup(void); | |
51 | ||
8bd26e3a | 52 | static int psci_boot_secondary(unsigned int cpu, struct task_struct *idle) |
05774088 SS |
53 | { |
54 | if (psci_ops.cpu_on) | |
55 | return psci_ops.cpu_on(cpu_logical_map(cpu), | |
37cf524f | 56 | virt_to_idmap(&secondary_startup)); |
05774088 SS |
57 | return -ENODEV; |
58 | } | |
59 | ||
60 | #ifdef CONFIG_HOTPLUG_CPU | |
be120397 MR |
61 | int psci_cpu_disable(unsigned int cpu) |
62 | { | |
63 | /* Fail early if we don't have CPU_OFF support */ | |
64 | if (!psci_ops.cpu_off) | |
65 | return -EOPNOTSUPP; | |
66 | ||
67 | /* Trusted OS will deny CPU_OFF */ | |
68 | if (psci_tos_resident_on(cpu)) | |
69 | return -EPERM; | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
b96fc2f3 | 74 | void psci_cpu_die(unsigned int cpu) |
05774088 | 75 | { |
be120397 MR |
76 | u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN << |
77 | PSCI_0_2_POWER_STATE_TYPE_SHIFT; | |
05774088 | 78 | |
be120397 MR |
79 | if (psci_ops.cpu_off) |
80 | psci_ops.cpu_off(state); | |
05774088 | 81 | |
be120397 MR |
82 | /* We should never return */ |
83 | panic("psci: cpu %d failed to shutdown\n", cpu); | |
05774088 | 84 | } |
c814ca02 | 85 | |
b96fc2f3 | 86 | int psci_cpu_kill(unsigned int cpu) |
c814ca02 AC |
87 | { |
88 | int err, i; | |
89 | ||
90 | if (!psci_ops.affinity_info) | |
91 | return 1; | |
92 | /* | |
93 | * cpu_kill could race with cpu_die and we can | |
94 | * potentially end up declaring this cpu undead | |
95 | * while it is dying. So, try again a few times. | |
96 | */ | |
97 | ||
98 | for (i = 0; i < 10; i++) { | |
99 | err = psci_ops.affinity_info(cpu_logical_map(cpu), 0); | |
100 | if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) { | |
101 | pr_info("CPU%d killed.\n", cpu); | |
102 | return 1; | |
103 | } | |
104 | ||
105 | msleep(10); | |
106 | pr_info("Retrying again to check for CPU kill\n"); | |
107 | } | |
108 | ||
109 | pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n", | |
110 | cpu, err); | |
111 | /* Make platform_cpu_kill() fail. */ | |
112 | return 0; | |
113 | } | |
114 | ||
05774088 SS |
115 | #endif |
116 | ||
117 | bool __init psci_smp_available(void) | |
118 | { | |
119 | /* is cpu_on available at least? */ | |
120 | return (psci_ops.cpu_on != NULL); | |
121 | } | |
122 | ||
75305275 | 123 | const struct smp_operations psci_smp_ops __initconst = { |
05774088 | 124 | .smp_boot_secondary = psci_boot_secondary, |
fdeb94b5 | 125 | #ifdef CONFIG_HOTPLUG_CPU |
be120397 | 126 | .cpu_disable = psci_cpu_disable, |
05774088 | 127 | .cpu_die = psci_cpu_die, |
c814ca02 | 128 | .cpu_kill = psci_cpu_kill, |
fdeb94b5 | 129 | #endif |
05774088 | 130 | }; |