[i2c] [patch 4/5] i2c board_info and i2c_new_device()

David Brownell david-b at pacbell.net
Thu Feb 15 09:46:10 CET 2007


This provides partial support for new-style I2C driver binding, which is
most useful on systems where SMBUS_QUICK probing is inadequate; that
includes many embedded Linux systems.

 - Define i2c_board_info and associated declaration calls, for mainboard
   specific code to declare what I2C devices are present and how they are
   configured.

 - Those declaration calls are statically linked (code in the init section)
   no matter how I2C is configured (a change in the build infrastructure).
   That exposes a new private data interface to the i2c-core component.
   
 - Define a new public function i2c_new_device(), so non-mainboard code
   (like a support module for a daughtercard) can declare board_info for
   I2C devices found there.

 - Export the previously-private i2c_unregister_device().

In addition to a device's existence, addressing, and preferred driver,
the board-specific information includes an IRQ (e.g. for an RTC alarm)
and a platform_data pointer.

Pending later patches using these new APIs, this is effectively a NOP.

Signed-off-by: David Brownell <dbrownell at users.sourceforge.net>

---
 drivers/Makefile            |    2 -
 drivers/i2c/Kconfig         |    5 +++
 drivers/i2c/Makefile        |    1 
 drivers/i2c/i2c-boardinfo.c |   64 ++++++++++++++++++++++++++++++++++++++++
 drivers/i2c/i2c-core.c      |   69 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/i2c/i2c-core.h      |   13 ++++++++
 include/linux/i2c.h         |   62 +++++++++++++++++++++++++++++++++++++++
 7 files changed, 214 insertions(+), 2 deletions(-)


