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) 2012 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/reboot.h>
22 #include <uapi/linux/psci.h>
24 #include <asm/compiler.h>
25 #include <asm/errno.h>
26 #include <asm/opcodes-sec.h>
27 #include <asm/opcodes-virt.h>
29 #include <asm/system_misc.h>
31 struct psci_operations psci_ops
;
33 static int (*invoke_psci_fn
)(u32
, u32
, u32
, u32
);
34 typedef int (*psci_initcall_t
)(const struct device_node
*);
41 PSCI_FN_AFFINITY_INFO
,
42 PSCI_FN_MIGRATE_INFO_TYPE
,
46 static u32 psci_function_id
[PSCI_FN_MAX
];
48 static int psci_to_linux_errno(int errno
)
51 case PSCI_RET_SUCCESS
:
53 case PSCI_RET_NOT_SUPPORTED
:
55 case PSCI_RET_INVALID_PARAMS
:
64 static u32
psci_power_state_pack(struct psci_power_state state
)
66 return ((state
.id
<< PSCI_0_2_POWER_STATE_ID_SHIFT
)
67 & PSCI_0_2_POWER_STATE_ID_MASK
) |
68 ((state
.type
<< PSCI_0_2_POWER_STATE_TYPE_SHIFT
)
69 & PSCI_0_2_POWER_STATE_TYPE_MASK
) |
70 ((state
.affinity_level
<< PSCI_0_2_POWER_STATE_AFFL_SHIFT
)
71 & PSCI_0_2_POWER_STATE_AFFL_MASK
);
75 * The following two functions are invoked via the invoke_psci_fn pointer
76 * and will not be inlined, allowing us to piggyback on the AAPCS.
78 static noinline
int __invoke_psci_fn_hvc(u32 function_id
, u32 arg0
, u32 arg1
,
88 : "r" (arg0
), "r" (arg1
), "r" (arg2
));
93 static noinline
int __invoke_psci_fn_smc(u32 function_id
, u32 arg0
, u32 arg1
,
103 : "r" (arg0
), "r" (arg1
), "r" (arg2
));
108 static int psci_get_version(void)
112 err
= invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION
, 0, 0, 0);
116 static int psci_cpu_suspend(struct psci_power_state state
,
117 unsigned long entry_point
)
122 fn
= psci_function_id
[PSCI_FN_CPU_SUSPEND
];
123 power_state
= psci_power_state_pack(state
);
124 err
= invoke_psci_fn(fn
, power_state
, entry_point
, 0);
125 return psci_to_linux_errno(err
);
128 static int psci_cpu_off(struct psci_power_state state
)
133 fn
= psci_function_id
[PSCI_FN_CPU_OFF
];
134 power_state
= psci_power_state_pack(state
);
135 err
= invoke_psci_fn(fn
, power_state
, 0, 0);
136 return psci_to_linux_errno(err
);
139 static int psci_cpu_on(unsigned long cpuid
, unsigned long entry_point
)
144 fn
= psci_function_id
[PSCI_FN_CPU_ON
];
145 err
= invoke_psci_fn(fn
, cpuid
, entry_point
, 0);
146 return psci_to_linux_errno(err
);
149 static int psci_migrate(unsigned long cpuid
)
154 fn
= psci_function_id
[PSCI_FN_MIGRATE
];
155 err
= invoke_psci_fn(fn
, cpuid
, 0, 0);
156 return psci_to_linux_errno(err
);
159 static int psci_affinity_info(unsigned long target_affinity
,
160 unsigned long lowest_affinity_level
)
165 fn
= psci_function_id
[PSCI_FN_AFFINITY_INFO
];
166 err
= invoke_psci_fn(fn
, target_affinity
, lowest_affinity_level
, 0);
170 static int psci_migrate_info_type(void)
175 fn
= psci_function_id
[PSCI_FN_MIGRATE_INFO_TYPE
];
176 err
= invoke_psci_fn(fn
, 0, 0, 0);
180 static int get_set_conduit_method(struct device_node
*np
)
184 pr_info("probing for conduit method from DT.\n");
186 if (of_property_read_string(np
, "method", &method
)) {
187 pr_warn("missing \"method\" property\n");
191 if (!strcmp("hvc", method
)) {
192 invoke_psci_fn
= __invoke_psci_fn_hvc
;
193 } else if (!strcmp("smc", method
)) {
194 invoke_psci_fn
= __invoke_psci_fn_smc
;
196 pr_warn("invalid \"method\" property: %s\n", method
);
202 static void psci_sys_reset(enum reboot_mode reboot_mode
, const char *cmd
)
204 invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET
, 0, 0, 0);
207 static void psci_sys_poweroff(void)
209 invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF
, 0, 0, 0);
213 * PSCI Function IDs for v0.2+ are well defined so use
216 static int psci_0_2_init(struct device_node
*np
)
220 err
= get_set_conduit_method(np
);
225 ver
= psci_get_version();
227 if (ver
== PSCI_RET_NOT_SUPPORTED
) {
228 /* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */
229 pr_err("PSCI firmware does not comply with the v0.2 spec.\n");
233 pr_info("PSCIv%d.%d detected in firmware.\n",
234 PSCI_VERSION_MAJOR(ver
),
235 PSCI_VERSION_MINOR(ver
));
237 if (PSCI_VERSION_MAJOR(ver
) == 0 &&
238 PSCI_VERSION_MINOR(ver
) < 2) {
240 pr_err("Conflicting PSCI version detected.\n");
245 pr_info("Using standard PSCI v0.2 function IDs\n");
246 psci_function_id
[PSCI_FN_CPU_SUSPEND
] = PSCI_0_2_FN_CPU_SUSPEND
;
247 psci_ops
.cpu_suspend
= psci_cpu_suspend
;
249 psci_function_id
[PSCI_FN_CPU_OFF
] = PSCI_0_2_FN_CPU_OFF
;
250 psci_ops
.cpu_off
= psci_cpu_off
;
252 psci_function_id
[PSCI_FN_CPU_ON
] = PSCI_0_2_FN_CPU_ON
;
253 psci_ops
.cpu_on
= psci_cpu_on
;
255 psci_function_id
[PSCI_FN_MIGRATE
] = PSCI_0_2_FN_MIGRATE
;
256 psci_ops
.migrate
= psci_migrate
;
258 psci_function_id
[PSCI_FN_AFFINITY_INFO
] = PSCI_0_2_FN_AFFINITY_INFO
;
259 psci_ops
.affinity_info
= psci_affinity_info
;
261 psci_function_id
[PSCI_FN_MIGRATE_INFO_TYPE
] =
262 PSCI_0_2_FN_MIGRATE_INFO_TYPE
;
263 psci_ops
.migrate_info_type
= psci_migrate_info_type
;
265 arm_pm_restart
= psci_sys_reset
;
267 pm_power_off
= psci_sys_poweroff
;
275 * PSCI < v0.2 get PSCI Function IDs via DT.
277 static int psci_0_1_init(struct device_node
*np
)
282 err
= get_set_conduit_method(np
);
287 pr_info("Using PSCI v0.1 Function IDs from DT\n");
289 if (!of_property_read_u32(np
, "cpu_suspend", &id
)) {
290 psci_function_id
[PSCI_FN_CPU_SUSPEND
] = id
;
291 psci_ops
.cpu_suspend
= psci_cpu_suspend
;
294 if (!of_property_read_u32(np
, "cpu_off", &id
)) {
295 psci_function_id
[PSCI_FN_CPU_OFF
] = id
;
296 psci_ops
.cpu_off
= psci_cpu_off
;
299 if (!of_property_read_u32(np
, "cpu_on", &id
)) {
300 psci_function_id
[PSCI_FN_CPU_ON
] = id
;
301 psci_ops
.cpu_on
= psci_cpu_on
;
304 if (!of_property_read_u32(np
, "migrate", &id
)) {
305 psci_function_id
[PSCI_FN_MIGRATE
] = id
;
306 psci_ops
.migrate
= psci_migrate
;
314 static const struct of_device_id psci_of_match
[] __initconst
= {
315 { .compatible
= "arm,psci", .data
= psci_0_1_init
},
316 { .compatible
= "arm,psci-0.2", .data
= psci_0_2_init
},
320 int __init
psci_init(void)
322 struct device_node
*np
;
323 const struct of_device_id
*matched_np
;
324 psci_initcall_t init_fn
;
326 np
= of_find_matching_node_and_match(NULL
, psci_of_match
, &matched_np
);
330 init_fn
= (psci_initcall_t
)matched_np
->data
;