Merge branch 'i2c-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/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
This commit is contained in:
commit
1c00650c23
22 changed files with 805 additions and 116 deletions
|
@ -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);
|
||||
(...)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
@ -423,12 +425,88 @@ 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
|
||||
* @adap: the adapter managing the device
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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))) {
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -975,7 +1054,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,
|
||||
|
@ -983,7 +1062,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. */
|
||||
|
@ -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 */
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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]);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
|
|
@ -167,13 +167,9 @@ static ssize_t i2cdev_write(struct file *file, const char __user *buf,
|
|||
if (count > 8192)
|
||||
count = 8192;
|
||||
|
||||
tmp = kmalloc(count, GFP_KERNEL);
|
||||
if (tmp == NULL)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(tmp, buf, count)) {
|
||||
kfree(tmp);
|
||||
return -EFAULT;
|
||||
}
|
||||
tmp = memdup_user(buf, count);
|
||||
if (IS_ERR(tmp))
|
||||
return PTR_ERR(tmp);
|
||||
|
||||
pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
|
||||
iminor(file->f_path.dentry->d_inode), count);
|
||||
|
@ -193,12 +189,50 @@ static int i2cdev_check(struct device *dev, void *addrp)
|
|||
return dev->driver ? -EBUSY : 0;
|
||||
}
|
||||
|
||||
/* walk up mux tree */
|
||||
static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = device_for_each_child(&adapter->dev, &addr, i2cdev_check);
|
||||
|
||||
if (!result && i2c_parent_is_i2c_adapter(adapter))
|
||||
result = i2cdev_check_mux_parents(
|
||||
to_i2c_adapter(adapter->dev.parent), addr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* recurse down mux tree */
|
||||
static int i2cdev_check_mux_children(struct device *dev, void *addrp)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (dev->type == &i2c_adapter_type)
|
||||
result = device_for_each_child(dev, addrp,
|
||||
i2cdev_check_mux_children);
|
||||
else
|
||||
result = i2cdev_check(dev, addrp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* This address checking function differs from the one in i2c-core
|
||||
in that it considers an address with a registered device, but no
|
||||
driver bound to it, as NOT busy. */
|
||||
static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr)
|
||||
{
|
||||
return device_for_each_child(&adapter->dev, &addr, i2cdev_check);
|
||||
int result = 0;
|
||||
|
||||
if (i2c_parent_is_i2c_adapter(adapter))
|
||||
result = i2cdev_check_mux_parents(
|
||||
to_i2c_adapter(adapter->dev.parent), addr);
|
||||
|
||||
if (!result)
|
||||
result = device_for_each_child(&adapter->dev, &addr,
|
||||
i2cdev_check_mux_children);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
|
||||
|
@ -219,9 +253,7 @@ static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
|
|||
if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
|
||||
return -EINVAL;
|
||||
|
||||
rdwr_pa = (struct i2c_msg *)
|
||||
kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
|
||||
GFP_KERNEL);
|
||||
rdwr_pa = kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);
|
||||
if (!rdwr_pa)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -247,15 +279,9 @@ static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
|
|||
break;
|
||||
}
|
||||
data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
|
||||
rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
|
||||
if (rdwr_pa[i].buf == NULL) {
|
||||
res = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i],
|
||||
rdwr_pa[i].len)) {
|
||||
++i; /* Needs to be kfreed too */
|
||||
res = -EFAULT;
|
||||
rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);
|
||||
if (IS_ERR(rdwr_pa[i].buf)) {
|
||||
res = PTR_ERR(rdwr_pa[i].buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
165
drivers/i2c/i2c-mux.c
Normal file
165
drivers/i2c/i2c-mux.c
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Multiplexed I2C bus driver.
|
||||
*
|
||||
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
|
||||
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
|
||||
* Copyright (c) 2009-2010 NSN GmbH & Co KG <michael.lawnick.ext@nsn.com>
|
||||
*
|
||||
* Simplifies access to complex multiplexed I2C bus topologies, by presenting
|
||||
* each multiplexed bus segment as an additional I2C adapter.
|
||||
* Supports multi-level mux'ing (mux behind a mux).
|
||||
*
|
||||
* Based on:
|
||||
* i2c-virt.c from Kumar Gala <galak@kernel.crashing.org>
|
||||
* i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc.
|
||||
* i2c-virtual.c from Brian Kuschak <bkuschak@yahoo.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
|
||||
/* multiplexer per channel data */
|
||||
struct i2c_mux_priv {
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_algorithm algo;
|
||||
|
||||
struct i2c_adapter *parent;
|
||||
void *mux_dev; /* the mux chip/device */
|
||||
u32 chan_id; /* the channel id */
|
||||
|
||||
int (*select)(struct i2c_adapter *, void *mux_dev, u32 chan_id);
|
||||
int (*deselect)(struct i2c_adapter *, void *mux_dev, u32 chan_id);
|
||||
};
|
||||
|
||||
static int i2c_mux_master_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg msgs[], int num)
|
||||
{
|
||||
struct i2c_mux_priv *priv = adap->algo_data;
|
||||
struct i2c_adapter *parent = priv->parent;
|
||||
int ret;
|
||||
|
||||
/* Switch to the right mux port and perform the transfer. */
|
||||
|
||||
ret = priv->select(parent, priv->mux_dev, priv->chan_id);
|
||||
if (ret >= 0)
|
||||
ret = parent->algo->master_xfer(parent, msgs, num);
|
||||
if (priv->deselect)
|
||||
priv->deselect(parent, priv->mux_dev, priv->chan_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
|
||||
u16 addr, unsigned short flags,
|
||||
char read_write, u8 command,
|
||||
int size, union i2c_smbus_data *data)
|
||||
{
|
||||
struct i2c_mux_priv *priv = adap->algo_data;
|
||||
struct i2c_adapter *parent = priv->parent;
|
||||
int ret;
|
||||
|
||||
/* Select the right mux port and perform the transfer. */
|
||||
|
||||
ret = priv->select(parent, priv->mux_dev, priv->chan_id);
|
||||
if (ret >= 0)
|
||||
ret = parent->algo->smbus_xfer(parent, addr, flags,
|
||||
read_write, command, size, data);
|
||||
if (priv->deselect)
|
||||
priv->deselect(parent, priv->mux_dev, priv->chan_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Return the parent's functionality */
|
||||
static u32 i2c_mux_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
struct i2c_mux_priv *priv = adap->algo_data;
|
||||
struct i2c_adapter *parent = priv->parent;
|
||||
|
||||
return parent->algo->functionality(parent);
|
||||
}
|
||||
|
||||
struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
||||
void *mux_dev, u32 force_nr, u32 chan_id,
|
||||
int (*select) (struct i2c_adapter *,
|
||||
void *, u32),
|
||||
int (*deselect) (struct i2c_adapter *,
|
||||
void *, u32))
|
||||
{
|
||||
struct i2c_mux_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return NULL;
|
||||
|
||||
/* Set up private adapter data */
|
||||
priv->parent = parent;
|
||||
priv->mux_dev = mux_dev;
|
||||
priv->chan_id = chan_id;
|
||||
priv->select = select;
|
||||
priv->deselect = deselect;
|
||||
|
||||
/* Need to do algo dynamically because we don't know ahead
|
||||
* of time what sort of physical adapter we'll be dealing with.
|
||||
*/
|
||||
if (parent->algo->master_xfer)
|
||||
priv->algo.master_xfer = i2c_mux_master_xfer;
|
||||
if (parent->algo->smbus_xfer)
|
||||
priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
|
||||
priv->algo.functionality = i2c_mux_functionality;
|
||||
|
||||
/* Now fill out new adapter structure */
|
||||
snprintf(priv->adap.name, sizeof(priv->adap.name),
|
||||
"i2c-%d-mux (chan_id %d)", i2c_adapter_id(parent), chan_id);
|
||||
priv->adap.owner = THIS_MODULE;
|
||||
priv->adap.id = parent->id;
|
||||
priv->adap.algo = &priv->algo;
|
||||
priv->adap.algo_data = priv;
|
||||
priv->adap.dev.parent = &parent->dev;
|
||||
|
||||
if (force_nr) {
|
||||
priv->adap.nr = force_nr;
|
||||
ret = i2c_add_numbered_adapter(&priv->adap);
|
||||
} else {
|
||||
ret = i2c_add_adapter(&priv->adap);
|
||||
}
|
||||
if (ret < 0) {
|
||||
dev_err(&parent->dev,
|
||||
"failed to add mux-adapter (error=%d)\n",
|
||||
ret);
|
||||
kfree(priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
|
||||
i2c_adapter_id(&priv->adap));
|
||||
|
||||
return &priv->adap;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_add_mux_adapter);
|
||||
|
||||
int i2c_del_mux_adapter(struct i2c_adapter *adap)
|
||||
{
|
||||
struct i2c_mux_priv *priv = adap->algo_data;
|
||||
int ret;
|
||||
|
||||
ret = i2c_del_adapter(adap);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_del_mux_adapter);
|
||||
|
||||
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
|
||||
MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses");
|
||||
MODULE_LICENSE("GPL v2");
|
18
drivers/i2c/muxes/Kconfig
Normal file
18
drivers/i2c/muxes/Kconfig
Normal file
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# Multiplexer I2C chip drivers configuration
|
||||
#
|
||||
|
||||
menu "Multiplexer I2C Chip support"
|
||||
depends on I2C_MUX
|
||||
|
||||
config I2C_MUX_PCA954x
|
||||
tristate "Philips PCA954x I2C Mux/switches"
|
||||
depends on EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for the Philips PCA954x
|
||||
I2C mux/switch devices.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called pca954x.
|
||||
|
||||
endmenu
|
8
drivers/i2c/muxes/Makefile
Normal file
8
drivers/i2c/muxes/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# Makefile for multiplexer I2C chip drivers.
|
||||
|
||||
obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o
|
||||
|
||||
ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
301
drivers/i2c/muxes/pca954x.c
Normal file
301
drivers/i2c/muxes/pca954x.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* I2C multiplexer
|
||||
*
|
||||
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
|
||||
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
|
||||
*
|
||||
* This module supports the PCA954x series of I2C multiplexer/switch chips
|
||||
* made by Philips Semiconductors.
|
||||
* This includes the:
|
||||
* PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547
|
||||
* and PCA9548.
|
||||
*
|
||||
* These chips are all controlled via the I2C bus itself, and all have a
|
||||
* single 8-bit register. The upstream "parent" bus fans out to two,
|
||||
* four, or eight downstream busses or channels; which of these
|
||||
* are selected is determined by the chip type and register contents. A
|
||||
* mux can select only one sub-bus at a time; a switch can select any
|
||||
* combination simultaneously.
|
||||
*
|
||||
* Based on:
|
||||
* pca954x.c from Kumar Gala <galak@kernel.crashing.org>
|
||||
* Copyright (C) 2006
|
||||
*
|
||||
* Based on:
|
||||
* pca954x.c from Ken Harrenstien
|
||||
* Copyright (C) 2004 Google, Inc. (Ken Harrenstien)
|
||||
*
|
||||
* Based on:
|
||||
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
|
||||
* and
|
||||
* pca9540.c from Jean Delvare <khali@linux-fr.org>.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
|
||||
#include <linux/i2c/pca954x.h>
|
||||
|
||||
#define PCA954X_MAX_NCHANS 8
|
||||
|
||||
enum pca_type {
|
||||
pca_9540,
|
||||
pca_9542,
|
||||
pca_9543,
|
||||
pca_9544,
|
||||
pca_9545,
|
||||
pca_9546,
|
||||
pca_9547,
|
||||
pca_9548,
|
||||
};
|
||||
|
||||
struct pca954x {
|
||||
enum pca_type type;
|
||||
struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
|
||||
|
||||
u8 last_chan; /* last register value */
|
||||
};
|
||||
|
||||
struct chip_desc {
|
||||
u8 nchans;
|
||||
u8 enable; /* used for muxes only */
|
||||
enum muxtype {
|
||||
pca954x_ismux = 0,
|
||||
pca954x_isswi
|
||||
} muxtype;
|
||||
};
|
||||
|
||||
/* Provide specs for the PCA954x types we know about */
|
||||
static const struct chip_desc chips[] = {
|
||||
[pca_9540] = {
|
||||
.nchans = 2,
|
||||
.enable = 0x4,
|
||||
.muxtype = pca954x_ismux,
|
||||
},
|
||||
[pca_9543] = {
|
||||
.nchans = 2,
|
||||
.muxtype = pca954x_isswi,
|
||||
},
|
||||
[pca_9544] = {
|
||||
.nchans = 4,
|
||||
.enable = 0x4,
|
||||
.muxtype = pca954x_ismux,
|
||||
},
|
||||
[pca_9545] = {
|
||||
.nchans = 4,
|
||||
.muxtype = pca954x_isswi,
|
||||
},
|
||||
[pca_9547] = {
|
||||
.nchans = 8,
|
||||
.enable = 0x8,
|
||||
.muxtype = pca954x_ismux,
|
||||
},
|
||||
[pca_9548] = {
|
||||
.nchans = 8,
|
||||
.muxtype = pca954x_isswi,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct i2c_device_id pca954x_id[] = {
|
||||
{ "pca9540", pca_9540 },
|
||||
{ "pca9542", pca_9540 },
|
||||
{ "pca9543", pca_9543 },
|
||||
{ "pca9544", pca_9544 },
|
||||
{ "pca9545", pca_9545 },
|
||||
{ "pca9546", pca_9545 },
|
||||
{ "pca9547", pca_9547 },
|
||||
{ "pca9548", pca_9548 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pca954x_id);
|
||||
|
||||
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
|
||||
for this as they will try to lock adapter a second time */
|
||||
static int pca954x_reg_write(struct i2c_adapter *adap,
|
||||
struct i2c_client *client, u8 val)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (adap->algo->master_xfer) {
|
||||
struct i2c_msg msg;
|
||||
char buf[1];
|
||||
|
||||
msg.addr = client->addr;
|
||||
msg.flags = 0;
|
||||
msg.len = 1;
|
||||
buf[0] = val;
|
||||
msg.buf = buf;
|
||||
ret = adap->algo->master_xfer(adap, &msg, 1);
|
||||
} else {
|
||||
union i2c_smbus_data data;
|
||||
ret = adap->algo->smbus_xfer(adap, client->addr,
|
||||
client->flags,
|
||||
I2C_SMBUS_WRITE,
|
||||
val, I2C_SMBUS_BYTE, &data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pca954x_select_chan(struct i2c_adapter *adap,
|
||||
void *client, u32 chan)
|
||||
{
|
||||
struct pca954x *data = i2c_get_clientdata(client);
|
||||
const struct chip_desc *chip = &chips[data->type];
|
||||
u8 regval;
|
||||
int ret = 0;
|
||||
|
||||
/* we make switches look like muxes, not sure how to be smarter */
|
||||
if (chip->muxtype == pca954x_ismux)
|
||||
regval = chan | chip->enable;
|
||||
else
|
||||
regval = 1 << chan;
|
||||
|
||||
/* Only select the channel if its different from the last channel */
|
||||
if (data->last_chan != regval) {
|
||||
ret = pca954x_reg_write(adap, client, regval);
|
||||
data->last_chan = regval;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pca954x_deselect_mux(struct i2c_adapter *adap,
|
||||
void *client, u32 chan)
|
||||
{
|
||||
struct pca954x *data = i2c_get_clientdata(client);
|
||||
|
||||
/* Deselect active channel */
|
||||
data->last_chan = 0;
|
||||
return pca954x_reg_write(adap, client, data->last_chan);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C init/probing/exit functions
|
||||
*/
|
||||
static int __devinit pca954x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
|
||||
struct pca954x_platform_data *pdata = client->dev.platform_data;
|
||||
int num, force;
|
||||
struct pca954x *data;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
|
||||
goto err;
|
||||
|
||||
data = kzalloc(sizeof(struct pca954x), GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
/* Read the mux register at addr to verify
|
||||
* that the mux is in fact present.
|
||||
*/
|
||||
if (i2c_smbus_read_byte(client) < 0) {
|
||||
dev_warn(&client->dev, "probe failed\n");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
data->type = id->driver_data;
|
||||
data->last_chan = 0; /* force the first selection */
|
||||
|
||||
/* Now create an adapter for each channel */
|
||||
for (num = 0; num < chips[data->type].nchans; num++) {
|
||||
force = 0; /* dynamic adap number */
|
||||
if (pdata) {
|
||||
if (num < pdata->num_modes)
|
||||
/* force static number */
|
||||
force = pdata->modes[num].adap_id;
|
||||
else
|
||||
/* discard unconfigured channels */
|
||||
break;
|
||||
}
|
||||
|
||||
data->virt_adaps[num] =
|
||||
i2c_add_mux_adapter(adap, client,
|
||||
force, num, pca954x_select_chan,
|
||||
(pdata && pdata->modes[num].deselect_on_exit)
|
||||
? pca954x_deselect_mux : NULL);
|
||||
|
||||
if (data->virt_adaps[num] == NULL) {
|
||||
ret = -ENODEV;
|
||||
dev_err(&client->dev,
|
||||
"failed to register multiplexed adapter"
|
||||
" %d as bus %d\n", num, force);
|
||||
goto virt_reg_failed;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&client->dev,
|
||||
"registered %d multiplexed busses for I2C %s %s\n",
|
||||
num, chips[data->type].muxtype == pca954x_ismux
|
||||
? "mux" : "switch", client->name);
|
||||
|
||||
return 0;
|
||||
|
||||
virt_reg_failed:
|
||||
for (num--; num >= 0; num--)
|
||||
i2c_del_mux_adapter(data->virt_adaps[num]);
|
||||
exit_free:
|
||||
kfree(data);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit pca954x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pca954x *data = i2c_get_clientdata(client);
|
||||
const struct chip_desc *chip = &chips[data->type];
|
||||
int i, err;
|
||||
|
||||
for (i = 0; i < chip->nchans; ++i)
|
||||
if (data->virt_adaps[i]) {
|
||||
err = i2c_del_mux_adapter(data->virt_adaps[i]);
|
||||
if (err)
|
||||
return err;
|
||||
data->virt_adaps[i] = NULL;
|
||||
}
|
||||
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver pca954x_driver = {
|
||||
.driver = {
|
||||
.name = "pca954x",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = pca954x_probe,
|
||||
.remove = __devexit_p(pca954x_remove),
|
||||
.id_table = pca954x_id,
|
||||
};
|
||||
|
||||
static int __init pca954x_init(void)
|
||||
{
|
||||
return i2c_add_driver(&pca954x_driver);
|
||||
}
|
||||
|
||||
static void __exit pca954x_exit(void)
|
||||
{
|
||||
i2c_del_driver(&pca954x_driver);
|
||||
}
|
||||
|
||||
module_init(pca954x_init);
|
||||
module_exit(pca954x_exit);
|
||||
|
||||
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
|
||||
MODULE_DESCRIPTION("PCA954x I2C mux/switch driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -322,10 +322,10 @@ do_attach( struct i2c_adapter *adapter )
|
|||
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
strlcpy(info.type, "therm_ds1775", I2C_NAME_SIZE);
|
||||
i2c_new_probed_device(adapter, &info, scan_ds1775);
|
||||
i2c_new_probed_device(adapter, &info, scan_ds1775, NULL);
|
||||
|
||||
strlcpy(info.type, "therm_adm1030", I2C_NAME_SIZE);
|
||||
i2c_new_probed_device(adapter, &info, scan_adm1030);
|
||||
i2c_new_probed_device(adapter, &info, scan_adm1030, NULL);
|
||||
|
||||
if( x.thermostat && x.fan ) {
|
||||
x.running = 1;
|
||||
|
|
|
@ -411,7 +411,7 @@ void __devinit init_bttv_i2c_ir(struct bttv *btv)
|
|||
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
|
||||
i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list);
|
||||
i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -117,7 +117,8 @@ static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw,
|
|||
break;
|
||||
}
|
||||
|
||||
return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0;
|
||||
return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ?
|
||||
-1 : 0;
|
||||
}
|
||||
|
||||
int cx18_i2c_register(struct cx18 *cx, unsigned idx)
|
||||
|
|
|
@ -364,17 +364,10 @@ int cx23885_i2c_register(struct cx23885_i2c *bus)
|
|||
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
|
||||
/*
|
||||
* We can't call i2c_new_probed_device() because it uses
|
||||
* quick writes for probing and the IR receiver device only
|
||||
* replies to reads.
|
||||
*/
|
||||
if (i2c_smbus_xfer(&bus->i2c_adap, addr_list[0], 0,
|
||||
I2C_SMBUS_READ, 0, I2C_SMBUS_QUICK,
|
||||
NULL) >= 0) {
|
||||
info.addr = addr_list[0];
|
||||
i2c_new_device(&bus->i2c_adap, &info);
|
||||
}
|
||||
/* Use quick read command for probe, some IR chips don't
|
||||
* support writes */
|
||||
i2c_new_probed_device(&bus->i2c_adap, &info, addr_list,
|
||||
i2c_probe_func_quick_read);
|
||||
}
|
||||
|
||||
return bus->i2c_rc;
|
||||
|
|
|
@ -193,24 +193,13 @@ void cx88_i2c_init_ir(struct cx88_core *core)
|
|||
0x18, 0x6b, 0x71,
|
||||
I2C_CLIENT_END
|
||||
};
|
||||
const unsigned short *addrp;
|
||||
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
|
||||
/*
|
||||
* We can't call i2c_new_probed_device() because it uses
|
||||
* quick writes for probing and at least some R receiver
|
||||
* devices only reply to reads.
|
||||
*/
|
||||
for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) {
|
||||
if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0,
|
||||
I2C_SMBUS_READ, 0,
|
||||
I2C_SMBUS_QUICK, NULL) >= 0) {
|
||||
info.addr = *addrp;
|
||||
i2c_new_device(&core->i2c_adap, &info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Use quick read command for probe, some IR chips don't
|
||||
* support writes */
|
||||
i2c_new_probed_device(&core->i2c_adap, &info, addr_list,
|
||||
i2c_probe_func_quick_read);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2385,7 +2385,7 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
|
|||
|
||||
if (dev->init_data.name)
|
||||
info.platform_data = &dev->init_data;
|
||||
i2c_new_probed_device(&dev->i2c_adap, &info, addr_list);
|
||||
i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL);
|
||||
}
|
||||
|
||||
void em28xx_card_setup(struct em28xx *dev)
|
||||
|
|
|
@ -183,8 +183,8 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr)
|
|||
return -1;
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
strlcpy(info.type, type, I2C_NAME_SIZE);
|
||||
return i2c_new_probed_device(adap, &info, addr_list) == NULL
|
||||
? -1 : 0;
|
||||
return i2c_new_probed_device(adap, &info, addr_list, NULL)
|
||||
== NULL ? -1 : 0;
|
||||
}
|
||||
|
||||
/* Only allow one IR receiver to be registered per board */
|
||||
|
@ -221,7 +221,8 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr)
|
|||
info.platform_data = init_data;
|
||||
strlcpy(info.type, type, I2C_NAME_SIZE);
|
||||
|
||||
return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0;
|
||||
return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ?
|
||||
-1 : 0;
|
||||
}
|
||||
|
||||
/* Instantiate the IR receiver device using probing -- undesirable */
|
||||
|
@ -249,7 +250,7 @@ struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv)
|
|||
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
|
||||
return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list);
|
||||
return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list, NULL);
|
||||
}
|
||||
|
||||
int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
|
||||
|
|
|
@ -381,7 +381,8 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
|
|||
|
||||
/* Create the i2c client */
|
||||
if (info->addr == 0 && probe_addrs)
|
||||
client = i2c_new_probed_device(adapter, info, probe_addrs);
|
||||
client = i2c_new_probed_device(adapter, info, probe_addrs,
|
||||
NULL);
|
||||
else
|
||||
client = i2c_new_device(adapter, info);
|
||||
|
||||
|
|
|
@ -329,7 +329,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
|
|||
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
|
||||
strlcpy(i2c_info.type, "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);
|
||||
if (!isp1301_i2c_client) {
|
||||
err("failed to connect I2C to ISP1301 USB Transceiver");
|
||||
|
|
|
@ -191,7 +191,7 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) {
|
|||
};
|
||||
|
||||
i2c_new_probed_device(&m2info->maven.adapter,
|
||||
&maven_info, addr_list);
|
||||
&maven_info, addr_list, NULL);
|
||||
}
|
||||
}
|
||||
return m2info;
|
||||
|
|
46
include/linux/i2c-mux.h
Normal file
46
include/linux/i2c-mux.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
*
|
||||
* i2c-mux.h - functions for the i2c-bus mux support
|
||||
*
|
||||
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
|
||||
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
|
||||
* Michael Lawnick <michael.lawnick.ext@nsn.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_I2C_MUX_H
|
||||
#define _LINUX_I2C_MUX_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/*
|
||||
* Called to create a i2c bus on a multiplexed bus segment.
|
||||
* The mux_dev and chan_id parameters are passed to the select
|
||||
* and deselect callback functions to perform hardware-specific
|
||||
* mux control.
|
||||
*/
|
||||
struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
||||
void *mux_dev, u32 force_nr, u32 chan_id,
|
||||
int (*select) (struct i2c_adapter *,
|
||||
void *mux_dev, u32 chan_id),
|
||||
int (*deselect) (struct i2c_adapter *,
|
||||
void *mux_dev, u32 chan_id));
|
||||
|
||||
int i2c_del_mux_adapter(struct i2c_adapter *adap);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _LINUX_I2C_MUX_H */
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/of.h> /* for struct device_node */
|
||||
|
||||
extern struct bus_type i2c_bus_type;
|
||||
extern struct device_type i2c_adapter_type;
|
||||
|
||||
/* --- General options ------------------------------------------------ */
|
||||
|
||||
|
@ -284,12 +285,18 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);
|
|||
|
||||
/* If you don't know the exact address of an I2C device, use this variant
|
||||
* instead, which can probe for device presence in a list of possible
|
||||
* addresses.
|
||||
* addresses. The "probe" callback function is optional. If it is provided,
|
||||
* it must return 1 on successful probe, 0 otherwise. If it is not provided,
|
||||
* a default probing method is used.
|
||||
*/
|
||||
extern 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));
|
||||
|
||||
/* Common custom probe functions */
|
||||
extern int i2c_probe_func_quick_read(struct i2c_adapter *, unsigned short addr);
|
||||
|
||||
/* For devices that use several addresses, use i2c_new_dummy() to make
|
||||
* client handles for the extra addresses.
|
||||
|
@ -362,6 +369,7 @@ struct i2c_adapter {
|
|||
char name[48];
|
||||
struct completion dev_released;
|
||||
|
||||
struct mutex userspace_clients_lock;
|
||||
struct list_head userspace_clients;
|
||||
};
|
||||
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
|
||||
|
@ -376,23 +384,16 @@ static inline void i2c_set_adapdata(struct i2c_adapter *dev, void *data)
|
|||
dev_set_drvdata(&dev->dev, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_lock_adapter - Prevent access to an I2C bus segment
|
||||
* @adapter: Target I2C bus segment
|
||||
*/
|
||||
static inline void i2c_lock_adapter(struct i2c_adapter *adapter)
|
||||
static inline int i2c_parent_is_i2c_adapter(const struct i2c_adapter *adapter)
|
||||
{
|
||||
rt_mutex_lock(&adapter->bus_lock);
|
||||
return adapter->dev.parent != NULL
|
||||
&& adapter->dev.parent->bus == &i2c_bus_type
|
||||
&& adapter->dev.parent->type == &i2c_adapter_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_unlock_adapter - Reauthorize access to an I2C bus segment
|
||||
* @adapter: Target I2C bus segment
|
||||
*/
|
||||
static inline void i2c_unlock_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
rt_mutex_unlock(&adapter->bus_lock);
|
||||
}
|
||||
/* Adapter locking functions, exported for shared pin cases */
|
||||
void i2c_lock_adapter(struct i2c_adapter *);
|
||||
void i2c_unlock_adapter(struct i2c_adapter *);
|
||||
|
||||
/*flags for the client struct: */
|
||||
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
|
||||
|
|
47
include/linux/i2c/pca954x.h
Normal file
47
include/linux/i2c/pca954x.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
*
|
||||
* pca954x.h - I2C multiplexer/switch support
|
||||
*
|
||||
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
|
||||
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
|
||||
* Michael Lawnick <michael.lawnick.ext@nsn.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _LINUX_I2C_PCA954X_H
|
||||
#define _LINUX_I2C_PCA954X_H
|
||||
|
||||
/* Platform data for the PCA954x I2C multiplexers */
|
||||
|
||||
/* Per channel initialisation data:
|
||||
* @adap_id: bus number for the adapter. 0 = don't care
|
||||
* @deselect_on_exit: set this entry to 1, if your H/W needs deselection
|
||||
* of this channel after transaction.
|
||||
*
|
||||
*/
|
||||
struct pca954x_platform_mode {
|
||||
int adap_id;
|
||||
unsigned int deselect_on_exit:1;
|
||||
};
|
||||
|
||||
/* Per mux/switch data, used with i2c_register_board_info */
|
||||
struct pca954x_platform_data {
|
||||
struct pca954x_platform_mode *modes;
|
||||
int num_modes;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_I2C_PCA954X_H */
|
Loading…
Reference in a new issue