blob: ce78c8ce919a1c7e3e792c1b13316f8cf3454dbf [file] [log] [blame]
Peng Fan47466162019-08-26 08:11:56 +00001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2019 NXP
4 */
5
Simon Glass0f2af882020-05-10 11:40:05 -06006#include <log.h>
Peng Fan2e0644a2023-04-28 12:08:09 +08007#include <firmware/imx/sci/sci.h>
Peng Fan429c9ac2019-08-26 08:12:06 +00008#include <asm/arch/sys_proto.h>
Simon Glass3ba929a2020-10-30 21:38:53 -06009#include <asm/global_data.h>
Peng Fan47466162019-08-26 08:11:56 +000010#include <dm/ofnode.h>
11#include <fdt_support.h>
Peng Fanae617972020-05-05 20:28:37 +080012#include <linux/libfdt.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060013#include <linux/printk.h>
Stefan Eichenberger00293b62024-12-11 13:18:55 +010014#include <cpu.h>
15#include <dm.h>
Peng Fan47466162019-08-26 08:11:56 +000016
17DECLARE_GLOBAL_DATA_PTR;
18
19static bool check_owned_resource(sc_rsrc_t rsrc_id)
20{
21 bool owned;
22
23 owned = sc_rm_is_resource_owned(-1, rsrc_id);
24
25 return owned;
26}
27
28static int disable_fdt_node(void *blob, int nodeoffset)
29{
30 int rc, ret;
31 const char *status = "disabled";
32
33 do {
34 rc = fdt_setprop(blob, nodeoffset, "status", status,
35 strlen(status) + 1);
36 if (rc) {
37 if (rc == -FDT_ERR_NOSPACE) {
38 ret = fdt_increase_size(blob, 512);
39 if (ret)
40 return ret;
41 }
42 }
43 } while (rc == -FDT_ERR_NOSPACE);
44
45 return rc;
46}
47
48static void update_fdt_with_owned_resources(void *blob)
49{
50 /*
51 * Traverses the fdt nodes, check its power domain and use
52 * the resource id in the power domain for checking whether
53 * it is owned by current partition
54 */
55 struct fdtdec_phandle_args args;
56 int offset = 0, depth = 0;
57 u32 rsrc_id;
58 int rc, i;
59
60 for (offset = fdt_next_node(blob, offset, &depth); offset > 0;
61 offset = fdt_next_node(blob, offset, &depth)) {
62 debug("Node name: %s, depth %d\n",
63 fdt_get_name(blob, offset, NULL), depth);
64
65 if (!fdt_get_property(blob, offset, "power-domains", NULL)) {
66 debug(" - ignoring node %s\n",
67 fdt_get_name(blob, offset, NULL));
68 continue;
69 }
70
71 if (!fdtdec_get_is_enabled(blob, offset)) {
72 debug(" - ignoring node %s\n",
73 fdt_get_name(blob, offset, NULL));
74 continue;
75 }
76
77 i = 0;
78 while (true) {
79 rc = fdtdec_parse_phandle_with_args(blob, offset,
80 "power-domains",
81 "#power-domain-cells",
82 0, i++, &args);
83 if (rc == -ENOENT) {
84 break;
85 } else if (rc) {
86 printf("Parse power-domains of %s wrong: %d\n",
87 fdt_get_name(blob, offset, NULL), rc);
88 continue;
89 }
90
91 rsrc_id = args.args[0];
92
93 if (!check_owned_resource(rsrc_id)) {
94 rc = disable_fdt_node(blob, offset);
95 if (!rc) {
96 printf("Disable %s rsrc %u not owned\n",
97 fdt_get_name(blob, offset, NULL),
98 rsrc_id);
99 } else {
100 printf("Unable to disable %s, err=%s\n",
101 fdt_get_name(blob, offset, NULL),
102 fdt_strerror(rc));
103 }
104 }
105 }
106 }
107}
108
Peng Fan429c9ac2019-08-26 08:12:06 +0000109static int config_smmu_resource_sid(int rsrc, int sid)
110{
111 int err;
112
Peng Fan429c9ac2019-08-26 08:12:06 +0000113 err = sc_rm_set_master_sid(-1, rsrc, sid);
114 debug("set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err);
Peng Fan25b7eb42023-06-15 18:08:58 +0800115 if (err) {
Peng Fan5bc54482020-05-05 20:28:46 +0800116 if (!check_owned_resource(rsrc)) {
117 printf("%s rsrc[%d] not owned\n", __func__, rsrc);
118 return -1;
119 }
Peng Fan429c9ac2019-08-26 08:12:06 +0000120 pr_err("fail set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err);
121 return -EINVAL;
122 }
123
124 return 0;
125}
126
127static int config_smmu_fdt_device_sid(void *blob, int device_offset, int sid)
128{
129 const char *name = fdt_get_name(blob, device_offset, NULL);
130 struct fdtdec_phandle_args args;
131 int rsrc, ret;
132 int proplen;
133 const fdt32_t *prop;
134 int i;
135
136 prop = fdt_getprop(blob, device_offset, "fsl,sc_rsrc_id", &proplen);
137 if (prop) {
138 int i;
139
140 debug("configure node %s sid 0x%x for %d resources\n",
141 name, sid, (int)(proplen / sizeof(fdt32_t)));
142 for (i = 0; i < proplen / sizeof(fdt32_t); ++i) {
143 ret = config_smmu_resource_sid(fdt32_to_cpu(prop[i]),
144 sid);
145 if (ret)
146 return ret;
147 }
148
149 return 0;
150 }
151
152 i = 0;
153 while (true) {
154 ret = fdtdec_parse_phandle_with_args(blob, device_offset,
155 "power-domains",
156 "#power-domain-cells",
157 0, i++, &args);
158 if (ret == -ENOENT) {
159 break;
160 } else if (ret) {
161 printf("Parse power-domains of node %s wrong: %d\n",
162 fdt_get_name(blob, device_offset, NULL), ret);
163 continue;
164 }
165
166 debug("configure node %s sid 0x%x rsrc=%d\n",
167 name, sid, rsrc);
168 rsrc = args.args[0];
169
170 ret = config_smmu_resource_sid(rsrc, sid);
171 if (ret)
172 break;
173 }
174
175 return ret;
176}
177
178static int config_smmu_fdt(void *blob)
179{
180 int offset, proplen, i, ret;
181 const fdt32_t *prop;
182 const char *name;
183
184 /* Legacy smmu bindings, still used by xen. */
185 offset = fdt_node_offset_by_compatible(blob, 0, "arm,mmu-500");
186 prop = fdt_getprop(blob, offset, "mmu-masters", &proplen);
187 if (offset > 0 && prop) {
188 debug("found legacy mmu-masters property\n");
189
190 for (i = 0; i < proplen / 8; ++i) {
191 u32 phandle = fdt32_to_cpu(prop[2 * i]);
192 int sid = fdt32_to_cpu(prop[2 * i + 1]);
193 int device_offset;
194
195 device_offset = fdt_node_offset_by_phandle(blob,
196 phandle);
197 if (device_offset < 0) {
198 pr_err("Not find device from mmu_masters: %d",
199 device_offset);
200 continue;
201 }
202 ret = config_smmu_fdt_device_sid(blob, device_offset,
203 sid);
204 if (ret)
205 return ret;
206 }
207
208 /* Ignore new bindings if old bindings found, just like linux. */
209 return 0;
210 }
211
212 /* Generic smmu bindings */
213 offset = 0;
214 while ((offset = fdt_next_node(blob, offset, NULL)) > 0) {
215 name = fdt_get_name(blob, offset, NULL);
216 prop = fdt_getprop(blob, offset, "iommus", &proplen);
217 if (!prop)
218 continue;
219 debug("node %s iommus proplen %d\n", name, proplen);
220
221 if (proplen == 12) {
222 int sid = fdt32_to_cpu(prop[1]);
223
224 config_smmu_fdt_device_sid(blob, offset, sid);
225 } else if (proplen != 4) {
226 debug("node %s ignore unexpected iommus proplen=%d\n",
227 name, proplen);
228 }
229 }
230
231 return 0;
232}
233
Masahiro Yamadaf7ed78b2020-06-26 15:13:33 +0900234static int ft_add_optee_node(void *fdt, struct bd_info *bd)
Peng Fan064fe832019-08-26 08:12:13 +0000235{
236 const char *path, *subpath;
237 int offs;
238
239 /*
240 * No TEE space allocated indicating no TEE running, so no
241 * need to add optee node in dts
242 */
243 if (!boot_pointer[1])
244 return 0;
245
246 offs = fdt_increase_size(fdt, 512);
247 if (offs) {
248 printf("No Space for dtb\n");
249 return 1;
250 }
251
252 path = "/firmware";
253 offs = fdt_path_offset(fdt, path);
254 if (offs < 0) {
255 path = "/";
256 offs = fdt_path_offset(fdt, path);
257
258 if (offs < 0) {
259 printf("Could not find root node.\n");
260 return offs;
261 }
262
263 subpath = "firmware";
264 offs = fdt_add_subnode(fdt, offs, subpath);
265 if (offs < 0) {
266 printf("Could not create %s node.\n", subpath);
267 return offs;
268 }
269 }
270
271 subpath = "optee";
272 offs = fdt_add_subnode(fdt, offs, subpath);
273 if (offs < 0) {
274 printf("Could not create %s node.\n", subpath);
275 return offs;
276 }
277
278 fdt_setprop_string(fdt, offs, "compatible", "linaro,optee-tz");
279 fdt_setprop_string(fdt, offs, "method", "smc");
280
281 return 0;
282}
283
Stefan Eichenberger00293b62024-12-11 13:18:55 +0100284static int delete_node(void *blob, const char *node)
285{
286 int nodeoffset;
287 int err;
288
289 nodeoffset = fdt_path_offset(blob, node);
290 if (nodeoffset < 0)
291 return -ENXIO;
292
293 err = fdt_del_node(blob, nodeoffset);
294 if (err)
295 return -EINVAL;
296
297 return 0;
298}
299
300static int change_property(void *blob, const char *node, const char *property,
301 const void *value, int len)
302{
303 int nodeoffset;
304 int err;
305
306 nodeoffset = fdt_path_offset(blob, node);
307 if (nodeoffset < 0)
308 return -ENXIO;
309
310 err = fdt_setprop(blob, nodeoffset, property, value, len);
311 if (err)
312 return -EINVAL;
313
314 return 0;
315}
316
317static void update_fdt_gpu_industrial_frequencies(void *blob)
318{
319 u32 gpu_opp_table[6];
320 u32 gpu_assigned_clocks[2];
321 int err;
322
323 gpu_opp_table[0] = cpu_to_fdt32(625000); /* Normal Core Clock */
324 gpu_opp_table[1] = cpu_to_fdt32(0);
325 gpu_opp_table[2] = cpu_to_fdt32(625000); /* Normal Shader Clock */
326 gpu_opp_table[3] = cpu_to_fdt32(0);
327 gpu_opp_table[4] = cpu_to_fdt32(400000); /* Low Shader and Core Clock */
328 gpu_opp_table[5] = cpu_to_fdt32(0);
329
330 gpu_assigned_clocks[0] = cpu_to_fdt32(625000000); /* Core Clock */
331 gpu_assigned_clocks[1] = cpu_to_fdt32(625000000); /* Shader Clock */
332
333 err = change_property(blob, "/bus@53100000/gpu@53100000",
334 "assigned-clock-rates", gpu_assigned_clocks,
335 sizeof(gpu_assigned_clocks));
336 if (err && err != ENXIO)
337 printf("Failed to set assigned-clock-rates for GPU0: %s\n",
338 fdt_strerror(err));
339
340 err = change_property(blob, "/bus@54100000/gpu@54100000",
341 "assigned-clock-rates", gpu_assigned_clocks,
342 sizeof(gpu_assigned_clocks));
343 if (err && err != ENXIO)
344 printf("Failed to set assigned-clock-rates for GPU1: %s\n",
345 fdt_strerror(err));
346
347 err = change_property(blob, "/bus@54100000/imx8_gpu1_ss@80000000",
348 "operating-points", &gpu_opp_table,
349 sizeof(gpu_opp_table));
350 if (err && err != ENXIO)
351 printf("Failed to set operating-points for GPU: %s\n",
352 fdt_strerror(err));
353}
354
355static void update_fdt_cpu_industrial_frequencies(void *blob)
356{
357 int err;
358
359 err = delete_node(blob, "/opp-table-0/opp-1200000000");
360 if (err && err != -ENXIO)
361 printf("Failed to delete 1.2 GHz node on A53: %s\n",
362 fdt_strerror(err));
363
364 err = delete_node(blob, "/opp-table-1/opp-1596000000");
365 if (err && err != -ENXIO)
366 printf("Failed to delete 1.596 GHz node on A72: %s\n",
367 fdt_strerror(err));
368}
369
370static void update_fdt_frequencies(void *blob)
371{
372 struct cpu_info cpu;
373 struct udevice *dev;
374 int err;
375
376 uclass_first_device(UCLASS_CPU, &dev);
377
378 err = cpu_get_info(dev, &cpu);
379 if (err) {
380 printf("Failed to get CPU info\n");
381 return;
382 }
383
384 /*
385 * Differentiate between the automotive and industrial variants of the
386 * i.MX8. The difference of these two CPUs is the maximum frequencies
387 * for the CPU and GPU.
388 * Core Automotive [max. MHz] Industrial [max. MHz]
389 * A53 1200 1104
390 * A72 1596 1296
391 * GPU Core 800 625
392 * GPU Shader 1000 625
393 *
394 * While the SCFW enforces these limits for the CPU, the OS cpufreq
395 * driver remains unaware, causing a mismatch between reported and
396 * actual frequencies. This is resolved by removing the unsupprted
397 * frequencies from the device tree.
398 *
399 * The GPU frequencies are not enforced by the SCFW, therefore without
400 * updating the device tree we overclock the GPU.
401 *
402 * Using the cpu_freq variable is the only know way to differentiate
403 * between the automotive and industrial variants of the i.MX8.
404 */
405 if (cpu.cpu_freq != 1104000000)
406 return;
407
408 update_fdt_cpu_industrial_frequencies(blob);
409 update_fdt_gpu_industrial_frequencies(blob);
410}
411
Masahiro Yamadaf7ed78b2020-06-26 15:13:33 +0900412int ft_system_setup(void *blob, struct bd_info *bd)
Peng Fan47466162019-08-26 08:11:56 +0000413{
Peng Fan429c9ac2019-08-26 08:12:06 +0000414 int ret;
Peng Fanae617972020-05-05 20:28:37 +0800415 int off;
416
417 if (CONFIG_BOOTAUX_RESERVED_MEM_BASE) {
418 off = fdt_add_mem_rsv(blob, CONFIG_BOOTAUX_RESERVED_MEM_BASE,
419 CONFIG_BOOTAUX_RESERVED_MEM_SIZE);
420 if (off < 0)
421 printf("Failed to reserve memory for bootaux: %s\n",
422 fdt_strerror(off));
423 }
Peng Fan429c9ac2019-08-26 08:12:06 +0000424
Peng Fan47466162019-08-26 08:11:56 +0000425 update_fdt_with_owned_resources(blob);
426
Stefan Eichenberger00293b62024-12-11 13:18:55 +0100427 update_fdt_frequencies(blob);
428
Peng Fan429c9ac2019-08-26 08:12:06 +0000429 if (is_imx8qm()) {
430 ret = config_smmu_fdt(blob);
431 if (ret)
432 return ret;
433 }
434
Peng Fan064fe832019-08-26 08:12:13 +0000435 return ft_add_optee_node(blob, bd);
Peng Fan47466162019-08-26 08:11:56 +0000436}