]>
Commit | Line | Data |
---|---|---|
9f779fa4 PF |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright 2019 NXP | |
4 | */ | |
5 | ||
6 | #include <common.h> | |
f7ae49fc | 7 | #include <log.h> |
9f779fa4 | 8 | #include <asm/arch/sci/sci.h> |
01cacf96 | 9 | #include <asm/arch/sys_proto.h> |
9f779fa4 PF |
10 | #include <dm/ofnode.h> |
11 | #include <fdt_support.h> | |
f44afd54 | 12 | #include <linux/libfdt.h> |
9f779fa4 PF |
13 | |
14 | DECLARE_GLOBAL_DATA_PTR; | |
15 | ||
16 | static bool check_owned_resource(sc_rsrc_t rsrc_id) | |
17 | { | |
18 | bool owned; | |
19 | ||
20 | owned = sc_rm_is_resource_owned(-1, rsrc_id); | |
21 | ||
22 | return owned; | |
23 | } | |
24 | ||
25 | static int disable_fdt_node(void *blob, int nodeoffset) | |
26 | { | |
27 | int rc, ret; | |
28 | const char *status = "disabled"; | |
29 | ||
30 | do { | |
31 | rc = fdt_setprop(blob, nodeoffset, "status", status, | |
32 | strlen(status) + 1); | |
33 | if (rc) { | |
34 | if (rc == -FDT_ERR_NOSPACE) { | |
35 | ret = fdt_increase_size(blob, 512); | |
36 | if (ret) | |
37 | return ret; | |
38 | } | |
39 | } | |
40 | } while (rc == -FDT_ERR_NOSPACE); | |
41 | ||
42 | return rc; | |
43 | } | |
44 | ||
45 | static void update_fdt_with_owned_resources(void *blob) | |
46 | { | |
47 | /* | |
48 | * Traverses the fdt nodes, check its power domain and use | |
49 | * the resource id in the power domain for checking whether | |
50 | * it is owned by current partition | |
51 | */ | |
52 | struct fdtdec_phandle_args args; | |
53 | int offset = 0, depth = 0; | |
54 | u32 rsrc_id; | |
55 | int rc, i; | |
56 | ||
57 | for (offset = fdt_next_node(blob, offset, &depth); offset > 0; | |
58 | offset = fdt_next_node(blob, offset, &depth)) { | |
59 | debug("Node name: %s, depth %d\n", | |
60 | fdt_get_name(blob, offset, NULL), depth); | |
61 | ||
62 | if (!fdt_get_property(blob, offset, "power-domains", NULL)) { | |
63 | debug(" - ignoring node %s\n", | |
64 | fdt_get_name(blob, offset, NULL)); | |
65 | continue; | |
66 | } | |
67 | ||
68 | if (!fdtdec_get_is_enabled(blob, offset)) { | |
69 | debug(" - ignoring node %s\n", | |
70 | fdt_get_name(blob, offset, NULL)); | |
71 | continue; | |
72 | } | |
73 | ||
74 | i = 0; | |
75 | while (true) { | |
76 | rc = fdtdec_parse_phandle_with_args(blob, offset, | |
77 | "power-domains", | |
78 | "#power-domain-cells", | |
79 | 0, i++, &args); | |
80 | if (rc == -ENOENT) { | |
81 | break; | |
82 | } else if (rc) { | |
83 | printf("Parse power-domains of %s wrong: %d\n", | |
84 | fdt_get_name(blob, offset, NULL), rc); | |
85 | continue; | |
86 | } | |
87 | ||
88 | rsrc_id = args.args[0]; | |
89 | ||
90 | if (!check_owned_resource(rsrc_id)) { | |
91 | rc = disable_fdt_node(blob, offset); | |
92 | if (!rc) { | |
93 | printf("Disable %s rsrc %u not owned\n", | |
94 | fdt_get_name(blob, offset, NULL), | |
95 | rsrc_id); | |
96 | } else { | |
97 | printf("Unable to disable %s, err=%s\n", | |
98 | fdt_get_name(blob, offset, NULL), | |
99 | fdt_strerror(rc)); | |
100 | } | |
101 | } | |
102 | } | |
103 | } | |
104 | } | |
105 | ||
01cacf96 PF |
106 | static int config_smmu_resource_sid(int rsrc, int sid) |
107 | { | |
108 | int err; | |
109 | ||
01cacf96 PF |
110 | err = sc_rm_set_master_sid(-1, rsrc, sid); |
111 | debug("set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err); | |
112 | if (err != SC_ERR_NONE) { | |
d52a03b1 PF |
113 | if (!check_owned_resource(rsrc)) { |
114 | printf("%s rsrc[%d] not owned\n", __func__, rsrc); | |
115 | return -1; | |
116 | } | |
01cacf96 PF |
117 | pr_err("fail set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err); |
118 | return -EINVAL; | |
119 | } | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | static int config_smmu_fdt_device_sid(void *blob, int device_offset, int sid) | |
125 | { | |
126 | const char *name = fdt_get_name(blob, device_offset, NULL); | |
127 | struct fdtdec_phandle_args args; | |
128 | int rsrc, ret; | |
129 | int proplen; | |
130 | const fdt32_t *prop; | |
131 | int i; | |
132 | ||
133 | prop = fdt_getprop(blob, device_offset, "fsl,sc_rsrc_id", &proplen); | |
134 | if (prop) { | |
135 | int i; | |
136 | ||
137 | debug("configure node %s sid 0x%x for %d resources\n", | |
138 | name, sid, (int)(proplen / sizeof(fdt32_t))); | |
139 | for (i = 0; i < proplen / sizeof(fdt32_t); ++i) { | |
140 | ret = config_smmu_resource_sid(fdt32_to_cpu(prop[i]), | |
141 | sid); | |
142 | if (ret) | |
143 | return ret; | |
144 | } | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
149 | i = 0; | |
150 | while (true) { | |
151 | ret = fdtdec_parse_phandle_with_args(blob, device_offset, | |
152 | "power-domains", | |
153 | "#power-domain-cells", | |
154 | 0, i++, &args); | |
155 | if (ret == -ENOENT) { | |
156 | break; | |
157 | } else if (ret) { | |
158 | printf("Parse power-domains of node %s wrong: %d\n", | |
159 | fdt_get_name(blob, device_offset, NULL), ret); | |
160 | continue; | |
161 | } | |
162 | ||
163 | debug("configure node %s sid 0x%x rsrc=%d\n", | |
164 | name, sid, rsrc); | |
165 | rsrc = args.args[0]; | |
166 | ||
167 | ret = config_smmu_resource_sid(rsrc, sid); | |
168 | if (ret) | |
169 | break; | |
170 | } | |
171 | ||
172 | return ret; | |
173 | } | |
174 | ||
175 | static int config_smmu_fdt(void *blob) | |
176 | { | |
177 | int offset, proplen, i, ret; | |
178 | const fdt32_t *prop; | |
179 | const char *name; | |
180 | ||
181 | /* Legacy smmu bindings, still used by xen. */ | |
182 | offset = fdt_node_offset_by_compatible(blob, 0, "arm,mmu-500"); | |
183 | prop = fdt_getprop(blob, offset, "mmu-masters", &proplen); | |
184 | if (offset > 0 && prop) { | |
185 | debug("found legacy mmu-masters property\n"); | |
186 | ||
187 | for (i = 0; i < proplen / 8; ++i) { | |
188 | u32 phandle = fdt32_to_cpu(prop[2 * i]); | |
189 | int sid = fdt32_to_cpu(prop[2 * i + 1]); | |
190 | int device_offset; | |
191 | ||
192 | device_offset = fdt_node_offset_by_phandle(blob, | |
193 | phandle); | |
194 | if (device_offset < 0) { | |
195 | pr_err("Not find device from mmu_masters: %d", | |
196 | device_offset); | |
197 | continue; | |
198 | } | |
199 | ret = config_smmu_fdt_device_sid(blob, device_offset, | |
200 | sid); | |
201 | if (ret) | |
202 | return ret; | |
203 | } | |
204 | ||
205 | /* Ignore new bindings if old bindings found, just like linux. */ | |
206 | return 0; | |
207 | } | |
208 | ||
209 | /* Generic smmu bindings */ | |
210 | offset = 0; | |
211 | while ((offset = fdt_next_node(blob, offset, NULL)) > 0) { | |
212 | name = fdt_get_name(blob, offset, NULL); | |
213 | prop = fdt_getprop(blob, offset, "iommus", &proplen); | |
214 | if (!prop) | |
215 | continue; | |
216 | debug("node %s iommus proplen %d\n", name, proplen); | |
217 | ||
218 | if (proplen == 12) { | |
219 | int sid = fdt32_to_cpu(prop[1]); | |
220 | ||
221 | config_smmu_fdt_device_sid(blob, offset, sid); | |
222 | } else if (proplen != 4) { | |
223 | debug("node %s ignore unexpected iommus proplen=%d\n", | |
224 | name, proplen); | |
225 | } | |
226 | } | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
94e4d028 PF |
231 | static int ft_add_optee_node(void *fdt, bd_t *bd) |
232 | { | |
233 | const char *path, *subpath; | |
234 | int offs; | |
235 | ||
236 | /* | |
237 | * No TEE space allocated indicating no TEE running, so no | |
238 | * need to add optee node in dts | |
239 | */ | |
240 | if (!boot_pointer[1]) | |
241 | return 0; | |
242 | ||
243 | offs = fdt_increase_size(fdt, 512); | |
244 | if (offs) { | |
245 | printf("No Space for dtb\n"); | |
246 | return 1; | |
247 | } | |
248 | ||
249 | path = "/firmware"; | |
250 | offs = fdt_path_offset(fdt, path); | |
251 | if (offs < 0) { | |
252 | path = "/"; | |
253 | offs = fdt_path_offset(fdt, path); | |
254 | ||
255 | if (offs < 0) { | |
256 | printf("Could not find root node.\n"); | |
257 | return offs; | |
258 | } | |
259 | ||
260 | subpath = "firmware"; | |
261 | offs = fdt_add_subnode(fdt, offs, subpath); | |
262 | if (offs < 0) { | |
263 | printf("Could not create %s node.\n", subpath); | |
264 | return offs; | |
265 | } | |
266 | } | |
267 | ||
268 | subpath = "optee"; | |
269 | offs = fdt_add_subnode(fdt, offs, subpath); | |
270 | if (offs < 0) { | |
271 | printf("Could not create %s node.\n", subpath); | |
272 | return offs; | |
273 | } | |
274 | ||
275 | fdt_setprop_string(fdt, offs, "compatible", "linaro,optee-tz"); | |
276 | fdt_setprop_string(fdt, offs, "method", "smc"); | |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
9f779fa4 PF |
281 | int ft_system_setup(void *blob, bd_t *bd) |
282 | { | |
01cacf96 | 283 | int ret; |
f44afd54 PF |
284 | int off; |
285 | ||
286 | if (CONFIG_BOOTAUX_RESERVED_MEM_BASE) { | |
287 | off = fdt_add_mem_rsv(blob, CONFIG_BOOTAUX_RESERVED_MEM_BASE, | |
288 | CONFIG_BOOTAUX_RESERVED_MEM_SIZE); | |
289 | if (off < 0) | |
290 | printf("Failed to reserve memory for bootaux: %s\n", | |
291 | fdt_strerror(off)); | |
292 | } | |
01cacf96 | 293 | |
9f779fa4 PF |
294 | update_fdt_with_owned_resources(blob); |
295 | ||
01cacf96 PF |
296 | if (is_imx8qm()) { |
297 | ret = config_smmu_fdt(blob); | |
298 | if (ret) | |
299 | return ret; | |
300 | } | |
301 | ||
94e4d028 | 302 | return ft_add_optee_node(blob, bd); |
9f779fa4 | 303 | } |