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/i2c-core.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 4 deletions(-) (limited to 'drivers/i2c/i2c-core.c') 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); -- cgit v1.2.3