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; 260 void *ufdt_blob; 261 int ret = -ENODEV; 262 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 } 304 305 if (fdt_check_header((void *)fdt_addr)) { 306 printf("Failed to get kernel dtb, ret=%d\n", ret); 307 return ret; 308 } 309 310 dtb_okay: 311 ufdt_blob = (void *)gd->fdt_blob; 312 gd->fdt_blob = (void *)fdt_addr; 313 314 hotkey_run(HK_FDT); 315 316 /* 317 * There is a phandle miss match between U-Boot and kernel dtb node, 318 * we fixup it in U-Boot live dt nodes. 319 * 320 * CRU: all nodes. 321 * GPIO: key nodes. 322 */ 323 phandles_fixup_cru((void *)gd->fdt_blob); 324 phandles_fixup_gpio((void *)gd->fdt_blob, (void *)ufdt_blob); 325 326 gd->flags |= GD_FLG_KDTB_READY; 327 of_live_build((void *)gd->fdt_blob, (struct device_node **)&gd->of_root); 328 dm_scan_fdt((void *)gd->fdt_blob, false); 329 330 /* 331 * There maybe something for the mmc devices to do after kernel dtb 332 * dm setup, eg: regain the clock device binding from kernel dtb. 333 */ 334 mmc_dm_reinit(); 335 336 /* Reserve 'reserved-memory' */ 337 ret = boot_fdt_add_sysmem_rsv_regions((void *)gd->fdt_blob); 338 if (ret) 339 return ret; 340 341 return 0; 342 } 343