Index: i2c/include/linux/i2c.h
===================================================================
--- i2c.orig/include/linux/i2c.h	2007-02-15 00:00:04.000000000 -0800
+++ i2c/include/linux/i2c.h	2007-02-15 00:00:08.000000000 -0800
@@ -163,6 +163,7 @@ struct i2c_client {
 	int usage_count;		/* How many accesses currently  */
 					/* to the client		*/
 	struct device dev;		/* the device structure		*/
+	int irq;			/* irq issued by device (or -1) */
 	struct list_head list;
 	char name[I2C_NAME_SIZE];
 	struct completion released;
@@ -184,6 +185,67 @@ static inline void i2c_set_clientdata (s
 	dev_set_drvdata (&dev->dev, data);
 }
 
+/**
+ * struct i2c_board_info - template for device creation
+ * @driver: identifies the driver to be bound to the device
+ * @dev_addr: stored in i2c_client.addr
+ * @bus_num: usually matches i2c_client.adapter->nr
+ * @platform_data: stored in i2c_client.dev.platform_data
+ * @irq: stored in i2c_client.irq
+
+ * I2C doesn't actually support hardware probing, although controllers and
+ * devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's
+ * a device at a given address.  Drivers commonly need more information than
+ * that, such as chip type, configuration, associated IRQ, and so on.
+ *
+ * i2c_board_info is used to build tables of information listing I2C devices
+ * that are present.  This information is used to grow the driver model tree
+ * for "new style" I2C drivers.  For mainboards this is done statically using
+ * i2c_register_board_info(), where @bus_num represents an adapter that isn't
+ * yet available.  For add-on boards, i2c_new_device() does this dynamically
+ * with the adapter already known.
+ */
+struct i2c_board_info {
+	char		driver[KOBJ_NAME_LEN];
+	short		bus_num;
+	unsigned short	dev_addr;
+	void		*platform_data;
+	int		irq;
+};
+
+/**
+ * I2C_BOARD_INFO - macro used to list an i2c device and its driver
+ * @driver_name: identifies the driver to use with the device
+ * @busnum: numbers the bus to which the device is connected; ignored
+ *	when i2c_new_device() explicitly idenfies that bus.
+ * @devaddr: the device's address on the bus.
+ *
+ * This macro initializes mandatory fields of a struct i2c_board_info,
+ * declaring what has been provided on a particular board.  Optional
+ * fields (such as the associated irq, or device-specific platform_data)
+ * are provided using conventional syntax.
+ */
+#define I2C_BOARD_INFO(driver_name,busnum,devaddr) \
+	.driver = driver_name, .bus_num = busnum, .dev_addr = devaddr
+
+
+/* Add-on boards should register/unregister their devices; e.g. a board
+ * with integrated I2C, a config eeprom, sensors, and a codec that's
+ * used in conjunction with the primary hardware.
+ */
+extern struct i2c_client *
+i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);
+
+extern void i2c_unregister_device(struct i2c_client *);
+
+/* Mainboard arch_initcall() code should register all its I2C devices.
+ * This is done at arch_initcall time, before declaring any i2c adapters.
+ * Modules for add-on boards must use other calls.
+ */
+extern int
+i2c_register_board_info(struct i2c_board_info const *info, unsigned n);
+
+
 /*
  * The following structs are for those who like to implement new bus drivers:
  * i2c_algorithm is the interface to a class of hardware solutions which can
Index: i2c/drivers/Makefile
===================================================================
--- i2c.orig/drivers/Makefile	2007-02-14 23:59:23.000000000 -0800
+++ i2c/drivers/Makefile	2007-02-15 00:00:08.000000000 -0800
@@ -58,7 +58,7 @@ obj-$(CONFIG_GAMEPORT)		+= input/gamepor
 obj-$(CONFIG_INPUT)		+= input/
 obj-$(CONFIG_I2O)		+= message/
 obj-$(CONFIG_RTC_LIB)		+= rtc/
-obj-$(CONFIG_I2C)		+= i2c/
+obj-y				+= i2c/
 obj-$(CONFIG_W1)		+= w1/
 obj-$(CONFIG_HWMON)		+= hwmon/
 obj-$(CONFIG_PHONE)		+= telephony/
Index: i2c/drivers/i2c/Kconfig
===================================================================
--- i2c.orig/drivers/i2c/Kconfig	2007-02-14 23:59:23.000000000 -0800
+++ i2c/drivers/i2c/Kconfig	2007-02-15 00:00:08.000000000 -0800
@@ -22,6 +22,11 @@ config I2C
 	  This I2C support can also be built as a module.  If so, the module
 	  will be called i2c-core.
 
+config I2C_BOARDINFO
+	boolean
+	depends on I2C
+	default y
+
 config I2C_CHARDEV
 	tristate "I2C device interface"
 	depends on I2C
Index: i2c/drivers/i2c/Makefile
===================================================================
--- i2c.orig/drivers/i2c/Makefile	2007-02-14 23:59:23.000000000 -0800
+++ i2c/drivers/i2c/Makefile	2007-02-15 00:00:08.000000000 -0800
@@ -2,6 +2,7 @@
 # Makefile for the i2c core.
 #
 
+obj-$(CONFIG_I2C_BOARDINFO)	+= i2c-boardinfo.o
 obj-$(CONFIG_I2C)		+= i2c-core.o
 obj-$(CONFIG_I2C_CHARDEV)	+= i2c-dev.o
 obj-y				+= busses/ chips/ algos/
Index: i2c/drivers/i2c/i2c-core.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ i2c/drivers/i2c/i2c-core.h	2007-02-15 00:00:08.000000000 -0800
@@ -0,0 +1,13 @@
+struct boardinfo {
+	struct list_head	list;
+	unsigned		n_board_info;
+	struct i2c_board_info	board_info[0];
+};
+
+/* board_lock protects board_list and first_dynamic_bus_num.
+ * nobody except i2c-core is allowed to use these symbols.
+ */
+extern struct mutex	__i2c_board_lock;
+extern struct list_head	__i2c_board_list;
+extern int		__i2c_first_dynamic_bus_num;
+
Index: i2c/drivers/i2c/i2c-boardinfo.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ i2c/drivers/i2c/i2c-boardinfo.c	2007-02-15 00:00:08.000000000 -0800
@@ -0,0 +1,64 @@
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+
+#include "i2c-core.h"
+
+
+DEFINE_MUTEX(__i2c_board_lock);
+LIST_HEAD(__i2c_board_list);
+int __i2c_first_dynamic_bus_num;
+
+/* These symbols are exported ONLY FOR i2c-core.
+ * No other users will be supported.
+ */
+EXPORT_SYMBOL_GPL(__i2c_board_lock);
+EXPORT_SYMBOL_GPL(__i2c_board_list);
+EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);
+
+/**
+ * i2c_register_board_info - statically declare I2C devices
+ * @info: vector of i2c device descriptors
+ * @len: how many descriptors in the vector
+ *
+ * Systems using the Linux I2C driver stack can declare tables of board info
+ * while they initialize.  This should be done in board-specific init code
+ * near arch_initcall() time, or equivalent, before any I2C adapter drivers are
+ * registered.  For example, mainboard init code could define several devices,
+ * as could the init code for each daughtercard in a board stack.
+ *
+ * The I2C devices will be created later, after the adapter for the relevant bus
+ * has been registered.  After that moment, standard driver model tools are used
+ * to bind "new style" I2C drivers to the devices.  The bus number for any device
+ * declared using this routine is not available for dynamic allocation.
+ *
+ * The board info passed can safely be __initdata, but be careful of embedded
+ * pointers (for platform_data, functions, etc) since that won't be copied.
+ */
+int __init
+i2c_register_board_info(struct i2c_board_info const *info, unsigned len)
+{
+	struct boardinfo	*bi;
+	int			i;
+
+	if (len == 0)
+		return 0;
+
+	bi = kmalloc(sizeof(*bi) + len * sizeof *info, GFP_KERNEL);
+	if (!bi)
+		return -ENOMEM;
+	bi->n_board_info = len;
+	memcpy(bi->board_info, info, len * sizeof *info);
+
+	mutex_lock(&__i2c_board_lock);
+
+	/* dynamic bus numbers will be assigned after the last static one */
+	for (i = 0; i < len; i++, info++) {
+		if (info->bus_num >= __i2c_first_dynamic_bus_num)
+			__i2c_first_dynamic_bus_num = info->bus_num + 1;
+	}
+
+	list_add_tail(&bi->list, &__i2c_board_list);
+	mutex_unlock(&__i2c_board_lock);
+
+	return 0;
+}
Index: i2c/drivers/i2c/i2c-core.c
===================================================================
--- i2c.orig/drivers/i2c/i2c-core.c	2007-02-15 00:00:04.000000000 -0800
+++ i2c/drivers/i2c/i2c-core.c	2007-02-15 00:00:08.000000000 -0800
@@ -35,6 +35,8 @@
 #include <linux/completion.h>
 #include <asm/uaccess.h>
 
