157be1825SJoseph Chen /*
257be1825SJoseph Chen * (C) Copyright 2019 Rockchip Electronics Co., Ltd
357be1825SJoseph Chen *
457be1825SJoseph Chen * SPDX-License-Identifier: GPL-2.0+
557be1825SJoseph Chen */
657be1825SJoseph Chen #include <common.h>
757be1825SJoseph Chen #include <boot_rkimg.h>
857be1825SJoseph Chen #include <dm.h>
957be1825SJoseph Chen #include <malloc.h>
1057be1825SJoseph Chen #include <of_live.h>
117bedd730SJoseph Chen #include <dm/device-internal.h>
1257be1825SJoseph Chen #include <dm/root.h>
1318b9d982SJoseph Chen #include <dm/uclass-internal.h>
1457be1825SJoseph Chen #include <asm/arch/hotkey.h>
1568f0b21bSJoseph Chen #include <asm/arch/resource_img.h>
1657be1825SJoseph Chen
1757be1825SJoseph Chen DECLARE_GLOBAL_DATA_PTR;
1857be1825SJoseph Chen
1918b9d982SJoseph Chen #ifdef CONFIG_USING_KERNEL_DTB_V2
dm_rm_kernel_dev(void)2018b9d982SJoseph Chen static int dm_rm_kernel_dev(void)
2118b9d982SJoseph Chen {
2218b9d982SJoseph Chen struct udevice *dev, *rec[10];
2318b9d982SJoseph Chen u32 uclass[] = { UCLASS_CRYPTO };
2418b9d982SJoseph Chen int i, j, k;
2518b9d982SJoseph Chen
2618b9d982SJoseph Chen for (i = 0, j = 0; i < ARRAY_SIZE(uclass); i++) {
2718b9d982SJoseph Chen for (uclass_find_first_device(uclass[i], &dev); dev;
2818b9d982SJoseph Chen uclass_find_next_device(&dev)) {
2918b9d982SJoseph Chen if (dev->flags & DM_FLAG_KNRL_DTB)
3018b9d982SJoseph Chen rec[j++] = dev;
3118b9d982SJoseph Chen }
3218b9d982SJoseph Chen
337bedd730SJoseph Chen for (k = 0; k < j; k++) {
347bedd730SJoseph Chen device_remove(rec[k], DM_REMOVE_NORMAL);
357bedd730SJoseph Chen device_unbind(rec[k]);
367bedd730SJoseph Chen }
3718b9d982SJoseph Chen }
3818b9d982SJoseph Chen
3918b9d982SJoseph Chen return 0;
4018b9d982SJoseph Chen }
4118b9d982SJoseph Chen
dm_rm_u_boot_dev(void)4218b9d982SJoseph Chen static int dm_rm_u_boot_dev(void)
4318b9d982SJoseph Chen {
4418b9d982SJoseph Chen struct udevice *dev, *rec[10];
4518b9d982SJoseph Chen u32 uclass[] = { UCLASS_ETH };
4618b9d982SJoseph Chen int del = 0;
4718b9d982SJoseph Chen int i, j, k;
4818b9d982SJoseph Chen
4918b9d982SJoseph Chen for (i = 0, j = 0; i < ARRAY_SIZE(uclass); i++) {
5018b9d982SJoseph Chen for (uclass_find_first_device(uclass[i], &dev); dev;
5118b9d982SJoseph Chen uclass_find_next_device(&dev)) {
5218b9d982SJoseph Chen if (dev->flags & DM_FLAG_KNRL_DTB)
5318b9d982SJoseph Chen del = 1;
5418b9d982SJoseph Chen else
5518b9d982SJoseph Chen rec[j++] = dev;
5618b9d982SJoseph Chen }
5718b9d982SJoseph Chen
5818b9d982SJoseph Chen /* remove u-boot dev if there is someone from kernel */
5918b9d982SJoseph Chen if (del) {
607bedd730SJoseph Chen for (k = 0; k < j; k++) {
617bedd730SJoseph Chen device_remove(rec[k], DM_REMOVE_NORMAL);
627bedd730SJoseph Chen device_unbind(rec[k]);
637bedd730SJoseph Chen }
6418b9d982SJoseph Chen }
6518b9d982SJoseph Chen }
6618b9d982SJoseph Chen
6718b9d982SJoseph Chen return 0;
6818b9d982SJoseph Chen }
6918b9d982SJoseph Chen
7018b9d982SJoseph Chen #else
7157be1825SJoseph Chen /* Here, only fixup cru phandle, pmucru is not included */
phandles_fixup_cru(const void * fdt)7257be1825SJoseph Chen static int phandles_fixup_cru(const void *fdt)
7357be1825SJoseph Chen {
744d85f76cSJoseph Chen const char *props[] = { "clocks", "assigned-clocks", "resets"};
7557be1825SJoseph Chen struct udevice *dev;
7657be1825SJoseph Chen struct uclass *uc;
7757be1825SJoseph Chen const char *comp;
7857be1825SJoseph Chen u32 id, nclocks;
7957be1825SJoseph Chen u32 *clocks;
8057be1825SJoseph Chen int phandle, ncells;
8157be1825SJoseph Chen int off, offset;
8257be1825SJoseph Chen int ret, length;
8357be1825SJoseph Chen int i, j;
8457be1825SJoseph Chen int first_phandle = -1;
8557be1825SJoseph Chen
8657be1825SJoseph Chen phandle = -ENODATA;
8757be1825SJoseph Chen ncells = -ENODATA;
8857be1825SJoseph Chen
8957be1825SJoseph Chen /* fdt points to kernel dtb, getting cru phandle and "#clock-cells" */
9057be1825SJoseph Chen for (offset = fdt_next_node(fdt, 0, NULL);
9157be1825SJoseph Chen offset >= 0;
9257be1825SJoseph Chen offset = fdt_next_node(fdt, offset, NULL)) {
9357be1825SJoseph Chen comp = fdt_getprop(fdt, offset, "compatible", NULL);
9457be1825SJoseph Chen if (!comp)
9557be1825SJoseph Chen continue;
9657be1825SJoseph Chen
9757be1825SJoseph Chen /* Actually, this is not a good method to get cru node */
9857be1825SJoseph Chen off = strlen(comp) - strlen("-cru");
9957be1825SJoseph Chen if (off > 0 && !strncmp(comp + off, "-cru", 4)) {
10057be1825SJoseph Chen phandle = fdt_get_phandle(fdt, offset);
10157be1825SJoseph Chen ncells = fdtdec_get_int(fdt, offset,
10257be1825SJoseph Chen "#clock-cells", -ENODATA);
10357be1825SJoseph Chen break;
10457be1825SJoseph Chen }
10557be1825SJoseph Chen }
10657be1825SJoseph Chen
10757be1825SJoseph Chen if (phandle == -ENODATA || ncells == -ENODATA)
10857be1825SJoseph Chen return 0;
10957be1825SJoseph Chen
11057be1825SJoseph Chen debug("%s: target cru: clock-cells:%d, phandle:0x%x\n",
11157be1825SJoseph Chen __func__, ncells, fdt32_to_cpu(phandle));
11257be1825SJoseph Chen
11357be1825SJoseph Chen /* Try to fixup all cru phandle from U-Boot dtb nodes */
11457be1825SJoseph Chen for (id = 0; id < UCLASS_COUNT; id++) {
11557be1825SJoseph Chen ret = uclass_get(id, &uc);
11657be1825SJoseph Chen if (ret)
11757be1825SJoseph Chen continue;
11857be1825SJoseph Chen
11957be1825SJoseph Chen if (list_empty(&uc->dev_head))
12057be1825SJoseph Chen continue;
12157be1825SJoseph Chen
12257be1825SJoseph Chen list_for_each_entry(dev, &uc->dev_head, uclass_node) {
12357be1825SJoseph Chen /* Only U-Boot node go further */
12457be1825SJoseph Chen if (!dev_read_bool(dev, "u-boot,dm-pre-reloc") &&
12557be1825SJoseph Chen !dev_read_bool(dev, "u-boot,dm-spl"))
12657be1825SJoseph Chen continue;
12757be1825SJoseph Chen
12857be1825SJoseph Chen for (i = 0; i < ARRAY_SIZE(props); i++) {
12957be1825SJoseph Chen if (!dev_read_prop(dev, props[i], &length))
13057be1825SJoseph Chen continue;
13157be1825SJoseph Chen
13257be1825SJoseph Chen clocks = malloc(length);
13357be1825SJoseph Chen if (!clocks)
13457be1825SJoseph Chen return -ENOMEM;
13557be1825SJoseph Chen
13657be1825SJoseph Chen /* Read "props[]" which contains cru phandle */
13757be1825SJoseph Chen nclocks = length / sizeof(u32);
13857be1825SJoseph Chen if (dev_read_u32_array(dev, props[i],
13957be1825SJoseph Chen clocks, nclocks)) {
14057be1825SJoseph Chen free(clocks);
14157be1825SJoseph Chen continue;
14257be1825SJoseph Chen }
14357be1825SJoseph Chen
14457be1825SJoseph Chen /* Fixup with kernel cru phandle */
14557be1825SJoseph Chen for (j = 0; j < nclocks; j += (ncells + 1)) {
14657be1825SJoseph Chen /*
14757be1825SJoseph Chen * Check: update pmucru phandle with cru
14857be1825SJoseph Chen * phandle by mistake.
14957be1825SJoseph Chen */
15057be1825SJoseph Chen if (first_phandle == -1)
15157be1825SJoseph Chen first_phandle = clocks[j];
15257be1825SJoseph Chen
15357be1825SJoseph Chen if (clocks[j] != first_phandle) {
15457be1825SJoseph Chen debug("WARN: %s: first cru phandle=%d, this=%d\n",
15557be1825SJoseph Chen dev_read_name(dev),
15657be1825SJoseph Chen first_phandle, clocks[j]);
15757be1825SJoseph Chen continue;
15857be1825SJoseph Chen }
15957be1825SJoseph Chen
16057be1825SJoseph Chen clocks[j] = phandle;
16157be1825SJoseph Chen }
16257be1825SJoseph Chen
16357be1825SJoseph Chen /*
16457be1825SJoseph Chen * Override live dt nodes but not fdt nodes,
16557be1825SJoseph Chen * because all U-Boot nodes has been imported
16657be1825SJoseph Chen * to live dt nodes, should use "dev_xxx()".
16757be1825SJoseph Chen */
16857be1825SJoseph Chen dev_write_u32_array(dev, props[i],
16957be1825SJoseph Chen clocks, nclocks);
17057be1825SJoseph Chen free(clocks);
17157be1825SJoseph Chen }
17257be1825SJoseph Chen }
17357be1825SJoseph Chen }
17457be1825SJoseph Chen
17557be1825SJoseph Chen return 0;
17657be1825SJoseph Chen }
17757be1825SJoseph Chen
phandles_fixup_gpio(const void * fdt,void * ufdt)17857be1825SJoseph Chen static int phandles_fixup_gpio(const void *fdt, void *ufdt)
17957be1825SJoseph Chen {
18057be1825SJoseph Chen struct udevice *dev;
18157be1825SJoseph Chen struct uclass *uc;
18257be1825SJoseph Chen const char *prop = "gpios";
18357be1825SJoseph Chen const char *comp;
18457be1825SJoseph Chen char *gpio_name[10];
18557be1825SJoseph Chen int gpio_off[10];
18657be1825SJoseph Chen int pinctrl;
18757be1825SJoseph Chen int offset;
18857be1825SJoseph Chen int i = 0;
18957be1825SJoseph Chen int n = 0;
19057be1825SJoseph Chen
19157be1825SJoseph Chen pinctrl = fdt_path_offset(fdt, "/pinctrl");
19257be1825SJoseph Chen if (pinctrl < 0)
19357be1825SJoseph Chen return 0;
19457be1825SJoseph Chen
19557be1825SJoseph Chen memset(gpio_name, 0, sizeof(gpio_name));
19657be1825SJoseph Chen for (offset = fdt_first_subnode(fdt, pinctrl);
19757be1825SJoseph Chen offset >= 0;
19857be1825SJoseph Chen offset = fdt_next_subnode(fdt, offset)) {
19957be1825SJoseph Chen /* assume the font nodes are gpio node */
20057be1825SJoseph Chen if (++i >= ARRAY_SIZE(gpio_name))
20157be1825SJoseph Chen break;
20257be1825SJoseph Chen
20357be1825SJoseph Chen comp = fdt_getprop(fdt, offset, "compatible", NULL);
20457be1825SJoseph Chen if (!comp)
20557be1825SJoseph Chen continue;
20657be1825SJoseph Chen
20757be1825SJoseph Chen if (!strcmp(comp, "rockchip,gpio-bank")) {
20857be1825SJoseph Chen gpio_name[n] = (char *)fdt_get_name(fdt, offset, NULL);
20957be1825SJoseph Chen gpio_off[n] = offset;
21057be1825SJoseph Chen n++;
21157be1825SJoseph Chen }
21257be1825SJoseph Chen }
21357be1825SJoseph Chen
21457be1825SJoseph Chen if (!gpio_name[0])
21557be1825SJoseph Chen return 0;
21657be1825SJoseph Chen
21757be1825SJoseph Chen if (uclass_get(UCLASS_KEY, &uc) || list_empty(&uc->dev_head))
21857be1825SJoseph Chen return 0;
21957be1825SJoseph Chen
22057be1825SJoseph Chen list_for_each_entry(dev, &uc->dev_head, uclass_node) {
22157be1825SJoseph Chen u32 new_phd, phd_old;
22257be1825SJoseph Chen char *name;
22357be1825SJoseph Chen ofnode ofn;
22457be1825SJoseph Chen
22557be1825SJoseph Chen if (!dev_read_bool(dev, "u-boot,dm-pre-reloc") &&
22657be1825SJoseph Chen !dev_read_bool(dev, "u-boot,dm-spl"))
22757be1825SJoseph Chen continue;
22857be1825SJoseph Chen
22957be1825SJoseph Chen if (dev_read_u32_array(dev, prop, &phd_old, 1))
23057be1825SJoseph Chen continue;
23157be1825SJoseph Chen
23257be1825SJoseph Chen ofn = ofnode_get_by_phandle(phd_old);
23357be1825SJoseph Chen if (!ofnode_valid(ofn))
23457be1825SJoseph Chen continue;
23557be1825SJoseph Chen
23657be1825SJoseph Chen name = (char *)ofnode_get_name(ofn);
23757be1825SJoseph Chen if (!name)
23857be1825SJoseph Chen continue;
23957be1825SJoseph Chen
2402a931879SJoseph Chen for (i = 0; i < ARRAY_SIZE(gpio_name); i++) {
24157be1825SJoseph Chen if (gpio_name[i] && !strcmp(name, gpio_name[i])) {
24257be1825SJoseph Chen new_phd = fdt_get_phandle(fdt, gpio_off[i]);
24357be1825SJoseph Chen dev_write_u32_array(dev, prop, &new_phd, 1);
24457be1825SJoseph Chen break;
24557be1825SJoseph Chen }
24657be1825SJoseph Chen }
24757be1825SJoseph Chen }
24857be1825SJoseph Chen
24957be1825SJoseph Chen return 0;
25057be1825SJoseph Chen }
25195eb462eSJoseph Chen #endif
25257be1825SJoseph Chen
board_mmc_dm_reinit(struct udevice * dev)25357be1825SJoseph Chen __weak int board_mmc_dm_reinit(struct udevice *dev)
25457be1825SJoseph Chen {
25557be1825SJoseph Chen return 0;
25657be1825SJoseph Chen }
25757be1825SJoseph Chen
mmc_dm_reinit(void)25857be1825SJoseph Chen static int mmc_dm_reinit(void)
25957be1825SJoseph Chen {
26057be1825SJoseph Chen struct udevice *dev;
26157be1825SJoseph Chen struct uclass *uc;
26257be1825SJoseph Chen int ret;
26357be1825SJoseph Chen
26457be1825SJoseph Chen if (uclass_get(UCLASS_MMC, &uc) || list_empty(&uc->dev_head))
26557be1825SJoseph Chen return 0;
26657be1825SJoseph Chen
26757be1825SJoseph Chen list_for_each_entry(dev, &uc->dev_head, uclass_node) {
26857be1825SJoseph Chen ret = board_mmc_dm_reinit(dev);
26957be1825SJoseph Chen if (ret)
27057be1825SJoseph Chen return ret;
27157be1825SJoseph Chen }
27257be1825SJoseph Chen
27357be1825SJoseph Chen return 0;
27457be1825SJoseph Chen }
27557be1825SJoseph Chen
27674ec585eSJoseph Chen /* Check by property: "/compatible" */
dtb_check_ok(void * kfdt,void * ufdt)27774ec585eSJoseph Chen static int dtb_check_ok(void *kfdt, void *ufdt)
27820647277SJoseph Chen {
27920647277SJoseph Chen const char *compat;
28074ec585eSJoseph Chen int index;
28120647277SJoseph Chen
28284cb34e7SJoseph Chen /* TODO */
28384cb34e7SJoseph Chen return 1;
28484cb34e7SJoseph Chen
28574ec585eSJoseph Chen for (index = 0;
28674ec585eSJoseph Chen compat = fdt_stringlist_get(ufdt, 0, "compatible",
28774ec585eSJoseph Chen index, NULL), compat;
28874ec585eSJoseph Chen index++) {
28974ec585eSJoseph Chen debug("u-compat: %s\n", compat);
29074ec585eSJoseph Chen if (!fdt_node_check_compatible(kfdt, 0, compat))
2918f98eecbSJoseph Chen return 1;
29274ec585eSJoseph Chen }
29320647277SJoseph Chen
29420647277SJoseph Chen return 0;
29520647277SJoseph Chen }
29620647277SJoseph Chen
init_kernel_dtb(void)29757be1825SJoseph Chen int init_kernel_dtb(void)
29857be1825SJoseph Chen {
29995eb462eSJoseph Chen #ifndef CONFIG_USING_KERNEL_DTB_V2
30095eb462eSJoseph Chen void *ufdt_blob = (void *)gd->fdt_blob;
30195eb462eSJoseph Chen #endif
302a4fb9a7eSJoseph Chen ulong fdt_addr = 0;
30303781805SJoseph Chen int ret = -ENODEV;
30457be1825SJoseph Chen
30518b9d982SJoseph Chen printf("DM: v%d\n", IS_ENABLED(CONFIG_USING_KERNEL_DTB_V2) ? 2 : 1);
30618b9d982SJoseph Chen
307a4fb9a7eSJoseph Chen /*
308a4fb9a7eSJoseph Chen * If memory size <= 128MB, we firstly try to get "fdt_addr1_r".
309a4fb9a7eSJoseph Chen */
310a4fb9a7eSJoseph Chen if (gd->ram_size <= SZ_128M)
311a4fb9a7eSJoseph Chen fdt_addr = env_get_ulong("fdt_addr1_r", 16, 0);
312a4fb9a7eSJoseph Chen
313a4fb9a7eSJoseph Chen if (!fdt_addr)
31457be1825SJoseph Chen fdt_addr = env_get_ulong("fdt_addr_r", 16, 0);
31557be1825SJoseph Chen if (!fdt_addr) {
31657be1825SJoseph Chen printf("No Found FDT Load Address.\n");
31720647277SJoseph Chen return -ENODEV;
31857be1825SJoseph Chen }
31957be1825SJoseph Chen
320*2f5c1c77SJoseph Chen #ifdef CONFIG_EMBED_KERNEL_DTB_ALWAYS
32103781805SJoseph Chen printf("Always embed kernel dtb\n");
32203781805SJoseph Chen goto dtb_embed;
323*2f5c1c77SJoseph Chen #endif
32457be1825SJoseph Chen ret = rockchip_read_dtb_file((void *)fdt_addr);
32520647277SJoseph Chen if (!ret) {
32620647277SJoseph Chen if (!dtb_check_ok((void *)fdt_addr, (void *)gd->fdt_blob)) {
32720647277SJoseph Chen ret = -EINVAL;
32820647277SJoseph Chen printf("Kernel dtb mismatch this platform!\n");
32920647277SJoseph Chen } else {
33020647277SJoseph Chen goto dtb_okay;
33120647277SJoseph Chen }
33220647277SJoseph Chen }
33320647277SJoseph Chen
334*2f5c1c77SJoseph Chen #ifdef CONFIG_EMBED_KERNEL_DTB
335*2f5c1c77SJoseph Chen #ifdef CONFIG_EMBED_KERNEL_DTB_ALWAYS
33603781805SJoseph Chen dtb_embed:
337*2f5c1c77SJoseph Chen #endif
3383fc8bcd2SJoseph Chen if (gd->fdt_blob_kern) {
33920647277SJoseph Chen if (!dtb_check_ok((void *)gd->fdt_blob_kern, (void *)gd->fdt_blob)) {
34020647277SJoseph Chen printf("Embedded kernel dtb mismatch this platform!\n");
34120647277SJoseph Chen return -EINVAL;
34220647277SJoseph Chen }
34320647277SJoseph Chen
34457be1825SJoseph Chen fdt_addr = (ulong)memalign(ARCH_DMA_MINALIGN,
34557be1825SJoseph Chen fdt_totalsize(gd->fdt_blob_kern));
34657be1825SJoseph Chen if (!fdt_addr)
34757be1825SJoseph Chen return -ENOMEM;
34857be1825SJoseph Chen
34903781805SJoseph Chen /*
35003781805SJoseph Chen * Alloc another space for this embed kernel dtb.
35103781805SJoseph Chen * Because "fdt_addr_r" *MUST* be the fdt passed to kernel.
35203781805SJoseph Chen */
35357be1825SJoseph Chen memcpy((void *)fdt_addr, gd->fdt_blob_kern,
35457be1825SJoseph Chen fdt_totalsize(gd->fdt_blob_kern));
35503781805SJoseph Chen printf("DTB: %s\n", CONFIG_EMBED_KERNEL_DTB_PATH);
356*2f5c1c77SJoseph Chen } else
357*2f5c1c77SJoseph Chen #endif
358*2f5c1c77SJoseph Chen {
35957be1825SJoseph Chen printf("Failed to get kernel dtb, ret=%d\n", ret);
3607952efb8SJoseph Chen return -ENOENT;
36157be1825SJoseph Chen }
36257be1825SJoseph Chen
36320647277SJoseph Chen dtb_okay:
36457be1825SJoseph Chen gd->fdt_blob = (void *)fdt_addr;
36557be1825SJoseph Chen hotkey_run(HK_FDT);
36657be1825SJoseph Chen
36795eb462eSJoseph Chen #ifndef CONFIG_USING_KERNEL_DTB_V2
36857be1825SJoseph Chen /*
36957be1825SJoseph Chen * There is a phandle miss match between U-Boot and kernel dtb node,
37057be1825SJoseph Chen * we fixup it in U-Boot live dt nodes.
37157be1825SJoseph Chen *
37257be1825SJoseph Chen * CRU: all nodes.
37357be1825SJoseph Chen * GPIO: key nodes.
37457be1825SJoseph Chen */
37557be1825SJoseph Chen phandles_fixup_cru((void *)gd->fdt_blob);
37657be1825SJoseph Chen phandles_fixup_gpio((void *)gd->fdt_blob, (void *)ufdt_blob);
37795eb462eSJoseph Chen #endif
37857be1825SJoseph Chen
379a85a3b31SJoseph Chen gd->flags |= GD_FLG_KDTB_READY;
380d276a5eaSJoseph Chen gd->of_root_f = gd->of_root;
38157be1825SJoseph Chen of_live_build((void *)gd->fdt_blob, (struct device_node **)&gd->of_root);
38257be1825SJoseph Chen dm_scan_fdt((void *)gd->fdt_blob, false);
38357be1825SJoseph Chen
38418b9d982SJoseph Chen #ifdef CONFIG_USING_KERNEL_DTB_V2
38518b9d982SJoseph Chen dm_rm_kernel_dev();
38618b9d982SJoseph Chen dm_rm_u_boot_dev();
38718b9d982SJoseph Chen #endif
38857be1825SJoseph Chen /*
38957be1825SJoseph Chen * There maybe something for the mmc devices to do after kernel dtb
39057be1825SJoseph Chen * dm setup, eg: regain the clock device binding from kernel dtb.
39157be1825SJoseph Chen */
39257be1825SJoseph Chen mmc_dm_reinit();
39357be1825SJoseph Chen
39457be1825SJoseph Chen /* Reserve 'reserved-memory' */
39557be1825SJoseph Chen ret = boot_fdt_add_sysmem_rsv_regions((void *)gd->fdt_blob);
39657be1825SJoseph Chen if (ret)
39757be1825SJoseph Chen return ret;
39857be1825SJoseph Chen
39957be1825SJoseph Chen return 0;
40057be1825SJoseph Chen }
40195eb462eSJoseph Chen
402