]>
Commit | Line | Data |
---|---|---|
573a3811 MY |
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> | |
9d922450 | 11 | #include <dm.h> |
573a3811 MY |
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 | }; |