+#include "i2c-core.h"
+
 
 static LIST_HEAD(adapters);
 static DEFINE_MUTEX(core_lists);
@@ -180,7 +182,53 @@ struct bus_type i2c_bus_type = {
 	.resume		= i2c_device_resume,
 };
 
-static void i2c_unregister_device(struct i2c_client *client)
+/**
+ * i2c_new_device - instantiate an i2c device for use with a new style driver
+ * @adap: the adapter managing the device
+ * @info: describes one I2C device
+ *
+ * Create a device to work with a new style i2c driver, where binding is
+ * handled through driver model probe()/remove() methods.
+ *
+ * This returns the new i2c client, which may be saved for later use with
+ * i2c_unregister_device(); or NULL to indicate an error.
+ */
+struct i2c_client *
+i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
+{
+	struct i2c_client	*client;
+	int			status;
+
+	client = kzalloc(sizeof *client, GFP_KERNEL);
+	if (!client)
+		return NULL;
+
+	client->adapter = adap;
+
+	strlcpy(client->name, info->driver, sizeof client->name);
+	client->dev.platform_data = info->platform_data;
+	client->addr = info->dev_addr;
+	client->irq = info->irq;
+
+	/* a new style driver may be bound to this device when we
+	 * return from this function, or any later moment (e.g. maybe
+	 * hotplugging will load the driver module).
+	 */
+	status = i2c_attach_client(client);
+	if (status < 0) {
+		kfree(client);
+		client = NULL;
+	}
+	return client;
+}
+EXPORT_SYMBOL_GPL(i2c_new_device);
+
+
+/**
+ * i2c_unregister_device - reverse effect of i2c_new_device()
+ * @client: value returned from i2c_new_device()
+ */
+void i2c_unregister_device(struct i2c_client *client)
 {
 	struct i2c_adapter	*adap = client->adapter;
 	struct i2c_driver	*driver = client->driver;
@@ -215,6 +263,7 @@ static void i2c_unregister_device(struct
 		put_device(&client->dev);
 
 }
+EXPORT_SYMBOL_GPL(i2c_unregister_device);
 
 
 /* ------------------------------------------------------------------------- */
@@ -325,6 +374,24 @@ out_list:
 	goto out_unlock;
 }
 
+static void i2c_scan_static_board_info(struct i2c_adapter *adap)
+{
+	struct boardinfo	*bi;
+
+	mutex_lock(&__i2c_board_lock);
+	list_for_each_entry(bi, &__i2c_board_list, list) {
+		struct i2c_board_info	*chip = bi->board_info;
+		unsigned		n;
+
+		for (n = bi->n_board_info; n > 0; n--, chip++) {
+			if (chip->bus_num != adap->nr)
+				continue;
+			(void) i2c_new_device(adap, chip);
+		}
+	}
+	mutex_unlock(&__i2c_board_lock);
+}
+
 static int i2c_do_del_adapter(struct device_driver *d, void *data)
 {
 	struct i2c_driver *driver = to_i2c_driver(d);



More information about the i2c mailing list