From: Peter Rosin <hidden> Date: 2016-11-21 13:32:43
Hi!
The code depends on the _available work in iio which can
be found in linux-next.
v2 -> v3 changes
- have the mux-controller in the parent node of any mux-controller consumer,
to hopefully satisfy complaint from Rob about dt complexity.
- improve commit message of the mux subsystem commit, making it more
general, as requested by Jonathan.
- remove priv member from struct mux_control and calculate it on the
fly. /Jonathan.
- make the function comments in mux-core.c kernel doc. /Jonathan
- add devm_mux_control_* to Documentation/driver.model/devres.txt. /Jonathan
- add common dt bindings for mux-controllers, refer to them from the
mux-gpio bindings.
- clarify how the gpio pins map to the mux state.
- separate CONFIG_ variables for the mux core and the mux gpio driver.
- improve Kconfig help texts.
- make CONFIG_MUX_GPIO depend on CONFIG_GPIOLIB.
- keep track of the number of mux states in the mux core.
- since the iio channel number is used as mux state, it was possible
to drop the state member from the mux_child struct.
- cleanup dt bindings for i2c-mux-simple, it had some of copy-paste
problems from ots origin (i2c-mux-gpio).
- select the mux control subsystem in config for the i2c-mux-simple driver.
- add entries to MAINTAINERS and my sign-off, I'm now satisfied and know
nothing in this to be ashamed of.
v1 -> v2 changes
- fixup export of mux_control_put reported by kbuild
- drop devicetree iio-ext-info property as noted by Lars-Peter,
and replace the functionality by exposing all ext_info
attributes of the parent channel for each of the muxed
channels. A cache on top of that and each muxed channel
gets its own view of the ext_info of the parent channel.
- implement idle-state for muxes
- clear out the cache on failure in order to force a mux
update on the following use
- cleanup the probe of i2c-mux-simple driver
- fix a bug in the i2c-mux-simple driver, where failure in
the selection of the mux caused a deadlock when the mux
was later unconditionally deselected.
I have a piece of hardware that is using the same 3 GPIO pins
to control four 8-way muxes. Three of them control ADC lines
to an ADS1015 chip with an iio driver, and the last one
controls the SDA line of an i2c bus. We have some deployed
code to handle this, but you do not want to see it or ever
hear about it. I'm not sure why I even mention it. Anyway,
the situation has nagged me to no end for quite some time.
So, after first getting more intimate with the i2c muxing code
and later discovering the drivers/iio/inkern.c file and
writing a couple of drivers making use of it, I came up with
what I think is an acceptable solution; add a generic mux
controller driver (and subsystem) that is shared between all
instances, and combine that with an iio mux driver and a new
generic i2c mux driver. The new i2c mux I called "simple"
since it is only hooking the i2c muxing and the new mux
controller (much like the alsa simple card driver does for ASoC).
My initial (private) version didn't add "mux" as a new bus,
but I couldn't get decent type-checking and nice devicetree
integration with that. It does however feel a bit rich to
add a new bus for something as small as mux controllers?
One thing that I would like to do, but don't see a solution
for, is to move the mux control code that is present in
various drivers in drivers/i2c/muxes to this new minimalistic
muxing subsystem, thus converting all present i2c muxes (but
perhaps not gates and arbitrators) to be i2c-mux-simple muxes.
I'm using an rwsem to lock a mux, but that isn't really a
perfect fit. Is there a better locking primitive that I don't
know about that fits better? I had a mutex at one point, but
that didn't allow any concurrent accesses at all. At least
the rwsem allows concurrent access as long as all users
agree on the mux state, but I suspect that the rwsem will
degrade to the mutex situation pretty quickly if there is
any contention.
Also, the "mux" name feels a bit ambitious, there are many muxes
in the world, and this tiny bit of code is probably not good
enough to be a nice fit for all...
Cheers,
Peter
Peter Rosin (7):
dt-bindings: document devicetree bindings for mux-controllers and
mux-gpio
misc: minimal mux subsystem and gpio-based mux controller
iio: inkern: api for manipulating ext_info of iio channels
dt-bindings: iio: iio-mux: document iio-mux bindings
iio: multiplexer: new iio category and iio-mux driver
dt-bindings: i2c: i2c-mux-simple: document i2c-mux-simple bindings
i2c: i2c-mux-simple: new driver
.../devicetree/bindings/i2c/i2c-mux-simple.txt | 76 ++++
.../bindings/iio/multiplexer/iio-mux.txt | 47 +++
.../devicetree/bindings/misc/mux-controller.txt | 76 ++++
.../devicetree/bindings/misc/mux-gpio.txt | 78 ++++
Documentation/driver-model/devres.txt | 6 +-
MAINTAINERS | 14 +
drivers/i2c/muxes/Kconfig | 13 +
drivers/i2c/muxes/Makefile | 1 +
drivers/i2c/muxes/i2c-mux-simple.c | 177 ++++++++
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/inkern.c | 55 +++
drivers/iio/multiplexer/Kconfig | 18 +
drivers/iio/multiplexer/Makefile | 6 +
drivers/iio/multiplexer/iio-mux.c | 450 +++++++++++++++++++++
drivers/misc/Kconfig | 23 ++
drivers/misc/Makefile | 2 +
drivers/misc/mux-core.c | 325 +++++++++++++++
drivers/misc/mux-gpio.c | 116 ++++++
include/linux/iio/consumer.h | 37 ++
include/linux/mux.h | 55 +++
21 files changed, 1576 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt
create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
create mode 100644 Documentation/devicetree/bindings/misc/mux-controller.txt
create mode 100644 Documentation/devicetree/bindings/misc/mux-gpio.txt
create mode 100644 drivers/i2c/muxes/i2c-mux-simple.c
create mode 100644 drivers/iio/multiplexer/Kconfig
create mode 100644 drivers/iio/multiplexer/Makefile
create mode 100644 drivers/iio/multiplexer/iio-mux.c
create mode 100644 drivers/misc/mux-core.c
create mode 100644 drivers/misc/mux-gpio.c
create mode 100644 include/linux/mux.h
--
2.1.4
From: Peter Rosin <hidden> Date: 2016-11-21 13:17:51
Extend the inkern api with functions for reading and writing ext_info
of iio channels.
Signed-off-by: Peter Rosin <redacted>
---
drivers/iio/inkern.c | 55 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/iio/consumer.h | 37 +++++++++++++++++++++++++++++
2 files changed, 92 insertions(+)
@@ -746,3 +746,58 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)returnret;}EXPORT_SYMBOL_GPL(iio_write_channel_raw);++intiio_get_channel_ext_info_count(structiio_channel*chan)+{+conststructiio_chan_spec_ext_info*ext_info;+unsignedinti=0;++if(!chan->channel->ext_info)+returni;++for(ext_info=chan->channel->ext_info;ext_info->name;ext_info++)+++i;++returni;+}+EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);++ssize_tiio_read_channel_ext_info(structiio_channel*chan,+constchar*attr,char*buf)+{+conststructiio_chan_spec_ext_info*ext_info;++if(!chan->channel->ext_info)+return-EINVAL;++for(ext_info=chan->channel->ext_info;ext_info->name;++ext_info){+if(strcmp(attr,ext_info->name))+continue;++returnext_info->read(chan->indio_dev,ext_info->private,+chan->channel,buf);+}++return-EINVAL;+}+EXPORT_SYMBOL_GPL(iio_read_channel_ext_info);++ssize_tiio_write_channel_ext_info(structiio_channel*chan,constchar*attr,+constchar*buf,size_tlen)+{+conststructiio_chan_spec_ext_info*ext_info;++if(!chan->channel->ext_info)+return-EINVAL;++for(ext_info=chan->channel->ext_info;ext_info->name;++ext_info){+if(strcmp(attr,ext_info->name))+continue;++returnext_info->write(chan->indio_dev,ext_info->private,+chan->channel,buf,len);+}++return-EINVAL;+}+EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
@@ -271,4 +271,41 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val,intiio_convert_raw_to_processed(structiio_channel*chan,intraw,int*processed,unsignedintscale);+/**+*iio_get_channel_ext_info_count()-getnumberofext_infoattributes+*connectedtothechannel.+*@chan:Thechannelbeingqueried+*+*Returnsthenumberofext_infoattributes+*/+intiio_get_channel_ext_info_count(structiio_channel*chan);++/**+*iio_read_channel_ext_info()-readext_infoattributefromagivenchannel+*@chan:Thechannelbeingqueried.+*@attr:Theext_infoattributetoread.+*@buf:Wheretostoretheattributevalue.Assumedtohold+*atleastPAGE_SIZEbytes.+*+*Returnsthenumberofbyteswrittentobuf(perhapsw/ozerotermination;+*itneednotevenbeastring),oranerrorcode.+*/+ssize_tiio_read_channel_ext_info(structiio_channel*chan,+constchar*attr,char*buf);++/**+*iio_write_channel_ext_info()-writeext_infoattributefromagivenchannel+*@chan:Thechannelbeingqueried.+*@attr:Theext_infoattributetoread.+*@buf:Thenewattributevalue.Stringsneedstobezero-+*terminated,buttheterminatorshouldnotbeincluded+*inthebelowlen.+*@len:Thesizeofthenewattributevalue.+*+*Returnsthenumberofacceptedbytes,whichshouldbethesameaslen.+*Anerrorcodecanalsobereturned.+*/+ssize_tiio_write_channel_ext_info(structiio_channel*chan,constchar*attr,+constchar*buf,size_tlen);+#endif
From: Peter Rosin <hidden> Date: 2016-11-21 13:33:07
Add a new minimalistic subsystem that handles multiplexer controllers.
When multiplexers are used in various places in the kernel, and the
same multiplexer controller can be used for several independent things,
there should be one place to implement support for said multiplexer
controller.
A single multiplexer controller can also be used to control several
parallel multiplexers, that are in turn used by different subsystems
in the kernel, leading to a need to coordinate multiplexer accesses.
The multiplexer subsystem handles this coordination.
This new mux controller subsystem comes with a single backend driver
that controls gpio based multiplexers.
Signed-off-by: Peter Rosin <redacted>
---
Documentation/driver-model/devres.txt | 6 +-
MAINTAINERS | 2 +
drivers/misc/Kconfig | 23 +++
drivers/misc/Makefile | 2 +
drivers/misc/mux-core.c | 325 ++++++++++++++++++++++++++++++++++
drivers/misc/mux-gpio.c | 116 ++++++++++++
include/linux/mux.h | 55 ++++++
7 files changed, 528 insertions(+), 1 deletion(-)
create mode 100644 drivers/misc/mux-core.c
create mode 100644 drivers/misc/mux-gpio.c
create mode 100644 include/linux/mux.h
@@ -0,0 +1,325 @@+/*+*Multiplexersubsystem+*+*Copyright(C)2016AxentiaTechnologiesAB+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*/++#define pr_fmt(fmt) "mux-core: " fmt++#include<linux/device.h>+#include<linux/err.h>+#include<linux/idr.h>+#include<linux/module.h>+#include<linux/mux.h>+#include<linux/of.h>+#include<linux/of_platform.h>+#include<linux/slab.h>++staticstructbus_typemux_bus_type={+.name="mux",+};++staticint__initmux_init(void)+{+returnbus_register(&mux_bus_type);+}++staticvoid__exitmux_exit(void)+{+bus_unregister(&mux_bus_type);+}++staticDEFINE_IDA(mux_ida);++staticvoidmux_control_release(structdevice*dev)+{+structmux_control*mux=to_mux_control(dev);++ida_simple_remove(&mux_ida,mux->id);+kfree(mux);+}++staticstructdevice_typemux_control_type={+.name="mux-control",+.release=mux_control_release,+};++/**+*mux_control_alloc-allocateamux-control+*@sizeof_priv:Sizeofextramemoryareaforprivateusebythecaller.+*+*Returnsthenewmux-control.+*/+structmux_control*mux_control_alloc(size_tsizeof_priv)+{+structmux_control*mux;++mux=kzalloc(sizeof(*mux)+sizeof_priv,GFP_KERNEL);+if(!mux)+returnNULL;++mux->dev.bus=&mux_bus_type;+mux->dev.type=&mux_control_type;+device_initialize(&mux->dev);+dev_set_drvdata(&mux->dev,mux);++init_rwsem(&mux->lock);++mux->id=ida_simple_get(&mux_ida,0,0,GFP_KERNEL);+if(mux->id<0){+pr_err("mux-controlX failed to get device id\n");+kfree(mux);+returnNULL;+}+dev_set_name(&mux->dev,"mux:control%d",mux->id);++mux->cached_state=-1;+mux->idle_state=-1;++returnmux;+}+EXPORT_SYMBOL_GPL(mux_control_alloc);++/**+*mux_control_register-registeramux-control,thusreadyingitforuse+*@mux:Themux-controltoregister.+*+*Returnszeroonsuccessoranegativeerrnoonerror.+*/+intmux_control_register(structmux_control*mux)+{+intret;++/* If the calling driver did not initialize of_node, do it here */+if(!mux->dev.of_node&&mux->dev.parent)+mux->dev.of_node=mux->dev.parent->of_node;++ret=device_add(&mux->dev);+if(ret<0)+returnret;++ret=of_platform_populate(mux->dev.of_node,NULL,NULL,&mux->dev);+if(ret<0)+device_del(&mux->dev);++returnret;+}+EXPORT_SYMBOL_GPL(mux_control_register);++/**+*mux_control_unregister-takethemux-controloff-line,reversingthe+*effextsofmux_control_register+*@mux:themux-controltounregister.+*/+voidmux_control_unregister(structmux_control*mux)+{+of_platform_depopulate(&mux->dev);+device_del(&mux->dev);+}+EXPORT_SYMBOL_GPL(mux_control_unregister);++/**+*mux_control_put-putawaythemux-controlforgood,reversingthe+*effectsofeithermux_control_allocormux_control_get+*@mux:Themux-controltoputaway.+*/+voidmux_control_put(structmux_control*mux)+{+if(!mux)+return;+put_device(&mux->dev);+}+EXPORT_SYMBOL_GPL(mux_control_put);++staticintmux_control_set(structmux_control*mux,intstate)+{+intret=mux->ops->set(mux,state);++mux->cached_state=ret<0?-1:state;++returnret;+}++/**+*mux_control_select-selectthegivenmultiplexerstate+*@mux:Themux-controltorequestachangeofstatefrom.+*@state:Thenewrequestedstate.+*+*Returns0iftherequestedstatewasalreadyactive,or1itthe+*mux-controlstatewaschangedtotherequestedstate.Oranegavive+*errnoonerror.+*Notethatthedifferenceinreturnvalueofzerooroneisof+*questionablevalue;especiallyifthemux-controlhasseveralindependent+*consumers,whichissomethingtheconsumersshouldnotbemaking+*assumptionsabout.+*+*Makesuretocallmux_control_deselectwhentheoperationiscompleteand+*themux-controlisfreeforotherstouse,butdonotcall+*mux_control_deselectifmux_control_selectfails.+*/+intmux_control_select(structmux_control*mux,intstate)+{+intret;++if(down_read_trylock(&mux->lock)){+if(mux->cached_state==state)+return0;++/* Sigh, the mux needs updating... */+up_read(&mux->lock);+}++/* ...or it's just contended. */+down_write(&mux->lock);++if(mux->cached_state==state){+/*+*Hmmm,someoneelsechangedthemuxtomyliking.+*ThatmakesmewonderhowlongIwaitedfornothing?+*/+downgrade_write(&mux->lock);+return0;+}++ret=mux_control_set(mux,state);+if(ret<0){+if(mux->idle_state!=-1)+mux_control_set(mux,mux->idle_state);++up_write(&mux->lock);+returnret;+}++downgrade_write(&mux->lock);++return1;+}+EXPORT_SYMBOL_GPL(mux_control_select);++/**+*mux_control_deselect-deselectthepreviouslyselectedmultiplexerstate+*@mux:Themux-controltodeselect.+*+*Returns0onsuccessandanegativeerrnoonerror.Anerrorcanonly+*occurifthemuxhasanidlestate.Notethatevenifanerroroccurs,the+*mux-controlisunlockedforotherstoaccess.+*/+intmux_control_deselect(structmux_control*mux)+{+intret=0;++if(mux->idle_state!=-1&&mux->cached_state!=mux->idle_state)+ret=mux_control_set(mux,mux->idle_state);++up_read(&mux->lock);++returnret;+}+EXPORT_SYMBOL_GPL(mux_control_deselect);++staticintof_dev_node_match(structdevice*dev,void*data)+{+returndev->of_node==data;+}++staticstructmux_control*of_find_mux_by_node(structdevice_node*np)+{+structdevice*dev;++dev=bus_find_device(&mux_bus_type,NULL,np,of_dev_node_match);++returndev?to_mux_control(dev):NULL;+}++/**+*mux_control_get-getthemux-controlforadevice+*@dev:Thedevicethatneedsamux-control.+*+*Returnsthemux-control.+*/+structmux_control*mux_control_get(structdevice*dev)+{+structmux_control*mux;++if(!dev->of_node)+returnERR_PTR(-ENODEV);++mux=of_find_mux_by_node(dev->of_node->parent);+if(!mux)+returnERR_PTR(-EPROBE_DEFER);++returnmux;+}+EXPORT_SYMBOL_GPL(mux_control_get);++staticvoiddevm_mux_control_free(structdevice*dev,void*res)+{+structmux_control*mux=*(structmux_control**)res;++mux_control_put(mux);+}++/**+*devm_mux_control_get-getthemux-controlforadevice,withresource+*management+*@dev:Thedevicethatneedsamux-control.+*+*Returnsthemux-control.+*/+structmux_control*devm_mux_control_get(structdevice*dev)+{+structmux_control**ptr,*mux;++ptr=devres_alloc(devm_mux_control_free,sizeof(*ptr),GFP_KERNEL);+if(!ptr)+returnERR_PTR(-ENOMEM);++mux=mux_control_get(dev);+if(IS_ERR(mux)){+devres_free(ptr);+returnmux;+}++*ptr=mux;+devres_add(dev,ptr);++returnmux;+}+EXPORT_SYMBOL_GPL(devm_mux_control_get);++staticintdevm_mux_control_match(structdevice*dev,void*res,void*data)+{+structmux_control**r=res;++if(!r||!*r){+WARN_ON(!r||!*r);+return0;+}++return*r==data;+}++/**+*devm_mux_control_put-resource-managedversionmux_control_put+*@dev:Thedevicethatoriginallygotthemux-control.+*@mux:Themux-controltoputaway.+*+*Notethatyoudonotnormallyneedtocallthisfunction.+*/+voiddevm_mux_control_put(structdevice*dev,structmux_control*mux)+{+WARN_ON(devres_release(dev,devm_mux_control_free,+devm_mux_control_match,mux));+}+EXPORT_SYMBOL_GPL(devm_mux_control_put);++subsys_initcall(mux_init);+module_exit(mux_exit);++MODULE_AUTHOR("Peter Rosin <peda@axentia.se");+MODULE_DESCRIPTION("MUX subsystem");+MODULE_LICENSE("GPL v2");
@@ -0,0 +1,76 @@+Common multiplexer controller bindings++A mux controller will have one, or several, child devices that uses (or+consumes) a mux controller. A mux controller can possibly control several+parallel multiplexers. The node for a mux controller will have one child+node for each multiplexer controller consumer.++A mux controller provides a number of states to its consumers, and the+state space is a simple zero-based enumeration. I.e. 0-1 for a 2-way+multiplexer, 0-7 for an 8-way multiplexer, etc.++Example:++ /*+ * Two consumers (one for an ADC line and one for an i2c bus) of+ * parallel 4-way multiplexers controlled by the same two GPIO-lines.+ */+ mux {+ compatible = "mux-gpio";++ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,+ <&pioA 1 GPIO_ACTIVE_HIGH>;++ adc {+ compatible = "iio-mux";+ io-channels = <&adc 0>;+ io-channel-names = "parent";++ #address-cells = <1>;+ #size-cells = <0>;++ sync-1@0 {+ reg = <0>;+ };++ in@1 {+ reg = <1>;+ };++ out@2 {+ reg = <2>;+ };++ sync-2@3 {+ reg = <3>;+ };+ };++ i2c-mux {+ compatible = "i2c-mux-simple,mux-locked";+ i2c-parent = <&i2c1>;++ #address-cells = <1>;+ #size-cells = <0>;++ i2c@0 {+ reg = <0>;+ #address-cells = <1>;+ #size-cells = <0>;++ ssd1307: oled@3c {+ /* ... */+ };+ };++ i2c@3 {+ reg = <3>;+ #address-cells = <1>;+ #size-cells = <0>;++ pca9555: pca9555@20 {+ /* ... */+ };+ };+ };+ };
@@ -0,0 +1,78 @@+GPIO-based multiplexer controller bindings++Define what GPIO pins are used to control a multiplexer. Or several+multiplexers, if the same pins control more than one multiplexer.++Required properties:+- compatible : "mux-gpio"+- mux-gpios : list of gpios used to control the multiplexer, least+ significant bit first.+* Standard mux-controller bindings as decribed in mux-controller.txt++Optional properties:+- idle-state : if present, the state the mux will have when idle.++The multiplexer state is defined as the number represented by the+multiplexer GPIO pins, where the first pin is the least significant+bit. And active pin is a binary 1, an inactive pin is a binary 0.++Example:+ mux {+ compatible = "mux-gpio";++ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,+ <&pioA 1 GPIO_ACTIVE_HIGH>;++ adc {+ compatible = "iio-mux";+ io-channels = <&adc 0>;+ io-channel-names = "parent";++ #address-cells = <1>;+ #size-cells = <0>;++ sync-1@0 {+ reg = <0>;+ };++ in@1 {+ reg = <1>;+ };++ out@2 {+ reg = <2>;+ };++ sync-2@3 {+ reg = <3>;+ };+ };++ i2c-mux {+ compatible = "i2c-mux-simple,mux-locked";+ i2c-parent = <&i2c1>;++ #address-cells = <1>;+ #size-cells = <0>;++ i2c@0 {+ reg = <0>;+ #address-cells = <1>;+ #size-cells = <0>;++ ssd1307: oled@3c {+ /* ... */+ };+ };++ i2c@3 {+ reg = <3>;+ #address-cells = <1>;+ #size-cells = <0>;++ pca9555: pca9555@20 {+ /* ... */+ };+ };+ };+ };
From: Peter Rosin <hidden> Date: 2016-11-21 13:33:22
When a multiplexer changes how an iio device behaves (for example
by feeding different signals to an ADC), this driver can be used
create one virtual iio channel for each multiplexer state.
Depends on the generic multiplexer subsystem.
Cache any ext_info values from the parent iio channel, creating a private
copy of the ext_info attributes for each multiplexer state/channel.
Signed-off-by: Peter Rosin <redacted>
---
MAINTAINERS | 1 +
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/multiplexer/Kconfig | 18 ++
drivers/iio/multiplexer/Makefile | 6 +
drivers/iio/multiplexer/iio-mux.c | 450 ++++++++++++++++++++++++++++++++++++++
6 files changed, 477 insertions(+)
create mode 100644 drivers/iio/multiplexer/Kconfig
create mode 100644 drivers/iio/multiplexer/Makefile
create mode 100644 drivers/iio/multiplexer/iio-mux.c
@@ -0,0 +1,18 @@+#+# Multiplexer drivers+#+# When adding new entries keep the list in alphabetical order++menu"Multiplexers"++configIIO_MUX+tristate"IIO multiplexer driver"+selectMULTIPLEXER+depends onOF+help+SayyesheretobuildsupportfortheIIOmultiplexer.++Tocompilethisdriverasamodule,chooseMhere:the+modulewillbecallediio-mux.++endmenu
@@ -0,0 +1,6 @@+#+# Makefile for industrial I/O multiplexer drivers+#++# When adding new entries keep the list in alphabetical order+obj-$(CONFIG_IIO_MUX)+=iio-mux.o
@@ -0,0 +1,450 @@+/*+*IIOmultiplexerdriver+*+*Copyright(C)2016AxentiaTechnologiesAB+*+*Author:PeterRosin<peda@axentia.se>+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*/++#include<linux/err.h>+#include<linux/iio/consumer.h>+#include<linux/iio/iio.h>+#include<linux/module.h>+#include<linux/mutex.h>+#include<linux/mux.h>+#include<linux/of.h>+#include<linux/platform_device.h>++structmux_ext_info_cache{+char*data;+size_tsize;+};++structmux_child{+structmux_ext_info_cache*ext_info_cache;+};++structmux{+intcached_state;+structmux_control*control;+structiio_channel*parent;+structiio_dev*indio_dev;+structiio_chan_spec*chan;+structiio_chan_spec_ext_info*ext_info;+structmux_child*child;+};++staticintiio_mux_select(structmux*mux,intidx)+{+structmux_child*child=&mux->child[idx];+structiio_chan_specconst*chan=&mux->chan[idx];+intret;+inti;++ret=mux_control_select(mux->control,chan->channel);+if(ret<0){+mux->cached_state=-1;+returnret;+}++if(mux->cached_state==chan->channel)+return0;++if(chan->ext_info){+for(i=0;chan->ext_info[i].name;++i){+constchar*attr=chan->ext_info[i].name;+structmux_ext_info_cache*cache;++cache=&child->ext_info_cache[i];++if(cache->size<0)+continue;++ret=iio_write_channel_ext_info(mux->parent,attr,+cache->data,+cache->size);++if(ret<0){+mux_control_deselect(mux->control);+mux->cached_state=-1;+returnret;+}+}+}+mux->cached_state=chan->channel;++return0;+}++staticvoidiio_mux_deselect(structmux*mux)+{+mux_control_deselect(mux->control);+}++staticintmux_read_raw(structiio_dev*indio_dev,+structiio_chan_specconst*chan,+int*val,int*val2,longmask)+{+structmux*mux=iio_priv(indio_dev);+intidx=chan-mux->chan;+intret;++ret=iio_mux_select(mux,idx);+if(ret<0)+returnret;++switch(mask){+caseIIO_CHAN_INFO_RAW:+ret=iio_read_channel_raw(mux->parent,val);+break;++caseIIO_CHAN_INFO_SCALE:+ret=iio_read_channel_scale(mux->parent,val,val2);+break;++default:+ret=-EINVAL;+}++iio_mux_deselect(mux);++returnret;+}++staticintmux_read_avail(structiio_dev*indio_dev,+structiio_chan_specconst*chan,+constint**vals,int*type,int*length,+longmask)+{+structmux*mux=iio_priv(indio_dev);+intidx=chan-mux->chan;+intret;++ret=iio_mux_select(mux,idx);+if(ret<0)+returnret;++switch(mask){+caseIIO_CHAN_INFO_RAW:+*type=IIO_VAL_INT;+ret=iio_read_avail_channel_raw(mux->parent,vals,length);+break;++default:+ret=-EINVAL;+}++iio_mux_deselect(mux);++returnret;+}++staticintmux_write_raw(structiio_dev*indio_dev,+structiio_chan_specconst*chan,+intval,intval2,longmask)+{+structmux*mux=iio_priv(indio_dev);+intidx=chan-mux->chan;+intret;++ret=iio_mux_select(mux,idx);+if(ret<0)+returnret;++switch(mask){+caseIIO_CHAN_INFO_RAW:+ret=iio_write_channel_raw(mux->parent,val);+break;++default:+ret=-EINVAL;+}++iio_mux_deselect(mux);++returnret;+}++staticconststructiio_infomux_info={+.read_raw=mux_read_raw,+.read_avail=mux_read_avail,+.write_raw=mux_write_raw,+.driver_module=THIS_MODULE,+};++staticssize_tmux_read_ext_info(structiio_dev*indio_dev,uintptr_tprivate,+structiio_chan_specconst*chan,char*buf)+{+structmux*mux=iio_priv(indio_dev);+intidx=chan-mux->chan;+ssize_tret;++ret=iio_mux_select(mux,idx);+if(ret<0)+returnret;++ret=iio_read_channel_ext_info(mux->parent,+mux->ext_info[private].name,+buf);++iio_mux_deselect(mux);++returnret;+}++staticssize_tmux_write_ext_info(structiio_dev*indio_dev,uintptr_tprivate,+structiio_chan_specconst*chan,+constchar*buf,size_tlen)+{+structdevice*dev=indio_dev->dev.parent;+structmux*mux=iio_priv(indio_dev);+intidx=chan-mux->chan;+char*new;+ssize_tret;++ret=iio_mux_select(mux,idx);+if(ret<0)+returnret;++new=devm_kmemdup(dev,buf,len+1,GFP_KERNEL);+if(!new){+iio_mux_deselect(mux);+return-ENOMEM;+}++new[len]=0;++ret=iio_write_channel_ext_info(mux->parent,+mux->ext_info[private].name,+buf,len);+if(ret<0){+iio_mux_deselect(mux);+devm_kfree(dev,new);+returnret;+}++devm_kfree(dev,mux->child[idx].ext_info_cache[private].data);+mux->child[idx].ext_info_cache[private].data=new;+mux->child[idx].ext_info_cache[private].size=len;++iio_mux_deselect(mux);++returnret;+}++staticintmux_configure_channel(structdevice*dev,structmux*mux,+structdevice_node*child_np,intidx)+{+structmux_child*child=&mux->child[idx];+structiio_chan_spec*chan=&mux->chan[idx];+structiio_chan_specconst*pchan=mux->parent->channel;+u32state;+char*page=NULL;+intnum_ext_info;+inti;+intret;++chan->indexed=1;+chan->output=pchan->output;+chan->datasheet_name=child_np->name;+chan->ext_info=mux->ext_info;++ret=iio_get_channel_type(mux->parent,&chan->type);+if(ret<0){+dev_err(dev,"failed to get parent channel type\n");+returnret;+}++if(iio_channel_has_info(pchan,IIO_CHAN_INFO_RAW))+chan->info_mask_separate|=BIT(IIO_CHAN_INFO_RAW);+if(iio_channel_has_info(pchan,IIO_CHAN_INFO_SCALE))+chan->info_mask_separate|=BIT(IIO_CHAN_INFO_SCALE);++if(iio_channel_has_available(pchan,IIO_CHAN_INFO_RAW))+chan->info_mask_separate_available|=BIT(IIO_CHAN_INFO_RAW);++ret=of_property_read_u32(child_np,"reg",&state);+if(ret<0){+dev_err(dev,"no reg property for node '%s'\n",child_np->name);+returnret;+}++if(state>=mux->control->states){+dev_err(dev,"invalid reg %u\n",state);+return-EINVAL;+}++for(i=0;i<idx;++i){+if(mux->chan[i].channel==state){+dev_err(dev,"double use of reg %u\n",state);+return-EINVAL;+}+}++chan->channel=state;++num_ext_info=iio_get_channel_ext_info_count(mux->parent);+if(num_ext_info){+page=devm_kzalloc(dev,PAGE_SIZE,GFP_KERNEL);+if(!page)+return-ENOMEM;+}+child->ext_info_cache=devm_kzalloc(dev,+sizeof(*child->ext_info_cache)*+num_ext_info,GFP_KERNEL);+for(i=0;i<num_ext_info;++i){+child->ext_info_cache[i].size=-1;++if(!pchan->ext_info[i].write)+continue;+if(!pchan->ext_info[i].read)+continue;++ret=iio_read_channel_ext_info(mux->parent,+mux->ext_info[i].name,+page);+if(ret<0){+dev_err(dev,"failed to get ext_info '%s'\n",+pchan->ext_info[i].name);+returnret;+}+if(ret>=PAGE_SIZE){+dev_err(dev,"too large ext_info '%s'\n",+pchan->ext_info[i].name);+return-EINVAL;+}++child->ext_info_cache[i].data=devm_kmemdup(dev,page,ret+1,+GFP_KERNEL);+child->ext_info_cache[i].data[ret]=0;+child->ext_info_cache[i].size=ret;+}++if(page)+devm_kfree(dev,page);++return0;+}++staticintmux_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structdevice_node*np=pdev->dev.of_node;+structdevice_node*child_np;+structiio_dev*indio_dev;+structiio_channel*parent;+structmux*mux;+intsizeof_ext_info;+intchildren;+intsizeof_priv;+inti;+intret;++if(!np)+return-ENODEV;++parent=devm_iio_channel_get(dev,"parent");+if(IS_ERR(parent)){+if(PTR_ERR(parent)!=-EPROBE_DEFER)+dev_err(dev,"failed to get parent channel\n");+returnPTR_ERR(parent);+}++sizeof_ext_info=iio_get_channel_ext_info_count(parent);+if(sizeof_ext_info){+sizeof_ext_info+=1;/* one extra entry for the sentinel */+sizeof_ext_info*=sizeof(*mux->ext_info);+}++children=of_get_child_count(np);+if(children<=0){+dev_err(dev,"not even a single child\n");+return-EINVAL;+}++sizeof_priv=sizeof(*mux);+sizeof_priv+=sizeof(*mux->child)*children;+sizeof_priv+=sizeof(*mux->chan)*children;+sizeof_priv+=sizeof_ext_info;++indio_dev=devm_iio_device_alloc(dev,sizeof_priv);+if(!indio_dev)+return-ENOMEM;++mux=iio_priv(indio_dev);+mux->child=(structmux_child*)(mux+1);+mux->chan=(structiio_chan_spec*)(mux->child+children);++platform_set_drvdata(pdev,indio_dev);++mux->parent=parent;+mux->cached_state=-1;++indio_dev->name=dev_name(dev);+indio_dev->dev.parent=dev;+indio_dev->info=&mux_info;+indio_dev->modes=INDIO_DIRECT_MODE;+indio_dev->channels=mux->chan;+indio_dev->num_channels=children;+if(sizeof_ext_info){+mux->ext_info=devm_kmemdup(dev,+parent->channel->ext_info,+sizeof_ext_info,GFP_KERNEL);+if(!mux->ext_info)+return-ENOMEM;++for(i=0;mux->ext_info[i].name;++i){+if(parent->channel->ext_info[i].read)+mux->ext_info[i].read=mux_read_ext_info;+if(parent->channel->ext_info[i].write)+mux->ext_info[i].write=mux_write_ext_info;+mux->ext_info[i].private=i;+}+}++mux->control=devm_mux_control_get(dev);+if(IS_ERR(mux->control)){+if(PTR_ERR(mux->control)!=-EPROBE_DEFER)+dev_err(dev,"failed to get control-mux\n");+returnPTR_ERR(mux->control);+}++i=0;+for_each_child_of_node(np,child_np){+ret=mux_configure_channel(dev,mux,child_np,i);+if(ret<0)+returnret;+i++;+}++ret=devm_iio_device_register(dev,indio_dev);+if(ret){+dev_err(dev,"failed to register iio device\n");+returnret;+}++return0;+}++staticconststructof_device_idmux_match[]={+{.compatible="iio-mux"},+{/* sentinel */}+};+MODULE_DEVICE_TABLE(of,mux_match);++staticstructplatform_drivermux_driver={+.probe=mux_probe,+.driver={+.name="iio-mux",+.of_match_table=mux_match,+},+};+module_platform_driver(mux_driver);++MODULE_DESCRIPTION("IIO multiplexer driver");+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");+MODULE_LICENSE("GPL v2");
@@ -0,0 +1,76 @@+Simple I2C Bus Mux++This binding describes an I2C bus multiplexer that uses a mux controller+from the mux subsystem to route the I2C signals.++ .-----. .-----.+ | dev | | dev |+ .------------. '-----' '-----'+ | SoC | | |+ | | .--------+--------'+ | .------. | .------+ child bus A, on MUX value set to 0+ | | I2C |-|--| Mux |+ | '------' | '--+---+ child bus B, on MUX value set to 1+ | .------. | | '----------+--------+--------.+ | | MUX- | | | | | |+ | | Ctrl |-|-----+ .-----. .-----. .-----.+ | '------' | | dev | | dev | | dev |+ '------------' '-----' '-----' '-----'++Required properties:+- compatible: i2c-mux-simple,mux-locked or i2c-mux-simple,parent-locked+- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side+ port is connected to.+* Standard I2C mux properties. See i2c-mux.txt in this directory.+* I2C child bus nodes. See i2c-mux.txt in this directory. The sub-bus number+ is also the mux-controller state described in ../misc/mux-controller.txt++For each i2c child node, an I2C child bus will be created. They will+be numbered based on their order in the device tree.++Whenever an access is made to a device on a child bus, the value set+in the relevant node's reg property will be set as the state in the+mux controller.++Example:+ mux {+ compatible = "mux-gpio";++ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,+ <&pioA 1 GPIO_ACTIVE_HIGH>;++ i2c-mux {+ compatible = "i2c-mux-simple,mux-locked";+ i2c-parent = <&i2c1>;++ #address-cells = <1>;+ #size-cells = <0>;++ i2c@1 {+ reg = <1>;+ #address-cells = <1>;+ #size-cells = <0>;++ ssd1307: oled@3c {+ compatible = "solomon,ssd1307fb-i2c";+ reg = <0x3c>;+ pwms = <&pwm 4 3000>;+ reset-gpios = <&gpio2 7 1>;+ reset-active-low;+ };+ };++ i2c@3 {+ reg = <3>;+ #address-cells = <1>;+ #size-cells = <0>;++ pca9555: pca9555@20 {+ compatible = "nxp,pca9555";+ gpio-controller;+ #gpio-cells = <2>;+ reg = <0x20>;+ };+ };+ };+ };
From: Peter Rosin <hidden> Date: 2016-11-21 13:33:51
This is a generic simple i2c mux that uses the generic multiplexer
subsystem to do the muxing.
The user can select if the mux is to be mux-locked and parent-locked
as described in Documentation/i2c/i2c-topology.
Signed-off-by: Peter Rosin <redacted>
---
drivers/i2c/muxes/Kconfig | 13 +++
drivers/i2c/muxes/Makefile | 1 +
drivers/i2c/muxes/i2c-mux-simple.c | 177 +++++++++++++++++++++++++++++++++++++
3 files changed, 191 insertions(+)
create mode 100644 drivers/i2c/muxes/i2c-mux-simple.c
@@ -0,0 +1,177 @@+/*+*GenericsimpleI2Cmultiplexer+*+*PeterRosin<peda@axentia.se>+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*/++#include<linux/i2c.h>+#include<linux/i2c-mux.h>+#include<linux/module.h>+#include<linux/mux.h>+#include<linux/of_device.h>+#include<linux/platform_device.h>++structmux{+structmux_control*control;++booldo_not_deselect;+};++staticinti2c_mux_select(structi2c_mux_core*muxc,u32chan)+{+structmux*mux=i2c_mux_priv(muxc);+intret;++ret=mux_control_select(mux->control,chan);+mux->do_not_deselect=ret<0;++returnret;+}++staticinti2c_mux_deselect(structi2c_mux_core*muxc,u32chan)+{+structmux*mux=i2c_mux_priv(muxc);++if(mux->do_not_deselect)+return0;++returnmux_control_deselect(mux->control);+}++staticstructi2c_adapter*mux_parent_adapter(structdevice*dev)+{+structdevice_node*np=dev->of_node;+structdevice_node*parent_np;+structi2c_adapter*parent;++parent_np=of_parse_phandle(np,"i2c-parent",0);+if(!parent_np){+dev_err(dev,"Cannot parse i2c-parent\n");+returnERR_PTR(-ENODEV);+}+parent=of_find_i2c_adapter_by_node(parent_np);+of_node_put(parent_np);+if(!parent)+returnERR_PTR(-EPROBE_DEFER);++returnparent;+}++staticconststructof_device_idi2c_mux_of_match[]={+{.compatible="i2c-mux-simple,parent-locked",+.data=(void*)0,},+{.compatible="i2c-mux-simple,mux-locked",+.data=(void*)1,},+{},+};+MODULE_DEVICE_TABLE(of,i2c_mux_of_match);++staticinti2c_mux_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structdevice_node*np=dev->of_node;+structdevice_node*child;+conststructof_device_id*match;+structi2c_mux_core*muxc;+structmux*mux;+structi2c_adapter*parent;+intchildren;+intret;++if(!np)+return-ENODEV;++mux=devm_kzalloc(dev,sizeof(*mux),GFP_KERNEL);+if(!mux)+return-ENOMEM;++mux->control=devm_mux_control_get(dev);+if(IS_ERR(mux->control)){+if(PTR_ERR(mux->control)!=-EPROBE_DEFER)+dev_err(dev,"failed to get control-mux\n");+returnPTR_ERR(mux->control);+}++parent=mux_parent_adapter(dev);+if(IS_ERR(parent)){+if(PTR_ERR(parent)!=-EPROBE_DEFER)+dev_err(dev,"failed to get i2c-parent adapter\n");+returnPTR_ERR(parent);+}++children=of_get_child_count(np);++muxc=i2c_mux_alloc(parent,dev,children,0,0,+i2c_mux_select,i2c_mux_deselect);+if(!muxc){+ret=-ENOMEM;+gotoerr_parent;+}+muxc->priv=mux;++platform_set_drvdata(pdev,muxc);++match=of_match_device(of_match_ptr(i2c_mux_of_match),dev);+if(match)+muxc->mux_locked=!!of_device_get_match_data(dev);++for_each_child_of_node(np,child){+u32chan;++ret=of_property_read_u32(child,"reg",&chan);+if(ret<0){+dev_err(dev,"no reg property for node '%s'\n",+child->name);+gotoerr_children;+}++if(chan>=mux->control->states){+dev_err(dev,"invalid reg %u\n",chan);+ret=-EINVAL;+gotoerr_children;+}++ret=i2c_mux_add_adapter(muxc,0,chan,0);+if(ret)+gotoerr_children;+}++dev_info(dev,"%d-port mux on %s adapter\n",children,parent->name);++return0;++err_children:+i2c_mux_del_adapters(muxc);+err_parent:+i2c_put_adapter(parent);++returnret;+}++staticinti2c_mux_remove(structplatform_device*pdev)+{+structi2c_mux_core*muxc=platform_get_drvdata(pdev);++i2c_mux_del_adapters(muxc);+i2c_put_adapter(muxc->parent);++return0;+}++staticstructplatform_driveri2c_mux_driver={+.probe=i2c_mux_probe,+.remove=i2c_mux_remove,+.driver={+.name="i2c-mux-simple",+.of_match_table=i2c_mux_of_match,+},+};+module_platform_driver(i2c_mux_driver);++MODULE_DESCRIPTION("Simple I2C multiplexer driver");+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");+MODULE_LICENSE("GPL v2");
@@ -0,0 +1,47 @@+IIO multiplexer bindings++If a multiplexer is used to select which hardware signal is fed to+e.g. an ADC channel, these bindings describe that situation.++Required properties:+- compatible : "iio-mux"+- io-channels : Channel node of the parent channel that has multiplexed+ input.+- io-channel-names : Should be "parent".+- #address-cells = <1>;+- #size-cells = <0>;++Required properties for iio-mux child nodes:+- reg : The multiplexer state as described in ../misc/mux-controller.txt++For each iio-mux child, an iio channel will be created whose number will+match the mux controller state.++Example:+ mux {+ compatible = "mux-gpio";++ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,+ <&pioA 1 GPIO_ACTIVE_HIGH>;++ adc {+ compatible = "iio-mux";+ io-channels = <&adc 0>;+ io-channel-names = "parent";++ #address-cells = <1>;+ #size-cells = <0>;++ sync@0 {+ reg = <0>;+ };++ in@1 {+ reg = <1>;+ };++ system-regulator@2 {+ reg = <2>;+ };+ };+ };
From: kbuild test robot <hidden> Date: 2016-11-21 14:33:34
Hi Peter,
[auto build test ERROR on linus/master]
[also build test ERROR on v4.9-rc6]
[cannot apply to next-20161117]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Peter-Rosin/mux-controller-abstraction-and-iio-i2c-muxes/20161121-215311
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
drivers/iio/multiplexer/iio-mux.c: In function 'mux_read_avail':
quoted
drivers/iio/multiplexer/iio-mux.c:134:9: error: implicit declaration of function 'iio_read_avail_channel_raw' [-Werror=implicit-function-declaration]
ret = iio_read_avail_channel_raw(mux->parent, vals, length);
^~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/iio/multiplexer/iio-mux.c: At top level:
quoted
drivers/iio/multiplexer/iio-mux.c:174:2: error: unknown field 'read_avail' specified in initializer
.read_avail = mux_read_avail,
^
quoted
drivers/iio/multiplexer/iio-mux.c:174:16: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.read_avail = mux_read_avail,
^~~~~~~~~~~~~~
drivers/iio/multiplexer/iio-mux.c:174:16: note: (near initialization for 'mux_info.read_raw_multi')
drivers/iio/multiplexer/iio-mux.c: In function 'mux_configure_channel':
quoted
drivers/iio/multiplexer/iio-mux.c:267:6: error: implicit declaration of function 'iio_channel_has_available' [-Werror=implicit-function-declaration]
if (iio_channel_has_available(pchan, IIO_CHAN_INFO_RAW))
^~~~~~~~~~~~~~~~~~~~~~~~~
quoted
drivers/iio/multiplexer/iio-mux.c:268:7: error: 'struct iio_chan_spec' has no member named 'info_mask_separate_available'; did you mean 'info_mask_separate'?
I have a piece of hardware that is using the same 3 GPIO pins
to control four 8-way muxes. Three of them control ADC lines
to an ADS1015 chip with an iio driver, and the last one
controls the SDA line of an i2c bus. We have some deployed
code to handle this, but you do not want to see it or ever
hear about it. I'm not sure why I even mention it. Anyway,
the situation has nagged me to no end for quite some time.
So, after first getting more intimate with the i2c muxing code
and later discovering the drivers/iio/inkern.c file and
writing a couple of drivers making use of it, I came up with
what I think is an acceptable solution; add a generic mux
controller driver (and subsystem) that is shared between all
instances, and combine that with an iio mux driver and a new
generic i2c mux driver. The new i2c mux I called "simple"
since it is only hooking the i2c muxing and the new mux
controller (much like the alsa simple card driver does for ASoC).
While abstracting this properly is all nice and good and the way it should
be done, but it also adds a lot of complexity and the devicetree adds a lot
of restrictions on what can actually be represented.
There is a certain point where the fabric on a PCB becomes so complex that
it deserves to be a device on its own (like the audio fabric drivers).
Especially when the hardware is built with a certain application in mind and
the driver is supposed to impose policy which reflects this application. The
latter can often not properly be described with the primitives the
devicetree can offer.
And I think your setup is very borderline what can be done in a declarative
way only and it adds a lot of complexity over a more imperative solution in
form of a driver. I think it is worth investigating about having a driver
that is specific to your fabric and handles the interdependencies of the
discrete components.
From: Peter Rosin <hidden> Date: 2016-11-23 13:19:55
On 2016-11-22 21:58, Lars-Peter Clausen wrote:
On 11/21/2016 02:17 PM, Peter Rosin wrote:
[...]
quoted
I have a piece of hardware that is using the same 3 GPIO pins
to control four 8-way muxes. Three of them control ADC lines
to an ADS1015 chip with an iio driver, and the last one
controls the SDA line of an i2c bus. We have some deployed
code to handle this, but you do not want to see it or ever
hear about it. I'm not sure why I even mention it. Anyway,
the situation has nagged me to no end for quite some time.
So, after first getting more intimate with the i2c muxing code
and later discovering the drivers/iio/inkern.c file and
writing a couple of drivers making use of it, I came up with
what I think is an acceptable solution; add a generic mux
controller driver (and subsystem) that is shared between all
instances, and combine that with an iio mux driver and a new
generic i2c mux driver. The new i2c mux I called "simple"
since it is only hooking the i2c muxing and the new mux
controller (much like the alsa simple card driver does for ASoC).
While abstracting this properly is all nice and good and the way it should
be done, but it also adds a lot of complexity and the devicetree adds a lot
of restrictions on what can actually be represented.
This is a characterization without any specifics. But is the
characterization true? You have two complaints, complexity
and restrictions with bindings.
There is a certain point where the fabric on a PCB becomes so complex that
it deserves to be a device on its own (like the audio fabric drivers).
Especially when the hardware is built with a certain application in mind and
the driver is supposed to impose policy which reflects this application. The
latter can often not properly be described with the primitives the
devicetree can offer.
And I think your setup is very borderline what can be done in a declarative
way only and it adds a lot of complexity over a more imperative solution in
form of a driver. I think it is worth investigating about having a driver
that is specific to your fabric and handles the interdependencies of the
discrete components.
So, there are three "new" concepts:
1. Sticking a mux in front of an AD-converter. That's not all that
novel, nor complex. Quite the opposite, I'd say. In fact, I find it
a bit amazing that there is no in-kernel support for it.
2. Reusing the same GPIO-pins to drive different muxes. There are
obviously chips that work this way (as Jonathan pointed out) and
these will at some point get used in Linux devices. I guess they
already are used, but that people handle them in userspace. Or
something? If this is complex, which I question, it will still need
support at some point. At least that's what I believe.
3. Using the same GPIO pins to mux things handled by different
subsystems. Right, this is a bit crazy, and I'd rather not have this
requirement, but this HW is what it is so I'll need to handle it in
some way. It is also what stops me from falling back to a userspace
solution, which is probably connected to why #1 and #2 is not supported
by the kernel; everybody probably does muxing in userspace. Which is
not necessarily a good idea, nor how it's supposed to be done...
So, the only thing that's out of the ordinary (as I see it), is #3.
The question that then needs an answer is how the in-kernel solution
for #1 and #2 would look if we do not consider #3.
And I claim that the desired solution to #1 and #2 is pretty close
to my proposal.
A. You do not want mux-controller drivers in every subsystem that
needs them.
B. You do not want every mux-consumer to know the specifics of how to
operate every kind of mux; there are muxes that are not controlled
with GPIO pins...
C. When you implement muxing in a subsystem, there will in some cases
be a need to handle parallel muxing, where there is a need to share
mux-controllers.
It just feels right to separate out the mux-controller and refer to
it from where a mux is needed. It solves #1 and #2. And, of course,
as a bonus #3 is also solved. But my bias is obvious.
And that leads us to the restrictions with the bindings. And the same
thing happens; the solution for #2 also solves #3.
So how do you refer to a mux-controller from where it's needed? My
first proposal used a dt phandle, for the second round I put them in
the parent node. It would be super if it was possible for the mux-
consumer to create the mux-controller device from the same dt
node that is already bound to the mux-consumer. The problem is that
the mux-consumer should not hard-code which mux-controller device it
should create. The best I can think of is some kind of 'mux-compatible'
attribute, that works like the standard 'compatible' attribute. That
would simplify the bindings for the normal case (#1) where the mux-
controller isn't shared (#2 and #3). Maybe it's possible to fix this
issue somehow? I simply don't know?
Cheers,
Peter
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Jonathan Cameron <jic23@kernel.org> Date: 2016-11-27 12:00:34
On 23/11/16 11:47, Peter Rosin wrote:
On 2016-11-22 21:58, Lars-Peter Clausen wrote:
quoted
On 11/21/2016 02:17 PM, Peter Rosin wrote:
[...]
quoted
I have a piece of hardware that is using the same 3 GPIO pins
to control four 8-way muxes. Three of them control ADC lines
to an ADS1015 chip with an iio driver, and the last one
controls the SDA line of an i2c bus. We have some deployed
code to handle this, but you do not want to see it or ever
hear about it. I'm not sure why I even mention it. Anyway,
the situation has nagged me to no end for quite some time.
So, after first getting more intimate with the i2c muxing code
and later discovering the drivers/iio/inkern.c file and
writing a couple of drivers making use of it, I came up with
what I think is an acceptable solution; add a generic mux
controller driver (and subsystem) that is shared between all
instances, and combine that with an iio mux driver and a new
generic i2c mux driver. The new i2c mux I called "simple"
since it is only hooking the i2c muxing and the new mux
controller (much like the alsa simple card driver does for ASoC).
While abstracting this properly is all nice and good and the way it should
be done, but it also adds a lot of complexity and the devicetree adds a lot
of restrictions on what can actually be represented.
This is a characterization without any specifics. But is the
characterization true? You have two complaints, complexity
and restrictions with bindings.
quoted
There is a certain point where the fabric on a PCB becomes so complex that
it deserves to be a device on its own (like the audio fabric drivers).
Especially when the hardware is built with a certain application in mind and
the driver is supposed to impose policy which reflects this application. The
latter can often not properly be described with the primitives the
devicetree can offer.
And I think your setup is very borderline what can be done in a declarative
way only and it adds a lot of complexity over a more imperative solution in
form of a driver. I think it is worth investigating about having a driver
that is specific to your fabric and handles the interdependencies of the
discrete components.
So, there are three "new" concepts:
1. Sticking a mux in front of an AD-converter. That's not all that
novel, nor complex. Quite the opposite, I'd say. In fact, I find it
a bit amazing that there is no in-kernel support for it.
As ever first person who needs it and has the skills to write it gets to do it ;)
Congratulations Peter ;)
2. Reusing the same GPIO-pins to drive different muxes. There are
obviously chips that work this way (as Jonathan pointed out) and
these will at some point get used in Linux devices. I guess they
already are used, but that people handle them in userspace. Or
something? If this is complex, which I question, it will still need
support at some point. At least that's what I believe.
3. Using the same GPIO pins to mux things handled by different
subsystems. Right, this is a bit crazy, and I'd rather not have this
requirement, but this HW is what it is so I'll need to handle it in
some way. It is also what stops me from falling back to a userspace
solution, which is probably connected to why #1 and #2 is not supported
by the kernel; everybody probably does muxing in userspace. Which is
not necessarily a good idea, nor how it's supposed to be done...
So, the only thing that's out of the ordinary (as I see it), is #3.
The question that then needs an answer is how the in-kernel solution
for #1 and #2 would look if we do not consider #3.
And I claim that the desired solution to #1 and #2 is pretty close
to my proposal.
A. You do not want mux-controller drivers in every subsystem that
needs them.
Agreed.
B. You do not want every mux-consumer to know the specifics of how to
operate every kind of mux; there are muxes that are not controlled
with GPIO pins...
C. When you implement muxing in a subsystem, there will in some cases
be a need to handle parallel muxing, where there is a need to share
mux-controllers.
It just feels right to separate out the mux-controller and refer to
it from where a mux is needed. It solves #1 and #2. And, of course,
as a bonus #3 is also solved. But my bias is obvious.
And that leads us to the restrictions with the bindings. And the same
thing happens; the solution for #2 also solves #3.
So how do you refer to a mux-controller from where it's needed? My
first proposal used a dt phandle, for the second round I put them in
the parent node. It would be super if it was possible for the mux-
consumer to create the mux-controller device from the same dt
node that is already bound to the mux-consumer. The problem is that
the mux-consumer should not hard-code which mux-controller device it
should create. The best I can think of is some kind of 'mux-compatible'
attribute, that works like the standard 'compatible' attribute. That
would simplify the bindings for the normal case (#1) where the mux-
controller isn't shared (#2 and #3). Maybe it's possible to fix this
issue somehow? I simply don't know?
As Lars stated, it's marginal. The question is not at what point do we
'have to' bother with a fabric driver, but rather at what point does it
make a our lives easier.
Take you nastiest mux case described earlier.
The ideal would be to represent the ADC and 3 muxes as (approximately) a
single ADC to userspace that just happens to have somewhere near 23 inputs.
To do that in device tree we need to describe
1 The adc
2 The three muxes
3 The software representation to pull all of these back into a single device.
That last part to my mind trips the balance to the point where a fabric driver
would make sense. It's not complex. Just a few lines of code tying all the
elements together without ending up with a fairly fiendish setup to describe in
device tree.
Also just wait until we have muxes stacked on muxes, with cross overs occuring.
Some of the ASoC parts can actually have effective loops if you try all the mux
combinations.
So question is do we have a 'simple case description' in device tree or force
fabric drivers everywhere? I think I'm in favour of the simple case - which handles
one of your two uses nicely. The second one to do the the recombining of channels after
the muxes, ends up looking to me like it needs a fabric driver.
Note we are only talking about bindings vs code based description here. I agree
entirely with the concept of a generic mux subsystem.
Jonathan
From: Peter Rosin <hidden> Date: 2016-11-29 16:03:07
On 2016-11-27 13:00, Jonathan Cameron wrote:
On 23/11/16 11:47, Peter Rosin wrote:
quoted
On 2016-11-22 21:58, Lars-Peter Clausen wrote:
quoted
On 11/21/2016 02:17 PM, Peter Rosin wrote:
[...]
quoted
I have a piece of hardware that is using the same 3 GPIO pins
to control four 8-way muxes. Three of them control ADC lines
to an ADS1015 chip with an iio driver, and the last one
controls the SDA line of an i2c bus. We have some deployed
code to handle this, but you do not want to see it or ever
hear about it. I'm not sure why I even mention it. Anyway,
the situation has nagged me to no end for quite some time.
So, after first getting more intimate with the i2c muxing code
and later discovering the drivers/iio/inkern.c file and
writing a couple of drivers making use of it, I came up with
what I think is an acceptable solution; add a generic mux
controller driver (and subsystem) that is shared between all
instances, and combine that with an iio mux driver and a new
generic i2c mux driver. The new i2c mux I called "simple"
since it is only hooking the i2c muxing and the new mux
controller (much like the alsa simple card driver does for ASoC).
While abstracting this properly is all nice and good and the way it should
be done, but it also adds a lot of complexity and the devicetree adds a lot
of restrictions on what can actually be represented.
This is a characterization without any specifics. But is the
characterization true? You have two complaints, complexity
and restrictions with bindings.
quoted
There is a certain point where the fabric on a PCB becomes so complex that
it deserves to be a device on its own (like the audio fabric drivers).
Especially when the hardware is built with a certain application in mind and
the driver is supposed to impose policy which reflects this application. The
latter can often not properly be described with the primitives the
devicetree can offer.
And I think your setup is very borderline what can be done in a declarative
way only and it adds a lot of complexity over a more imperative solution in
form of a driver. I think it is worth investigating about having a driver
that is specific to your fabric and handles the interdependencies of the
discrete components.
So, there are three "new" concepts:
1. Sticking a mux in front of an AD-converter. That's not all that
novel, nor complex. Quite the opposite, I'd say. In fact, I find it
a bit amazing that there is no in-kernel support for it.
As ever first person who needs it and has the skills to write it gets to do it ;)
Congratulations Peter ;)
quoted
2. Reusing the same GPIO-pins to drive different muxes. There are
obviously chips that work this way (as Jonathan pointed out) and
these will at some point get used in Linux devices. I guess they
already are used, but that people handle them in userspace. Or
something? If this is complex, which I question, it will still need
support at some point. At least that's what I believe.
3. Using the same GPIO pins to mux things handled by different
subsystems. Right, this is a bit crazy, and I'd rather not have this
requirement, but this HW is what it is so I'll need to handle it in
some way. It is also what stops me from falling back to a userspace
solution, which is probably connected to why #1 and #2 is not supported
by the kernel; everybody probably does muxing in userspace. Which is
not necessarily a good idea, nor how it's supposed to be done...
So, the only thing that's out of the ordinary (as I see it), is #3.
The question that then needs an answer is how the in-kernel solution
for #1 and #2 would look if we do not consider #3.
And I claim that the desired solution to #1 and #2 is pretty close
to my proposal.
A. You do not want mux-controller drivers in every subsystem that
needs them.
Agreed.
quoted
B. You do not want every mux-consumer to know the specifics of how to
operate every kind of mux; there are muxes that are not controlled
with GPIO pins...
C. When you implement muxing in a subsystem, there will in some cases
be a need to handle parallel muxing, where there is a need to share
mux-controllers.
It just feels right to separate out the mux-controller and refer to
it from where a mux is needed. It solves #1 and #2. And, of course,
as a bonus #3 is also solved. But my bias is obvious.
And that leads us to the restrictions with the bindings. And the same
thing happens; the solution for #2 also solves #3.
So how do you refer to a mux-controller from where it's needed? My
first proposal used a dt phandle, for the second round I put them in
the parent node. It would be super if it was possible for the mux-
consumer to create the mux-controller device from the same dt
node that is already bound to the mux-consumer. The problem is that
the mux-consumer should not hard-code which mux-controller device it
should create. The best I can think of is some kind of 'mux-compatible'
attribute, that works like the standard 'compatible' attribute. That
would simplify the bindings for the normal case (#1) where the mux-
controller isn't shared (#2 and #3). Maybe it's possible to fix this
issue somehow? I simply don't know?
As Lars stated, it's marginal. The question is not at what point do we
'have to' bother with a fabric driver, but rather at what point does it
make a our lives easier.
Take you nastiest mux case described earlier.
The ideal would be to represent the ADC and 3 muxes as (approximately) a
single ADC to userspace that just happens to have somewhere near 23 inputs.
To do that in device tree we need to describe
1 The adc
2 The three muxes
3 The software representation to pull all of these back into a single device.
That last part to my mind trips the balance to the point where a fabric driver
would make sense. It's not complex. Just a few lines of code tying all the
elements together without ending up with a fairly fiendish setup to describe in
device tree.
Also just wait until we have muxes stacked on muxes, with cross overs occuring.
Some of the ASoC parts can actually have effective loops if you try all the mux
combinations.
So question is do we have a 'simple case description' in device tree or force
fabric drivers everywhere? I think I'm in favour of the simple case - which handles
one of your two uses nicely. The second one to do the the recombining of channels after
the muxes, ends up looking to me like it needs a fabric driver.
Note we are only talking about bindings vs code based description here. I agree
entirely with the concept of a generic mux subsystem.
Ok, take the simple case - an adc with a mux in front of it.
We do not want to build a whole new iio-mux subsystem like the one under
i2c, so from iio we want to refer to the actual mux controller driver
which lives under the mux subsystem. Getting this far is what solves my
"second need" [2] from the v2 thread.
Agreed, doing this w/o a fabric driver spills the guts and it might be
cleaner to build a fabric driver that takes a reference to the dpot and
the mux controller and just knows the rest. In the end this fabric driver
requires two things to actually make a difference; some way to instantiate
drivers without the help of device-tree and some way make those drivers
only provide in-kernel access (and preferably it should hide the dpot from
userspace too, while at it).
But doing all that in a fabric driver is not going to change the fact that
the iio-mux driver is useful on its own. I bet someone else is going to
reuse it somewhere down the line. Which means that a fabric driver would
perhaps be nice for my "second need", but not critical, it works pretty
well to just piggyback on the general solution .
Over to my "first need" [1] from the v2 thread.
The above iio-mux driver handles the three muxed adc lines beautifully,
just refer all three iio-muxes to the same mux controller. Agreed, with
a fabric driver I could get one device with 25 channels instead of three
devices with 8 channels each plus one unmuxed line directly from the adc
device. However, that doesn't bother me at all, I may even think it is
preferable because otherwise I'd have to come up with some way to
identify which channel is which in that big 25-channel jungle. Another
drawback with having a fabric driver here is that it would need to be an
i2c-mux driver, because one of the key points of doing a fabric driver
for my first need was to hide the shared mux, right? Instead, I have the
new i2c-mux-simple driver that builds an i2c-mux using any mux controller
from the mux subsystem (something that may prove useful to others in the
future), which in my case is the same mux controller that also muxes the
three adc lines.
In short, doing fabric drivers buys me almost nothing besides having a
slightly more distinct device tree. All the components used to describe
this are either already accepted drivers or usable by others (at least
the way I see it).
Cheers,
Peter
[1] three adc lines and an i2c bus muxed with the same gpio pins
[2] mcp4561 dpot -> dpot-dac -> envelope-detector-adc -> iio-mux
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Jonathan Cameron <jic23@kernel.org> Date: 2016-12-03 14:42:17
On 29/11/16 16:02, Peter Rosin wrote:
On 2016-11-27 13:00, Jonathan Cameron wrote:
quoted
On 23/11/16 11:47, Peter Rosin wrote:
quoted
On 2016-11-22 21:58, Lars-Peter Clausen wrote:
quoted
On 11/21/2016 02:17 PM, Peter Rosin wrote:
[...]
quoted
I have a piece of hardware that is using the same 3 GPIO pins
to control four 8-way muxes. Three of them control ADC lines
to an ADS1015 chip with an iio driver, and the last one
controls the SDA line of an i2c bus. We have some deployed
code to handle this, but you do not want to see it or ever
hear about it. I'm not sure why I even mention it. Anyway,
the situation has nagged me to no end for quite some time.
So, after first getting more intimate with the i2c muxing code
and later discovering the drivers/iio/inkern.c file and
writing a couple of drivers making use of it, I came up with
what I think is an acceptable solution; add a generic mux
controller driver (and subsystem) that is shared between all
instances, and combine that with an iio mux driver and a new
generic i2c mux driver. The new i2c mux I called "simple"
since it is only hooking the i2c muxing and the new mux
controller (much like the alsa simple card driver does for ASoC).
While abstracting this properly is all nice and good and the way it should
be done, but it also adds a lot of complexity and the devicetree adds a lot
of restrictions on what can actually be represented.
This is a characterization without any specifics. But is the
characterization true? You have two complaints, complexity
and restrictions with bindings.
quoted
There is a certain point where the fabric on a PCB becomes so complex that
it deserves to be a device on its own (like the audio fabric drivers).
Especially when the hardware is built with a certain application in mind and
the driver is supposed to impose policy which reflects this application. The
latter can often not properly be described with the primitives the
devicetree can offer.
And I think your setup is very borderline what can be done in a declarative
way only and it adds a lot of complexity over a more imperative solution in
form of a driver. I think it is worth investigating about having a driver
that is specific to your fabric and handles the interdependencies of the
discrete components.
So, there are three "new" concepts:
1. Sticking a mux in front of an AD-converter. That's not all that
novel, nor complex. Quite the opposite, I'd say. In fact, I find it
a bit amazing that there is no in-kernel support for it.
As ever first person who needs it and has the skills to write it gets to do it ;)
Congratulations Peter ;)
quoted
2. Reusing the same GPIO-pins to drive different muxes. There are
obviously chips that work this way (as Jonathan pointed out) and
these will at some point get used in Linux devices. I guess they
already are used, but that people handle them in userspace. Or
something? If this is complex, which I question, it will still need
support at some point. At least that's what I believe.
3. Using the same GPIO pins to mux things handled by different
subsystems. Right, this is a bit crazy, and I'd rather not have this
requirement, but this HW is what it is so I'll need to handle it in
some way. It is also what stops me from falling back to a userspace
solution, which is probably connected to why #1 and #2 is not supported
by the kernel; everybody probably does muxing in userspace. Which is
not necessarily a good idea, nor how it's supposed to be done...
So, the only thing that's out of the ordinary (as I see it), is #3.
The question that then needs an answer is how the in-kernel solution
for #1 and #2 would look if we do not consider #3.
And I claim that the desired solution to #1 and #2 is pretty close
to my proposal.
A. You do not want mux-controller drivers in every subsystem that
needs them.
Agreed.
quoted
B. You do not want every mux-consumer to know the specifics of how to
operate every kind of mux; there are muxes that are not controlled
with GPIO pins...
C. When you implement muxing in a subsystem, there will in some cases
be a need to handle parallel muxing, where there is a need to share
mux-controllers.
It just feels right to separate out the mux-controller and refer to
it from where a mux is needed. It solves #1 and #2. And, of course,
as a bonus #3 is also solved. But my bias is obvious.
And that leads us to the restrictions with the bindings. And the same
thing happens; the solution for #2 also solves #3.
So how do you refer to a mux-controller from where it's needed? My
first proposal used a dt phandle, for the second round I put them in
the parent node. It would be super if it was possible for the mux-
consumer to create the mux-controller device from the same dt
node that is already bound to the mux-consumer. The problem is that
the mux-consumer should not hard-code which mux-controller device it
should create. The best I can think of is some kind of 'mux-compatible'
attribute, that works like the standard 'compatible' attribute. That
would simplify the bindings for the normal case (#1) where the mux-
controller isn't shared (#2 and #3). Maybe it's possible to fix this
issue somehow? I simply don't know?
As Lars stated, it's marginal. The question is not at what point do we
'have to' bother with a fabric driver, but rather at what point does it
make a our lives easier.
Take you nastiest mux case described earlier.
The ideal would be to represent the ADC and 3 muxes as (approximately) a
single ADC to userspace that just happens to have somewhere near 23 inputs.
To do that in device tree we need to describe
1 The adc
2 The three muxes
3 The software representation to pull all of these back into a single device.
That last part to my mind trips the balance to the point where a fabric driver
would make sense. It's not complex. Just a few lines of code tying all the
elements together without ending up with a fairly fiendish setup to describe in
device tree.
Also just wait until we have muxes stacked on muxes, with cross overs occuring.
Some of the ASoC parts can actually have effective loops if you try all the mux
combinations.
So question is do we have a 'simple case description' in device tree or force
fabric drivers everywhere? I think I'm in favour of the simple case - which handles
one of your two uses nicely. The second one to do the the recombining of channels after
the muxes, ends up looking to me like it needs a fabric driver.
Note we are only talking about bindings vs code based description here. I agree
entirely with the concept of a generic mux subsystem.
Ok, take the simple case - an adc with a mux in front of it.
We do not want to build a whole new iio-mux subsystem like the one under
i2c, so from iio we want to refer to the actual mux controller driver
which lives under the mux subsystem. Getting this far is what solves my
"second need" [2] from the v2 thread.
Agreed, doing this w/o a fabric driver spills the guts and it might be
cleaner to build a fabric driver that takes a reference to the dpot and
the mux controller and just knows the rest. In the end this fabric driver
requires two things to actually make a difference; some way to instantiate
drivers without the help of device-tree and some way make those drivers
only provide in-kernel access (and preferably it should hide the dpot from
userspace too, while at it).
But doing all that in a fabric driver is not going to change the fact that
the iio-mux driver is useful on its own. I bet someone else is going to
reuse it somewhere down the line. Which means that a fabric driver would
perhaps be nice for my "second need", but not critical, it works pretty
well to just piggyback on the general solution .
Absolutely. No denying the usefulness of having a nice little mux subsystem
with the resulting iio-mux driver.
Over to my "first need" [1] from the v2 thread.
The above iio-mux driver handles the three muxed adc lines beautifully,
just refer all three iio-muxes to the same mux controller. Agreed, with
a fabric driver I could get one device with 25 channels instead of three
devices with 8 channels each plus one unmuxed line directly from the adc
device. However, that doesn't bother me at all, I may even think it is
preferable because otherwise I'd have to come up with some way to
identify which channel is which in that big 25-channel jungle. Another
drawback with having a fabric driver here is that it would need to be an
i2c-mux driver, because one of the key points of doing a fabric driver
for my first need was to hide the shared mux, right? Instead, I have the
new i2c-mux-simple driver that builds an i2c-mux using any mux controller
from the mux subsystem (something that may prove useful to others in the
future), which in my case is the same mux controller that also muxes the
three adc lines.
In short, doing fabric drivers buys me almost nothing besides having a
slightly more distinct device tree. All the components used to describe
this are either already accepted drivers or usable by others (at least
the way I see it).
Fair enough. Perhaps it's not worthwhile in this case, but lets keep
the concept in mind as we move forward and see if anyone else has
more complex hardware than you do ;)
(there is always something out there!)
Cheers,
Peter
[1] three adc lines and an i2c bus muxed with the same gpio pins
[2] mcp4561 dpot -> dpot-dac -> envelope-detector-adc -> iio-mux