xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/kernel_dtb.c (revision e12b5efd268e1005d4dd24711b7aeefc0edf34bb)
1 /*
2  * (C) Copyright 2019 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6 #include <common.h>
7 #include <boot_rkimg.h>
8 #include <dm.h>
9 #include <malloc.h>
10 #include <of_live.h>
11 #include <dm/device-internal.h>
12 #include <dm/root.h>
13 #include <dm/uclass-internal.h>
14 #include <asm/arch/hotkey.h>
15 
16 DECLARE_GLOBAL_DATA_PTR;
17 
18 #ifdef CONFIG_USING_KERNEL_DTB_V2
19 static int dm_rm_kernel_dev(void)
20 {
21 	struct udevice *dev, *rec[10];
22 	u32 uclass[] = { UCLASS_CRYPTO };
23 	int i, j, k;
24 
25 	for (i = 0, j = 0; i < ARRAY_SIZE(uclass); i++) {
26 		for (uclass_find_first_device(uclass[i], &dev); dev;
27 		     uclass_find_next_device(&dev)) {
28 			if (dev->flags & DM_FLAG_KNRL_DTB)
29 				rec[j++] = dev;
30 		}
31 
32 		for (k = 0; k < j; k++) {
33 			device_remove(rec[k], DM_REMOVE_NORMAL);
34 			device_unbind(rec[k]);
35 		}
36 	}
37 
38 	return 0;
39 }
40 
41 static int dm_rm_u_boot_dev(void)
42 {
43 	struct udevice *dev, *rec[10];
44 	u32 uclass[] = { UCLASS_ETH };
45 	int del = 0;
46 	int i, j, k;
47 
48 	for (i = 0, j = 0; i < ARRAY_SIZE(uclass); i++) {
49 		for (uclass_find_first_device(uclass[i], &dev); dev;
50 		     uclass_find_next_device(&dev)) {
51 			if (dev->flags & DM_FLAG_KNRL_DTB)
52 				del = 1;
53 			else
54 				rec[j++] = dev;
55 		}
56 
57 		/* remove u-boot dev if there is someone from kernel */
58 		if (del) {
59 			for (k = 0; k < j; k++) {
60 				device_remove(rec[k], DM_REMOVE_NORMAL);
61 				device_unbind(rec[k]);
62 			}
63 		}
64 	}
65 
66 	return 0;
67 }
68 
69 #else
70 /* Here, only fixup cru phandle, pmucru is not included */
71 static int phandles_fixup_cru(const void *fdt)
72 {
73 	const char *props[] = { "clocks", "assigned-clocks", "resets"};
74 	struct udevice *dev;
75 	struct uclass *uc;
76 	const char *comp;
77 	u32 id, nclocks;
78 	u32 *clocks;
79 	int phandle, ncells;
80 	int off, offset;
81 	int ret, length;
82 	int i, j;
83 	int first_phandle = -1;
84 
85 	phandle = -ENODATA;
86 	ncells = -ENODATA;
87 
88 	/* fdt points to kernel dtb, getting cru phandle and "#clock-cells" */
89 	for (offset = fdt_next_node(fdt, 0, NULL);
90 	     offset >= 0;
91 	     offset = fdt_next_node(fdt, offset, NULL)) {
92 		comp = fdt_getprop(fdt, offset, "compatible", NULL);
93 		if (!comp)
94 			continue;
95 
96 		/* Actually, this is not a good method to get cru node */
97 		off = strlen(comp) - strlen("-cru");
98 		if (off > 0 && !strncmp(comp + off, "-cru", 4)) {
99 			phandle = fdt_get_phandle(fdt, offset);
100 			ncells = fdtdec_get_int(fdt, offset,
101 						"#clock-cells", -ENODATA);
102 			break;
103 		}
104 	}
105 
106 	if (phandle == -ENODATA || ncells == -ENODATA)
107 		return 0;
108 
109 	debug("%s: target cru: clock-cells:%d, phandle:0x%x\n",
110 	      __func__, ncells, fdt32_to_cpu(phandle));
111 
112 	/* Try to fixup all cru phandle from U-Boot dtb nodes */
113 	for (id = 0; id < UCLASS_COUNT; id++) {
114 		ret = uclass_get(id, &uc);
115 		if (ret)
116 			continue;
117 
118 		if (list_empty(&uc->dev_head))
119 			continue;
120 
121 		list_for_each_entry(dev, &uc->dev_head, uclass_node) {
122 			/* Only U-Boot node go further */
123 			if (!dev_read_bool(dev, "u-boot,dm-pre-reloc") &&
124 			    !dev_read_bool(dev, "u-boot,dm-spl"))
125 				continue;
126 
127 			for (i = 0; i < ARRAY_SIZE(props); i++) {
128 				if (!dev_read_prop(dev, props[i], &length))
129 					continue;
130 
131 				clocks = malloc(length);
132 				if (!clocks)
133 					return -ENOMEM;
134 
135 				/* Read "props[]" which contains cru phandle */
136 				nclocks = length / sizeof(u32);
137 				if (dev_read_u32_array(dev, props[i],
138 						       clocks, nclocks)) {
139 					free(clocks);
140 					continue;
141 				}
142 
143 				/* Fixup with kernel cru phandle */
144 				for (j = 0; j < nclocks; j += (ncells + 1)) {
145 					/*
146 					 * Check: update pmucru phandle with cru
147 					 * phandle by mistake.
148 					 */
149 					if (first_phandle == -1)
150 						first_phandle = clocks[j];
151 
152 					if (clocks[j] != first_phandle) {
153 						debug("WARN: %s: first cru phandle=%d, this=%d\n",
154 						      dev_read_name(dev),
155 						      first_phandle, clocks[j]);
156 						continue;
157 					}
158 
159 					clocks[j] = phandle;
160 				}
161 
162 				/*
163 				 * Override live dt nodes but not fdt nodes,
164 				 * because all U-Boot nodes has been imported
165 				 * to live dt nodes, should use "dev_xxx()".
166 				 */
167 				dev_write_u32_array(dev, props[i],
168 						    clocks, nclocks);
169 				free(clocks);
170 			}
171 		}
172 	}
173 
174 	return 0;
175 }
176 
177 static int phandles_fixup_gpio(const void *fdt, void *ufdt)
178 {
179 	struct udevice *dev;
180 	struct uclass *uc;
181 	const char *prop = "gpios";
182 	const char *comp;
183 	char *gpio_name[10];
184 	int gpio_off[10];
185 	int pinctrl;
186 	int offset;
187 	int i = 0;
188 	int n = 0;
189 
190 	pinctrl = fdt_path_offset(fdt, "/pinctrl");
191 	if (pinctrl < 0)
192 		return 0;
193 
194 	memset(gpio_name, 0, sizeof(gpio_name));
195 	for (offset = fdt_first_subnode(fdt, pinctrl);
196 	     offset >= 0;
197 	     offset = fdt_next_subnode(fdt, offset)) {
198 		/* assume the font nodes are gpio node */
199 		if (++i >= ARRAY_SIZE(gpio_name))
200 			break;
201 
202 		comp = fdt_getprop(fdt, offset, "compatible", NULL);
203 		if (!comp)
204 			continue;
205 
206 		if (!strcmp(comp, "rockchip,gpio-bank")) {
207 			gpio_name[n] = (char *)fdt_get_name(fdt, offset, NULL);
208 			gpio_off[n]  = offset;
209 			n++;
210 		}
211 	}
212 
213 	if (!gpio_name[0])
214 		return 0;
215 
216 	if (uclass_get(UCLASS_KEY, &uc) || list_empty(&uc->dev_head))
217 		return 0;
218 
219 	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
220 		u32 new_phd, phd_old;
221 		char *name;
222 		ofnode ofn;
223 
224 		if (!dev_read_bool(dev, "u-boot,dm-pre-reloc") &&
225 		    !dev_read_bool(dev, "u-boot,dm-spl"))
226 			continue;
227 
228 		if (dev_read_u32_array(dev, prop, &phd_old, 1))
229 			continue;
230 
231 		ofn = ofnode_get_by_phandle(phd_old);
232 		if (!ofnode_valid(ofn))
233 			continue;
234 
235 		name = (char *)ofnode_get_name(ofn);
236 		if (!name)
237 			continue;
238 
239 		for (i = 0; i < ARRAY_SIZE(gpio_name); i++) {
240 			if (gpio_name[i] && !strcmp(name, gpio_name[i])) {
241 				new_phd = fdt_get_phandle(fdt, gpio_off[i]);
242 				dev_write_u32_array(dev, prop, &new_phd, 1);
243 				break;
244 			}
245 		}
246 	}
247 
248 	return 0;
249 }
250 #endif
251 
252 __weak int board_mmc_dm_reinit(struct udevice *dev)
253 {
254 	return 0;
255 }
256 
257 static int mmc_dm_reinit(void)
258 {
259 	struct udevice *dev;
260 	struct uclass *uc;
261 	int ret;
262 
263 	if (uclass_get(UCLASS_MMC, &uc) || list_empty(&uc->dev_head))
264 		return 0;
265 
266 	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
267 		ret = board_mmc_dm_reinit(dev);
268 		if (ret)
269 			return ret;
270 	}
271 
272 	return 0;
273 }
274 
275 /* Check by property: "/compatible" */
276 static int dtb_check_ok(void *kfdt, void *ufdt)
277 {
278 	const char *compat;
279 	int index;
280 
281 	/* TODO */
282 	return 1;
283 
284 	for (index = 0;
285 	     compat = fdt_stringlist_get(ufdt, 0, "compatible",
286 					 index, NULL), compat;
287 	     index++) {
288 		debug("u-compat: %s\n", compat);
289 		if (!fdt_node_check_compatible(kfdt, 0, compat))
290 			return 1;
291 	}
292 
293 	return 0;
294 }
295 
296 int init_kernel_dtb(void)
297 {
298 #ifndef CONFIG_USING_KERNEL_DTB_V2
299 	void *ufdt_blob = (void *)gd->fdt_blob;
300 #endif
301 	ulong fdt_addr = 0;
302 	int ret = -ENODEV;
303 
304 	printf("DM: v%d\n", IS_ENABLED(CONFIG_USING_KERNEL_DTB_V2) ? 2 : 1);
305 
306 	/*
307 	 * If memory size <= 128MB, we firstly try to get "fdt_addr1_r".
308 	 */
309 	if (gd->ram_size <= SZ_128M)
310 		fdt_addr = env_get_ulong("fdt_addr1_r", 16, 0);
311 
312 	if (!fdt_addr)
313 		fdt_addr = env_get_ulong("fdt_addr_r", 16, 0);
314 	if (!fdt_addr) {
315 		printf("No Found FDT Load Address.\n");
316 		return -ENODEV;
317 	}
318 
319 	if (IS_ENABLED(CONFIG_EMBED_KERNEL_DTB_ALWAYS)) {
320 		printf("Always embed kernel dtb\n");
321 		goto dtb_embed;
322 	}
323 
324 	ret = rockchip_read_dtb_file((void *)fdt_addr);
325 	if (!ret) {
326 		if (!dtb_check_ok((void *)fdt_addr, (void *)gd->fdt_blob)) {
327 			ret = -EINVAL;
328 			printf("Kernel dtb mismatch this platform!\n");
329 		} else {
330 			goto dtb_okay;
331 		}
332 	}
333 
334 dtb_embed:
335 	if (gd->fdt_blob_kern) {
336 		if (!dtb_check_ok((void *)gd->fdt_blob_kern, (void *)gd->fdt_blob)) {
337 			printf("Embedded kernel dtb mismatch this platform!\n");
338 			return -EINVAL;
339 		}
340 
341 		fdt_addr = (ulong)memalign(ARCH_DMA_MINALIGN,
342 				fdt_totalsize(gd->fdt_blob_kern));
343 		if (!fdt_addr)
344 			return -ENOMEM;
345 
346 		/*
347 		 * Alloc another space for this embed kernel dtb.
348 		 * Because "fdt_addr_r" *MUST* be the fdt passed to kernel.
349 		 */
350 		memcpy((void *)fdt_addr, gd->fdt_blob_kern,
351 		       fdt_totalsize(gd->fdt_blob_kern));
352 		printf("DTB: %s\n", CONFIG_EMBED_KERNEL_DTB_PATH);
353 	} else {
354 		printf("Failed to get kernel dtb, ret=%d\n", ret);
355 		return -ENOENT;
356 	}
357 
358 dtb_okay:
359 	gd->fdt_blob = (void *)fdt_addr;
360 	hotkey_run(HK_FDT);
361 
362 #ifndef CONFIG_USING_KERNEL_DTB_V2
363 	/*
364 	 * There is a phandle miss match between U-Boot and kernel dtb node,
365 	 * we fixup it in U-Boot live dt nodes.
366 	 *
367 	 * CRU:	 all nodes.
368 	 * GPIO: key nodes.
369 	 */
370 	phandles_fixup_cru((void *)gd->fdt_blob);
371 	phandles_fixup_gpio((void *)gd->fdt_blob, (void *)ufdt_blob);
372 #endif
373 
374 	gd->flags |= GD_FLG_KDTB_READY;
375 	gd->of_root_f = gd->of_root;
376 	of_live_build((void *)gd->fdt_blob, (struct device_node **)&gd->of_root);
377 	dm_scan_fdt((void *)gd->fdt_blob, false);
378 
379 #ifdef CONFIG_USING_KERNEL_DTB_V2
380 	dm_rm_kernel_dev();
381 	dm_rm_u_boot_dev();
382 #endif
383 	/*
384 	 * There maybe something for the mmc devices to do after kernel dtb
385 	 * dm setup, eg: regain the clock device binding from kernel dtb.
386 	 */
387 	mmc_dm_reinit();
388 
389 	/* Reserve 'reserved-memory' */
390 	ret = boot_fdt_add_sysmem_rsv_regions((void *)gd->fdt_blob);
391 	if (ret)
392 		return ret;
393 
394 	return 0;
395 }
396 
397