From 9a94241afcc9a481691a9c29b7460217925b59b8 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 11 Aug 2010 18:20:56 +0200 Subject: i2c: Add support for custom probe function The probe method used by i2c_new_probed_device() may not be suitable for all cases. Let the caller provide its own, optional probe function. Signed-off-by: Jean Delvare Acked-by: Mauro Carvalho Chehab --- drivers/i2c/i2c-core.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/i2c/i2c-core.c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index df937df845eb..cf14ca063181 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1464,14 +1464,18 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) struct i2c_client * i2c_new_probed_device(struct i2c_adapter *adap, struct i2c_board_info *info, - unsigned short const *addr_list) + unsigned short const *addr_list, + int (*probe)(struct i2c_adapter *, unsigned short addr)) { int i; - /* Stop here if the bus doesn't support probing */ - if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) { - dev_err(&adap->dev, "Probing not supported\n"); - return NULL; + if (!probe) { + /* Stop here if the bus doesn't support probing */ + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) { + dev_err(&adap->dev, "Probing not supported\n"); + return NULL; + } + probe = i2c_default_probe; } for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) { @@ -1490,7 +1494,7 @@ i2c_new_probed_device(struct i2c_adapter *adap, } /* Test address responsiveness */ - if (i2c_default_probe(adap, addr_list[i])) + if (probe(adap, addr_list[i])) break; } -- cgit v1.2.2 From d44f19d586b6113fb5db10e1a36457f0db3b01aa Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 11 Aug 2010 18:20:57 +0200 Subject: V4L/DVB: Use custom I2C probing function mechanism Now that i2c-core offers the possibility to provide custom probing function for I2C devices, let's make use of it. Signed-off-by: Jean Delvare Acked-by: Mauro Carvalho Chehab --- drivers/i2c/i2c-core.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/i2c/i2c-core.c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index cf14ca063181..6e1c2f54d9cf 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1461,6 +1461,13 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) return err; } +int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr) +{ + return i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_QUICK, NULL) >= 0; +} +EXPORT_SYMBOL_GPL(i2c_probe_func_quick_read); + struct i2c_client * i2c_new_probed_device(struct i2c_adapter *adap, struct i2c_board_info *info, -- cgit v1.2.2 From fe61e07e9ebc890c70d97a1f72ddaad4bee2d848 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 11 Aug 2010 18:20:58 +0200 Subject: i2c: Move adapter locking helpers to i2c-core Uninline i2c adapter locking helper functions, move them to i2c-core, and use them in i2c-core itself. The functions are still exported for external users. This makes future updates to the locking model (which will be needed for multiplexing support) possible and transparent. Signed-off-by: Jean Delvare Cc: Michael Lawnick --- drivers/i2c/i2c-core.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'drivers/i2c/i2c-core.c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 6e1c2f54d9cf..e09e143379b1 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -429,6 +429,35 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) __i2c_check_addr_busy); } +/** + * i2c_lock_adapter - Get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +void i2c_lock_adapter(struct i2c_adapter *adapter) +{ + rt_mutex_lock(&adapter->bus_lock); +} +EXPORT_SYMBOL_GPL(i2c_lock_adapter); + +/** + * i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +static int i2c_trylock_adapter(struct i2c_adapter *adapter) +{ + return rt_mutex_trylock(&adapter->bus_lock); +} + +/** + * i2c_unlock_adapter - Release exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +void i2c_unlock_adapter(struct i2c_adapter *adapter) +{ + rt_mutex_unlock(&adapter->bus_lock); +} +EXPORT_SYMBOL_GPL(i2c_unlock_adapter); + /** * i2c_new_device - instantiate an i2c device * @adap: the adapter managing the device @@ -1238,12 +1267,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) #endif if (in_atomic() || irqs_disabled()) { - ret = rt_mutex_trylock(&adap->bus_lock); + ret = i2c_trylock_adapter(adap); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { - rt_mutex_lock(&adap->bus_lock); + i2c_lock_adapter(adap); } /* Retry automatically on arbitration loss */ @@ -1255,7 +1284,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (time_after(jiffies, orig_jiffies + adap->timeout)) break; } - rt_mutex_unlock(&adap->bus_lock); + i2c_unlock_adapter(adap); return ret; } else { @@ -2013,7 +2042,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, flags &= I2C_M_TEN | I2C_CLIENT_PEC; if (adapter->algo->smbus_xfer) { - rt_mutex_lock(&adapter->bus_lock); + i2c_lock_adapter(adapter); /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; @@ -2027,7 +2056,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, orig_jiffies + adapter->timeout)) break; } - rt_mutex_unlock(&adapter->bus_lock); + i2c_unlock_adapter(adapter); } else res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, command, protocol, data); -- cgit v1.2.2 From d6703281ff20df6c2c0ea7c39a0cc2fa8be3e680 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 11 Aug 2010 18:20:59 +0200 Subject: i2c: Drop dummy variable Now that bus_for_each_drv() is no longer __must_check, we can drop the dummy variable that was used to store the returned value. Signed-off-by: Jean Delvare Cc: Justin P. Mattock --- drivers/i2c/i2c-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/i2c/i2c-core.c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index e09e143379b1..bb02f8e21a3b 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -789,7 +789,7 @@ static int __process_new_adapter(struct device_driver *d, void *data) static int i2c_register_adapter(struct i2c_adapter *adap) { - int res = 0, dummy; + int res = 0; /* Can't register until after driver model init */ if (unlikely(WARN_ON(!i2c_bus_type.p))) { @@ -830,8 +830,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) /* Notify drivers */ mutex_lock(&core_lock); - dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, - __process_new_adapter); + bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); mutex_unlock(&core_lock); return 0; -- cgit v1.2.2 From 8031d79baedabd44590d047d97a310eb0e0e4e0c Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 11 Aug 2010 18:21:00 +0200 Subject: i2c: Make i2c_default_probe self-sufficient Make i2c_default_probe self-sufficient, so that callers don't have to do functionality checks themselves. This ensures everything is and will stay consistent. Signed-off-by: Jean Delvare --- drivers/i2c/i2c-core.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) (limited to 'drivers/i2c/i2c-core.c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index bb02f8e21a3b..ba86af63f5cf 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1378,13 +1378,17 @@ static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr) I2C_SMBUS_BYTE_DATA, &dummy); else #endif - if ((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50 - || !i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK)) - err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, - I2C_SMBUS_BYTE, &dummy); - else + if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50) + && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK)) err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0, I2C_SMBUS_QUICK, NULL); + else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE, &dummy); + else { + dev_warn(&adap->dev, "No suitable probing method supported\n"); + err = -EOPNOTSUPP; + } return err >= 0; } @@ -1465,16 +1469,6 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) if (!(adapter->class & driver->class)) goto exit_free; - /* Stop here if the bus doesn't support probing */ - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE)) { - if (address_list[0] == I2C_CLIENT_END) - goto exit_free; - - dev_warn(&adapter->dev, "Probing not supported\n"); - err = -EOPNOTSUPP; - goto exit_free; - } - for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) { dev_dbg(&adapter->dev, "found normal entry for adapter %d, " "addr 0x%02x\n", adap_id, address_list[i]); @@ -1504,14 +1498,8 @@ i2c_new_probed_device(struct i2c_adapter *adap, { int i; - if (!probe) { - /* Stop here if the bus doesn't support probing */ - if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) { - dev_err(&adap->dev, "Probing not supported\n"); - return NULL; - } + if (!probe) probe = i2c_default_probe; - } for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) { /* Check address validity */ -- cgit v1.2.2 From dafc50d141c27959dbd3a1cfe9857a86d23402a7 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 11 Aug 2010 18:21:01 +0200 Subject: i2c: Use a separate mutex for userspace client lists Moving userspace-instantiated clients to separate lists wasn't nearly enough to avoid deadlocks in multiplexed bus cases. We also want to have a dedicated mutex to protect each list. Signed-off-by: Jean Delvare Cc: Michael Lawnick --- drivers/i2c/i2c-core.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/i2c/i2c-core.c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index ba86af63f5cf..97f96b66653c 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -662,9 +662,9 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr, return -EINVAL; /* Keep track of the added device */ - i2c_lock_adapter(adap); + mutex_lock(&adap->userspace_clients_lock); list_add_tail(&client->detected, &adap->userspace_clients); - i2c_unlock_adapter(adap); + mutex_unlock(&adap->userspace_clients_lock); dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", info.type, info.addr); @@ -703,7 +703,7 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr, /* Make sure the device was added through sysfs */ res = -ENOENT; - i2c_lock_adapter(adap); + mutex_lock(&adap->userspace_clients_lock); list_for_each_entry_safe(client, next, &adap->userspace_clients, detected) { if (client->addr == addr) { @@ -716,7 +716,7 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr, break; } } - i2c_unlock_adapter(adap); + mutex_unlock(&adap->userspace_clients_lock); if (res < 0) dev_err(dev, "%s: Can't find device in list\n", @@ -798,6 +798,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) } rt_mutex_init(&adap->bus_lock); + mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); /* Set default timeout to 1 second if not already set */ @@ -1003,7 +1004,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) return res; /* Remove devices instantiated from sysfs */ - i2c_lock_adapter(adap); + mutex_lock(&adap->userspace_clients_lock); list_for_each_entry_safe(client, next, &adap->userspace_clients, detected) { dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name, @@ -1011,7 +1012,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) list_del(&client->detected); i2c_unregister_device(client); } - i2c_unlock_adapter(adap); + mutex_unlock(&adap->userspace_clients_lock); /* Detach any active clients. This can't fail, thus we do not checking the returned value. */ -- cgit v1.2.2 From 0826374bff57411d239f2fcb15da3c35af0a93cd Mon Sep 17 00:00:00 2001 From: Michael Lawnick Date: Wed, 11 Aug 2010 18:21:02 +0200 Subject: i2c: Multiplexed I2C bus core support Add multiplexed bus core support. I2C multiplexer and switches like pca954x get instantiated as new adapters per port. Signed-off-by: Michael Lawnick Acked-by: Rodolfo Giometti Signed-off-by: Jean Delvare --- drivers/i2c/i2c-core.c | 64 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 7 deletions(-) (limited to 'drivers/i2c/i2c-core.c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 97f96b66653c..6649176de940 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -20,7 +20,9 @@ /* With some changes from Kyösti Mälkki . All SMBus-related things are written by Frodo Looijaard SMBus 2.0 support by Mark Studebaker and - Jean Delvare */ + Jean Delvare + Mux support by Rodolfo Giometti and + Michael Lawnick */ #include #include @@ -423,10 +425,48 @@ static int __i2c_check_addr_busy(struct device *dev, void *addrp) return 0; } +/* walk up mux tree */ +static int i2c_check_mux_parents(struct i2c_adapter *adapter, int addr) +{ + int result; + + result = device_for_each_child(&adapter->dev, &addr, + __i2c_check_addr_busy); + + if (!result && i2c_parent_is_i2c_adapter(adapter)) + result = i2c_check_mux_parents( + to_i2c_adapter(adapter->dev.parent), addr); + + return result; +} + +/* recurse down mux tree */ +static int i2c_check_mux_children(struct device *dev, void *addrp) +{ + int result; + + if (dev->type == &i2c_adapter_type) + result = device_for_each_child(dev, addrp, + i2c_check_mux_children); + else + result = __i2c_check_addr_busy(dev, addrp); + + return result; +} + static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) { - return device_for_each_child(&adapter->dev, &addr, - __i2c_check_addr_busy); + int result = 0; + + if (i2c_parent_is_i2c_adapter(adapter)) + result = i2c_check_mux_parents( + to_i2c_adapter(adapter->dev.parent), addr); + + if (!result) + result = device_for_each_child(&adapter->dev, &addr, + i2c_check_mux_children); + + return result; } /** @@ -435,7 +475,10 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) */ void i2c_lock_adapter(struct i2c_adapter *adapter) { - rt_mutex_lock(&adapter->bus_lock); + if (i2c_parent_is_i2c_adapter(adapter)) + i2c_lock_adapter(to_i2c_adapter(adapter->dev.parent)); + else + rt_mutex_lock(&adapter->bus_lock); } EXPORT_SYMBOL_GPL(i2c_lock_adapter); @@ -445,7 +488,10 @@ EXPORT_SYMBOL_GPL(i2c_lock_adapter); */ static int i2c_trylock_adapter(struct i2c_adapter *adapter) { - return rt_mutex_trylock(&adapter->bus_lock); + if (i2c_parent_is_i2c_adapter(adapter)) + return i2c_trylock_adapter(to_i2c_adapter(adapter->dev.parent)); + else + return rt_mutex_trylock(&adapter->bus_lock); } /** @@ -454,7 +500,10 @@ static int i2c_trylock_adapter(struct i2c_adapter *adapter) */ void i2c_unlock_adapter(struct i2c_adapter *adapter) { - rt_mutex_unlock(&adapter->bus_lock); + if (i2c_parent_is_i2c_adapter(adapter)) + i2c_unlock_adapter(to_i2c_adapter(adapter->dev.parent)); + else + rt_mutex_unlock(&adapter->bus_lock); } EXPORT_SYMBOL_GPL(i2c_unlock_adapter); @@ -743,10 +792,11 @@ static const struct attribute_group *i2c_adapter_attr_groups[] = { NULL }; -static struct device_type i2c_adapter_type = { +struct device_type i2c_adapter_type = { .groups = i2c_adapter_attr_groups, .release = i2c_adapter_dev_release, }; +EXPORT_SYMBOL_GPL(i2c_adapter_type); #ifdef CONFIG_I2C_COMPAT static struct class_compat *i2c_adapter_compat_class; -- cgit v1.2.2