From 9c1600eda42e52796f49b36cf15b9debcfd09bea Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 1 May 2007 23:26:31 +0200 Subject: 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: David Brownell Signed-off-by: Jean Delvare --- drivers/i2c/Kconfig | 5 +++ drivers/i2c/Makefile | 1 + drivers/i2c/i2c-boardinfo.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/i2c-core.c | 90 +++++++++++++++++++++++++++++++++++++++++++-- drivers/i2c/i2c-core.h | 31 ++++++++++++++++ 5 files changed, 213 insertions(+), 4 deletions(-) create mode 100644 drivers/i2c/i2c-boardinfo.c create mode 100644 drivers/i2c/i2c-core.h (limited to 'drivers/i2c') diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 11935f66fcd..74c8518b69e 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -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 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 71c5a854ac5..ba26e6cbe74 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -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/ diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c new file mode 100644 index 00000000000..ffb35f09df0 --- /dev/null +++ b/drivers/i2c/i2c-boardinfo.c @@ -0,0 +1,90 @@ +/* + * 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 +#include + +#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; +} diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 3aac1b5c7cb..a7dcdc69b9e 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -35,6 +35,8 @@ #include #include +#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); diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h new file mode 100644 index 00000000000..cd5bff87485 --- /dev/null +++ b/drivers/i2c/i2c-core.h @@ -0,0 +1,31 @@ +/* + * i2c-core.h - interfaces internal to the I2C framework + * + * 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. + */ + +struct i2c_devinfo { + struct list_head list; + int busnum; + struct i2c_board_info board_info; +}; + +/* board_lock protects board_list and first_dynamic_bus_num. + * only i2c core components are 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; + -- cgit v1.2.3