* Re: [v6,2/2] i2c: qup: support SMBus block read
From: Wolfram Sang @ 2016-11-29 21:46 UTC (permalink / raw)
To: Austin Christ
Cc: wsa, linux-arm-msm, timur, linux-kernel, rruigrok, nkaje,
linux-i2c, sricharan, linux-arm-kernel, cov
In-Reply-To: <1476203277-6893-3-git-send-email-austinwc@codeaurora.org>
[-- Attachment #1.1: Type: text/plain, Size: 698 bytes --]
On Tue, Oct 11, 2016 at 10:27:57AM -0600, Austin Christ wrote:
> From: Naveen Kaje <nkaje@codeaurora.org>
>
> I2C QUP driver relies on SMBus emulation support from the framework.
> To handle SMBus block reads, the driver should check I2C_M_RECV_LEN
> flag and should read the first byte received as the message length.
>
> The driver configures the QUP hardware to read one byte. Once the
> message length is known from this byte, the QUP hardware is configured
> to read the rest.
>
> Signed-off-by: Naveen Kaje <nkaje@codeaurora.org>
> Signed-off-by: Austin Christ <austinwc@codeaurora.org>
> Reviewed-by: Sricharan R <sricharan@codeaurora.org>
Applied to for-next, thanks!
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [v6,1/2] i2c: qup: add ACPI support
From: Wolfram Sang @ 2016-11-29 21:46 UTC (permalink / raw)
To: Austin Christ
Cc: wsa, linux-arm-msm, timur, linux-kernel, rruigrok, nkaje,
linux-i2c, sricharan, linux-arm-kernel, cov
In-Reply-To: <1476203277-6893-2-git-send-email-austinwc@codeaurora.org>
[-- Attachment #1.1: Type: text/plain, Size: 422 bytes --]
On Tue, Oct 11, 2016 at 10:27:56AM -0600, Austin Christ wrote:
> From: Naveen Kaje <nkaje@codeaurora.org>
>
> Add support to get the device parameters from ACPI. Assume
> that the clocks are managed by firmware.
>
> Signed-off-by: Naveen Kaje <nkaje@codeaurora.org>
> Signed-off-by: Austin Christ <austinwc@codeaurora.org>
> Reviewed-by: Sricharan R <sricharan@codeaurora.org>
Applied to for-next, thanks!
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [Patch V5 2/2] i2c: imx: add low power i2c bus driver
From: Wolfram Sang @ 2016-11-29 21:35 UTC (permalink / raw)
To: Gao Pan
Cc: wsa, u.kleine-koenig, cmo, robh, vz, linux-i2c, frank.li,
fugang.duan
In-Reply-To: <1479714704-3014-2-git-send-email-pandy.gao@nxp.com>
[-- Attachment #1: Type: text/plain, Size: 1094 bytes --]
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
Here you say GPL v2 or later, while at the end you declare GPL V2 only.
This needs to be made consistent.
> +static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id)
> +{
> + struct lpi2c_imx_struct *lpi2c_imx = dev_id;
> + unsigned int temp;
> +
> + lpi2c_imx_intctrl(lpi2c_imx, 0);
> + temp = readl(lpi2c_imx->base + LPI2C_MSR);
> +
> + if (temp & MSR_RDF) {
> + lpi2c_imx_read_rxfifo(lpi2c_imx);
> + return IRQ_HANDLED;
> + }
> +
> + if (temp & MSR_TDF) {
> + lpi2c_imx_write_txfifo(lpi2c_imx);
> + return IRQ_HANDLED;
> + }
> +
> + complete(&lpi2c_imx->complete);
> +
> + return IRQ_HANDLED;
> +}
Minor nit: Why can't you have one single exit point (e.g. only one
'return IRQ_HANDLED') here?
> +MODULE_LICENSE("GPL v2");
Here is GPL v2 only...
Rest looks good. Thanks Vladimir for the reviews!
Thanks,
Wolfram
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v5 1/3] i2c: pxa: Add support for the I2C units found in Armada 3700
From: Wolfram Sang @ 2016-11-29 21:17 UTC (permalink / raw)
To: Romain Perier
Cc: Wolfram Sang, linux-i2c, 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
In-Reply-To: <20161121133247.29889-2-romain.perier@free-electrons.com>
[-- Attachment #1: Type: text/plain, Size: 1060 bytes --]
> + 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;
> + }
Okay, having the same code twice is not nice as well.
Sorry for missing this in the first review and going a step back, but I
think now the best solution is to have again a REGS_A3700 struct, but we
should extend it with new entries for the shifted bits. Then in the init
code, you can do something like:
i2c->fm_mask = pxa_reg_layout[i2c_type].fm_mask ?: ICR_FM;
Makes sense?
Thanks,
Wolfram
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH 1/1] i2c: designware: Consolidate default functionality bits
From: Wolfram Sang @ 2016-11-29 19:20 UTC (permalink / raw)
To: Alexander Stein
Cc: Jarkko Nikula, Andy Shevchenko, Mika Westerberg, Wolfram Sang,
linux-i2c
In-Reply-To: <1479725000-22513-1-git-send-email-alexander.stein@systec-electronic.com>
[-- Attachment #1: Type: text/plain, Size: 344 bytes --]
On Mon, Nov 21, 2016 at 11:43:20AM +0100, Alexander Stein wrote:
> Use a common place for default functionality bits for both platform
> and pci driver.
>
> Signed-off-by: Alexander Stein <alexander.stein@systec-electronic.com>
Applied to for-next, thanks! For the other change discussed, an
incremental patch needs to be send now.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v3] i2c: i2c-mux-gpio: update mux with gpiod_set_array_value_cansleep
From: Wolfram Sang @ 2016-11-29 19:15 UTC (permalink / raw)
To: Peter Rosin; +Cc: linux-kernel, Peter Korsgaard, Wolfram Sang, linux-i2c
In-Reply-To: <1480011568-13254-1-git-send-email-peda@axentia.se>
[-- Attachment #1: Type: text/plain, Size: 300 bytes --]
On Thu, Nov 24, 2016 at 07:19:28PM +0100, Peter Rosin wrote:
> If the gpio controller supports it and the gpio lines are concentrated
> to one gpio chip, the mux controller pins will get updated simultaneously.
>
> Signed-off-by: Peter Rosin <peda@axentia.se>
Applied to for-next, thanks!
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v2 0/3] i2c: octeon: thunder: Fix i2c not working on Octeon
From: Wolfram Sang @ 2016-11-29 19:14 UTC (permalink / raw)
To: Steven J. Hill
Cc: Jan Glauber, Wolfram Sang, linux-i2c, linux-mips, Paul Burton,
David Daney
In-Reply-To: <6237fab7-fc5f-90fd-a652-b304f2a21a43@cavium.com>
[-- Attachment #1: Type: text/plain, Size: 575 bytes --]
On Tue, Nov 29, 2016 at 01:10:30PM -0600, Steven J. Hill wrote:
> On 11/29/2016 12:37 PM, Wolfram Sang wrote:
> >
> > Okay, I will pick up patch 1/3 from Jan (the revert) and read your
> > comment above as an ack. Paul, is your tested-by still valid?
> >
> Just to be explicitly clear, the patch revert allows I2C on OCTEON
> to work again. I apologize if that was not clear.
Partly. It was not clear to me if only patch 1 was enough to get Paul's
system to boot again or if it needed all 3 patches to do that.
Anyway, the revert will be in v4.9 for sure.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v2 0/3] i2c: octeon: thunder: Fix i2c not working on Octeon
From: Steven J. Hill @ 2016-11-29 19:10 UTC (permalink / raw)
To: Wolfram Sang
Cc: Jan Glauber, Wolfram Sang, linux-i2c, linux-mips, Paul Burton,
David Daney
In-Reply-To: <20161129183707.GA3378@katana>
On 11/29/2016 12:37 PM, Wolfram Sang wrote:
>
> Okay, I will pick up patch 1/3 from Jan (the revert) and read your
> comment above as an ack. Paul, is your tested-by still valid?
>
Just to be explicitly clear, the patch revert allows I2C on OCTEON
to work again. I apologize if that was not clear.
Steve
^ permalink raw reply
* Re: [PATCH V5] i2c: mux: pca954x: Add ACPI support for pca954x
From: Wolfram Sang @ 2016-11-29 19:08 UTC (permalink / raw)
To: Tin Huynh
Cc: Wolfram Sang, Peter Rosin, linux-i2c, linux-kernel, linux-acpi,
Loc Ho, Thang Nguyen, Phong Vo, patches
In-Reply-To: <1480414363-16318-1-git-send-email-tnhuynh@apm.com>
[-- Attachment #1: Type: text/plain, Size: 203 bytes --]
On Tue, Nov 29, 2016 at 05:12:43PM +0700, Tin Huynh wrote:
> This patch enables ACPI support for mux-pca954x driver.
>
> Signed-off-by: Tin Huynh <tnhuynh@apm.com>
Applied to for-next, thanks!
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v2 0/3] i2c: octeon: thunder: Fix i2c not working on Octeon
From: Wolfram Sang @ 2016-11-29 18:37 UTC (permalink / raw)
To: Steven J. Hill
Cc: Jan Glauber, Wolfram Sang, linux-i2c, linux-mips, Paul Burton,
David Daney
In-Reply-To: <52c6e31e-9351-fa26-aefe-4f1415324adf@cavium.com>
[-- Attachment #1: Type: text/plain, Size: 603 bytes --]
On Tue, Nov 29, 2016 at 12:22:34PM -0600, Steven J. Hill wrote:
> On 11/29/2016 03:19 AM, Jan Glauber wrote:
> >
> > if possible we should at least revert commit 70121f7f3725. I should get
> > access to an Octeon 71xx board tomorrow, but I'm afraid we'll miss the
> > deadline for a well tested fix that works across all machines.
> >
> I second Jan's advice. Please revert the commit and we'll have the
> fix for the next release.
Okay, I will pick up patch 1/3 from Jan (the revert) and read your
comment above as an ack. Paul, is your tested-by still valid?
Thanks,
Wolfram
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v2 0/3] i2c: octeon: thunder: Fix i2c not working on Octeon
From: Steven J. Hill @ 2016-11-29 18:22 UTC (permalink / raw)
To: Jan Glauber, Wolfram Sang
Cc: Wolfram Sang, linux-i2c, linux-mips, Paul Burton, David Daney
In-Reply-To: <20161129091928.GB29487@hardcore>
On 11/29/2016 03:19 AM, Jan Glauber wrote:
>
> if possible we should at least revert commit 70121f7f3725. I should get
> access to an Octeon 71xx board tomorrow, but I'm afraid we'll miss the
> deadline for a well tested fix that works across all machines.
>
I second Jan's advice. Please revert the commit and we'll have the
fix for the next release.
^ permalink raw reply
* RE: [Patch V5 2/2] i2c: imx: add low power i2c bus driver
From: Pandy Gao @ 2016-11-29 8:22 UTC (permalink / raw)
To: wsa@the-dreams.de, u.kleine-koenig@pengutronix.de,
cmo@melexis.com, robh@kernel.org, vz@mleia.com,
wsa-dev@sang-engineering.com
Cc: linux-i2c@vger.kernel.org, Frank Li, Andy Duan
In-Reply-To: <1479714704-3014-2-git-send-email-pandy.gao@nxp.com>
Ping...
> -----Original Message-----
> From: Pandy Gao
> Sent: Monday, November 21, 2016 3:52 PM
> To: wsa@the-dreams.de; u.kleine-koenig@pengutronix.de;
> cmo@melexis.com; robh@kernel.org; vz@mleia.com; wsa-dev@sang-
> engineering.com
> Cc: linux-i2c@vger.kernel.org; Pandy Gao <pandy.gao@nxp.com>; Frank
> Li <frank.li@nxp.com>; Andy Duan <fugang.duan@nxp.com>
> Subject: [Patch V5 2/2] i2c: imx: add low power i2c bus driver
>
> This patch adds lpi2c bus driver to support new i.MX products which use
> lpi2c instead of the old imx i2c.
>
> The lpi2c can continue operating in stop mode when an appropriate clock
> is available. It is also designed for low CPU overhead with DMA offloading
> of FIFO register accesses.
>
> Signed-off-by: Gao Pan <pandy.gao@nxp.com>
> Reviewed-by: Fugang Duan <B38611@freescale.com>
> Reviewed-by: Vladimir Zapolskiy <vz@mleia.com>
> ---
> V2:
> -stop i2c transfer under the wrong condition -add timeout check in
> while() domain
>
> V3:
> -fix typo inside commit message and the driver.
>
> V4:
> As Vladimir Zapolskiy's review, the version do below changes:
> -split devicetree binding with driver
> -prepare clk in probe()
> -replace "1<<n" BIT(n)
> -remove unnecessary variables
> -fix typo
> -use module_platform_driver()
> -use i2c_add_adapter()
>
> V5:
> -unprepare clk in lpi2c_imx_remove()
> -disable clk when lpi2c_imx_config() fails -unprepare clk when
> i2c_add_adapter() fails
>
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-imx-lpi2c.c | 649
> +++++++++++++++++++++++++++++++++++++
> 3 files changed, 660 insertions(+)
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index
> d252276..7cff9ec 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -597,6 +597,16 @@ config I2C_IMX
> This driver can also be built as a module. If so, the module
> will be called i2c-imx.
>
> +config I2C_IMX_LPI2C
> + tristate "IMX Low Power I2C interface"
> + depends on ARCH_MXC || COMPILE_TEST
> + help
> + Say Y here if you want to use the Low Power IIC bus controller
> + on the Freescale i.MX processors.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-imx-lpi2c.
> +
> config I2C_IOP3XX
> tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface"
> depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX ||
> ARCH_IOP13XX diff --git a/drivers/i2c/busses/Makefile
> b/drivers/i2c/busses/Makefile index 29764cc..05195cd 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_I2C_HIX5HD2) += i2c-
> hix5hd2.o
> obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
> obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o
> obj-$(CONFIG_I2C_IMX) += i2c-imx.o
> +obj-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-lpi2c.o
> obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
> obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
> obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
> diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-
> imx-lpi2c.c
> new file mode 100644
> index 0000000..af5d1b6
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
> @@ -0,0 +1,649 @@
> +/*
> + * This is i.MX low power i2c controller driver.
> + *
> + * Copyright 2016 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +
> +#define DRIVER_NAME "imx-lpi2c"
> +
> +#define LPI2C_PARAM 0x04 /* i2c RX/TX FIFO size */
> +#define LPI2C_MCR 0x10 /* i2c contrl register */
> +#define LPI2C_MSR 0x14 /* i2c status register */
> +#define LPI2C_MIER 0x18 /* i2c interrupt enable */
> +#define LPI2C_MCFGR0 0x20 /* i2c master configuration */
> +#define LPI2C_MCFGR1 0x24 /* i2c master configuration */
> +#define LPI2C_MCFGR2 0x28 /* i2c master configuration */
> +#define LPI2C_MCFGR3 0x2C /* i2c master configuration */
> +#define LPI2C_MCCR0 0x48 /* i2c master clk configuration */
> +#define LPI2C_MCCR1 0x50 /* i2c master clk configuration */
> +#define LPI2C_MFCR 0x58 /* i2c master FIFO control */
> +#define LPI2C_MFSR 0x5C /* i2c master FIFO status */
> +#define LPI2C_MTDR 0x60 /* i2c master TX data register */
> +#define LPI2C_MRDR 0x70 /* i2c master RX data register */
> +
> +/* i2c command */
> +#define TRAN_DATA 0X00
> +#define RECV_DATA 0X01
> +#define GEN_STOP 0X02
> +#define RECV_DISCARD 0X03
> +#define GEN_START 0X04
> +#define START_NACK 0X05
> +#define START_HIGH 0X06
> +#define START_HIGH_NACK 0X07
> +
> +#define MCR_MEN BIT(0)
> +#define MCR_RST BIT(1)
> +#define MCR_DOZEN BIT(2)
> +#define MCR_DBGEN BIT(3)
> +#define MCR_RTF BIT(8)
> +#define MCR_RRF BIT(9)
> +#define MSR_TDF BIT(0)
> +#define MSR_RDF BIT(1)
> +#define MSR_SDF BIT(9)
> +#define MSR_NDF BIT(10)
> +#define MSR_ALF BIT(11)
> +#define MSR_MBF BIT(24)
> +#define MSR_BBF BIT(25)
> +#define MIER_TDIE BIT(0)
> +#define MIER_RDIE BIT(1)
> +#define MIER_SDIE BIT(9)
> +#define MIER_NDIE BIT(10)
> +#define MCFGR1_AUTOSTOP BIT(8)
> +#define MCFGR1_IGNACK BIT(9)
> +#define MRDR_RXEMPTY BIT(14)
> +
> +#define I2C_CLK_RATIO 2
> +#define CHUNK_DATA 256
> +
> +#define LPI2C_RX_FIFOSIZE 4
> +#define LPI2C_TX_FIFOSIZE 4
> +
> +#define LPI2C_DEFAULT_RATE 100000
> +#define STARDARD_MAX_BITRATE 400000
> +#define FAST_MAX_BITRATE 1000000
> +#define FAST_PLUS_MAX_BITRATE 3400000
> +#define HIGHSPEED_MAX_BITRATE 5000000
> +
> +enum lpi2c_imx_mode {
> + STANDARD, /* 100+Kbps */
> + FAST, /* 400+Kbps */
> + FAST_PLUS, /* 1.0+Mbps */
> + HS, /* 3.4+Mbps */
> + ULTRA_FAST, /* 5.0+Mbps */
> +};
> +
> +enum lpi2c_imx_pincfg {
> + TWO_PIN_OD,
> + TWO_PIN_OO,
> + TWO_PIN_PP,
> + FOUR_PIN_PP,
> +};
> +
> +struct lpi2c_imx_struct {
> + struct i2c_adapter adapter;
> + struct clk *clk;
> + void __iomem *base;
> + __u8 *rx_buf;
> + __u8 *tx_buf;
> + struct completion complete;
> + unsigned int msglen;
> + unsigned int delivered;
> + unsigned int block_data;
> + unsigned int bitrate;
> + enum lpi2c_imx_mode mode;
> +};
> +
> +static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx,
> + unsigned int enable)
> +{
> + writel(enable, lpi2c_imx->base + LPI2C_MIER); }
> +
> +static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx) {
> + unsigned long orig_jiffies = jiffies;
> + unsigned int temp;
> +
> + while (1) {
> + temp = readl(lpi2c_imx->base + LPI2C_MSR);
> +
> + /* check for arbitration lost, clear if set */
> + if (temp & MSR_ALF) {
> + writel(temp, lpi2c_imx->base + LPI2C_MSR);
> + return -EAGAIN;
> + }
> +
> + if (temp & (MSR_BBF | MSR_MBF))
> + break;
> +
> + if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500)))
> {
> + dev_dbg(&lpi2c_imx->adapter.dev, "bus not
> work\n");
> + return -ETIMEDOUT;
> + }
> + schedule();
> + }
> +
> + return 0;
> +}
> +
> +static void lpi2c_imx_set_mode(struct lpi2c_imx_struct *lpi2c_imx) {
> + unsigned int bitrate = lpi2c_imx->bitrate;
> + enum lpi2c_imx_mode mode;
> +
> + if (bitrate < STARDARD_MAX_BITRATE)
> + mode = STANDARD;
> + else if (bitrate < FAST_MAX_BITRATE)
> + mode = FAST;
> + else if (bitrate < FAST_PLUS_MAX_BITRATE)
> + mode = FAST_PLUS;
> + else if (bitrate < HIGHSPEED_MAX_BITRATE)
> + mode = HS;
> + else
> + mode = ULTRA_FAST;
> +
> + lpi2c_imx->mode = mode;
> +}
> +
> +static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx,
> + struct i2c_msg *msgs)
> +{
> + unsigned int temp;
> + u8 read;
> +
> + temp = readl(lpi2c_imx->base + LPI2C_MCR);
> + temp |= MCR_RRF | MCR_RTF;
> + writel(temp, lpi2c_imx->base + LPI2C_MCR);
> + writel(0x7f00, lpi2c_imx->base + LPI2C_MSR);
> +
> + read = msgs->flags & I2C_M_RD;
> + temp = (msgs->addr << 1 | read) | (GEN_START << 8);
> + writel(temp, lpi2c_imx->base + LPI2C_MTDR);
> +
> + return lpi2c_imx_bus_busy(lpi2c_imx);
> +}
> +
> +static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx) {
> + unsigned long orig_jiffies = jiffies;
> + unsigned int temp;
> +
> + writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR);
> +
> + do {
> + temp = readl(lpi2c_imx->base + LPI2C_MSR);
> + if (temp & MSR_SDF)
> + break;
> +
> + if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500)))
> {
> + dev_dbg(&lpi2c_imx->adapter.dev, "stop
> timeout\n");
> + break;
> + }
> + schedule();
> +
> + } while (1);
> +}
> +
> +/* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD =
> CLKHI/2 */
> +static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx) {
> + u8 prescale, filt, sethold, clkhi, clklo, datavd;
> + unsigned int clk_rate, clk_cycle;
> + enum lpi2c_imx_pincfg pincfg;
> + unsigned int temp;
> +
> + lpi2c_imx_set_mode(lpi2c_imx);
> +
> + clk_rate = clk_get_rate(lpi2c_imx->clk);
> + if (lpi2c_imx->mode == HS || lpi2c_imx->mode == ULTRA_FAST)
> + filt = 0;
> + else
> + filt = 2;
> +
> + for (prescale = 0; prescale <= 7; prescale++) {
> + clk_cycle = clk_rate / ((1 << prescale) * lpi2c_imx-
> >bitrate)
> + - 3 - (filt >> 1);
> + clkhi = (clk_cycle + I2C_CLK_RATIO) / (I2C_CLK_RATIO +
> 1);
> + clklo = clk_cycle - clkhi;
> + if (clklo < 64)
> + break;
> + }
> +
> + if (prescale > 7)
> + return -EINVAL;
> +
> + /* set MCFGR1: PINCFG, PRESCALE, IGNACK */
> + if (lpi2c_imx->mode == ULTRA_FAST)
> + pincfg = TWO_PIN_OO;
> + else
> + pincfg = TWO_PIN_OD;
> + temp = prescale | pincfg << 24;
> +
> + if (lpi2c_imx->mode == ULTRA_FAST)
> + temp |= MCFGR1_IGNACK;
> +
> + writel(temp, lpi2c_imx->base + LPI2C_MCFGR1);
> +
> + /* set MCFGR2: FILTSDA, FILTSCL */
> + temp = (filt << 16) | (filt << 24);
> + writel(temp, lpi2c_imx->base + LPI2C_MCFGR2);
> +
> + /* set MCCR: DATAVD, SETHOLD, CLKHI, CLKLO */
> + sethold = clkhi;
> + datavd = clkhi >> 1;
> + temp = datavd << 24 | sethold << 16 | clkhi << 8 | clklo;
> +
> + if (lpi2c_imx->mode == HS)
> + writel(temp, lpi2c_imx->base + LPI2C_MCCR1);
> + else
> + writel(temp, lpi2c_imx->base + LPI2C_MCCR0);
> +
> + return 0;
> +}
> +
> +static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
> +{
> + unsigned int temp;
> + int ret;
> +
> + ret = clk_enable(lpi2c_imx->clk);
> + if (ret)
> + return ret;
> +
> + temp = MCR_RST;
> + writel(temp, lpi2c_imx->base + LPI2C_MCR);
> + writel(0, lpi2c_imx->base + LPI2C_MCR);
> +
> + ret = lpi2c_imx_config(lpi2c_imx);
> + if (ret)
> + goto clk_disable;
> +
> + temp = readl(lpi2c_imx->base + LPI2C_MCR);
> + temp |= MCR_MEN;
> + writel(temp, lpi2c_imx->base + LPI2C_MCR);
> +
> + return 0;
> +
> +clk_disable:
> + clk_disable(lpi2c_imx->clk);
> +
> + return ret;
> +}
> +
> +static int lpi2c_imx_master_disable(struct lpi2c_imx_struct *lpi2c_imx)
> +{
> + u32 temp;
> +
> + temp = readl(lpi2c_imx->base + LPI2C_MCR);
> + temp &= ~MCR_MEN;
> + writel(temp, lpi2c_imx->base + LPI2C_MCR);
> +
> + clk_disable(lpi2c_imx->clk);
> +
> + return 0;
> +}
> +
> +static int lpi2c_imx_msg_complete(struct lpi2c_imx_struct *lpi2c_imx) {
> + unsigned long timeout;
> +
> + timeout = wait_for_completion_timeout(&lpi2c_imx->complete,
> HZ);
> +
> + return timeout ? 0 : -ETIMEDOUT;
> +}
> +
> +static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx) {
> + unsigned long orig_jiffies = jiffies;
> + u32 txcnt;
> +
> + do {
> + txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
> +
> + if (readl(lpi2c_imx->base + LPI2C_MSR) & MSR_NDF) {
> + dev_dbg(&lpi2c_imx->adapter.dev, "NDF
> detected\n");
> + return -EIO;
> + }
> +
> + if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500)))
> {
> + dev_dbg(&lpi2c_imx->adapter.dev, "txfifo
> empty timeout\n");
> + return -ETIMEDOUT;
> + }
> + schedule();
> +
> + } while (txcnt);
> +
> + return 0;
> +}
> +
> +static void lpi2c_imx_set_tx_watermark(struct lpi2c_imx_struct
> +*lpi2c_imx) {
> + writel(LPI2C_TX_FIFOSIZE >> 1, lpi2c_imx->base +
> LPI2C_MFCR); }
> +
> +static void lpi2c_imx_set_rx_watermark(struct lpi2c_imx_struct
> +*lpi2c_imx) {
> + unsigned int temp, remaining;
> +
> + remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
> +
> + if (remaining > (LPI2C_RX_FIFOSIZE >> 1))
> + temp = LPI2C_RX_FIFOSIZE >> 1;
> + else
> + temp = 0;
> +
> + writel(temp << 16, lpi2c_imx->base + LPI2C_MFCR); }
> +
> +static void lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx)
> +{
> + unsigned int data, txcnt;
> +
> + txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
> +
> + while (txcnt < LPI2C_TX_FIFOSIZE) {
> + if (lpi2c_imx->delivered == lpi2c_imx->msglen)
> + break;
> +
> + data = lpi2c_imx->tx_buf[lpi2c_imx->delivered++];
> + writel(data, lpi2c_imx->base + LPI2C_MTDR);
> + txcnt++;
> + }
> +
> + if (lpi2c_imx->delivered < lpi2c_imx->msglen)
> + lpi2c_imx_intctrl(lpi2c_imx, MIER_TDIE | MIER_NDIE);
> + else
> + complete(&lpi2c_imx->complete);
> +}
> +
> +static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx) {
> + unsigned int blocklen, remaining;
> + unsigned int temp, data;
> +
> + do {
> + data = readl(lpi2c_imx->base + LPI2C_MRDR);
> + if (data & MRDR_RXEMPTY)
> + break;
> +
> + lpi2c_imx->rx_buf[lpi2c_imx->delivered++] = data & 0xff;
> + } while (1);
> +
> + /*
> + * First byte is the length of remaining packet in the SMBus block
> + * data read. Add it to msgs->len.
> + */
> + if (lpi2c_imx->block_data) {
> + blocklen = lpi2c_imx->rx_buf[0];
> + lpi2c_imx->msglen += blocklen;
> + }
> +
> + remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
> +
> + if (!remaining) {
> + complete(&lpi2c_imx->complete);
> + return;
> + }
> +
> + /* not finished, still waiting for rx data */
> + lpi2c_imx_set_rx_watermark(lpi2c_imx);
> +
> + /* multiple receive commands */
> + if (lpi2c_imx->block_data) {
> + lpi2c_imx->block_data = 0;
> + temp = remaining;
> + temp |= (RECV_DATA << 8);
> + writel(temp, lpi2c_imx->base + LPI2C_MTDR);
> + } else if (!(lpi2c_imx->delivered & 0xff)) {
> + temp = (remaining > CHUNK_DATA ? CHUNK_DATA :
> remaining) - 1;
> + temp |= (RECV_DATA << 8);
> + writel(temp, lpi2c_imx->base + LPI2C_MTDR);
> + }
> +
> + lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE); }
> +
> +static void lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx,
> + struct i2c_msg *msgs)
> +{
> + lpi2c_imx->tx_buf = msgs->buf;
> + lpi2c_imx_set_tx_watermark(lpi2c_imx);
> + lpi2c_imx_write_txfifo(lpi2c_imx);
> +}
> +
> +static void lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx,
> + struct i2c_msg *msgs)
> +{
> + unsigned int temp;
> +
> + lpi2c_imx->rx_buf = msgs->buf;
> + lpi2c_imx->block_data = msgs->flags & I2C_M_RECV_LEN;
> +
> + lpi2c_imx_set_rx_watermark(lpi2c_imx);
> + temp = msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs-
> >len - 1;
> + temp |= (RECV_DATA << 8);
> + writel(temp, lpi2c_imx->base + LPI2C_MTDR);
> +
> + lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE); }
> +
> +static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
> + struct i2c_msg *msgs, int num)
> +{
> + struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(adapter);
> + unsigned int temp;
> + int i, result;
> +
> + result = lpi2c_imx_master_enable(lpi2c_imx);
> + if (result)
> + return result;
> +
> + for (i = 0; i < num; i++) {
> + result = lpi2c_imx_start(lpi2c_imx, &msgs[i]);
> + if (result)
> + goto disable;
> +
> + /* quick smbus */
> + if (num == 1 && msgs[0].len == 0)
> + goto stop;
> +
> + lpi2c_imx->delivered = 0;
> + lpi2c_imx->msglen = msgs[i].len;
> + init_completion(&lpi2c_imx->complete);
> +
> + if (msgs[i].flags & I2C_M_RD)
> + lpi2c_imx_read(lpi2c_imx, &msgs[i]);
> + else
> + lpi2c_imx_write(lpi2c_imx, &msgs[i]);
> +
> + result = lpi2c_imx_msg_complete(lpi2c_imx);
> + if (result)
> + goto stop;
> +
> + if (!(msgs[i].flags & I2C_M_RD)) {
> + result = lpi2c_imx_txfifo_empty(lpi2c_imx);
> + if (result)
> + goto stop;
> + }
> + }
> +
> +stop:
> + lpi2c_imx_stop(lpi2c_imx);
> +
> + temp = readl(lpi2c_imx->base + LPI2C_MSR);
> + if ((temp & MSR_NDF) && !result)
> + result = -EIO;
> +
> +disable:
> + lpi2c_imx_master_disable(lpi2c_imx);
> +
> + dev_dbg(&lpi2c_imx->adapter.dev, "<%s> exit with: %s: %d\n",
> __func__,
> + (result < 0) ? "error" : "success msg",
> + (result < 0) ? result : num);
> +
> + return (result < 0) ? result : num;
> +}
> +
> +static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id) {
> + struct lpi2c_imx_struct *lpi2c_imx = dev_id;
> + unsigned int temp;
> +
> + lpi2c_imx_intctrl(lpi2c_imx, 0);
> + temp = readl(lpi2c_imx->base + LPI2C_MSR);
> +
> + if (temp & MSR_RDF) {
> + lpi2c_imx_read_rxfifo(lpi2c_imx);
> + return IRQ_HANDLED;
> + }
> +
> + if (temp & MSR_TDF) {
> + lpi2c_imx_write_txfifo(lpi2c_imx);
> + return IRQ_HANDLED;
> + }
> +
> + complete(&lpi2c_imx->complete);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static u32 lpi2c_imx_func(struct i2c_adapter *adapter) {
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
> + I2C_FUNC_SMBUS_READ_BLOCK_DATA;
> +}
> +
> +static struct i2c_algorithm lpi2c_imx_algo = {
> + .master_xfer = lpi2c_imx_xfer,
> + .functionality = lpi2c_imx_func,
> +};
> +
> +static const struct of_device_id lpi2c_imx_of_match[] = {
> + { .compatible = "fsl,imx7ulp-lpi2c" },
> + { .compatible = "fsl,imx8dv-lpi2c" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match);
> +
> +static int lpi2c_imx_probe(struct platform_device *pdev) {
> + struct lpi2c_imx_struct *lpi2c_imx;
> + struct resource *res;
> + int irq, ret;
> +
> + lpi2c_imx = devm_kzalloc(&pdev->dev, sizeof(*lpi2c_imx),
> GFP_KERNEL);
> + if (!lpi2c_imx)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + lpi2c_imx->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(lpi2c_imx->base))
> + return PTR_ERR(lpi2c_imx->base);
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + dev_err(&pdev->dev, "can't get irq number\n");
> + return irq;
> + }
> +
> + lpi2c_imx->adapter.owner = THIS_MODULE;
> + lpi2c_imx->adapter.algo = &lpi2c_imx_algo;
> + lpi2c_imx->adapter.dev.parent = &pdev->dev;
> + lpi2c_imx->adapter.dev.of_node = pdev->dev.of_node;
> + strlcpy(lpi2c_imx->adapter.name, pdev->name,
> + sizeof(lpi2c_imx->adapter.name));
> +
> + lpi2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(lpi2c_imx->clk)) {
> + dev_err(&pdev->dev, "can't get I2C peripheral clock\n");
> + return PTR_ERR(lpi2c_imx->clk);
> + }
> +
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "clock-frequency", &lpi2c_imx-
> >bitrate);
> + if (ret)
> + lpi2c_imx->bitrate = LPI2C_DEFAULT_RATE;
> +
> + ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, 0,
> + pdev->name, lpi2c_imx);
> + if (ret) {
> + dev_err(&pdev->dev, "can't claim irq %d\n", irq);
> + return ret;
> + }
> +
> + i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx);
> + platform_set_drvdata(pdev, lpi2c_imx);
> +
> + ret = clk_prepare(lpi2c_imx->clk);
> + if (ret) {
> + dev_err(&pdev->dev, "clk prepare failed %d\n", ret);
> + return ret;
> + }
> +
> + ret = i2c_add_adapter(&lpi2c_imx->adapter);
> + if (ret)
> + goto clk_unprepare;
> +
> + dev_info(&lpi2c_imx->adapter.dev, "LPI2C adapter
> registered\n");
> +
> + return 0;
> +
> +clk_unprepare:
> + clk_unprepare(lpi2c_imx->clk);
> +
> + return ret;
> +}
> +
> +static int lpi2c_imx_remove(struct platform_device *pdev) {
> + struct lpi2c_imx_struct *lpi2c_imx =
> platform_get_drvdata(pdev);
> +
> + i2c_del_adapter(&lpi2c_imx->adapter);
> +
> + clk_unprepare(lpi2c_imx->clk);
> +
> + return 0;
> +}
> +
> +static struct platform_driver lpi2c_imx_driver = {
> + .probe = lpi2c_imx_probe,
> + .remove = lpi2c_imx_remove,
> + .driver = {
> + .name = DRIVER_NAME,
> + .of_match_table = lpi2c_imx_of_match,
> + },
> +};
> +
> +module_platform_driver(lpi2c_imx_driver);
> +
> +MODULE_AUTHOR("Gao Pan <pandy.gao@nxp.com>");
> MODULE_DESCRIPTION("I2C
> +adapter driver for LPI2C bus"); MODULE_LICENSE("GPL v2");
> --
> 1.9.1
^ permalink raw reply
* Re: [PATCH v3 0/7] mux controller abstraction and iio/i2c muxes
From: Peter Rosin @ 2016-11-29 16:02 UTC (permalink / raw)
To: Jonathan Cameron, Lars-Peter Clausen,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <d287dc3b-3605-d85d-1aa6-40f3bdcdc72c-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On 2016-11-27 13:00, Jonathan Cameron wrote:
> On 23/11/16 11:47, Peter Rosin wrote:
>> On 2016-11-22 21:58, Lars-Peter Clausen wrote:
>>> On 11/21/2016 02:17 PM, Peter Rosin wrote:
>>> [...]
>>>> I have a piece of hardware that is using the same 3 GPIO pins
>>>> to control four 8-way muxes. Three of them control ADC lines
>>>> to an ADS1015 chip with an iio driver, and the last one
>>>> controls the SDA line of an i2c bus. We have some deployed
>>>> code to handle this, but you do not want to see it or ever
>>>> hear about it. I'm not sure why I even mention it. Anyway,
>>>> the situation has nagged me to no end for quite some time.
>>>>
>>>> So, after first getting more intimate with the i2c muxing code
>>>> and later discovering the drivers/iio/inkern.c file and
>>>> writing a couple of drivers making use of it, I came up with
>>>> what I think is an acceptable solution; add a generic mux
>>>> controller driver (and subsystem) that is shared between all
>>>> instances, and combine that with an iio mux driver and a new
>>>> generic i2c mux driver. The new i2c mux I called "simple"
>>>> since it is only hooking the i2c muxing and the new mux
>>>> controller (much like the alsa simple card driver does for ASoC).
>>>
>>> While abstracting this properly is all nice and good and the way it should
>>> be done, but it also adds a lot of complexity and the devicetree adds a lot
>>> of restrictions on what can actually be represented.
>>
>> This is a characterization without any specifics. But is the
>> characterization true? You have two complaints, complexity
>> and restrictions with bindings.
>>
>>> There is a certain point where the fabric on a PCB becomes so complex that
>>> it deserves to be a device on its own (like the audio fabric drivers).
>>> Especially when the hardware is built with a certain application in mind and
>>> the driver is supposed to impose policy which reflects this application. The
>>> latter can often not properly be described with the primitives the
>>> devicetree can offer.
>>>
>>> And I think your setup is very borderline what can be done in a declarative
>>> way only and it adds a lot of complexity over a more imperative solution in
>>> form of a driver. I think it is worth investigating about having a driver
>>> that is specific to your fabric and handles the interdependencies of the
>>> discrete components.
>>
>> So, there are three "new" concepts:
>>
>> 1. Sticking a mux in front of an AD-converter. That's not all that
>> novel, nor complex. Quite the opposite, I'd say. In fact, I find it
>> a bit amazing that there is no in-kernel support for it.
> As ever first person who needs it and has the skills to write it gets to do it ;)
> Congratulations Peter ;)
>>
>> 2. Reusing the same GPIO-pins to drive different muxes. There are
>> obviously chips that work this way (as Jonathan pointed out) and
>> these will at some point get used in Linux devices. I guess they
>> already are used, but that people handle them in userspace. Or
>> something? If this is complex, which I question, it will still need
>> support at some point. At least that's what I believe.
>>
>> 3. Using the same GPIO pins to mux things handled by different
>> subsystems. Right, this is a bit crazy, and I'd rather not have this
>> requirement, but this HW is what it is so I'll need to handle it in
>> some way. It is also what stops me from falling back to a userspace
>> solution, which is probably connected to why #1 and #2 is not supported
>> by the kernel; everybody probably does muxing in userspace. Which is
>> not necessarily a good idea, nor how it's supposed to be done...
>>
>> So, the only thing that's out of the ordinary (as I see it), is #3.
>> The question that then needs an answer is how the in-kernel solution
>> for #1 and #2 would look if we do not consider #3.
>>
>> And I claim that the desired solution to #1 and #2 is pretty close
>> to my proposal.
>>
>> A. You do not want mux-controller drivers in every subsystem that
>> needs them.
> Agreed.
>>
>> B. You do not want every mux-consumer to know the specifics of how to
>> operate every kind of mux; there are muxes that are not controlled
>> with GPIO pins...
>>
>> C. When you implement muxing in a subsystem, there will in some cases
>> be a need to handle parallel muxing, where there is a need to share
>> mux-controllers.
>>
>> It just feels right to separate out the mux-controller and refer to
>> it from where a mux is needed. It solves #1 and #2. And, of course,
>> as a bonus #3 is also solved. But my bias is obvious.
>>
>> And that leads us to the restrictions with the bindings. And the same
>> thing happens; the solution for #2 also solves #3.
>>
>> So how do you refer to a mux-controller from where it's needed? My
>> first proposal used a dt phandle, for the second round I put them in
>> the parent node. It would be super if it was possible for the mux-
>> consumer to create the mux-controller device from the same dt
>> node that is already bound to the mux-consumer. The problem is that
>> the mux-consumer should not hard-code which mux-controller device it
>> should create. The best I can think of is some kind of 'mux-compatible'
>> attribute, that works like the standard 'compatible' attribute. That
>> would simplify the bindings for the normal case (#1) where the mux-
>> controller isn't shared (#2 and #3). Maybe it's possible to fix this
>> issue somehow? I simply don't know?
> As Lars stated, it's marginal. The question is not at what point do we
> 'have to' bother with a fabric driver, but rather at what point does it
> make a our lives easier.
>
> Take you nastiest mux case described earlier.
> The ideal would be to represent the ADC and 3 muxes as (approximately) a
> single ADC to userspace that just happens to have somewhere near 23 inputs.
>
> To do that in device tree we need to describe
>
> 1 The adc
> 2 The three muxes
> 3 The software representation to pull all of these back into a single device.
>
> That last part to my mind trips the balance to the point where a fabric driver
> would make sense. It's not complex. Just a few lines of code tying all the
> elements together without ending up with a fairly fiendish setup to describe in
> device tree.
>
> Also just wait until we have muxes stacked on muxes, with cross overs occuring.
> Some of the ASoC parts can actually have effective loops if you try all the mux
> combinations.
>
> So question is do we have a 'simple case description' in device tree or force
> fabric drivers everywhere? I think I'm in favour of the simple case - which handles
> one of your two uses nicely. The second one to do the the recombining of channels after
> the muxes, ends up looking to me like it needs a fabric driver.
>
> Note we are only talking about bindings vs code based description here. I agree
> entirely with the concept of a generic mux subsystem.
Ok, take the simple case - an adc with a mux in front of it.
We do not want to build a whole new iio-mux subsystem like the one under
i2c, so from iio we want to refer to the actual mux controller driver
which lives under the mux subsystem. Getting this far is what solves my
"second need" [2] from the v2 thread.
Agreed, doing this w/o a fabric driver spills the guts and it might be
cleaner to build a fabric driver that takes a reference to the dpot and
the mux controller and just knows the rest. In the end this fabric driver
requires two things to actually make a difference; some way to instantiate
drivers without the help of device-tree and some way make those drivers
only provide in-kernel access (and preferably it should hide the dpot from
userspace too, while at it).
But doing all that in a fabric driver is not going to change the fact that
the iio-mux driver is useful on its own. I bet someone else is going to
reuse it somewhere down the line. Which means that a fabric driver would
perhaps be nice for my "second need", but not critical, it works pretty
well to just piggyback on the general solution .
Over to my "first need" [1] from the v2 thread.
The above iio-mux driver handles the three muxed adc lines beautifully,
just refer all three iio-muxes to the same mux controller. Agreed, with
a fabric driver I could get one device with 25 channels instead of three
devices with 8 channels each plus one unmuxed line directly from the adc
device. However, that doesn't bother me at all, I may even think it is
preferable because otherwise I'd have to come up with some way to
identify which channel is which in that big 25-channel jungle. Another
drawback with having a fabric driver here is that it would need to be an
i2c-mux driver, because one of the key points of doing a fabric driver
for my first need was to hide the shared mux, right? Instead, I have the
new i2c-mux-simple driver that builds an i2c-mux using any mux controller
from the mux subsystem (something that may prove useful to others in the
future), which in my case is the same mux controller that also muxes the
three adc lines.
In short, doing fabric drivers buys me almost nothing besides having a
slightly more distinct device tree. All the components used to describe
this are either already accepted drivers or usable by others (at least
the way I see it).
Cheers,
Peter
[1] three adc lines and an i2c bus muxed with the same gpio pins
[2] mcp4561 dpot -> dpot-dac -> envelope-detector-adc -> iio-mux
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5 4/9] dt-bindings: iio: iio-mux: document iio-mux bindings
From: Peter Rosin @ 2016-11-29 15:56 UTC (permalink / raw)
To: linux-kernel
Cc: 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: <1480414245-14034-5-git-send-email-peda@axentia.se>
On 2016-11-29 11:10, Peter Rosin wrote:
> +Example:
> + mux: mux-controller {
> + compatible = "mux-gpio";
> + #mux-control-cells = <0>;
> +
> + mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
> + <&pioA 1 GPIO_ACTIVE_HIGH>;
> + };
> +
> + adc-mux {
> + compatible = "iio-mux";
> + io-channels = <&adc 0>;
> + io-channel-names = "parent";
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + mux-controls = <&mux>;
> +
> + sync@0 {
> + reg = <0>;
> + };
> +
> + in@1 {
> + reg = <1>;
> + };
> +
> + system-regulator@2 {
> + reg = <2>;
> + };
> + };
Hmmm, a more compact binding would be to just use an array of strings
instead of a list of children for the mux channels, and use the array
index as channel number, like so:
adc-mux {
compatible = "iio-mux";
io-channels = <&adc 0>;
io-channel-names = "parent";
mux-controls = <&mux>;
channels = "sync", "in", "system-regulator";
};
If you need to skip a low-number channel, you'd just put an empty string
for that channel. If you need to skip channels at the end, just stop
short.
Can anyone think of any reason to add anything to the channel nodes
that makes the string-array ineffective? If so, or if that comes up
later, it could be optional and in that case you could look for the
channels property first and then, if not present, iterate over child
nodes.
Opinions? I like it, it's a lot more compact...
Cheers,
Peter
^ permalink raw reply
* Re: [PATCH V5] i2c: mux: pca954x: Add ACPI support for pca954x
From: Peter Rosin @ 2016-11-29 15:50 UTC (permalink / raw)
To: Tin Huynh, Mika Westerberg
Cc: Wolfram Sang, linux-i2c, linux-kernel, linux-acpi, Loc Ho,
Thang Nguyen, Phong Vo, patches
In-Reply-To: <CANQ9TuC64k8Ub_Pn01aJkQqL6kKrcXaJp35bPJR6c5WScUHj9A@mail.gmail.com>
On 2016-11-29 16:12, Tin Huynh wrote:
> On Tue, Nov 29, 2016 at 5:15 PM, Peter Rosin <peda@axentia.se> wrote:
>> On 2016-11-29 11:12, Tin Huynh wrote:
>>> This patch enables ACPI support for mux-pca954x driver.
>>
>> Looks good, thanks!
>>
>>> Signed-off-by: Tin Huynh <tnhuynh@apm.com>
>>
>> Acked-by: Peter Rosin <peda@axentia.se>
>>
>> Cheers,
>> Peter
>>
> Added Mika
> However , in http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/drivers/gpio/gpio-pca953x.c?id=f32517bf1ae0a2de72b3f27200233bd3ad65bfeb
> log , i didn't see the CONFIG_ACPI and CONFIG_PTR.
> I have been acked with the same patch to support for i2c-leds (
> without CONFIG_ACPI)
> About logic , if we don't enable CONFIG_ACPI , acpi_match_table will
> be NULL . So , the probe functions will be not called
> and nothing happens.
> I though we should use V3 (https://lkml.org/lkml/2016/11/28/893)
>
V3 will work, but you waste resources (not much, but still) for the
ACPI table even though it is never going to be used, so it's best
to #ifdef it out. (At least I think so, but maybe GCC sees that the
variable isn't really used for anything useful and manages to kill
it off? Anyway, I'm assuming that there really are resources wasted
if you don't do the #ifdef...)
But the thing is that you do not want a bunch of #ifdefs *in* the
code. It's ok to #ifdef out a variable, but not *in* the code. That
way lies madness.
So the code is going to have a call to acpi_match_device(). If
CONFIG_ACPI isn't set, that function compiles to an inline function
simply returning NULL, but the compiler still evaluates its args
and complains if the above variable is not found, which it isn't
since it's #ifdef out. So, we need to "hide" the variable if it
doesn't exist, using the ACPI_PTR macro.
For gpio-pca953x, the same reasoning can be used, but the resource
waste is even less in that case.
Cheers,
Peter
^ permalink raw reply
* Re: [PATCH V5] i2c: mux: pca954x: Add ACPI support for pca954x
From: Tin Huynh @ 2016-11-29 15:12 UTC (permalink / raw)
To: Peter Rosin, Mika Westerberg
Cc: Wolfram Sang, linux-i2c, linux-kernel, linux-acpi, Loc Ho,
Thang Nguyen, Phong Vo, patches
In-Reply-To: <d1576b0e-75e7-a8e7-b752-301d125ea765@axentia.se>
On Tue, Nov 29, 2016 at 5:15 PM, Peter Rosin <peda@axentia.se> wrote:
> On 2016-11-29 11:12, Tin Huynh wrote:
>> This patch enables ACPI support for mux-pca954x driver.
>
> Looks good, thanks!
>
>> Signed-off-by: Tin Huynh <tnhuynh@apm.com>
>
> Acked-by: Peter Rosin <peda@axentia.se>
>
> Cheers,
> Peter
>
Added Mika
However , in http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/drivers/gpio/gpio-pca953x.c?id=f32517bf1ae0a2de72b3f27200233bd3ad65bfeb
log , i didn't see the CONFIG_ACPI and CONFIG_PTR.
I have been acked with the same patch to support for i2c-leds (
without CONFIG_ACPI)
About logic , if we don't enable CONFIG_ACPI , acpi_match_table will
be NULL . So , the probe functions will be not called
and nothing happens.
I though we should use V3 (https://lkml.org/lkml/2016/11/28/893)
^ permalink raw reply
* Re: [PATCH V5] i2c: mux: pca954x: Add ACPI support for pca954x
From: Peter Rosin @ 2016-11-29 10:15 UTC (permalink / raw)
To: Tin Huynh, Wolfram Sang
Cc: linux-i2c, linux-kernel, linux-acpi, Loc Ho, Thang Nguyen,
Phong Vo, patches
In-Reply-To: <1480414363-16318-1-git-send-email-tnhuynh@apm.com>
On 2016-11-29 11:12, Tin Huynh wrote:
> This patch enables ACPI support for mux-pca954x driver.
Looks good, thanks!
> Signed-off-by: Tin Huynh <tnhuynh@apm.com>
Acked-by: Peter Rosin <peda@axentia.se>
Cheers,
Peter
^ permalink raw reply
* [PATCH V5] i2c: mux: pca954x: Add ACPI support for pca954x
From: Tin Huynh @ 2016-11-29 10:12 UTC (permalink / raw)
To: Wolfram Sang, Peter Rosin
Cc: linux-i2c, linux-kernel, linux-acpi, Loc Ho, Thang Nguyen,
Phong Vo, patches, Tin Huynh
This patch enables ACPI support for mux-pca954x driver.
Signed-off-by: Tin Huynh <tnhuynh@apm.com>
---
drivers/i2c/muxes/i2c-mux-pca954x.c | 28 +++++++++++++++++++++++++++-
1 files changed, 27 insertions(+), 1 deletions(-)
Change from V4:
-Add ACPI_PTR.
Change from V3:
-Readd CONFIG_ACPI.
Change from V2:
-Remove CONFIG_ACPI.
Change from V1 :
-Don't shadow id variable.
-Include sorted header.
-Redefine acpi_device_id.
-Add CONFIG_ACPI.
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index 1091346..f81f8b5 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -35,6 +35,7 @@
* warranty of any kind, whether express or implied.
*/
+#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
@@ -120,6 +121,21 @@ struct pca954x {
};
MODULE_DEVICE_TABLE(i2c, pca954x_id);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id pca954x_acpi_ids[] = {
+ { .id = "PCA9540", .driver_data = pca_9540 },
+ { .id = "PCA9542", .driver_data = pca_9540 },
+ { .id = "PCA9543", .driver_data = pca_9543 },
+ { .id = "PCA9544", .driver_data = pca_9544 },
+ { .id = "PCA9545", .driver_data = pca_9545 },
+ { .id = "PCA9546", .driver_data = pca_9545 },
+ { .id = "PCA9547", .driver_data = pca_9547 },
+ { .id = "PCA9548", .driver_data = pca_9548 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, pca954x_acpi_ids);
+#endif
+
#ifdef CONFIG_OF
static const struct of_device_id pca954x_of_match[] = {
{ .compatible = "nxp,pca9540", .data = &chips[pca_9540] },
@@ -245,8 +261,17 @@ static int pca954x_probe(struct i2c_client *client,
match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
if (match)
data->chip = of_device_get_match_data(&client->dev);
- else
+ else if (id)
data->chip = &chips[id->driver_data];
+ else {
+ const struct acpi_device_id *acpi_id;
+
+ acpi_id = acpi_match_device(ACPI_PTR(pca954x_acpi_ids),
+ &client->dev);
+ if (!acpi_id)
+ return -ENODEV;
+ data->chip = &chips[acpi_id->driver_data];
+ }
data->last_chan = 0; /* force the first selection */
@@ -321,6 +346,7 @@ static int pca954x_resume(struct device *dev)
.name = "pca954x",
.pm = &pca954x_pm,
.of_match_table = of_match_ptr(pca954x_of_match),
+ .acpi_match_table = ACPI_PTR(pca954x_acpi_ids),
},
.probe = pca954x_probe,
.remove = pca954x_remove,
--
1.7.1
^ permalink raw reply related
* [PATCH v5 3/9] iio: inkern: api for manipulating ext_info of iio channels
From: Peter Rosin @ 2016-11-29 10:10 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: <1480414245-14034-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 | 60 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/iio/consumer.h | 37 +++++++++++++++++++++++++++
2 files changed, 97 insertions(+)
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index b0f4630a163f..4848b8129e6c 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -863,3 +863,63 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
return ret;
}
EXPORT_SYMBOL_GPL(iio_write_channel_raw);
+
+unsigned 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);
+
+static const struct iio_chan_spec_ext_info *iio_lookup_ext_info(
+ const struct iio_channel *chan,
+ const char *attr)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ if (!chan->channel->ext_info)
+ return NULL;
+
+ for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) {
+ if (!strcmp(attr, ext_info->name))
+ return ext_info;
+ }
+
+ return NULL;
+}
+
+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;
+
+ ext_info = iio_lookup_ext_info(chan, attr);
+ if (!ext_info)
+ return -EINVAL;
+
+ return ext_info->read(chan->indio_dev, ext_info->private,
+ chan->channel, buf);
+}
+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;
+
+ ext_info = iio_lookup_ext_info(chan, attr);
+ if (!ext_info)
+ return -EINVAL;
+
+ return ext_info->write(chan->indio_dev, ext_info->private,
+ chan->channel, buf, len);
+}
+EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index 47eeec3218b5..5e347a9805fd 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -312,4 +312,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
+ */
+unsigned 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 v5 9/9] misc: mux-adg792a: add mux controller driver for ADG792A/G
From: Peter Rosin @ 2016-11-29 10:10 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
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-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1480414245-14034-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Analog Devices ADG792A/G is a triple 4:1 mux.
Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---
drivers/misc/Kconfig | 12 ++++
drivers/misc/Makefile | 1 +
drivers/misc/mux-adg792a.c | 154 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 167 insertions(+)
create mode 100644 drivers/misc/mux-adg792a.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2ce675e410c5..45567a444bbf 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -780,6 +780,18 @@ menuconfig MULTIPLEXER
if MULTIPLEXER
+config MUX_ADG792A
+ tristate "Analog Devices ADG792A/ADG792G Multiplexers"
+ depends on I2C
+ help
+ ADG792A and ADG792G Wide Bandwidth Triple 4:1 Multiplexers
+
+ The driver supports both operating the three multiplexers in
+ parellel and operating them independently.
+
+ To compile the driver as a module, choose M here: the module will
+ be called mux-adg792a.
+
config MUX_GPIO
tristate "GPIO-controlled Multiplexer"
depends on OF && GPIOLIB
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 0befa2bba762..10ab8d34c9e5 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -54,6 +54,7 @@ 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_ADG792A) += mux-adg792a.o
obj-$(CONFIG_MUX_GPIO) += mux-gpio.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
diff --git a/drivers/misc/mux-adg792a.c b/drivers/misc/mux-adg792a.c
new file mode 100644
index 000000000000..279dcf381695
--- /dev/null
+++ b/drivers/misc/mux-adg792a.c
@@ -0,0 +1,154 @@
+/*
+ * Multiplexer driver for Analog Devices ADG792A/G Triple 4:1 mux
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mux.h>
+
+#define ADG792A_LDSW BIT(0)
+#define ADG792A_RESET BIT(1)
+#define ADG792A_DISABLE(mux) (0x50 | (mux))
+#define ADG792A_DISABLE_ALL (0x5f)
+#define ADG792A_MUX(mux, state) (0xc0 | (((mux) + 1) << 2) | (state))
+#define ADG792A_MUX_ALL(state) (0xc0 | (state))
+
+#define ADG792A_DISABLE_STATE (4)
+#define ADG792A_KEEP_STATE (5)
+
+static int adg792a_set(struct mux_control *mux, int state)
+{
+ struct i2c_client *i2c = to_i2c_client(mux->chip->dev.parent);
+ u8 cmd;
+
+ if (mux->chip->controllers == 1) {
+ /* parallel mux controller operation */
+ if (state == ADG792A_DISABLE_STATE)
+ cmd = ADG792A_DISABLE_ALL;
+ else
+ cmd = ADG792A_MUX_ALL(state);
+ } else {
+ unsigned int controller = mux_control_get_index(mux);
+
+ if (state == ADG792A_DISABLE_STATE)
+ cmd = ADG792A_DISABLE(controller);
+ else
+ cmd = ADG792A_MUX(controller, state);
+ }
+
+ return i2c_smbus_write_byte_data(i2c, cmd, ADG792A_LDSW);
+}
+
+static const struct mux_control_ops adg792a_ops = {
+ .set = adg792a_set,
+};
+
+static int adg792a_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct mux_chip *mux_chip;
+ bool parallel;
+ int ret;
+ int i;
+
+ parallel = of_property_read_bool(i2c->dev.of_node, "adi,parallel");
+
+ mux_chip = mux_chip_alloc(dev, parallel ? 1 : 3, 0);
+ if (!mux_chip)
+ return -ENOMEM;
+
+ mux_chip->ops = &adg792a_ops;
+ dev_set_drvdata(dev, mux_chip);
+
+ ret = i2c_smbus_write_byte_data(i2c, ADG792A_DISABLE_ALL,
+ ADG792A_RESET | ADG792A_LDSW);
+ if (ret < 0)
+ goto free_mux_chip;
+
+ for (i = 0; i < mux_chip->controllers; ++i) {
+ struct mux_control *mux = &mux_chip->mux[i];
+ u32 idle_state;
+
+ mux->states = 4;
+
+ ret = of_property_read_u32_index(i2c->dev.of_node,
+ "adi,idle-state", i,
+ &idle_state);
+ if (ret >= 0) {
+ if (idle_state > ADG792A_KEEP_STATE) {
+ dev_err(dev, "invalid idle-state %u\n",
+ idle_state);
+ ret = -EINVAL;
+ goto free_mux_chip;
+ }
+ if (idle_state != ADG792A_KEEP_STATE)
+ mux->idle_state = idle_state;
+ }
+ }
+
+ ret = mux_chip_register(mux_chip);
+ if (ret < 0) {
+ dev_err(dev, "failed to register mux-chip\n");
+ goto free_mux_chip;
+ }
+
+ if (parallel)
+ dev_info(dev, "1 triple 4-way mux-controller registered\n");
+ else
+ dev_info(dev, "3 4-way mux-controllers registered\n");
+
+ return 0;
+
+free_mux_chip:
+ mux_chip_free(mux_chip);
+ return ret;
+}
+
+static int adg792a_remove(struct i2c_client *i2c)
+{
+ struct mux_chip *mux_chip = to_mux_chip(&i2c->dev);
+
+ mux_chip_unregister(mux_chip);
+ mux_chip_free(mux_chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id adg792a_id[] = {
+ { .name = "adg792a", },
+ { .name = "adg792g", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adg792a_id);
+
+static const struct of_device_id adg792a_of_match[] = {
+ { .compatible = "adi,adg792a", },
+ { .compatible = "adi,adg792g", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adg792a_of_match);
+
+static struct i2c_driver adg792a_driver = {
+ .driver = {
+ .name = "adg792a",
+ .of_match_table = of_match_ptr(adg792a_of_match),
+ },
+ .probe = adg792a_probe,
+ .remove = adg792a_remove,
+ .id_table = adg792a_id,
+};
+module_i2c_driver(adg792a_driver);
+
+MODULE_DESCRIPTION("Analog Devices ADG792A/G Triple 4:1 mux driver");
+MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org");
+MODULE_LICENSE("GPL v2");
--
2.1.4
--
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 8/9] dt-bindings: mux-adg792a: document devicetree bindings for ADG792A/G mux
From: Peter Rosin @ 2016-11-29 10:10 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
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-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1480414245-14034-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Analog Devices ADG792A/G is a triple 4:1 mux.
Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---
.../devicetree/bindings/misc/mux-adg792a.txt | 82 ++++++++++++++++++++++
1 file changed, 82 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/mux-adg792a.txt
diff --git a/Documentation/devicetree/bindings/misc/mux-adg792a.txt b/Documentation/devicetree/bindings/misc/mux-adg792a.txt
new file mode 100644
index 000000000000..8d5c9de09832
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/mux-adg792a.txt
@@ -0,0 +1,82 @@
+Bindings for Analog Devices ADG792A/G Triple 4:1 Multiplexers
+
+Required properties:
+- compatible : "adi,adg792a" or "adi,adg792g"
+- #mux-control-cells : <0> if parallel, or <1> if not.
+* Standard mux-controller bindings as decribed in mux-controller.txt
+
+Optional properties:
+- adi,parallel : if present, the three muxes are bound together with a single
+ mux controller, controlling all three muxes in parallel.
+- adi,idle-state : if present, array of states the three mux controllers will
+ have when idle (or, if parallel, a single idle-state).
+
+Mux controller states 0 through 3 correspond to signals A through D in the
+datasheet. Mux controller states 4 and 5 are only available as possible idle
+states. State 4 represents that nothing is connected, and state 5 represents
+that the mux controller keeps the mux in its previously selected state during
+the idle period. State 5 is the default idle state.
+
+Example:
+
+ /* three independent mux controllers (of which one is used) */
+ &i2c0 {
+ mux: adg792a@50 {
+ compatible = "adi,adg792a";
+ reg = <0x50>;
+ #mux-control-cells = <1>;
+ };
+ };
+
+ adc-mux {
+ compatible = "iio-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ mux-controls = <&mux 1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sync-1@0 {
+ reg = <0>;
+ };
+
+ in@2 {
+ reg = <2>;
+ };
+ };
+
+
+ /*
+ * Three parallel muxes with one mux controller, useful e.g. if
+ * the adc is differential, thus needing two signals to be muxed
+ * simultaneously for correct operation.
+ */
+ &i2c0 {
+ pmux: adg792a@50 {
+ compatible = "adi,adg792a";
+ reg = <0x50>;
+ #mux-control-cells = <0>;
+ adi,parallel;
+ };
+ };
+
+ diff-adc-mux {
+ compatible = "iio-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ mux-controls = <&pmux>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sync-1@0 {
+ reg = <0>;
+ };
+
+ in@2 {
+ reg = <2>;
+ };
+ };
--
2.1.4
--
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 7/9] i2c: i2c-mux-simple: new driver
From: Peter Rosin @ 2016-11-29 10:10 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
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-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1480414245-14034-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
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-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---
drivers/i2c/muxes/Kconfig | 13 +++
drivers/i2c/muxes/Makefile | 1 +
drivers/i2c/muxes/i2c-mux-simple.c | 179 +++++++++++++++++++++++++++++++++++++
3 files changed, 193 insertions(+)
create mode 100644 drivers/i2c/muxes/i2c-mux-simple.c
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index 10b3d17ae3ea..565921e09a96 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 9948fa45037f..6821d95c92a3 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -11,5 +11,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..4a03493e1ad7
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-mux-simple.c
@@ -0,0 +1,179 @@
+/*
+ * Generic simple I2C multiplexer
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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, NULL);
+ 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-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
--
2.1.4
--
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 6/9] dt-bindings: i2c: i2c-mux-simple: document i2c-mux-simple bindings
From: Peter Rosin @ 2016-11-29 10:10 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: <1480414245-14034-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../devicetree/bindings/i2c/i2c-mux-simple.txt | 81 ++++++++++++++++++++++
1 file changed, 81 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..18e023d52933
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt
@@ -0,0 +1,81 @@
+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.
+- mux-controls: The phandle of the mux controller to use for operating the
+ mux.
+* 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: mux-controller {
+ compatible = "mux-gpio";
+ #mux-control-cells = <0>;
+
+ 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>;
+
+ mux-controls = <&mux>;
+
+ 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 v5 5/9] iio: multiplexer: new iio category and iio-mux driver
From: Peter Rosin @ 2016-11-29 10:10 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: <1480414245-14034-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
to 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 | 457 ++++++++++++++++++++++++++++++++++++++
6 files changed, 484 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 77045ae15865..16490fbd1721 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6239,6 +6239,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 a918270d6f54..b3c8c6ef0dff 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -83,6 +83,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 33fa4026f92c..93c769cd99bf 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -28,6 +28,7 @@ obj-y += humidity/
obj-y += imu/
obj-y += light/
obj-y += magnetometer/
+obj-y += multiplexer/
obj-y += orientation/
obj-y += potentiometer/
obj-y += potentiostat/
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..3ea4acdb45e2
--- /dev/null
+++ b/drivers/iio/multiplexer/iio-mux.c
@@ -0,0 +1,457 @@
+/*
+ * 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 = 0;
+ for_each_child_of_node(np, child_np) {
+ if (of_get_property(child_np, "reg", NULL))
+ children++;
+ }
+ 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, NULL);
+ 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) {
+ if (!of_get_property(child_np, "reg", NULL))
+ continue;
+
+ 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 v5 4/9] dt-bindings: iio: iio-mux: document iio-mux bindings
From: Peter Rosin @ 2016-11-29 10:10 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: <1480414245-14034-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../bindings/iio/multiplexer/iio-mux.txt | 51 ++++++++++++++++++++++
MAINTAINERS | 6 +++
2 files changed, 57 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..403912631dcf
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
@@ -0,0 +1,51 @@
+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>;
+- mux-controls : Mux controller node to use for operating the mux
+
+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: mux-controller {
+ compatible = "mux-gpio";
+ #mux-control-cells = <0>;
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+ };
+
+ adc-mux {
+ compatible = "iio-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mux-controls = <&mux>;
+
+ sync@0 {
+ reg = <0>;
+ };
+
+ in@1 {
+ reg = <1>;
+ };
+
+ system-regulator@2 {
+ reg = <2>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index dc7498682752..77045ae15865 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6234,6 +6234,12 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
F: drivers/iio/adc/envelope-detector.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
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