diff options
-rw-r--r-- | drivers/i2c/i2c-core.c | 126 | ||||
-rw-r--r-- | include/linux/i2c.h | 1 |
2 files changed, 101 insertions, 26 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index a7dcdc69b9e..c50229bd9f8 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -320,38 +320,19 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter) mutex_unlock(&__i2c_board_lock); } - -/* ----- - * i2c_add_adapter is called from within the algorithm layer, - * when a new hw adapter registers. A new device is register to be - * available for clients. - */ -int i2c_add_adapter(struct i2c_adapter *adap) +static int i2c_register_adapter(struct i2c_adapter *adap) { - int id, res = 0; + int res = 0; struct list_head *item; struct i2c_driver *driver; - mutex_lock(&core_lists); - - if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) { - res = -ENOMEM; - goto out_unlock; - } - - res = idr_get_new(&i2c_adapter_idr, adap, &id); - if (res < 0) { - if (res == -EAGAIN) - res = -ENOMEM; - goto out_unlock; - } - - adap->nr = id & MAX_ID_MASK; mutex_init(&adap->bus_lock); mutex_init(&adap->clist_lock); - list_add_tail(&adap->list,&adapters); INIT_LIST_HEAD(&adap->clients); + mutex_lock(&core_lists); + list_add_tail(&adap->list, &adapters); + /* Add the adapter to the driver core. * If the parent pointer is not set up, * we add this adapter to the host bus. @@ -370,6 +351,10 @@ int i2c_add_adapter(struct i2c_adapter *adap) dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); + /* create pre-declared device nodes for new-style drivers */ + if (adap->nr < __i2c_first_dynamic_bus_num) + i2c_scan_static_board_info(adap); + /* let legacy drivers scan this bus for matching devices */ list_for_each(item,&drivers) { driver = list_entry(item, struct i2c_driver, list); @@ -388,6 +373,93 @@ out_list: goto out_unlock; } +/** + * i2c_add_adapter - declare i2c adapter, use dynamic bus number + * @adapter: the adapter to add + * + * This routine is used to declare an I2C adapter when its bus number + * doesn't matter. Examples: for I2C adapters dynamically added by + * USB links or PCI plugin cards. + * + * When this returns zero, a new bus number was allocated and stored + * in adap->nr, and the specified adapter became available for clients. + * Otherwise, a negative errno value is returned. + */ +int i2c_add_adapter(struct i2c_adapter *adapter) +{ + int id, res = 0; + +retry: + if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) + return -ENOMEM; + + mutex_lock(&core_lists); + /* "above" here means "above or equal to", sigh */ + res = idr_get_new_above(&i2c_adapter_idr, adapter, + __i2c_first_dynamic_bus_num, &id); + mutex_unlock(&core_lists); + + if (res < 0) { + if (res == -EAGAIN) + goto retry; + return res; + } + + adapter->nr = id; + return i2c_register_adapter(adapter); +} +EXPORT_SYMBOL(i2c_add_adapter); + +/** + * i2c_add_numbered_adapter - declare i2c adapter, use static bus number + * @adap: the adapter to register (with adap->nr initialized) + * + * This routine is used to declare an I2C adapter when its bus number + * matters. Example: for I2C adapters from system-on-chip CPUs, or + * otherwise built in to the system's mainboard, and where i2c_board_info + * is used to properly configure I2C devices. + * + * If no devices have pre-been declared for this bus, then be sure to + * register the adapter before any dynamically allocated ones. Otherwise + * the required bus ID may not be available. + * + * When this returns zero, the specified adapter became available for + * clients using the bus number provided in adap->nr. Also, the table + * of I2C devices pre-declared using i2c_register_board_info() is scanned, + * and the appropriate driver model device nodes are created. Otherwise, a + * negative errno value is returned. + */ +int i2c_add_numbered_adapter(struct i2c_adapter *adap) +{ + int id; + int status; + + if (adap->nr & ~MAX_ID_MASK) + return -EINVAL; + +retry: + if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) + return -ENOMEM; + + mutex_lock(&core_lists); + /* "above" here means "above or equal to", sigh; + * we need the "equal to" result to force the result + */ + status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id); + if (status == 0 && id != adap->nr) { + status = -EBUSY; + idr_remove(&i2c_adapter_idr, id); + } + mutex_unlock(&core_lists); + if (status == -EAGAIN) + goto retry; + + if (status == 0) + status = i2c_register_adapter(adap); + return status; +} +EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); + int i2c_del_adapter(struct i2c_adapter *adap) { struct list_head *item, *_n; @@ -452,7 +524,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) /* wait for sysfs to drop all references */ wait_for_completion(&adap->dev_released); - /* free dynamically allocated bus id */ + /* free bus id */ idr_remove(&i2c_adapter_idr, adap->nr); dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); @@ -493,6 +565,9 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) driver->driver.owner = owner; driver->driver.bus = &i2c_bus_type; + /* for new style drivers, when registration returns the driver core + * will have called probe() for all matching-but-unbound devices. + */ res = driver_register(&driver->driver); if (res) return res; @@ -1377,7 +1452,6 @@ EXPORT_SYMBOL_GPL(i2c_adapter_dev_release); EXPORT_SYMBOL_GPL(i2c_adapter_class); EXPORT_SYMBOL_GPL(i2c_bus_type); -EXPORT_SYMBOL(i2c_add_adapter); EXPORT_SYMBOL(i2c_del_adapter); EXPORT_SYMBOL(i2c_del_driver); EXPORT_SYMBOL(i2c_attach_client); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 382a43bf3ad..36d6814a6df 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -363,6 +363,7 @@ struct i2c_client_address_data { */ extern int i2c_add_adapter(struct i2c_adapter *); extern int i2c_del_adapter(struct i2c_adapter *); +extern int i2c_add_numbered_adapter(struct i2c_adapter *); extern int i2c_register_driver(struct module *, struct i2c_driver *); extern int i2c_del_driver(struct i2c_driver *); |