[U-Boot] [PATCH 21/51] common: Extend board-specific DT fixup

Mario Six mario.six at gdsys.cc
Fri Jul 14 12:55:07 UTC 2017


Commit 2a79275 ("dm: Add callback to modify the device tree")
implemented a board-specific callback that can modify U-Boot's device
tree before relocation to accomodate a range of hardware variations of
certain boards.

However, this approach only turns out to be useful when the copy of the
device tree before relocation is actually writeable. If e.g. a device
boots out of a NOR flash, this scheme does not work, since the
modification of the flash's contents is not possible, and the unmodified
device tree is relocated and used by U-Boot.

To circumvent this problem, we split the modification process into two
phases.

In phase one we only collect information about the board by querying the
hardware (reading GPIO values, probing I2C chips, etc.), and store the
obtained information in a special, board-specific structure that is part
of the global data structure. This phase runs prior to relocation, and
utilizes the pre-relocation DM to query hardware information.

In phase two, we read the information back from the structure, and do
the actual manipulation of the device tree. This phase occurs *after*
the relocation of the U-Boot image, but before the driver model is
initialized from the device tree. Since the device tree lives in RAM
alongside the U-Boot image after relocation, the tree is definitely
writeable at this point.

Each phase is implemented by a board-specific callback function: phase
one by board_fix_fdt_get_info, which takes no arguments, and phase two
by board_fix_fdt_change, which takes the writeable device tree blob as
its sole argument. Since the structure where the gathered hardware
information is stored is necessarily board-dependent, we create a
include file where the boards utilizing the functionality can define
their individually needed structures.

This commit also adapts the controlcenterdc board's fixup function to
the new scheme.

Signed-off-by: Mario Six <mario.six at gdsys.cc>
---

 board/gdsys/a38x/controlcenterdc.c |  20 +++++-
 common/board_f.c                   |   6 +-
 common/board_r.c                   |  10 +++
 doc/driver-model/fdt-fixup.txt     | 129 ++++++++++++++++++++++++-------------
 include/asm-generic/global_data.h  |   7 ++
 include/common.h                   |   3 +-
 include/fdt_fixup.h                |   5 ++
 7 files changed, 130 insertions(+), 50 deletions(-)
 create mode 100644 include/fdt_fixup.h

diff --git a/board/gdsys/a38x/controlcenterdc.c b/board/gdsys/a38x/controlcenterdc.c
index 32168d3576..05e18bac4c 100644
--- a/board/gdsys/a38x/controlcenterdc.c
+++ b/board/gdsys/a38x/controlcenterdc.c
@@ -236,7 +236,7 @@ int board_late_init(void)
 	return 0;
 }
 
-int board_fix_fdt(void *rw_fdt_blob)
+int board_fix_fdt_get_info(void)
 {
 	struct udevice *bus = NULL;
 	uint k;
@@ -254,7 +254,23 @@ int board_fix_fdt(void *rw_fdt_blob)
 		snprintf(name, 64,
 			 "/soc/internal-regs/i2c at 11000/pca9698@%02x", k);
 
-		if (!dm_i2c_simple_probe(bus, k))
+		gd->board_fixup_data.chip_exists[k - 0x21] = dm_i2c_simple_probe(bus, k);
+	}
+
+	return 0;
+}
+
+int board_fix_fdt_change(void *rw_fdt_blob)
+{
+	struct of_board_fixup_data data = gd->board_fixup_data;
+	uint k;
+	char name[64];
+
+	for (k = 0x21; k <= 0x26; k++) {
+		snprintf(name, 64,
+			 "/soc/internal-regs/i2c at 11000/pca9698@%02x", k);
+
+		if (!data.chip_exists[k - 0x21])
 			fdt_disable_by_ofname(rw_fdt_blob, name);
 	}
 
diff --git a/common/board_f.c b/common/board_f.c
index b258a1a73c..b835bd22bc 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -660,9 +660,9 @@ static int setup_reloc(void)
 }
 
 #ifdef CONFIG_OF_BOARD_FIXUP
-static int fix_fdt(void)
+static int fix_fdt_get_info(void)
 {
-	return board_fix_fdt((void *)gd->fdt_blob);
+	return board_fix_fdt_get_info();
 }
 #endif
 
@@ -887,7 +887,7 @@ static const init_fnc_t init_sequence_f[] = {
 #endif
 	display_new_sp,
 #ifdef CONFIG_OF_BOARD_FIXUP
-	fix_fdt,
+	fix_fdt_get_info,
 #endif
 	INIT_FUNC_WATCHDOG_RESET
 	reloc_fdt,
diff --git a/common/board_r.c b/common/board_r.c
index 199cadbed1..25f27f0843 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -694,6 +694,13 @@ static int run_main_loop(void)
 	return 0;
 }
 
