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