diff mbox

[RFC,2/2] drivers/base: declare phandle DT nodes as components

Message ID 9f8bbe28b00160cab2cedffa3f8fb42121035964.1391793068.git.moinejf@free.fr (mailing list archive)
State New, archived
Headers show

Commit Message

Jean-Francois Moine Feb. 7, 2014, 4:53 p.m. UTC
At system startup time, some devices depends on the availability of
some other devices before starting. The infrastructure for componentised
subsystems permits to handle this dependence, each driver defining
its own role.

This patch does an automatic creation of the lowest components in
case of DT. This permits simple devices to be part of complex
componentised subsystems without any specific code.

When created from DT, the device dependence is generally indicated by
phandle: a device which is pointed to by a phandle must be started
before the pointing device(s).

So, at device register time, the devices which are pointed to by a
phandle are declared as components, except when they declared
themselves as such in their probe function.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/base/component.c | 12 ++++++++++++
 drivers/base/core.c      | 18 ++++++++++++++++++
 include/linux/of.h       |  2 ++
 3 files changed, 32 insertions(+)

Comments

Russell King - ARM Linux Feb. 7, 2014, 5:43 p.m. UTC | #1
On Fri, Feb 07, 2014 at 05:53:27PM +0100, Jean-Francois Moine wrote:
> At system startup time, some devices depends on the availability of
> some other devices before starting. The infrastructure for componentised
> subsystems permits to handle this dependence, each driver defining
> its own role.
> 
> This patch does an automatic creation of the lowest components in
> case of DT. This permits simple devices to be part of complex
> componentised subsystems without any specific code.

A component with no operations makes precisely no sense - with that,
there's no way for the component to be a stand-alone driver.  Your
approach forces your ideas onto every DT device that is referenced
as a phandle.  That's extremely restrictive.

I don't want the component stuff knowing anything about OF.  I don't
want it knowing about driver matching.  I don't want it knowing about
ACPI either.  That's the whole point behind it - it is 100% agnostic
about how that stuff works.

The model is quite simply this:

- a master device is the covering component for the "card"
  - the master device knows what components to expect by some means.
    In the case of DT, that's by phandle references to the components.
  - the master device handles the component independent setup of the
    "card", creating the common resources that are required.  When it's
    ready, it asks for the components to be bound.
  - upon removal of any component, the master component is unbound,
    which triggers the removal of the "card" from the subsystem.
  - as part of the removal, sub-components are unbound.
  - the master device should have as /little/ knowledge about the
    components as possible to permit component re-use.

- a component driver should be independent of it's master.
  - A component which is probed from the device model should simply
    register itself using component_add() with an appropriate operations
    structure, and a removal function which deletes itself.
  - When the driver is ready to be initialised (when the "card" level
    resources have been set in place) the "bind" method will be called.
    At this point, the component does everything that a classical driver
    model driver would do in it's probe callback.
  - unbind is the same as remove in the classical driver model.

So, please, no DT stuff in the component support - it's simply not
required.
diff mbox

Patch

diff --git a/drivers/base/component.c b/drivers/base/component.c
index 0a39d7a..3cab26b 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -17,6 +17,7 @@ 
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 struct master {
 	struct list_head node;
@@ -336,6 +337,7 @@  EXPORT_SYMBOL_GPL(component_bind_all);
 int component_add(struct device *dev, const struct component_ops *ops)
 {
 	struct component *component;
+	struct device_node *np;
 	int ret;
 
 	component = kzalloc(sizeof(*component), GFP_KERNEL);
@@ -356,6 +358,11 @@  int component_add(struct device *dev, const struct component_ops *ops)
 
 		kfree(component);
 	}
+
+	np = dev->of_node;
+	if (np)
+		np->_flags |= OF_DEV_COMPONENT;
+
 	mutex_unlock(&component_mutex);
 
 	return ret < 0 ? ret : 0;
@@ -365,6 +372,7 @@  EXPORT_SYMBOL_GPL(component_add);
 void component_del(struct device *dev, const struct component_ops *ops)
 {
 	struct component *c, *component = NULL;
+	struct device_node *np;
 
 	mutex_lock(&component_mutex);
 	list_for_each_entry(c, &component_list, node)
@@ -377,6 +385,10 @@  void component_del(struct device *dev, const struct component_ops *ops)
 	if (component && component->master)
 		take_down_master(component->master);
 
+	np = dev->of_node;
+	if (np)
+		np->_flags &= ~OF_DEV_COMPONENT;
+
 	mutex_unlock(&component_mutex);
 
 	WARN_ON(!component);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 2b56717..0517b91 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -27,6 +27,7 @@ 
 #include <linux/pm_runtime.h>
 #include <linux/netdevice.h>
 #include <linux/sysfs.h>
+#include <linux/component.h>
 
 #include "base.h"
 #include "power/power.h"
@@ -980,6 +981,7 @@  int device_add(struct device *dev)
 	struct device *parent = NULL;
 	struct kobject *kobj;
 	struct class_interface *class_intf;
+	struct device_node *np;
 	int error = -EINVAL;
 
 	dev = get_device(dev);
@@ -1088,6 +1090,15 @@  int device_add(struct device *dev)
 				class_intf->add_dev(dev, class_intf);
 		mutex_unlock(&dev->class->p->mutex);
 	}
+
+	/* if DT-created device referenced by phandle, create a component */
+	np = dev->of_node;
+	if (np && np->phandle &&
+	    !(np->_flags & OF_DEV_COMPONENT)) {
+		component_add(dev, NULL);
+		np->_flags |= OF_DEV_IMPLICIT_COMPONENT;
+	}
+
 done:
 	put_device(dev);
 	return error;
@@ -1189,10 +1200,17 @@  void device_del(struct device *dev)
 {
 	struct device *parent = dev->parent;
 	struct class_interface *class_intf;
+	struct device_node *np;
 
 	/* Notify clients of device removal.  This call must come
 	 * before dpm_sysfs_remove().
 	 */
+	np = dev->of_node;
+	if (np && (np->_flags & OF_DEV_COMPONENT)) {
+		component_del(dev, NULL);
+		np->_flags &= ~OF_DEV_IMPLICIT_COMPONENT;
+	}
+
 	if (dev->bus)
 		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 					     BUS_NOTIFY_DEL_DEVICE, dev);
diff --git a/include/linux/of.h b/include/linux/of.h
index 70c64ba..40f1c34 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -59,6 +59,8 @@  struct device_node {
 	struct	proc_dir_entry *pde;	/* this node's proc directory */
 	struct	kref kref;
 	unsigned long _flags;
+#define OF_DEV_COMPONENT 1
+#define OF_DEV_IMPLICIT_COMPONENT 2
 	void	*data;
 #if defined(CONFIG_SPARC)
 	const char *path_component_name;