All of lore.kernel.org
 help / color / mirror / Atom feed
* [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

* [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

* 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 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 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

* 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 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

* 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 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

* 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

* 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

* 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

* 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]     ` <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

* 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]         ` <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.