1 /* 2 * Copyright (C) 2017 Masahiro Yamada <yamada.masahiro@socionext.com> 3 * 4 * Based on drivers/firmware/psci.c from Linux: 5 * Copyright (C) 2015 ARM Limited 6 * 7 * SPDX-License-Identifier: GPL-2.0+ 8 */ 9 10 #include <common.h> 11 #include <dm.h> 12 #include <dm/lists.h> 13 #include <libfdt.h> 14 #include <linux/arm-smccc.h> 15 #include <linux/errno.h> 16 #include <linux/psci.h> 17 18 psci_fn *invoke_psci_fn; 19 20 static unsigned long __invoke_psci_fn_hvc(unsigned long function_id, 21 unsigned long arg0, unsigned long arg1, 22 unsigned long arg2) 23 { 24 struct arm_smccc_res res; 25 26 arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); 27 return res.a0; 28 } 29 30 static unsigned long __invoke_psci_fn_smc(unsigned long function_id, 31 unsigned long arg0, unsigned long arg1, 32 unsigned long arg2) 33 { 34 struct arm_smccc_res res; 35 36 arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); 37 return res.a0; 38 } 39 40 static int psci_bind(struct udevice *dev) 41 { 42 /* No SYSTEM_RESET support for PSCI 0.1 */ 43 if (device_is_compatible(dev, "arm,psci-0.2") || 44 device_is_compatible(dev, "arm,psci-1.0")) { 45 int ret; 46 47 /* bind psci-sysreset optionally */ 48 ret = device_bind_driver(dev, "psci-sysreset", "psci-sysreset", 49 NULL); 50 if (ret) 51 debug("PSCI System Reset was not bound.\n"); 52 } 53 54 return 0; 55 } 56 57 static int psci_probe(struct udevice *dev) 58 { 59 const char *method; 60 61 method = dev_read_prop(dev, "method", NULL); 62 if (!method) { 63 printf("missing \"method\" property\n"); 64 return -ENXIO; 65 } 66 67 if (!strcmp("hvc", method)) { 68 invoke_psci_fn = __invoke_psci_fn_hvc; 69 } else if (!strcmp("smc", method)) { 70 invoke_psci_fn = __invoke_psci_fn_smc; 71 } else { 72 printf("invalid \"method\" property: %s\n", method); 73 return -EINVAL; 74 } 75 76 return 0; 77 } 78 79 static const struct udevice_id psci_of_match[] = { 80 { .compatible = "arm,psci" }, 81 { .compatible = "arm,psci-0.2" }, 82 { .compatible = "arm,psci-1.0" }, 83 {}, 84 }; 85 86 U_BOOT_DRIVER(psci) = { 87 .name = "psci", 88 .id = UCLASS_FIRMWARE, 89 .of_match = psci_of_match, 90 .bind = psci_bind, 91 .probe = psci_probe, 92 }; 93