The patch titled i2c: Add support for virtual I2C adapters has been removed from the -mm tree. Its filename is i2c-add-support-for-virtual-i2c-adapters.patch This patch was probably dropped from -mm because it has now been merged into a subsystem tree or into Linus's tree, or because it was folded into its parent patch in the -mm tree. ------------------------------------------------------ Subject: i2c: Add support for virtual I2C adapters From: Kumar Gala <galak@xxxxxxxxxxxxxxxxxxx> Virtual adapters are useful to handle multiplexed I2C bus topologies, by presenting each multiplexed segment as a I2C adapter. Typically, either a mux (or switch) exists which is an I2C device on the parent bus. One selects a given child bus via programming the mux and then all the devices on that bus become present on the parent bus. The intent is to allow multiple devices of the same type to exist in a system which would normally have address conflicts. Since virtual adapters will get registered in an I2C client's detect function we have to expose versions of i2c_{add,del}_adapter for i2c_{add,del}_virt_adapter to call that don't lock. Additionally, i2c_virt_master_xfer (and i2c_virt_smbus_xfer) acquire the parent->bus_lock and call the parent's master_xfer directly. This is because on a i2c_virt_master_xfer we have issue an i2c write on the parent bus to select the given virtual adapter, then do the i2c operation on the parent bus, followed by another i2c write on the parent to deslect the virtual adapter. Signed-off-by: Kumar Gala <galak@xxxxxxxxxxxxxxxxxxx> Cc: Jean Delvare <khali@xxxxxxxxxxxx> Cc: Greg KH <greg@xxxxxxxxx> Signed-off-by: Andrew Morton <akpm@xxxxxxxx> --- drivers/i2c/Kconfig | 9 ++ drivers/i2c/Makefile | 1 drivers/i2c/i2c-core.c | 45 ++++++---- drivers/i2c/i2c-virt.c | 174 +++++++++++++++++++++++++++++++++++++++ include/linux/i2c-id.h | 2 include/linux/i2c.h | 20 ++++ 6 files changed, 237 insertions(+), 14 deletions(-) diff -puN drivers/i2c/i2c-core.c~i2c-add-support-for-virtual-i2c-adapters drivers/i2c/i2c-core.c --- devel/drivers/i2c/i2c-core.c~i2c-add-support-for-virtual-i2c-adapters 2006-04-22 01:39:11.000000000 -0700 +++ devel-akpm/drivers/i2c/i2c-core.c 2006-04-22 01:39:11.000000000 -0700 @@ -150,22 +150,31 @@ static struct device_attribute dev_attr_ */ int i2c_add_adapter(struct i2c_adapter *adap) { + int res; + + mutex_lock(&core_lists); + res = i2c_add_adapter_nolock(adap); + mutex_unlock(&core_lists); + + return res; +} + +int i2c_add_adapter_nolock(struct i2c_adapter *adap) +{ int id, 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; + goto out; } res = idr_get_new(&i2c_adapter_idr, adap, &id); if (res < 0) { if (res == -EAGAIN) res = -ENOMEM; - goto out_unlock; + goto out; } adap->nr = id & MAX_ID_MASK; @@ -203,21 +212,30 @@ int i2c_add_adapter(struct i2c_adapter * driver->attach_adapter(adap); } -out_unlock: - mutex_unlock(&core_lists); +out: return res; } - +EXPORT_SYMBOL_GPL(i2c_add_adapter_nolock); int i2c_del_adapter(struct i2c_adapter *adap) { + int res; + + mutex_lock(&core_lists); + res = i2c_del_adapter_nolock(adap); + mutex_unlock(&core_lists); + + return res; +} + +int i2c_del_adapter_nolock(struct i2c_adapter *adap) +{ struct list_head *item, *_n; struct i2c_adapter *adap_from_list; struct i2c_driver *driver; struct i2c_client *client; int res = 0; - mutex_lock(&core_lists); /* First make sure that this adapter was ever added */ list_for_each_entry(adap_from_list, &adapters, list) { @@ -228,7 +246,7 @@ int i2c_del_adapter(struct i2c_adapter * pr_debug("i2c-core: attempting to delete unregistered " "adapter [%s]\n", adap->name); res = -EINVAL; - goto out_unlock; + goto out; } list_for_each(item,&drivers) { @@ -238,7 +256,7 @@ int i2c_del_adapter(struct i2c_adapter * dev_err(&adap->dev, "detach_adapter failed " "for driver [%s]\n", driver->driver.name); - goto out_unlock; + goto out; } } @@ -251,7 +269,7 @@ int i2c_del_adapter(struct i2c_adapter * dev_err(&adap->dev, "detach_client failed for client " "[%s] at address 0x%02x\n", client->name, client->addr); - goto out_unlock; + goto out; } } @@ -272,11 +290,10 @@ int i2c_del_adapter(struct i2c_adapter * dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); - out_unlock: - mutex_unlock(&core_lists); +out: return res; } - +EXPORT_SYMBOL_GPL(i2c_del_adapter_nolock); /* ----- * What follows is the "upwards" interface: commands for talking to clients, diff -puN /dev/null drivers/i2c/i2c-virt.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/i2c/i2c-virt.c 2006-04-22 01:39:11.000000000 -0700 @@ -0,0 +1,174 @@ +/* + * i2c-virtual.c - Virtual I2C bus driver. + * + * Simplifies access to complex multiplexed I2C bus topologies, by presenting + * each multiplexed bus segment as a virtual I2C adapter. Supports multi-level + * mux'ing (mux behind a mux). + * + * Based on: + * i2c-virtual.c from Copyright (c) 2004 Google, Inc. (Ken Harrenstien) + * i2c-virtual.c from Brian Kuschak <bkuschak@xxxxxxxxx> + * which was: + * Adapted from i2c-adap-ibm_ocp.c + * Original file Copyright 2000-2002 MontaVista Software Inc. + * + * 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/i2c.h> +#include <linux/i2c-id.h> + +struct i2c_virt_priv { + struct i2c_adapter *parent_adap; + struct i2c_client *client; /* The mux chip/device */ + + u32 id; /* the mux id */ + + /* fn which enables the mux */ + int (*select) (struct i2c_adapter *, struct i2c_client *, u32); + + /* fn which disables the mux */ + int (*deselect) (struct i2c_adapter *, struct i2c_client *, u32); +}; + +#define VIRT_TIMEOUT (HZ/2) +#define VIRT_RETRIES 3 + +static int +i2c_virt_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct i2c_virt_priv *priv = adap->algo_data; + struct i2c_adapter *parent = priv->parent_adap; + int ret; + + /* Grab the lock for the parent adapter. We already hold the lock for + the virtual adapter. Then select the right mux port and perform + the transfer. + */ + + mutex_lock(&parent->bus_lock); + ret = priv->select(parent, priv->client, priv->id); + if (ret >= 0) + ret = parent->algo->master_xfer(parent, msgs, num); + priv->deselect(parent, priv->client, priv->id); + mutex_unlock(&parent->bus_lock); + + return ret; +} + +static int +i2c_virt_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_virt_priv *priv = adap->algo_data; + struct i2c_adapter *parent = priv->parent_adap; + int ret; + + /* Grab the lock for the parent adapter. We already hold the lock for + the virtual adapter. Then select the right mux port and perform + the transfer. + */ + + mutex_lock(&parent->bus_lock); + ret = priv->select(parent, priv->client, priv->id); + if (ret == 0) + ret = parent->algo->smbus_xfer(parent, addr, flags, + read_write, command, size, data); + priv->deselect(parent, priv->client, priv->id); + mutex_unlock(&parent->bus_lock); + + return ret; +} + +/* return the parent's functionality for the virtual adapter */ +static u32 i2c_virt_functionality(struct i2c_adapter *adap) +{ + struct i2c_virt_priv *priv = adap->algo_data; + struct i2c_adapter *parent = priv->parent_adap; + + return parent->algo->functionality(parent); +} + +struct i2c_adapter * +i2c_add_virt_adapter(struct i2c_adapter *parent, struct i2c_client *client, + u32 mux_val, + int (*select_cb) (struct i2c_adapter *, + struct i2c_client *, u32), + int (*deselect_cb) (struct i2c_adapter *, + struct i2c_client *, u32)) +{ + struct i2c_adapter *adap; + struct i2c_virt_priv *priv; + struct i2c_algorithm *algo; + + adap = kzalloc(sizeof(struct i2c_adapter) + sizeof(struct i2c_virt_priv) + + sizeof(struct i2c_algorithm), GFP_KERNEL); + if (!adap) + return NULL; + + priv = (struct i2c_virt_priv *)(adap + 1); + algo = (struct i2c_algorithm *)(priv + 1); + + /* Set up private adapter data */ + priv->parent_adap = parent; + priv->client = client; + priv->id = mux_val; + priv->select = select_cb; + priv->deselect = deselect_cb; + + /* Need to do algo dynamically because we don't know ahead + of time what sort of physical adapter we'll be dealing with. + */ + algo->master_xfer = (parent->algo->master_xfer + ? i2c_virt_master_xfer : NULL); + algo->smbus_xfer = (parent->algo->smbus_xfer + ? i2c_virt_smbus_xfer : NULL); + algo->functionality = i2c_virt_functionality; + + /* Now fill out new adapter structure */ + snprintf(adap->name, sizeof(adap->name), + "Virtual I2C (i2c-%d, mux %02x:%02x)", + i2c_adapter_id(parent), client->addr, mux_val); + adap->id = I2C_HW_VIRT | i2c_adapter_id(parent); + adap->algo = algo; + adap->algo_data = priv; + adap->timeout = VIRT_TIMEOUT; + adap->retries = VIRT_RETRIES; + adap->dev.parent = &parent->dev; + + if (i2c_add_adapter_nolock(adap) < 0) { + kfree(adap); + return NULL; + } + + printk(KERN_NOTICE "i2c-%d: Virtual I2C bus " + "(Physical bus i2c-%d, multiplexer 0x%02x port %d)\n", + i2c_adapter_id(adap), i2c_adapter_id(parent), + client->addr, mux_val); + + return adap; +} + +int i2c_del_virt_adapter(struct i2c_adapter *adap) +{ + int ret; + + ret = i2c_del_adapter_nolock(adap); + if (ret < 0) + return ret; + kfree(adap); + + return 0; +} + +EXPORT_SYMBOL_GPL(i2c_add_virt_adapter); +EXPORT_SYMBOL_GPL(i2c_del_virt_adapter); + +MODULE_AUTHOR("Kumar Gala <galak@xxxxxxxxxxxxxxxxxxx>"); +MODULE_DESCRIPTION("Virtual I2C driver for multiplexed I2C busses"); +MODULE_LICENSE("GPL"); diff -puN drivers/i2c/Kconfig~i2c-add-support-for-virtual-i2c-adapters drivers/i2c/Kconfig --- devel/drivers/i2c/Kconfig~i2c-add-support-for-virtual-i2c-adapters 2006-04-22 01:39:11.000000000 -0700 +++ devel-akpm/drivers/i2c/Kconfig 2006-04-22 01:39:11.000000000 -0700 @@ -34,6 +34,15 @@ config I2C_CHARDEV This support is also available as a module. If so, the module will be called i2c-dev. +config I2C_VIRT + tristate "I2C virtual adapter support" + depends on I2C + help + Say Y here if you want the I2C core to support the ability to have + virtual adapters. Virtual adapters are useful to handle multiplexed + I2C bus topologies, by presenting each multiplexed segment as a + I2C adapter. + source drivers/i2c/algos/Kconfig source drivers/i2c/busses/Kconfig source drivers/i2c/chips/Kconfig diff -puN drivers/i2c/Makefile~i2c-add-support-for-virtual-i2c-adapters drivers/i2c/Makefile --- devel/drivers/i2c/Makefile~i2c-add-support-for-virtual-i2c-adapters 2006-04-22 01:39:11.000000000 -0700 +++ devel-akpm/drivers/i2c/Makefile 2006-04-22 01:39:11.000000000 -0700 @@ -3,6 +3,7 @@ # obj-$(CONFIG_I2C) += i2c-core.o +obj-$(CONFIG_I2C_VIRT) += i2c-virt.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-y += busses/ chips/ algos/ diff -puN include/linux/i2c.h~i2c-add-support-for-virtual-i2c-adapters include/linux/i2c.h --- devel/include/linux/i2c.h~i2c-add-support-for-virtual-i2c-adapters 2006-04-22 01:39:11.000000000 -0700 +++ devel-akpm/include/linux/i2c.h 2006-04-22 01:39:11.000000000 -0700 @@ -294,6 +294,10 @@ struct i2c_client_address_data { extern int i2c_add_adapter(struct i2c_adapter *); extern int i2c_del_adapter(struct i2c_adapter *); +/* Assume the caller has the core_list lock already */ +extern int i2c_add_adapter_nolock(struct i2c_adapter *); +extern int i2c_del_adapter_nolock(struct i2c_adapter *); + extern int i2c_register_driver(struct module *, struct i2c_driver *); extern int i2c_del_driver(struct i2c_driver *); @@ -440,6 +444,22 @@ union i2c_smbus_data { #define I2C_SMBUS_I2C_BLOCK_DATA 6 #define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ +/* + * Called to create a 'virtual' i2c bus which represents a multiplexed bus + * segment. The client and mux_val are passed to the select and deselect + * callback functions to perform hardware-specific mux control. + * + * The caller is expected to have the core_lists lock + */ +struct i2c_adapter * +i2c_add_virt_adapter(struct i2c_adapter *parent, struct i2c_client *client, + u32 mux_val, + int (*select_cb) (struct i2c_adapter *, + struct i2c_client *, u32), + int (*deselect_cb) (struct i2c_adapter *, + struct i2c_client *, u32)); + +int i2c_del_virt_adapter(struct i2c_adapter *adap); /* ----- commands for the ioctl like i2c_command call: * note that additional calls are defined in the algorithm and hw diff -puN include/linux/i2c-id.h~i2c-add-support-for-virtual-i2c-adapters include/linux/i2c-id.h --- devel/include/linux/i2c-id.h~i2c-add-support-for-virtual-i2c-adapters 2006-04-22 01:39:11.000000000 -0700 +++ devel-akpm/include/linux/i2c-id.h 2006-04-22 01:39:11.000000000 -0700 @@ -267,4 +267,6 @@ #define I2C_HW_SAA7146 0x060000 /* SAA7146 video decoder bus */ #define I2C_HW_SAA7134 0x090000 /* SAA7134 video decoder bus */ +#define I2C_HW_VIRT 0x80000000 /* a virtual adapter */ + #endif /* LINUX_I2C_ID_H */ _ Patches currently in -mm which might be from galak@xxxxxxxxxxxxxxxxxxx are i2c-pca954x-i2c-mux-driver.patch i2c-mpc-fix-up-error-handling.patch i2c-pca954x-fix-initial-access-to-first-mux-switch-port.patch git-powerpc.patch - To unsubscribe from this list: send the line "unsubscribe mm-commits" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html