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