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