i2c: Multiplexed I2C bus core support.
Subsystems (3)
PATCH 1/2 i2c: Multiplexed I2C bus core support.
Subject: [PATCH 1/2] i2c: Multiplexed I2C bus core support.
--- drivers/i2c/Kconfig | 10 +++ drivers/i2c/Makefile | 3 +- drivers/i2c/i2c-core.c | 73 +++++++++++++++++-- drivers/i2c/i2c-dev.c | 32 ++++++++- drivers/i2c/i2c-mux.c | 167 ++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/muxes/Kconfig | 8 ++ drivers/i2c/muxes/Makefile | 6 ++ include/linux/i2c.h | 14 ++++ 8 files changed, 303 insertions(+), 10 deletions(-) create mode 100755 drivers/i2c/i2c-mux.c create mode 100755 drivers/i2c/muxes/Kconfig create mode 100755 drivers/i2c/muxes/Makefile
drivers/i2c/Kconfig +10 -0
@@ -35,6 +35,16 @@ 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. + +source drivers/i2c/muxes/Kconfig + config I2C_CHARDEV tristate "I2C device interface" help
drivers/i2c/Makefile +2 -1
@@ -4,8 +4,9 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2c-core.o +obj-$(CONFIG_I2C_MUX) += i2c-mux.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o -obj-y += busses/ chips/ algos/ +obj-y += busses/ chips/ muxes/ algos/ ifeq ($(CONFIG_I2C_DEBUG_CORE),y) EXTRA_CFLAGS += -DDEBUG
drivers/i2c/i2c-core.c +3 -1
@@ -20,7 +20,9 @@/* With some changes from Kyösti Mälkki <[email protected]>. All SMBus-related things are written by Frodo Looijaard <[email protected]> SMBus 2.0 support by Mark Studebaker <mdsxyz123-/[email protected]> and -Jean Delvare <[email protected]> */ +Jean Delvare <[email protected]> +Mux support by Rodolfo Giometti <giometti-AVVDYK/kqiJWk0Htik3J/[email protected]> and +Michael Lawnick <[email protected]>*/ #include <linux/module.h> #include <linux/kernel.h>
@@ -937,9 +939,66 @@ static int __i2c_check_addr(struct device *dev,
void *addrp)
return 0;
}
+static int i2c_check_clients(struct i2c_adapter *adapter, int addr) +{
+ int result = 0;
+
+ result = device_for_each_child(&adapter->dev, &addr, __i2c_check_addr);
+
+ if (!result && (adapter->dev.parent->bus == &i2c_bus_type))
+ result = i2c_check_clients(to_i2c_adapter(adapter->dev.parent), 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 = 0;
+
+ result = i2c_check_clients(adapter, addr);
+
+ if (!result && (adapter->dev.parent->bus == &i2c_bus_type))
+ {
+ struct i2c_client dummy;
+ char buff;
+
+ dummy.adapter = to_i2c_adapter(adapter->dev.parent);
+ dummy.addr = addr;
+ dummy.flags = 0;
+
+ result = i2c_master_recv(&dummy, &buff, 1) == 1;
+ }
+
+ return result;
+}
+
+static void i2c_mux_tree_lock(struct i2c_adapter *adap) +{
+ mutex_lock_nested(&adap->bus_lock, SINGLE_DEPTH_NESTING);
+ if(adap->dev.parent->bus == &i2c_bus_type)
+ i2c_mux_tree_lock(to_i2c_adapter(adap->dev.parent));
+}
+
+static int i2c_mux_tree_trylock(struct i2c_adapter *adap) +{
+ int ret;
+
+ ret = mutex_trylock(&adap->bus_lock);
+
+ if(ret && (adap->dev.parent->bus == &i2c_bus_type)) {
+ ret = i2c_mux_tree_trylock(to_i2c_adapter(adap->dev.parent));
+ if (!ret)
+ mutex_unlock(&adap->bus_lock);
+ }
+
+ return ret;
+}
+
+static void i2c_mux_tree_unlock(struct i2c_adapter *adap) +{
+ if(adap->dev.parent->bus == &i2c_bus_type)
+ i2c_mux_tree_unlock(to_i2c_adapter(adap->dev.parent));
+ mutex_unlock(&adap->bus_lock);
}
/**
@@ -1092,12 +1151,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
#endif
if (in_atomic() || irqs_disabled()) {
- ret = mutex_trylock(&adap->bus_lock);
+ ret = i2c_mux_tree_trylock(adap);
if (!ret)
/* I2C activity is ongoing. */ return -EAGAIN;
} else {
- mutex_lock_nested(&adap->bus_lock, adap->level);
+ i2c_mux_tree_lock(adap);
}
/* Retry automatically on arbitration loss */
@@ -1109,7 +1168,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
- mutex_unlock(&adap->bus_lock);
+ i2c_mux_tree_unlock(adap);
return ret;
} else {
@@ -1913,7 +1972,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) {
- mutex_lock(&adapter->bus_lock);
+ i2c_mux_tree_lock(adapter);
/* Retry automatically on arbitration loss */ orig_jiffies = jiffies;
@@ -1927,7 +1986,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
orig_jiffies + adapter->timeout))
break;
}
- mutex_unlock(&adapter->bus_lock);
+ i2c_mux_tree_unlock(adapter);
} else
res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
command, protocol, data);
drivers/i2c/i2c-dev.c +0 -0
@@ -190,16 +190,44 @@ static int i2cdev_check(struct device *dev, void
*addrp)
if (!client || client->addr != *(unsigned int *)addrp)
return 0;
-
+
return dev->driver ? -EBUSY : 0;
}
+static int i2cdev_check_clients(struct i2c_adapter *adapter, int addr) +{
+ int result = 0;
+
+ result = device_for_each_child(&adapter->dev, &addr, i2cdev_check);
+
+ if (!result && (adapter->dev.parent->bus == &i2c_bus_type))
+ result = i2cdev_check_clients(to_i2c_adapter(adapter->dev.parent), 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 = 0;
+
+ result = i2cdev_check_clients(adapter, addr);
+
+ if (!result && (adapter->dev.parent->bus == &i2c_bus_type))
+ {
+ struct i2c_client dummy;
+ char buff;
+
+ dummy.adapter = to_i2c_adapter(adapter->dev.parent);
+ dummy.addr = addr;
+ dummy.flags = 0;
+
+ result = i2c_master_recv(&dummy, &buff, 1) == 1;
+ }
+
+ return result;
}
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
drivers/i2c/i2c-mux.c new file +7 -0
@@ -0,0 +1,167 @@ +/* +* Multiplexed I2C bus driver. +* +* Copyright (c) 2008-2009 Rodolfo Giometti <[email protected]> +* Copyright (c) 2008-2009 Eurotech S.p.A. <[email protected]> +* +* Simplifies access to complex multiplexed I2C bus topologies, by
presenting
+ * each multiplexed bus segment as a virtual I2C adapter. Supports multi-level
+ * mux'ing (mux behind a mux).
+ *
+ * Based on:
+ * i2c-virt.c from Kumar Gala <[email protected]> + * i2c-virtual.c from Copyright (c) 2004 Google, Inc. (Ken Harrenstien) + * i2c-virtual.c from Brian Kuschak <bkuschak-/[email protected]>
+ * which was:
+ * Adapted from i2c-adap-ibm_ocp.c
+ * Original file Copyright 2000-2002 MontaVista Software Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+
+struct i2c_mux_priv {
+ struct i2c_adapter adap;
+ struct i2c_algorithm algo;
+
+ struct i2c_adapter *parent_adap;
+ void *client; /* The mux chip/device */
+ u32 id; /* the mux id */
+
+ int (*select)(struct i2c_adapter *, void *, u32);
+ int (*deselect)(struct i2c_adapter *, void *, u32);
+};
+
+#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_adap;
+ int ret;
+
+ /* Select the right mux port and perform the transfer. */
+
+ ret = priv->select(parent, priv->client, priv->id);
+ if (ret >= 0)
+ ret = parent->algo->master_xfer(parent, msgs, num);
+ priv->deselect(parent, priv->client, priv->id);
+
+ 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_adap;
+ int ret;
+
+ /* Select the right mux port and perform the transfer. */
+
+ ret = priv->select(parent, priv->client, priv->id);
+ if (ret == 0)
+ ret = parent->algo->smbus_xfer(parent, addr, flags,
+ read_write, command, size, data);
+ priv->deselect(parent, priv->client, priv->id);
+
+ 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_adap;
+
+ return parent->algo->functionality(parent);
+}
+
+struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
+ void *client, u32 force_nr, u32 mux_val,
+ int (*select_cb) (struct i2c_adapter *,
+ void *, u32),
+ int (*deselect_cb) (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_adap = parent;
+ priv->client = client;
+ priv->id = mux_val;
+ priv->select = select_cb;
+ priv->deselect = deselect_cb;
+
+ /* Need to do algo dynamically because we don't know ahead
+ * of time what sort of physical adapter we'll be dealing with.
+ */
+ 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 (mux %02x)",
+ i2c_adapter_id(parent), mux_val);
+ priv->adap.owner = THIS_MODULE;
+ priv->adap.id = i2c_adapter_id(parent);
+ priv->adap.algo = &priv->algo;
+ priv->adap.algo_data = priv;
+ priv->adap.timeout = VIRT_TIMEOUT;
+ priv->adap.retries = VIRT_RETRIES;
+ 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) {
+ kfree(priv);
+ return NULL;
+ }
+
+ dev_info(&parent->dev, "i2c-mux-%d: Virtual I2C bus - "
+ "physical bus i2c-%d, client %02x, port %d\n",
+ i2c_adapter_id(&priv->adap), i2c_adapter_id(parent), ((struct
i2c_client*)priv->client)->addr, mux_val);
+
+ return &priv->adap;
+}
+EXPORT_SYMBOL_GPL(i2c_add_mux_adapter);
+
+int i2c_del_mux_adapter(struct i2c_adapter *adap)
+{
+ struct i2c_mux_priv *priv = adap->algo_data;
+ int ret;
+
+ ret = i2c_del_adapter(adap);
+ if (ret < 0)
+ return ret;
+ kfree(priv);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_del_mux_adapter);
+
+MODULE_AUTHOR("Rodolfo Giometti <[email protected]>"); +MODULE_DESCRIPTION("Virtual I2C driver for multiplexed I2C busses"); +MODULE_LICENSE("GPL v2");
drivers/i2c/muxes/Kconfig new file +8 -0
@@ -0,0 +1,8 @@ +# +# Multiplexer I2C chip drivers configuration +# + +menu "Multiplexer I2C Chip support" + depends on I2C && I2C_MUX && EXPERIMENTAL + +endmenu
drivers/i2c/muxes/Makefile new file +6 -0
@@ -0,0 +1,6 @@ +# +# Makefile for multiplexer I2C chip drivers. + +ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) +EXTRA_CFLAGS += -DDEBUG -DI2C_DEBUG_CORE +endif
include/linux/i2c.h +14 -0
@@ -580,6 +580,20 @@ union i2c_smbus_data {#define I2C_SMBUS_BLOCK_PROC_CALL 7/* SMBus 2.0 */ #define I2C_SMBUS_I2C_BLOCK_DATA 8 +/* +* Called to create a 'virtual' i2c bus which represents a multiplexed bus +* segment. The client and mux_val are passed to the select and deselect +* callback functions to perform hardware-specific mux control. +*/ +struct i2c_adapter *i2c_add_mux_adapter (struct i2c_adapter *parent, +void *client,u32 force_nr,u32 mux_val, +int (*select_cb) (struct i2c_adapter *, +void *,u32 ), +int (*deselect_cb) (struct i2c_adapter *, +void *,u32 )); + +int i2c_del_mux_adapter (struct i2c_adapter *adap); +#ifdef __KERNEL__
Signature · 1 line
1.6.1.2
A message that is not in the archive (sent off-list or not yet received): [email protected]
Michael Lawnick
Acked-byRe: [PATCH 1/2] i2c: driver for PCA954x I2C multiplexer series.
Subject: [PATCH 2/2] i2c: driver for PCA954x I2C multiplexer series.
--- drivers/i2c/muxes/Kconfig | 9 ++ drivers/i2c/muxes/Makefile | 2 + drivers/i2c/muxes/pca954x.c | 325 +++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pca954x.h | 13 ++ 4 files changed, 349 insertions(+), 0 deletions(-) create mode 100755 drivers/i2c/muxes/pca954x.c create mode 100755 include/linux/i2c/pca954x.h
drivers/i2c/muxes/Kconfig +9 -0
@@ -5,4 +5,13 @@ menu "Multiplexer I2C Chip support" depends on I2C && I2C_MUX && EXPERIMENTAL +config I2CMUX_PCA954x + tristate "Philips PCA953x 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
drivers/i2c/muxes/Makefile +2 -0
@@ -1,6 +1,8 @@ # # Makefile for multiplexer I2C chip drivers. +obj-$(CONFIG_I2CMUX_PCA954x) += pca954x.o + ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG -DI2C_DEBUG_CORE endif
drivers/i2c/muxes/pca954x.c new file +14 -0
@@ -0,0 +1,325 @@ +/* +* I2C multiplexer +* +* Copyright (c) 2008-2009 Rodolfo Giometti <[email protected]> +* Copyright (c) 2008-2009 Eurotech S.p.A. <[email protected]> +* +* 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 (normally at 0x70). 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 <[email protected]>
+ * 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-/[email protected]>
+ * and
+ * pca9540.c from Jean Delvare <[email protected]>, which was + * based on pcf8574.c from the same project by Frodo Looijaard, + * Philip Edelbrock, Dan Eaton and Aurelien Jarno.
+ *
+ * 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/i2c.h>
+#include <linux/init.h>
+#include <linux/platform_device.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 {
+ struct i2c_client *client;
+ struct i2c_client dev;
+ unsigned int type;
+ struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
+
+ u8 last_chan;
+ unsigned int deselect_on_exit:1;
+ int (*is_chan_connected)(struct i2c_adapter *adap,
+ struct i2c_client *client, u32 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 struct chip_desc chips[] = {
+ [pca_9540] = {
+ .nchans = 2,
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9542] = {
+ .nchans = 2,
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9543] = {
+ .nchans = 2,
+ .enable = 0x0,
+ .muxtype = pca954x_isswi,
+ },
+ [pca_9544] = {
+ .nchans = 4,
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9545] = {
+ .nchans = 4,
+ .enable = 0x0,
+ .muxtype = pca954x_isswi,
+ },
+ [pca_9546] = {
+ .nchans = 4,
+ .enable = 0x0,
+ .muxtype = pca954x_isswi,
+ },
+ [pca_9547] = {
+ .nchans = 8,
+ .enable = 0x8,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9548] = {
+ .nchans = 8,
+ .enable = 0x0,
+ .muxtype = pca954x_isswi,
+ },
+};
+
+static const struct i2c_device_id pca954x_id[] = {
+ { "pca9540", pca_9540 },
+ { "pca9542", pca_9542 },
+ { "pca9543", pca_9543 },
+ { "pca9544", pca_9544 },
+ { "pca9545", pca_9545 },
+ { "pca9546", pca_9546 },
+ { "pca9547", pca_9547 },
+ { "pca9548", pca_9548 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pca954x_id);
+
+static int pca954x_xfer(struct i2c_adapter *adap,
+ struct i2c_client *client, int read_write, u8 *val)
+{
+ int ret = -ENODEV;
+
+ if (adap->algo->master_xfer) {
+ struct i2c_msg msg;
+ char buf[1];
+
+ msg.addr = client->addr;
+ msg.flags = (read_write == I2C_SMBUS_READ ? I2C_M_RD : 0);
+ msg.len = 1;
+ buf[0] = *val;
+ msg.buf = buf;
+ ret = adap->algo->master_xfer(adap, &msg, 1);
+ } else if (adap->algo->smbus_xfer) {
+ union i2c_smbus_data data;
+ ret = adap->algo->smbus_xfer(adap, client->addr,
+ client->flags & I2C_M_TEN,
+ read_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((struct i2c_client *) client);
+ struct chip_desc *chip = &chips[data->type];
+ u8 regval = 0;
+ int ret = 0;
+
+ /* Callback to check for bus connection */
+ if (data->is_chan_connected) {
+ ret = data->is_chan_connected(adap, client, chan);
+ if (!ret)
+ return -EBUSY;
+ }
+
+ /* 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_xfer(adap, client, I2C_SMBUS_WRITE, ®val);
+ 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((struct i2c_client *) client);
+ struct chip_desc *chip = &chips[data->type];
+ u8 regval = 0;
+
+ /* Check if we have to stay on the last channel we selected */
+ if (!data->deselect_on_exit && (chip->muxtype == pca954x_ismux))
+ return 0;
+
+ /* Signal we are deselecting active channel */
+ data->last_chan = -1;
+
+ regval = chan | ~chip->enable;
+ return pca954x_xfer(adap, client, I2C_SMBUS_WRITE, ®val);
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+static int 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 i, n, f;
+ 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. This does two things: it verifies
+ * that the mux is in fact present, and fetches its current
+ * contents for possible use with a future deselect algorithm.
+ */
+ i = i2c_smbus_read_byte(client);
+ if (i < 0) {
+ dev_warn(&client->dev, "probe failed\n");
+ goto exit_free;
+ }
+
+ data->type = id->driver_data;
+ data->last_chan = -1; /* force the first selection */
+
+ /* Now create virtual busses and adapters for them */
+ for (i = 0; i < chips[data->type].nchans; i++) {
+ f = 0; /* dynamic adap number */
+ if (pdata && (i < pdata->num_modes))
+ f = pdata->modes[i].adap_id; /* force static number */
+
+ data->virt_adaps[i] = i2c_add_mux_adapter(adap, client, f, i,
+ &pca954x_select_chan,
+ &pca954x_deselect_mux);
+ if (data->virt_adaps[i] == NULL) {
+ ret = -ENODEV;
+ goto virt_reg_failed;
+ }
+
+ data->deselect_on_exit = pdata ?
+ pdata->modes[i].deselect_on_exit : 1;
+ data->is_chan_connected = pdata ?
+ pdata->modes[i].is_chan_connected : NULL;
+ }
+
+ dev_info(&client->dev, "registered %d virtual busses for I2C %s %s\n",
+ i, chips[data->type].muxtype == pca954x_ismux ?
+ "mux" : "switch", client->name);
+
+ return 0;
+
+virt_reg_failed:
+ for (n = 0; n < i; n++)
+ i2c_del_mux_adapter(data->virt_adaps[n]);
+exit_free:
+ kfree(data);
+err:
+ return ret;
+}
+
+static int __devexit pca954x_remove(struct i2c_client *client) +{
+ struct pca954x *data = i2c_get_clientdata(client);
+ 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 <[email protected]>"); +MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); +MODULE_LICENSE("GPL v2");
include/linux/i2c/pca954x.h new file +13 -0
@@ -0,0 +1,13 @@ +/* Platform data for the PCA954x I2C multiplexers */ + +struct pca954x_platform_mode { +int adap_id; +unsigned int deselect_on_exit:1 ; +int (*is_chan_connected)(struct i2c_adapter *adap, +struct i2c_client *client,u32 chan); +}; + +struct pca954x_platform_data { +struct pca954x_platform_mode *modes; +int num_modes; +};
Signature · 1 line
1.6.1.2
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 1/2] i2c: driver for PCA954x I2C multiplexer series.
ignore this noise please
PATCH 2/2 i2c: driver for PCA954x I2C multiplexer series.
In reply to a message not in the archiveSometimes you check and check and... and then sh*# happens.
Subject: [PATCH 2/2] i2c: driver for PCA954x I2C multiplexer series.
--- drivers/i2c/muxes/Kconfig | 9 ++ drivers/i2c/muxes/Makefile | 2 + drivers/i2c/muxes/pca954x.c | 325 +++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pca954x.h | 13 ++ 4 files changed, 349 insertions(+), 0 deletions(-) create mode 100755 drivers/i2c/muxes/pca954x.c create mode 100755 include/linux/i2c/pca954x.h
drivers/i2c/muxes/Kconfig +9 -0
@@ -5,4 +5,13 @@ menu "Multiplexer I2C Chip support" depends on I2C && I2C_MUX && EXPERIMENTAL +config I2CMUX_PCA954x + tristate "Philips PCA953x 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
drivers/i2c/muxes/Makefile +2 -0
@@ -1,6 +1,8 @@ # # Makefile for multiplexer I2C chip drivers. +obj-$(CONFIG_I2CMUX_PCA954x) += pca954x.o + ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG -DI2C_DEBUG_CORE endif
drivers/i2c/muxes/pca954x.c new file +14 -0
@@ -0,0 +1,325 @@ +/* +* I2C multiplexer +* +* Copyright (c) 2008-2009 Rodolfo Giometti <[email protected]> +* Copyright (c) 2008-2009 Eurotech S.p.A. <[email protected]> +* +* 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 (normally at 0x70). 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 <[email protected]>
+ * 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-/[email protected]>
+ * and
+ * pca9540.c from Jean Delvare <[email protected]>, which was + * based on pcf8574.c from the same project by Frodo Looijaard, + * Philip Edelbrock, Dan Eaton and Aurelien Jarno.
+ *
+ * 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/i2c.h>
+#include <linux/init.h>
+#include <linux/platform_device.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 {
+ struct i2c_client *client;
+ struct i2c_client dev;
+ unsigned int type;
+ struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
+
+ u8 last_chan;
+ unsigned int deselect_on_exit:1;
+ int (*is_chan_connected)(struct i2c_adapter *adap,
+ struct i2c_client *client, u32 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 struct chip_desc chips[] = {
+ [pca_9540] = {
+ .nchans = 2,
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9542] = {
+ .nchans = 2,
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9543] = {
+ .nchans = 2,
+ .enable = 0x0,
+ .muxtype = pca954x_isswi,
+ },
+ [pca_9544] = {
+ .nchans = 4,
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9545] = {
+ .nchans = 4,
+ .enable = 0x0,
+ .muxtype = pca954x_isswi,
+ },
+ [pca_9546] = {
+ .nchans = 4,
+ .enable = 0x0,
+ .muxtype = pca954x_isswi,
+ },
+ [pca_9547] = {
+ .nchans = 8,
+ .enable = 0x8,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9548] = {
+ .nchans = 8,
+ .enable = 0x0,
+ .muxtype = pca954x_isswi,
+ },
+};
+
+static const struct i2c_device_id pca954x_id[] = {
+ { "pca9540", pca_9540 },
+ { "pca9542", pca_9542 },
+ { "pca9543", pca_9543 },
+ { "pca9544", pca_9544 },
+ { "pca9545", pca_9545 },
+ { "pca9546", pca_9546 },
+ { "pca9547", pca_9547 },
+ { "pca9548", pca_9548 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pca954x_id);
+
+static int pca954x_xfer(struct i2c_adapter *adap,
+ struct i2c_client *client, int read_write, u8 *val)
+{
+ int ret = -ENODEV;
+
+ if (adap->algo->master_xfer) {
+ struct i2c_msg msg;
+ char buf[1];
+
+ msg.addr = client->addr;
+ msg.flags = (read_write == I2C_SMBUS_READ ? I2C_M_RD : 0);
+ msg.len = 1;
+ buf[0] = *val;
+ msg.buf = buf;
+ ret = adap->algo->master_xfer(adap, &msg, 1);
+ } else if (adap->algo->smbus_xfer) {
+ union i2c_smbus_data data;
+ ret = adap->algo->smbus_xfer(adap, client->addr,
+ client->flags & I2C_M_TEN,
+ read_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((struct i2c_client *) client);
+ struct chip_desc *chip = &chips[data->type];
+ u8 regval = 0;
+ int ret = 0;
+
+ /* Callback to check for bus connection */
+ if (data->is_chan_connected) {
+ ret = data->is_chan_connected(adap, client, chan);
+ if (!ret)
+ return -EBUSY;
+ }
+
+ /* 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_xfer(adap, client, I2C_SMBUS_WRITE, ®val);
+ 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((struct i2c_client *) client);
+ struct chip_desc *chip = &chips[data->type];
+ u8 regval = 0;
+
+ /* Check if we have to stay on the last channel we selected */
+ if (!data->deselect_on_exit && (chip->muxtype == pca954x_ismux))
+ return 0;
+
+ /* Signal we are deselecting active channel */
+ data->last_chan = -1;
+
+ regval = chan | ~chip->enable;
+ return pca954x_xfer(adap, client, I2C_SMBUS_WRITE, ®val);
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+static int 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 i, n, f;
+ 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. This does two things: it verifies
+ * that the mux is in fact present, and fetches its current
+ * contents for possible use with a future deselect algorithm.
+ */
+ i = i2c_smbus_read_byte(client);
+ if (i < 0) {
+ dev_warn(&client->dev, "probe failed\n");
+ goto exit_free;
+ }
+
+ data->type = id->driver_data;
+ data->last_chan = -1; /* force the first selection */
+
+ /* Now create virtual busses and adapters for them */
+ for (i = 0; i < chips[data->type].nchans; i++) {
+ f = 0; /* dynamic adap number */
+ if (pdata && (i < pdata->num_modes))
+ f = pdata->modes[i].adap_id; /* force static number */
+
+ data->virt_adaps[i] = i2c_add_mux_adapter(adap, client, f, i,
+ &pca954x_select_chan,
+ &pca954x_deselect_mux);
+ if (data->virt_adaps[i] == NULL) {
+ ret = -ENODEV;
+ goto virt_reg_failed;
+ }
+
+ data->deselect_on_exit = pdata ?
+ pdata->modes[i].deselect_on_exit : 1;
+ data->is_chan_connected = pdata ?
+ pdata->modes[i].is_chan_connected : NULL;
+ }
+
+ dev_info(&client->dev, "registered %d virtual busses for I2C %s %s\n",
+ i, chips[data->type].muxtype == pca954x_ismux ?
+ "mux" : "switch", client->name);
+
+ return 0;
+
+virt_reg_failed:
+ for (n = 0; n < i; n++)
+ i2c_del_mux_adapter(data->virt_adaps[n]);
+exit_free:
+ kfree(data);
+err:
+ return ret;
+}
+
+static int __devexit pca954x_remove(struct i2c_client *client) +{
+ struct pca954x *data = i2c_get_clientdata(client);
+ 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 <[email protected]>"); +MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); +MODULE_LICENSE("GPL v2");
include/linux/i2c/pca954x.h new file +13 -0
@@ -0,0 +1,13 @@ +/* Platform data for the PCA954x I2C multiplexers */ + +struct pca954x_platform_mode { +int adap_id; +unsigned int deselect_on_exit:1 ; +int (*is_chan_connected)(struct i2c_adapter *adap, +struct i2c_client *client,u32 chan); +}; + +struct pca954x_platform_data { +struct pca954x_platform_mode *modes; +int num_modes; +};
-- 1.6.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 2/2] i2c: driver for PCA954x I2C multiplexer series.
Hi Michael,
Quoted 6 lines · Michael Lawnick
Sometimes you check and check and... and then sh*# happens.
Subject: [PATCH 2/2] i2c: driver for PCA954x I2C multiplexer series.
Signed-off-by Rodolfo Giometti <[email protected]>Acked-by Michael Lawnick <[email protected]>
Review:
Quoted 15 lines
--- drivers/i2c/muxes/Kconfig | 9 ++ drivers/i2c/muxes/Makefile | 2 + drivers/i2c/muxes/pca954x.c | 325 +++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pca954x.h | 13 ++ 4 files changed, 349 insertions(+), 0 deletions(-) create mode 100755 drivers/i2c/muxes/pca954x.c create mode 100755 include/linux/i2c/pca954x.hdrivers/i2c/muxes/Kconfig +0
-0
drivers/i2c/muxes/Kconfig line 8
Quoted 4 lines
drivers/i2c/muxes/Kconfig +1
-0@@ -5,4 +5,13 @@ menu "Multiplexer I2C Chip support" depends on I2C && I2C_MUX && EXPERIMENTAL +config I2CMUX_PCA954x
I would prefer I2C_MUX instead of I2CMUX.
drivers/i2c/muxes/Kconfig line 9
Quoted 1 line
drivers/i2c/muxes/Kconfig +1
-0+ tristate "Philips PCA953x I2C Mux/switches"
Typo: PCA954x.
Quoted 13 lines
drivers/i2c/muxes/Kconfig +7
-0+ 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. + endmenudrivers/i2c/muxes/Makefile +0
-0
drivers/i2c/muxes/Makefile line 6
Quoted 6 lines
drivers/i2c/muxes/Makefile +2
-0@@ -1,6 +1,8 @@ # # Makefile for multiplexer I2C chip drivers. +obj-$(CONFIG_I2CMUX_PCA954x) += pca954x.o + ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
CONFIG_I2C_DEBUG_CHIP is gone.
Quoted 12 lines
drivers/i2c/muxes/Makefile +0
-0EXTRA_CFLAGS += -DDEBUG -DI2C_DEBUG_CORE endifdrivers/i2c/muxes/pca954x.c new file +4
-0@@ -0,0 +1,325 @@ +/* +* I2C multiplexer +* +* Copyright (c) 2008-2009 Rodolfo Giometti <[email protected]>
drivers/i2c/muxes/pca954x.c line 14
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+* Copyright (c) 2008-2009 Eurotech S.p.A. <[email protected]> +* +* 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 (normally at 0x70). The upstream "parent" bus fans
This is confusing. 0x70 is the device's I2C address, not a register address. There is no register address for these chips, as they have a single register. So I wouldn't mention the address at all.
Quoted 9 lines
drivers/i2c/muxes/pca954x.c new file +9
-0+* 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 <[email protected]> +* Copyright (C) 2006 +*
drivers/i2c/muxes/pca954x.c line 33
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+* Based on: +* pca954x.c from Ken Harrenstien +* Copyright (C) 2004 Google, Inc. (Ken Harrenstien) +* +* Based on: +* i2c-virtual_cb.c from Brian Kuschak <bkuschak-/[email protected]> +* and +* pca9540.c from Jean Delvare <[email protected]>, which was +* based on pcf8574.c from the same project by Frodo Looijaard, +* Philip Edelbrock, Dan Eaton and Aurelien Jarno.
Once again I think you go a little too deep in history. You can skip the reference to the pcf8574.c drivers which is completely unrelated to I2C bus multiplexing.
drivers/i2c/muxes/pca954x.c line 42
Quoted 9 lines
drivers/i2c/muxes/pca954x.c new file +9
-0+* +* 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/i2c.h> +#include <linux/init.h> +#include <linux/platform_device.h>
You don't need this one. OTOH you need <linux/slab.h> for kzalloc() and <linux/device.h> for dev_info() etc.
Quoted 9 lines
drivers/i2c/muxes/pca954x.c new file +9
-0+ +#include <linux/i2c/pca954x.h> + +#define PCA954X_MAX_NCHANS 8 + +enum pca_type { + pca_9540, + pca_9542, + pca_9543,
drivers/i2c/muxes/pca954x.c line 61
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+ pca_9544, + pca_9545, + pca_9546, + pca_9547, + pca_9548, +}; + +struct pca954x { +struct i2c_client *client; +struct i2c_client dev;
You don't use either anywhere.
drivers/i2c/muxes/pca954x.c line 62
Quoted 1 line
drivers/i2c/muxes/pca954x.c new file +1
-0+unsigned int type;
Why not enum pca_type?
drivers/i2c/muxes/pca954x.c line 68
Quoted 6 lines
drivers/i2c/muxes/pca954x.c new file +6
-0+struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS ]; + +u8 last_chan; + unsigned int deselect_on_exit:1; +int (*is_chan_connected)(struct i2c_adapter *adap, +struct i2c_client *client,u32 chan);
What's this?
drivers/i2c/muxes/pca954x.c line 77
Quoted 9 lines
drivers/i2c/muxes/pca954x.c new file +9
-0+}; + +struct chip_desc { +u8 nchans; +u8 enable;/* used for muxes only */ +enum muxtype { + pca954x_ismux =0 , + pca954x_isswi + } muxtype;
Why don't you simply go with a "is_switch" flag? Or you could even use enable == 0 to differentiate between muxes and switches.
drivers/i2c/muxes/pca954x.c line 81
Quoted 4 lines
drivers/i2c/muxes/pca954x.c new file +4
-0+}; + +/* Provide specs for the PCA954x types we know about */ +static struct chip_desc chips[] = {
Could be const.
drivers/i2c/muxes/pca954x.c line 94
Quoted 13 lines
drivers/i2c/muxes/pca954x.c new file +13
-0+ [pca_9540] = { + .nchans = 2, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9542] = { + .nchans = 2, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9543] = { + .nchans = 2, + .enable = 0x0,
You don't have to explicitly mention 0 values when initializing static structs.
Quoted 18 lines
drivers/i2c/muxes/pca954x.c new file +18
-0+ .muxtype = pca954x_isswi, + }, + [pca_9544] = { + .nchans = 4, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9545] = { + .nchans = 4, + .enable = 0x0, + .muxtype = pca954x_isswi, + }, + [pca_9546] = { + .nchans = 4, + .enable = 0x0, + .muxtype = pca954x_isswi, + }, + [pca_9547] = {
drivers/i2c/muxes/pca954x.c line 122
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+ .nchans = 8, + .enable = 0x8, + .muxtype = pca954x_ismux, + }, + [pca_9548] = { + .nchans = 8, + .enable = 0x0, + .muxtype = pca954x_isswi, + }, +};
As far as I can see, the 9540 and 9542 are handled the same, and so are the 9545 and 9546. So you could remove 2 entries from the table above, saving some space.
Quoted 6 lines
drivers/i2c/muxes/pca954x.c new file +6
-0+ +static const struct i2c_device_id pca954x_id[] = { + {"pca9540" , pca_9540 }, + {"pca9542" , pca_9542 }, + {"pca9543" , pca_9543 }, + {"pca9544" , pca_9544 },
drivers/i2c/muxes/pca954x.c line 138
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+ { "pca9545", pca_9545 }, + { "pca9546", pca_9546 }, + { "pca9547", pca_9547 }, + { "pca9548", pca_9548 }, + { } +}; +MODULE_DEVICE_TABLE (i2c, pca954x_id); + +static int pca954x_xfer (struct i2c_adapter *adap, +struct i2c_client *client,int read_write,u8 *val)
val should be const u8 *, as you do not modify the value (but see below).
drivers/i2c/muxes/pca954x.c line 150
Quoted 12 lines
drivers/i2c/muxes/pca954x.c new file +12
-0+{ +int ret = -ENODEV ; + +if (adap->algo->master_xfer) { +struct i2c_msg msg; +char buf[1 ]; + + msg.addr = client->addr; + msg.flags = (read_write ==I2C_SMBUS_READ ?I2C_M_RD :0 ); + msg.len =1 ; + buf[0 ] = *val; + msg.buf = buf;
As you passed val by address, you might point msg.buf to it directly, without an intermediate buffer. If you want to use a buffer, then you have no reason to pass val by address.
drivers/i2c/muxes/pca954x.c line 152
Quoted 2 lines
drivers/i2c/muxes/pca954x.c new file +2
-0+ ret = adap->algo->master_xfer (adap, &msg,1 ); + }else if (adap->algo->smbus_xfer) {
The underlying adapter must implement either method, so a simple "else" will do.
drivers/i2c/muxes/pca954x.c line 155
Quoted 3 lines
drivers/i2c/muxes/pca954x.c new file +3
-0+union i2c_smbus_data data; + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags & I2C_M_TEN,
You don't actually need to care about this flag, as the PCA954x doesn't use 10-bit addressing.
drivers/i2c/muxes/pca954x.c line 158
Quoted 3 lines
drivers/i2c/muxes/pca954x.c new file +3
-0+ read_write, + *val, I2C_SMBUS_BYTE, &data); + }
As you only use the above function for writing, you could simplify it by hard-coding read_write to 0. This function is for your driver only, so there's no point it making it more complex than it needs to be. Then you might consider renaming it to pca954x_reg_write().
The above is basically a reimplementation of i2c_smbus_write_byte() bypassing the bus locking. The idea that many mux drivers will do that kind of thing doesn't really please me. It's digging deep into the guts of the i2c subsystem. It is very easy to get it wrong, and any further change to the i2c-core may have to be duplicated in all mux drivers. There's only one for now, but I presume there will be a lot more someday.
It might be better to implement "nolock" variants of i2c_transfer() and i2c_smbus_xfer(), and maybe some helper functions, so that mux drivers can just call them. I seem to recall that this is what an earlier candidate patch for I2C multiplexing support did. We can go with your approach for now, as I don't want to delay the inclusion of your patches. I'll look into this in parallel. It won't be lightweight for sure, so I have to think about it.
In the meantime, I would appreciate a comment at the top of this function, explaining what it does and why it is needed.
drivers/i2c/muxes/pca954x.c line 166
Quoted 8 lines
drivers/i2c/muxes/pca954x.c new file +8
-0+ +return ret; +} + +static int pca954x_select_chan (struct i2c_adapter *adap, +void *client,u32 chan) +{ +struct pca954x *data =i2c_get_clientdata ((struct i2c_client *) client);
Cast from void * isn't needed.
drivers/i2c/muxes/pca954x.c line 168
Quoted 2 lines
drivers/i2c/muxes/pca954x.c new file +2
-0+struct chip_desc *chip = &chips[data->type]; +u8 regval =0 ;
Useless initialization.
drivers/i2c/muxes/pca954x.c line 176
Quoted 8 lines
drivers/i2c/muxes/pca954x.c new file +8
-0+int ret =0 ; + +/* Callback to check for bus connection */ +if (data->is_chan_connected) { + ret = data->is_chan_connected (adap, client, chan); +if (!ret) +return -EBUSY ; + }
I am confused. What are you doing here? Which ports are used should be determined at initialization time.
drivers/i2c/muxes/pca954x.c line 182
Quoted 6 lines
drivers/i2c/muxes/pca954x.c new file +6
-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;
Looks good to me. If we want to be smarter, then we need to let the user pass channel aggregation information through platform data. This would certainly be useful in some cases, for example for some Tyan boards. But this can be implemented later.
Quoted 4 lines
drivers/i2c/muxes/pca954x.c new file +4
-0+ +/* Only select the channel if its different from the last channel */ +if (data->last_chan != chan) { + ret =pca954x_xfer (adap, client,I2C_SMBUS_WRITE , ®val);
drivers/i2c/muxes/pca954x.c line 196
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+ 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 ((struct i2c_client *) client);
Cast not needed.
drivers/i2c/muxes/pca954x.c line 198
Quoted 2 lines
drivers/i2c/muxes/pca954x.c new file +2
-0+struct chip_desc *chip = &chips[data->type]; +u8 regval =0 ;
Useless initialization.
drivers/i2c/muxes/pca954x.c line 201
Quoted 3 lines
drivers/i2c/muxes/pca954x.c new file +3
-0+ +/* Check if we have to stay on the last channel we selected */ +if (!data->deselect_on_exit && (chip->muxtype == pca954x_ismux))
Test looks suspicious. I can't see why muxes and switches would handle deselect_on_exit differently.
BTW, I think it would be better to set deselect to NULL if calling deselect isn't desired. There's little point in calling a function which will never do anything.
drivers/i2c/muxes/pca954x.c line 205
Quoted 4 lines
drivers/i2c/muxes/pca954x.c new file +4
-0+return 0 ; + +/* Signal we are deselecting active channel */ + data->last_chan =-1 ;
last_chan is a u8, it can't be set to -1.
drivers/i2c/muxes/pca954x.c line 207
Quoted 2 lines
drivers/i2c/muxes/pca954x.c new file +2
-0+ + regval = chan | ~chip->enable;
This looks wrong, basically it is setting all bits except the one in chip->enable to 1. I doubt this is what you want. Also, this looks valid only for mux chips, what about switches?
drivers/i2c/muxes/pca954x.c line 215
Quoted 8 lines
drivers/i2c/muxes/pca954x.c new file +8
-0+return pca954x_xfer (adap, client,I2C_SMBUS_WRITE , ®val); +} + +/* +* I2C init/probing/exit functions +*/ +static int pca954x_probe (struct i2c_client *client, +const struct i2c_device_id *id)
Missing __devinit.
drivers/i2c/muxes/pca954x.c line 219
Quoted 4 lines
drivers/i2c/muxes/pca954x.c new file +4
-0+{ +struct i2c_adapter *adap =to_i2c_adapter (client->dev.parent); +struct pca954x_platform_data *pdata = client->dev.platform_data; +int i, n, f;
Please come up with better variable names. One-letter variables are a pain to read and search for.
Quoted 16 lines
drivers/i2c/muxes/pca954x.c new file +16
-0+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. This does two things: it verifies +* that the mux is in fact present, and fetches its current
drivers/i2c/muxes/pca954x.c line 245
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+* contents for possible use with a future deselect algorithm. +*/ + i =i2c_smbus_read_byte (client); +if (i <0 ) { +dev_warn (&client->dev,"probe failed\n" ); + goto exit_free; + } + + data->type = id->driver_data; + data->last_chan =-1 ;/* force the first selection */
As I wrote above, -1 isn't a possible u8 value.
drivers/i2c/muxes/pca954x.c line 247
Quoted 2 lines
drivers/i2c/muxes/pca954x.c new file +2
-0+ +/* Now create virtual busses and adapters for them */
"busses and adapters" is redundant.
drivers/i2c/muxes/pca954x.c line 250
Quoted 3 lines
drivers/i2c/muxes/pca954x.c new file +3
-0+for (i =0 ; i < chips[data->type].nchans; i++) { + f =0 ;/* dynamic adap number */ +if (pdata && (i < pdata->num_modes))
Looks wrong. If there are fewer "modes" provided than the device has channels, then no adapters should be instantiated for the the remaining channels. The loop should iterate over min(chips[data->type].nchans, pdata->num_modes).
drivers/i2c/muxes/pca954x.c line 257
Quoted 7 lines
drivers/i2c/muxes/pca954x.c new file +7
-0+ f = pdata->modes[i].adap_id;/* force static number */ + + data->virt_adaps[i] =i2c_add_mux_adapter (adap, client, f, i, + &pca954x_select_chan, + &pca954x_deselect_mux); +if (data->virt_adaps[i] ==NULL ) { + ret = -ENODEV ;
This certainly deserves an error message in the logs.
drivers/i2c/muxes/pca954x.c line 262
Quoted 5 lines
drivers/i2c/muxes/pca954x.c new file +5
-0+ goto virt_reg_failed; + } + + data->deselect_on_exit = pdata ? + pdata->modes[i].deselect_on_exit :1 ;
What was your rationale for defaulting to 1? Limiting the total capacitance of the overall bus? As long as we only select one channel at a time, I don't think this is likely to be an issue.
I would default to 0, for performance reasons. This is also more consistent. Otherwise, not specifying platform data at all results in deselect_on_exit = 1 while specifying platform data but not specifying deselect_on_exit results in deselect_on_exit = 0. That's confusing.
drivers/i2c/muxes/pca954x.c line 264
Quoted 2 lines
drivers/i2c/muxes/pca954x.c new file +2
-0+ data->is_chan_connected = pdata ? + pdata->modes[i].is_chan_connected :NULL ;
This is racy. You need to set all attributes _before_ you register the adapter.
drivers/i2c/muxes/pca954x.c line 275
Quoted 11 lines
drivers/i2c/muxes/pca954x.c new file +11
-0+ } + +dev_info (&client->dev,"registered %d virtual busses for I2C %s %s\n" , + i, chips[data->type].muxtype == pca954x_ismux ? +"mux" :"switch" , client->name); + +return 0 ; + +virt_reg_failed: +for (n =0 ; n < i; n++) +i2c_del_mux_adapter (data->virt_adaps[n]);
You can instead decrease i from its current value down to 0. This way you don't need to introduce another loop variable.
include/linux/i2c/pca954x.h
Quoted 56 lines
drivers/i2c/muxes/pca954x.c new file +50
-0+exit_free: +kfree (data); +err: +return ret; +} + +static int __devexit pca954x_remove(struct i2c_client *client) +{ +struct pca954x *data =i2c_get_clientdata (client); +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 <[email protected]>" ); +MODULE_DESCRIPTION ("PCA954x I2C mux/switch driver" ); +MODULE_LICENSE ("GPL v2" );include/linux/i2c/pca954x.h new file +0
-0@@ -0,0 +1,13 @@
Please add the standard copyright and license comment. Please also add standard #ifdef/#endif to protect against multiple inclusions.
include/linux/i2c/pca954x.h line 13
Quoted 13 lines
include/linux/i2c/pca954x.h new file +13
-0+/* Platform data for the PCA954x I2C multiplexers */ + +struct pca954x_platform_mode { +int adap_id; +unsigned int deselect_on_exit:1 ; +int (*is_chan_connected)(struct i2c_adapter *adap, +struct i2c_client *client,u32 chan); +}; + +struct pca954x_platform_data { +struct pca954x_platform_mode *modes; +int num_modes; +};
These structure needs full description, as it is a public interface.
As I don't have any hardware to test your code, I will try adding a special mode to i2c-stub where it would instantiate a virtual PCA9540 chip, and present 3 adapters to the user instead of 1. That way I should be able to preform some testing on my own.
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 2/2] i2c: driver for PCA954x I2C multiplexer series.
Hi Michael,
Quoted 4 lines · Jean Delvare
As I don't have any hardware to test your code, I will try adding a special mode to i2c-stub where it would instantiate a virtual PCA9540 chip, and present 3 adapters to the user instead of 1. That way I should be able to preform some testing on my own.
I've done this, and the tests results so far look good. If you can submit updated patches addressing my concerns, they should be ready to go in kernel 2.6.35. Further improvements and fixes can happen later.
Thanks,
Signature · 1 line
Jean Delvare
Michael Lawnick said the following:
Quoted 2 lines
Subject: [PATCH 1/2] i2c: Multiplexed I2C bus core support.
Ping.
(I dream to see it in .34)
Signature · 2 lines
KR Michael
A message that is not in the archive (sent off-list or not yet received): [email protected]
Hi Michael,
Quoted 6 lines · Michael Lawnick
Michael Lawnick said the following:
Quoted 1 line
Subject: [PATCH 1/2] i2c: Multiplexed I2C bus core support.
Ping.
(I dream to see it in .34)
This isn't going to happen, sorry. The new i2c feature in 2.6.34 will be SMBus alert support, which had been waiting for about 2 years. Multiplexing support will have to wait for the next merge window. Right now I am working on flushing my patch queues as much as I can. Even if I had the time to look into your patches this week (and honestly it doesn't seem realistic) it would be too late to make it into this merge window. Your patches will need some testing which can't realistically be achieved in a couple days.
I understand that you are getting impatient and I would be as well if I were you, but I'm doing the best I can with the limited time I have.
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
Jean Delvare said the following:
Quoted 8 lines
Hi Michael,
Quoted 5 lines · Michael Lawnick
Michael Lawnick said the following:
Quoted 1 line
Subject: [PATCH 1/2] i2c: Multiplexed I2C bus core support.
Ping.
Quoted 8 lines
Quoted 1 line
(I dream to see it in .34)
This isn't going to happen, sorry.
[...]I understand that you are getting impatient and I would be as well if I were you, but I'm doing the best I can with the limited time I have.
I just had a dream ...
Signature · 2 lines
KR Michael
Hi Michael,
Sorry for the long delay. Here finally comes a review of your code.
Quoted 11 lines · Michael Lawnick
Subject: [PATCH 1/2] i2c: Multiplexed I2C bus core support.
Signed-off-by Rodolfo Giometti <[email protected]>Signed-off-by Michael Lawnick <[email protected]>--- drivers/i2c/Kconfig | 10 +++ drivers/i2c/Makefile | 3 +- drivers/i2c/i2c-core.c | 73 +++++++++++++++++-- drivers/i2c/i2c-dev.c | 32 ++++++++- drivers/i2c/i2c-mux.c | 167++++++++++++++++++++++++++++++++++++++++++++
Your e-mail client wraps long lines, this caused your patch to not apply. I had to edit it manually before I was able to apply it. Please fix your setup so that I don't have to do it again next time.
Please also make sure to run your patch through scripts/checkpatch.pl. I found many trailing whitespace, and even after removing them, the script still finds 9 style errors which should be fixed.
Quoted 34 lines
drivers/i2c/muxes/Kconfig | 8 ++ drivers/i2c/muxes/Makefile | 6 ++ include/linux/i2c.h | 14 ++++ 8 files changed, 303 insertions(+), 10 deletions(-) create mode 100755 drivers/i2c/i2c-mux.c create mode 100755 drivers/i2c/muxes/Kconfig create mode 100755 drivers/i2c/muxes/Makefiledrivers/i2c/Kconfig +10
-0@@ -35,6 +35,16 @@ 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. + +source drivers/i2c/muxes/Kconfig + config I2C_CHARDEV tristate "I2C device interface" helpdrivers/i2c/Makefile +0
-0
drivers/i2c/Makefile line 9
Quoted 7 lines
drivers/i2c/Makefile +2
-1@@ -4,8 +4,9 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2c-core.o +obj-$(CONFIG_I2C_MUX) += i2c-mux.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o -obj-y += busses/ chips/ algos/ +obj-y += busses/ chips/ muxes/ algos/
This doesn't work. You can't ask the build system to operate on an empty directory. You'll have to move the inclusion of this directory to the patch adding the first mux driver. I know it's not nice, but there's no other way I know of.
Quoted 8 lines
drivers/i2c/Makefile +0
-0ifeq ($(CONFIG_I2C_DEBUG_CORE),y) EXTRA_CFLAGS += -DDEBUGdrivers/i2c/i2c-core.c +0
-0
drivers/i2c/i2c-core.c line 25
Quoted 7 lines
drivers/i2c/i2c-core.c +3
-1@@ -20,7 +20,9 @@/* With some changes from Kyösti Mälkki <[email protected]>. All SMBus-related things are written by Frodo Looijaard <[email protected]> SMBus 2.0 support by Mark Studebaker <mdsxyz123-/[email protected]> and -Jean Delvare <[email protected]> */ +Jean Delvare <[email protected]> +Mux support by Rodolfo Giometti <giometti-AVVDYK/kqiJWk0Htik3J/[email protected]> and +Michael Lawnick <[email protected]>*/
Missing space before end of comment.
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, · line 945
Quoted 11 lines
drivers/i2c/i2c-core.c +3
-0#include <linux/module.h> #include <linux/kernel.h> @@ -937,9 +939,66 @@ static int __i2c_check_addr(struct device *dev, void *addrp)return 0 ; } +static int i2c_check_clients (struct i2c_adapter *adapter,int addr) +{ +int result =0 ;
Useless initialization.
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, · line 949
Quoted 4 lines
drivers/i2c/i2c-core.c +4
-0+ + result =device_for_each_child (&adapter->dev, &addr, __i2c_check_addr); + +if (!result && (adapter->dev.parent->bus == &i2c_bus_type))
All devices don't have a parent, so you should first check that adapter->dev.parent isn't NULL. Try your code with i2c-stub for example, it would crash.
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, · line 958
Quoted 10 lines
drivers/i2c/i2c-core.c +6
-1+ result =i2c_check_clients (to_i2c_adapter (adapter->dev.parent), 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 =0 ;
Useless initialization. There are many other cases below, I won't comment on each but please fix them all.
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, · line 962
Quoted 4 lines
drivers/i2c/i2c-core.c +4
-0+ + result =i2c_check_clients (adapter, addr); + +if (!result && (adapter->dev.parent->bus == &i2c_bus_type))
Here again, you have no guarantee that the adapter device has a parent. There are many other occurrences of this below. Maybe you want a small helper function.
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, · line 966
Quoted 4 lines
drivers/i2c/i2c-core.c +4
-0+ { +struct i2c_client dummy; +char buff; +
You must zero all the struct i2c_client fields before you use it.
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, · line 972
Quoted 6 lines
drivers/i2c/i2c-core.c +6
-0+ dummy.adapter =to_i2c_adapter (adapter->dev.parent); + dummy.addr = addr; + dummy.flags =0 ; + + result =i2c_master_recv (&dummy, &buff,1 ) ==1 ; + }
I don't understand this part, please explain, and add a comment before the code if the above is really needed. I would think that the recursive address business check would be sufficient, is it not?
We should avoid as much as possible to run arbitrary transactions on the I2C bus. We are already using such arbitrary transactions for device detections, but we are trying to move away from this now. Let's not add more such cases. Some devices may hang on transactions they don't expect!
If the above is really needed, it will have to be implemented differently anyway, as you have no guarantee that the adapter supports raw I2C transactions. Most SMBus controllers don't.
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, · line 979
Quoted 7 lines
drivers/i2c/i2c-core.c +7
-0+ +return result; +} + +static void i2c_mux_tree_lock (struct i2c_adapter *adap) +{ +mutex_lock_nested (&adap->bus_lock,SINGLE_DEPTH_NESTING );
i2c-core moved to rt_mutex, so your code will need to be updated accordingly.
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, · line 982
Quoted 3 lines
drivers/i2c/i2c-core.c +3
-0+if (adap->dev.parent->bus == &i2c_bus_type) +i2c_mux_tree_lock (to_i2c_adapter (adap->dev.parent)); +}
In the end, each time one wants access to a segment of the tree, they will have to lock the root segment to succeed. So do we really need to lock each segment in turn? Can't we just look for the root and lock only it? This would be faster.
Quoted 75 lines
drivers/i2c/i2c-core.c +21
-0+ +static int i2c_mux_tree_trylock (struct i2c_adapter *adap) +{ +int ret; + + ret =mutex_trylock (&adap->bus_lock); + +if (ret && (adap->dev.parent->bus == &i2c_bus_type)) { + ret =i2c_mux_tree_trylock (to_i2c_adapter (adap->dev.parent)); +if (!ret) +mutex_unlock (&adap->bus_lock); + } + +return ret; +} + +static void i2c_mux_tree_unlock (struct i2c_adapter *adap) +{ +if (adap->dev.parent->bus == &i2c_bus_type) +i2c_mux_tree_unlock (to_i2c_adapter (adap->dev.parent)); +mutex_unlock (&adap->bus_lock); }/**
drivers/i2c/i2c-core.c +2
-2@@ -1092,12 +1151,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)#endif if (in_atomic () ||irqs_disabled ()) { - ret =mutex_trylock (&adap->bus_lock); + ret =i2c_mux_tree_trylock (adap);if (!ret)/* I2C activity is ongoing. */ return -EAGAIN ; }else { -mutex_lock_nested (&adap->bus_lock, adap->level); +i2c_mux_tree_lock (adap); }drivers/i2c/i2c-core.c +1
-1/* Retry automatically on arbitration loss */ @@ -1109,7 +1168,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (time_after(jiffies, orig_jiffies + adap->timeout)) break; } -mutex_unlock (&adap->bus_lock); +i2c_mux_tree_unlock (adap);return ret;drivers/i2c/i2c-core.c +1
-1}else {@@ -1913,7 +1972,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) { -mutex_lock (&adapter->bus_lock); +i2c_mux_tree_lock (adapter);/* Retry automatically on arbitration loss */ drivers/i2c/i2c-core.c +1
-1orig_jiffies = jiffies;@@ -1927,7 +1986,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, orig_jiffies + adapter->timeout)) break; } -mutex_unlock (&adapter->bus_lock); +i2c_mux_tree_unlock (adapter); }else res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,drivers/i2c/i2c-core.c +0
-0command, protocol, data);drivers/i2c/i2c-dev.c +0
-0
drivers/i2c/i2c-dev.c static int i2cdev_check(struct device *dev, void · line 194
Quoted 6 lines
drivers/i2c/i2c-dev.c +1
-1@@ -190,16 +190,44 @@ static int i2cdev_check(struct device *dev, void *addrp)if (!client || client->addr != *(unsigned int *)addrp)return 0 ; - +
Please don't include whitespace changes in your patch.
Quoted 30 lines
drivers/i2c/i2c-dev.c +20
-1return dev->driver ? -EBUSY :0 ; } +static int i2cdev_check_clients (struct i2c_adapter *adapter,int addr) +{ +int result =0 ; + + result =device_for_each_child (&adapter->dev, &addr, i2cdev_check); + +if (!result && (adapter->dev.parent->bus == &i2c_bus_type)) + result =i2cdev_check_clients (to_i2c_adapter (adapter->dev.parent), 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 =0 ; + + result =i2cdev_check_clients (adapter, addr); + +if (!result && (adapter->dev.parent->bus == &i2c_bus_type)) + { +struct i2c_client dummy; +char buff;
drivers/i2c/i2c-dev.c static int i2cdev_check(struct device *dev, void · line 233
Quoted 10 lines
drivers/i2c/i2c-dev.c +9
-0+ + dummy.adapter =to_i2c_adapter (adapter->dev.parent); + dummy.addr = addr; + dummy.flags =0 ; + + result =i2c_master_recv (&dummy, &buff,1 ) ==1 ; + } + +return result; }
This duplicated code in i2c-dev starts being a problem, but I guess it can't be avoided as long as i2c-dev doesn't properly integrate with the standard device driver binding model :(
Quoted 15 lines
static noinline int i2cdev_ioctl_rdrw(struct
i2c_client*client,drivers/i2c/i2c-mux.c new file +7
-0@@ -0,0 +1,167 @@ +/* +* Multiplexed I2C bus driver. +* +* Copyright (c) 2008-2009 Rodolfo Giometti <[email protected]> +* Copyright (c) 2008-2009 Eurotech S.p.A. <[email protected]> +* +* Simplifies access to complex multiplexed I2C bus topologies, by
Quoted 2 lines
presenting
+ * each multiplexed bus segment as a virtual I2C adapter. Supports
I would prefer the term "separate I2C adapter" rather than "virtual I2C adapter". There's nothing virtual about these bus segments. i2c-stub is a virtual I2C adapter.
Quoted 6 lines
multi-level
+ * mux'ing (mux behind a mux).
+ *
+ * Based on:
+ *i2c-virt.cfrom Kumar Gala <[email protected]>
+ *i2c-virtual.cfrom Copyright (c) 2004 Google, Inc. (Ken Harrenstien)
"from Copyright" looks bad.
Quoted 4 lines
+ *
i2c-virtual.cfrom Brian Kuschak <bkuschak-/[email protected]>
+ * which was:
+ * Adapted fromi2c-adap-ibm_ocp.c
+ * Original file Copyright 2000-2002 MontaVista Software Inc.
There's probably no point in going that far in history. I doubt i2c-adap-ibm_ocp.c was a bus multiplexer driver.
drivers/i2c/i2c-mux.c
Quoted 10 lines
+ *
+ * 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.
+ */drivers/i2c/i2c-mux.c new file +5
-0+ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/i2c-id.h>
You don't need this one.
drivers/i2c/i2c-mux.c
Quoted 2 lines
drivers/i2c/i2c-mux.c new file +2
-0+ +struct i2c_mux_priv {
Would be good to document what this structure represents conceptually.
drivers/i2c/i2c-mux.c
Quoted 4 lines
drivers/i2c/i2c-mux.c new file +4
-0+struct i2c_adapter adap; +struct i2c_algorithm algo; + +struct i2c_adapter *parent_adap;
Why not just "parent"?
drivers/i2c/i2c-mux.c
Quoted 1 line
drivers/i2c/i2c-mux.c new file +1
-0+void *client;/* The mux chip/device */
"client" is a confusing term, because it is heavily used in the i2c subsystem and drivers to refer to I2C devices. As the mux chip does not have to be an I2C device, I'd rather use a more generic term, such as "mux" or "mux_dev".
drivers/i2c/i2c-mux.c
Quoted 1 line
drivers/i2c/i2c-mux.c new file +1
-0+u32 id;/* the mux id */
Comment looks wrong, I think this is a channel ID for the given mux device and not a mux device ID?
drivers/i2c/i2c-mux.c
Quoted 3 lines
drivers/i2c/i2c-mux.c new file +3
-0+ +int (*select)(struct i2c_adapter *,void *,u32 ); +int (*deselect)(struct i2c_adapter *,void *,u32 );
Please name the arguments, otherwise it's difficult to figure out what each argument represents. Not sure why you don't simply pass a struct i2c_mux_priv, BTW. It contains all the information you need.
I'm unsure why deselect would take what I presume is a mux id argument? I'm not even sure what this function would do... Not all chips can deselect all their channels. Some very simple I2C multiplexers are controlled by a single GPIO pin, and must always have exactly one channel selected. So the deselect callback should certainly be optional.
Quoted 6 lines
drivers/i2c/i2c-mux.c new file +6
-0+}; + +#define VIRT_TIMEOUT (HZ/2) +#define VIRT_RETRIES 3 + +static int i2c_mux_master_xfer(struct i2c_adapter *adap,
drivers/i2c/i2c-mux.c
Quoted 10 lines
drivers/i2c/i2c-mux.c new file +10
-0+ struct i2c_msg msgs[], int num) +{ +struct i2c_mux_priv *priv = adap->algo_data; +struct i2c_adapter *parent = priv->parent_adap; +int ret; + +/* Select the right mux port and perform the transfer. */ + + ret = priv->select (parent, priv->client, priv->id); +if (ret >=0 )
Should be "ret == 0" for consistency with the next function.
drivers/i2c/i2c-mux.c
Quoted 2 lines
drivers/i2c/i2c-mux.c new file +2
-0+ ret = parent->algo->master_xfer (parent, msgs, num); + priv->deselect (parent, priv->client, priv->id);
As discussed earlier, I don't think that we want to always deselect the multiplexer channels after a transfer, for performance reasons. Probably the implementation of deselect() should be optional, and reserved to the few cases where it is really desirable (for example tuner chips behind gates to reduce electrical noise on TV adapters.)
Quoted 29 lines
drivers/i2c/i2c-mux.c new file +29
-0+ +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_adap; +int ret; + +/* Select the right mux port and perform the transfer. */ + + ret = priv->select (parent, priv->client, priv->id); +if (ret ==0 ) + ret = parent->algo->smbus_xfer (parent, addr, flags, + read_write, command, size, data); + priv->deselect (parent, priv->client, priv->id); + +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_adap;
drivers/i2c/i2c-mux.c
Quoted 10 lines
drivers/i2c/i2c-mux.c new file +10
-0+ +return parent->algo->functionality (parent); +} + +struct i2c_adapter *i2c_add_mux_adapter (struct i2c_adapter *parent, +void *client,u32 force_nr,u32 mux_val, +int (*select_cb) (struct i2c_adapter *, +void *,u32 ), +int (*deselect_cb) (struct i2c_adapter *, +void *,u32 ))
Please drop the trailing _cb, it's already pretty clear that these are callback functions.
drivers/i2c/i2c-mux.c
Quoted 5 lines
drivers/i2c/i2c-mux.c new file +5
-0+{ +struct i2c_mux_priv *priv; +int ret; + + priv =kzalloc (sizeof (struct i2c_mux_priv ),GFP_KERNEL );
You must include <linux/slab.h> for this.
Quoted 12 lines
drivers/i2c/i2c-mux.c new file +12
-0+if (!priv) +return NULL ; + +/* Set up private adapter data */ + priv->parent_adap = parent; + priv->client = client; + priv->id = mux_val; + priv->select = select_cb; + priv->deselect = deselect_cb; + +/* Need to do algo dynamically because we don't know ahead +* of time what sort of physical adapter we'll be dealing with.
drivers/i2c/i2c-mux.c
Quoted 10 lines
drivers/i2c/i2c-mux.c new file +10
-0+*/ +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 (mux %02x)",
I don't get the use of hexadecimal to represent a channel number/id. Decimal would be more readable. I would also use the term "channel" or "port" before that ID, rather than "mux".
drivers/i2c/i2c-mux.c
Quoted 3 lines
drivers/i2c/i2c-mux.c new file +3
-0+ i2c_adapter_id(parent), mux_val); + priv->adap.owner =THIS_MODULE ; + priv->adap.id =i2c_adapter_id (parent);
No, this is incorrect. I know the name i2c_adapter_id is very confusing, but it's returning a bus number, not a bus ID. If you need the parent's ID, you must copy it manually, there's no helper function for that.
drivers/i2c/i2c-mux.c
Quoted 4 lines
drivers/i2c/i2c-mux.c new file +4
-0+ priv->adap.algo = &priv->algo; + priv->adap.algo_data = priv; + priv->adap.timeout =VIRT_TIMEOUT ; + priv->adap.retries =VIRT_RETRIES ;
I don't think these make sense. The actual transaction will happen at the root level, that's where the relevant timeout and retries settings are. The values above will never be used, so I'd rather not set them.
drivers/i2c/i2c-mux.c
Quoted 9 lines
drivers/i2c/i2c-mux.c new file +9
-0+ 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 ) {
Might be worth an error message?
Quoted 8 lines
drivers/i2c/i2c-mux.c new file +7
-0+kfree (priv); +return NULL ; + } + + dev_info(&parent->dev, "i2c-mux-%d: Virtual I2C bus - " + "physical bus i2c-%d, client %02x, port %d\n", + i2c_adapter_id(&priv->adap), i2c_adapter_id(parent), ((structi2c_client*)priv->client)->addr,
mux_val);
This is wrong and will crash for non-I2C mux chips. You just can't assume that it is safe to cast priv->client to an i2c_client.
Quoted 28 lines
drivers/i2c/i2c-mux.c new file +22
-0+ +return &priv->adap; +} +EXPORT_SYMBOL_GPL (i2c_add_mux_adapter); + +int i2c_del_mux_adapter (struct i2c_adapter *adap) +{ +struct i2c_mux_priv *priv = adap->algo_data; +int ret; + + ret =i2c_del_adapter (adap); +if (ret <0 ) +return ret; +kfree (priv); + +return 0 ; +} +EXPORT_SYMBOL_GPL (i2c_del_mux_adapter); + +MODULE_AUTHOR ("Rodolfo Giometti <[email protected]>" ); +MODULE_DESCRIPTION ("Virtual I2C driver for multiplexed I2C busses" ); +MODULE_LICENSE ("GPL v2" );drivers/i2c/muxes/Kconfig new file +0
-0
drivers/i2c/muxes/Kconfig line 6
Quoted 6 lines
drivers/i2c/muxes/Kconfig new file +6
-0@@ -0,0 +1,8 @@ +# +# Multiplexer I2C chip drivers configuration +# + +menu "Multiplexer I2C Chip support" + depends on I2C && I2C_MUX && EXPERIMENTAL
I2C_MUX itself already depends in I2C, so I guess it's enough to depend on I2C_MUX.
drivers/i2c/muxes/Makefile line 4
Quoted 12 lines
drivers/i2c/muxes/Kconfig new file +2
-0+ +endmenudrivers/i2c/muxes/Makefile new file +4
-0@@ -0,0 +1,6 @@ +# +# Makefile for multiplexer I2C chip drivers. + +ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
CONFIG_I2C_DEBUG_CHIP is gone. I suggest you introduce a new option, CONFIG_I2C_DEBUG_MUX.
drivers/i2c/muxes/Makefile line 5
Quoted 1 line
drivers/i2c/muxes/Makefile new file +1
-0+EXTRA_CFLAGS += -DDEBUG -DI2C_DEBUG_CORE
-DDEBUG should be enough.
Quoted 13 lines
drivers/i2c/muxes/Makefile new file +1
-0+endifinclude/linux/i2c.h +4
-0@@ -580,6 +580,20 @@ union i2c_smbus_data {#define I2C_SMBUS_BLOCK_PROC_CALL 7/* SMBus 2.0 */ #define I2C_SMBUS_I2C_BLOCK_DATA 8 +/* +* Called to create a 'virtual' i2c bus which represents a multiplexed bus +* segment. The client and mux_val are passed to the select and deselect +* callback functions to perform hardware-specific mux control.
include/linux/i2c.h union i2c_smbus_data { · line 596
Quoted 10 lines
include/linux/i2c.h +10
-0+*/ +struct i2c_adapter *i2c_add_mux_adapter (struct i2c_adapter *parent, +void *client,u32 force_nr,u32 mux_val, +int (*select_cb) (struct i2c_adapter *, +void *,u32 ), +int (*deselect_cb) (struct i2c_adapter *, +void *,u32 )); + +int i2c_del_mux_adapter (struct i2c_adapter *adap); +
These functions are not available if mux support wasn't selected, so their declarations shouldn't be there in that case. In fact, I would suggest that you move these to a separate header, <linux/i2c-mux.h>, which only multiplexer drivers (and i2c-mux.c) would include. This keeps things nicely separated.
include/linux/i2c.h union i2c_smbus_data { · line 599
Quoted 3 lines
include/linux/i2c.h +0
-0#ifdef __KERNEL__
I'll go on with reviewing the PCA954x mux driver.
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
One thing I forgot:
Quoted 16 lines · Jean Delvare
Review
Quoted 3 lines · Michael Lawnick
(quoted diff) +3
-0+static int i2c_check_clients(struct i2c_adapter *adapter, int addr) +{ + int result = 0;Useless initialization.
drivers/i2c/Kconfig config I2C_COMPAT
Quoted 4 lines
drivers/i2c/Kconfig +4
-0+ + result = device_for_each_child(&adapter->dev, &addr, __i2c_check_addr); + + if (!result && (adapter->dev.parent->bus == &i2c_bus_type))All devices don't have a parent, so you should first check that
adapter->dev.parentisn't NULL. Try your code with i2c-stub for example, it would crash.
Quoted 4 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ result = i2c_check_clients(to_i2c_adapter(adapter->dev.parent), addr); + + return result; +}
As discussed some weeks ago, this isn't actually sufficient. You don't only need to check the parent segments for address business, you also need to check all child segments, recursively. If any child segment has a device using the address in question, then you can't use it.
This may be more difficult to implement. In particular, you'll have to pay attention to locking.
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
Jean Delvare said the following:
Quoted 12 lines
One thing I forgot:
Quoted 4 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ result = i2c_check_clients(to_i2c_adapter(adapter->dev.parent), addr); + + return result; +}As discussed some weeks ago, this isn't actually sufficient. You don't only need to check the parent segments for address business, you also need to check all child segments, recursively. If any child segment has a device using the address in question, then you can't use it.
Quoted 3 lines
This may be more difficult to implement. In particular, you'll have to pay attention to locking.
:-) This can't happen if we keep the part you commented on in the other mail about probing for client one level above. Then this situation can't arise.
Signature · 2 lines
KR Michael
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 13 lines · Michael Lawnick
Jean Delvare said the following:
Quoted 12 lines
One thing I forgot:
Quoted 4 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ result = i2c_check_clients(to_i2c_adapter(adapter->dev.parent), addr); + + return result; +}As discussed some weeks ago, this isn't actually sufficient. You don't only need to check the parent segments for address business, you also need to check all child segments, recursively. If any child segment has a device using the address in question, then you can't use it.
Quoted 6 lines
Quoted 3 lines
This may be more difficult to implement. In particular, you'll have to pay attention to locking.
:-) This can't happen if we keep the part you commented on in the other mail about probing for client one level above. Then this situation can't arise.
I don't understand. In your code, the probe is done at the parent level, where address business had already been tested. What is needed is child segments checking, so the other side of the tree. I just can't see how your code would help with that.
Can you please explain why the probe is needed, and what it is doing that the standard address business check didn't cover already?
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
Jean Delvare said the following:
Quoted 21 lines · Michael Lawnick
Quoted 13 lines
Jean Delvare said the following:
Quoted 12 lines
One thing I forgot:
Quoted 4 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ result = i2c_check_clients(to_i2c_adapter(adapter->dev.parent), addr); + + return result; +}As discussed some weeks ago, this isn't actually sufficient. You don't only need to check the parent segments for address business, you also need to check all child segments, recursively. If any child segment has a device using the address in question, then you can't use it.
Quoted 6 lines
Quoted 3 lines
This may be more difficult to implement. In particular, you'll have to pay attention to locking.
:-) This can't happen if we keep the part you commented on in the other mail about probing for client one level above. Then this situation can't arise.
Quoted 8 lines
I don't understand. In your code, the probe is done at the parent level, where address business had already been tested. What is needed is child segments checking, so the other side of the tree. I just can't see how your code would help with that.
Can you please explain why the probe is needed, and what it is doing that the standard address business check didn't cover already?
Well, these are my thoughts:
The generic situation is
--- MUXn-1 --- MUXn -+- MUXn+1 ---
|
client/deviceA device gets physically visible on all buses beyond the one it is connected to.
Given the bus tree we can decide whether a device is really present on a particular bus if we
- H/W probe it on selected level. If does not respond, the current bus is wrong.
- H/W probe it one level higher. If it responds, the current bus is wrong. The first probing is already done in standard initialization sequence. I added the second probing. By recursively calling __i2c_check_addr() it is possible to reduce H/W-probing to ambiguous cases.
The current implementation implies that mux'es are reset to 'neutral' after every bus transaction. If this would not be the case, switching of MUXn+1 to neutral needed to be added.
Signature · 2 lines
KR Michael
A message that is not in the archive (sent off-list or not yet received): [email protected]
Hi Michael,
Quoted 25 lines · Michael Lawnick
Jean Delvare said the following:
Quoted 14 lines · Michael Lawnick
Quoted 6 lines
Jean Delvare said the following:
Quoted 5 lines
As discussed some weeks ago, this isn't actually sufficient. You don't only need to check the parent segments for address business, you also need to check all child segments, recursively. If any child segment has a device using the address in question, then you can't use it.
Quoted 6 lines
Quoted 2 lines
This may be more difficult to implement. In particular, you'll have to pay attention to locking.
:-) This can't happen if we keep the part you commented on in the other mail about probing for client one level above. Then this situation can't arise.
Quoted 7 lines
I don't understand. In your code, the probe is done at the parent level, where address business had already been tested. What is needed is child segments checking, so the other side of the tree. I just can't see how your code would help with that.
Can you please explain why the probe is needed, and what it is doing that the standard address business check didn't cover already?
Well, these are my thoughts:
Quoted 8 lines
The generic situation is
--- MUXn-1 --- MUXn -+- MUXn+1 ---
| client/deviceA device gets physically visible on all buses beyond the one it is connected to.
True.
Quoted 2 lines
Given the bus tree we can decide whether a device is really present on a particular bus if we
Please note that we don't have to do this. Other code, in the kernel or in user-space, is responsible for telling us where the devices are, and we should trust them. Auto-detected devices is a purely optional extension, the usage of which is discouraged as much as possible. My opinion is that I2C device auto-detection should be simply prohibited on multiplexes I2C buses (that is, such i2c_adapters would have their class set to 0). I even would like to enforce this, by either preventing the instantiation of a mux device on an i2c_adapter with a non-0 class, or by resetting the class to 0, with a big warning in the kernel logs, as soon as a mux device is instantiated.
As a matter of fact, your own code sets the adapter class to 0 for all "virtual adapters" (which I call branches.)
Quoted 2 lines
- H/W probe it on selected level. If does not respond, the current bus is wrong.
Or the device simply doesn't respond to the message format you used. There is no universal probe transaction for I2C devices.
Quoted 1 line
- H/W probe it one level higher. If it responds, the current bus is wrong.
And if it doesn't respond, you can't conclude.
Quoted 1 line
The first probing is already done in standard initialization sequence. I
I'm not sure I really understand what you mean there, so please let me repeat here: by default, no probing is done at the hardware level when an I2C device (i2c_client) is instantiated. i2c_register_board_info() and i2c_new_device() do not issue any transaction on the bus. Only i2c_new_probed() device and i2c_driver's .detect() callbacks do, and using these isn't recommended. The former is fine-grained enough that it should work in combination with muxes, but the latter isn't.
Quoted 2 lines
added the second probing. By recursively calling
__i2c_check_addr()it is possible to reduce H/W-probing to ambiguous cases.
We don't want to do hardware probing at all to determine address business. Look at the i2c-core code as it exists today: address business is a solely software issue as far as i2c-core is concerned; I see no good reason to change that just because we add support for multiplexing.
So, in order to determine whether an address is busy, we have to check all parent bus segments (which you already do) and all child bus segments (recursively.) Please change your patch to do this.
Quoted 3 lines
The current implementation implies that mux'es are reset to 'neutral' after every bus transaction. If this would not be the case, switching of MUXn+1 to neutral needed to be added.
As I said in my review, I don't think we want to switch all muxes back to neutral after every transaction, because it is very costly for I2C-based muxes (and your pca954x driver even offers a way to bypass the reset.) There is also the problem of the initial state of the mux chip, before we load its driver: we have no reason to believe it is in neutral state. On top of that, not all muxes _can_ be switched to neutral. Think of simple muxes controlled by a single GPIO, these have always exactly one child branch selected.
Add to this that I don't know of any mux chip which can be auto-detected. You will always have to explicitly instantiate the mux device, so you might as well explicitly instantiate all other devices on that bus. We might extend i2c_new_device() to create multiple devices at once (like i2c_register_board_info does) for this purpose.
I hope this clarified my point.
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
Jean Delvare said the following:
Quoted 8 lines
We don't want to do hardware probing at all to determine address business. Look at the i2c-core code as it exists today: address business is a solely software issue as far as i2c-core is concerned; I see no good reason to change that just because we add support for multiplexing.
I hope this clarified my point.
I think you were clear, given the unhappiness I feel :-(
I'll be back...
Signature · 2 lines
Kind Regards, Michael
Jean Delvare said the following:
Quoted 3 lines
Hi Michael,
Sorry for the long delay. Here finally comes a review of your code.
Thank you!
[Very much comments snipped]
It will take a while to process that, so reply to certain points and V2
might last some day^wwee^w time.
Signature · 2 lines
KR Michael
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 9 lines · Michael Lawnick
Jean Delvare said the following:
Quoted 3 lines
Hi Michael,
Sorry for the long delay. Here finally comes a review of your code.
Thank you!
[Very much comments snipped]
It will take a while to process that, so reply to certain points and V2
might last some day^wwee^w time.
Given how long it took me to review the code, I can't blame you for that ;)
Signature · 1 line
Jean Delvare