+#ifdef CONFIG_OF_BOARD_FIXUP
+static int fix_fdt_change(void)
+{
+	return board_fix_fdt_change((void *)gd->fdt_blob);
+}
+#endif
+
 /*
  * Over time we hope to remove these functions with code fragments and
  * stub funtcions, and instead call the relevant function directly.
@@ -731,6 +738,9 @@ static init_fnc_t init_sequence_r[] = {
 #ifdef CONFIG_OF_LIVE
 	initr_of_live,
 #endif
+#ifdef CONFIG_OF_BOARD_FIXUP
+	fix_fdt_change,
+#endif
 #ifdef CONFIG_DM
 	initr_dm,
 #endif
diff --git a/doc/driver-model/fdt-fixup.txt b/doc/driver-model/fdt-fixup.txt
index 70344bd2c3..9a9467edd8 100644
--- a/doc/driver-model/fdt-fixup.txt
+++ b/doc/driver-model/fdt-fixup.txt
@@ -52,81 +52,122 @@ tree (at least after the relocation) would greatly simplify the solution of
 this problem, it is a non-negligible task to implement it, an a interim
 solution is needed to address the problem at least in the medium-term.
 
-Hence, we propose a solution to this problem by offering a board-specific
-call-back function, which is passed a writeable pointer to the device tree.
-This function is called before the device tree is relocated, and specifically
-before the main U-Boot's driver model is instantiated, hence the main U-Boot
-"sees" all modifications to the device tree made in this function. Furthermore,
-we have the pre-relocation driver model at our disposal at this stage, which
-means that we can query the hardware for the existence and variety of the
-components easily.
+Hence, we propose a solution to this problem by offering a set of two board-specific
+call-back functions:
+
+ * One function which is called prior to relocation that gathers information
+   about the device using the pre-relocation DM instance and stores the
+   collected information in a special data structure within the global data
+   section; using the pre-relocation driver model means that we can query the
+   hardware for the existence and variety of the components easily.
+ * A second function which is called just after relocation, but before the
+   post-relocation DM is instantiated; it is passed a writeable pointer to the
+   device tree, so that it can exectute the actual modifications of the device
+   tree based upon the information gathered by the first function.
+
+Notice that since the "modification function" is called prior to the DM
+instantiation, the main U-Boot "sees" all modifications to the device tree made
+by the function.
 
 2. Implementation
 -----------------
 
 To take advantage of the pre-relocation device tree manipulation mechanism,
-boards have to implement the function board_fix_fdt, which has the following
-signature:
+boards have to implement the functions board_fix_fdt_get_info and
+board_fix_fdt_change, which have the following signatures:
 
-int board_fix_fdt (void *rw_fdt_blob)
+int board_fix_fdt_get_info (void)
+int board_fix_fdt_change (void *rw_fdt_blob)
 
-The passed-in void pointer is a writeable pointer to the device tree, which can
-be used to manipulate the device tree using e.g. functions from
-include/fdt_support.h. The return value should either be 0 in case of
-successful execution of the device tree manipulation or something else for a
+The void pointer passed to board_fix_fdt_change is a writeable pointer to the
+device tree, which can be used to manipulate the device tree using e.g.
+functions from include/fdt_support.h. The return value for both functions
+should either be 0 in case of successful execution of the device information
+gathering and device tree manipulation respectively or something else for a
 failure. Note that returning a non-null value from the function will
 unrecoverably halt the boot process, as with any function from init_sequence_f
-(in common/board_f.c).
+(in common/board_f.c) or init_sequence_r (in common/board_r.c).  .
 
-Furthermore, the Kconfig option OF_BOARD_FIXUP has to be set for the function
+Furthermore, the Kconfig option OF_BOARD_FIXUP has to be set for the functions
 to be called:
 
 Device Tree Control
 -> [*] Board-specific manipulation of Device Tree
 
-+----------------------------------------------------------+
-| WARNING: The actual manipulation of the device tree has  |
-| to be the _last_ set of operations in board_fix_fdt!     |
-| Since the pre-relocation driver model does not adapt to  |
-| changes made to the device tree either, its references   |
-| into the device tree will be invalid after manipulating  |
-| it, and unpredictable behavior might occur when          |
-| functions that rely on them are executed!                |
-+----------------------------------------------------------+
+The transfer of the collected device information is accomplished via the
+board_fixup_data sub-structure in the global data structure. The layout of this
+structure is board-defined, and every user of the mechanism should define a
+suitable set of fields in include/fdt_fixup.h surrounded by preprocessor #ifdef
+instructions. For example, a board with the target Kconfig variable
+CONFIG_TARGET_SAMPLEBOARD that needs to check the existence of two I2C devices
+and has to determine a board variant identified by an int, might add the
+following member declarations for itself:
 
-Hence, the recommended layout of the board_fixup_fdt call-back function is the
-following:
+#ifdef CONFIG_TARGET_SAMPLEBOARD
+    bool chip1_present;
+    bool chip2_present;
+    int variant;
+#endif
 
-int board_fix_fdt(void *rw_fdt_blob)
+A schemetic implementation of the two functions might then look like the following:
+
+int board_fix_fdt_get_info(void)
 {
 	/* Collect information about device's hardware and store them in e.g.
-	   local variables */
+       local variables, e.g. chip1_present, chip2_present, variant. Return a
+       non-zero value if the information gathering failed. */
+
+    /* Store the collected information in the sub-structure, so they will be
+       available to board_fix_fdt_change: */
 
