lkml.org 
[lkml]   [2006]   [Nov]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: [PATCH][UPDATE] i2c: Add support for virtual I2C adapters
Is there a reason why the files and config options have been renamed
from i2c-virtual to i2c-virt?

On 4/7/06, Kumar Gala <galak@kernel.crashing.org> wrote:
> Any comments or acceptance of this patch?
>
> - k
>
> On Mar 30, 2006, at 5:05 PM, Kumar Gala wrote:
>
> > 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@kernel.crashing.org>
> >
> > ---
> > commit 862cbc263e3d3e44028d7465a912847cf5366163
> > tree 2c91bad8eb66cab9727f3071831a916ada41edf8
> > parent 5d4fe2c1ce83c3e967ccc1ba3d580c1a5603a866
> > author Kumar Gala <galak@kernel.crashing.org> Thu, 30 Mar 2006
> > 17:03:42 -0600
> > committer Kumar Gala <galak@kernel.crashing.org> Thu, 30 Mar 2006
> > 17:03:42 -0600
> >
> > drivers/i2c/Kconfig | 9 ++
> > drivers/i2c/Makefile | 1
> > drivers/i2c/i2c-core.c | 42 ++++++++----
> > drivers/i2c/i2c-virt.c | 173 +++++++++++++++++++++++++++++++++++++
> > +++++++++++
> > include/linux/i2c-id.h | 2 +
> > include/linux/i2c.h | 20 ++++++
> > 6 files changed, 234 insertions(+), 13 deletions(-)
> >
> > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> > index 24383af..b8a8fc1 100644
> > --- a/drivers/i2c/Kconfig
> > +++ b/drivers/i2c/Kconfig
> > @@ -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 --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> > index 71c5a85..4467db2 100644
> > --- a/drivers/i2c/Makefile
> > +++ b/drivers/i2c/Makefile
> > @@ -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 --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
> > index 45e2cdf..64c1c9e 100644
> > --- a/drivers/i2c/i2c-core.c
> > +++ b/drivers/i2c/i2c-core.c
> > @@ -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,29 @@ int i2c_add_adapter(struct i2c_adapter *
> > driver->attach_adapter(adap);
> > }
> >
> > -out_unlock:
> > - mutex_unlock(&core_lists);
> > +out:
> > return res;
> > }
> >
> > -
> > 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 +245,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 +255,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 +268,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,8 +289,7 @@ 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;
> > }
> >
> > diff --git a/drivers/i2c/i2c-virt.c b/drivers/i2c/i2c-virt.c
> > new file mode 100644
> > index 0000000..2bd9ea3
> > --- /dev/null
> > +++ b/drivers/i2c/i2c-virt.c
> > @@ -0,0 +1,173 @@
> > +/*
> > + * 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@yahoo.com>
> > + * 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);
> > + if ((ret = priv->select(parent, priv->client, priv->id)) >= 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);
> > + if ((ret = priv->select(parent, priv->client, priv->id)) == 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;
> > +
> > + if (!(adap = kzalloc(sizeof(struct i2c_adapter)
> > + + sizeof(struct i2c_virt_priv)
> > + + sizeof(struct i2c_algorithm), GFP_KERNEL)))
> > + 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;
> > +
> > + if ((ret = i2c_del_adapter_nolock(adap)) < 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@kernel.crashing.org>");
> > +MODULE_DESCRIPTION("Virtual I2C driver for multiplexed I2C busses");
> > +MODULE_LICENSE("GPL");
> > diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
> > index c8b81f4..66d5533 100644
> > --- a/include/linux/i2c-id.h
> > +++ b/include/linux/i2c-id.h
> > @@ -265,4 +265,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 */
> > diff --git a/include/linux/i2c.h b/include/linux/i2c.h
> > index 1635ee2..ba41f97 100644
> > --- a/include/linux/i2c.h
> > +++ b/include/linux/i2c.h
> > @@ -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
> >
> > -
> > To unsubscribe from this list: send the line "unsubscribe linux-
> > kernel" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
> > Please read the FAQ at http://www.tux.org/lkml/
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2006-11-29 02:43    [W:0.062 / U:0.172 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site