[U-Boot,10/16] dm: board: Add documentation

Submitted by Simon Glass on March 19, 2017, 6:59 p.m.

Details

Message ID 20170319185935.20950-11-sjg@chromium.org
State New
Delegated to: Bin Meng
Headers show

Commit Message

Simon Glass March 19, 2017, 6:59 p.m.
Add some documentation for the new board init system. This describes how
it works and how to migrate to it.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 doc/driver-model/board-info.txt | 181 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 181 insertions(+)
 create mode 100644 doc/driver-model/board-info.txt

Patch hide | download patch | download mbox

diff --git a/doc/driver-model/board-info.txt b/doc/driver-model/board-info.txt
new file mode 100644
index 0000000000..86db096e28
--- /dev/null
+++ b/doc/driver-model/board-info.txt
@@ -0,0 +1,181 @@ 
+How to handle board init with Driver Model
+==========================================
+
+Motivation
+----------
+
+At present U-Boot has a lot of ad-hoc init functions related to boards, for
+board_early_init_f, board_misc_init_f() and dram_init().
+
+There are used in different ways by different boards as useful hooks to
+do the required init and sequence it correctly. Some functions are always
+enabled but have a __weak default. Some are controlled by the existence
+of a CONFIG.
+
+There are two main init sequences: board_init_f() (f for running from
+read-only flash) which runs before relocation and board_init_r() (r for
+relocated) which runs afterwards.
+
+One problem with the current sequence is that it has a lot of
+arch-specific #ifdefs around various functions. There are also #ifdefs
+for various features. With a driver-model based approach it should be
+possible to remove these over time since the board-specific code can move into
+drivers and does not need to be called from the init sequence.
+
+
+Design
+------
+
+A new uclass (UCLASS_BOARD) is defined. The uclass has one important method:
+phase(). This is called to handle a particular phase of board init. Phases are
+defined by enum board_phase_t. For example, the existing board_early_init_f()
+function is replaced by the BOARD_F_EARLY_INIT_F phase.
+
+When the init sequence wants to initiate the BOARD_F_EARLY_INIT_F phase it
+calls the phase() method of all the devices that implement that phase. It
+knows which ones implement it because they call board_support_phase() or
+board_support_phase_mask() in their probe method to register which phases they
+support.
+
+Multiple devices can implement the same phase. The init sequence calls these
+devices one by one. It is also possible for a device to 'claim' a phase, such
+that no further devices are called. The ordering of devices is as per the
+usual driver-model approach: the same order as the device tree nodes, or
+ordered by device name in the case of U_BOOT_DEVICE() declarations.
+
+With this approach a call to board_early_init_f() is replaced with a call to
+board_walk_opt_phase() which handles the phase but does not complain if no
+device handles it. For a mandatory phase, board_walk_phase() can be used.
+
+After starting up you can use the 'board phases' command to see what phases
+were executed:
+
+	=> board phases
+	1 arch_cpu_init_dm
+	0 board_early_init_f
+	1 checkcpu
+	0 misc_init_f
+	1 dram_init
+	1 reserve_arch
+
+This shows that four phases were executed and each had a single device which
+handled that phase.
+
+
+Example
+-------
+
+Below is an example with sandbox:
+
+
+static int sandbox_phase(struct udevice *dev, enum board_phase_t phase)
+{
+	struct sandbox_state *state = state_get_current();
+
+	switch (phase) {
+	case BOARD_F_DRAM_INIT:
+		gd->ram_size = state->ram_size;
+		return 0;
+	default:
+		return -ENOSYS;
+	}
+
+	return 0;
+}
+
+static int sandbox_board_probe(struct udevice *dev)
+{
+	return board_support_phase(dev, BOARD_F_DRAM_INIT);
+}
+
+static const struct board_ops sandbox_board_ops = {
+	.phase		= sandbox_phase,
+};
+
+/* Name this starting with underscore so it will be called last */
+U_BOOT_DRIVER(_sandbox_board_drv) = {
+	.name		= "sandbox_board",
+	.id		= UCLASS_BOARD,
+	.ops		= &sandbox_board_ops,
+	.probe		= sandbox_board_probe,
+	.flags		= DM_FLAG_PRE_RELOC,
+};
+
+U_BOOT_DEVICE(sandbox_board) = {
+	.name		= "sandbox_board",
+};
+
+
+This is a normal driver which supports the phase() method. The U_BOOT_DEVICE()
+declaration instantiates the device. It supports only one phase:
+BOARD_F_DRAM_INIT.
+
+If you look at common/board_f.c you will see how dram_init() uses this:
+
+int dram_init(void)
+{
+	return board_walk_phase(BOARD_F_DRAM_INIT);
+}
+
+That call will end up calling:
+
+	sandbox_phase(dev, BOARD_F_DRAM_INIT)
+
+There is quite a bit of boilerplate with the driver declaration. It would be
+quite easy to replace most of it with a macro, like:
+
+U_BOOT_BOARD_DRV(_sandbox_board, _sandbox_board_drv);
+
+but for now this is not attempted, to keep everything in the open.
+
+
+Porting suggestions
+-------------------
+
+You may find that you have all of your init handlers in a single file in your
+board directory. If so you can simply add a driver similar to the above that
+handles all the phases you use, and make your phase() method call the existing
+functions in that file. You should rename the functions to avoid confusion
+with the legacy method names and make them static since they will not be called
+from outside your file.
+
+If you have handlers in multiple files you can either:
+
+- Add a separate driver in each file
+- Use a single driver and have it call out to functions in other files for some
+    of the phases.
+
+Probably the first approach is better.
+
+You can perform the work in three patches:
+
+- one to add the board drivers and support
+- one to switch on CONFIG_BOARD_ENABLE
+- one to remove the now-unused legacy code
+
+This allows you to bisect easily if you find problems later.
+
+
+Required work
+-------------
+
+Before this feature can be applied to mainline it must support all phases. If
+we do not do thing up front then adding a new method may require porting all
+boards over to use that method. There is only a single control on whether or
+not to use this features (CONFIG_BOARD_ENABLE) so it is all or nothing.
+
+The post-relocation init phases therefore need to be added, and a review of
+pre-relocation phases needs to be completed to find anything that is missing.
+
+
+Future work
+-----------
+
+Apart from the required work, once all boards are ported to this framework we
+should be able to clean up the init sequences to remove all the #ifdefs.
+
+
+--
+Simon Glass
+sjg@chromium.org
+20-Mar-17