[U-Boot] [PATCH 04/16] dm: board: Add a uclass for init functions
Simon Glass
sjg at chromium.org
Sun Mar 19 18:59:23 UTC 2017
Add a uclass to handle board init. This allows drivers to be provided to
perform the various phases of init. Functions are provided to call all
devices that can handle a particular phase.
Signed-off-by: Simon Glass <sjg at chromium.org>
---
common/Kconfig | 31 +++++++
common/init/Makefile | 1 +
common/init/board-uclass.c | 108 ++++++++++++++++++++++++
include/asm-generic/global_data.h | 5 ++
include/board.h | 170 ++++++++++++++++++++++++++++++++++++++
include/dm/uclass-id.h | 1 +
6 files changed, 316 insertions(+)
create mode 100644 common/init/board-uclass.c
create mode 100644 include/board.h
diff --git a/common/Kconfig b/common/Kconfig
index 8f73c8f757..7d2bd15371 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -395,6 +395,36 @@ config DISPLAY_BOARDINFO
menu "Start-up hooks"
+config BOARD
+ bool "Support using driver model for board start-up hooks, etc."
+ help
+ This adds support for the board uclass and associated functions. With
+ this you can create and use BOARD drivers. However unless
+ BOARD_ENABLE is also set, the existing init sequence will continue
+ to be used. This is designed to ease migration of boards, since
+ support for both methods can be provided during the transition
+ period.
+
+config SPL_BOARD
+ bool "Support using driver model for board start-up hooks, etc. in SPL"
+ help
+ This adds support for the board uclass and associated functions in
+ SPL. With this you can create and use BOARD drivers. However unless
+ BOARD_ENABLE is also set, the existing init sequence will continue
+ to be used. This is designed to ease migration of boards, since
+ support for both methods can be provided during the transition
+ period.
+
+config BOARD_ENABLE
+ bool "Use driver model instead of ad-hoc board init functions"
+ depends on BOARD
+ help
+ This replaces the ad-hoc start-up functions like board_early_init_f()
+ with a driver-model-based interface. With this enabled, boards
+ provide one or more drivers which implement these phases of init as
+ well as access to the board decription. Existing init functions are
+ no-longer called.
+
config ARCH_EARLY_INIT_R
bool "Call arch-specific init soon after relocation"
default y if X86
@@ -414,6 +444,7 @@ config ARCH_MISC_INIT
config BOARD_EARLY_INIT_F
bool "Call board-specific init before relocation"
+ depends on !BOARD_ENABLE
default y if X86
help
Some boards need to perform initialisation as soon as possible
diff --git a/common/init/Makefile b/common/init/Makefile
index 4902635f53..923ce8e139 100644
--- a/common/init/Makefile
+++ b/common/init/Makefile
@@ -5,3 +5,4 @@
#
obj-y += board_init.o
+obj-$(CONFIG_$(SPL_)BOARD) += board-uclass.o
diff --git a/common/init/board-uclass.c b/common/init/board-uclass.c
new file mode 100644
index 0000000000..2ca65f44da
--- /dev/null
+++ b/common/init/board-uclass.c
@@ -0,0 +1,108 @@
+/*
+ * Board driver interface
+ *
+ * Copyright (c) 2017 Google, Inc
+ * Written by Simon Glass <sjg at chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <board.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int board_phase(struct udevice *dev, enum board_phase_t phase)
+{
+ struct board_uc_priv *priv = dev_get_uclass_priv(dev);
+ struct board_ops *ops = board_get_ops(dev);
+
+ if (!ops->phase)
+ return -ENOSYS;
+ if (!priv->phase_mask && phase == BOARD_PHASE_FIRST) {
+ printf("Device '%s' supports no phases\n", dev->name);
+ return -EINVAL;
+ }
+ if (!(priv->phase_mask & board_phase_mask(phase)))
+ return -ENOSYS;
+
+ return ops->phase(dev, phase);
+}
+
+int board_walk_phase_count(enum board_phase_t phase, bool verbose)
+{
+ struct udevice *dev;
+ int count = 0;
+ int ret;
+
+ for (ret = uclass_first_device(UCLASS_BOARD, &dev);
+ dev;
+ uclass_next_device(&dev)) {
+ ret = board_phase(dev, phase);
+ if (ret == 0) {
+ count++;
+ } else if (ret == BOARD_PHASE_CLAIMED) {
+ count++;
+ debug("Phase %d claimed by '%s'\n", phase, dev->name);
+ break;
+ } else if (ret != -ENOSYS) {
+ gd->phase_count[phase] += count;
+ return ret;
+ }
+ }
+
+ if (!count) {
+ if (verbose)
+ printf("Unable to find driver for phase %d\n", phase);
+ return -ENOSYS;
+ }
+ gd->phase_count[phase] += count;
+
+ return count;
+}
+
+int board_walk_phase(enum board_phase_t phase)
+{
+ int ret;
+
+ ret = board_walk_phase_count(phase, true);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int board_walk_opt_phase(enum board_phase_t phase)
+{
+ int ret;
+
+ ret = board_walk_phase_count(phase, false);
+ if (ret < 0 && ret != -ENOSYS)
+ return ret;
+
+ return 0;
+}
+
+int board_support_phase(struct udevice *dev, enum board_phase_t phase)
+{
+ struct board_uc_priv *priv = dev_get_uclass_priv(dev);
+
+ priv->phase_mask |= board_phase_mask(phase);
+
+ return 0;
+}
+
+int board_support_phase_mask(struct udevice *dev, ulong phase_mask)
+{
+ struct board_uc_priv *priv = dev_get_uclass_priv(dev);
+
+ priv->phase_mask = phase_mask;
+
+ return 0;
+}
+
+UCLASS_DRIVER(board) = {
+ .id = UCLASS_BOARD,
+ .name = "board",
+ .per_device_auto_alloc_size = sizeof(struct board_uc_priv),
+};
diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h
index 5b356dd231..fea1e916ed 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -21,6 +21,7 @@
*/
#ifndef __ASSEMBLY__
+#include <board.h>
#include <membuff.h>
#include <linux/list.h>
@@ -107,6 +108,10 @@ typedef struct global_data {
ulong video_top; /* Top of video frame buffer area */
ulong video_bottom; /* Bottom of video frame buffer area */
#endif
+#ifdef CONFIG_BOARD
+ /* number of drivers which handled each phase */
+ uint8_t phase_count[BOARD_PHASE_COUNT];
+#endif
} gd_t;
#endif
diff --git a/include/board.h b/include/board.h
new file mode 100644
index 0000000000..0975f5ac12
--- /dev/null
+++ b/include/board.h
@@ -0,0 +1,170 @@
+/*
+ * Board driver interface
+ *
+ * Copyright (c) 2017 Google, Inc
+ * Written by Simon Glass <sjg at chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __BOARD_H
+#define __BOARD_H
+
+/* Include dm.h here because board.h comes before dm.h in include order */
+#include <dm.h>
+
+/*
+ * This file provides access to board drivers, which are responsible for
+ * initing the board as well as (in future) querying its state.
+ */
+
+/* Phases of init - please update phase_name also */
+enum board_phase_t {
+ /*
+ * Pre-relocation phases.
+ * TODO(sjg at chromium.org): At present these are named the same as the
+ * functions they replace to avoid confusion. However this naming is
+ * not very consistent. At some point once enough boards uses this
+ * interface, we could rename some of these.
+ *
+ * TODO(sjg at chromium.org): arch_cpu_init() and mach_cpu_init() are
+ * called before driver model is ready so we cannot yet add them to
+ * this interface. Hopefully this can be adjusted later:
+ * BOARD_F_ARCH_CPU_INIT,
+ * BOARD_F_MACH_CPU_INIT,
+ */
+ BOARD_PHASE_FIRST,
+ BOARD_F_ARCH_CPU_INIT_DM = BOARD_PHASE_FIRST,
+ BOARD_F_EARLY_INIT_F,
+ BOARD_F_CHECKCPU,
+ BOARD_F_MISC_INIT_F,
+ BOARD_F_DRAM_INIT,
+ BOARD_F_RESERVE_ARCH,
+
+ /*
+ * Post-relocation phases go here:
+ *
+ * BOARD_R_...
+ */
+
+ BOARD_PHASE_TEST, /* For sandbox testing */
+ BOARD_PHASE_COUNT,
+ BOARD_PHASE_INVALID, /* For sandbox testing */
+};
+
+static inline ulong board_phase_mask(enum board_phase_t phase)
+{
+ return 1UL << (ulong)phase;
+}
+
+/*
+ * Return this from phase() to indicate that no more devices should handle this
+ * phase
+ */
+#define BOARD_PHASE_CLAIMED EUSERS
+
+/* Operations for the board driver */
+struct board_ops {
+ /**
+ * phase() - Execute a phase of board init
+ *
+ * @dev: Device to use
+ * @phase: Phase to execute
+ * @return 0 if done, -ENOSYS if not supported (which is often fine),
+ BOARD_PHASE_CLAIMED if this was handled and that processing
+ of this phase should stop (i.e. do not send it to other
+ devices), other error if something went wrong
+ */
+ int (*phase)(struct udevice *dev, enum board_phase_t phase);
+
+ /**
+ * get_desc() - Get a description string for a board
+ *
+ * @dev: Device to check (UCLASS_BOARD)
+ * @buf: Buffer to place string
+ * @size: Size of string space
+ * @return 0 if OK, -ENOSPC if buffer is too small, other -ve on error
+ */
+ int (*get_desc)(struct udevice *dev, char *buf, int size);
+};
+
+#define board_get_ops(dev) ((struct board_ops *)(dev)->driver->ops)
+
+/* Private uclass information about each device */
+struct board_uc_priv {
+ ulong phase_mask; /* Mask of phases supported by this device */
+};
+
+/**
+ * board_phase() - Execute a phase of board init on a device
+ *
+ * @phase: Phase to execute
+ * @return 0 if done, -ENOSYS if not supported by this device, other error if
+ * something went wrong
+ */
+int board_phase(struct udevice *dev, enum board_phase_t phase);
+
+/**
+ * board_walk_phase() - Execute a phase of board init
+ *
+ * This works through the available board devices asking each one to perform
+ * the requested init phase. The process continues until there are no more
+ * board devices.
+ *
+ * If no board device provides the phase, this function returns -ENOSYS.
+ *
+ * @phase: Phase to execute
+ * @return 0 if done, -ENOSYS if not supported, other error if something went
+ * wrong
+ */
+int board_walk_phase(enum board_phase_t phase);
+
+/**
+ * board_opt_walk_phase() - Execute an optional phase of board init
+ *
+ * This works through the available board devices asking each one to perform
+ * the requested init phase. The process continues until there are no more
+ * board devices.
+ *
+ * If no board device provides the phase, this function returns 0.
+ *
+ * @phase: Phase to execute
+ * @return 0 if done, other error if something went wrong
+ */
+int board_walk_opt_phase(enum board_phase_t phase);
+
+/**
+ * board_walk_phase_count() - Execute an optional phase of board init
+ *
+ * This works through the available board devices asking each one to perform
+ * the requested init phase. The process continues until there are no more
+ * board devices.
+ *
+ * If no board provides the phase, this function returns -ENOSYS.
+ *
+ * @phase: Phase to execute
+ * @return number of devices which handled this phase if done, -ve error if
+ * something went wrong
+ */
+int board_walk_phase_count(enum board_phase_t phase, bool verbose);
+
+/**
+ * board_support_phase() - Mark a board device as supporting the given phase
+ *
+ * @dev: Board device
+ * @phase: Phase to execute
+ * @return 0
+ */
+int board_support_phase(struct udevice *dev, enum board_phase_t phase);
+
+/**
+ * board_support_phase_mask() - Mark a board device as supporting given phases
+ *
+ * @dev: Board device
+ * @phase_mask: Mask of phases to execute, built up by ORing board_phase_mask()
+ * values together
+ * @return 0
+ */
+int board_support_phase_mask(struct udevice *dev, ulong phase_mask);
+
+#endif
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 8c92d0b030..166194fead 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -27,6 +27,7 @@ enum uclass_id {
/* U-Boot uclasses start here - in alphabetical order */
UCLASS_ADC, /* Analog-to-digital converter */
UCLASS_AHCI, /* SATA disk controller */
+ UCLASS_BOARD, /* Board-specific driver */
UCLASS_BLK, /* Block device */
UCLASS_CLK, /* Clock source, e.g. used by peripherals */
UCLASS_CPU, /* CPU, typically part of an SoC */
--
2.12.0.367.g23dc2f6d3c-goog
More information about the U-Boot
mailing list