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