psci.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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. #define pr_fmt(fmt) "psci: " fmt
  16. #include <linux/init.h>
  17. #include <linux/of.h>
  18. #include <linux/reboot.h>
  19. #include <linux/pm.h>
  20. #include <uapi/linux/psci.h>
  21. #include <asm/compiler.h>
  22. #include <asm/errno.h>
  23. #include <asm/opcodes-sec.h>
  24. #include <asm/opcodes-virt.h>
  25. #include <asm/psci.h>
  26. #include <asm/system_misc.h>
  27. struct psci_operations psci_ops;
  28. static int (*invoke_psci_fn)(u32, u32, u32, u32);
  29. typedef int (*psci_initcall_t)(const struct device_node *);
  30. enum psci_function {
  31. PSCI_FN_CPU_SUSPEND,
  32. PSCI_FN_CPU_ON,
  33. PSCI_FN_CPU_OFF,
  34. PSCI_FN_MIGRATE,
  35. PSCI_FN_AFFINITY_INFO,
  36. PSCI_FN_MIGRATE_INFO_TYPE,
  37. PSCI_FN_MAX,
  38. };
  39. static u32 psci_function_id[PSCI_FN_MAX];
  40. static int psci_to_linux_errno(int errno)
  41. {
  42. switch (errno) {
  43. case PSCI_RET_SUCCESS:
  44. return 0;
  45. case PSCI_RET_NOT_SUPPORTED:
  46. return -EOPNOTSUPP;
  47. case PSCI_RET_INVALID_PARAMS:
  48. return -EINVAL;
  49. case PSCI_RET_DENIED:
  50. return -EPERM;
  51. };
  52. return -EINVAL;
  53. }
  54. static u32 psci_power_state_pack(struct psci_power_state state)
  55. {
  56. return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT)
  57. & PSCI_0_2_POWER_STATE_ID_MASK) |
  58. ((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT)
  59. & PSCI_0_2_POWER_STATE_TYPE_MASK) |
  60. ((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT)
  61. & PSCI_0_2_POWER_STATE_AFFL_MASK);
  62. }
  63. /*
  64. * The following two functions are invoked via the invoke_psci_fn pointer
  65. * and will not be inlined, allowing us to piggyback on the AAPCS.
  66. */
  67. static noinline int __invoke_psci_fn_hvc(u32 function_id, u32 arg0, u32 arg1,
  68. u32 arg2)
  69. {
  70. asm volatile(
  71. __asmeq("%0", "r0")
  72. __asmeq("%1", "r1")
  73. __asmeq("%2", "r2")
  74. __asmeq("%3", "r3")
  75. __HVC(0)
  76. : "+r" (function_id)
  77. : "r" (arg0), "r" (arg1), "r" (arg2));
  78. return function_id;
  79. }
  80. static noinline int __invoke_psci_fn_smc(u32 function_id, u32 arg0, u32 arg1,
  81. u32 arg2)
  82. {
  83. asm volatile(
  84. __asmeq("%0", "r0")
  85. __asmeq("%1", "r1")
  86. __asmeq("%2", "r2")
  87. __asmeq("%3", "r3")
  88. __SMC(0)
  89. : "+r" (function_id)
  90. : "r" (arg0), "r" (arg1), "r" (arg2));
  91. return function_id;
  92. }
  93. static int psci_get_version(void)
  94. {
  95. int err;
  96. err = invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
  97. return err;
  98. }
  99. static int psci_cpu_suspend(struct psci_power_state state,
  100. unsigned long entry_point)
  101. {
  102. int err;
  103. u32 fn, power_state;
  104. fn = psci_function_id[PSCI_FN_CPU_SUSPEND];
  105. power_state = psci_power_state_pack(state);
  106. err = invoke_psci_fn(fn, power_state, entry_point, 0);
  107. return psci_to_linux_errno(err);
  108. }
  109. static int psci_cpu_off(struct psci_power_state state)
  110. {
  111. int err;
  112. u32 fn, power_state;
  113. fn = psci_function_id[PSCI_FN_CPU_OFF];
  114. power_state = psci_power_state_pack(state);
  115. err = invoke_psci_fn(fn, power_state, 0, 0);
  116. return psci_to_linux_errno(err);
  117. }
  118. static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
  119. {
  120. int err;
  121. u32 fn;
  122. fn = psci_function_id[PSCI_FN_CPU_ON];
  123. err = invoke_psci_fn(fn, cpuid, entry_point, 0);
  124. return psci_to_linux_errno(err);
  125. }
  126. static int psci_migrate(unsigned long cpuid)
  127. {
  128. int err;
  129. u32 fn;
  130. fn = psci_function_id[PSCI_FN_MIGRATE];
  131. err = invoke_psci_fn(fn, cpuid, 0, 0);
  132. return psci_to_linux_errno(err);
  133. }
  134. static int psci_affinity_info(unsigned long target_affinity,
  135. unsigned long lowest_affinity_level)
  136. {
  137. int err;
  138. u32 fn;
  139. fn = psci_function_id[PSCI_FN_AFFINITY_INFO];
  140. err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0);
  141. return err;
  142. }
  143. static int psci_migrate_info_type(void)
  144. {
  145. int err;
  146. u32 fn;
  147. fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE];
  148. err = invoke_psci_fn(fn, 0, 0, 0);
  149. return err;
  150. }
  151. static int get_set_conduit_method(struct device_node *np)
  152. {
  153. const char *method;
  154. pr_info("probing for conduit method from DT.\n");
  155. if (of_property_read_string(np, "method", &method)) {
  156. pr_warn("missing \"method\" property\n");
  157. return -ENXIO;
  158. }
  159. if (!strcmp("hvc", method)) {
  160. invoke_psci_fn = __invoke_psci_fn_hvc;
  161. } else if (!strcmp("smc", method)) {
  162. invoke_psci_fn = __invoke_psci_fn_smc;
  163. } else {
  164. pr_warn("invalid \"method\" property: %s\n", method);
  165. return -EINVAL;
  166. }
  167. return 0;
  168. }
  169. static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd)
  170. {
  171. invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
  172. }
  173. static void psci_sys_poweroff(void)
  174. {
  175. invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
  176. }
  177. /*
  178. * PSCI Function IDs for v0.2+ are well defined so use
  179. * standard values.
  180. */
  181. static int psci_0_2_init(struct device_node *np)
  182. {
  183. int err, ver;
  184. err = get_set_conduit_method(np);
  185. if (err)
  186. goto out_put_node;
  187. ver = psci_get_version();
  188. if (ver == PSCI_RET_NOT_SUPPORTED) {
  189. /* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */
  190. pr_err("PSCI firmware does not comply with the v0.2 spec.\n");
  191. err = -EOPNOTSUPP;
  192. goto out_put_node;
  193. } else {
  194. pr_info("PSCIv%d.%d detected in firmware.\n",
  195. PSCI_VERSION_MAJOR(ver),
  196. PSCI_VERSION_MINOR(ver));
  197. if (PSCI_VERSION_MAJOR(ver) == 0 &&
  198. PSCI_VERSION_MINOR(ver) < 2) {
  199. err = -EINVAL;
  200. pr_err("Conflicting PSCI version detected.\n");
  201. goto out_put_node;
  202. }
  203. }
  204. pr_info("Using standard PSCI v0.2 function IDs\n");
  205. psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN_CPU_SUSPEND;
  206. psci_ops.cpu_suspend = psci_cpu_suspend;
  207. psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
  208. psci_ops.cpu_off = psci_cpu_off;
  209. psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN_CPU_ON;
  210. psci_ops.cpu_on = psci_cpu_on;
  211. psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN_MIGRATE;
  212. psci_ops.migrate = psci_migrate;
  213. psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN_AFFINITY_INFO;
  214. psci_ops.affinity_info = psci_affinity_info;
  215. psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =
  216. PSCI_0_2_FN_MIGRATE_INFO_TYPE;
  217. psci_ops.migrate_info_type = psci_migrate_info_type;
  218. arm_pm_restart = psci_sys_reset;
  219. pm_power_off = psci_sys_poweroff;
  220. out_put_node:
  221. of_node_put(np);
  222. return err;
  223. }
  224. /*
  225. * PSCI < v0.2 get PSCI Function IDs via DT.
  226. */
  227. static int psci_0_1_init(struct device_node *np)
  228. {
  229. u32 id;
  230. int err;
  231. err = get_set_conduit_method(np);
  232. if (err)
  233. goto out_put_node;
  234. pr_info("Using PSCI v0.1 Function IDs from DT\n");
  235. if (!of_property_read_u32(np, "cpu_suspend", &id)) {
  236. psci_function_id[PSCI_FN_CPU_SUSPEND] = id;
  237. psci_ops.cpu_suspend = psci_cpu_suspend;
  238. }
  239. if (!of_property_read_u32(np, "cpu_off", &id)) {
  240. psci_function_id[PSCI_FN_CPU_OFF] = id;
  241. psci_ops.cpu_off = psci_cpu_off;
  242. }
  243. if (!of_property_read_u32(np, "cpu_on", &id)) {
  244. psci_function_id[PSCI_FN_CPU_ON] = id;
  245. psci_ops.cpu_on = psci_cpu_on;
  246. }
  247. if (!of_property_read_u32(np, "migrate", &id)) {
  248. psci_function_id[PSCI_FN_MIGRATE] = id;
  249. psci_ops.migrate = psci_migrate;
  250. }
  251. out_put_node:
  252. of_node_put(np);
  253. return err;
  254. }
  255. static const struct of_device_id psci_of_match[] __initconst = {
  256. { .compatible = "arm,psci", .data = psci_0_1_init},
  257. { .compatible = "arm,psci-0.2", .data = psci_0_2_init},
  258. {},
  259. };
  260. int __init psci_init(void)
  261. {
  262. struct device_node *np;
  263. const struct of_device_id *matched_np;
  264. psci_initcall_t init_fn;
  265. np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);
  266. if (!np)
  267. return -ENODEV;
  268. init_fn = (psci_initcall_t)matched_np->data;
  269. return init_fn(np);
  270. }