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 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 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 */ 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 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 253 __weak int board_mmc_dm_reinit(struct udevice *dev) 254 { 255 return 0; 256 } 257 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" */ 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 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 if (IS_ENABLED(CONFIG_EMBED_KERNEL_DTB_ALWAYS)) { 321 resource_init_list(); 322 printf("Always embed kernel dtb\n"); 323 goto dtb_embed; 324 } 325 326 ret = rockchip_read_dtb_file((void *)fdt_addr); 327 if (!ret) { 328 if (!dtb_check_ok((void *)fdt_addr, (void *)gd->fdt_blob)) { 329 ret = -EINVAL; 330 printf("Kernel dtb mismatch this platform!\n"); 331 } else { 332 goto dtb_okay; 333 } 334 } 335 336 dtb_embed: 337 if (gd->fdt_blob_kern) { 338 if (!dtb_check_ok((void *)gd->fdt_blob_kern, (void *)gd->fdt_blob)) { 339 printf("Embedded kernel dtb mismatch this platform!\n"); 340 return -EINVAL; 341 } 342 343 fdt_addr = (ulong)memalign(ARCH_DMA_MINALIGN, 344 fdt_totalsize(gd->fdt_blob_kern)); 345 if (!fdt_addr) 346 return -ENOMEM; 347 348 /* 349 * Alloc another space for this embed kernel dtb. 350 * Because "fdt_addr_r" *MUST* be the fdt passed to kernel. 351 */ 352 memcpy((void *)fdt_addr, gd->fdt_blob_kern, 353 fdt_totalsize(gd->fdt_blob_kern)); 354 printf("DTB: %s\n", CONFIG_EMBED_KERNEL_DTB_PATH); 355 } else { 356 printf("Failed to get kernel dtb, ret=%d\n", ret); 357 return -ENOENT; 358 } 359 360 dtb_okay: 361 gd->fdt_blob = (void *)fdt_addr; 362 hotkey_run(HK_FDT); 363 364 #ifndef CONFIG_USING_KERNEL_DTB_V2 365 /* 366 * There is a phandle miss match between U-Boot and kernel dtb node, 367 * we fixup it in U-Boot live dt nodes. 368 * 369 * CRU: all nodes. 370 * GPIO: key nodes. 371 */ 372 phandles_fixup_cru((void *)gd->fdt_blob); 373 phandles_fixup_gpio((void *)gd->fdt_blob, (void *)ufdt_blob); 374 #endif 375 376 gd->flags |= GD_FLG_KDTB_READY; 377 gd->of_root_f = gd->of_root; 378 of_live_build((void *)gd->fdt_blob, (struct device_node **)&gd->of_root); 379 dm_scan_fdt((void *)gd->fdt_blob, false); 380 381 #ifdef CONFIG_USING_KERNEL_DTB_V2 382 dm_rm_kernel_dev(); 383 dm_rm_u_boot_dev(); 384 #endif 385 /* 386 * There maybe something for the mmc devices to do after kernel dtb 387 * dm setup, eg: regain the clock device binding from kernel dtb. 388 */ 389 mmc_dm_reinit(); 390 391 /* Reserve 'reserved-memory' */ 392 ret = boot_fdt_add_sysmem_rsv_regions((void *)gd->fdt_blob); 393 if (ret) 394 return ret; 395 396 return 0; 397 } 398 399