[U-Boot,02/14] dm: Add Driver cores design document

Message ID 1344426150-1229-2-git-send-email-marex@denx.de
State Accepted
Headers show

Commit Message

Marek Vasut Aug. 8, 2012, 11:42 a.m.
From: Pavel Herrmann <morpheus.ibis@gmail.com>

Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
 doc/driver-model/UDM-cores.txt |  127 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)
 create mode 100644 doc/driver-model/UDM-cores.txt


Wolfgang Denk Sept. 2, 2012, 4 p.m. | #1
Dear Marek Vasut,

In message <1344426150-1229-2-git-send-email-marex@denx.de> you wrote:
> From: Pavel Herrmann <morpheus.ibis@gmail.com>
> Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
> ---
>  doc/driver-model/UDM-cores.txt |  127 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 127 insertions(+)
>  create mode 100644 doc/driver-model/UDM-cores.txt

Applied, thanks.

Best regards,

Wolfgang Denk


diff --git a/doc/driver-model/UDM-cores.txt b/doc/driver-model/UDM-cores.txt
new file mode 100644
index 0000000..56a417c
--- /dev/null
+++ b/doc/driver-model/UDM-cores.txt
@@ -0,0 +1,127 @@ 
+The U-Boot Driver Model Project
+Driver cores API document
+Pavel Herrmann <morpheus.ibis@gmail.com>
+1) Overview
+  Driver cores will be used as a wrapper for devices of the same type, and as
+  an abstraction for device driver APIs. For each driver API (which roughly
+  correspond to device types), there will be one driver core. Each driver core
+  will implement three APIs - a driver API (which will be the same as API of
+  drivers the core wraps around), a core API (which will be implemented by all
+  cores) and a command API (core-specific API which will be exposed to
+  commands).
+  A) Command API
+    The command API will provide access to shared functionality for a specific
+    device, which is currently located mostly in commands. Commands will be
+    rewritten to be more lightweight by using this API. As this API will be
+    different for each core, it is out of scope of this document.
+  B) Driver API
+    The driver API will act as a wrapper around actual device drivers,
+    providing a single entrypoint for device access. All functions in this API
+    have an instance* argument (probably called "this" or "i"), which will be
+    examined by the core, and a correct function for the specified driver will
+    get called.
+    If the core gets called with a group instance pointer (as discussed in
+    design), it will automatically select the instance that is associated
+    with this core, and use it as target of the call. if the group contains
+    multiple instances of a single type, the caller must explicitly use an
+    accessor to select the correct instance.
+    This accessor will look like:
+      struct instance *get_instance_from_group(struct instance *group, int i)
+    When called with a non-group instance, it will simply return the instance.
+  C) Core API
+    The core API will be implemented by all cores, and will provide
+    functionality for getting driver instances from non-driver code. This API
+    will consist of following functions:
+      int get_count(struct instance *core);
+      struct instance* get_instance(struct instance *core, int index);
+      int init(struct instance *core);
+      int bind(struct instance *core, struct instance *dev, void *ops,
+	       void *hint);
+      int unbind(struct instance *core, instance *dev);
+      int replace(struct instance *core, struct_instance *new_dev,
+		  struct instance *old_dev);
+      int destroy(struct instance *core);
+      int reloc(struct instance *new_core, struct instance *old_core);
+      The 'hint' parameter of bind() serves for additional data a driver can
+      pass to the core, to help it create the correct internal state for this
+      instance. the replace() function will get called during instance
+      relocation, and will replace the old instance with the new one, keeping
+      the internal state untouched.
+2) Lifetime of a driver core
+  Driver cores will be initialized at runtime, to limit memory footprint in
+  early-init stage, when we have to fit into ~1KB of memory. All active cores
+  will be stored in a tree structure (referenced as "Core tree") in global data,
+  which provides good tradeoff between size and access time.
+  Every core will have a number constant associated with it, which will be used
+  to find the instance in Core tree, and to refer to the core in all calls
+  working with the Core tree.
+  The Core Tree should be implemented using B-tree (or a similar structure)
+  to guarantee acceptable time overhead in all cases.
+  Code for working with the core (i2c in this example) follows:
+    core_init(CORE_I2C);
+      This will check whether we already have a i2c core, and if not it creates
+      a new instance and adds it into the Core tree. This will not be exported,
+      all code should depend on get_core_instance to init the core when
+      necessary.
+    get_core_instance(CORE_I2C);
+      This is an accessor into the Core tree, which will return the instance
+      of i2c core, creating it if necessary
+    core_bind(CORE_I2C, instance, driver_ops);
+      This will get called in bind() function of a driver, and will add the
+      instance into cores internal list of devices. If the core is not found, it
+      will get created.
+    driver_activate(instance *inst);
+      This call will recursively activate all devices necessary for using the
+      specified device. the code could be simplified as:
+        {
+        if (is_activated(inst))
+          return;
+        driver_activate(inst->bus);
+        get_driver(inst)->probe(inst);
+        }
+      The case with multiple parents will need to be handled here as well.
+      get_driver is an accessor to available drivers, which will get struct
+      driver based on a name in the instance.
+    i2c_write(instance *inst, ...);
+      An actual call to some method of the driver. This code will look like:
+        {
+        driver_activate(inst);
+        struct instance *core = get_core_instance(CORE_I2C);
+        device_ops = get_ops(inst);
+        device_ops->write(...);
+        }
+      get_ops will not be an exported function, it will be internal and specific
+      to the core, as it needs to know how are the ops stored, and what type
+      they are.
+  Please note that above examples represent the algorithm, not the actual code,
+  as they are missing checks for validity of return values.
+  core_init() function will get called the first time the core is requested,
+  either by core_link() or core_get_instance(). This way, the cores will get
+  created only when they are necessary, which will reduce our memory footprint.