Commit 9c1600ed authored by David Brownell's avatar David Brownell Committed by Jean Delvare
Browse files

i2c: Add i2c_board_info and i2c_new_device()



This provides partial support for new-style I2C driver binding.  It builds
on "struct i2c_board_info" declarations that identify I2C devices on a given
board.  This is needed on systems with I2C devices that can't be fully probed
and/or autoconfigured, such as many embedded Linux configurations where the
way a given I2C device is wired may affect how it must be used.

There are two models for declaring such devices:

 * LATE -- using a public function i2c_new_device().  This lets modules
   declare I2C devices found *AFTER* a given I2C adapter becomes available.
   
   For example, a PCI card could create adapters giving access to utility
   chips on that card, and this would be used to associate those chips with
   those adapters.

 * EARLY -- from arch_initcall() level code, using a non-exported function
   i2c_register_board_info().  This copies the declarations *BEFORE* such
   an i2c_adapter becomes available, arranging that i2c_new_device() will
   be called later when i2c-core registers the relevant i2c_adapter.

   For example, arch/.../.../board-*.c files would declare the I2C devices
   along with their platform data, and I2C devices would behave much like
   PNPACPI devices.  (That is, both enumerate from board-specific tables.)

To match the exported i2c_new_device(), the previously-private function
i2c_unregister_device() is now exported.

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

Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
parent 4298cfc3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ obj-$(CONFIG_GAMEPORT) += input/gameport/
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/
+5 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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/
+90 −0
Original line number Diff line number Diff line
/*
 * i2c-boardinfo.h - collect pre-declarations of I2C devices
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/kernel.h>
#include <linux/i2c.h>

#include "i2c-core.h"


/* These symbols are exported ONLY FOR the i2c core.
 * No other users will be supported.
 */
DEFINE_MUTEX(__i2c_board_lock);
EXPORT_SYMBOL_GPL(__i2c_board_lock);

LIST_HEAD(__i2c_board_list);
EXPORT_SYMBOL_GPL(__i2c_board_list);

int __i2c_first_dynamic_bus_num;
EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);


/**
 * i2c_register_board_info - statically declare I2C devices
 * @busnum: identifies the bus to which these devices belong
 * @info: vector of i2c device descriptors
 * @len: how many descriptors in the vector; may be zero to reserve
 *	the specified bus number.
 *
 * 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 driver is
 * 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(int busnum,
	struct i2c_board_info const *info, unsigned len)
{
	int status;

	mutex_lock(&__i2c_board_lock);

	/* dynamic bus numbers will be assigned after the last static one */
	if (busnum >= __i2c_first_dynamic_bus_num)
		__i2c_first_dynamic_bus_num = busnum + 1;

	for (status = 0; len; len--, info++) {
		struct i2c_devinfo	*devinfo;

		devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
		if (!devinfo) {
			pr_debug("i2c-core: can't register boardinfo!\n");
			status = -ENOMEM;
			break;
		}

		devinfo->busnum = busnum;
		devinfo->board_info = *info;
		list_add_tail(&devinfo->list, &__i2c_board_list);
	}

	mutex_unlock(&__i2c_board_lock);

	return status;
}
+86 −4
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@
#include <linux/completion.h>
#include <asm/uaccess.h>

#include "i2c-core.h"


static LIST_HEAD(adapters);
static LIST_HEAD(drivers);
@@ -162,6 +164,11 @@ static void i2c_client_release(struct device *dev)
	complete(&client->released);
}

static void i2c_client_dev_release(struct device *dev)
{
	kfree(to_i2c_client(dev));
}

static ssize_t show_client_name(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct i2c_client *client = to_i2c_client(dev);
@@ -195,7 +202,60 @@ 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; bus_num is ignored
 *
 * Create a device to work with a new style i2c driver, where binding is
 * handled through driver model probe()/remove() methods.  This call is not
 * appropriate for use by mainboad initialization logic, which usually runs
 * during an arch_initcall() long before any i2c_adapter could exist.
 *
 * 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;

	client->dev.platform_data = info->platform_data;
	client->flags = info->flags;
	client->addr = info->addr;
	client->irq = info->irq;

	strlcpy(client->driver_name, info->driver_name,
		sizeof(client->driver_name));
	strlcpy(client->name, info->type, sizeof(client->name));

	/* 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).  and the device
	 * refcount model is the standard driver model one.
	 */
	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	*adapter = client->adapter;
	struct i2c_driver	*driver = client->driver;
@@ -213,6 +273,7 @@ static void i2c_unregister_device(struct i2c_client *client)

	device_unregister(&client->dev);
}
EXPORT_SYMBOL_GPL(i2c_unregister_device);


/* ------------------------------------------------------------------------- */
@@ -243,6 +304,22 @@ struct class i2c_adapter_class = {
	.dev_attrs		= i2c_adapter_attrs,
};

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
	struct i2c_devinfo	*devinfo;

	mutex_lock(&__i2c_board_lock);
	list_for_each_entry(devinfo, &__i2c_board_list, list) {
		if (devinfo->busnum == adapter->nr
				&& !i2c_new_device(adapter,
						&devinfo->board_info))
			printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",
				i2c_adapter_id(adapter),
				devinfo->board_info.addr);
	}
	mutex_unlock(&__i2c_board_lock);
}


/* -----
 * i2c_add_adapter is called from within the algorithm layer,
@@ -311,7 +388,6 @@ out_list:
	goto out_unlock;
}


int i2c_del_adapter(struct i2c_adapter *adap)
{
	struct list_head  *item, *_n;
@@ -541,9 +617,15 @@ int i2c_attach_client(struct i2c_client *client)
	client->usage_count = 0;

	client->dev.parent = &client->adapter->dev;
	client->dev.driver = &client->driver->driver;
	client->dev.bus = &i2c_bus_type;
	client->dev.release = &i2c_client_release;

	if (client->driver)
		client->dev.driver = &client->driver->driver;

	if (client->driver && !is_newstyle_driver(client->driver))
		client->dev.release = i2c_client_release;
	else
		client->dev.release = i2c_client_dev_release;

	snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
		"%d-%04x", i2c_adapter_id(adapter), client->addr);
Loading