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