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>
27 #include <asm/system_misc.h>
29 struct psci_operations psci_ops
;
31 static int (*invoke_psci_fn
)(u32
, u32
, u32
, u32
);
32 typedef int (*psci_initcall_t
)(const struct device_node
*);
34 asmlinkage
int __invoke_psci_fn_hvc(u32
, u32
, u32
, u32
);
35 asmlinkage
int __invoke_psci_fn_smc(u32
, u32
, u32
, u32
);
42 PSCI_FN_AFFINITY_INFO
,
43 PSCI_FN_MIGRATE_INFO_TYPE
,
47 static u32 psci_function_id
[PSCI_FN_MAX
];
49 static int psci_to_linux_errno(int errno
)
52 case PSCI_RET_SUCCESS
:
54 case PSCI_RET_NOT_SUPPORTED
:
56 case PSCI_RET_INVALID_PARAMS
:
65 static u32
psci_power_state_pack(struct psci_power_state state
)
67 return ((state
.id
<< PSCI_0_2_POWER_STATE_ID_SHIFT
)
68 & PSCI_0_2_POWER_STATE_ID_MASK
) |
69 ((state
.type
<< PSCI_0_2_POWER_STATE_TYPE_SHIFT
)
70 & PSCI_0_2_POWER_STATE_TYPE_MASK
) |
71 ((state
.affinity_level
<< PSCI_0_2_POWER_STATE_AFFL_SHIFT
)
72 & PSCI_0_2_POWER_STATE_AFFL_MASK
);
75 static int psci_get_version(void)
79 err
= invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION
, 0, 0, 0);
83 static int psci_cpu_suspend(struct psci_power_state state
,
84 unsigned long entry_point
)
89 fn
= psci_function_id
[PSCI_FN_CPU_SUSPEND
];
90 power_state
= psci_power_state_pack(state
);
91 err
= invoke_psci_fn(fn
, power_state
, entry_point
, 0);
92 return psci_to_linux_errno(err
);
95 static int psci_cpu_off(struct psci_power_state state
)
100 fn
= psci_function_id
[PSCI_FN_CPU_OFF
];
101 power_state
= psci_power_state_pack(state
);
102 err
= invoke_psci_fn(fn
, power_state
, 0, 0);
103 return psci_to_linux_errno(err
);
106 static int psci_cpu_on(unsigned long cpuid
, unsigned long entry_point
)
111 fn
= psci_function_id
[PSCI_FN_CPU_ON
];
112 err
= invoke_psci_fn(fn
, cpuid
, entry_point
, 0);
113 return psci_to_linux_errno(err
);
116 static int psci_migrate(unsigned long cpuid
)
121 fn
= psci_function_id
[PSCI_FN_MIGRATE
];
122 err
= invoke_psci_fn(fn
, cpuid
, 0, 0);
123 return psci_to_linux_errno(err
);
126 static int psci_affinity_info(unsigned long target_affinity
,
127 unsigned long lowest_affinity_level
)
132 fn
= psci_function_id
[PSCI_FN_AFFINITY_INFO
];
133 err
= invoke_psci_fn(fn
, target_affinity
, lowest_affinity_level
, 0);
137 static int psci_migrate_info_type(void)
142 fn
= psci_function_id
[PSCI_FN_MIGRATE_INFO_TYPE
];
143 err
= invoke_psci_fn(fn
, 0, 0, 0);
147 static int get_set_conduit_method(struct device_node
*np
)
151 pr_info("probing for conduit method from DT.\n");
153 if (of_property_read_string(np
, "method", &method
)) {
154 pr_warn("missing \"method\" property\n");
158 if (!strcmp("hvc", method
)) {
159 invoke_psci_fn
= __invoke_psci_fn_hvc
;
160 } else if (!strcmp("smc", method
)) {
161 invoke_psci_fn
= __invoke_psci_fn_smc
;
163 pr_warn("invalid \"method\" property: %s\n", method
);
169 static void psci_sys_reset(enum reboot_mode reboot_mode
, const char *cmd
)
171 invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET
, 0, 0, 0);
174 static void psci_sys_poweroff(void)
176 invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF
, 0, 0, 0);
180 * PSCI Function IDs for v0.2+ are well defined so use
183 static int psci_0_2_init(struct device_node
*np
)
187 err
= get_set_conduit_method(np
);
192 ver
= psci_get_version();
194 if (ver
== PSCI_RET_NOT_SUPPORTED
) {
195 /* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */
196 pr_err("PSCI firmware does not comply with the v0.2 spec.\n");
200 pr_info("PSCIv%d.%d detected in firmware.\n",
201 PSCI_VERSION_MAJOR(ver
),
202 PSCI_VERSION_MINOR(ver
));
204 if (PSCI_VERSION_MAJOR(ver
) == 0 &&
205 PSCI_VERSION_MINOR(ver
) < 2) {
207 pr_err("Conflicting PSCI version detected.\n");
212 pr_info("Using standard PSCI v0.2 function IDs\n");
213 psci_function_id
[PSCI_FN_CPU_SUSPEND
] = PSCI_0_2_FN_CPU_SUSPEND
;
214 psci_ops
.cpu_suspend
= psci_cpu_suspend
;
216 psci_function_id
[PSCI_FN_CPU_OFF
] = PSCI_0_2_FN_CPU_OFF
;
217 psci_ops
.cpu_off
= psci_cpu_off
;
219 psci_function_id
[PSCI_FN_CPU_ON
] = PSCI_0_2_FN_CPU_ON
;
220 psci_ops
.cpu_on
= psci_cpu_on
;
222 psci_function_id
[PSCI_FN_MIGRATE
] = PSCI_0_2_FN_MIGRATE
;
223 psci_ops
.migrate
= psci_migrate
;
225 psci_function_id
[PSCI_FN_AFFINITY_INFO
] = PSCI_0_2_FN_AFFINITY_INFO
;
226 psci_ops
.affinity_info
= psci_affinity_info
;
228 psci_function_id
[PSCI_FN_MIGRATE_INFO_TYPE
] =
229 PSCI_0_2_FN_MIGRATE_INFO_TYPE
;
230 psci_ops
.migrate_info_type
= psci_migrate_info_type
;
232 arm_pm_restart
= psci_sys_reset
;
234 pm_power_off
= psci_sys_poweroff
;
242 * PSCI < v0.2 get PSCI Function IDs via DT.
244 static int psci_0_1_init(struct device_node
*np
)
249 err
= get_set_conduit_method(np
);
254 pr_info("Using PSCI v0.1 Function IDs from DT\n");
256 if (!of_property_read_u32(np
, "cpu_suspend", &id
)) {
257 psci_function_id
[PSCI_FN_CPU_SUSPEND
] = id
;
258 psci_ops
.cpu_suspend
= psci_cpu_suspend
;
261 if (!of_property_read_u32(np
, "cpu_off", &id
)) {
262 psci_function_id
[PSCI_FN_CPU_OFF
] = id
;
263 psci_ops
.cpu_off
= psci_cpu_off
;
266 if (!of_property_read_u32(np
, "cpu_on", &id
)) {
267 psci_function_id
[PSCI_FN_CPU_ON
] = id
;
268 psci_ops
.cpu_on
= psci_cpu_on
;
271 if (!of_property_read_u32(np
, "migrate", &id
)) {
272 psci_function_id
[PSCI_FN_MIGRATE
] = id
;
273 psci_ops
.migrate
= psci_migrate
;
281 static const struct of_device_id
const psci_of_match
[] __initconst
= {
282 { .compatible
= "arm,psci", .data
= psci_0_1_init
},
283 { .compatible
= "arm,psci-0.2", .data
= psci_0_2_init
},
287 int __init
psci_init(void)
289 struct device_node
*np
;
290 const struct of_device_id
*matched_np
;
291 psci_initcall_t init_fn
;
293 np
= of_find_matching_node_and_match(NULL
, psci_of_match
, &matched_np
);
297 init_fn
= (psci_initcall_t
)matched_np
->data
;