From: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>
To: Linux I2C <linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
Cc: Michael Lawnick <ml.lawnick-Mmb7MZpHnFY@public.gmane.org>,
Rodolfo Giometti
<giometti-AVVDYK/kqiJWk0Htik3J/w@public.gmane.org>
Subject: [PATCH] i2c-stub: Add support for bus multiplexing
Date: Tue, 20 Apr 2010 14:40:34 +0200 [thread overview]
Message-ID: <20100420144034.40d5985c@hyperion.delvare> (raw)
From: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>
Subject: i2c-stub: Add support for bus multiplexing
Emulate a PCA9540 mux chip on request. There are two reasons for
implementing this:
* This makes it possible to emulate two chips at the same address.
* This lets us test the multiplexing code.
Signed-off-by: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>
Cc: Michael Lawnick <ml.lawnick-Mmb7MZpHnFY@public.gmane.org>
Cc: Rodolfo Giometti <giometti-AVVDYK/kqiJWk0Htik3J/w@public.gmane.org>
---
Documentation/i2c/i2c-stub | 9 ++
drivers/i2c/busses/i2c-stub.c | 179 +++++++++++++++++++++++++++++++++++++----
2 files changed, 171 insertions(+), 17 deletions(-)
--- linux-2.6.34-rc5.orig/drivers/i2c/busses/i2c-stub.c 2010-04-20 08:18:24.000000000 +0200
+++ linux-2.6.34-rc5/drivers/i2c/busses/i2c-stub.c 2010-04-20 13:42:53.000000000 +0200
@@ -2,7 +2,7 @@
i2c-stub.c - I2C/SMBus chip emulator
Copyright (c) 2004 Mark M. Hoffman <mhoffman-xQSgfq/1h4JiLUuM0BA3LQ@public.gmane.org>
- Copyright (C) 2007 Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>
+ Copyright (C) 2007-2010 Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@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
@@ -33,11 +33,26 @@
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \
I2C_FUNC_SMBUS_I2C_BLOCK)
+#define PCA9540_ADDR 0x70
+#define PCA9540_CHAN_MASK 0x07
+#define PCA9540_CHAN0_EN 0x04
+#define PCA9540_CHAN1_EN 0x05
+
static unsigned short chip_addr[MAX_CHIPS];
module_param_array(chip_addr, ushort, NULL, S_IRUGO);
MODULE_PARM_DESC(chip_addr,
"Chip addresses (up to 10, between 0x03 and 0x77)");
+static unsigned short pca9540_addr_0[MAX_CHIPS];
+module_param_array(pca9540_addr_0, ushort, NULL, S_IRUGO);
+MODULE_PARM_DESC(pca9540_addr_0,
+ "Chip addresses on PCA9540 mux branch 0 (up to 10)");
+
+static unsigned short pca9540_addr_1[MAX_CHIPS];
+module_param_array(pca9540_addr_1, ushort, NULL, S_IRUGO);
+MODULE_PARM_DESC(pca9540_addr_1,
+ "Chip addresses on PCA9540 mux branch 1 (up to 10)");
+
static unsigned long functionality = STUB_FUNC;
module_param(functionality, ulong, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(functionality, "Override functionality bitfield");
@@ -49,10 +64,41 @@ struct stub_chip {
};
static struct stub_chip *stub_chips;
+static struct stub_chip *pca9540_chips_0;
+static struct stub_chip *pca9540_chips_1;
+static int have_pca9540;
+static u8 pca9540_state;
+
+static struct stub_chip *pca9540_get_chip(u16 addr)
+{
+ const unsigned short *pca9540_addr;
+ struct stub_chip *pca9540_chips;
+ int i;
+
+ switch (pca9540_state & PCA9540_CHAN_MASK) {
+ case PCA9540_CHAN0_EN:
+ pca9540_addr = pca9540_addr_0;
+ pca9540_chips = pca9540_chips_0;
+ break;
+ case PCA9540_CHAN1_EN:
+ pca9540_addr = pca9540_addr_1;
+ pca9540_chips = pca9540_chips_1;
+ break;
+ default:
+ return NULL;
+ }
+
+ for (i = 0; i < MAX_CHIPS && pca9540_addr[i]; i++) {
+ if (addr == pca9540_addr[i])
+ return pca9540_chips + i;
+ }
+ return NULL;
+}
/* Return negative errno on error. */
-static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
- char read_write, u8 command, int size, union i2c_smbus_data * data)
+static s32 stub_xfer_normal(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write, u8 command, int size,
+ union i2c_smbus_data *data)
{
s32 ret;
int i, len;
@@ -65,6 +111,19 @@ static s32 stub_xfer(struct i2c_adapter
break;
}
}
+ /* If a PCA9540 mux chip is emulated, check its state to find out
+ * which devices are reachable */
+ if (have_pca9540) {
+ struct stub_chip *chip_muxed = pca9540_get_chip(addr);
+ if (chip_muxed) {
+ if (chip) {
+ dev_err(&adap->dev, "Bad bus topology, "
+ "duplicate address 0x%02x\n", addr);
+ return -EIO;
+ }
+ chip = chip_muxed;
+ }
+ }
if (!chip)
return -ENODEV;
@@ -157,6 +216,39 @@ static s32 stub_xfer(struct i2c_adapter
return ret;
}
+/* Emulate a PCA9540 mux chip */
+static s32 stub_xfer_pca9540(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write, u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ if (size != I2C_SMBUS_BYTE) {
+ dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (read_write == I2C_SMBUS_WRITE) {
+ pca9540_state = command;
+ dev_dbg(&adap->dev, "PCA9540 - set to 0x%02x.\n", command);
+ } else {
+ data->byte = pca9540_state;
+ dev_dbg(&adap->dev, "PCA9540 - read 0x%02x.\n", data->byte);
+ }
+
+ return 0;
+}
+
+static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
+ char read_write, u8 command, int size, union i2c_smbus_data *data)
+{
+ if (have_pca9540 && addr == PCA9540_ADDR) {
+ return stub_xfer_pca9540(adap, addr, flags, read_write,
+ command, size, data);
+ } else {
+ return stub_xfer_normal(adap, addr, flags, read_write,
+ command, size, data);
+ }
+}
+
static u32 stub_func(struct i2c_adapter *adapter)
{
return STUB_FUNC & functionality;
@@ -174,36 +266,89 @@ static struct i2c_adapter stub_adapter =
.name = "SMBus stub driver",
};
-static int __init i2c_stub_init(void)
+static int __init i2c_stub_parse_addresses(const unsigned short *addr,
+ struct stub_chip **chipsp, const char *suffix)
{
- int i, ret;
+ int i;
- if (!chip_addr[0]) {
- printk(KERN_ERR "i2c-stub: Please specify a chip address\n");
- return -ENODEV;
- }
+ /* Nothing to do if the address list is empty */
+ if (!addr[0])
+ return 0;
- for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) {
- if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) {
+ for (i = 0; i < MAX_CHIPS && addr[i]; i++) {
+ if (addr[i] < 0x03 || addr[i] > 0x77) {
printk(KERN_ERR "i2c-stub: Invalid chip address "
- "0x%02x\n", chip_addr[i]);
+ "0x%02x\n", addr[i]);
+ return -EINVAL;
+ }
+ if (have_pca9540 && addr[i] == PCA9540_ADDR) {
+ printk(KERN_ERR "i2c-stub: Address 0x%02x reserved "
+ "for PCA9540 mux\n", addr[i]);
return -EINVAL;
}
- printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n",
- chip_addr[i]);
+ printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x%s\n",
+ addr[i], suffix);
}
/* Allocate memory for all chips at once */
- stub_chips = kzalloc(i * sizeof(struct stub_chip), GFP_KERNEL);
- if (!stub_chips) {
+ *chipsp = kzalloc(i * sizeof(struct stub_chip), GFP_KERNEL);
+ if (!*chipsp) {
printk(KERN_ERR "i2c-stub: Out of memory\n");
return -ENOMEM;
}
+ return 0;
+}
+
+static int __init i2c_stub_init(void)
+{
+ int ret;
+
+ have_pca9540 = pca9540_addr_0[0] || pca9540_addr_1[0];
+ if (!chip_addr[0] && !have_pca9540) {
+ printk(KERN_ERR "i2c-stub: Please specify a chip address\n");
+ return -ENODEV;
+ }
+
+ ret = i2c_stub_parse_addresses(chip_addr, &stub_chips, "");
+ if (ret)
+ goto err_free;
+
+ ret = i2c_stub_parse_addresses(pca9540_addr_0, &pca9540_chips_0,
+ " on PCA9540 mux branch 0");
+ if (ret)
+ goto err_free;
+
+ ret = i2c_stub_parse_addresses(pca9540_addr_1, &pca9540_chips_1,
+ " on PCA9540 mux branch 1");
+ if (ret)
+ goto err_free;
+
+ /* Bus multiplexing isn't compatible with device auto-detection */
+ if (have_pca9540)
+ stub_adapter.class = 0;
+
ret = i2c_add_adapter(&stub_adapter);
if (ret)
- kfree(stub_chips);
+ goto err_free;
+
+ /* Instantiate PCA9540 multiplexer chip if requested */
+ if (have_pca9540) {
+ struct i2c_board_info info;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "pca9540", I2C_NAME_SIZE);
+ info.addr = PCA9540_ADDR;
+ i2c_new_device(&stub_adapter, &info);
+ }
+
+ return 0;
+
+ err_free:
+ kfree(stub_chips);
+ kfree(pca9540_chips_0);
+ kfree(pca9540_chips_1);
return ret;
}
--- linux-2.6.34-rc5.orig/Documentation/i2c/i2c-stub 2010-03-05 11:08:47.000000000 +0100
+++ linux-2.6.34-rc5/Documentation/i2c/i2c-stub 2010-04-20 14:05:17.000000000 +0200
@@ -33,6 +33,15 @@ PARAMETERS:
int chip_addr[10]:
The SMBus addresses to emulate chips at.
+int pca9540_addr_0[10]:
+int pca9540_addr_1[10]:
+ The SMBus addresses to emulate chips at on the two branches
+ of an emulated PCA9540 mux chip. The PCA9540 chip is emulated
+ at address 0x70 if and only if at least one address is provided
+ for either parameter. Also note that the adapter class is reset
+ in this case, so you always have to explicitly instantiate your
+ devices, auto-detection will no longer work.
+
unsigned long functionality:
Functionality override, to disable some commands. See I2C_FUNC_*
constants in <linux/i2c.h> for the suitable values. For example,
--
Jean Delvare
next reply other threads:[~2010-04-20 12:40 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-04-20 12:40 Jean Delvare [this message]
[not found] ` <20100420144034.40d5985c-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2010-04-20 15:10 ` [PATCH v2] i2c-stub: Add support for bus multiplexing Jean Delvare
[not found] ` <20100420171042.37e38804-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2010-04-20 18:27 ` Rodolfo Giometti
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20100420144034.40d5985c@hyperion.delvare \
--to=khali-puyad+kwke1g9huczpvpmw@public.gmane.org \
--cc=giometti-AVVDYK/kqiJWk0Htik3J/w@public.gmane.org \
--cc=linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=ml.lawnick-Mmb7MZpHnFY@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.