diff --git a/doc/README.fdt-control b/doc/README.fdt-control index d6ab7bf5706fce70928ddcdf8c4cfd02bbc736d0..446401c9e99c4547d8069dfbd9c3678a6e19f7dd 100644 --- a/doc/README.fdt-control +++ b/doc/README.fdt-control @@ -184,6 +184,24 @@ The full device tree is available to U-Boot proper, but normally only a subset 'SPL Support' in doc/driver-model/README.txt for more details. +Using several DTBs in the SPL (CONFIG_SPL_MULTI_DTB) +---------------------------------------------------- +In some rare cases it is desirable to let SPL be able to select one DTB among +many. This usually not very useful as the DTB for the SPL is small and usually +fits several platforms. However the DTB sometimes include information that do +work on several platforms (like IO tuning parameters). +In this case it is possible to use CONFIG_SPL_MULTI_DTB. This option appends to +the SPL a FIT image containing several DTBs listed in SPL_OF_LIST. +board_fit_config_name_match() is called to select the right DTB. + +If board_fit_config_name_match() relies on DM (DM driver to access an EEPROM +containing the board ID for example), it possible to start with a generic DTB +and then switch over to the right DTB after the detection. For this purpose, +the platform code must call fdtdec_resetup(). Based on the returned flag, the +platform may have to re-initiliaze the DM subusystem using dm_uninit() and +dm_init_and_scan(). + + Limitations ----------- diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index dffd6b2602674efbb1cfce64268de01041c0521d..78dcf40bff486841d7c34a2f49b4979806940f0c 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -77,6 +77,10 @@ typedef struct global_data { #ifdef CONFIG_OF_LIVE struct device_node *of_root; #endif + +#if CONFIG_IS_ENABLED(MULTI_DTB_FIT) + const void *multi_dtb_fit; /* uncompressed multi-dtb FIT image */ +#endif struct jt_funcs *jt; /* jump table */ char env_buf[32]; /* buffer for env_get() before reloc. */ #ifdef CONFIG_TRACE diff --git a/include/fdtdec.h b/include/fdtdec.h index b15da00fb28ccc8477edd90a1c174f6194a8cef9..f1bcbf837ffbe6e92b354c4f7ff5bc21c4017625 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -951,6 +951,27 @@ int fdtdec_setup_memory_banksize(void); */ int fdtdec_setup(void); +#if CONFIG_IS_ENABLED(MULTI_DTB_FIT) +/** + * fdtdec_resetup() - Set up the device tree again + * + * The main difference with fdtdec_setup() is that it returns if the fdt has + * changed because a better match has been found. + * This is typically used for boards that rely on a DM driver to detect the + * board type. This function sould be called by the board code after the stuff + * needed by board_fit_config_name_match() to operate porperly is available. + * If this functions signals that a rescan is necessary, the board code must + * unbind all the drivers using dm_uninit() and then rescan the DT with + * dm_init_and_scan(). + * + * @param rescan Returns a flag indicating that fdt has changed and rescanning + * the fdt is required + * + * @return 0 if OK, -ve on error + */ +int fdtdec_resetup(int *rescan); +#endif + /** * Board-specific FDT initialization. Returns the address to a device tree blob. * Called when CONFIG_OF_BOARD is defined, or if CONFIG_OF_SEPARATE is defined diff --git a/lib/fdtdec.c b/lib/fdtdec.c index cbdc0778258bb33305033528e7d13c1c3268a407..7bbc6d445e440397e23f6b2ef7269d3db091ae5a 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -1275,14 +1275,55 @@ int fdtdec_setup(void) * If so, pick the most relevant */ fdt_blob = locate_dtb_in_fit(gd->fdt_blob); - if (fdt_blob) + if (fdt_blob) { + gd->multi_dtb_fit = gd->fdt_blob; gd->fdt_blob = fdt_blob; + } + # endif #endif return fdtdec_prepare_fdt(); } +#if CONFIG_IS_ENABLED(MULTI_DTB_FIT) +int fdtdec_resetup(int *rescan) +{ + void *fdt_blob; + + /* + * If the current DTB is part of a compressed FIT image, + * try to locate the best match from the uncompressed + * FIT image stillpresent there. Save the time and space + * required to uncompress it again. + */ + if (gd->multi_dtb_fit) { + fdt_blob = locate_dtb_in_fit(gd->multi_dtb_fit); + + if (fdt_blob == gd->fdt_blob) { + /* + * The best match did not change. no need to tear down + * the DM and rescan the fdt. + */ + *rescan = 0; + return 0; + } + + *rescan = 1; + gd->fdt_blob = fdt_blob; + return fdtdec_prepare_fdt(); + } + + /* + * If multi_dtb_fit is NULL, it means that blob appended to u-boot is + * not a FIT image containings DTB, but a single DTB. There is no need + * to teard down DM and rescan the DT in this case. + */ + *rescan = 0; + return 0; +} +#endif + #ifdef CONFIG_NR_DRAM_BANKS int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id, phys_addr_t *basep, phys_size_t *sizep, bd_t *bd)