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.
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.
11 * Copyright (C) 2013 ARM Limited
13 * Author: Will Deacon <will.deacon@arm.com>
16 #define pr_fmt(fmt) "psci: " fmt
18 #include <linux/init.h>
20 #include <linux/smp.h>
22 #include <asm/compiler.h>
23 #include <asm/cpu_ops.h>
24 #include <asm/errno.h>
26 #include <asm/smp_plat.h>
28 #define PSCI_POWER_STATE_TYPE_STANDBY 0
29 #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1
31 struct psci_power_state
{
37 struct psci_operations
{
38 int (*cpu_suspend
)(struct psci_power_state state
,
39 unsigned long entry_point
);
40 int (*cpu_off
)(struct psci_power_state state
);
41 int (*cpu_on
)(unsigned long cpuid
, unsigned long entry_point
);
42 int (*migrate
)(unsigned long cpuid
);
45 static struct psci_operations psci_ops
;
47 static int (*invoke_psci_fn
)(u64
, u64
, u64
, u64
);
57 static u32 psci_function_id
[PSCI_FN_MAX
];
59 #define PSCI_RET_SUCCESS 0
60 #define PSCI_RET_EOPNOTSUPP -1
61 #define PSCI_RET_EINVAL -2
62 #define PSCI_RET_EPERM -3
64 static int psci_to_linux_errno(int errno
)
67 case PSCI_RET_SUCCESS
:
69 case PSCI_RET_EOPNOTSUPP
:
80 #define PSCI_POWER_STATE_ID_MASK 0xffff
81 #define PSCI_POWER_STATE_ID_SHIFT 0
82 #define PSCI_POWER_STATE_TYPE_MASK 0x1
83 #define PSCI_POWER_STATE_TYPE_SHIFT 16
84 #define PSCI_POWER_STATE_AFFL_MASK 0x3
85 #define PSCI_POWER_STATE_AFFL_SHIFT 24
87 static u32
psci_power_state_pack(struct psci_power_state state
)
89 return ((state
.id
& PSCI_POWER_STATE_ID_MASK
)
90 << PSCI_POWER_STATE_ID_SHIFT
) |
91 ((state
.type
& PSCI_POWER_STATE_TYPE_MASK
)
92 << PSCI_POWER_STATE_TYPE_SHIFT
) |
93 ((state
.affinity_level
& PSCI_POWER_STATE_AFFL_MASK
)
94 << PSCI_POWER_STATE_AFFL_SHIFT
);
98 * The following two functions are invoked via the invoke_psci_fn pointer
99 * and will not be inlined, allowing us to piggyback on the AAPCS.
101 static noinline
int __invoke_psci_fn_hvc(u64 function_id
, u64 arg0
, u64 arg1
,
111 : "r" (arg0
), "r" (arg1
), "r" (arg2
));
116 static noinline
int __invoke_psci_fn_smc(u64 function_id
, u64 arg0
, u64 arg1
,
126 : "r" (arg0
), "r" (arg1
), "r" (arg2
));
131 static int psci_cpu_suspend(struct psci_power_state state
,
132 unsigned long entry_point
)
137 fn
= psci_function_id
[PSCI_FN_CPU_SUSPEND
];
138 power_state
= psci_power_state_pack(state
);
139 err
= invoke_psci_fn(fn
, power_state
, entry_point
, 0);
140 return psci_to_linux_errno(err
);
143 static int psci_cpu_off(struct psci_power_state state
)
148 fn
= psci_function_id
[PSCI_FN_CPU_OFF
];
149 power_state
= psci_power_state_pack(state
);
150 err
= invoke_psci_fn(fn
, power_state
, 0, 0);
151 return psci_to_linux_errno(err
);
154 static int psci_cpu_on(unsigned long cpuid
, unsigned long entry_point
)
159 fn
= psci_function_id
[PSCI_FN_CPU_ON
];
160 err
= invoke_psci_fn(fn
, cpuid
, entry_point
, 0);
161 return psci_to_linux_errno(err
);
164 static int psci_migrate(unsigned long cpuid
)
169 fn
= psci_function_id
[PSCI_FN_MIGRATE
];
170 err
= invoke_psci_fn(fn
, cpuid
, 0, 0);
171 return psci_to_linux_errno(err
);
174 static const struct of_device_id psci_of_match
[] __initconst
= {
175 { .compatible
= "arm,psci", },
179 int __init
psci_init(void)
181 struct device_node
*np
;
186 np
= of_find_matching_node(NULL
, psci_of_match
);
190 pr_info("probing function IDs from device-tree\n");
192 if (of_property_read_string(np
, "method", &method
)) {
193 pr_warning("missing \"method\" property\n");
198 if (!strcmp("hvc", method
)) {
199 invoke_psci_fn
= __invoke_psci_fn_hvc
;
200 } else if (!strcmp("smc", method
)) {
201 invoke_psci_fn
= __invoke_psci_fn_smc
;
203 pr_warning("invalid \"method\" property: %s\n", method
);
208 if (!of_property_read_u32(np
, "cpu_suspend", &id
)) {
209 psci_function_id
[PSCI_FN_CPU_SUSPEND
] = id
;
210 psci_ops
.cpu_suspend
= psci_cpu_suspend
;
213 if (!of_property_read_u32(np
, "cpu_off", &id
)) {
214 psci_function_id
[PSCI_FN_CPU_OFF
] = id
;
215 psci_ops
.cpu_off
= psci_cpu_off
;
218 if (!of_property_read_u32(np
, "cpu_on", &id
)) {
219 psci_function_id
[PSCI_FN_CPU_ON
] = id
;
220 psci_ops
.cpu_on
= psci_cpu_on
;
223 if (!of_property_read_u32(np
, "migrate", &id
)) {
224 psci_function_id
[PSCI_FN_MIGRATE
] = id
;
225 psci_ops
.migrate
= psci_migrate
;
235 static int __init
cpu_psci_cpu_init(struct device_node
*dn
, unsigned int cpu
)
240 static int __init
cpu_psci_cpu_prepare(unsigned int cpu
)
242 if (!psci_ops
.cpu_on
) {
243 pr_err("no cpu_on method, not booting CPU%d\n", cpu
);
250 static int cpu_psci_cpu_boot(unsigned int cpu
)
252 int err
= psci_ops
.cpu_on(cpu_logical_map(cpu
), __pa(secondary_entry
));
254 pr_err("psci: failed to boot CPU%d (%d)\n", cpu
, err
);
259 #ifdef CONFIG_HOTPLUG_CPU
260 static int cpu_psci_cpu_disable(unsigned int cpu
)
262 /* Fail early if we don't have CPU_OFF support */
263 if (!psci_ops
.cpu_off
)
268 static void cpu_psci_cpu_die(unsigned int cpu
)
272 * There are no known implementations of PSCI actually using the
273 * power state field, pass a sensible default for now.
275 struct psci_power_state state
= {
276 .type
= PSCI_POWER_STATE_TYPE_POWER_DOWN
,
279 ret
= psci_ops
.cpu_off(state
);
281 pr_crit("psci: unable to power off CPU%u (%d)\n", cpu
, ret
);
285 const struct cpu_operations cpu_psci_ops
= {
287 .cpu_init
= cpu_psci_cpu_init
,
288 .cpu_prepare
= cpu_psci_cpu_prepare
,
289 .cpu_boot
= cpu_psci_cpu_boot
,
290 #ifdef CONFIG_HOTPLUG_CPU
291 .cpu_disable
= cpu_psci_cpu_disable
,
292 .cpu_die
= cpu_psci_cpu_die
,