-	/* Do device tree manipulation using the values previously collected */
+    gd->board_fixup_data.chip1_present = chip1_present;
+    gd->board_fixup_data.chip2_present = chip2_present;
+    gd->board_fixup_data.variant = variant;
 
-	/* Return 0 on successful manipulation and non-zero otherwise */
+    return 0;
 }
 
-If this convention is kept, both an "additive" approach, meaning that nodes for
-detected components are added to the device tree, as well as a "subtractive"
-approach, meaning that nodes for absent components are removed from the tree,
-as well as a combination of both approaches should work.
+int board_fix_fdt_change(void *rw_fdt_blob)
+{
+    /* Read back the information from the sub-structure: */
+
+    bool chip1_present = gd->board_fixup_data.chip1_present;
+    bool chip2_present = gd->board_fixup_data.chip2_present;
+    int variant = gd->board_fixup_data.variant;
+
+    /* Employ device tree manipulation based on the information: */
+
+    if (chip1_present) {
+        ...
+    }
+
+    if (chip2_present) {
+        ...
+    }
+
+    switch(variant)
+        ...
+    }
+}
+
+This convention of split functions also enables that both an "additive"
+approach for DT manipulation, meaning that nodes for detected components are
+added to the device tree, as well as a "subtractive" approach, meaning that
+nodes for absent components are removed from the tree, as well as a combination
+of both approaches should work.
 
 3. Example
 ----------
 
-The controlcenterdc board (board/gdsys/a38x/controlcenterdc.c) features a
-board_fix_fdt function, in which six GPIO expanders (which might be present or
+The controlcenterdc board (board/gdsys/a38x/controlcenterdc.c) features
+board_fix_fdt functions, in which six GPIO expanders (which might be present or
 not, since they are on daughter boards) on a I2C bus are queried for, and
 subsequently deactivated in the device tree if they are not present.
 
-Note that the dm_i2c_simple_probe function does not use the device tree, hence
-it is safe to call it after the tree has already been manipulated.
-
 4. Work to be done
 ------------------
 
-* The application of device tree overlay should be possible in board_fixup_fdt,
-  but has not been tested at this stage.
+* The application of device tree overlay should be possible in
+  board_fixup_fdt_change, but has not been tested at this stage.
 
-2017-01-06, Mario Six <mario.six at gdsys.cc>
+2017-07-04, Mario Six <mario.six at gdsys.cc>
diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h
index fb90be9d3e..731a4163a9 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -24,6 +24,10 @@
 #include <membuff.h>
 #include <linux/list.h>
 
+#ifdef CONFIG_OF_BOARD_FIXUP
+#include <fdt_fixup.h>
+#endif
+
 typedef struct global_data {
 	bd_t *bd;
 	unsigned long flags;
@@ -75,6 +79,9 @@ typedef struct global_data {
 #ifdef CONFIG_OF_LIVE
 	struct device_node *of_root;
 #endif
+#ifdef CONFIG_OF_BOARD_FIXUP
+	struct of_board_fixup_data board_fixup_data;
+#endif
 	struct jt_funcs *jt;		/* jump table */
 	char env_buf[32];		/* buffer for getenv() before reloc. */
 #ifdef CONFIG_TRACE
diff --git a/include/common.h b/include/common.h
index 751665f8a4..80a004d4b4 100644
--- a/include/common.h
+++ b/include/common.h
@@ -416,7 +416,8 @@ extern ssize_t spi_write (uchar *, int, uchar *, int);
 
 /* $(BOARD)/$(BOARD).c */
 int board_early_init_f (void);
-int board_fix_fdt (void *rw_fdt_blob); /* manipulate the U-Boot fdt before its relocation */
+int board_fix_fdt_get_info (void); /* manipulate the U-Boot fdt before its relocation */
+int board_fix_fdt_change (void *rw_fdt_blob); /* manipulate the U-Boot fdt before its relocation */
 int board_late_init (void);
 int board_postclk_init (void); /* after clocks/timebase, before env/serial */
 int board_early_init_r (void);
diff --git a/include/fdt_fixup.h b/include/fdt_fixup.h
new file mode 100644
index 0000000000..9390f0a633
--- /dev/null
+++ b/include/fdt_fixup.h
@@ -0,0 +1,5 @@
+struct of_board_fixup_data {
+#ifdef CONFIG_TARGET_CONTROLCENTERDC
+	bool chip_exists[6];
+#endif
+};
-- 
2.11.0



More information about the U-Boot mailing list