* [PATCH 1/1] i2c: designware: Consolidate default functionality bits
From: Alexander Stein @ 2016-11-21 10:43 UTC (permalink / raw)
To: Jarkko Nikula, Andy Shevchenko, Mika Westerberg, Wolfram Sang
Cc: Alexander Stein, linux-i2c
Use a common place for default functionality bits for both platform
and pci driver.
Signed-off-by: Alexander Stein <alexander.stein@systec-electronic.com>
---
This patch requires commit c3ae106050b9 ("i2c: designware: Implement support
for SMBus block read and write") which is included in current i2c/for-next
branch.
BTW: Do merrifield and medfield actually not support 10bit addressing?
drivers/i2c/busses/i2c-designware-core.h | 8 ++++++++
drivers/i2c/busses/i2c-designware-pcidrv.c | 9 +--------
drivers/i2c/busses/i2c-designware-platdrv.c | 9 +--------
3 files changed, 10 insertions(+), 16 deletions(-)
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 0d44d2a..26250b4 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -22,6 +22,14 @@
*
*/
+#include <linux/i2c.h>
+
+#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
+ I2C_FUNC_SMBUS_BYTE | \
+ I2C_FUNC_SMBUS_BYTE_DATA | \
+ I2C_FUNC_SMBUS_WORD_DATA | \
+ I2C_FUNC_SMBUS_BLOCK_DATA | \
+ I2C_FUNC_SMBUS_I2C_BLOCK)
#define DW_IC_CON_MASTER 0x1
#define DW_IC_CON_SPEED_STD 0x2
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index 8ffe2da..300802e7 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -71,13 +71,6 @@ struct dw_pci_controller {
DW_IC_CON_SLAVE_DISABLE | \
DW_IC_CON_RESTART_EN)
-#define DW_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
- I2C_FUNC_SMBUS_BYTE | \
- I2C_FUNC_SMBUS_BYTE_DATA | \
- I2C_FUNC_SMBUS_WORD_DATA | \
- I2C_FUNC_SMBUS_BLOCK_DATA | \
- I2C_FUNC_SMBUS_I2C_BLOCK)
-
/* Merrifield HCNT/LCNT/SDA hold time */
static struct dw_scl_sda_cfg mrfld_config = {
.ss_hcnt = 0x2f8,
@@ -250,7 +243,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
}
dev->functionality = controller->functionality |
- DW_DEFAULT_FUNCTIONALITY;
+ DW_IC_DEFAULT_FUNCTIONALITY;
dev->master_cfg = controller->bus_cfg;
if (controller->scl_sda_cfg) {
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 7429dfa..08153ea 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -219,14 +219,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
if (r)
return r;
- dev->functionality =
- I2C_FUNC_I2C |
- I2C_FUNC_10BIT_ADDR |
- I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_BLOCK_DATA |
- I2C_FUNC_SMBUS_I2C_BLOCK;
+ dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
DW_IC_CON_RESTART_EN;
--
2.7.3
^ permalink raw reply related
* Re: [BUG] i2c-designware silently fails on long transfers
From: Russell King - ARM Linux @ 2016-11-21 10:43 UTC (permalink / raw)
To: Mika Westerberg
Cc: Andrew Jackson, Liviu Dudau, Wolfram Sang, Jarkko Nikula,
Andy Shevchenko, linux-i2c, linux-arm-kernel
In-Reply-To: <20161121102901.GF1446@lahna.fi.intel.com>
On Mon, Nov 21, 2016 at 12:29:01PM +0200, Mika Westerberg wrote:
> On Fri, Nov 18, 2016 at 07:35:42PM +0000, Russell King - ARM Linux wrote:
> > Another mitigation would be to lower the I2C bus frequency on Juno from
> > 400kHz to 100kHz, so that there's 4x longer IRQ latency possible.
> > However, even that isn't going to be reliable - even going to 100kHz
> > isn't going to allow the above case to be solved - the interrupt is
> > delayed by around 2ms, and it takes about 1.4ms to send/receive 16 bytes
> > at 100kHz. (9 * 16 / (100*10^3)).
> >
> > So, I think all hope is lost for i2c-designware on Juno to cope with
> > reading the EDID from TDA998x reliably.
>
> :-(
>
> I wonder if we can get it work more reliably by using DMA (provided that
> there are DMA channels available for I2C in Juno)? That would allow the
> hardware to perform longer reads without relying on how fast the
> interrupt handler is able to empty the Rx FIFO.
It would need to DMA to the Tx FIFO to keep it filled - it triggers the
stop condition when the Tx FIFO empties. From what I can see in the
driver, the Tx FIFO not only takes the data but also a "command" to tell
the hardware what to do.
The Rx FIFO would also need DMA to avoid it overflowing due to high
interrupt latency.
I don't know what state DMA is in on the Juno, or even whether it has
DMA - it has a PL330 DMA controller, but I see nothing in the DT files
making use of it.
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* Re: [PATCH 2/2] i2c: designware: fix rx fifo depth tracking
From: Mika Westerberg @ 2016-11-21 10:40 UTC (permalink / raw)
To: Russell King
Cc: Andrew Jackson, Liviu Dudau, Wolfram Sang, Jarkko Nikula,
Andy Shevchenko, linux-i2c, linux-arm-kernel
In-Reply-To: <E1c7p18-0000R6-4w@rmk-PC.armlinux.org.uk>
On Fri, Nov 18, 2016 at 07:40:10PM +0000, Russell King wrote:
> When loading the TX fifo to receive bytes on the I2C bus, we incorrectly
> count the number of bytes:
>
> rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR);
>
> while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
> if (rx_limit - dev->rx_outstanding <= 0)
> break;
> rx_limit--;
> dev->rx_outstanding++;
> }
>
> DW_IC_RXFLR indicates how many bytes are available to be read in the
> FIFO, dev->rx_fifo_depth is the FIFO size, and dev->rx_outstanding is
> the number of bytes that we've requested to be read so far, but which
> have not been read.
>
> Firstly, increasing dev->rx_outstanding and decreasing rx_limit and then
> comparing them results in each byte consuming "two" bytes in this
> tracking, so this is obviously wrong.
>
> Secondly, the number of bytes that _could_ be received into the FIFO at
> any time is the number of bytes we have so far requested but not yet
> read from the FIFO - in other words dev->rx_outstanding.
>
> So, in order to request enough bytes to fill the RX FIFO, we need to
> request dev->rx_fifo_depth - dev->rx_outstanding bytes.
>
> Modifying the code thusly results in us reaching the maximum number of
> bytes outstanding each time we queue more "receive" operations, provided
> the transfer allows that to happen.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
^ permalink raw reply
* Re: [PATCH v5 6/6] i2c: use an IRQ to report Host Notify events, not alert
From: Benjamin Tissoires @ 2016-11-21 10:52 UTC (permalink / raw)
To: Wolfram Sang; +Cc: Dmitry Torokhov, linux-i2c, linux-kernel, Jean Delvare
In-Reply-To: <20161107002034.GB1442@katana>
Hi Wolfram,
On Nov 07 2016 or thereabouts, Wolfram Sang wrote:
> On Thu, Oct 13, 2016 at 02:10:40PM +0200, Benjamin Tissoires wrote:
> > The current SMBus Host Notify implementation relies on .alert() to
> > relay its notifications. However, the use cases where SMBus Host
> > Notify is needed currently is to signal data ready on touchpads.
> >
> > This is closer to an IRQ than a custom API through .alert().
> > Given that the 2 touchpad manufacturers (Synaptics and Elan) that
> > use SMBus Host Notify don't put any data in the SMBus payload, the
> > concept actually matches one to one.
>
> I see the advantages. The only question I have: What if we encounter
> devices in the future which do put data in the payload? Can this
> mechanism be extended to handle that?
I guess I haven't convinced you with my answer. Is there anything I can
do to get this series in v4.10 or do you prefer waiting for v4.11?
Cheers,
Benjamin
>
> >
> > Benefits are multiple:
> > - simpler code and API: the client will just have an IRQ, and
> > nothing needs to be added in the adapter beside internally
> > enabling it.
> > - no more specific workqueue, the threading is handled by IRQ core
> > directly (when required)
> > - no more races when removing the device (the drivers are already
> > required to disable irq on remove)
> > - simpler handling for drivers: use plain regular IRQs
> > - no more dependency on i2c-smbus for i2c-i801 (and any other adapter)
> > - the IRQ domain is created automatically when the adapter exports
> > the Host Notify capability
> > - the IRQ are assign only if ACPI, OF and the caller did not assign
> > one already
> > - the domain is automatically destroyed on remove
> > - fewer lines of code (minus 20, yeah!)
> >
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>
> Thanks for keeping at it!
>
^ permalink raw reply
* Re: [BUG] i2c-designware silently fails on long transfers
From: Mika Westerberg @ 2016-11-21 11:09 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Andrew Jackson, Liviu Dudau, Wolfram Sang, Jarkko Nikula,
Andy Shevchenko, linux-i2c, linux-arm-kernel
In-Reply-To: <20161121104329.GB1041@n2100.armlinux.org.uk>
On Mon, Nov 21, 2016 at 10:43:29AM +0000, Russell King - ARM Linux wrote:
> On Mon, Nov 21, 2016 at 12:29:01PM +0200, Mika Westerberg wrote:
> > On Fri, Nov 18, 2016 at 07:35:42PM +0000, Russell King - ARM Linux wrote:
> > > Another mitigation would be to lower the I2C bus frequency on Juno from
> > > 400kHz to 100kHz, so that there's 4x longer IRQ latency possible.
> > > However, even that isn't going to be reliable - even going to 100kHz
> > > isn't going to allow the above case to be solved - the interrupt is
> > > delayed by around 2ms, and it takes about 1.4ms to send/receive 16 bytes
> > > at 100kHz. (9 * 16 / (100*10^3)).
> > >
> > > So, I think all hope is lost for i2c-designware on Juno to cope with
> > > reading the EDID from TDA998x reliably.
> >
> > :-(
> >
> > I wonder if we can get it work more reliably by using DMA (provided that
> > there are DMA channels available for I2C in Juno)? That would allow the
> > hardware to perform longer reads without relying on how fast the
> > interrupt handler is able to empty the Rx FIFO.
>
> It would need to DMA to the Tx FIFO to keep it filled - it triggers the
> stop condition when the Tx FIFO empties. From what I can see in the
> driver, the Tx FIFO not only takes the data but also a "command" to tell
> the hardware what to do.
Yes, the command is either "read" or "write" (meaning even for Rx a
write to the Tx FIFO is needed).
> The Rx FIFO would also need DMA to avoid it overflowing due to high
> interrupt latency.
I guess for Rx we would need to supply a dummy buffer of "read" commands
for the Tx FIFO in addition to reading bytes from Rx FIFO. But still
that might help to improve reliability in case of Juno.
> I don't know what state DMA is in on the Juno, or even whether it has
> DMA - it has a PL330 DMA controller, but I see nothing in the DT files
> making use of it.
OK
^ permalink raw reply
* Re: [BUG] i2c-designware silently fails on long transfers
From: Liviu Dudau @ 2016-11-21 11:21 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Mika Westerberg, Andrew Jackson, Wolfram Sang, Jarkko Nikula,
Andy Shevchenko, linux-i2c, linux-arm-kernel
In-Reply-To: <20161121104329.GB1041@n2100.armlinux.org.uk>
On Mon, Nov 21, 2016 at 10:43:29AM +0000, Russell King - ARM Linux wrote:
> On Mon, Nov 21, 2016 at 12:29:01PM +0200, Mika Westerberg wrote:
> > On Fri, Nov 18, 2016 at 07:35:42PM +0000, Russell King - ARM Linux wrote:
> > > Another mitigation would be to lower the I2C bus frequency on Juno from
> > > 400kHz to 100kHz, so that there's 4x longer IRQ latency possible.
> > > However, even that isn't going to be reliable - even going to 100kHz
> > > isn't going to allow the above case to be solved - the interrupt is
> > > delayed by around 2ms, and it takes about 1.4ms to send/receive 16 bytes
> > > at 100kHz. (9 * 16 / (100*10^3)).
> > >
> > > So, I think all hope is lost for i2c-designware on Juno to cope with
> > > reading the EDID from TDA998x reliably.
> >
> > :-(
> >
> > I wonder if we can get it work more reliably by using DMA (provided that
> > there are DMA channels available for I2C in Juno)? That would allow the
> > hardware to perform longer reads without relying on how fast the
> > interrupt handler is able to empty the Rx FIFO.
>
> It would need to DMA to the Tx FIFO to keep it filled - it triggers the
> stop condition when the Tx FIFO empties. From what I can see in the
> driver, the Tx FIFO not only takes the data but also a "command" to tell
> the hardware what to do.
>
> The Rx FIFO would also need DMA to avoid it overflowing due to high
> interrupt latency.
>
> I don't know what state DMA is in on the Juno, or even whether it has
> DMA - it has a PL330 DMA controller, but I see nothing in the DT files
> making use of it.
The only thing we have tested PL330 with was UART and I2S. I'm not sure we
can really use it with I2C given the above hardware configuration issue.
The other thing we have tried in our private branches was to repeat the EDID
transfer a number of times until the checksum was correct. Andrew Jackson
sent a patch a year or so back to have some ridiculously high number of
retries (30) and that has been rightfully shut down in the upstream.
The unfortunate thing is that the monitors and TVs that we use inside ARM
for testing don't seem to trigger this issue on a regular basis. We had one
or two small 7" TVs that did that, and the brute force retry of EDID transfer
sorted them out for the limited use that we had for them.
Best regards,
Liviu
>
> --
> RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
> FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
> according to speedtest.net.
--
====================
| I would like to |
| fix the world, |
| but they're not |
| giving me the |
\ source code! /
---------------
¯\_(ツ)_/¯
^ permalink raw reply
* Re: [BUG] i2c-designware silently fails on long transfers
From: Russell King - ARM Linux @ 2016-11-21 11:36 UTC (permalink / raw)
To: Liviu Dudau
Cc: Mika Westerberg, Andrew Jackson, Wolfram Sang, Jarkko Nikula,
Andy Shevchenko, linux-i2c, linux-arm-kernel
In-Reply-To: <20161121112135.GE1005@e106497-lin.cambridge.arm.com>
On Mon, Nov 21, 2016 at 11:21:35AM +0000, Liviu Dudau wrote:
> On Mon, Nov 21, 2016 at 10:43:29AM +0000, Russell King - ARM Linux wrote:
> > It would need to DMA to the Tx FIFO to keep it filled - it triggers the
> > stop condition when the Tx FIFO empties. From what I can see in the
> > driver, the Tx FIFO not only takes the data but also a "command" to tell
> > the hardware what to do.
> >
> > The Rx FIFO would also need DMA to avoid it overflowing due to high
> > interrupt latency.
> >
> > I don't know what state DMA is in on the Juno, or even whether it has
> > DMA - it has a PL330 DMA controller, but I see nothing in the DT files
> > making use of it.
>
> The only thing we have tested PL330 with was UART and I2S. I'm not sure we
> can really use it with I2C given the above hardware configuration issue.
>
> The other thing we have tried in our private branches was to repeat the EDID
> transfer a number of times until the checksum was correct. Andrew Jackson
> sent a patch a year or so back to have some ridiculously high number of
> retries (30) and that has been rightfully shut down in the upstream.
>
> The unfortunate thing is that the monitors and TVs that we use inside ARM
> for testing don't seem to trigger this issue on a regular basis. We had one
> or two small 7" TVs that did that, and the brute force retry of EDID transfer
> sorted them out for the limited use that we had for them.
Due to the way the TDA998x works, it doesn't have much to do with the TV.
The TDA998x is instructed to read up to 128 bytes of EDID data into its
own internal buffer. It then does so, and raises an interrupt (or we
notice that the interrupt flag is set when there's no hardware interrupt
line), and we then read the EDID data out of the TDA998x.
At the point we're reading from the TDA998x, it has loaded the data from
the DDC bus and the DDC bus is idle... we don't have direct access to
the DDC bus.
The only reason I can think that the checksum would pass is if you were
somehow ending up with data in the buffer that did cause the basic
checksum to pass, even though you had an incomplete read.
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* Re: [BUG] i2c-designware silently fails on long transfers
From: Robin Murphy @ 2016-11-21 12:11 UTC (permalink / raw)
To: Russell King - ARM Linux, Mika Westerberg
Cc: Wolfram Sang, Andrew Jackson, Liviu Dudau, Jarkko Nikula,
linux-i2c, Andy Shevchenko, linux-arm-kernel
In-Reply-To: <20161121104329.GB1041@n2100.armlinux.org.uk>
On 21/11/16 10:43, Russell King - ARM Linux wrote:
> On Mon, Nov 21, 2016 at 12:29:01PM +0200, Mika Westerberg wrote:
>> On Fri, Nov 18, 2016 at 07:35:42PM +0000, Russell King - ARM Linux wrote:
>>> Another mitigation would be to lower the I2C bus frequency on Juno from
>>> 400kHz to 100kHz, so that there's 4x longer IRQ latency possible.
>>> However, even that isn't going to be reliable - even going to 100kHz
>>> isn't going to allow the above case to be solved - the interrupt is
>>> delayed by around 2ms, and it takes about 1.4ms to send/receive 16 bytes
>>> at 100kHz. (9 * 16 / (100*10^3)).
>>>
>>> So, I think all hope is lost for i2c-designware on Juno to cope with
>>> reading the EDID from TDA998x reliably.
>>
>> :-(
>>
>> I wonder if we can get it work more reliably by using DMA (provided that
>> there are DMA channels available for I2C in Juno)? That would allow the
>> hardware to perform longer reads without relying on how fast the
>> interrupt handler is able to empty the Rx FIFO.
>
> It would need to DMA to the Tx FIFO to keep it filled - it triggers the
> stop condition when the Tx FIFO empties. From what I can see in the
> driver, the Tx FIFO not only takes the data but also a "command" to tell
> the hardware what to do.
>
> The Rx FIFO would also need DMA to avoid it overflowing due to high
> interrupt latency.
>
> I don't know what state DMA is in on the Juno, or even whether it has
> DMA - it has a PL330 DMA controller, but I see nothing in the DT files
> making use of it.
There's no DMA request wired up for I2C, unfortunately - only for the
UARTs, I2S, FPFGA tile and trace unit. I recall Liviu did have a patch
to lower the I2C clock as the mitigation you mention, which I thought
had made it into mainline, but apparently not.
Robin.
^ permalink raw reply
* Re: [RFC PATCH v2 2/7] misc: minimal mux subsystem and gpio-based mux controller
From: Peter Rosin @ 2016-11-21 13:03 UTC (permalink / raw)
To: Jonathan Cameron, linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald-Stadler, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <7fad4fd7-fffe-5214-b25f-dca923f8c466-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On 2016-11-19 16:34, Jonathan Cameron wrote:
> On 17/11/16 21:48, Peter Rosin wrote:
>> When both the iio subsystem and the i2c subsystem wants to update
>> the same mux, there needs to be some coordination. Invent a new
>> minimal "mux" subsystem that handles this.
> I'd probably put something more general in the description. Lots of things
> may need the same infrastructure. This is just an example.
I'll make it more general for the next round.
> Few bits inline.
>
> Also, I suspect you will fairly rapidly have a need for a strobe signal
> as well. A lot of mux chips that are more than 2 way seem to have them to
> allow multiple chips to be synchronized.
I think that can be easily added later, when/if it's needed.
>>
>> Add a single backend driver for this new subsystem that can
>> control gpio based multiplexers.
>> ---
>> drivers/misc/Kconfig | 6 +
>> drivers/misc/Makefile | 2 +
>> drivers/misc/mux-core.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++++
>> drivers/misc/mux-gpio.c | 115 +++++++++++++++++++
>> include/linux/mux.h | 53 +++++++++
>> 5 files changed, 475 insertions(+)
>> create mode 100644 drivers/misc/mux-core.c
>> create mode 100644 drivers/misc/mux-gpio.c
>> create mode 100644 include/linux/mux.h
>>
>> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
>> index 64971baf11fa..9e119bb78d82 100644
>> --- a/drivers/misc/Kconfig
>> +++ b/drivers/misc/Kconfig
>> @@ -766,6 +766,12 @@ config PANEL_BOOT_MESSAGE
>> An empty message will only clear the display at driver init time. Any other
>> printf()-formatted message is valid with newline and escape codes.
>>
>> +config MUX_GPIO
>> + tristate "GPIO-controlled MUX controller"
>> + depends on OF
>> + help
>> + GPIO-controlled MUX controller
>> +
>> source "drivers/misc/c2port/Kconfig"
>> source "drivers/misc/eeprom/Kconfig"
>> source "drivers/misc/cb710/Kconfig"
>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>> index 31983366090a..92b547bcbac1 100644
>> --- a/drivers/misc/Makefile
>> +++ b/drivers/misc/Makefile
>> @@ -53,6 +53,8 @@ obj-$(CONFIG_ECHO) += echo/
>> obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
>> obj-$(CONFIG_CXL_BASE) += cxl/
>> obj-$(CONFIG_PANEL) += panel.o
>> +obj-$(CONFIG_MUX_GPIO) += mux-core.o
>> +obj-$(CONFIG_MUX_GPIO) += mux-gpio.o
>>
>> lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
>> lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
>> diff --git a/drivers/misc/mux-core.c b/drivers/misc/mux-core.c
>> new file mode 100644
>> index 000000000000..7a8bf003a92c
>> --- /dev/null
>> +++ b/drivers/misc/mux-core.c
>> @@ -0,0 +1,299 @@
>> +/*
>> + * Multiplexer subsystem
>> + *
>> + * Copyright (C) 2016 Axentia Technologies AB
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#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/slab.h>
>> +
>> +static struct bus_type mux_bus_type = {
>> + .name = "mux",
>> +};
>> +
>> +static int __init mux_init(void)
>> +{
>> + return bus_register(&mux_bus_type);
>> +}
>> +
>> +static void __exit mux_exit(void)
>> +{
>> + bus_unregister(&mux_bus_type);
>> +}
>> +
>> +static DEFINE_IDA(mux_ida);
>> +
>> +static void mux_control_release(struct device *dev)
>> +{
>> + struct mux_control *mux = to_mux_control(dev);
>> +
>> + ida_simple_remove(&mux_ida, mux->id);
>> + kfree(mux);
>> +}
>> +
>> +static struct device_type mux_control_type = {
>> + .name = "mux-control",
>> + .release = mux_control_release,
>> +};
>> +
>> +/*
>> + * Allocate a mux-control, plus an extra memory area for private use
>> + * by the caller.
>> + */
>> +struct mux_control *mux_control_alloc(size_t sizeof_priv)
>> +{
>> + struct mux_control *mux;
>> +
> Worth planning ahead for spi controlled muxes and others that need their
> structures to be carefully aligned to avoid dma cacheline fun?
> Easy enough to add later I guess.
Right, I'll leave it for later when/if it's needed.
>> + mux = kzalloc(sizeof(*mux) + sizeof_priv, GFP_KERNEL);
>> + if (!mux)
>> + return NULL;
>> +
>> + 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->priv = mux + 1;
> Needed? Or just do it with a bit of pointer math where the access is needed?
Good suggestion, I'll add an inline function and drop the priv member.
>> +
>> + 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);
>> + return NULL;
>> + }
>> + dev_set_name(&mux->dev, "mux:control%d", mux->id);
>> +
>> + mux->cached_state = -1;
>> + mux->idle_state = -1;
>> +
>> + return mux;
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_alloc);
>> +
>> +/*
>> + * Register the mux-control, thus readying it for use.
> Either single line comment style - or perhaps kernel doc the lot...
Kernel doc was the plan from the start, coming up...
>> + */
>> +int mux_control_register(struct mux_control *mux)
>> +{
>> + /* 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;
>> +
>> + return device_add(&mux->dev);
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_register);
>> +
>> +/*
>> + * Take the mux-control off-line.
>> + */
>> +void mux_control_unregister(struct mux_control *mux)
>> +{
>> + device_del(&mux->dev);
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_unregister);
>> +
>> +/*
>> + * Put away the mux-control for good.
>> + */
>> +void mux_control_put(struct mux_control *mux)
>> +{
>> + if (!mux)
>> + return;
>> + put_device(&mux->dev);
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_put);
>> +
>> +static int mux_control_set(struct mux_control *mux, int state)
>> +{
>> + int ret = mux->ops->set(mux, state);
>> +
>> + mux->cached_state = ret < 0 ? -1 : state;
>> +
>> + return ret;
>> +}
>> +
>> +/*
>> + * Select the given multiplexer channel. Call mux_control_deselect()
>> + * when the operation is complete on the multiplexer channel, and the
>> + * multiplexer is free for others to use.
>> + */
>> +int mux_control_select(struct mux_control *mux, int state)
>> +{
>> + int ret;
>> +
>> + if (down_read_trylock(&mux->lock)) {
>> + if (mux->cached_state == state)
>> + return 0;
>> +
>> + /* Sigh, the mux needs updating... */
>> + up_read(&mux->lock);
>> + }
>> +
>> + /* ...or it's just contended. */
>> + down_write(&mux->lock);
>> +
>> + if (mux->cached_state == state) {
>> + /*
>> + * Hmmm, someone else changed the mux to my liking.
>> + * That makes me wonder how long I waited for nothing...
>> + */
>> + downgrade_write(&mux->lock);
>> + return 0;
>> + }
>> +
>> + 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);
>> + return ret;
>> + }
>> +
>> + downgrade_write(&mux->lock);
>> +
>> + return 1;
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_select);
>> +
>> +/*
>> + * Deselect the previously selected multiplexer channel.
>> + */
>> +int mux_control_deselect(struct mux_control *mux)
>> +{
>> + int ret = 0;
>> +
>> + if (mux->idle_state != -1 && mux->cached_state != mux->idle_state)
>> + ret = mux_control_set(mux, mux->idle_state);
>> +
>> + up_read(&mux->lock);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_deselect);
>> +
>> +static int of_dev_node_match(struct device *dev, void *data)
>> +{
>> + return dev->of_node == data;
>> +}
>> +
>> +static struct mux_control *of_find_mux_by_node(struct device_node *np)
>> +{
>> + struct device *dev;
>> +
>> + dev = bus_find_device(&mux_bus_type, NULL, np, of_dev_node_match);
>> +
>> + return dev ? to_mux_control(dev) : NULL;
>> +}
>> +
>> +static struct mux_control *of_mux_control_get(struct device_node *np, int index)
>> +{
>> + struct device_node *mux_np;
>> + struct mux_control *mux;
>> +
>> + mux_np = of_parse_phandle(np, "control-muxes", index);
>> + if (!mux_np)
>> + return NULL;
>> +
>> + mux = of_find_mux_by_node(mux_np);
>> + of_node_put(mux_np);
>> +
>> + return mux;
>> +}
>> +
>> +/*
>> + * Get a named mux.
>> + */
>> +struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
>> +{
>> + struct device_node *np = dev->of_node;
>> + struct mux_control *mux;
>> + int index;
>> +
>> + index = of_property_match_string(np, "control-mux-names", mux_name);
>> + if (index < 0) {
>> + dev_err(dev, "failed to get control-mux %s:%s(%i)\n",
>> + np->full_name, mux_name ?: "", index);
>> + return ERR_PTR(index);
>> + }
>> +
>> + mux = of_mux_control_get(np, index);
>> + if (!mux)
>> + return ERR_PTR(-EPROBE_DEFER);
>> +
>> + return mux;
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_get);
>> +
>> +static void devm_mux_control_free(struct device *dev, void *res)
>> +{
>> + struct mux_control *mux = *(struct mux_control **)res;
>> +
>> + mux_control_put(mux);
>> +}
>> +
>> +/*
>> + * Get a named mux, with resource management.
>> + */
> Guess it might be elsewhere in patch set but remember to add this to
> the global list of devm interfaces (in Documentation somewhere.. IIRC)
Right, now that you mention it, I remember having seen that document
at some point. I'll look it up.
Thanks for all comments!
Cheers,
Peter
>> +struct mux_control *devm_mux_control_get(struct device *dev,
>> + const char *mux_name)
>> +{
>> + struct mux_control **ptr, *mux;
>> +
>> + ptr = devres_alloc(devm_mux_control_free, sizeof(*ptr), GFP_KERNEL);
>> + if (!ptr)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + mux = mux_control_get(dev, mux_name);
>> + if (IS_ERR(mux)) {
>> + devres_free(ptr);
>> + return mux;
>> + }
>> +
>> + *ptr = mux;
>> + devres_add(dev, ptr);
>> +
>> + return mux;
>> +}
>> +EXPORT_SYMBOL_GPL(devm_mux_control_get);
>> +
>> +static int devm_mux_control_match(struct device *dev, void *res, void *data)
>> +{
>> + struct mux_control **r = res;
>> +
>> + if (!r || !*r) {
>> + WARN_ON(!r || !*r);
>> + return 0;
>> + }
>> +
>> + return *r == data;
>> +}
>> +
>> +/*
>> + * Resource-managed version mux_control_put.
>> + */
>> +void devm_mux_control_put(struct device *dev, struct mux_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-koto5C5qi+TLoDKTGw+V6w@public.gmane.org");
>> +MODULE_DESCRIPTION("MUX subsystem");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/misc/mux-gpio.c b/drivers/misc/mux-gpio.c
>> new file mode 100644
>> index 000000000000..2ddd7fb24078
>> --- /dev/null
>> +++ b/drivers/misc/mux-gpio.c
>> @@ -0,0 +1,115 @@
>> +/*
>> + * GPIO-controlled multiplexer driver
>> + *
>> + * Copyright (C) 2016 Axentia Technologies AB
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/module.h>
>> +#include <linux/mux.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +
>> +struct mux_gpio {
>> + struct gpio_descs *gpios;
>> +};
>> +
>> +static int mux_gpio_set(struct mux_control *mux, int val)
>> +{
>> + struct mux_gpio *mux_gpio = mux->priv;
>> + int i;
>> +
>> + for (i = 0; i < mux_gpio->gpios->ndescs; i++)
>> + gpiod_set_value_cansleep(mux_gpio->gpios->desc[i],
>> + val & (1 << i));
>> +
>> + return 0;
>> +}
>> +
>> +static const struct mux_control_ops mux_gpio_ops = {
>> + .set = mux_gpio_set,
>> +};
>> +
>> +static const struct of_device_id mux_gpio_dt_ids[] = {
>> + { .compatible = "mux-gpio", },
>> + { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids);
>> +
>> +static int mux_gpio_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct device_node *np = pdev->dev.of_node;
>> + struct mux_control *mux;
>> + struct mux_gpio *mux_gpio;
>> + u32 idle_state;
>> + int ret;
>> +
>> + if (!np)
>> + return -ENODEV;
>> +
>> + mux = mux_control_alloc(sizeof(*mux_gpio));
>> + if (!mux)
>> + return -ENOMEM;
>> + mux_gpio = mux->priv;
>> + mux->dev.parent = dev;
>> + mux->ops = &mux_gpio_ops;
>> +
>> + platform_set_drvdata(pdev, mux);
>> +
>> + mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
>> + if (IS_ERR(mux_gpio->gpios)) {
>> + if (PTR_ERR(mux_gpio->gpios) != -EPROBE_DEFER)
>> + dev_err(dev, "failed to get gpios\n");
>> + mux_control_put(mux);
>> + return PTR_ERR(mux_gpio->gpios);
>> + }
>> +
>> + ret = of_property_read_u32(np, "idle-state", &idle_state);
>> + if (ret >= 0) {
>> + if (idle_state >= (1 << mux_gpio->gpios->ndescs)) {
>> + dev_err(dev, "invalid idle-state %u\n", idle_state);
>> + return -EINVAL;
>> + }
>> + mux->idle_state = idle_state;
>> + }
>> +
>> + ret = mux_control_register(mux);
>> + if (ret < 0) {
>> + dev_err(dev, "failed to register mux_control\n");
>> + mux_control_put(mux);
>> + return ret;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int mux_gpio_remove(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct mux_control *mux = to_mux_control(dev);
>> +
>> + mux_control_unregister(mux);
>> + mux_control_put(mux);
>> + return 0;
>> +}
>> +
>> +static struct platform_driver mux_gpio_driver = {
>> + .driver = {
>> + .name = "mux-gpio",
>> + .of_match_table = of_match_ptr(mux_gpio_dt_ids),
>> + },
>> + .probe = mux_gpio_probe,
>> + .remove = mux_gpio_remove,
>> +};
>> +module_platform_driver(mux_gpio_driver);
>> +
>> +MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org");
>> +MODULE_DESCRIPTION("GPIO-controlled multiplexer driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/mux.h b/include/linux/mux.h
>> new file mode 100644
>> index 000000000000..5b21b8184056
>> --- /dev/null
>> +++ b/include/linux/mux.h
>> @@ -0,0 +1,53 @@
>> +/*
>> + * mux.h - definitions for the multiplexer interface
>> + *
>> + * Copyright (C) 2016 Axentia Technologies AB
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef _LINUX_MUX_H
>> +#define _LINUX_MUX_H
>> +
>> +#include <linux/device.h>
>> +#include <linux/rwsem.h>
>> +
>> +struct mux_control;
>> +
>> +struct mux_control_ops {
>> + int (*set)(struct mux_control *mux, int reg);
>> +};
>> +
>> +struct mux_control {
>> + struct rw_semaphore lock; /* protects the state of the mux */
>> +
>> + struct device dev;
>> + int id;
>> +
>> + int cached_state;
>> + int idle_state;
>> +
>> + const struct mux_control_ops *ops;
>> +
>> + void *priv;
>> +};
>> +
>> +#define to_mux_control(x) container_of((x), struct mux_control, dev)
>> +
>> +struct mux_control *mux_control_alloc(size_t sizeof_priv);
>> +int mux_control_register(struct mux_control *mux);
>> +void mux_control_unregister(struct mux_control *mux);
>> +void mux_control_put(struct mux_control *mux);
>> +
>> +int mux_control_select(struct mux_control *mux, int state);
>> +int mux_control_deselect(struct mux_control *mux);
>> +
>> +struct mux_control *mux_control_get(struct device *dev,
>> + const char *mux_name);
>> +struct mux_control *devm_mux_control_get(struct device *dev,
>> + const char *mux_name);
>> +void devm_mux_control_put(struct device *dev, struct mux_control *mux);
>> +
>> +#endif /* _LINUX_MUX_H */
>>
>
--
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
^ permalink raw reply
* [PATCH v3 1/7] dt-bindings: document devicetree bindings for mux-controllers and mux-gpio
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../devicetree/bindings/misc/mux-controller.txt | 76 +++++++++++++++++++++
.../devicetree/bindings/misc/mux-gpio.txt | 78 ++++++++++++++++++++++
MAINTAINERS | 5 ++
3 files changed, 159 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/mux-controller.txt
create mode 100644 Documentation/devicetree/bindings/misc/mux-gpio.txt
diff --git a/Documentation/devicetree/bindings/misc/mux-controller.txt b/Documentation/devicetree/bindings/misc/mux-controller.txt
new file mode 100644
index 000000000000..6946b5428546
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/mux-controller.txt
@@ -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 {
+ /* ... */
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/misc/mux-gpio.txt b/Documentation/devicetree/bindings/misc/mux-gpio.txt
new file mode 100644
index 000000000000..23b87913f4b3
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/mux-gpio.txt
@@ -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 {
+ /* ... */
+ };
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index ad9b965e5e44..7d797f087822 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8244,6 +8244,11 @@ S: Orphan
F: drivers/mmc/host/mmc_spi.c
F: include/linux/spi/mmc_spi.h
+MULTIPLEXER SUBSYSTEM
+M: Peter Rosin <peda@axentia.se>
+S: Maintained
+F: Documentation/devicetree/bindings/misc/mux-*
+
MULTISOUND SOUND DRIVER
M: Andrew Veliath <andrewtv@usa.net>
S: Maintained
--
2.1.4
^ permalink raw reply related
* [PATCH v3 2/7] misc: minimal mux subsystem and gpio-based mux controller
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
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 <peda@axentia.se>
---
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
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index 167070895498..947bfcfac9ae 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -330,7 +330,11 @@ MEM
devm_kzalloc()
MFD
- devm_mfd_add_devices()
+ devm_mfd_add_devices()
+
+MUX
+ devm_mux_control_get()
+ devm_mux_control_put()
PCI
pcim_enable_device() : after success, all PCI ops become managed
diff --git a/MAINTAINERS b/MAINTAINERS
index 7d797f087822..37974aeee750 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8248,6 +8248,8 @@ MULTIPLEXER SUBSYSTEM
M: Peter Rosin <peda@axentia.se>
S: Maintained
F: Documentation/devicetree/bindings/misc/mux-*
+F: include/linux/mux.h
+F: drivers/misc/mux-*
MULTISOUND SOUND DRIVER
M: Andrew Veliath <andrewtv@usa.net>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 64971baf11fa..a3ca79e082c7 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -766,6 +766,29 @@ config PANEL_BOOT_MESSAGE
An empty message will only clear the display at driver init time. Any other
printf()-formatted message is valid with newline and escape codes.
+config MULTIPLEXER
+ tristate "Multiplexer subsystem"
+ help
+ Multiplexer controller subsystem. Multiplexers are used in a
+ variety of settings, and this subsystem abstracts their use
+ so that the rest of the kernel sees a common interface. When
+ multiple parallel multiplexers are controlled by one single
+ multiplexer controller, this subsystem also coordinates the
+ multiplexer accesses.
+
+if MULTIPLEXER
+
+config MUX_GPIO
+ tristate "GPIO-controlled MUX controller"
+ depends on OF && GPIOLIB
+ help
+ GPIO-controlled MUX controller.
+
+ To compile this driver as a module, choose M here: the module will
+ be called mux-gpio.
+
+endif
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 31983366090a..0befa2bba762 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,6 +53,8 @@ obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_PANEL) += panel.o
+obj-$(CONFIG_MULTIPLEXER) += mux-core.o
+obj-$(CONFIG_MUX_GPIO) += mux-gpio.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
diff --git a/drivers/misc/mux-core.c b/drivers/misc/mux-core.c
new file mode 100644
index 000000000000..5142caa19236
--- /dev/null
+++ b/drivers/misc/mux-core.c
@@ -0,0 +1,325 @@
+/*
+ * Multiplexer subsystem
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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>
+
+static struct bus_type mux_bus_type = {
+ .name = "mux",
+};
+
+static int __init mux_init(void)
+{
+ return bus_register(&mux_bus_type);
+}
+
+static void __exit mux_exit(void)
+{
+ bus_unregister(&mux_bus_type);
+}
+
+static DEFINE_IDA(mux_ida);
+
+static void mux_control_release(struct device *dev)
+{
+ struct mux_control *mux = to_mux_control(dev);
+
+ ida_simple_remove(&mux_ida, mux->id);
+ kfree(mux);
+}
+
+static struct device_type mux_control_type = {
+ .name = "mux-control",
+ .release = mux_control_release,
+};
+
+/**
+ * mux_control_alloc - allocate a mux-control
+ * @sizeof_priv: Size of extra memory area for private use by the caller.
+ *
+ * Returns the new mux-control.
+ */
+struct mux_control *mux_control_alloc(size_t sizeof_priv)
+{
+ struct mux_control *mux;
+
+ mux = kzalloc(sizeof(*mux) + sizeof_priv, GFP_KERNEL);
+ if (!mux)
+ return NULL;
+
+ 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);
+ return NULL;
+ }
+ dev_set_name(&mux->dev, "mux:control%d", mux->id);
+
+ mux->cached_state = -1;
+ mux->idle_state = -1;
+
+ return mux;
+}
+EXPORT_SYMBOL_GPL(mux_control_alloc);
+
+/**
+ * mux_control_register - register a mux-control, thus readying it for use
+ * @mux: The mux-control to register.
+ *
+ * Returns zero on success or a negative errno on error.
+ */
+int mux_control_register(struct mux_control *mux)
+{
+ int ret;
+
+ /* 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)
+ return ret;
+
+ ret = of_platform_populate(mux->dev.of_node, NULL, NULL, &mux->dev);
+ if (ret < 0)
+ device_del(&mux->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mux_control_register);
+
+/**
+ * mux_control_unregister - take the mux-control off-line, reversing the
+ * effexts of mux_control_register
+ * @mux: the mux-control to unregister.
+ */
+void mux_control_unregister(struct mux_control *mux)
+{
+ of_platform_depopulate(&mux->dev);
+ device_del(&mux->dev);
+}
+EXPORT_SYMBOL_GPL(mux_control_unregister);
+
+/**
+ * mux_control_put - put away the mux-control for good, reversing the
+ * effects of either mux_control_alloc or mux_control_get
+ * @mux: The mux-control to put away.
+ */
+void mux_control_put(struct mux_control *mux)
+{
+ if (!mux)
+ return;
+ put_device(&mux->dev);
+}
+EXPORT_SYMBOL_GPL(mux_control_put);
+
+static int mux_control_set(struct mux_control *mux, int state)
+{
+ int ret = mux->ops->set(mux, state);
+
+ mux->cached_state = ret < 0 ? -1 : state;
+
+ return ret;
+}
+
+/**
+ * mux_control_select - select the given multiplexer state
+ * @mux: The mux-control to request a change of state from.
+ * @state: The new requested state.
+ *
+ * Returns 0 if the requested state was already active, or 1 it the
+ * mux-control state was changed to the requested state. Or a negavive
+ * errno on error.
+ * Note that the difference in return value of zero or one is of
+ * questionable value; especially if the mux-control has several independent
+ * consumers, which is something the consumers should not be making
+ * assumptions about.
+ *
+ * Make sure to call mux_control_deselect when the operation is complete and
+ * the mux-control is free for others to use, but do not call
+ * mux_control_deselect if mux_control_select fails.
+ */
+int mux_control_select(struct mux_control *mux, int state)
+{
+ int ret;
+
+ if (down_read_trylock(&mux->lock)) {
+ if (mux->cached_state == state)
+ return 0;
+
+ /* Sigh, the mux needs updating... */
+ up_read(&mux->lock);
+ }
+
+ /* ...or it's just contended. */
+ down_write(&mux->lock);
+
+ if (mux->cached_state == state) {
+ /*
+ * Hmmm, someone else changed the mux to my liking.
+ * That makes me wonder how long I waited for nothing?
+ */
+ downgrade_write(&mux->lock);
+ return 0;
+ }
+
+ 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);
+ return ret;
+ }
+
+ downgrade_write(&mux->lock);
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(mux_control_select);
+
+/**
+ * mux_control_deselect - deselect the previously selected multiplexer state
+ * @mux: The mux-control to deselect.
+ *
+ * Returns 0 on success and a negative errno on error. An error can only
+ * occur if the mux has an idle state. Note that even if an error occurs, the
+ * mux-control is unlocked for others to access.
+ */
+int mux_control_deselect(struct mux_control *mux)
+{
+ int ret = 0;
+
+ if (mux->idle_state != -1 && mux->cached_state != mux->idle_state)
+ ret = mux_control_set(mux, mux->idle_state);
+
+ up_read(&mux->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mux_control_deselect);
+
+static int of_dev_node_match(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static struct mux_control *of_find_mux_by_node(struct device_node *np)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&mux_bus_type, NULL, np, of_dev_node_match);
+
+ return dev ? to_mux_control(dev) : NULL;
+}
+
+/**
+ * mux_control_get - get the mux-control for a device
+ * @dev: The device that needs a mux-control.
+ *
+ * Returns the mux-control.
+ */
+struct mux_control *mux_control_get(struct device *dev)
+{
+ struct mux_control *mux;
+
+ if (!dev->of_node)
+ return ERR_PTR(-ENODEV);
+
+ mux = of_find_mux_by_node(dev->of_node->parent);
+ if (!mux)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return mux;
+}
+EXPORT_SYMBOL_GPL(mux_control_get);
+
+static void devm_mux_control_free(struct device *dev, void *res)
+{
+ struct mux_control *mux = *(struct mux_control **)res;
+
+ mux_control_put(mux);
+}
+
+/**
+ * devm_mux_control_get - get the mux-control for a device, with resource
+ * management
+ * @dev: The device that needs a mux-control.
+ *
+ * Returns the mux-control.
+ */
+struct mux_control *devm_mux_control_get(struct device *dev)
+{
+ struct mux_control **ptr, *mux;
+
+ ptr = devres_alloc(devm_mux_control_free, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ mux = mux_control_get(dev);
+ if (IS_ERR(mux)) {
+ devres_free(ptr);
+ return mux;
+ }
+
+ *ptr = mux;
+ devres_add(dev, ptr);
+
+ return mux;
+}
+EXPORT_SYMBOL_GPL(devm_mux_control_get);
+
+static int devm_mux_control_match(struct device *dev, void *res, void *data)
+{
+ struct mux_control **r = res;
+
+ if (!r || !*r) {
+ WARN_ON(!r || !*r);
+ return 0;
+ }
+
+ return *r == data;
+}
+
+/**
+ * devm_mux_control_put - resource-managed version mux_control_put
+ * @dev: The device that originally got the mux-control.
+ * @mux: The mux-control to put away.
+ *
+ * Note that you do not normally need to call this function.
+ */
+void devm_mux_control_put(struct device *dev, struct mux_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");
diff --git a/drivers/misc/mux-gpio.c b/drivers/misc/mux-gpio.c
new file mode 100644
index 000000000000..e9baca35f400
--- /dev/null
+++ b/drivers/misc/mux-gpio.c
@@ -0,0 +1,116 @@
+/*
+ * GPIO-controlled multiplexer driver
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mux.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+struct mux_gpio {
+ struct gpio_descs *gpios;
+};
+
+static int mux_gpio_set(struct mux_control *mux, int val)
+{
+ struct mux_gpio *mux_gpio = mux_control_priv(mux);
+ int i;
+
+ for (i = 0; i < mux_gpio->gpios->ndescs; i++)
+ gpiod_set_value_cansleep(mux_gpio->gpios->desc[i],
+ val & (1 << i));
+
+ return 0;
+}
+
+static const struct mux_control_ops mux_gpio_ops = {
+ .set = mux_gpio_set,
+};
+
+static const struct of_device_id mux_gpio_dt_ids[] = {
+ { .compatible = "mux-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids);
+
+static int mux_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct mux_control *mux;
+ struct mux_gpio *mux_gpio;
+ u32 idle_state;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ mux = mux_control_alloc(sizeof(*mux_gpio));
+ if (!mux)
+ return -ENOMEM;
+ mux_gpio = mux_control_priv(mux);
+ mux->dev.parent = dev;
+ mux->ops = &mux_gpio_ops;
+
+ platform_set_drvdata(pdev, mux);
+
+ mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
+ if (IS_ERR(mux_gpio->gpios)) {
+ if (PTR_ERR(mux_gpio->gpios) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get gpios\n");
+ mux_control_put(mux);
+ return PTR_ERR(mux_gpio->gpios);
+ }
+ mux->states = 1 << mux_gpio->gpios->ndescs;
+
+ ret = of_property_read_u32(np, "idle-state", &idle_state);
+ if (ret >= 0) {
+ if (idle_state >= mux->states) {
+ dev_err(dev, "invalid idle-state %u\n", idle_state);
+ return -EINVAL;
+ }
+ mux->idle_state = idle_state;
+ }
+
+ ret = mux_control_register(mux);
+ if (ret < 0) {
+ dev_err(dev, "failed to register mux_control\n");
+ mux_control_put(mux);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mux_gpio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mux_control *mux = to_mux_control(dev);
+
+ mux_control_unregister(mux);
+ mux_control_put(mux);
+ return 0;
+}
+
+static struct platform_driver mux_gpio_driver = {
+ .driver = {
+ .name = "mux-gpio",
+ .of_match_table = of_match_ptr(mux_gpio_dt_ids),
+ },
+ .probe = mux_gpio_probe,
+ .remove = mux_gpio_remove,
+};
+module_platform_driver(mux_gpio_driver);
+
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se");
+MODULE_DESCRIPTION("GPIO-controlled multiplexer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mux.h b/include/linux/mux.h
new file mode 100644
index 000000000000..2f1472192d6d
--- /dev/null
+++ b/include/linux/mux.h
@@ -0,0 +1,55 @@
+/*
+ * mux.h - definitions for the multiplexer interface
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_MUX_H
+#define _LINUX_MUX_H
+
+#include <linux/device.h>
+#include <linux/rwsem.h>
+
+struct mux_control;
+
+struct mux_control_ops {
+ int (*set)(struct mux_control *mux, int reg);
+};
+
+struct mux_control {
+ struct rw_semaphore lock; /* protects the state of the mux */
+
+ struct device dev;
+ int id;
+
+ unsigned int states;
+ int cached_state;
+ int idle_state;
+
+ const struct mux_control_ops *ops;
+};
+
+#define to_mux_control(x) container_of((x), struct mux_control, dev)
+
+static inline void *mux_control_priv(struct mux_control *mux)
+{
+ return mux + 1;
+}
+
+struct mux_control *mux_control_alloc(size_t sizeof_priv);
+int mux_control_register(struct mux_control *mux);
+void mux_control_unregister(struct mux_control *mux);
+void mux_control_put(struct mux_control *mux);
+
+int mux_control_select(struct mux_control *mux, int state);
+int mux_control_deselect(struct mux_control *mux);
+
+struct mux_control *mux_control_get(struct device *dev);
+struct mux_control *devm_mux_control_get(struct device *dev);
+void devm_mux_control_put(struct device *dev, struct mux_control *mux);
+
+#endif /* _LINUX_MUX_H */
--
2.1.4
^ permalink raw reply related
* [PATCH v3 3/7] iio: inkern: api for manipulating ext_info of iio channels
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
Extend the inkern api with functions for reading and writing ext_info
of iio channels.
Signed-off-by: Peter Rosin <peda@axentia.se>
---
drivers/iio/inkern.c | 55 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/iio/consumer.h | 37 +++++++++++++++++++++++++++++
2 files changed, 92 insertions(+)
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index c4757e6367e7..efe418282cc3 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -746,3 +746,58 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
return ret;
}
EXPORT_SYMBOL_GPL(iio_write_channel_raw);
+
+int iio_get_channel_ext_info_count(struct iio_channel *chan)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+ unsigned int i = 0;
+
+ if (!chan->channel->ext_info)
+ return i;
+
+ for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++)
+ ++i;
+
+ return i;
+}
+EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);
+
+ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
+ const char *attr, char *buf)
+{
+ const struct iio_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;
+
+ return ext_info->read(chan->indio_dev, ext_info->private,
+ chan->channel, buf);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_ext_info);
+
+ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
+ const char *buf, size_t len)
+{
+ const struct iio_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;
+
+ return ext_info->write(chan->indio_dev, ext_info->private,
+ chan->channel, buf, len);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index 9edccfba1ffb..1e908ccb11f0 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -271,4 +271,41 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val,
int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
int *processed, unsigned int scale);
+/**
+ * iio_get_channel_ext_info_count() - get number of ext_info attributes
+ * connected to the channel.
+ * @chan: The channel being queried
+ *
+ * Returns the number of ext_info attributes
+ */
+int iio_get_channel_ext_info_count(struct iio_channel *chan);
+
+/**
+ * iio_read_channel_ext_info() - read ext_info attribute from a given channel
+ * @chan: The channel being queried.
+ * @attr: The ext_info attribute to read.
+ * @buf: Where to store the attribute value. Assumed to hold
+ * at least PAGE_SIZE bytes.
+ *
+ * Returns the number of bytes written to buf (perhaps w/o zero termination;
+ * it need not even be a string), or an error code.
+ */
+ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
+ const char *attr, char *buf);
+
+/**
+ * iio_write_channel_ext_info() - write ext_info attribute from a given channel
+ * @chan: The channel being queried.
+ * @attr: The ext_info attribute to read.
+ * @buf: The new attribute value. Strings needs to be zero-
+ * terminated, but the terminator should not be included
+ * in the below len.
+ * @len: The size of the new attribute value.
+ *
+ * Returns the number of accepted bytes, which should be the same as len.
+ * An error code can also be returned.
+ */
+ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
+ const char *buf, size_t len);
+
#endif
--
2.1.4
^ permalink raw reply related
* [PATCH v3 4/7] dt-bindings: iio: iio-mux: document iio-mux bindings
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../bindings/iio/multiplexer/iio-mux.txt | 47 ++++++++++++++++++++++
MAINTAINERS | 6 +++
2 files changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
diff --git a/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt b/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
new file mode 100644
index 000000000000..2807333ccc86
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
@@ -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>;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 37974aeee750..3fc0dbbc04ea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6124,6 +6124,12 @@ L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/rc/iguanair.c
+IIO MULTIPLEXER
+M: Peter Rosin <peda@axentia.se>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
+
IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23@kernel.org>
R: Hartmut Knaack <knaack.h@gmx.de>
--
2.1.4
^ permalink raw reply related
* [PATCH v3 5/7] iio: multiplexer: new iio category and iio-mux driver
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
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 <peda@axentia.se>
---
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
diff --git a/MAINTAINERS b/MAINTAINERS
index 3fc0dbbc04ea..02d9072aae16 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6129,6 +6129,7 @@ M: Peter Rosin <peda@axentia.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
+F: drivers/iio/multiplexer/iio-mux.c
IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23@kernel.org>
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 6743b18194fb..dcb541d0d70e 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -82,6 +82,7 @@ source "drivers/iio/humidity/Kconfig"
source "drivers/iio/imu/Kconfig"
source "drivers/iio/light/Kconfig"
source "drivers/iio/magnetometer/Kconfig"
+source "drivers/iio/multiplexer/Kconfig"
source "drivers/iio/orientation/Kconfig"
if IIO_TRIGGER
source "drivers/iio/trigger/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 87e4c4369e2f..f9879c29cf6f 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -27,6 +27,7 @@ obj-y += humidity/
obj-y += imu/
obj-y += light/
obj-y += magnetometer/
+obj-y += multiplexer/
obj-y += orientation/
obj-y += potentiometer/
obj-y += pressure/
diff --git a/drivers/iio/multiplexer/Kconfig b/drivers/iio/multiplexer/Kconfig
new file mode 100644
index 000000000000..70a044510686
--- /dev/null
+++ b/drivers/iio/multiplexer/Kconfig
@@ -0,0 +1,18 @@
+#
+# Multiplexer drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Multiplexers"
+
+config IIO_MUX
+ tristate "IIO multiplexer driver"
+ select MULTIPLEXER
+ depends on OF
+ help
+ Say yes here to build support for the IIO multiplexer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iio-mux.
+
+endmenu
diff --git a/drivers/iio/multiplexer/Makefile b/drivers/iio/multiplexer/Makefile
new file mode 100644
index 000000000000..68be3c4abd07
--- /dev/null
+++ b/drivers/iio/multiplexer/Makefile
@@ -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
diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c
new file mode 100644
index 000000000000..0b9d04914241
--- /dev/null
+++ b/drivers/iio/multiplexer/iio-mux.c
@@ -0,0 +1,450 @@
+/*
+ * IIO multiplexer driver
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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>
+
+struct mux_ext_info_cache {
+ char *data;
+ size_t size;
+};
+
+struct mux_child {
+ struct mux_ext_info_cache *ext_info_cache;
+};
+
+struct mux {
+ int cached_state;
+ struct mux_control *control;
+ struct iio_channel *parent;
+ struct iio_dev *indio_dev;
+ struct iio_chan_spec *chan;
+ struct iio_chan_spec_ext_info *ext_info;
+ struct mux_child *child;
+};
+
+static int iio_mux_select(struct mux *mux, int idx)
+{
+ struct mux_child *child = &mux->child[idx];
+ struct iio_chan_spec const *chan = &mux->chan[idx];
+ int ret;
+ int i;
+
+ ret = mux_control_select(mux->control, chan->channel);
+ if (ret < 0) {
+ mux->cached_state = -1;
+ return ret;
+ }
+
+ if (mux->cached_state == chan->channel)
+ return 0;
+
+ if (chan->ext_info) {
+ for (i = 0; chan->ext_info[i].name; ++i) {
+ const char *attr = chan->ext_info[i].name;
+ struct mux_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;
+ return ret;
+ }
+ }
+ }
+ mux->cached_state = chan->channel;
+
+ return 0;
+}
+
+static void iio_mux_deselect(struct mux *mux)
+{
+ mux_control_deselect(mux->control);
+}
+
+static int mux_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mux *mux = iio_priv(indio_dev);
+ int idx = chan - mux->chan;
+ int ret;
+
+ ret = iio_mux_select(mux, idx);
+ if (ret < 0)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_read_channel_raw(mux->parent, val);
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = iio_read_channel_scale(mux->parent, val, val2);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ iio_mux_deselect(mux);
+
+ return ret;
+}
+
+static int mux_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct mux *mux = iio_priv(indio_dev);
+ int idx = chan - mux->chan;
+ int ret;
+
+ ret = iio_mux_select(mux, idx);
+ if (ret < 0)
+ return ret;
+
+ switch (mask) {
+ case IIO_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);
+
+ return ret;
+}
+
+static int mux_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mux *mux = iio_priv(indio_dev);
+ int idx = chan - mux->chan;
+ int ret;
+
+ ret = iio_mux_select(mux, idx);
+ if (ret < 0)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_write_channel_raw(mux->parent, val);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ iio_mux_deselect(mux);
+
+ return ret;
+}
+
+static const struct iio_info mux_info = {
+ .read_raw = mux_read_raw,
+ .read_avail = mux_read_avail,
+ .write_raw = mux_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static ssize_t mux_read_ext_info(struct iio_dev *indio_dev, uintptr_t private,
+ struct iio_chan_spec const *chan, char *buf)
+{
+ struct mux *mux = iio_priv(indio_dev);
+ int idx = chan - mux->chan;
+ ssize_t ret;
+
+ ret = iio_mux_select(mux, idx);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_read_channel_ext_info(mux->parent,
+ mux->ext_info[private].name,
+ buf);
+
+ iio_mux_deselect(mux);
+
+ return ret;
+}
+
+static ssize_t mux_write_ext_info(struct iio_dev *indio_dev, uintptr_t private,
+ struct iio_chan_spec const *chan,
+ const char *buf, size_t len)
+{
+ struct device *dev = indio_dev->dev.parent;
+ struct mux *mux = iio_priv(indio_dev);
+ int idx = chan - mux->chan;
+ char *new;
+ ssize_t ret;
+
+ ret = iio_mux_select(mux, idx);
+ if (ret < 0)
+ return ret;
+
+ 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);
+ return ret;
+ }
+
+ 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);
+
+ return ret;
+}
+
+static int mux_configure_channel(struct device *dev, struct mux *mux,
+ struct device_node *child_np, int idx)
+{
+ struct mux_child *child = &mux->child[idx];
+ struct iio_chan_spec *chan = &mux->chan[idx];
+ struct iio_chan_spec const *pchan = mux->parent->channel;
+ u32 state;
+ char *page = NULL;
+ int num_ext_info;
+ int i;
+ int ret;
+
+ 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");
+ return ret;
+ }
+
+ 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);
+ return ret;
+ }
+
+ 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);
+ return ret;
+ }
+ 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);
+
+ return 0;
+}
+
+static int mux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child_np;
+ struct iio_dev *indio_dev;
+ struct iio_channel *parent;
+ struct mux *mux;
+ int sizeof_ext_info;
+ int children;
+ int sizeof_priv;
+ int i;
+ int ret;
+
+ 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");
+ return PTR_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 = (struct mux_child *)(mux + 1);
+ mux->chan = (struct iio_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");
+ return PTR_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)
+ return ret;
+ i++;
+ }
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret) {
+ dev_err(dev, "failed to register iio device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id mux_match[] = {
+ { .compatible = "iio-mux" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mux_match);
+
+static struct platform_driver mux_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");
--
2.1.4
^ permalink raw reply related
* [PATCH v3 6/7] dt-bindings: i2c: i2c-mux-simple: document i2c-mux-simple bindings
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../devicetree/bindings/i2c/i2c-mux-simple.txt | 76 ++++++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt
diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt
new file mode 100644
index 000000000000..decfd742b297
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt
@@ -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>;
+ };
+ };
+ };
+ };
--
2.1.4
^ permalink raw reply related
* [PATCH v3 7/7] i2c: i2c-mux-simple: new driver
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
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 <peda@axentia.se>
---
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
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index 96de9ce5669b..fb4f587d822a 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -73,6 +73,19 @@ config I2C_MUX_REG
This driver can also be built as a module. If so, the module
will be called i2c-mux-reg.
+config I2C_MUX_SIMPLE
+ tristate "Simple I2C multiplexer"
+ select MULTIPLEXER
+ depends on OF
+ help
+ If you say yes to this option, support will be included for a
+ simple generic I2C multiplexer. This driver provides access to
+ I2C busses connected through a MUX, which is controlled
+ by a generic MUX controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-mux-simple.
+
config I2C_DEMUX_PINCTRL
tristate "pinctrl-based I2C demultiplexer"
depends on PINCTRL && OF
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index 7c267c29b191..eea1fb9e6466 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -10,5 +10,6 @@ obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o
+obj-$(CONFIG_I2C_MUX_SIMPLE) += i2c-mux-simple.o
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/muxes/i2c-mux-simple.c b/drivers/i2c/muxes/i2c-mux-simple.c
new file mode 100644
index 000000000000..a2f1dc8a3295
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-mux-simple.c
@@ -0,0 +1,177 @@
+/*
+ * Generic simple I2C multiplexer
+ *
+ * Peter Rosin <peda@axentia.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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>
+
+struct mux {
+ struct mux_control *control;
+
+ bool do_not_deselect;
+};
+
+static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct mux *mux = i2c_mux_priv(muxc);
+ int ret;
+
+ ret = mux_control_select(mux->control, chan);
+ mux->do_not_deselect = ret < 0;
+
+ return ret;
+}
+
+static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct mux *mux = i2c_mux_priv(muxc);
+
+ if (mux->do_not_deselect)
+ return 0;
+
+ return mux_control_deselect(mux->control);
+}
+
+static struct i2c_adapter *mux_parent_adapter(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *parent_np;
+ struct i2c_adapter *parent;
+
+ parent_np = of_parse_phandle(np, "i2c-parent", 0);
+ if (!parent_np) {
+ dev_err(dev, "Cannot parse i2c-parent\n");
+ return ERR_PTR(-ENODEV);
+ }
+ parent = of_find_i2c_adapter_by_node(parent_np);
+ of_node_put(parent_np);
+ if (!parent)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return parent;
+}
+
+static const struct of_device_id i2c_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);
+
+static int i2c_mux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *child;
+ const struct of_device_id *match;
+ struct i2c_mux_core *muxc;
+ struct mux *mux;
+ struct i2c_adapter *parent;
+ int children;
+ int ret;
+
+ 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");
+ return PTR_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");
+ return PTR_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;
+ goto err_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) {
+ u32 chan;
+
+ ret = of_property_read_u32(child, "reg", &chan);
+ if (ret < 0) {
+ dev_err(dev, "no reg property for node '%s'\n",
+ child->name);
+ goto err_children;
+ }
+
+ if (chan >= mux->control->states) {
+ dev_err(dev, "invalid reg %u\n", chan);
+ ret = -EINVAL;
+ goto err_children;
+ }
+
+ ret = i2c_mux_add_adapter(muxc, 0, chan, 0);
+ if (ret)
+ goto err_children;
+ }
+
+ dev_info(dev, "%d-port mux on %s adapter\n", children, parent->name);
+
+ return 0;
+
+err_children:
+ i2c_mux_del_adapters(muxc);
+err_parent:
+ i2c_put_adapter(parent);
+
+ return ret;
+}
+
+static int i2c_mux_remove(struct platform_device *pdev)
+{
+ struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
+
+ i2c_mux_del_adapters(muxc);
+ i2c_put_adapter(muxc->parent);
+
+ return 0;
+}
+
+static struct platform_driver i2c_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");
--
2.1.4
^ permalink raw reply related
* [PATCH v3 0/7] mux controller abstraction and iio/i2c muxes
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
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
^ permalink raw reply
* [PATCH v5 1/3] i2c: pxa: Add support for the I2C units found in Armada 3700
From: Romain Perier @ 2016-11-21 13:32 UTC (permalink / raw)
To: Wolfram Sang, linux-i2c-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jason Cooper,
Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
Thomas Petazzoni, Nadav Haklai, Omri Itach, Shadi Ammouri,
Yahuda Yitschak, Hanna Hawa, Neta Zur Hershkovits, Igal Liberman,
Marcin Wojtas <mw>
In-Reply-To: <20161121133247.29889-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
The Armada 3700 has two I2C controllers that is compliant with the I2C
Bus Specificiation 2.1, supports multi-master and different bus speed:
Standard mode (up to 100 KHz), Fast mode (up to 400 KHz),
High speed mode (up to 3.4 Mhz).
This IP block has a lot of similarity with the PXA, except some register
offsets and bitfield. This commits adds a basic support for this I2C
unit.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Tested-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
Changes in v5:
- Don't define registers for armada-3700, we can re-use the ones
for PXA3XX.
- Define registers mask when OF is not used, in probe_pdata.
Changes in v4:
- Replaced the type of hs_mask and fm_mask by u32, instead of
unsigned int, As writel() take an u32 as first argument...
Changes in v3:
- Replaced the type of hs_mask and fm_mask by unsigned int,
instead of unsigned long.
drivers/i2c/busses/Kconfig | 2 +-
drivers/i2c/busses/i2c-pxa.c | 24 ++++++++++++++++++++++--
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index d252276..2f56a26 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -763,7 +763,7 @@ config I2C_PUV3
config I2C_PXA
tristate "Intel PXA2XX I2C adapter"
- depends on ARCH_PXA || ARCH_MMP || (X86_32 && PCI && OF)
+ depends on ARCH_PXA || ARCH_MMP || ARCH_MVEBU || (X86_32 && PCI && OF)
help
If you have devices in the PXA I2C bus, say yes to this option.
This driver can also be built as a module. If so, the module
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index e28b825..374d17c 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -98,6 +98,7 @@ static const struct platform_device_id i2c_pxa_id_table[] = {
{ "pxa3xx-pwri2c", REGS_PXA3XX },
{ "ce4100-i2c", REGS_CE4100 },
{ "pxa910-i2c", REGS_PXA910 },
+ { "armada-3700-i2c", REGS_PXA3XX },
{ },
};
MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
@@ -122,7 +123,9 @@ MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
#define ICR_SADIE (1 << 13) /* slave address detected int enable */
#define ICR_UR (1 << 14) /* unit reset */
#define ICR_FM (1 << 15) /* fast mode */
+#define ICR_BUSMODE_FM (1 << 16) /* shifted fast mode for armada-3700 */
#define ICR_HS (1 << 16) /* High Speed mode */
+#define ICR_BUSMODE_HS (1 << 17) /* shifted high speed mode for armada-3700 */
#define ICR_GPIOEN (1 << 19) /* enable GPIO mode for SCL in HS */
#define ISR_RWM (1 << 0) /* read/write mode */
@@ -193,6 +196,8 @@ struct pxa_i2c {
unsigned char master_code;
unsigned long rate;
bool highmode_enter;
+ u32 fm_mask;
+ u32 hs_mask;
};
#define _IBMR(i2c) ((i2c)->reg_ibmr)
@@ -503,8 +508,8 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c)
writel(i2c->slave_addr, _ISAR(i2c));
/* set control register values */
- writel(I2C_ICR_INIT | (i2c->fast_mode ? ICR_FM : 0), _ICR(i2c));
- writel(readl(_ICR(i2c)) | (i2c->high_mode ? ICR_HS : 0), _ICR(i2c));
+ writel(I2C_ICR_INIT | (i2c->fast_mode ? i2c->fm_mask : 0), _ICR(i2c));
+ writel(readl(_ICR(i2c)) | (i2c->high_mode ? i2c->hs_mask : 0), _ICR(i2c));
#ifdef CONFIG_I2C_PXA_SLAVE
dev_info(&i2c->adap.dev, "Enabling slave mode\n");
@@ -1137,6 +1142,7 @@ static const struct of_device_id i2c_pxa_dt_ids[] = {
{ .compatible = "mrvl,pxa-i2c", .data = (void *)REGS_PXA2XX },
{ .compatible = "mrvl,pwri2c", .data = (void *)REGS_PXA3XX },
{ .compatible = "mrvl,mmp-twsi", .data = (void *)REGS_PXA910 },
+ { .compatible = "marvell,armada-3700-i2c", .data = (void *)REGS_PXA3XX },
{}
};
MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
@@ -1158,6 +1164,13 @@ static int i2c_pxa_probe_dt(struct platform_device *pdev, struct pxa_i2c *i2c,
i2c->use_pio = 1;
if (of_get_property(np, "mrvl,i2c-fast-mode", NULL))
i2c->fast_mode = 1;
+ if (of_device_is_compatible(np, "marvell,armada-3700-i2c")) {
+ i2c->fm_mask = ICR_BUSMODE_FM;
+ i2c->hs_mask = ICR_BUSMODE_HS;
+ } else {
+ i2c->fm_mask = ICR_FM;
+ i2c->hs_mask = ICR_HS;
+ }
*i2c_types = (enum pxa_i2c_types)(of_id->data);
@@ -1181,6 +1194,13 @@ static int i2c_pxa_probe_pdata(struct platform_device *pdev,
i2c->master_code = 0xe;
i2c->rate = plat->rate;
}
+ if (!strcmp(id->name, "armada-3700-i2c")) {
+ i2c->fm_mask = ICR_BUSMODE_FM;
+ i2c->hs_mask = ICR_BUSMODE_HS;
+ } else {
+ i2c->fm_mask = ICR_FM;
+ i2c->hs_mask = ICR_HS;
+ }
return 0;
}
--
2.9.3
--
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
^ permalink raw reply related
* [PATCH v5 2/3] arm64: dts: marvell: Add I2C definitions for the Armada 3700
From: Romain Perier @ 2016-11-21 13:32 UTC (permalink / raw)
To: Wolfram Sang, linux-i2c-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jason Cooper,
Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
Thomas Petazzoni, Nadav Haklai, Omri Itach, Shadi Ammouri,
Yahuda Yitschak, Hanna Hawa, Neta Zur Hershkovits, Igal Liberman,
Marcin Wojtas <mw>
In-Reply-To: <20161121133247.29889-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
The Armada 3700 has two i2c bus interface units, this commit adds the
definitions of the corresponding device nodes. It also enables the node
on the development board for this SoC.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Acked-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
arch/arm64/boot/dts/marvell/armada-3720-db.dts | 4 ++++
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 18 ++++++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/arch/arm64/boot/dts/marvell/armada-3720-db.dts b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
index 1372e9a6..16d84af 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts
+++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
@@ -62,6 +62,10 @@
};
};
+&i2c0 {
+ status = "okay";
+};
+
/* CON3 */
&sata {
status = "okay";
diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
index e9bd587..1b0fd21 100644
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -98,6 +98,24 @@
/* 32M internal register @ 0xd000_0000 */
ranges = <0x0 0x0 0xd0000000 0x2000000>;
+ i2c0: i2c@11000 {
+ compatible = "marvell,armada-3700-i2c";
+ reg = <0x11000 0x24>;
+ clocks = <&nb_periph_clk 10>;
+ interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+ mrvl,i2c-fast-mode;
+ status = "disabled";
+ };
+
+ i2c1: i2c@11080 {
+ compatible = "marvell,armada-3700-i2c";
+ reg = <0x11080 0x24>;
+ clocks = <&nb_periph_clk 9>;
+ interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+ mrvl,i2c-fast-mode;
+ status = "disabled";
+ };
+
uart0: serial@12000 {
compatible = "marvell,armada-3700-uart";
reg = <0x12000 0x400>;
--
2.9.3
--
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
^ permalink raw reply related
* [PATCH v5 3/3] dt-bindings: i2c: pxa: Update the documentation for the Armada 3700
From: Romain Perier @ 2016-11-21 13:32 UTC (permalink / raw)
To: Wolfram Sang, linux-i2c-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jason Cooper,
Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
Thomas Petazzoni, Nadav Haklai, Omri Itach, Shadi Ammouri,
Yahuda Yitschak, Hanna Hawa, Neta Zur Hershkovits, Igal Liberman,
Marcin Wojtas <mw>
In-Reply-To: <20161121133247.29889-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
This commit documents the compatible string to have the compatibility for
the I2C unit found in the Armada 3700.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Changes in v5:
- Added the tag 'Acked-by', by Rob Herring
Changes in v2:
- Fixed wrong compatible string, it should be "marvell,armada-3700-i2c"
and not "marvell,armada-3700".
Documentation/devicetree/bindings/i2c/i2c-pxa.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/i2c/i2c-pxa.txt b/Documentation/devicetree/bindings/i2c/i2c-pxa.txt
index 12b78ac..d30f0b1 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-pxa.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-pxa.txt
@@ -7,6 +7,7 @@ Required properties :
compatible processor, e.g. pxa168, pxa910, mmp2, mmp3.
For the pxa2xx/pxa3xx, an additional node "mrvl,pxa-i2c" is required
as shown in the example below.
+ For the Armada 3700, the compatible should be "marvell,armada-3700-i2c".
Recommended properties :
--
2.9.3
--
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
^ permalink raw reply related
* [PATCH v5 0/3] Add basic support for the I2C units of the Armada 3700
From: Romain Perier @ 2016-11-21 13:32 UTC (permalink / raw)
To: Wolfram Sang, linux-i2c
Cc: devicetree, Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland,
Kumar Gala, linux-arm-kernel, Jason Cooper, Andrew Lunn,
Sebastian Hesselbarth, Gregory Clement, Thomas Petazzoni,
Nadav Haklai, Omri Itach, Shadi Ammouri, Yahuda Yitschak,
Hanna Hawa, Neta Zur Hershkovits, Igal Liberman,
Marcin Wojtas <mw>
This series add basic support for the I2C bus interface units present
in the Armada 3700 to the pxa-i2c driver. It also add the definitions of
the device nodes to the devicetree at the SoC level and for its official
development board: the Armada 3720 DB.
Romain Perier (3):
i2c: pxa: Add support for the I2C units found in Armada 3700
arm64: dts: marvell: Add I2C definitions for the Armada 3700
dt-bindings: i2c: pxa: Update the documentation for the Armada 3700
Documentation/devicetree/bindings/i2c/i2c-pxa.txt | 1 +
arch/arm64/boot/dts/marvell/armada-3720-db.dts | 4 ++++
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 18 +++++++++++++++++
drivers/i2c/busses/Kconfig | 2 +-
drivers/i2c/busses/i2c-pxa.c | 24 +++++++++++++++++++++--
5 files changed, 46 insertions(+), 3 deletions(-)
--
2.9.3
^ permalink raw reply
* Re: [PATCH v3 5/7] iio: multiplexer: new iio category and iio-mux driver
From: kbuild test robot @ 2016-11-21 14:33 UTC (permalink / raw)
Cc: kbuild-all, linux-kernel, Peter Rosin, Wolfram Sang, Rob Herring,
Mark Rutland, Jonathan Cameron, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald-Stadler, Jonathan Corbet,
Arnd Bergmann, Greg Kroah-Hartman, linux-i2c, devicetree,
linux-iio, linux-doc
In-Reply-To: <1479734235-18837-6-git-send-email-peda@axentia.se>
[-- Attachment #1: Type: text/plain, Size: 6412 bytes --]
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':
>> 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:
>> drivers/iio/multiplexer/iio-mux.c:174:2: error: unknown field 'read_avail' specified in initializer
.read_avail = mux_read_avail,
^
>> 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':
>> 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))
^~~~~~~~~~~~~~~~~~~~~~~~~
>> 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'?
chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW);
^~
cc1: some warnings being treated as errors
vim +/iio_read_avail_channel_raw +134 drivers/iio/multiplexer/iio-mux.c
128 if (ret < 0)
129 return ret;
130
131 switch (mask) {
132 case IIO_CHAN_INFO_RAW:
133 *type = IIO_VAL_INT;
> 134 ret = iio_read_avail_channel_raw(mux->parent, vals, length);
135 break;
136
137 default:
138 ret = -EINVAL;
139 }
140
141 iio_mux_deselect(mux);
142
143 return ret;
144 }
145
146 static int mux_write_raw(struct iio_dev *indio_dev,
147 struct iio_chan_spec const *chan,
148 int val, int val2, long mask)
149 {
150 struct mux *mux = iio_priv(indio_dev);
151 int idx = chan - mux->chan;
152 int ret;
153
154 ret = iio_mux_select(mux, idx);
155 if (ret < 0)
156 return ret;
157
158 switch (mask) {
159 case IIO_CHAN_INFO_RAW:
160 ret = iio_write_channel_raw(mux->parent, val);
161 break;
162
163 default:
164 ret = -EINVAL;
165 }
166
167 iio_mux_deselect(mux);
168
169 return ret;
170 }
171
172 static const struct iio_info mux_info = {
173 .read_raw = mux_read_raw,
> 174 .read_avail = mux_read_avail,
175 .write_raw = mux_write_raw,
176 .driver_module = THIS_MODULE,
177 };
178
179 static ssize_t mux_read_ext_info(struct iio_dev *indio_dev, uintptr_t private,
180 struct iio_chan_spec const *chan, char *buf)
181 {
182 struct mux *mux = iio_priv(indio_dev);
183 int idx = chan - mux->chan;
184 ssize_t ret;
185
186 ret = iio_mux_select(mux, idx);
187 if (ret < 0)
188 return ret;
189
190 ret = iio_read_channel_ext_info(mux->parent,
191 mux->ext_info[private].name,
192 buf);
193
194 iio_mux_deselect(mux);
195
196 return ret;
197 }
198
199 static ssize_t mux_write_ext_info(struct iio_dev *indio_dev, uintptr_t private,
200 struct iio_chan_spec const *chan,
201 const char *buf, size_t len)
202 {
203 struct device *dev = indio_dev->dev.parent;
204 struct mux *mux = iio_priv(indio_dev);
205 int idx = chan - mux->chan;
206 char *new;
207 ssize_t ret;
208
209 ret = iio_mux_select(mux, idx);
210 if (ret < 0)
211 return ret;
212
213 new = devm_kmemdup(dev, buf, len + 1, GFP_KERNEL);
214 if (!new) {
215 iio_mux_deselect(mux);
216 return -ENOMEM;
217 }
218
219 new[len] = 0;
220
221 ret = iio_write_channel_ext_info(mux->parent,
222 mux->ext_info[private].name,
223 buf, len);
224 if (ret < 0) {
225 iio_mux_deselect(mux);
226 devm_kfree(dev, new);
227 return ret;
228 }
229
230 devm_kfree(dev, mux->child[idx].ext_info_cache[private].data);
231 mux->child[idx].ext_info_cache[private].data = new;
232 mux->child[idx].ext_info_cache[private].size = len;
233
234 iio_mux_deselect(mux);
235
236 return ret;
237 }
238
239 static int mux_configure_channel(struct device *dev, struct mux *mux,
240 struct device_node *child_np, int idx)
241 {
242 struct mux_child *child = &mux->child[idx];
243 struct iio_chan_spec *chan = &mux->chan[idx];
244 struct iio_chan_spec const *pchan = mux->parent->channel;
245 u32 state;
246 char *page = NULL;
247 int num_ext_info;
248 int i;
249 int ret;
250
251 chan->indexed = 1;
252 chan->output = pchan->output;
253 chan->datasheet_name = child_np->name;
254 chan->ext_info = mux->ext_info;
255
256 ret = iio_get_channel_type(mux->parent, &chan->type);
257 if (ret < 0) {
258 dev_err(dev, "failed to get parent channel type\n");
259 return ret;
260 }
261
262 if (iio_channel_has_info(pchan, IIO_CHAN_INFO_RAW))
263 chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW);
264 if (iio_channel_has_info(pchan, IIO_CHAN_INFO_SCALE))
265 chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE);
266
> 267 if (iio_channel_has_available(pchan, IIO_CHAN_INFO_RAW))
> 268 chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW);
269
270 ret = of_property_read_u32(child_np, "reg", &state);
271 if (ret < 0) {
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 56692 bytes --]
^ permalink raw reply
* Re: [RFC PATCH v2 3/7] iio: inkern: api for manipulating ext_info of iio channels
From: Lars-Peter Clausen @ 2016-11-21 15:45 UTC (permalink / raw)
To: Jonathan Cameron, Peter Rosin,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
Peter Meerwald-Stadler, Arnd Bergmann, Greg Kroah-Hartman,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <d5cd7550-2cde-5beb-2cdb-ad6d501fc0ef-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On 11/19/2016 04:38 PM, Jonathan Cameron wrote:
> On 17/11/16 21:48, Peter Rosin wrote:
>> Extend the inkern api with functions for reading and writing ext_info
>> of iio channels.
> I'd like Lars' feedback on this one.
>
> Superficially looks fine to me but I am not as familiar with this interface
> as Lars is ;) (he wrote it IIRC:)
The implementation looks OK. I'm not necessarily convinced about the concept
though, but the code is manageable so I guess it is OK.
The final version should add kernel API documentation for the new functions.
>> ---
>> drivers/iio/inkern.c | 55 ++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/iio/consumer.h | 6 +++++
>> 2 files changed, 61 insertions(+)
>>
>> diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
>> index cfca17ba2535..a8099b164222 100644
>> --- a/drivers/iio/inkern.c
>> +++ b/drivers/iio/inkern.c
>> @@ -850,3 +850,58 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
>> return ret;
>> }
>> EXPORT_SYMBOL_GPL(iio_write_channel_raw);
>> +
>> +int iio_get_channel_ext_info_count(struct iio_channel *chan)
should be unsigned.
>> +{
>> + const struct iio_chan_spec_ext_info *ext_info;
>> + unsigned int i = 0;
>> +
>> + if (!chan->channel->ext_info)
>> + return i;
>> +
>> + for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++)
>> + ++i;
>> +
>> + return i;
>> +}
>> +EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);
>> +
>> +ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
>> + const char *attr, char *buf)
>> +{
>> + const struct iio_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;
You could factor the lookup out into a helper function that is used for both
read and write. And also stop searching once a match was found.
>> +
>> + return ext_info->read(chan->indio_dev, ext_info->private,
>> + chan->channel, buf);
>> + }
>> +
>> + return -EINVAL;
>> +}
>> +EXPORT_SYMBOL_GPL(iio_read_channel_ext_info);
>> +
>> +ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
>> + const char *buf, size_t len)
>> +{
>> + const struct iio_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;
>> +
>> + return ext_info->write(chan->indio_dev, ext_info->private,
>> + chan->channel, buf, len);
>> + }
>> +
>> + return -EINVAL;
>> +}
>> +EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
>> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
>> index 9a4f336d8b4a..471dece8729a 100644
>> --- a/include/linux/iio/consumer.h
>> +++ b/include/linux/iio/consumer.h
>> @@ -299,4 +299,10 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val,
>> int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
>> int *processed, unsigned int scale);
>>
>> +int iio_get_channel_ext_info_count(struct iio_channel *chan);
>> +ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
>> + const char *attr, char *buf);
>> +ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
>> + const char *buf, size_t len);
>> +
>> #endif
>>
>
^ permalink raw reply
* Re: [RFC PATCH v2 3/7] iio: inkern: api for manipulating ext_info of iio channels
From: Peter Rosin @ 2016-11-21 16:07 UTC (permalink / raw)
To: Lars-Peter Clausen, Jonathan Cameron, linux-kernel
Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
Peter Meerwald-Stadler, Arnd Bergmann, Greg Kroah-Hartman,
linux-i2c, devicetree, linux-iio
In-Reply-To: <6ea462c2-53ae-0f50-deb9-e500a97f2ac4@metafoo.de>
On 2016-11-21 16:45, Lars-Peter Clausen wrote:
> On 11/19/2016 04:38 PM, Jonathan Cameron wrote:
>> On 17/11/16 21:48, Peter Rosin wrote:
>>> Extend the inkern api with functions for reading and writing ext_info
>>> of iio channels.
>> I'd like Lars' feedback on this one.
>>
>> Superficially looks fine to me but I am not as familiar with this interface
>> as Lars is ;) (he wrote it IIRC:)
>
> The implementation looks OK. I'm not necessarily convinced about the concept
> though, but the code is manageable so I guess it is OK.
>
> The final version should add kernel API documentation for the new functions.
I added that in v3 that I sent earlier today.
>>> ---
>>> drivers/iio/inkern.c | 55 ++++++++++++++++++++++++++++++++++++++++++++
>>> include/linux/iio/consumer.h | 6 +++++
>>> 2 files changed, 61 insertions(+)
>>>
>>> diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
>>> index cfca17ba2535..a8099b164222 100644
>>> --- a/drivers/iio/inkern.c
>>> +++ b/drivers/iio/inkern.c
>>> @@ -850,3 +850,58 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
>>> return ret;
>>> }
>>> EXPORT_SYMBOL_GPL(iio_write_channel_raw);
>>> +
>>> +int iio_get_channel_ext_info_count(struct iio_channel *chan)
>
> should be unsigned.
Correct, I had a plan to do that change, but it slipped my mind. Thanks
for catching it!
>>> +{
>>> + const struct iio_chan_spec_ext_info *ext_info;
>>> + unsigned int i = 0;
>>> +
>>> + if (!chan->channel->ext_info)
>>> + return i;
>>> +
>>> + for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++)
>>> + ++i;
>>> +
>>> + return i;
>>> +}
>>> +EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);
>>> +
>>> +ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
>>> + const char *attr, char *buf)
>>> +{
>>> + const struct iio_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;
>
> You could factor the lookup out into a helper function that is used for both
> read and write. And also stop searching once a match was found.
I'll do a lookup helper for v4. But the stop-searching-thing is already
done with the return statements at the end of the for-loops.
Thanks for looking!
Cheers,
Peter
>>> +
>>> + return ext_info->read(chan->indio_dev, ext_info->private,
>>> + chan->channel, buf);
>>> + }
>>> +
>>> + return -EINVAL;
>>> +}
>>> +EXPORT_SYMBOL_GPL(iio_read_channel_ext_info);
>>> +
>>> +ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
>>> + const char *buf, size_t len)
>>> +{
>>> + const struct iio_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;
>>> +
>>> + return ext_info->write(chan->indio_dev, ext_info->private,
>>> + chan->channel, buf, len);
>>> + }
>>> +
>>> + return -EINVAL;
>>> +}
>>> +EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
>>> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
>>> index 9a4f336d8b4a..471dece8729a 100644
>>> --- a/include/linux/iio/consumer.h
>>> +++ b/include/linux/iio/consumer.h
>>> @@ -299,4 +299,10 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val,
>>> int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
>>> int *processed, unsigned int scale);
>>>
>>> +int iio_get_channel_ext_info_count(struct iio_channel *chan);
>>> +ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
>>> + const char *attr, char *buf);
>>> +ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
>>> + const char *buf, size_t len);
>>> +
>>> #endif
>>>
>>
>
^ permalink raw reply
* [Patch V5 1/2] i2c: imx: add devicetree binding for lpi2c
From: Gao Pan @ 2016-11-21 7:51 UTC (permalink / raw)
To: wsa, u.kleine-koenig, cmo, robh, vz, wsa-dev
Cc: linux-i2c, pandy.gao, frank.li, fugang.duan
Add a binding document for lpi2c driver
Signed-off-by: Gao Pan <pandy.gao@nxp.com>
---
.../devicetree/bindings/i2c/i2c-imx-lpi2c.txt | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.txt b/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.txt
new file mode 100644
index 0000000..70c054a
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.txt
@@ -0,0 +1,20 @@
+* Freescale Low Power Inter IC (LPI2C) for i.MX
+
+Required properties:
+- compatible :
+ - "fsl,imx7ulp-lpi2c" for LPI2C compatible with the one integrated on i.MX7ULP soc
+ - "fsl,imx8dv-lpi2c" for LPI2C compatible with the one integrated on i.MX8DV soc
+- reg : address and length of the lpi2c master registers
+- interrupt-parent : core interrupt controller
+- interrupts : lpi2c interrupt
+- clocks : lpi2c clock specifier
+
+Examples:
+
+lpi2c7: lpi2c7@40A50000 {
+ compatible = "fsl,imx8dv-lpi2c";
+ reg = <0x40A50000 0x10000>;
+ interrupt-parent = <&intc>;
+ interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX7ULP_CLK_LPI2C7>;
+};
--
1.9.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox