1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2017 Masahiro Yamada <yamada.masahiro@socionext.com> 4 * 5 * Based on drivers/firmware/psci.c from Linux: 6 * Copyright (C) 2015 ARM Limited 7 */ 8 9 #include <common.h> 10 #include <dm.h> 11 #include <dm/lists.h> 12 #include <linux/libfdt.h> 13 #include <linux/arm-smccc.h> 14 #include <linux/errno.h> 15 #include <linux/printk.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 pr_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 DECLARE_GLOBAL_DATA_PTR; 60 const char *method; 61 62 method = fdt_stringlist_get(gd->fdt_blob, dev_of_offset(dev), "method", 63 0, NULL); 64 if (!method) { 65 pr_warn("missing \"method\" property\n"); 66 return -ENXIO; 67 } 68 69 if (!strcmp("hvc", method)) { 70 invoke_psci_fn = __invoke_psci_fn_hvc; 71 } else if (!strcmp("smc", method)) { 72 invoke_psci_fn = __invoke_psci_fn_smc; 73 } else { 74 pr_warn("invalid \"method\" property: %s\n", method); 75 return -EINVAL; 76 } 77 78 return 0; 79 } 80 81 static const struct udevice_id psci_of_match[] = { 82 { .compatible = "arm,psci" }, 83 { .compatible = "arm,psci-0.2" }, 84 { .compatible = "arm,psci-1.0" }, 85 {}, 86 }; 87 88 U_BOOT_DRIVER(psci) = { 89 .name = "psci", 90 .id = UCLASS_FIRMWARE, 91 .of_match = psci_of_match, 92 .bind = psci_bind, 93 .probe = psci_probe, 94 }; 95