1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2016 NXP Semiconductor, Inc.
4 */
5
6#include <common.h>
7#include <asm/cache.h>
8#include <linux/libfdt.h>
9#include <fdt_support.h>
10#include <linux/sizes.h>
11#include <linux/kernel.h>
12#include <asm/psci.h>
13#if CONFIG_IS_ENABLED(ARMV8_SEC_FIRMWARE_SUPPORT)
14#include <asm/armv8/sec_firmware.h>
15#endif
16
17int fdt_psci(void *fdt)
18{
19#if defined(CONFIG_ARMV7_PSCI) || defined(CONFIG_ARMV8_PSCI) || \
20	defined(CONFIG_SEC_FIRMWARE_ARMV8_PSCI)
21	int nodeoff;
22	unsigned int psci_ver = 0;
23	int tmp;
24
25	nodeoff = fdt_path_offset(fdt, "/cpus");
26	if (nodeoff < 0) {
27		printf("couldn't find /cpus\n");
28		return nodeoff;
29	}
30
31	/* add 'enable-method = "psci"' to each cpu node */
32	for (tmp = fdt_first_subnode(fdt, nodeoff);
33	     tmp >= 0;
34	     tmp = fdt_next_subnode(fdt, tmp)) {
35		const struct fdt_property *prop;
36		int len;
37
38		prop = fdt_get_property(fdt, tmp, "device_type", &len);
39		if (!prop)
40			continue;
41		if (len < 4)
42			continue;
43		if (strcmp(prop->data, "cpu"))
44			continue;
45
46		/*
47		 * Not checking rv here, our approach is to skip over errors in
48		 * individual cpu nodes, hopefully some of the nodes are
49		 * processed correctly and those will boot
50		 */
51		fdt_setprop_string(fdt, tmp, "enable-method", "psci");
52	}
53
54	nodeoff = fdt_path_offset(fdt, "/psci");
55	if (nodeoff >= 0)
56		goto init_psci_node;
57
58	nodeoff = fdt_path_offset(fdt, "/");
59	if (nodeoff < 0)
60		return nodeoff;
61
62	nodeoff = fdt_add_subnode(fdt, nodeoff, "psci");
63	if (nodeoff < 0)
64		return nodeoff;
65
66init_psci_node:
67#if CONFIG_IS_ENABLED(ARMV8_SEC_FIRMWARE_SUPPORT)
68	psci_ver = sec_firmware_support_psci_version();
69#elif defined(CONFIG_ARMV7_PSCI_1_0) || defined(CONFIG_ARMV8_PSCI)
70	psci_ver = ARM_PSCI_VER_1_0;
71#elif defined(CONFIG_ARMV7_PSCI_0_2)
72	psci_ver = ARM_PSCI_VER_0_2;
73#endif
74	if (psci_ver >= ARM_PSCI_VER_1_0) {
75		tmp = fdt_setprop_string(fdt, nodeoff,
76				"compatible", "arm,psci-1.0");
77		if (tmp)
78			return tmp;
79	}
80
81	if (psci_ver >= ARM_PSCI_VER_0_2) {
82		tmp = fdt_appendprop_string(fdt, nodeoff,
83				"compatible", "arm,psci-0.2");
84		if (tmp)
85			return tmp;
86	}
87
88#if !CONFIG_IS_ENABLED(ARMV8_SEC_FIRMWARE_SUPPORT)
89	/*
90	 * The Secure firmware framework isn't able to support PSCI version 0.1.
91	 */
92	if (psci_ver < ARM_PSCI_VER_0_2) {
93		tmp = fdt_appendprop_string(fdt, nodeoff,
94				"compatible", "arm,psci");
95		if (tmp)
96			return tmp;
97		tmp = fdt_setprop_u32(fdt, nodeoff, "cpu_suspend",
98				ARM_PSCI_FN_CPU_SUSPEND);
99		if (tmp)
100			return tmp;
101		tmp = fdt_setprop_u32(fdt, nodeoff, "cpu_off",
102				ARM_PSCI_FN_CPU_OFF);
103		if (tmp)
104			return tmp;
105		tmp = fdt_setprop_u32(fdt, nodeoff, "cpu_on",
106				ARM_PSCI_FN_CPU_ON);
107		if (tmp)
108			return tmp;
109		tmp = fdt_setprop_u32(fdt, nodeoff, "migrate",
110				ARM_PSCI_FN_MIGRATE);
111		if (tmp)
112			return tmp;
113	}
114#endif
115
116	tmp = fdt_setprop_string(fdt, nodeoff, "method", "smc");
117	if (tmp)
118		return tmp;
119
120	tmp = fdt_setprop_string(fdt, nodeoff, "status", "okay");
121	if (tmp)
122		return tmp;
123
124#endif
125	return 0;
126}
127