/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl>
SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and
- Jean Delvare <khali@linux-fr.org> */
+ Jean Delvare <khali@linux-fr.org>
+ Mux support by Rodolfo Giometti <giometti@enneenne.com> and
+ Michael Lawnick <michael.lawnick.ext@nsn.com> */
#include <linux/module.h>
#include <linux/kernel.h>
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;
}
/**
*/
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);
*/
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);
}
/**
*/
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);
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);
/* 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) {
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",
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;
}
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 */
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,
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. */