* [PATCH v2 1/2] i2c: Multiplexed I2C bus core support @ 2010-05-04 12:46 Michael Lawnick [not found] ` <4BE01741.1010909-Mmb7MZpHnFY@public.gmane.org> 0 siblings, 1 reply; 16+ messages in thread From: Michael Lawnick @ 2010-05-04 12:46 UTC (permalink / raw) To: Linux I2C; +Cc: Delvare, Jean , Rodolfo Giometti [-- Attachment #1: Type: text/plain, Size: 0 bytes --] [-- Attachment #2: 0001-mux_core_support.patch --] [-- Type: text/plain, Size: 16889 bytes --] Add multiplexed bus core support. I2C multiplexer and switches like pca954x get instantiated as new adapters per output. Signed-off-by: Michael Lawnick <ml.lawnick-Mmb7MZpHnFY@public.gmane.org> --- Based on kernel 2.6.33 + [PATCH] i2c-core: Use per-adapter userspace device lists by Jean Delware <20100424201914.08b3f008-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> <http://article.gmane.org/gmane.linux.drivers.i2c/5994/> drivers/i2c/Kconfig | 8 ++ drivers/i2c/Makefile | 1 + drivers/i2c/i2c-core.c | 93 ++++++++++++++++++++++-- drivers/i2c/i2c-dev.c | 46 ++++++++++++- drivers/i2c/i2c-mux.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c-mux.h | 46 ++++++++++++ include/linux/i2c.h | 11 +++ 7 files changed, 381 insertions(+), 8 deletions(-) create mode 100755 drivers/i2c/i2c-mux.c create mode 100755 include/linux/i2c-mux.h diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 8d8a00e..2cd6d78 100755 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -36,6 +36,14 @@ config I2C_COMPAT other user-space package which expects i2c adapters to be class devices. If you don't know, say Y. +config I2C_MUX + tristate "I2C bus multiplexing support" + depends on I2C + 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. + config I2C_CHARDEV tristate "I2C device interface" help diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index ba26e6c..a488e8b 100755 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o +obj-$(CONFIG_I2C_MUX) += i2c-mux.o obj-y += busses/ chips/ algos/ ifeq ($(CONFIG_I2C_DEBUG_CORE),y) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index fc93e28..682966a 100755 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -20,7 +20,10 @@ /* With some changes from Kyösti Mälkki <kmalkki-kf+aQKke1yb1KXRcyAk9cg@public.gmane.org>. All SMBus-related things are written by Frodo Looijaard <frodol-B0qZmFHriGg@public.gmane.org> SMBus 2.0 support by Mark Studebaker <mdsxyz123-/E1597aS9LQAvxtiuMwx3w@public.gmane.org> and - Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> */ + Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> + Mux support by Rodolfo Giometti <giometti-AVVDYK/kqiJWk0Htik3J/w@public.gmane.org> and + Michael Lawnick <michael.lawnick.ext-OYasijW0DpE@public.gmane.org> */ + #include <linux/module.h> #include <linux/kernel.h> @@ -288,6 +291,12 @@ struct i2c_client *i2c_verify_client(struct device *dev) } EXPORT_SYMBOL(i2c_verify_client); +static int i2c_parent_is_i2c_adapter(const struct i2c_adapter *adapter) +{ + return adapter->dev.parent != NULL + && adapter->dev.parent->bus == &i2c_bus_type; +} + /** * i2c_new_device - instantiate an i2c device @@ -624,6 +633,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) rt_mutex_init(&adap->bus_lock); INIT_LIST_HEAD(&adap->userspace_clients); + INIT_LIST_HEAD(&adap->mux_list_head); /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) @@ -949,9 +959,78 @@ static int __i2c_check_addr(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); + + 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_childs(struct i2c_adapter *adapter, int addr) +{ + int result = 0; + struct i2c_adapter *child, *next; + + list_for_each_entry_safe(child, next, &adapter->mux_list_head, + mux_list) { + result = device_for_each_child(&child->dev, &addr, + __i2c_check_addr); + if (result) + break; + result = i2c_check_mux_childs(child, addr); + } + return result; +} + static int i2c_check_addr(struct i2c_adapter *adapter, int addr) { - return device_for_each_child(&adapter->dev, &addr, __i2c_check_addr); + int result; + + result = i2c_check_mux_parents(adapter, addr); + + if (!result) + result = i2c_check_mux_childs(adapter, addr); + + return result; +} + +static void i2c_mux_tree_lock(struct i2c_adapter *adap) +{ + if (i2c_parent_is_i2c_adapter(adap)) + i2c_mux_tree_lock(to_i2c_adapter(adap->dev.parent)); + else + i2c_lock_adapter(adap); +} + +static int i2c_mux_tree_trylock(struct i2c_adapter *adap) +{ + int ret; + struct i2c_adapter *parent; + + if (i2c_parent_is_i2c_adapter(adap)) { + parent = to_i2c_adapter(adap->dev.parent); + ret = i2c_mux_tree_trylock(parent); + } else { + ret = i2c_trylock_adapter(adap); + } + + return ret; +} + +static void i2c_mux_tree_unlock(struct i2c_adapter *adap) +{ + if (i2c_parent_is_i2c_adapter(adap)) + i2c_mux_tree_unlock(to_i2c_adapter(adap->dev.parent)); + else + i2c_unlock_adapter(adap); } /** @@ -1104,12 +1183,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_mux_tree_trylock(adap); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { - rt_mutex_lock(&adap->bus_lock); + i2c_mux_tree_lock(adap); } /* Retry automatically on arbitration loss */ @@ -1121,7 +1200,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_mux_tree_unlock(adap); return ret; } else { @@ -1857,7 +1936,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_mux_tree_lock(adapter); /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; @@ -1871,7 +1950,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_mux_tree_unlock(adapter); } else res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, command, protocol, data); diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index f4110aa..da5327f 100755 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -193,12 +193,56 @@ static int i2cdev_check(struct device *dev, void *addrp) return dev->driver ? -EBUSY : 0; } +static int i2cdev_parent_is_i2c_adapter(const struct i2c_adapter *adapter) +{ + return adapter->dev.parent != NULL + && adapter->dev.parent->bus == &i2c_bus_type; +} + +/* 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 && i2cdev_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_childs(struct i2c_adapter *adapter, int addr) +{ + int result = 0; + struct i2c_adapter *child, *next; + + list_for_each_entry_safe(child, next, &adapter->mux_list_head, + mux_list) { + result = device_for_each_child(&child->dev, &addr, + i2cdev_check); + if (result) + break; + result = i2cdev_check_mux_childs(child, addr); + } + 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; + + result = i2cdev_check_mux_parents(adapter, addr); + + if (!result) + result = i2cdev_check_mux_childs(adapter, addr); + + return result; } static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client, diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c new file mode 100755 index 0000000..f88d29a --- /dev/null +++ b/drivers/i2c/i2c-mux.c @@ -0,0 +1,184 @@ +/* + * Multiplexed I2C bus driver. + * + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org> + * Copyright (c) 2008-2009 Eurotech S.p.A. <info-ymFC7zkAqBI1GQ1Ptb7lUw@public.gmane.org> + * + * 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-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r@public.gmane.org> + * i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc. + * i2c-virtual.c from Brian Kuschak <bkuschak-/E1597aS9LQAvxtiuMwx3w@public.gmane.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. + */ + +/* Adapted to kernel 2.6.33 by Michael Lawnick <michael.lawnick.ext-OYasijW0DpE@public.gmane.org> */ + +#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 *adap, void *mux_dev, u32 chan_id); + int (*deselect)(struct i2c_adapter *adap, void *mux_dev, u32 chan_id); +}; + +#define VIRT_TIMEOUT (HZ/2) +#define VIRT_RETRIES 3 + +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 for the virtual adapter */ +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; + } + + list_add_tail(&priv->adap.mux_list, &parent->mux_list_head); + + dev_info(&parent->dev, "i2c-mux-%d: Multiplexed I2C bus on " + "parent bus i2c-%d, chan_id %d\n", + i2c_adapter_id(&priv->adap), i2c_adapter_id(parent), chan_id); + + 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; + struct i2c_adapter *child, *next, *parent; + + if (adap->dev.parent != NULL + && adap->dev.parent->bus == &i2c_bus_type) { + parent = to_i2c_adapter(adap->dev.parent); + list_for_each_entry_safe(child, next, &parent->mux_list_head, + mux_list) { + if (child == adap) { + list_del(&child->mux_list); + break; + } + } + } + + 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-k2GhghHVRtY@public.gmane.org>"); +MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h new file mode 100755 index 0000000..0d5d6c8 --- /dev/null +++ b/include/linux/i2c-mux.h @@ -0,0 +1,46 @@ +/* + * + * i2c-mux.h - functions for the i2c-bus mux support + * + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org> + * Copyright (c) 2008-2009 Eurotech S.p.A. <info-ymFC7zkAqBI1GQ1Ptb7lUw@public.gmane.org> + * Michael Lawnick <michael.lawnick.ext-OYasijW0DpE@public.gmane.org> + * + * 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 *adap, + 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_H */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 1fd7b12..898a8c1 100755 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -349,6 +349,8 @@ struct i2c_adapter { struct completion dev_released; struct list_head userspace_clients; + struct list_head mux_list_head; + struct list_head mux_list; }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) @@ -380,6 +382,15 @@ static inline void i2c_unlock_adapter(struct i2c_adapter *adapter) rt_mutex_unlock(&adapter->bus_lock); } +/** + * i2c_trylock_adapter - Try to prevent access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +static inline int i2c_trylock_adapter(struct i2c_adapter *adapter) +{ + return rt_mutex_trylock(&adapter->bus_lock); +} + /*flags for the client struct: */ #define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ #define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */ -- 1.6.2.1 ^ permalink raw reply related [flat|nested] 16+ messages in thread
[parent not found: <4BE01741.1010909-Mmb7MZpHnFY@public.gmane.org>]
* [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x [not found] ` <4BE01741.1010909-Mmb7MZpHnFY@public.gmane.org> @ 2010-05-04 12:48 ` Michael Lawnick [not found] ` <4BE01795.1090605-Mmb7MZpHnFY@public.gmane.org> 2010-05-04 12:52 ` [PATCH v2 1/2] i2c: Multiplexed I2C bus core support Rodolfo Giometti ` (2 subsequent siblings) 3 siblings, 1 reply; 16+ messages in thread From: Michael Lawnick @ 2010-05-04 12:48 UTC (permalink / raw) To: Linux I2C; +Cc: Delvare, Jean , Rodolfo Giometti [-- Attachment #1: Type: text/plain, Size: 0 bytes --] [-- Attachment #2: 0002-pca954x-mux.patch --] [-- Type: text/plain, Size: 12150 bytes --] Add multiplexed bus core support. I2C driver for PCA954x I2C multiplexer series. Signed-off-by: Michael Lawnick <ml.lawnick-Mmb7MZpHnFY@public.gmane.org> --- Based on kernel 2.6.33 + [PATCH] i2c-core: Use per-adapter userspace device lists by Jean Delware <20100424201914.08b3f008-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> <http://article.gmane.org/gmane.linux.drivers.i2c/5994/> drivers/i2c/Kconfig | 2 + drivers/i2c/Makefile | 2 +- drivers/i2c/muxes/Kconfig | 17 +++ drivers/i2c/muxes/Makefile | 8 + drivers/i2c/muxes/pca954x.c | 302 +++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pca954x.h | 47 +++++++ 6 files changed, 377 insertions(+), 1 deletions(-) create mode 100755 drivers/i2c/muxes/Kconfig create mode 100755 drivers/i2c/muxes/Makefile create mode 100755 drivers/i2c/muxes/pca954x.c create mode 100755 include/linux/i2c/pca954x.h diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 2cd6d78..21b0ac0 100755 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -44,6 +44,8 @@ config I2C_MUX handle multiplexed I2C bus topologies, by presenting each multiplexed segment as a I2C adapter. +source drivers/i2c/muxes/Kconfig + config I2C_CHARDEV tristate "I2C device interface" help diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index a488e8b..1769a16 100755 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_MUX) += i2c-mux.o -obj-y += busses/ chips/ algos/ +obj-y += busses/ chips/ muxes/ algos/ ifeq ($(CONFIG_I2C_DEBUG_CORE),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig new file mode 100755 index 0000000..ec22db0 --- /dev/null +++ b/drivers/i2c/muxes/Kconfig @@ -0,0 +1,17 @@ +# +# Multiplexer I2C chip drivers configuration +# + +menu "Multiplexer I2C Chip support" + depends on I2C_MUX && EXPERIMENTAL + +config I2C_MUX_PCA954x + tristate "Philips PCA954x I2C Mux/switches" + 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 diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile new file mode 100755 index 0000000..575c1ca --- /dev/null +++ b/drivers/i2c/muxes/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for multiplexer I2C chip drivers. + +obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o + +ifeq ($(I2C_DEBUG_BUS),y) +EXTRA_CFLAGS += -DDEBUG +endif diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c new file mode 100755 index 0000000..12b5eb7 --- /dev/null +++ b/drivers/i2c/muxes/pca954x.c @@ -0,0 +1,302 @@ +/* + * I2C multiplexer + * + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org> + * Copyright (c) 2008-2009 Eurotech S.p.A. <info-ymFC7zkAqBI1GQ1Ptb7lUw@public.gmane.org> + * + * 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-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r@public.gmane.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-/E1597aS9LQAvxtiuMwx3w@public.gmane.org> + * and + * pca9540.c from Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.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; +}; + +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 = I2C_SMBUS_WRITE; + 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 != chan) { + ret = pca954x_reg_write(adap, client, regval); + data->last_chan = chan; + } + + 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; + printk(KERN_ERR + "%s %s: failed to register multiplexed adapter" + " %d at bus %d\n", __FILE__, __func__, 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-k2GhghHVRtY@public.gmane.org>"); +MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/i2c/pca954x.h b/include/linux/i2c/pca954x.h new file mode 100755 index 0000000..8032269 --- /dev/null +++ b/include/linux/i2c/pca954x.h @@ -0,0 +1,47 @@ +/* + * + * pca954x.h - I2C multiplexer/switch support + * + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org> + * Copyright (c) 2008-2009 Eurotech S.p.A. <info-ymFC7zkAqBI1GQ1Ptb7lUw@public.gmane.org> + * Michael Lawnick <michael.lawnick.ext-OYasijW0DpE@public.gmane.org> + * + * 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 _I2C_PCA954X_H +#define _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 /*_I2C_PCA954X_H*/ -- 1.6.2.1 ^ permalink raw reply related [flat|nested] 16+ messages in thread
[parent not found: <4BE01795.1090605-Mmb7MZpHnFY@public.gmane.org>]
* Re: [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x [not found] ` <4BE01795.1090605-Mmb7MZpHnFY@public.gmane.org> @ 2010-05-04 12:52 ` Rodolfo Giometti 2010-06-09 16:41 ` Jean Delvare 2010-06-13 12:11 ` Jean Delvare 2 siblings, 0 replies; 16+ messages in thread From: Rodolfo Giometti @ 2010-05-04 12:52 UTC (permalink / raw) To: Michael Lawnick; +Cc: Linux I2C, Delvare, Jean On Tue, May 04, 2010 at 02:48:21PM +0200, Michael Lawnick wrote: > Add multiplexed bus core support. I2C driver for PCA954x > I2C multiplexer series. > > Signed-off-by: Michael Lawnick <ml.lawnick-Mmb7MZpHnFY@public.gmane.org> Acked-by: Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org> -- GNU/Linux Solutions e-mail: giometti-AVVDYK/kqiJWk0Htik3J/w@public.gmane.org Linux Device Driver giometti-k2GhghHVRtY@public.gmane.org Embedded Systems phone: +39 349 2432127 UNIX programming skype: rodolfo.giometti Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x [not found] ` <4BE01795.1090605-Mmb7MZpHnFY@public.gmane.org> 2010-05-04 12:52 ` Rodolfo Giometti @ 2010-06-09 16:41 ` Jean Delvare [not found] ` <20100609184143.06488f2f-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> 2010-06-13 12:11 ` Jean Delvare 2 siblings, 1 reply; 16+ messages in thread From: Jean Delvare @ 2010-06-09 16:41 UTC (permalink / raw) To: Michael Lawnick; +Cc: Linux I2C, Rodolfo Giometti Hi Michael, On Tue, 04 May 2010 14:48:21 +0200, Michael Lawnick wrote: > Add multiplexed bus core support. I2C driver for PCA954x > I2C multiplexer series. > > Signed-off-by: Michael Lawnick <ml.lawnick-Mmb7MZpHnFY@public.gmane.org> Review: > > --- > Based on kernel 2.6.33 + > [PATCH] i2c-core: Use per-adapter userspace device lists > by Jean Delware <20100424201914.08b3f008-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> > <http://article.gmane.org/gmane.linux.drivers.i2c/5994/> > > drivers/i2c/Kconfig | 2 + > drivers/i2c/Makefile | 2 +- > drivers/i2c/muxes/Kconfig | 17 +++ > drivers/i2c/muxes/Makefile | 8 + > drivers/i2c/muxes/pca954x.c | 302 +++++++++++++++++++++++++++++++++++++++++++ > include/linux/i2c/pca954x.h | 47 +++++++ > 6 files changed, 377 insertions(+), 1 deletions(-) > create mode 100755 drivers/i2c/muxes/Kconfig > create mode 100755 drivers/i2c/muxes/Makefile > create mode 100755 drivers/i2c/muxes/pca954x.c > create mode 100755 include/linux/i2c/pca954x.h I don't really care, as I use quilt for my patches, but the above modes are wrong: should be 100644. > > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig > index 2cd6d78..21b0ac0 100755 > --- a/drivers/i2c/Kconfig > +++ b/drivers/i2c/Kconfig > @@ -44,6 +44,8 @@ config I2C_MUX > handle multiplexed I2C bus topologies, by presenting each > multiplexed segment as a I2C adapter. > > +source drivers/i2c/muxes/Kconfig > + > config I2C_CHARDEV > tristate "I2C device interface" > help > diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile > index a488e8b..1769a16 100755 > --- a/drivers/i2c/Makefile > +++ b/drivers/i2c/Makefile > @@ -6,7 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o > obj-$(CONFIG_I2C) += i2c-core.o > obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o > obj-$(CONFIG_I2C_MUX) += i2c-mux.o > -obj-y += busses/ chips/ algos/ > +obj-y += busses/ chips/ muxes/ algos/ > > ifeq ($(CONFIG_I2C_DEBUG_CORE),y) > EXTRA_CFLAGS += -DDEBUG > diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig > new file mode 100755 > index 0000000..ec22db0 > --- /dev/null > +++ b/drivers/i2c/muxes/Kconfig > @@ -0,0 +1,17 @@ > +# > +# Multiplexer I2C chip drivers configuration > +# > + > +menu "Multiplexer I2C Chip support" > + depends on I2C_MUX && EXPERIMENTAL The dependency on EXPERIMENTAL is odd for a menu. I'd rather move it down to individual drivers, so that we can them remove them later on a case-by-case basis. > + > +config I2C_MUX_PCA954x > + tristate "Philips PCA954x I2C Mux/switches" > + 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 > diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile > new file mode 100755 > index 0000000..575c1ca > --- /dev/null > +++ b/drivers/i2c/muxes/Makefile > @@ -0,0 +1,8 @@ > +# > +# Makefile for multiplexer I2C chip drivers. > + > +obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o > + > +ifeq ($(I2C_DEBUG_BUS),y) I think you mean CONFIG_I2C_DEBUG_BUS here. > +EXTRA_CFLAGS += -DDEBUG > +endif > diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c > new file mode 100755 > index 0000000..12b5eb7 > --- /dev/null > +++ b/drivers/i2c/muxes/pca954x.c > @@ -0,0 +1,302 @@ > +/* > + * I2C multiplexer > + * > + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org> > + * Copyright (c) 2008-2009 Eurotech S.p.A. <info-ymFC7zkAqBI1GQ1Ptb7lUw@public.gmane.org> > + * > + * 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-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r@public.gmane.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-/E1597aS9LQAvxtiuMwx3w@public.gmane.org> > + * and > + * pca9540.c from Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.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; This indentation doesn't make much sense. > +}; > + > +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; Useless initialization. > + > + if (adap->algo->master_xfer) { > + struct i2c_msg msg; > + char buf[1]; > + > + msg.addr = client->addr; > + msg.flags = I2C_SMBUS_WRITE; That's not correct. You want to use 0 here (as writes are the default, in the absence of I2C_M_RD). > + 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 != chan) { > + ret = pca954x_reg_write(adap, client, regval); > + data->last_chan = chan; > + } > + > + 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); Functions are already addresses, the leading & isn't needed. > + > + if (data->virt_adaps[num] == NULL) { > + ret = -ENODEV; > + printk(KERN_ERR Please use dev_err(). > + "%s %s: failed to register multiplexed adapter" > + " %d at bus %d\n", __FILE__, __func__, num, I think you mean "as bus", not "at bus". > + 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-k2GhghHVRtY@public.gmane.org>"); > +MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/i2c/pca954x.h b/include/linux/i2c/pca954x.h > new file mode 100755 > index 0000000..8032269 > --- /dev/null > +++ b/include/linux/i2c/pca954x.h > @@ -0,0 +1,47 @@ > +/* > + * > + * pca954x.h - I2C multiplexer/switch support > + * > + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org> > + * Copyright (c) 2008-2009 Eurotech S.p.A. <info-ymFC7zkAqBI1GQ1Ptb7lUw@public.gmane.org> > + * Michael Lawnick <michael.lawnick.ext-OYasijW0DpE@public.gmane.org> > + * > + * 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 _I2C_PCA954X_H Should be prefixed with _LINUX to avoid any confusion. > +#define _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; Please use tabs for indentation. > +}; > + > +#endif /*_I2C_PCA954X_H*/ Missing spaces. I've fixed all these small issues myself; no need to resend. Thanks (to you and Rodolfo as well) for your contribution! -- Jean Delvare ^ permalink raw reply [flat|nested] 16+ messages in thread
[parent not found: <20100609184143.06488f2f-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>]
* Re: [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x [not found] ` <20100609184143.06488f2f-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> @ 2010-06-10 10:15 ` Michael Lawnick 0 siblings, 0 replies; 16+ messages in thread From: Michael Lawnick @ 2010-06-10 10:15 UTC (permalink / raw) To: Jean Delvare; +Cc: Linux I2C, Rodolfo Giometti Jean Delvare said the following: > Hi Michael, > > On Tue, 04 May 2010 14:48:21 +0200, Michael Lawnick wrote: >> Add multiplexed bus core support. I2C driver for PCA954x >> I2C multiplexer series. >> >> Signed-off-by: Michael Lawnick <ml.lawnick-Mmb7MZpHnFY@public.gmane.org> > > > Review: ... > I've fixed all these small issues myself; no need to resend. Thanks (to > you and Rodolfo as well) for your contribution! > Thank You! -- KR Michael ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x [not found] ` <4BE01795.1090605-Mmb7MZpHnFY@public.gmane.org> 2010-05-04 12:52 ` Rodolfo Giometti 2010-06-09 16:41 ` Jean Delvare @ 2010-06-13 12:11 ` Jean Delvare [not found] ` <20100613141122.1d5b1423-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> 2 siblings, 1 reply; 16+ messages in thread From: Jean Delvare @ 2010-06-13 12:11 UTC (permalink / raw) To: Michael Lawnick; +Cc: Linux I2C, Rodolfo Giometti Hi Michael, I just hit a bug... On Tue, 04 May 2010 14:48:21 +0200, Michael Lawnick wrote: > +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 != chan) { That's not right. data->last_chan is 0 after initialization or deselection, but selecting channel 0 is valid and must be possible. So you either want to use a different initialization value (e.g. 0xff) or store the register value in data->last_chan instead of the channel number. I went for the latter. > + ret = pca954x_reg_write(adap, client, regval); > + data->last_chan = chan; > + } > + > + return ret; > +} -- Jean Delvare ^ permalink raw reply [flat|nested] 16+ messages in thread
[parent not found: <20100613141122.1d5b1423-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>]
* Re: [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x [not found] ` <20100613141122.1d5b1423-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> @ 2010-06-14 9:09 ` Michael Lawnick 0 siblings, 0 replies; 16+ messages in thread From: Michael Lawnick @ 2010-06-14 9:09 UTC (permalink / raw) To: Jean Delvare; +Cc: Linux I2C, Rodolfo Giometti Jean Delvare said the following: > Hi Michael, > > I just hit a bug... > > On Tue, 04 May 2010 14:48:21 +0200, Michael Lawnick wrote: >> +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 != chan) { > > That's not right. data->last_chan is 0 after initialization or > deselection, but selecting channel 0 is valid and must be possible. > > So you either want to use a different initialization value (e.g. 0xff) > or store the register value in data->last_chan instead of the channel > number. I went for the latter. ACK -- KR Michael ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/2] i2c: Multiplexed I2C bus core support [not found] ` <4BE01741.1010909-Mmb7MZpHnFY@public.gmane.org> 2010-05-04 12:48 ` [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x Michael Lawnick @ 2010-05-04 12:52 ` Rodolfo Giometti 2010-05-17 7:36 ` Michael Lawnick 2010-06-10 13:24 ` Jean Delvare 3 siblings, 0 replies; 16+ messages in thread From: Rodolfo Giometti @ 2010-05-04 12:52 UTC (permalink / raw) To: Michael Lawnick; +Cc: Linux I2C, Delvare, Jean On Tue, May 04, 2010 at 02:46:57PM +0200, Michael Lawnick wrote: > Add multiplexed bus core support. I2C multiplexer and switches > like pca954x get instantiated as new adapters per output. > > Signed-off-by: Michael Lawnick <ml.lawnick-Mmb7MZpHnFY@public.gmane.org> Acked-by: Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org> -- GNU/Linux Solutions e-mail: giometti-AVVDYK/kqiJWk0Htik3J/w@public.gmane.org Linux Device Driver giometti-k2GhghHVRtY@public.gmane.org Embedded Systems phone: +39 349 2432127 UNIX programming skype: rodolfo.giometti Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/2] i2c: Multiplexed I2C bus core support [not found] ` <4BE01741.1010909-Mmb7MZpHnFY@public.gmane.org> 2010-05-04 12:48 ` [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x Michael Lawnick 2010-05-04 12:52 ` [PATCH v2 1/2] i2c: Multiplexed I2C bus core support Rodolfo Giometti @ 2010-05-17 7:36 ` Michael Lawnick [not found] ` <4BF0F200.5090604-Mmb7MZpHnFY@public.gmane.org> 2010-06-10 13:24 ` Jean Delvare 3 siblings, 1 reply; 16+ messages in thread From: Michael Lawnick @ 2010-05-17 7:36 UTC (permalink / raw) To: Linux I2C; +Cc: Delvare, Jean Michael Lawnick said the following: > Add multiplexed bus core support. ... Ping Jean, still any chance to get that done into .35? -- KR Michael Lawnick ^ permalink raw reply [flat|nested] 16+ messages in thread
[parent not found: <4BF0F200.5090604-Mmb7MZpHnFY@public.gmane.org>]
* Re: [PATCH v2 1/2] i2c: Multiplexed I2C bus core support [not found] ` <4BF0F200.5090604-Mmb7MZpHnFY@public.gmane.org> @ 2010-05-17 8:15 ` Jean Delvare 0 siblings, 0 replies; 16+ messages in thread From: Jean Delvare @ 2010-05-17 8:15 UTC (permalink / raw) To: Michael Lawnick; +Cc: Linux I2C Hi Michael, On Mon, 17 May 2010 09:36:32 +0200, Michael Lawnick wrote: > Michael Lawnick said the following: > > Add multiplexed bus core support. > ... > > Ping > > Jean, still any chance to get that done into .35? I hope so, yes. I am busy with the w83795 driver right now, hope to get some time to review your updated patches this week. Stay tuned. -- Jean Delvare ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/2] i2c: Multiplexed I2C bus core support [not found] ` <4BE01741.1010909-Mmb7MZpHnFY@public.gmane.org> ` (2 preceding siblings ...) 2010-05-17 7:36 ` Michael Lawnick @ 2010-06-10 13:24 ` Jean Delvare [not found] ` <20100610152411.5497461c-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> 3 siblings, 1 reply; 16+ messages in thread From: Jean Delvare @ 2010-06-10 13:24 UTC (permalink / raw) To: Michael Lawnick; +Cc: Linux I2C, Rodolfo Giometti Hi Michael, On Tue, 04 May 2010 14:46:57 +0200, Michael Lawnick wrote: > Add multiplexed bus core support. I2C multiplexer and switches > like pca954x get instantiated as new adapters per output. > > Signed-off-by: Michael Lawnick <ml.lawnick-Mmb7MZpHnFY@public.gmane.org> > > --- > Based on kernel 2.6.33 + > [PATCH] i2c-core: Use per-adapter userspace device lists > by Jean Delware <20100424201914.08b3f008-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> > <http://article.gmane.org/gmane.linux.drivers.i2c/5994/> Future patches should be based on 2.6.34 at least... 2.6.35-rc2 would be even better. Review follows. Need some more work I fear, but not that much... Please let me know if you are going to submit another iteration, or if you prefer to let me fix things the way I like (in which case I'll still ask you to test my version of the patch when I'm done.) > > drivers/i2c/Kconfig | 8 ++ > drivers/i2c/Makefile | 1 + > drivers/i2c/i2c-core.c | 93 ++++++++++++++++++++++-- > drivers/i2c/i2c-dev.c | 46 ++++++++++++- > drivers/i2c/i2c-mux.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/i2c-mux.h | 46 ++++++++++++ > include/linux/i2c.h | 11 +++ > 7 files changed, 381 insertions(+), 8 deletions(-) > create mode 100755 drivers/i2c/i2c-mux.c > create mode 100755 include/linux/i2c-mux.h > > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig > index 8d8a00e..2cd6d78 100755 > --- a/drivers/i2c/Kconfig > +++ b/drivers/i2c/Kconfig > @@ -36,6 +36,14 @@ config I2C_COMPAT > other user-space package which expects i2c adapters to be class > devices. If you don't know, say Y. > > +config I2C_MUX > + tristate "I2C bus multiplexing support" > + depends on I2C Dependency on I2C isn't needed: this configuration item is already in an "if I2C" block. A dependency on EXPERIMENTAL might be a good idea for the time being though. > + 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. For support that can be build as a module, we usually add a sentence saying so and giving the name of the module. > + I would move config I2C_MUX below config I2C_CHARDEV, to make it consistent with Makefile, and also because users expect new options to be added after existing options. > config I2C_CHARDEV > tristate "I2C device interface" > help > diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile > index ba26e6c..a488e8b 100755 > --- a/drivers/i2c/Makefile > +++ b/drivers/i2c/Makefile > @@ -5,6 +5,7 @@ > obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o > obj-$(CONFIG_I2C) += i2c-core.o > obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o > +obj-$(CONFIG_I2C_MUX) += i2c-mux.o > obj-y += busses/ chips/ algos/ > > ifeq ($(CONFIG_I2C_DEBUG_CORE),y) > diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c > index fc93e28..682966a 100755 > --- a/drivers/i2c/i2c-core.c > +++ b/drivers/i2c/i2c-core.c > @@ -20,7 +20,10 @@ > /* With some changes from Kyösti Mälkki <kmalkki-kf+aQKke1yb1KXRcyAk9cg@public.gmane.org>. > All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> > SMBus 2.0 support by Mark Studebaker <mdsxyz123-/E1597aS9LQAvxtiuMwx3w@public.gmane.org> and > - Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> */ > + Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> > + Mux support by Rodolfo Giometti <giometti-AVVDYK/kqiJWk0Htik3J/w@public.gmane.org> and > + Michael Lawnick <michael.lawnick.ext-OYasijW0DpE@public.gmane.org> */ > + Extra blank line. > > #include <linux/module.h> > #include <linux/kernel.h> > @@ -288,6 +291,12 @@ struct i2c_client *i2c_verify_client(struct device *dev) > } > EXPORT_SYMBOL(i2c_verify_client); > > +static int i2c_parent_is_i2c_adapter(const struct i2c_adapter *adapter) > +{ > + return adapter->dev.parent != NULL > + && adapter->dev.parent->bus == &i2c_bus_type; > +} > + > > /** > * i2c_new_device - instantiate an i2c device > @@ -624,6 +633,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) > > rt_mutex_init(&adap->bus_lock); > INIT_LIST_HEAD(&adap->userspace_clients); > + INIT_LIST_HEAD(&adap->mux_list_head); > > /* Set default timeout to 1 second if not already set */ > if (adap->timeout == 0) > @@ -949,9 +959,78 @@ static int __i2c_check_addr(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); > + > + 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_childs(struct i2c_adapter *adapter, int addr) "childs" doesn't exist in proper English, the right work is "children" ;) > +{ > + int result = 0; > + struct i2c_adapter *child, *next; > + > + list_for_each_entry_safe(child, next, &adapter->mux_list_head, > + mux_list) { > + result = device_for_each_child(&child->dev, &addr, > + __i2c_check_addr); > + if (result) > + break; > + result = i2c_check_mux_childs(child, addr); This value is ignored, except for the last child. I think you should break here too. > + } > + return result; > +} > + > static int i2c_check_addr(struct i2c_adapter *adapter, int addr) > { > - return device_for_each_child(&adapter->dev, &addr, __i2c_check_addr); > + int result; > + > + result = i2c_check_mux_parents(adapter, addr); > + > + if (!result) > + result = i2c_check_mux_childs(adapter, addr); > + > + return result; > +} I am worried about (the lack of) locking when the above function is called. The problem isn't new with your patch, but your patch makes it worse: beforehand, the driver core would complain if we hit a race here, because we would try to create two clients with the same bus_id as a result. But now, we may create 2 conflicting clients with different bus_id, so the driver core will not complain. I'll think about it some more, and add locking if needed. > +static void i2c_mux_tree_lock(struct i2c_adapter *adap) > +{ > + if (i2c_parent_is_i2c_adapter(adap)) > + i2c_mux_tree_lock(to_i2c_adapter(adap->dev.parent)); > + else > + i2c_lock_adapter(adap); > +} > + > +static int i2c_mux_tree_trylock(struct i2c_adapter *adap) > +{ > + int ret; > + struct i2c_adapter *parent; > + > + if (i2c_parent_is_i2c_adapter(adap)) { > + parent = to_i2c_adapter(adap->dev.parent); > + ret = i2c_mux_tree_trylock(parent); > + } else { > + ret = i2c_trylock_adapter(adap); > + } > + > + return ret; > +} > + > +static void i2c_mux_tree_unlock(struct i2c_adapter *adap) > +{ > + if (i2c_parent_is_i2c_adapter(adap)) > + i2c_mux_tree_unlock(to_i2c_adapter(adap->dev.parent)); > + else > + i2c_unlock_adapter(adap); > } > > /** > @@ -1104,12 +1183,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_mux_tree_trylock(adap); > if (!ret) > /* I2C activity is ongoing. */ > return -EAGAIN; > } else { > - rt_mutex_lock(&adap->bus_lock); > + i2c_mux_tree_lock(adap); > } > > /* Retry automatically on arbitration loss */ > @@ -1121,7 +1200,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_mux_tree_unlock(adap); > > return ret; > } else { > @@ -1857,7 +1936,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_mux_tree_lock(adapter); > > /* Retry automatically on arbitration loss */ > orig_jiffies = jiffies; > @@ -1871,7 +1950,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_mux_tree_unlock(adapter); > } else > res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, > command, protocol, data); I'm worried that this approach is risky. We now use i2c_mux_tree_*lock internally, but still expose the i2c_*lock_adapter() API externally. This API is _not_ mux-aware, and will do the wrong thing if called on a bus segment behind a mux. I'd rather not have two APIs when we really only need one. Let's just update the current public API to be mux-aware, and use that everywhere. > diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c > index f4110aa..da5327f 100755 > --- a/drivers/i2c/i2c-dev.c > +++ b/drivers/i2c/i2c-dev.c > @@ -193,12 +193,56 @@ static int i2cdev_check(struct device *dev, void *addrp) > return dev->driver ? -EBUSY : 0; > } > > +static int i2cdev_parent_is_i2c_adapter(const struct i2c_adapter *adapter) > +{ > + return adapter->dev.parent != NULL > + && adapter->dev.parent->bus == &i2c_bus_type; > +} This is an exact duplicate of the same function in i2c-core. I'd rather make it an inline function in <linux/i2c.h>, or export the one from i2c-core.c. We have enough duplicate code in i2c-dev.c already :( > + > +/* 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 && i2cdev_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_childs(struct i2c_adapter *adapter, int addr) children > +{ > + int result = 0; > + struct i2c_adapter *child, *next; > + > + list_for_each_entry_safe(child, next, &adapter->mux_list_head, > + mux_list) { > + result = device_for_each_child(&child->dev, &addr, > + i2cdev_check); > + if (result) > + break; > + result = i2cdev_check_mux_childs(child, addr); Same as in the i2c-core variant, this value is ignored, I think this isn't correct. > + } > + 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; > + > + result = i2cdev_check_mux_parents(adapter, addr); > + > + if (!result) > + result = i2cdev_check_mux_childs(adapter, addr); > + > + return result; > } > > static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client, > diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c > new file mode 100755 > index 0000000..f88d29a > --- /dev/null > +++ b/drivers/i2c/i2c-mux.c > @@ -0,0 +1,184 @@ > +/* > + * Multiplexed I2C bus driver. > + * > + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org> > + * Copyright (c) 2008-2009 Eurotech S.p.A. <info-ymFC7zkAqBI1GQ1Ptb7lUw@public.gmane.org> > + * > + * 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-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r@public.gmane.org> > + * i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc. > + * i2c-virtual.c from Brian Kuschak <bkuschak-/E1597aS9LQAvxtiuMwx3w@public.gmane.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. > + */ > + > +/* Adapted to kernel 2.6.33 by Michael Lawnick <michael.lawnick.ext@nsn.com> */ I'd rather have you add your copyright with the other ones already present above. > + > +#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 *adap, void *mux_dev, u32 chan_id); > + int (*deselect)(struct i2c_adapter *adap, void *mux_dev, u32 chan_id); The two "adap" can go away... I insisted on having parameter names for the other two; for structures, it's usually pretty clear what we're talking about. > +}; > + > +#define VIRT_TIMEOUT (HZ/2) > +#define VIRT_RETRIES 3 These two are no longer used and can thus be deleted. > + > +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.*/ Missing space at end of comment. > + > + 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.*/ Ditto. > + > + 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); Strange alignment. > + if (priv->deselect) > + priv->deselect(parent, priv->mux_dev, priv->chan_id); > + > + return ret; > +} > + > +/* Return the parent's functionality for the virtual adapter */ Please avoid referring to "virtual adapters". The fact here is that all bus segments of a given tree share the same functionality, because it is a characteristic of the controller - nothing virtual here. > +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); I'm only half happy with this name. One problem is that it isn't unique: if you have 2 mux chips on the same root segment, this will lead to duplicate segments with the same name. Another problem is that it completely hides the name of the controller (which, I admit, we may recover by walking sysfs, but still...) This isn't a blocker point, but something to keep in mind for further improvements. It might even make sense to let the caller provide the format for the segment names. > + 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; > + } > + > + list_add_tail(&priv->adap.mux_list, &parent->mux_list_head); The driver core maintains a device tree already, do you really need to keep your own? We've tried hard to remove all redundancy between i2c-core and the driver core in the last couple years, so I really would like us to not add such redundancy back now. If you have a good reason to have your own list, please explain. If not, please get rid of it. > + > + dev_info(&parent->dev, "i2c-mux-%d: Multiplexed I2C bus on " > + "parent bus i2c-%d, chan_id %d\n", > + i2c_adapter_id(&priv->adap), i2c_adapter_id(parent), chan_id); Have you looked at the output? I expect some redundancy, as you use dev_info() on the parent, i2c_adapter_id(parent) is already included. I think I'd rather call dev_info() on the newly spawned child, so you can get rid of the leading "i2c-mux-%d". BTW, please use dev_name() instead of hard-coding i2c-%d for the parent. > + > + 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; > + struct i2c_adapter *child, *next, *parent; Would look nicer with all structs in front and ret last, IMHO. > + > + if (adap->dev.parent != NULL > + && adap->dev.parent->bus == &i2c_bus_type) { This is a copy of i2c_parent_is_i2c_adapter(), isn't it? That's one more reason to export it from i2c-core. > + parent = to_i2c_adapter(adap->dev.parent); Note that we could have i2c_parent_is_i2c_adapter() return the parent adapter, if it proves useful in practice. > + list_for_each_entry_safe(child, next, &parent->mux_list_head, > + mux_list) { > + if (child == adap) { > + list_del(&child->mux_list); > + break; > + } > + } > + } That being said... Using the driver core's list would save you the hassle altogether. > + > + 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-k2GhghHVRtY@public.gmane.org>"); > +MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h > new file mode 100755 > index 0000000..0d5d6c8 > --- /dev/null > +++ b/include/linux/i2c-mux.h > @@ -0,0 +1,46 @@ > +/* > + * > + * i2c-mux.h - functions for the i2c-bus mux support > + * > + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org> > + * Copyright (c) 2008-2009 Eurotech S.p.A. <info-ymFC7zkAqBI1GQ1Ptb7lUw@public.gmane.org> > + * Michael Lawnick <michael.lawnick.ext-OYasijW0DpE@public.gmane.org> > + * > + * 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 *adap, > + 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_H */ You actually mean /* _LINUX_I2C_MUX_H */. > diff --git a/include/linux/i2c.h b/include/linux/i2c.h > index 1fd7b12..898a8c1 100755 > --- a/include/linux/i2c.h > +++ b/include/linux/i2c.h > @@ -349,6 +349,8 @@ struct i2c_adapter { > struct completion dev_released; > > struct list_head userspace_clients; > + struct list_head mux_list_head; > + struct list_head mux_list; As said before, I would much prefer if we didn't have to introduce these. > }; > #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) > > @@ -380,6 +382,15 @@ static inline void i2c_unlock_adapter(struct i2c_adapter *adapter) > rt_mutex_unlock(&adapter->bus_lock); > } > > +/** > + * i2c_trylock_adapter - Try to prevent access to an I2C bus segment > + * @adapter: Target I2C bus segment > + */ > +static inline int i2c_trylock_adapter(struct i2c_adapter *adapter) > +{ > + return rt_mutex_trylock(&adapter->bus_lock); > +} > + > /*flags for the client struct: */ > #define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ > #define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */ Thanks, -- Jean Delvare ^ permalink raw reply [flat|nested] 16+ messages in thread
[parent not found: <20100610152411.5497461c-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>]
* Re: [PATCH v2 1/2] i2c: Multiplexed I2C bus core support [not found] ` <20100610152411.5497461c-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> @ 2010-06-11 6:31 ` Michael Lawnick [not found] ` <4C11D85F.7010604-Mmb7MZpHnFY@public.gmane.org> 2010-06-15 8:52 ` Michael Lawnick 1 sibling, 1 reply; 16+ messages in thread From: Michael Lawnick @ 2010-06-11 6:31 UTC (permalink / raw) To: Jean Delvare; +Cc: Linux I2C, Rodolfo Giometti Hi, beside all the other issues, Jean Delvare said the following: > Hi Michael, > >> + list_add_tail(&priv->adap.mux_list, &parent->mux_list_head); > > The driver core maintains a device tree already, do you really need to > keep your own? We've tried hard to remove all redundancy between > i2c-core and the driver core in the last couple years, so I really would > like us to not add such redundancy back now. > > If you have a good reason to have your own list, please explain. If > not, please get rid of it. > this is a core part of mux management. You need it for checking whether the current client that is to be added is already present. The problem is to efficiently parse down a mux tree towards the leaves. Sub-muxes are registered as i2c clients to their upper bus. I found no way to separate muxes from normal clients. So there were two ideas: invent a client-type field in i2c-client or to hold a separate list. The first one e.g. would have the possibility to provide basic info in sysFs but I feared a general debate. So I decided to go the other way which is hidden to the outer world. If you don't like this way, I'd need some ideas how to do it. -- KR Michael ^ permalink raw reply [flat|nested] 16+ messages in thread
[parent not found: <4C11D85F.7010604-Mmb7MZpHnFY@public.gmane.org>]
* Re: [PATCH v2 1/2] i2c: Multiplexed I2C bus core support [not found] ` <4C11D85F.7010604-Mmb7MZpHnFY@public.gmane.org> @ 2010-06-11 9:40 ` Jean Delvare [not found] ` <20100611114003.01c15f62-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> 0 siblings, 1 reply; 16+ messages in thread From: Jean Delvare @ 2010-06-11 9:40 UTC (permalink / raw) To: Michael Lawnick; +Cc: Linux I2C, Rodolfo Giometti Hi Michael, On Fri, 11 Jun 2010 08:31:59 +0200, Michael Lawnick wrote: > Jean Delvare said the following: > > Hi Michael, > > > >> + list_add_tail(&priv->adap.mux_list, &parent->mux_list_head); > > > > The driver core maintains a device tree already, do you really need to > > keep your own? We've tried hard to remove all redundancy between > > i2c-core and the driver core in the last couple years, so I really would > > like us to not add such redundancy back now. > > > > If you have a good reason to have your own list, please explain. If > > not, please get rid of it. > > > this is a core part of mux management. You need it for checking whether > the current client that is to be added is already present. > The problem is to efficiently parse down a mux tree towards the leaves. > Sub-muxes are registered as i2c clients to their upper bus. This is correct, but irrelevant, as far as I can see. When walking an I2C segment tree towards the leaves, you care about the segments themselves (struct i2c_adapter), not the mux chips. In your architecture, muxed segments are direct children of the root segment from the device tree perspective, not children of the mux chip. > I found no way to separate muxes from normal clients. If you have to, it is easy, the driver core has support for this already. Search for i2c_client_type and i2c_adapter_type in i2c-core.c. We could add i2c_mux_type easily. But as I wrote above, I don't think we need this. > So there were two ideas: > invent a client-type field in i2c-client or to hold a separate list. > The first one e.g. would have the possibility to provide basic info in sysFs > but I feared a general debate. So I decided to go the other way which is > hidden to the outer world. > > If you don't like this way, I'd need some ideas how to do it. I would walk the device tree maintained by the driver core (using device_for_each_child) and filtering out irrelevant devices. Basically filtering on dev->type == &i2c_adapter_type should do the trick, methinks. Is there anything I am missing? Thanks, -- Jean Delvare ^ permalink raw reply [flat|nested] 16+ messages in thread
[parent not found: <20100611114003.01c15f62-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>]
* Re: [PATCH v2 1/2] i2c: Multiplexed I2C bus core support [not found] ` <20100611114003.01c15f62-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> @ 2010-06-15 8:53 ` Michael Lawnick 0 siblings, 0 replies; 16+ messages in thread From: Michael Lawnick @ 2010-06-15 8:53 UTC (permalink / raw) To: Jean Delvare; +Cc: Linux I2C Hi Jean, Jean Delvare said the following: > > Hi Michael, ... > > I would walk the device tree maintained by the driver core (using > > device_for_each_child) and filtering out irrelevant devices. Basically > > filtering on dev->type == &i2c_adapter_type should do the trick, > > methinks. > > You are right, I missed that somehow when investigating for a solution. Until your post I was convinced that the adapters are all registered to bus only. I now rechecked by some small code and found that I can throw away these awefull lists. Iteration will follow. -- KR Michael ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/2] i2c: Multiplexed I2C bus core support [not found] ` <20100610152411.5497461c-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> 2010-06-11 6:31 ` Michael Lawnick @ 2010-06-15 8:52 ` Michael Lawnick [not found] ` <4C173F61.8000007-Mmb7MZpHnFY@public.gmane.org> 1 sibling, 1 reply; 16+ messages in thread From: Michael Lawnick @ 2010-06-15 8:52 UTC (permalink / raw) To: Jean Delvare; +Cc: Linux I2C Hi Jean, Jean Delvare said the following: ... >> if (adapter->algo->smbus_xfer) { >> - rt_mutex_lock(&adapter->bus_lock); >> + i2c_mux_tree_lock(adapter); >> >> /* Retry automatically on arbitration loss */ >> orig_jiffies = jiffies; >> @@ -1871,7 +1950,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_mux_tree_unlock(adapter); >> } else >> res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, >> command, protocol, data); > > I'm worried that this approach is risky. We now use i2c_mux_tree_*lock > internally, but still expose the i2c_*lock_adapter() API externally. > This API is _not_ mux-aware, and will do the wrong thing if called on a > bus segment behind a mux. > > I'd rather not have two APIs when we really only need one. Let's just > update the current public API to be mux-aware, and use that everywhere. Agree, but I'd vote to do it not inline as its now. Otherwise we start to export mux implementation details in i2c.h > >> diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c >> index f4110aa..da5327f 100755 >> --- a/drivers/i2c/i2c-dev.c >> +++ b/drivers/i2c/i2c-dev.c >> @@ -193,12 +193,56 @@ static int i2cdev_check(struct device *dev, void *addrp) >> return dev->driver ? -EBUSY : 0; >> } >> >> +static int i2cdev_parent_is_i2c_adapter(const struct i2c_adapter *adapter) >> +{ >> + return adapter->dev.parent != NULL >> + && adapter->dev.parent->bus == &i2c_bus_type; >> +} > > This is an exact duplicate of the same function in i2c-core. I'd rather > make it an inline function in <linux/i2c.h>, or export the one from > i2c-core.c. We have enough duplicate code in i2c-dev.c already :( Must this and the above be done in an extra patch or can it be mixed into this mux patch? -- KR Michael ^ permalink raw reply [flat|nested] 16+ messages in thread
[parent not found: <4C173F61.8000007-Mmb7MZpHnFY@public.gmane.org>]
* Re: [PATCH v2 1/2] i2c: Multiplexed I2C bus core support [not found] ` <4C173F61.8000007-Mmb7MZpHnFY@public.gmane.org> @ 2010-06-15 9:00 ` Jean Delvare 0 siblings, 0 replies; 16+ messages in thread From: Jean Delvare @ 2010-06-15 9:00 UTC (permalink / raw) To: Michael Lawnick; +Cc: Linux I2C Hi Michael, On Tue, 15 Jun 2010 10:52:49 +0200, Michael Lawnick wrote: > Hi Jean, > > Jean Delvare said the following: > ... > >> if (adapter->algo->smbus_xfer) { > >> - rt_mutex_lock(&adapter->bus_lock); > >> + i2c_mux_tree_lock(adapter); > >> > >> /* Retry automatically on arbitration loss */ > >> orig_jiffies = jiffies; > >> @@ -1871,7 +1950,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_mux_tree_unlock(adapter); > >> } else > >> res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, > >> command, protocol, data); > > > > I'm worried that this approach is risky. We now use i2c_mux_tree_*lock > > internally, but still expose the i2c_*lock_adapter() API externally. > > This API is _not_ mux-aware, and will do the wrong thing if called on a > > bus segment behind a mux. > > > > I'd rather not have two APIs when we really only need one. Let's just > > update the current public API to be mux-aware, and use that everywhere. > > Agree, but I'd vote to do it not inline as its now. Otherwise we start to export > mux implementation details in i2c.h Of course. In fact I am preparing a patch which moves these functions to i2c-core, uninlines them, and uses them as appropriate. Your multiplexing patch would stack on top of it, so it is nice and clean and self-contained. > >> diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c > >> index f4110aa..da5327f 100755 > >> --- a/drivers/i2c/i2c-dev.c > >> +++ b/drivers/i2c/i2c-dev.c > >> @@ -193,12 +193,56 @@ static int i2cdev_check(struct device *dev, void *addrp) > >> return dev->driver ? -EBUSY : 0; > >> } > >> > >> +static int i2cdev_parent_is_i2c_adapter(const struct i2c_adapter *adapter) > >> +{ > >> + return adapter->dev.parent != NULL > >> + && adapter->dev.parent->bus == &i2c_bus_type; > >> +} > > > > This is an exact duplicate of the same function in i2c-core. I'd rather > > make it an inline function in <linux/i2c.h>, or export the one from > > i2c-core.c. We have enough duplicate code in i2c-dev.c already :( > > Must this and the above be done in an extra patch or can it be mixed into this mux patch? Whatever is easier for you, both are fine with me. -- Jean Delvare ^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2010-06-15 9:00 UTC | newest] Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2010-05-04 12:46 [PATCH v2 1/2] i2c: Multiplexed I2C bus core support Michael Lawnick [not found] ` <4BE01741.1010909-Mmb7MZpHnFY@public.gmane.org> 2010-05-04 12:48 ` [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x Michael Lawnick [not found] ` <4BE01795.1090605-Mmb7MZpHnFY@public.gmane.org> 2010-05-04 12:52 ` Rodolfo Giometti 2010-06-09 16:41 ` Jean Delvare [not found] ` <20100609184143.06488f2f-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> 2010-06-10 10:15 ` Michael Lawnick 2010-06-13 12:11 ` Jean Delvare [not found] ` <20100613141122.1d5b1423-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> 2010-06-14 9:09 ` Michael Lawnick 2010-05-04 12:52 ` [PATCH v2 1/2] i2c: Multiplexed I2C bus core support Rodolfo Giometti 2010-05-17 7:36 ` Michael Lawnick [not found] ` <4BF0F200.5090604-Mmb7MZpHnFY@public.gmane.org> 2010-05-17 8:15 ` Jean Delvare 2010-06-10 13:24 ` Jean Delvare [not found] ` <20100610152411.5497461c-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> 2010-06-11 6:31 ` Michael Lawnick [not found] ` <4C11D85F.7010604-Mmb7MZpHnFY@public.gmane.org> 2010-06-11 9:40 ` Jean Delvare [not found] ` <20100611114003.01c15f62-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org> 2010-06-15 8:53 ` Michael Lawnick 2010-06-15 8:52 ` Michael Lawnick [not found] ` <4C173F61.8000007-Mmb7MZpHnFY@public.gmane.org> 2010-06-15 9:00 ` Jean Delvare
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.