i2c: Multiplexed I2C bus core support
Subsystems (3)
PATCH v2 1/2 i2c: Multiplexed I2C bus core support
Add multiplexed bus core support. I2C multiplexer and switches like pca954x get instantiated as new adapters per output.
---
Based on kernel 2.6.33 +
[PATCH] i2c-core: Use per-adapter userspace device lists
by Jean Delware <[email protected]>
<http://article.gmane.org/gmane.linux.drivers.i2c/5994/>
drivers/i2c/Kconfig | 8 ++ drivers/i2c/Makefile | 1 + drivers/i2c/i2c-core.c | 93 ++++++++++++++++++++++-- drivers/i2c/i2c-dev.c | 46 ++++++++++++- drivers/i2c/i2c-mux.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c-mux.h | 46 ++++++++++++ include/linux/i2c.h | 11 +++ 7 files changed, 381 insertions(+), 8 deletions(-) create mode 100755 drivers/i2c/i2c-mux.c create mode 100755 include/linux/i2c-mux.h
drivers/i2c/Kconfig +8 -0
@@ -36,6 +36,14 @@ config I2C_COMPAT other user-space package which expects i2c adapters to be class devices. If you don't know, say Y. +config I2C_MUX + tristate "I2C bus multiplexing support" + depends on I2C + help + Say Y here if you want the I2C core to support the ability to + handle multiplexed I2C bus topologies, by presenting each + multiplexed segment as a I2C adapter. + config I2C_CHARDEV tristate "I2C device interface" help
drivers/i2c/Makefile +1 -0
@@ -5,6 +5,7 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o +obj-$(CONFIG_I2C_MUX) += i2c-mux.o obj-y += busses/ chips/ algos/ ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
drivers/i2c/i2c-core.c +86 -7
147 lines not on this page · raw message
drivers/i2c/i2c-dev.c +45 -1
57 lines not on this page · raw message
drivers/i2c/i2c-mux.c new file +184 -0
184 lines not on this page · raw message
include/linux/i2c-mux.h new file +46 -0
46 lines not on this page · raw message
include/linux/i2c.h +11 -0
@@ -349,6 +349,8 @@ struct i2c_adapter {struct completion dev_released;struct list_head userspace_clients; +struct list_head mux_list_head; +struct list_head mux_list; };#define to_i2c_adapter (d) container_of(d, struct i2c_adapter, dev)
@@ -380,6 +382,15 @@ static inline void i2c_unlock_adapter(struct i2c_adapter *adapter)rt_mutex_unlock (&adapter->bus_lock); } +/** +* i2c_trylock_adapter - Try to prevent access to an I2C bus segment +* @adapter: Target I2C bus segment +*/ +static inline int i2c_trylock_adapter (struct i2c_adapter *adapter) +{ +return rt_mutex_trylock (&adapter->bus_lock); +} +/*flags for the client struct: */ #define I2C_CLIENT_PEC 0x04/* Use Packet Error Checking */ #define I2C_CLIENT_TEN 0x10/* we have a ten bit chip address */
Signature · 1 line
1.6.2.1
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH v2 2/2 i2c: Multiplexed I2C bus multiplexer driver pca954x
Add multiplexed bus core support. I2C driver for PCA954x I2C multiplexer series.
---
Based on kernel 2.6.33 +
[PATCH] i2c-core: Use per-adapter userspace device lists
by Jean Delware <[email protected]>
<http://article.gmane.org/gmane.linux.drivers.i2c/5994/>
drivers/i2c/Kconfig | 2 + drivers/i2c/Makefile | 2 +- drivers/i2c/muxes/Kconfig | 17 +++ drivers/i2c/muxes/Makefile | 8 + drivers/i2c/muxes/pca954x.c | 302 +++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pca954x.h | 47 +++++++ 6 files changed, 377 insertions(+), 1 deletions(-) create mode 100755 drivers/i2c/muxes/Kconfig create mode 100755 drivers/i2c/muxes/Makefile create mode 100755 drivers/i2c/muxes/pca954x.c create mode 100755 include/linux/i2c/pca954x.h
drivers/i2c/Kconfig +2 -0
@@ -44,6 +44,8 @@ config I2C_MUX handle multiplexed I2C bus topologies, by presenting each multiplexed segment as a I2C adapter. +source drivers/i2c/muxes/Kconfig + config I2C_CHARDEV tristate "I2C device interface" help
drivers/i2c/Makefile +1 -1
@@ -6,7 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_MUX) += i2c-mux.o -obj-y += busses/ chips/ algos/ +obj-y += busses/ chips/ muxes/ algos/ ifeq ($(CONFIG_I2C_DEBUG_CORE),y) EXTRA_CFLAGS += -DDEBUG
drivers/i2c/muxes/Kconfig new file +17 -0
@@ -0,0 +1,17 @@ +# +# Multiplexer I2C chip drivers configuration +# + +menu "Multiplexer I2C Chip support" + depends on I2C_MUX && EXPERIMENTAL + +config I2C_MUX_PCA954x + tristate "Philips PCA954x I2C Mux/switches" + help + If you say yes here you get support for the Philips PCA954x + I2C mux/switch devices. + + This driver can also be built as a module. If so, the module + will be called pca954x. + +endmenu
drivers/i2c/muxes/Makefile new file +8 -0
@@ -0,0 +1,8 @@ +# +# Makefile for multiplexer I2C chip drivers. + +obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o + +ifeq ($(I2C_DEBUG_BUS),y) +EXTRA_CFLAGS += -DDEBUG +endif
drivers/i2c/muxes/pca954x.c new file +302 -0
302 lines not on this page · raw message
include/linux/i2c/pca954x.h new file +47 -0
47 lines not on this page · raw message
Signature · 1 line
1.6.2.1
A message that is not in the archive (sent off-list or not yet received): [email protected]
Rodolfo Giometti
Acked-byRe: [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x
Quoted 4 lines · Michael Lawnick
Add multiplexed bus core support. I2C driver for PCA954x I2C multiplexer series.
Signed-off-by Michael Lawnick <[email protected]>
Signature · 5 lines
GNU/Linux Solutions e-mail: giometti-AVVDYK/kqiJWk0Htik3J/[email protected] Linux Device Driver [email protected] Embedded Systems phone: +39 349 2432127 UNIX programming skype: rodolfo.giometti Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it
Re: [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x
In reply to a message not in the archiveHi Michael,
Quoted 4 lines · Michael Lawnick
Add multiplexed bus core support. I2C driver for PCA954x I2C multiplexer series.
Signed-off-by Michael Lawnick <[email protected]>
Review:
Quoted 7 lines
---
Based on kernel 2.6.33 +
[PATCH] i2c-core: Use per-adapter userspace device lists
by Jean Delware <[email protected]>
<http://article.gmane.org/gmane.linux.drivers.i2c/5994/>
Quoted 11 lines
drivers/i2c/Kconfig | 2 + drivers/i2c/Makefile | 2 +- drivers/i2c/muxes/Kconfig | 17 +++ drivers/i2c/muxes/Makefile | 8 + drivers/i2c/muxes/pca954x.c | 302 +++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pca954x.h | 47 +++++++ 6 files changed, 377 insertions(+), 1 deletions(-) create mode 100755 drivers/i2c/muxes/Kconfig create mode 100755 drivers/i2c/muxes/Makefile create mode 100755 drivers/i2c/muxes/pca954x.c create mode 100755 include/linux/i2c/pca954x.h
I don't really care, as I use quilt for my patches, but the above modes are wrong: should be 100644.
Quoted 33 lines
drivers/i2c/Kconfig +2
-0@@ -44,6 +44,8 @@ config I2C_MUX handle multiplexed I2C bus topologies, by presenting each multiplexed segment as a I2C adapter. +source drivers/i2c/muxes/Kconfig + config I2C_CHARDEV tristate "I2C device interface" helpdrivers/i2c/Makefile +1
-1@@ -6,7 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_MUX) += i2c-mux.o -obj-y += busses/ chips/ algos/ +obj-y += busses/ chips/ muxes/ algos/ ifeq ($(CONFIG_I2C_DEBUG_CORE),y) EXTRA_CFLAGS += -DDEBUGdrivers/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,17 @@ +# +# Multiplexer I2C chip drivers configuration +# + +menu "Multiplexer I2C Chip support" + depends on I2C_MUX && EXPERIMENTAL
The dependency on EXPERIMENTAL is odd for a menu. I'd rather move it down to individual drivers, so that we can them remove them later on a case-by-case basis.
Quoted 17 lines
drivers/i2c/muxes/Kconfig new file +11
-0+ +config I2C_MUX_PCA954x + tristate "Philips PCA954x I2C Mux/switches" + help + If you say yes here you get support for the Philips PCA954x + I2C mux/switch devices. + + This driver can also be built as a module. If so, the module + will be called pca954x. + +endmenudrivers/i2c/muxes/Makefile new file +0
-0
drivers/i2c/muxes/Makefile line 6
Quoted 6 lines
drivers/i2c/muxes/Makefile new file +6
-0@@ -0,0 +1,8 @@ +# +# Makefile for multiplexer I2C chip drivers. + +obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o + +ifeq ($(I2C_DEBUG_BUS),y)
I think you mean CONFIG_I2C_DEBUG_BUS here.
Quoted 62 lines
drivers/i2c/muxes/Makefile new file +2
-0+EXTRA_CFLAGS += -DDEBUG +endifdrivers/i2c/muxes/pca954x.c new file +54
-0@@ -0,0 +1,302 @@ +/* +* 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. 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]>. +* +* This file is licensed under the terms of the GNU General Public +* License version 2. This program is licensed "as is" without any +* warranty of any kind, whether express or implied. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/i2c-mux.h> + +#include <linux/i2c/pca954x.h> + +#define PCA954X_MAX_NCHANS 8 + +enum pca_type { + pca_9540, + pca_9542, + pca_9543, + pca_9544, + pca_9545,
drivers/i2c/muxes/pca954x.c line 64
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+ pca_9546, + pca_9547, + pca_9548, +}; + +struct pca954x { +enum pca_type type; +struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS ]; + +u8 last_chan;
This indentation doesn't make much sense.
Quoted 51 lines
drivers/i2c/muxes/pca954x.c new file +51
-0+}; + +struct chip_desc { +u8 nchans; +u8 enable;/* used for muxes only */ +enum muxtype { + pca954x_ismux =0 , + pca954x_isswi + } muxtype; +}; + +/* Provide specs for the PCA954x types we know about */ +static const struct chip_desc chips[] = { + [pca_9540] = { + .nchans =2 , + .enable =0x4 , + .muxtype = pca954x_ismux, + }, + [pca_9543] = { + .nchans =2 , + .muxtype = pca954x_isswi, + }, + [pca_9544] = { + .nchans =4 , + .enable =0x4 , + .muxtype = pca954x_ismux, + }, + [pca_9545] = { + .nchans =4 , + .muxtype = pca954x_isswi, + }, + [pca_9547] = { + .nchans =8 , + .enable =0x8 , + .muxtype = pca954x_ismux, + }, + [pca_9548] = { + .nchans =8 , + .muxtype = pca954x_isswi, + }, +}; + +static const struct i2c_device_id pca954x_id[] = { + {"pca9540" , pca_9540 }, + {"pca9542" , pca_9540 }, + {"pca9543" , pca_9543 }, + {"pca9544" , pca_9544 }, + {"pca9545" , pca_9545 }, + {"pca9546" , pca_9545 }, + {"pca9547" , pca_9547 }, + {"pca9548" , pca_9548 },
drivers/i2c/muxes/pca954x.c line 125
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+ { } +}; +MODULE_DEVICE_TABLE (i2c, pca954x_id); + +/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer() +for this as they will try to lock adapter a second time */ +static int pca954x_reg_write (struct i2c_adapter *adap, +struct i2c_client *client,u8 val) +{ +int ret = -ENODEV ;
Useless initialization.
drivers/i2c/muxes/pca954x.c line 132
Quoted 7 lines
drivers/i2c/muxes/pca954x.c new file +7
-0+ +if (adap->algo->master_xfer) { +struct i2c_msg msg; +char buf[1 ]; + + msg.addr = client->addr; + msg.flags =I2C_SMBUS_WRITE ;
That's not correct. You want to use 0 here (as writes are the default, in the absence of I2C_M_RD).
Quoted 89 lines
drivers/i2c/muxes/pca954x.c new file +89
-0+ msg.len =1 ; + buf[0 ] = val; + msg.buf = buf; + ret = adap->algo->master_xfer (adap, &msg,1 ); + }else { +union i2c_smbus_data data; + ret = adap->algo->smbus_xfer (adap, client->addr, + client->flags, +I2C_SMBUS_WRITE , + val,I2C_SMBUS_BYTE , &data); + } + +return ret; +} + +static int pca954x_select_chan (struct i2c_adapter *adap, +void *client,u32 chan) +{ +struct pca954x *data =i2c_get_clientdata (client); +const struct chip_desc *chip = &chips[data->type]; +u8 regval; +int ret =0 ; + +/* we make switches look like muxes, not sure how to be smarter */ +if (chip->muxtype == pca954x_ismux) + regval = chan | chip->enable; +else + regval =1 << chan; + +/* Only select the channel if its different from the last channel */ +if (data->last_chan != chan) { + ret =pca954x_reg_write (adap, client, regval); + data->last_chan = chan; + } + +return ret; +} + +static int pca954x_deselect_mux (struct i2c_adapter *adap, +void *client,u32 chan) +{ +struct pca954x *data =i2c_get_clientdata (client); + +/* Deselect active channel */ + data->last_chan =0 ; +return pca954x_reg_write (adap, client, data->last_chan); +} + +/* +* I2C init/probing/exit functions +*/ +static int __devinit pca954x_probe (struct i2c_client *client, +const struct i2c_device_id *id) +{ +struct i2c_adapter *adap =to_i2c_adapter (client->dev.parent); +struct pca954x_platform_data *pdata = client->dev.platform_data; +int num, force; +struct pca954x *data; +int ret = -ENODEV ; + +if (!i2c_check_functionality (adap,I2C_FUNC_SMBUS_BYTE )) + goto err; + + data =kzalloc (sizeof (struct pca954x ),GFP_KERNEL ); +if (!data) { + ret = -ENOMEM ; + goto err; + } + +i2c_set_clientdata (client, data); + +/* Read the mux register at addr to verify +* that the mux is in fact present. +*/ +if (i2c_smbus_read_byte (client) <0 ) { +dev_warn (&client->dev,"probe failed\n" ); + goto exit_free; + } + + data->type = id->driver_data; + data->last_chan =0 ;/* force the first selection */ + +/* Now create an adapter for each channel */ +for (num =0 ; num < chips[data->type].nchans; num++) { + force =0 ;/* dynamic adap number */ +if (pdata) { +if (num < pdata->num_modes) +/* force static number */ + force = pdata->modes[num].adap_id;
drivers/i2c/muxes/pca954x.c line 231
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+else +/* discard unconfigured channels */ +break ; + } + + data->virt_adaps[num] = +i2c_add_mux_adapter (adap, client, + force, num, &pca954x_select_chan, + (pdata && pdata->modes[num].deselect_on_exit) + ? &pca954x_deselect_mux :NULL );
Functions are already addresses, the leading & isn't needed.
drivers/i2c/muxes/pca954x.c line 235
Quoted 4 lines
drivers/i2c/muxes/pca954x.c new file +4
-0+ +if (data->virt_adaps[num] ==NULL ) { + ret = -ENODEV ; + printk(KERN_ERR
Please use dev_err().
drivers/i2c/muxes/pca954x.c line 237
Quoted 2 lines
drivers/i2c/muxes/pca954x.c new file +2
-0+"%s %s: failed to register multiplexed adapter" + " %d at bus %d\n", __FILE__, __func__, num,
I think you mean "as bus", not "at bus".
Quoted 86 lines
drivers/i2c/muxes/pca954x.c new file +65
-0+ force); + goto virt_reg_failed; + } + } + +dev_info (&client->dev, +"registered %d multiplexed busses for I2C %s %s\n" , + num, chips[data->type].muxtype == pca954x_ismux + ?"mux" :"switch" , client->name); + +return 0 ; + +virt_reg_failed: +for (num--; num >=0 ; num--) +i2c_del_mux_adapter (data->virt_adaps[num]); +exit_free: +kfree (data); +err: +return ret; +} + +static int __devexit pca954x_remove(struct i2c_client *client) +{ +struct pca954x *data =i2c_get_clientdata (client); +const struct chip_desc *chip = &chips[data->type]; +int i, err; + +for (i =0 ; i < chip->nchans; ++i) +if (data->virt_adaps[i]) { + err =i2c_del_mux_adapter (data->virt_adaps[i]); +if (err) +return err; + data->virt_adaps[i] =NULL ; + } + +kfree (data); +return 0 ; +} + +static struct i2c_driver pca954x_driver = { + .driver = { + .name ="pca954x" , + .owner =THIS_MODULE , + }, + .probe = pca954x_probe, + .remove =__devexit_p (pca954x_remove), + .id_table = pca954x_id, +}; + +static int __init pca954x_init (void ) +{ +return i2c_add_driver (&pca954x_driver); +} + +static void __exit pca954x_exit(void) +{ +i2c_del_driver (&pca954x_driver); +} + +module_init (pca954x_init); +module_exit (pca954x_exit); + +MODULE_AUTHOR ("Rodolfo Giometti <[email protected]>" ); +MODULE_DESCRIPTION ("PCA954x I2C mux/switch driver" ); +MODULE_LICENSE ("GPL v2" );include/linux/i2c/pca954x.h new file +15
-0@@ -0,0 +1,47 @@ +/* +* +* pca954x.h - I2C multiplexer/switch support +* +* Copyright (c) 2008-2009 Rodolfo Giometti <[email protected]> +* Copyright (c) 2008-2009 Eurotech S.p.A. <[email protected]> +* Michael Lawnick <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of
include/linux/i2c/pca954x.h line 25
Quoted 10 lines
include/linux/i2c/pca954x.h new file +10
-0+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _I2C_PCA954X_H
Should be prefixed with _LINUX to avoid any confusion.
Quoted 9 lines
include/linux/i2c/pca954x.h new file +9
-0+#define _I2C_PCA954X_H + +/* Platform data for the PCA954x I2C multiplexers */ + +/* Per channel initialisation data: +* @adap_id: bus number for the adapter. 0 = don't care +* @deselect_on_exit: set this entry to 1, if your H/W needs deselection +* of this channel after transaction. +*
include/linux/i2c/pca954x.h line 44
Quoted 10 lines
include/linux/i2c/pca954x.h new file +10
-0+*/ +struct pca954x_platform_mode { +int adap_id; +unsigned int deselect_on_exit:1 ; +}; + +/* Per mux/switch data, used with i2c_register_board_info */ +struct pca954x_platform_data { +struct pca954x_platform_mode *modes; +int num_modes;
Please use tabs for indentation.
include/linux/i2c/pca954x.h line 47
Quoted 3 lines
include/linux/i2c/pca954x.h new file +3
-0+}; + +#endif /*_I2C_PCA954X_H*/
Missing spaces.
I've fixed all these small issues myself; no need to resend. Thanks (to you and Rodolfo as well) for your contribution!
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x
Jean Delvare said the following:
Quoted 10 lines
Hi Michael,
Quoted 4 lines · Michael Lawnick
Add multiplexed bus core support. I2C driver for PCA954x I2C multiplexer series.
Signed-off-by Michael Lawnick <[email protected]>Review:
...
Quoted 3 lines
I've fixed all these small issues myself; no need to resend. Thanks (to you and Rodolfo as well) for your contribution!
Thank You!
Signature · 2 lines
KR Michael
Re: [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x
In reply to a message not in the archiveHi Michael,
I just hit a bug...
Quoted 6 lines · Michael Lawnick
drivers/i2c/muxes/pca954x.c new file +6
-0+static int pca954x_select_chan (struct i2c_adapter *adap, +void *client,u32 chan) +{ +struct pca954x *data =i2c_get_clientdata (client); +const struct chip_desc *chip = &chips[data->type]; +u8 regval;
drivers/i2c/muxes/pca954x.c line 163
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+int ret =0 ; + +/* we make switches look like muxes, not sure how to be smarter */ +if (chip->muxtype == pca954x_ismux) + regval = chan | chip->enable; +else + regval =1 << chan; + +/* Only select the channel if its different from the last channel */ +if (data->last_chan != chan) {
That's not right. data->last_chan is 0 after initialization or deselection, but selecting channel 0 is valid and must be possible.
So you either want to use a different initialization value (e.g. 0xff) or store the register value in data->last_chan instead of the channel number. I went for the latter.
Quoted 6 lines
drivers/i2c/muxes/pca954x.c new file +6
-0+ ret =pca954x_reg_write (adap, client, regval); + data->last_chan = chan; + } + +return ret; +}
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH v2 2/2] i2c: Multiplexed I2C bus multiplexer driver pca954x
Jean Delvare said the following:
Quoted 25 lines
Hi Michael,
I just hit a bug...
Quoted 6 lines · Michael Lawnick
drivers/i2c/muxes/pca954x.c new file +6
-0+static int pca954x_select_chan (struct i2c_adapter *adap, +void *client,u32 chan) +{ +struct pca954x *data =i2c_get_clientdata (client); +const struct chip_desc *chip = &chips[data->type]; +u8 regval;drivers/i2c/muxes/pca954x.c line 163
Quoted 10 lines
drivers/i2c/muxes/pca954x.c new file +10
-0+int ret =0 ; + +/* we make switches look like muxes, not sure how to be smarter */ +if (chip->muxtype == pca954x_ismux) + regval = chan | chip->enable; +else + regval =1 << chan; + +/* Only select the channel if its different from the last channel */ +if (data->last_chan != chan) {That's not right.
data->last_chanis 0 after initialization or deselection, but selecting channel 0 is valid and must be possible.
Quoted 3 lines
So you either want to use a different initialization value (e.g.
0xff) or store the register value indata->last_chaninstead of the channel number. I went for the latter.
ACK
Signature · 2 lines
KR Michael
Rodolfo Giometti
Acked-byQuoted 4 lines · Michael Lawnick
Add multiplexed bus core support. I2C multiplexer and switches like pca954x get instantiated as new adapters per output.
Signed-off-by Michael Lawnick <[email protected]>
Signature · 5 lines
GNU/Linux Solutions e-mail: giometti-AVVDYK/kqiJWk0Htik3J/[email protected] Linux Device Driver [email protected] Embedded Systems phone: +39 349 2432127 UNIX programming skype: rodolfo.giometti Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it
Michael Lawnick said the following:
Quoted 1 line
Add multiplexed bus core support.
...
Ping
Jean, still any chance to get that done into .35?
Signature · 2 lines
KR Michael Lawnick
A message that is not in the archive (sent off-list or not yet received): [email protected]
Hi Michael,
Quoted 7 lines · Michael Lawnick
Michael Lawnick said the following:
Quoted 1 line
Add multiplexed bus core support.
...
Ping
Jean, still any chance to get that done into .35?
I hope so, yes. I am busy with the w83795 driver right now, hope to get some time to review your updated patches this week. Stay tuned.
Signature · 1 line
Jean Delvare
Hi Michael,
Quoted 10 lines · Michael Lawnick
Add multiplexed bus core support. I2C multiplexer and switches like pca954x get instantiated as new adapters per output.
Signed-off-by Michael Lawnick <[email protected]>---
Based on kernel 2.6.33 +
[PATCH] i2c-core: Use per-adapter userspace device lists
by Jean Delware <[email protected]>
<http://article.gmane.org/gmane.linux.drivers.i2c/5994/>
Future patches should be based on 2.6.34 at least... 2.6.35-rc2 would be even better.
Review follows. Need some more work I fear, but not that much... Please let me know if you are going to submit another iteration, or if you prefer to let me fix things the way I like (in which case I'll still ask you to test my version of the patch when I'm done.)
Quoted 17 lines
drivers/i2c/Kconfig | 8 ++ drivers/i2c/Makefile | 1 + drivers/i2c/i2c-core.c | 93 ++++++++++++++++++++++-- drivers/i2c/i2c-dev.c | 46 ++++++++++++- drivers/i2c/i2c-mux.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c-mux.h | 46 ++++++++++++ include/linux/i2c.h | 11 +++ 7 files changed, 381 insertions(+), 8 deletions(-) create mode 100755 drivers/i2c/i2c-mux.c create mode 100755 include/linux/i2c-mux.hdrivers/i2c/Kconfig +0
-0
drivers/i2c/Kconfig config I2C_COMPAT · line 41
Quoted 6 lines
drivers/i2c/Kconfig +3
-0@@ -36,6 +36,14 @@ config I2C_COMPAT other user-space package which expects i2c adapters to be class devices. If you don't know, say Y. +config I2C_MUX + tristate "I2C bus multiplexing support" + depends on I2C
Dependency on I2C isn't needed: this configuration item is already in an "if I2C" block. A dependency on EXPERIMENTAL might be a good idea for the time being though.
drivers/i2c/Kconfig config I2C_COMPAT · line 45
Quoted 4 lines
drivers/i2c/Kconfig +4
-0+ help + Say Y here if you want the I2C core to support the ability to + handle multiplexed I2C bus topologies, by presenting each + multiplexed segment as a I2C adapter.
For support that can be build as a module, we usually add a sentence saying so and giving the name of the module.
drivers/i2c/Kconfig config I2C_COMPAT · line 46
Quoted 1 line
drivers/i2c/Kconfig +1
-0+
I would move config I2C_MUX below config I2C_CHARDEV, to make it consistent with Makefile, and also because users expect new options to be added after existing options.
Quoted 20 lines
drivers/i2c/Kconfig +0
-0config I2C_CHARDEV tristate "I2C device interface" helpdrivers/i2c/Makefile +1
-0@@ -5,6 +5,7 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o +obj-$(CONFIG_I2C_MUX) += i2c-mux.o obj-y += busses/ chips/ algos/ ifeq ($(CONFIG_I2C_DEBUG_CORE),y)drivers/i2c/i2c-core.c +0
-0
drivers/i2c/i2c-core.c line 26
Quoted 8 lines
drivers/i2c/i2c-core.c +4
-1@@ -20,7 +20,10 @@/* 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]> */ +
Extra blank line.
Quoted 34 lines
drivers/i2c/i2c-core.c +13
-0#include <linux/module.h> #include <linux/kernel.h> @@ -288,6 +291,12 @@ struct i2c_client *i2c_verify_client(struct device *dev) }EXPORT_SYMBOL (i2c_verify_client); +static int i2c_parent_is_i2c_adapter (const struct i2c_adapter *adapter) +{ +return adapter->dev.parent !=NULL + && adapter->dev.parent->bus == &i2c_bus_type; +} +/** * i2c_new_device - instantiate an i2c device @@ -624,6 +633,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)rt_mutex_init (&adap->bus_lock);INIT_LIST_HEAD (&adap->userspace_clients); +INIT_LIST_HEAD (&adap->mux_list_head);/* Set default timeout to 1 second if not already set */ if (adap->timeout ==0 )@@ -949,9 +959,78 @@ static int __i2c_check_addr(struct device *dev, void *addrp)return 0 ; } +/* walk up mux tree */ +static int i2c_check_mux_parents (struct i2c_adapter *adapter,int addr) +{ +int result; + + result =device_for_each_child (&adapter->dev, &addr, __i2c_check_addr);
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, void *addrp) · line 977
Quoted 10 lines
drivers/i2c/i2c-core.c +10
-0+ +if (!result &&i2c_parent_is_i2c_adapter (adapter)) + result =i2c_check_mux_parents ( +to_i2c_adapter (adapter->dev.parent), addr); + +return result; +} + +/* recurse down mux tree */ +static int i2c_check_mux_childs (struct i2c_adapter *adapter,int addr)
"childs" doesn't exist in proper English, the right work is "children" ;)
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, void *addrp) · line 988
Quoted 11 lines
drivers/i2c/i2c-core.c +11
-0+{ +int result =0 ; +struct i2c_adapter *child, *next; + +list_for_each_entry_safe (child, next, &adapter->mux_list_head, + mux_list) { + result =device_for_each_child (&child->dev, &addr, + __i2c_check_addr); +if (result) +break ; + result =i2c_check_mux_childs (child, addr);
This value is ignored, except for the last child. I think you should break here too.
Quoted 6 lines
drivers/i2c/i2c-core.c +4
-0+ } +return result; +} +static int i2c_check_addr (struct i2c_adapter *adapter,int addr) {
drivers/i2c/i2c-core.c static int __i2c_check_addr(struct device *dev, void *addrp) · line 1003
Quoted 10 lines
drivers/i2c/i2c-core.c +9
-1-return device_for_each_child (&adapter->dev, &addr, __i2c_check_addr); +int result; + + result =i2c_check_mux_parents (adapter, addr); + +if (!result) + result =i2c_check_mux_childs (adapter, addr); + +return result; +}
I am worried about (the lack of) locking when the above function is called. The problem isn't new with your patch, but your patch makes it
worse: beforehand, the driver core would complain if we hit a race here, because we would try to create two clients with the same bus_id as a result. But now, we may create 2 conflicting clients with different bus_id, so the driver core will not complain.
I'll think about it some more, and add locking if needed.
Quoted 66 lines
drivers/i2c/i2c-core.c +33
-4+static void i2c_mux_tree_lock (struct i2c_adapter *adap) +{ +if (i2c_parent_is_i2c_adapter (adap)) +i2c_mux_tree_lock (to_i2c_adapter (adap->dev.parent)); +else +i2c_lock_adapter (adap); +} + +static int i2c_mux_tree_trylock (struct i2c_adapter *adap) +{ +int ret; +struct i2c_adapter *parent; + +if (i2c_parent_is_i2c_adapter (adap)) { + parent =to_i2c_adapter (adap->dev.parent); + ret =i2c_mux_tree_trylock (parent); + }else { + ret =i2c_trylock_adapter (adap); + } + +return ret; +} + +static void i2c_mux_tree_unlock (struct i2c_adapter *adap) +{ +if (i2c_parent_is_i2c_adapter (adap)) +i2c_mux_tree_unlock (to_i2c_adapter (adap->dev.parent)); +else +i2c_unlock_adapter (adap); }/** @@ -1104,12 +1183,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)#endif if (in_atomic () ||irqs_disabled ()) { - ret =rt_mutex_trylock (&adap->bus_lock); + ret =i2c_mux_tree_trylock (adap);if (!ret)/* I2C activity is ongoing. */ return -EAGAIN ; }else { -rt_mutex_lock (&adap->bus_lock); +i2c_mux_tree_lock (adap); }/* Retry automatically on arbitration loss */ @@ -1121,7 +1200,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)if (time_after (jiffies, orig_jiffies + adap->timeout))break ; } -rt_mutex_unlock (&adap->bus_lock); +i2c_mux_tree_unlock (adap);return ret; }else {@@ -1857,7 +1936,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, flags &=I2C_M_TEN |I2C_CLIENT_PEC ;if (adapter->algo->smbus_xfer) { -rt_mutex_lock (&adapter->bus_lock); +i2c_mux_tree_lock (adapter);/* Retry automatically on arbitration loss */ orig_jiffies = jiffies;
drivers/i2c/i2c-core.c s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, · line 1956
Quoted 8 lines
drivers/i2c/i2c-core.c +1
-1@@ -1871,7 +1950,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, orig_jiffies + adapter->timeout))break ; } -rt_mutex_unlock (&adapter->bus_lock); +i2c_mux_tree_unlock (adapter); }else res =i2c_smbus_xfer_emulated (adapter,addr,flags,read_write, command, protocol, data);
I'm worried that this approach is risky. We now use i2c_mux_tree_*lock internally, but still expose the i2c_*lock_adapter() API externally. This API is _not_ mux-aware, and will do the wrong thing if called on a bus segment behind a mux.
I'd rather not have two APIs when we really only need one. Let's just update the current public API to be mux-aware, and use that everywhere.
drivers/i2c/i2c-dev.c static int i2cdev_check(struct device *dev, void *addrp) · line 200
Quoted 13 lines
drivers/i2c/i2c-dev.c +5
-0@@ -193,12 +193,56 @@ static int i2cdev_check(struct device *dev, void *addrp)return dev->driver ? -EBUSY :0 ; } +static int i2cdev_parent_is_i2c_adapter (const struct i2c_adapter *adapter) +{ +return adapter->dev.parent !=NULL + && adapter->dev.parent->bus == &i2c_bus_type; +}
This is an exact duplicate of the same function in i2c-core. I'd rather make it an inline function in <linux/i2c.h>, or export the one from i2c-core.c. We have enough duplicate code in i2c-dev.c already :(
Quoted 7 lines
drivers/i2c/i2c-dev.c +7
-0+ +/* walk up mux tree */ +static int i2cdev_check_mux_parents (struct i2c_adapter *adapter,int addr) +{ +int result; + + result =device_for_each_child (&adapter->dev, &addr, i2cdev_check);
drivers/i2c/i2c-dev.c static int i2cdev_check(struct device *dev, void *addrp) · line 217
Quoted 10 lines
drivers/i2c/i2c-dev.c +10
-0+ +if (!result &&i2cdev_parent_is_i2c_adapter (adapter)) + result =i2cdev_check_mux_parents ( +to_i2c_adapter (adapter->dev.parent), addr); + +return result; +} + +/* recurse down mux tree */ +static int i2cdev_check_mux_childs (struct i2c_adapter *adapter,int addr)
children
drivers/i2c/i2c-dev.c static int i2cdev_check(struct device *dev, void *addrp) · line 228
Quoted 11 lines
drivers/i2c/i2c-dev.c +11
-0+{ +int result =0 ; +struct i2c_adapter *child, *next; + +list_for_each_entry_safe (child, next, &adapter->mux_list_head, + mux_list) { + result =device_for_each_child (&child->dev, &addr, + i2cdev_check); +if (result) +break ; + result =i2cdev_check_mux_childs (child, addr);
Same as in the i2c-core variant, this value is ignored, I think this isn't correct.
Quoted 38 lines
drivers/i2c/i2c-dev.c +12
-1+ } +return result; +} +/* This address checking function differs from the one in i2c-core in that it considers an address with a registered device, but no driver bound to it, as NOT busy. */ static int i2cdev_check_addr (struct i2c_adapter *adapter,unsigned int addr) { -return device_for_each_child (&adapter->dev, &addr, i2cdev_check); +int result; + + result =i2cdev_check_mux_parents (adapter, addr); + +if (!result) + result =i2cdev_check_mux_childs (adapter, addr); + +return result; } static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,drivers/i2c/i2c-mux.c new file +11
-0@@ -0,0 +1,184 @@ +/* +* 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 an additional I2C adapter. +* Supports multi-level mux'ing (mux behind a mux). +* +* Based on:
drivers/i2c/i2c-mux.c line 21
Quoted 10 lines
drivers/i2c/i2c-mux.c new file +10
-0+* i2c-virt.c from Kumar Gala <[email protected]> +* i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc. +* i2c-virtual.c from Brian Kuschak <bkuschak-/[email protected]> +* +* This file is licensed under the terms of the GNU General Public +* License version 2. This program is licensed "as is" without any +* warranty of any kind, whether express or implied. +*/ + +/* Adapted to kernel 2.6.33 by Michael Lawnick <[email protected]> */
I'd rather have you add your copyright with the other ones already present above.
Quoted 8 lines
drivers/i2c/i2c-mux.c new file +8
-0+ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/i2c-mux.h> + +/* multiplexer per channel data */
drivers/i2c/i2c-mux.c line 39
Quoted 10 lines
drivers/i2c/i2c-mux.c new file +10
-0+struct i2c_mux_priv { +struct i2c_adapter adap; +struct i2c_algorithm algo; + +struct i2c_adapter *parent; +void *mux_dev;/* the mux chip/device */ +u32 chan_id;/* the channel id */ + +int (*select)(struct i2c_adapter *adap,void *mux_dev,u32 chan_id); +int (*deselect)(struct i2c_adapter *adap,void *mux_dev,u32 chan_id);
The two "adap" can go away... I insisted on having parameter names for the other two; for structures, it's usually pretty clear what we're talking about.
drivers/i2c/i2c-mux.c line 43
Quoted 4 lines
drivers/i2c/i2c-mux.c new file +4
-0+}; + +#define VIRT_TIMEOUT (HZ/2) +#define VIRT_RETRIES 3
These two are no longer used and can thus be deleted.
drivers/i2c/i2c-mux.c line 52
Quoted 9 lines
drivers/i2c/i2c-mux.c new file +9
-0+ +static int i2c_mux_master_xfer (struct i2c_adapter *adap, +struct i2c_msg msgs[],int num) +{ +struct i2c_mux_priv *priv = adap->algo_data; +struct i2c_adapter *parent = priv->parent; +int ret; + +/* Switch to the right mux port and perform the transfer.*/
Missing space at end of comment.
Quoted 10 lines
drivers/i2c/i2c-mux.c new file +10
-0+ + ret = priv->select (parent, priv->mux_dev, priv->chan_id); +if (ret >=0 ) + ret = parent->algo->master_xfer (parent, msgs, num); +if (priv->deselect) + priv->deselect (parent, priv->mux_dev, priv->chan_id); + +return ret; +} +
drivers/i2c/i2c-mux.c line 72
Quoted 10 lines
drivers/i2c/i2c-mux.c new file +10
-0+static int i2c_mux_smbus_xfer (struct i2c_adapter *adap, +u16 addr,unsigned short flags, +char read_write,u8 command, +int size,union i2c_smbus_data *data) +{ +struct i2c_mux_priv *priv = adap->algo_data; +struct i2c_adapter *parent = priv->parent; +int ret; + +/* Select the right mux port and perform the transfer.*/
Ditto.
drivers/i2c/i2c-mux.c line 77
Quoted 5 lines
drivers/i2c/i2c-mux.c new file +5
-0+ + ret = priv->select (parent, priv->mux_dev, priv->chan_id); +if (ret >=0 ) + ret = parent->algo->smbus_xfer (parent, addr, flags, + read_write, command, size, data);
Strange alignment.
drivers/i2c/i2c-mux.c line 84
Quoted 7 lines
drivers/i2c/i2c-mux.c new file +7
-0+if (priv->deselect) + priv->deselect (parent, priv->mux_dev, priv->chan_id); + +return ret; +} + +/* Return the parent's functionality for the virtual adapter */
Please avoid referring to "virtual adapters". The fact here is that all bus segments of a given tree share the same functionality, because it is a characteristic of the controller - nothing virtual here.
Quoted 31 lines
drivers/i2c/i2c-mux.c new file +31
-0+static u32 i2c_mux_functionality (struct i2c_adapter *adap) +{ +struct i2c_mux_priv *priv = adap->algo_data; +struct i2c_adapter *parent = priv->parent; + +return parent->algo->functionality (parent); +} + +struct i2c_adapter *i2c_add_mux_adapter (struct i2c_adapter *parent, +void *mux_dev,u32 force_nr,u32 chan_id, +int (*select) (struct i2c_adapter *, +void *,u32 ), +int (*deselect) (struct i2c_adapter *, +void *,u32 )) +{ +struct i2c_mux_priv *priv; +int ret; + + priv =kzalloc (sizeof (struct i2c_mux_priv ),GFP_KERNEL ); +if (!priv) +return NULL ; + +/* Set up private adapter data */ + priv->parent = parent; + priv->mux_dev = mux_dev; + priv->chan_id = chan_id; + priv->select = select; + priv->deselect = deselect; + +/* Need to do algo dynamically because we don't know ahead +* of time what sort of physical adapter we'll be dealing with.
drivers/i2c/i2c-mux.c line 125
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 (chan_id %d)" ,i2c_adapter_id (parent), chan_id);
I'm only half happy with this name. One problem is that it isn't
unique: if you have 2 mux chips on the same root segment, this will lead to duplicate segments with the same name. Another problem is that it completely hides the name of the controller (which, I admit, we may recover by walking sysfs, but still...)
This isn't a blocker point, but something to keep in mind for further improvements. It might even make sense to let the caller provide the format for the segment names.
Quoted 10 lines
drivers/i2c/i2c-mux.c new file +10
-0+ priv->adap.owner =THIS_MODULE ; + priv->adap.id = parent->id; + priv->adap.algo = &priv->algo; + priv->adap.algo_data = priv; + priv->adap.dev.parent = &parent->dev; + +if (force_nr) { + priv->adap.nr = force_nr; + ret =i2c_add_numbered_adapter (&priv->adap); + }else {
drivers/i2c/i2c-mux.c line 145
Quoted 10 lines
drivers/i2c/i2c-mux.c new file +10
-0+ ret =i2c_add_adapter (&priv->adap); + } +if (ret <0 ) { +dev_err (&parent->dev,"failed to add mux-adapter (error=%d)\n" , + ret); +kfree (priv); +return NULL ; + } + +list_add_tail (&priv->adap.mux_list, &parent->mux_list_head);
The driver core maintains a device tree already, do you really need to keep your own? We've tried hard to remove all redundancy between i2c-core and the driver core in the last couple years, so I really would like us to not add such redundancy back now.
If you have a good reason to have your own list, please explain. If not, please get rid of it.
drivers/i2c/i2c-mux.c line 149
Quoted 4 lines
drivers/i2c/i2c-mux.c new file +4
-0+ +dev_info (&parent->dev,"i2c-mux-%d: Multiplexed I2C bus on " +"parent bus i2c-%d, chan_id %d\n" , +i2c_adapter_id (&priv->adap),i2c_adapter_id (parent), chan_id);
Have you looked at the output? I expect some redundancy, as you use dev_info() on the parent, i2c_adapter_id(parent) is already included. I think I'd rather call dev_info() on the newly spawned child, so you can get rid of the leading "i2c-mux-%d". BTW, please use dev_name() instead of hard-coding i2c-%d for the parent.
drivers/i2c/i2c-mux.c line 159
Quoted 10 lines
drivers/i2c/i2c-mux.c new file +10
-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; +struct i2c_adapter *child, *next, *parent;
Would look nicer with all structs in front and ret last, IMHO.
drivers/i2c/i2c-mux.c line 162
Quoted 3 lines
drivers/i2c/i2c-mux.c new file +3
-0+ +if (adap->dev.parent !=NULL + && adap->dev.parent->bus == &i2c_bus_type) {
This is a copy of i2c_parent_is_i2c_adapter(), isn't it? That's one more reason to export it from i2c-core.
drivers/i2c/i2c-mux.c line 163
Quoted 1 line
drivers/i2c/i2c-mux.c new file +1
-0+ parent =to_i2c_adapter (adap->dev.parent);
Note that we could have i2c_parent_is_i2c_adapter() return the parent adapter, if it proves useful in practice.
drivers/i2c/i2c-mux.c line 171
Quoted 8 lines
drivers/i2c/i2c-mux.c new file +8
-0+list_for_each_entry_safe (child, next, &parent->mux_list_head, + mux_list) { +if (child == adap) { +list_del (&child->mux_list); +break ; + } + } + }
That being said... Using the driver core's list would save you the hassle altogether.
Quoted 55 lines
drivers/i2c/i2c-mux.c new file +13
-0+ + 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 ("I2C driver for multiplexed I2C busses" ); +MODULE_LICENSE ("GPL v2" );include/linux/i2c-mux.h new file +36
-0@@ -0,0 +1,46 @@ +/* +* +* i2c-mux.h - functions for the i2c-bus mux support +* +* Copyright (c) 2008-2009 Rodolfo Giometti <[email protected]> +* Copyright (c) 2008-2009 Eurotech S.p.A. <[email protected]> +* Michael Lawnick <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _LINUX_I2C_MUX_H +#define _LINUX_I2C_MUX_H + +#ifdef __KERNEL__ + +/* +* Called to create a i2c bus on a multiplexed bus segment. +* The mux_dev and chan_id parameters are passed to the select +* and deselect callback functions to perform hardware-specific +* mux control. +*/ +struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, + void *mux_dev, u32 force_nr, u32 chan_id,
include/linux/i2c-mux.h line 46
Quoted 10 lines
include/linux/i2c-mux.h new file +10
-0+int (*select) (struct i2c_adapter *adap, +void *mux_dev,u32 chan_id), + int (*deselect) (struct i2c_adapter *, + void *mux_dev, u32 chan_id)); + +int i2c_del_mux_adapter (struct i2c_adapter *adap); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_I2C_H */
You actually mean /* _LINUX_I2C_MUX_H */.
include/linux/i2c.h struct i2c_adapter { · line 353
Quoted 10 lines
include/linux/i2c.h +2
-0@@ -349,6 +349,8 @@ struct i2c_adapter {struct completion dev_released;struct list_head userspace_clients; +struct list_head mux_list_head; +struct list_head mux_list;
As said before, I would much prefer if we didn't have to introduce these.
Quoted 9 lines
include/linux/i2c.h +2
-0};#define to_i2c_adapter (d) container_of(d, struct i2c_adapter, dev)@@ -380,6 +382,15 @@ static inline void i2c_unlock_adapter(struct i2c_adapter *adapter)rt_mutex_unlock (&adapter->bus_lock); } +/** +* i2c_trylock_adapter - Try to prevent access to an I2C bus segment
include/linux/i2c.h static inline void i2c_unlock_adapter(struct i2c_adapter *adapter) · line 396
Quoted 10 lines
include/linux/i2c.h +7
-0+* @adapter: Target I2C bus segment +*/ +static inline int i2c_trylock_adapter (struct i2c_adapter *adapter) +{ +return rt_mutex_trylock (&adapter->bus_lock); +} +/*flags for the client struct: */ #define I2C_CLIENT_PEC 0x04/* Use Packet Error Checking */ #define I2C_CLIENT_TEN 0x10/* we have a ten bit chip address */
Thanks,
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
Hi,
beside all the other issues,
Jean Delvare said the following:
Quoted 9 lines
Hi Michael,
drivers/i2c/i2c-mux.c line 145
Quoted 1 line
drivers/i2c/i2c-mux.c new file +1
-0+list_add_tail (&priv->adap.mux_list, &parent->mux_list_head);The driver core maintains a device tree already, do you really need to keep your own? We've tried hard to remove all redundancy between i2c-core and the driver core in the last couple years, so I really would like us to not add such redundancy back now.
Quoted 3 lines
If you have a good reason to have your own list, please explain. If not, please get rid of it.
this is a core part of mux management. You need it for checking whether the current client that is to be added is already present.
The problem is to efficiently parse down a mux tree towards the leaves. Sub-muxes are registered as i2c clients to their upper bus. I found no way to separate muxes from normal clients. So there were two ideas:
invent a client-type field in i2c-client or to hold a separate list. The first one e.g. would have the possibility to provide basic info in sysFs but I feared a general debate. So I decided to go the other way which is hidden to the outer world.
If you don't like this way, I'd need some ideas how to do it.
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 10 lines · Michael Lawnick
Jean Delvare said the following:
Quoted 9 lines
Hi Michael,
drivers/i2c/i2c-mux.c line 145
Quoted 1 line
drivers/i2c/i2c-mux.c new file +1
-0+list_add_tail (&priv->adap.mux_list, &parent->mux_list_head);The driver core maintains a device tree already, do you really need to keep your own? We've tried hard to remove all redundancy between i2c-core and the driver core in the last couple years, so I really would like us to not add such redundancy back now.
Quoted 7 lines
Quoted 3 lines
If you have a good reason to have your own list, please explain. If not, please get rid of it.
this is a core part of mux management. You need it for checking whether the current client that is to be added is already present. The problem is to efficiently parse down a mux tree towards the leaves. Sub-muxes are registered as i2c clients to their upper bus.
This is correct, but irrelevant, as far as I can see. When walking an I2C segment tree towards the leaves, you care about the segments themselves (struct i2c_adapter), not the mux chips. In your architecture, muxed segments are direct children of the root segment from the device tree perspective, not children of the mux chip.
Quoted 1 line
I found no way to separate muxes from normal clients.
If you have to, it is easy, the driver core has support for this already. Search for i2c_client_type and i2c_adapter_type in i2c-core.c. We could add i2c_mux_type easily. But as I wrote above, I don't think we need this.
Quoted 7 lines
So there were two ideas:
invent a client-type field in i2c-client or to hold a separate list. The first one e.g. would have the possibility to provide basic info in sysFs but I feared a general debate. So I decided to go the other way which is hidden to the outer world.If you don't like this way, I'd need some ideas how to do it.
I would walk the device tree maintained by the driver core (using device_for_each_child) and filtering out irrelevant devices. Basically filtering on dev->type == &i2c_adapter_type should do the trick, methinks. Is there anything I am missing?
Thanks,
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
Hi Jean,
Jean Delvare said the following:
Quoted 1 line
Quoted 1 line
Hi Michael,
...
Quoted 5 lines
Quoted 5 lines
I would walk the device tree maintained by the driver core (using
device_for_each_child) and filtering out irrelevant devices. Basically filtering ondev->type==&i2c_adapter_typeshould do the trick, methinks.
You are right, I missed that somehow when investigating for a solution. Until your post I was convinced that the adapters are all registered to bus only. I now rechecked by some small code and found that I can throw away these awefull lists.
Iteration will follow.
-- KR Michael
Hi Jean,
Jean Delvare said the following:
...
Quoted 21 lines
Quoted 7 lines
drivers/i2c/i2c-core.c +1
-1if (adapter->algo->smbus_xfer) { -rt_mutex_lock (&adapter->bus_lock); +i2c_mux_tree_lock (adapter);/* Retry automatically on arbitration loss */ orig_jiffies = jiffies;drivers/i2c/i2c-core.c s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, · line 1956
Quoted 8 lines
drivers/i2c/i2c-core.c +1
-1@@ -1871,7 +1950,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, orig_jiffies + adapter->timeout))break ; } -rt_mutex_unlock (&adapter->bus_lock); +i2c_mux_tree_unlock (adapter); }else res =i2c_smbus_xfer_emulated (adapter,addr,flags,read_write, command, protocol, data);I'm worried that this approach is risky. We now use i2c_mux_tree_*lock internally, but still expose the i2c_*lock_adapter() API externally. This API is _not_ mux-aware, and will do the wrong thing if called on a bus segment behind a mux.
Quoted 2 lines
I'd rather not have two APIs when we really only need one. Let's just update the current public API to be mux-aware, and use that everywhere.
Agree, but I'd vote to do it not inline as its now. Otherwise we start to export mux implementation details in i2c.h
Quoted 9 lines
Quoted 9 lines
drivers/i2c/i2c-dev.c +0
-0@@ -193,12 +193,56 @@ static int i2cdev_check(struct device *dev, void *addrp)return dev->driver ? -EBUSY :0 ; }
Quoted 9 lines
drivers/i2c/i2c-dev.c static int i2cdev_check(struct device *dev, void *addrp) · line 200
Quoted 5 lines
drivers/i2c/i2c-dev.c +5
-0+static int i2cdev_parent_is_i2c_adapter (const struct i2c_adapter *adapter) +{ +return adapter->dev.parent !=NULL + && adapter->dev.parent->bus == &i2c_bus_type; +}This is an exact duplicate of the same function in i2c-core. I'd rather make it an inline function in <
linux/i2c.h>, or export the one fromi2c-core.c. We have enough duplicate code ini2c-dev.calready :(
Must this and the above be done in an extra patch or can it be mixed into this mux patch?
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
Hi Jean,
Jean Delvare said the following:
...Quoted 21 lines
Quoted 7 lines
drivers/i2c/i2c-core.c +1
-1if (adapter->algo->smbus_xfer) { -rt_mutex_lock (&adapter->bus_lock); +i2c_mux_tree_lock (adapter);/* Retry automatically on arbitration loss */ orig_jiffies = jiffies;drivers/i2c/i2c-core.c s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, · line 1956
Quoted 8 lines
drivers/i2c/i2c-core.c +1
-1@@ -1871,7 +1950,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, orig_jiffies + adapter->timeout))break ; } -rt_mutex_unlock (&adapter->bus_lock); +i2c_mux_tree_unlock (adapter); }else res =i2c_smbus_xfer_emulated (adapter,addr,flags,read_write, command, protocol, data);I'm worried that this approach is risky. We now use i2c_mux_tree_*lock internally, but still expose the i2c_*lock_adapter() API externally. This API is _not_ mux-aware, and will do the wrong thing if called on a bus segment behind a mux.
Quoted 5 lines
Quoted 2 lines
I'd rather not have two APIs when we really only need one. Let's just update the current public API to be mux-aware, and use that everywhere.
Agree, but I'd vote to do it not inline as its now. Otherwise we start to export mux implementation details in
i2c.h
Of course. In fact I am preparing a patch which moves these functions to i2c-core, uninlines them, and uses them as appropriate. Your multiplexing patch would stack on top of it, so it is nice and clean and self-contained.
Quoted 18 lines
Quoted 8 lines
Quoted 8 lines
drivers/i2c/i2c-dev.c +0
-0@@ -193,12 +193,56 @@ static int i2cdev_check(struct device *dev, void *addrp)return dev->driver ? -EBUSY :0 ; }Quoted 9 lines
drivers/i2c/i2c-dev.c static int i2cdev_check(struct device *dev, void *addrp) · line 200
Quoted 5 lines
drivers/i2c/i2c-dev.c +5
-0+static int i2cdev_parent_is_i2c_adapter (const struct i2c_adapter *adapter) +{ +return adapter->dev.parent !=NULL + && adapter->dev.parent->bus == &i2c_bus_type; +}This is an exact duplicate of the same function in i2c-core. I'd rather make it an inline function in <
linux/i2c.h>, or export the one fromi2c-core.c. We have enough duplicate code ini2c-dev.calready :(
Quoted 1 line
Must this and the above be done in an extra patch or can it be mixed into this mux patch?
Whatever is easier for you, both are fine with me.
Signature · 1 line
Jean Delvare