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