Masahiro Yamada | 836c55d | 2017-04-14 11:10:24 +0900 | [diff] [blame] | 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/device.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 (of_device_is_compatible(dev, "arm,psci-0.2") || |
| 44 | of_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 | DECLARE_GLOBAL_DATA_PTR; |
| 60 | const char *method; |
| 61 | |
| 62 | method = fdt_stringlist_get(gd->fdt_blob, dev->of_offset, "method", 0, |
| 63 | NULL); |
| 64 | if (!method) { |
| 65 | printf("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 | printf("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 | }; |