Skip to content

Commit

Permalink
Merge branch 'i2c-for-linus' of git://git.kernel.org/pub/scm/linux/ke…
Browse files Browse the repository at this point in the history
…rnel/git/jdelvare/staging

* 'i2c-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging:
  i2c: I2C bus multiplexer driver pca954x
  i2c: Multiplexed I2C bus core support
  i2c: Use a separate mutex for userspace client lists
  i2c: Make i2c_default_probe self-sufficient
  i2c: Drop dummy variable
  i2c: Move adapter locking helpers to i2c-core
  V4L/DVB: Use custom I2C probing function mechanism
  i2c: Add support for custom probe function
  i2c-dev: Use memdup_user
  i2c-dev: Remove unnecessary kmalloc casts
  • Loading branch information
torvalds committed Aug 12, 2010
2 parents 26df076 + 7f52813 commit 1c00650
Show file tree
Hide file tree
Showing 22 changed files with 805 additions and 116 deletions.
2 changes: 1 addition & 1 deletion Documentation/i2c/instantiating-devices
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
strlcpy(i2c_info.name, "isp1301_pnx", I2C_NAME_SIZE);
isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
normal_i2c);
normal_i2c, NULL);
i2c_put_adapter(i2c_adap);
(...)
}
Expand Down
13 changes: 13 additions & 0 deletions drivers/i2c/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ config I2C_CHARDEV
This support is also available as a module. If so, the module
will be called i2c-dev.

config I2C_MUX
tristate "I2C bus multiplexing support"
depends on EXPERIMENTAL
help
Say Y here if you want the I2C core to support the ability to
handle multiplexed I2C bus topologies, by presenting each
multiplexed segment as a I2C adapter.

This support is also available as a module. If so, the module
will be called i2c-mux.

source drivers/i2c/muxes/Kconfig

config I2C_HELPER_AUTO
bool "Autoselect pertinent helper modules"
default y
Expand Down
3 changes: 2 additions & 1 deletion drivers/i2c/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-y += algos/ busses/
obj-$(CONFIG_I2C_MUX) += i2c-mux.o
obj-y += algos/ busses/ muxes/

ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
EXTRA_CFLAGS += -DDEBUG
Expand Down
158 changes: 118 additions & 40 deletions drivers/i2c/i2c-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
/* 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>
Expand Down Expand Up @@ -423,11 +425,87 @@ 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;
}

/**
* i2c_lock_adapter - Get exclusive access to an I2C bus segment
* @adapter: Target I2C bus segment
*/
void i2c_lock_adapter(struct i2c_adapter *adapter)
{
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);

/**
* 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)
{
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);
}

/**
* i2c_unlock_adapter - Release exclusive access to an I2C bus segment
* @adapter: Target I2C bus segment
*/
void i2c_unlock_adapter(struct i2c_adapter *adapter)
{
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);

/**
* i2c_new_device - instantiate an i2c device
Expand Down Expand Up @@ -633,9 +711,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);

Expand Down Expand Up @@ -674,7 +752,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) {
Expand All @@ -687,7 +765,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",
Expand All @@ -714,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;
Expand Down Expand Up @@ -760,7 +839,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))) {
Expand All @@ -769,6 +848,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 */
Expand Down Expand Up @@ -801,8 +881,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;
Expand Down Expand Up @@ -975,15 +1054,15 @@ 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,
client->addr);
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. */
Expand Down Expand Up @@ -1238,12 +1317,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 */
Expand All @@ -1255,7 +1334,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 {
Expand Down Expand Up @@ -1350,13 +1429,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;
}
Expand Down Expand Up @@ -1437,16 +1520,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]);
Expand All @@ -1461,18 +1534,23 @@ 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,
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)
probe = i2c_default_probe;

for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
/* Check address validity */
Expand All @@ -1490,7 +1568,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;
}

Expand Down Expand Up @@ -2002,7 +2080,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;
Expand All @@ -2016,7 +2094,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);
Expand Down
Loading

0 comments on commit 1c00650

Please sign in to comment.