* Re: [PATCH v6 9/9] misc: mux-adg792a: add mux controller driver for ADG792A/G
From: Jonathan Cameron @ 2017-01-01 11:24 UTC (permalink / raw)
To: Peter Rosin, linux-kernel
Cc: Wolfram Sang, Rob Herring, Mark Rutland, 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: <1480493823-21462-10-git-send-email-peda@axentia.se>
On 30/11/16 08:17, Peter Rosin wrote:
> Analog Devices ADG792A/G is a triple 4:1 mux.
>
> Signed-off-by: Peter Rosin <peda@axentia.se>
Looks pretty good. Some minor suggestions inline.
This convinced me of two things:
1. Need a separate subsystem directory for muxes - having them under misc
is going to lead to long term mess.
2. Devm alloc and registration functions will make the drivers all simpler.
Also, browsing through ADIs list of muxes and switches it's clear that
one classic case will be where an i2c octal or similar switch is used with
outputs wired together in weird combinations to act as a mux. Going to
be 'fun' describing that.
There are also potentially cross point switches to be described ;)
(I had to look up what one of those was ;)
Crosspoints aren't implausible as front ends for ADCs as you might
want to be able rapidly sample any 2 of say 16 channels coming from
for example a max14661. We'd have to figure out how to add buffered
capture support with sensible restrictions to the iio-mux driver
to do that - realistically I think we would just not allow buffered
capture with the mux having to switch. Without hardware support
(i.e. an ADC with external mux control) it would be too slow.
Always good to bury some idle thoughts deep in the review of a random
driver ;) I'll never be able to remember where they were let alone
anyone else.
Jonathan
> ---
> 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.
parallel
> +
> + 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..7d309a78af65
> --- /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@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/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);
This makes me wonder if we can have a more generic binding.
mux-poles = 3 vs mux-poles = 1?
> + 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");
I'd use the relay / switch standard description for this so
'triple pole, quadruple throw mux registered'.
> + else
> + dev_info(dev, "3 4-way mux-controllers registered\n");
'3x single pole, quadruple throw muxes registered'.
> +
> + 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 = dev_get_drvdata(&i2c->dev);
> +
Definitely looking like it's worth managed versions of mux_chip_register and
mux_chip_alloc given this is another case where they would let us get rid
of the remove function entirely.
> + 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@axentia.se");
> +MODULE_LICENSE("GPL v2");
>
^ permalink raw reply
* Re: [PATCH v6 8/9] dt-bindings: mux-adg792a: document devicetree bindings for ADG792A/G mux
From: Jonathan Cameron @ 2017-01-01 11:00 UTC (permalink / raw)
To: Peter Rosin, linux-kernel
Cc: Wolfram Sang, Rob Herring, Mark Rutland, 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: <1480493823-21462-9-git-send-email-peda@axentia.se>
On 30/11/16 08:17, Peter Rosin wrote:
> Analog Devices ADG792A/G is a triple 4:1 mux.
>
> Signed-off-by: Peter Rosin <peda@axentia.se>
Few comments inline. Worth adding anything about the gpio (output pins) to
the binding at this stage as well? Would certainly be nice to support
them.
Jonathan
> ---
> .../devicetree/bindings/misc/mux-adg792a.txt | 64 ++++++++++++++++++++++
> 1 file changed, 64 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..4677f9ab1c55
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/misc/mux-adg792a.txt
> @@ -0,0 +1,64 @@
> +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).
Hmm. These are actually a policy decision. As only one policy will make
sense for a given set of hardware probably fine to have it in here I guess.
Might be worth adding a note to say this though.
> +
> +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.
I'm never a great fan of magic numbers. Can we represent this more cleanly by
breaking it into multiple properties?
Optional:
adi,idle-switch-to-channel : switch to this channel when idle.
adi,idle-high-impedance : <boolean> the nothing connected state?
If neither present leaves it in previous 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>;
> +
> + channels = "sync-1", "", "out";
> + };
> +
> +
> + /*
> + * 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>;
> +
> + channels = "sync-1", "", "out";
> + };
>
^ permalink raw reply
* Re: [PATCH v6 7/9] i2c: i2c-mux-simple: new driver
From: Jonathan Cameron @ 2017-01-01 10:31 UTC (permalink / raw)
To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Wolfram Sang, Rob Herring, Mark Rutland, 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: <1480493823-21462-8-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
On 30/11/16 08:17, Peter Rosin wrote:
> 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>
Looks good to me though as I haven't really commented on it I'll give
an Ack rather than a reviewed by.
Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@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");
>
^ permalink raw reply
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver
From: kbuild test robot @ 2017-01-01 2:02 UTC (permalink / raw)
To: Tony Lindgren
Cc: kbuild-all-JC7UmRfGjtg, Linus Walleij, Gary Bisson,
Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA, Lokesh Vutla
In-Reply-To: <20161230183732.5595-3-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 8995 bytes --]
Hi Nishanth,
[auto build test ERROR on pinctrl/for-next]
[also build test ERROR on v4.10-rc1 next-20161224]
[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/Tony-Lindgren/Add-TI-iodelay-driver-using-pinctrl-cells/20161231-024542
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git for-next
config: i386-allyesconfig (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=i386
All errors (new ones prefixed by >>):
drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_get_pingroup':
drivers/pinctrl/ti/pinctrl-ti-iodelay.c:383:6: error: implicit declaration of function 'pinctrl_generic_get_group' [-Werror=implicit-function-declaration]
g = pinctrl_generic_get_group(iod->pctl, selector);
^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/ti/pinctrl-ti-iodelay.c:383:4: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
g = pinctrl_generic_get_group(iod->pctl, selector);
^
>> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:391:10: error: dereferencing pointer to incomplete type 'struct group_desc'
return g->data;
^~
drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_dt_node_to_map':
>> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:562:10: error: implicit declaration of function 'pinctrl_generic_add_group' [-Werror=implicit-function-declaration]
error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g);
^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/ti/pinctrl-ti-iodelay.c: At top level:
drivers/pinctrl/ti/pinctrl-ti-iodelay.c:729:22: error: 'pinctrl_generic_get_group_count' undeclared here (not in a function)
.get_groups_count = pinctrl_generic_get_group_count,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:730:20: error: 'pinctrl_generic_get_group_name' undeclared here (not in a function)
.get_group_name = pinctrl_generic_get_group_name,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:731:20: error: 'pinctrl_generic_get_group_pins' undeclared here (not in a function)
.get_group_pins = pinctrl_generic_get_group_pins,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_get_pingroup':
drivers/pinctrl/ti/pinctrl-ti-iodelay.c:392:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
cc1: some warnings being treated as errors
vim +391 drivers/pinctrl/ti/pinctrl-ti-iodelay.c
377 */
378 static struct ti_iodelay_pingroup *
379 ti_iodelay_get_pingroup(struct ti_iodelay_device *iod, unsigned int selector)
380 {
381 struct group_desc *g;
382
> 383 g = pinctrl_generic_get_group(iod->pctl, selector);
384 if (!g) {
385 dev_err(iod->dev, "%s could not find pingroup %i\n", __func__,
386 selector);
387
388 return NULL;
389 }
390
> 391 return g->data;
392 }
393
394 /**
395 * ti_iodelay_pin_to_offset() - get pin register offset based on the pin index
396 * @iod: iodelay driver instance
397 * @selector: Pin index
398 */
399 static unsigned int ti_iodelay_pin_to_offset(struct ti_iodelay_device *iod,
400 unsigned int selector)
401 {
402 const struct ti_iodelay_reg_data *r = iod->reg_data;
403 unsigned int offset;
404
405 offset = selector * r->regmap_config->reg_stride;
406 offset *= r->reg_nr_per_pin;
407 offset += r->reg_start_offset;
408
409 return offset;
410 }
411
412 /**
413 * ti_iodelay_offset_to_pin() - get a pin index based on the register offset
414 * @iod: iodelay driver instance
415 * @offset: register offset from the base
416 */
417 static int ti_iodelay_offset_to_pin(struct ti_iodelay_device *iod,
418 unsigned int offset)
419 {
420 const struct ti_iodelay_reg_data *r = iod->reg_data;
421 unsigned int index;
422
423 if (offset > r->regmap_config->max_register) {
424 dev_err(iod->dev, "mux offset out of range: 0x%x (0x%x)\n",
425 offset, r->regmap_config->max_register);
426 return -EINVAL;
427 }
428
429 index = (offset - r->reg_start_offset) / r->regmap_config->reg_stride;
430 index /= r->reg_nr_per_pin;
431
432 return index;
433 }
434
435 /**
436 *
437 * @pctldev: Pin controller driver
438 * @np: Device node
439 * @pinctrl_spec: Parsed arguments from device tree
440 * @pins: Array of pins in the pin group
441 * @pin_index: Pin index in the pin array
442 * @data: Pin controller driver specific data
443 *
444 */
445 static int ti_iodelay_node_iterator(struct pinctrl_dev *pctldev,
446 struct device_node *np,
447 const struct of_phandle_args *pinctrl_spec,
448 int *pins, int pin_index, void *data)
449 {
450 struct ti_iodelay_device *iod;
451 struct ti_iodelay_cfg *cfg = data;
452 const struct ti_iodelay_reg_data *r;
453 struct pinctrl_pin_desc *pd;
454 int pin;
455
456 iod = pinctrl_dev_get_drvdata(pctldev);
457 if (!iod)
458 return -EINVAL;
459
460 r = iod->reg_data;
461
462 if (pinctrl_spec->args_count < r->reg_nr_per_pin) {
463 dev_err(iod->dev, "invalid args_count for spec: %i\n",
464 pinctrl_spec->args_count);
465
466 return -EINVAL;
467 }
468
469 /* Index plus two value cells */
470 cfg[pin_index].offset = pinctrl_spec->args[0];
471 cfg[pin_index].a_delay = pinctrl_spec->args[1] & 0xffff;
472 cfg[pin_index].g_delay = pinctrl_spec->args[2] & 0xffff;
473
474 pin = ti_iodelay_offset_to_pin(iod, cfg[pin_index].offset);
475 if (pin < 0) {
476 dev_err(iod->dev, "could not add functions for %s %ux\n",
477 np->name, cfg[pin_index].offset);
478 return -ENODEV;
479 }
480 pins[pin_index] = pin;
481
482 pd = &iod->pa[pin];
483 pd->drv_data = &cfg[pin_index];
484
485 dev_dbg(iod->dev, "%s offset=%x a_delay = %d g_delay = %d\n",
486 np->name, cfg[pin_index].offset, cfg[pin_index].a_delay,
487 cfg[pin_index].g_delay);
488
489 return 0;
490 }
491
492 /**
493 * ti_iodelay_dt_node_to_map() - Map a device tree node to appropriate group
494 * @pctldev: pinctrl device representing IODelay device
495 * @np: Node Pointer (device tree)
496 * @map: Pinctrl Map returned back to pinctrl framework
497 * @num_maps: Number of maps (1)
498 *
499 * Maps the device tree description into a group of configuration parameters
500 * for iodelay block entry.
501 *
502 * Return: 0 in case of success, else appropriate error value
503 */
504 static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev,
505 struct device_node *np,
506 struct pinctrl_map **map,
507 unsigned int *num_maps)
508 {
509 struct ti_iodelay_device *iod;
510 struct ti_iodelay_cfg *cfg;
511 struct ti_iodelay_pingroup *g;
512 const char *name = "pinctrl-pin-array";
513 int rows, *pins, error = -EINVAL, i;
514
515 iod = pinctrl_dev_get_drvdata(pctldev);
516 if (!iod)
517 return -EINVAL;
518
519 rows = pinctrl_count_index_with_args(np, name);
520 if (rows == -EINVAL)
521 return rows;
522
523 *map = devm_kzalloc(iod->dev, sizeof(**map), GFP_KERNEL);
524 if (!*map)
525 return -ENOMEM;
526 *num_maps = 0;
527
528 g = devm_kzalloc(iod->dev, sizeof(*g), GFP_KERNEL);
529 if (!g) {
530 error = -ENOMEM;
531 goto free_map;
532 }
533
534 pins = devm_kzalloc(iod->dev, sizeof(*pins) * rows, GFP_KERNEL);
535 if (!pins)
536 goto free_group;
537
538 cfg = devm_kzalloc(iod->dev, sizeof(*cfg) * rows, GFP_KERNEL);
539 if (!cfg) {
540 error = -ENOMEM;
541 goto free_pins;
542 }
543
544 for (i = 0; i < rows; i++) {
545 struct of_phandle_args pinctrl_spec;
546
547 error = pinctrl_parse_index_with_args(np, name, i,
548 &pinctrl_spec);
549 if (error)
550 goto free_data;
551
552 error = ti_iodelay_node_iterator(pctldev, np, &pinctrl_spec,
553 pins, i, cfg);
554 if (error)
555 goto free_data;
556 }
557
558 g->cfg = cfg;
559 g->ncfg = i;
560 g->config = PIN_CONFIG_END;
561
> 562 error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g);
563 if (error < 0)
564 goto free_data;
565
---
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: 57326 bytes --]
^ permalink raw reply
* Re: [PATCH linux 5/6] hwmon: occ: Add hwmon implementation for the P8 OCC
From: kbuild test robot @ 2017-01-01 0:25 UTC (permalink / raw)
To: eajames.ibm
Cc: kbuild-all, linux, jdelvare, corbet, mark.rutland, robh+dt, wsa,
andrew, joel, devicetree, linux-doc, linux-hwmon, linux-i2c,
linux-kernel, Edward A. James
In-Reply-To: <1483120568-21082-6-git-send-email-eajames.ibm@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1194 bytes --]
Hi Edward,
[auto build test ERROR on hwmon/hwmon-next]
[also build test ERROR on v4.10-rc1 next-20161224]
[cannot apply to linux/master]
[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/eajames-ibm-gmail-com/drivers-hwmon-Add-On-Chip-Controller-driver/20161231-021324
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
config: x86_64-randconfig-n0-01010656 (attached as .config)
compiler: gcc-4.8 (Debian 4.8.4-1) 4.8.4
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
>> ERROR: "occ_sysfs_start" [drivers/hwmon/occ/p8_occ_i2c.ko] undefined!
>> ERROR: "occ_sysfs_stop" [drivers/hwmon/occ/p8_occ_i2c.ko] undefined!
>> ERROR: "occ_stop" [drivers/hwmon/occ/occ_p8.ko] undefined!
>> ERROR: "occ_start" [drivers/hwmon/occ/occ_p8.ko] undefined!
>> ERROR: "occ_get_sensor" [drivers/hwmon/occ/occ_p8.ko] undefined!
---
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: 21665 bytes --]
^ permalink raw reply
* Re: [PATCH v5 5/7] i2c: designware: add SLAVE mode functions
From: kbuild test robot @ 2016-12-31 23:45 UTC (permalink / raw)
Cc: kbuild-all-JC7UmRfGjtg, wsa-z923LK4zBo2bacvFa/9K2g,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
jarkko.nikula-VuQAYsv1563Yd54FQh9/CA,
andriy.shevchenko-VuQAYsv1563Yd54FQh9/CA,
mika.westerberg-VuQAYsv1563Yd54FQh9/CA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
Luis.Oliveira-HKixBCOQz3hWk0Htik3J/w,
Ramiro.Oliveira-HKixBCOQz3hWk0Htik3J/w,
Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
CARLOS.PALMINHA-HKixBCOQz3hWk0Htik3J/w
In-Reply-To: <15538bd29b3ab62608a4e9153af6ee5d4bdc79e2.1482934380.git.lolivei-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 6825 bytes --]
Hi Luis,
[auto build test ERROR on wsa/i2c/for-next]
[also build test ERROR on v4.10-rc1 next-20161224]
[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/Luis-Oliveira/i2c-designware-Cleaning-and-comment-style-fixes/20161229-010120
base: https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git i2c/for-next
config: i386-randconfig-c0-01010626 (attached as .config)
compiler: gcc-4.9 (Debian 4.9.4-2) 4.9.4
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
drivers/i2c/busses/i2c-designware-slave.c: In function 'i2c_dw_irq_handler_slave':
>> drivers/i2c/busses/i2c-designware-slave.c:293:3: error: implicit declaration of function 'i2c_slave_event' [-Werror=implicit-function-declaration]
i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val);
^
>> drivers/i2c/busses/i2c-designware-slave.c:293:31: error: 'I2C_SLAVE_WRITE_REQUESTED' undeclared (first use in this function)
i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val);
^
drivers/i2c/busses/i2c-designware-slave.c:293:31: note: each undeclared identifier is reported only once for each function it appears in
In file included from include/linux/err.h:4:0,
from drivers/i2c/busses/i2c-designware-slave.c:23:
>> drivers/i2c/busses/i2c-designware-slave.c:300:6: error: 'I2C_SLAVE_WRITE_RECEIVED' undeclared (first use in this function)
I2C_SLAVE_WRITE_RECEIVED, &val)) {
^
include/linux/compiler.h:149:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^
drivers/i2c/busses/i2c-designware-slave.c:299:5: note: in expansion of macro 'if'
if (!i2c_slave_event(dev->slave,
^
>> drivers/i2c/busses/i2c-designware-slave.c:312:7: error: 'I2C_SLAVE_READ_REQUESTED' undeclared (first use in this function)
I2C_SLAVE_READ_REQUESTED, &val))
^
include/linux/compiler.h:149:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^
drivers/i2c/busses/i2c-designware-slave.c:311:4: note: in expansion of macro 'if'
if (!i2c_slave_event(dev->slave,
^
>> drivers/i2c/busses/i2c-designware-slave.c:318:36: error: 'I2C_SLAVE_READ_PROCESSED' undeclared (first use in this function)
if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED,
^
include/linux/compiler.h:149:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^
drivers/i2c/busses/i2c-designware-slave.c:318:3: note: in expansion of macro 'if'
if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED,
^
>> drivers/i2c/busses/i2c-designware-slave.c:322:31: error: 'I2C_SLAVE_STOP' undeclared (first use in this function)
i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
^
drivers/i2c/busses/i2c-designware-slave.c: At top level:
>> drivers/i2c/busses/i2c-designware-slave.c:359:2: error: unknown field 'reg_slave' specified in initializer
.reg_slave = i2c_dw_reg_slave,
^
drivers/i2c/busses/i2c-designware-slave.c:359:2: warning: excess elements in struct initializer
drivers/i2c/busses/i2c-designware-slave.c:359:2: warning: (near initialization for 'i2c_dw_algo')
>> drivers/i2c/busses/i2c-designware-slave.c:360:2: error: unknown field 'unreg_slave' specified in initializer
.unreg_slave = i2c_dw_unreg_slave,
^
drivers/i2c/busses/i2c-designware-slave.c:360:2: warning: excess elements in struct initializer
drivers/i2c/busses/i2c-designware-slave.c:360:2: warning: (near initialization for 'i2c_dw_algo')
cc1: some warnings being treated as errors
vim +/i2c_slave_event +293 drivers/i2c/busses/i2c-designware-slave.c
287 dw_readl(dev, DW_IC_CLR_START_DET);
288 if (stat & DW_IC_INTR_ACTIVITY)
289 dw_readl(dev, DW_IC_CLR_ACTIVITY);
290 if (stat & DW_IC_INTR_RX_OVER)
291 dw_readl(dev, DW_IC_CLR_RX_OVER);
292 if ((stat & DW_IC_INTR_RX_FULL) && (stat & DW_IC_INTR_STOP_DET))
> 293 i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val);
294
295 if (slave_activity) {
296 if (stat & DW_IC_INTR_RD_REQ) {
297 if (stat & DW_IC_INTR_RX_FULL) {
298 val = dw_readl(dev, DW_IC_DATA_CMD);
299 if (!i2c_slave_event(dev->slave,
> 300 I2C_SLAVE_WRITE_RECEIVED, &val)) {
301 dev_dbg(dev->dev, "Byte %X acked!",
302 val);
303 }
304 dw_readl(dev, DW_IC_CLR_RD_REQ);
305 stat = i2c_dw_read_clear_intrbits_slave(dev);
306 } else {
307 dw_readl(dev, DW_IC_CLR_RD_REQ);
308 dw_readl(dev, DW_IC_CLR_RX_UNDER);
309 stat = i2c_dw_read_clear_intrbits_slave(dev);
310 }
311 if (!i2c_slave_event(dev->slave,
> 312 I2C_SLAVE_READ_REQUESTED, &val))
313 dw_writel(dev, val, DW_IC_DATA_CMD);
314 }
315 }
316
317 if (stat & DW_IC_INTR_RX_DONE) {
> 318 if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED,
319 &val))
320 dw_readl(dev, DW_IC_CLR_RX_DONE);
321
> 322 i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
323 stat = i2c_dw_read_clear_intrbits_slave(dev);
324 return true;
325 }
326
327 if (stat & DW_IC_INTR_RX_FULL) {
328 val = dw_readl(dev, DW_IC_DATA_CMD);
329 if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,
330 &val))
331 dev_dbg(dev->dev, "Byte %X acked!", val);
332 } else {
333 i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
334 stat = i2c_dw_read_clear_intrbits_slave(dev);
335 }
336
337 if (stat & DW_IC_INTR_TX_OVER)
338 dw_readl(dev, DW_IC_CLR_TX_OVER);
339
340 return 1;
341 }
342
343 static irqreturn_t i2c_dw_isr_slave(int this_irq, void *dev_id)
344 {
345 struct dw_i2c_dev *dev = dev_id;
346 int ret;
347
348 i2c_dw_read_clear_intrbits_slave(dev);
349 ret = i2c_dw_irq_handler_slave(dev);
350
351 if (ret > 0)
352 complete(&dev->cmd_complete);
353
354 return IRQ_RETVAL(ret);
355 }
356
357 static struct i2c_algorithm i2c_dw_algo = {
358 .functionality = i2c_dw_func,
> 359 .reg_slave = i2c_dw_reg_slave,
> 360 .unreg_slave = i2c_dw_unreg_slave,
361 };
362
363 void i2c_dw_disable_slave(struct dw_i2c_dev *dev)
---
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: 27840 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver
From: kbuild test robot @ 2016-12-31 22:52 UTC (permalink / raw)
To: Tony Lindgren
Cc: kbuild-all-JC7UmRfGjtg, Linus Walleij, Gary Bisson,
Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA, Lokesh Vutla
In-Reply-To: <20161230183732.5595-3-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 3592 bytes --]
Hi Nishanth,
[auto build test ERROR on pinctrl/for-next]
[also build test ERROR on v4.10-rc1 next-20161224]
[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/Tony-Lindgren/Add-TI-iodelay-driver-using-pinctrl-cells/20161231-024542
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git for-next
config: arm64-allmodconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=arm64
All errors (new ones prefixed by >>):
drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_get_pingroup':
>> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:383:6: error: implicit declaration of function 'pinctrl_generic_get_group' [-Werror=implicit-function-declaration]
g = pinctrl_generic_get_group(iod->pctl, selector);
^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/ti/pinctrl-ti-iodelay.c:383:4: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
g = pinctrl_generic_get_group(iod->pctl, selector);
^
drivers/pinctrl/ti/pinctrl-ti-iodelay.c:391:10: error: dereferencing pointer to incomplete type 'struct group_desc'
return g->data;
^~
drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_dt_node_to_map':
drivers/pinctrl/ti/pinctrl-ti-iodelay.c:562:10: error: implicit declaration of function 'pinctrl_generic_add_group' [-Werror=implicit-function-declaration]
error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g);
^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/ti/pinctrl-ti-iodelay.c: At top level:
>> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:729:22: error: 'pinctrl_generic_get_group_count' undeclared here (not in a function)
.get_groups_count = pinctrl_generic_get_group_count,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/ti/pinctrl-ti-iodelay.c:730:20: error: 'pinctrl_generic_get_group_name' undeclared here (not in a function)
.get_group_name = pinctrl_generic_get_group_name,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/ti/pinctrl-ti-iodelay.c:731:20: error: 'pinctrl_generic_get_group_pins' undeclared here (not in a function)
.get_group_pins = pinctrl_generic_get_group_pins,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_get_pingroup':
drivers/pinctrl/ti/pinctrl-ti-iodelay.c:392:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
cc1: some warnings being treated as errors
vim +/pinctrl_generic_get_group +383 drivers/pinctrl/ti/pinctrl-ti-iodelay.c
377 */
378 static struct ti_iodelay_pingroup *
379 ti_iodelay_get_pingroup(struct ti_iodelay_device *iod, unsigned int selector)
380 {
381 struct group_desc *g;
382
> 383 g = pinctrl_generic_get_group(iod->pctl, selector);
384 if (!g) {
385 dev_err(iod->dev, "%s could not find pingroup %i\n", __func__,
386 selector);
387
388 return NULL;
389 }
390
> 391 return g->data;
392 }
393
394 /**
---
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: 53495 bytes --]
^ permalink raw reply
* Re: [PATCH v3] net: ethernet: faraday: To support device tree usage.
From: Arnd Bergmann @ 2016-12-31 20:23 UTC (permalink / raw)
To: Florian Fainelli
Cc: Greentime Hu, netdev, devicetree, andrew, linux-kernel, jiri
In-Reply-To: <351d1ae7-ca51-7962-0e52-38cdc69bcebd@gmail.com>
On Saturday, December 31, 2016 10:48:39 AM CET Florian Fainelli wrote:
>
> On 12/29/2016 11:37 PM, Greentime Hu wrote:
> > Signed-off-by: Greentime Hu <green.hu@gmail.com>
>
> This is not enough, you need to add a Device Tree binding document under
> Documentation/devicetree/bindings/net/ which documents this compatible
> string, as well as additional properties that may be required to
> describe this hardware block.
We already have
Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
for the same hardware (though used by a different driver).
I'd suggest renaming that one to a more generic file name and
adding the new compatible string there.
Aside from that, every patch should also have a changelog comment.
Arnd
^ permalink raw reply
* Re: [PATCH v3] net: ethernet: faraday: To support device tree usage.
From: Florian Fainelli @ 2016-12-31 18:48 UTC (permalink / raw)
To: Greentime Hu, netdev-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, andrew-g2DYL2Zd6BY,
arnd-r2nGTMty4D4, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
jiri-rHqAuBHg3fBzbRFIqnYvSA
In-Reply-To: <1483083470-15779-1-git-send-email-green.hu-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On 12/29/2016 11:37 PM, Greentime Hu wrote:
> Signed-off-by: Greentime Hu <green.hu-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
This is not enough, you need to add a Device Tree binding document under
Documentation/devicetree/bindings/net/ which documents this compatible
string, as well as additional properties that may be required to
describe this hardware block.
--
Florian
--
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: [v2 2/2] MIPS: BMIPS: Add support SPI device nodes
From: Florian Fainelli @ 2016-12-31 18:42 UTC (permalink / raw)
To: Jaedon Shin, Ralf Baechle, Mark Brown
Cc: Kevin Cernekee, Rob Herring, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-spi-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161230063001.944-3-jaedon.shin-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Hi Jaedon,
On 12/29/2016 10:30 PM, Jaedon Shin wrote:
> Adds SPI device nodes to BCM7xxx MIPS based SoCs.
>
> Signed-off-by: Jaedon Shin <jaedon.shin-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
> +
> +&qspi {
> + status = "okay";
> +
> + m25p80@0 {
> + compatible = "m25p80";
> + reg = <0>;
> + spi-max-frequency = <0x2625a00>;
Sorry for not noticing this earlier, can we have the frequency in a
decimal form?
With that fixed, feel free to add:
Reviewed-by: Florian Fainelli <f.fainelli-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Thanks!
--
Florian
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" 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 v2] clk: cdce925: add support for CDCE913, CDCE937, and CDCE949
From: Akinobu Mita @ 2016-12-31 18:04 UTC (permalink / raw)
To: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Akinobu Mita, Mike Looijmans, Michael Turquette, Stephen Boyd
The CDCE925 is a member of the CDCE(L)9xx programmable clock generator
family. There are also CDCE913, CDCE937, CDCE949 which have different
number of PLLs and outputs.
The clk-cdce925 driver supports only CDCE925 in the family. This adds
support for the CDCE913, CDCE937, CDCE949, too.
Signed-off-by: Akinobu Mita <akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Mike Looijmans <mike.looijmans-Oq418RWZeHk@public.gmane.org>
Cc: Michael Turquette <mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
* Changes from v1
- Add Acked-by line from Rob Herring
- Rename local variable 'cdce925_regmap_config' to 'config',
suggested by Stephen Boyd
.../devicetree/bindings/clock/ti,cdce925.txt | 15 ++-
drivers/clk/Kconfig | 11 ++-
drivers/clk/clk-cdce925.c | 108 ++++++++++++++++-----
3 files changed, 100 insertions(+), 34 deletions(-)
diff --git a/Documentation/devicetree/bindings/clock/ti,cdce925.txt b/Documentation/devicetree/bindings/clock/ti,cdce925.txt
index 4c7669a..0d01f2d 100644
--- a/Documentation/devicetree/bindings/clock/ti,cdce925.txt
+++ b/Documentation/devicetree/bindings/clock/ti,cdce925.txt
@@ -1,15 +1,22 @@
-Binding for TO CDCE925 programmable I2C clock synthesizers.
+Binding for TI CDCE913/925/937/949 programmable I2C clock synthesizers.
Reference
This binding uses the common clock binding[1].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
-[2] http://www.ti.com/product/cdce925
+[2] http://www.ti.com/product/cdce913
+[3] http://www.ti.com/product/cdce925
+[4] http://www.ti.com/product/cdce937
+[5] http://www.ti.com/product/cdce949
The driver provides clock sources for each output Y1 through Y5.
Required properties:
- - compatible: Shall be "ti,cdce925"
+ - compatible: Shall be one of the following:
+ - "ti,cdce913": 1-PLL, 3 Outputs
+ - "ti,cdce925": 2-PLL, 5 Outputs
+ - "ti,cdce937": 3-PLL, 7 Outputs
+ - "ti,cdce949": 4-PLL, 9 Outputs
- reg: I2C device address.
- clocks: Points to a fixed parent clock that provides the input frequency.
- #clock-cells: From common clock bindings: Shall be 1.
@@ -18,7 +25,7 @@ Optional properties:
- xtal-load-pf: Crystal load-capacitor value to fine-tune performance on a
board, or to compensate for external influences.
-For both PLL1 and PLL2 an optional child node can be used to specify spread
+For all PLL1, PLL2, ... an optional child node can be used to specify spread
spectrum clocking parameters for a board.
- spread-spectrum: SSC mode as defined in the data sheet.
- spread-spectrum-center: Use "centered" mode instead of "max" mode. When
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index d051edd..8f67dc9 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -104,16 +104,17 @@ config COMMON_CLK_CDCE706
This driver supports TI CDCE706 programmable 3-PLL clock synthesizer.
config COMMON_CLK_CDCE925
- tristate "Clock driver for TI CDCE925 devices"
+ tristate "Clock driver for TI CDCE913/925/937/949 devices"
depends on I2C
depends on OF
select REGMAP_I2C
help
---help---
- This driver supports the TI CDCE925 programmable clock synthesizer.
- The chip contains two PLLs with spread-spectrum clocking support and
- five output dividers. The driver only supports the following setup,
- and uses a fixed setting for the output muxes.
+ This driver supports the TI CDCE913/925/937/949 programmable clock
+ synthesizer. Each chip has different number of PLLs and outputs.
+ For example, the CDCE925 contains two PLLs with spread-spectrum
+ clocking support and five output dividers. The driver only supports
+ the following setup, and uses a fixed setting for the output muxes.
Y1 is derived from the input clock
Y2 and Y3 derive from PLL1
Y4 and Y5 derive from PLL2
diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c
index f793b2d..c933be0 100644
--- a/drivers/clk/clk-cdce925.c
+++ b/drivers/clk/clk-cdce925.c
@@ -1,8 +1,8 @@
/*
- * Driver for TI Dual PLL CDCE925 clock synthesizer
+ * Driver for TI Multi PLL CDCE913/925/937/949 clock synthesizer
*
- * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1
- * and Y4/Y5 to PLL2. PLL frequency is set on a first-come-first-serve
+ * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1,
+ * Y4/Y5 to PLL2, and so on. PLL frequency is set on a first-come-first-serve
* basis. Clients can directly request any frequency that the chip can
* deliver using the standard clk framework. In addition, the device can
* be configured and activated via the devicetree.
@@ -19,11 +19,32 @@
#include <linux/slab.h>
#include <linux/gcd.h>
-/* The chip has 2 PLLs which can be routed through dividers to 5 outputs.
+/* Each chip has different number of PLLs and outputs, for example:
+ * The CECE925 has 2 PLLs which can be routed through dividers to 5 outputs.
* Model this as 2 PLL clocks which are parents to the outputs.
*/
-#define NUMBER_OF_PLLS 2
-#define NUMBER_OF_OUTPUTS 5
+
+enum {
+ CDCE913,
+ CDCE925,
+ CDCE937,
+ CDCE949,
+};
+
+struct clk_cdce925_chip_info {
+ int num_plls;
+ int num_outputs;
+};
+
+static const struct clk_cdce925_chip_info clk_cdce925_chip_info_tbl[] = {
+ [CDCE913] = { .num_plls = 1, .num_outputs = 3 },
+ [CDCE925] = { .num_plls = 2, .num_outputs = 5 },
+ [CDCE937] = { .num_plls = 3, .num_outputs = 7 },
+ [CDCE949] = { .num_plls = 4, .num_outputs = 9 },
+};
+
+#define MAX_NUMBER_OF_PLLS 4
+#define MAX_NUMBER_OF_OUTPUTS 9
#define CDCE925_REG_GLOBAL1 0x01
#define CDCE925_REG_Y1SPIPDIVH 0x02
@@ -43,7 +64,7 @@ struct clk_cdce925_output {
struct clk_hw hw;
struct clk_cdce925_chip *chip;
u8 index;
- u16 pdiv; /* 1..127 for Y2-Y5; 1..1023 for Y1 */
+ u16 pdiv; /* 1..127 for Y2-Y9; 1..1023 for Y1 */
};
#define to_clk_cdce925_output(_hw) \
container_of(_hw, struct clk_cdce925_output, hw)
@@ -60,8 +81,9 @@ struct clk_cdce925_pll {
struct clk_cdce925_chip {
struct regmap *regmap;
struct i2c_client *i2c_client;
- struct clk_cdce925_pll pll[NUMBER_OF_PLLS];
- struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS];
+ const struct clk_cdce925_chip_info *chip_info;
+ struct clk_cdce925_pll pll[MAX_NUMBER_OF_PLLS];
+ struct clk_cdce925_output clk[MAX_NUMBER_OF_OUTPUTS];
};
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
@@ -284,6 +306,18 @@ static void cdce925_clk_set_pdiv(struct clk_cdce925_output *data, u16 pdiv)
case 4:
regmap_update_bits(data->chip->regmap, 0x27, 0x7F, pdiv);
break;
+ case 5:
+ regmap_update_bits(data->chip->regmap, 0x36, 0x7F, pdiv);
+ break;
+ case 6:
+ regmap_update_bits(data->chip->regmap, 0x37, 0x7F, pdiv);
+ break;
+ case 7:
+ regmap_update_bits(data->chip->regmap, 0x46, 0x7F, pdiv);
+ break;
+ case 8:
+ regmap_update_bits(data->chip->regmap, 0x47, 0x7F, pdiv);
+ break;
}
}
@@ -302,6 +336,14 @@ static void cdce925_clk_activate(struct clk_cdce925_output *data)
case 4:
regmap_update_bits(data->chip->regmap, 0x24, 0x03, 0x03);
break;
+ case 5:
+ case 6:
+ regmap_update_bits(data->chip->regmap, 0x34, 0x03, 0x03);
+ break;
+ case 7:
+ case 8:
+ regmap_update_bits(data->chip->regmap, 0x44, 0x03, 0x03);
+ break;
}
}
@@ -474,15 +516,6 @@ static const struct clk_ops cdce925_clk_y1_ops = {
.set_rate = cdce925_clk_y1_set_rate,
};
-
-static struct regmap_config cdce925_regmap_config = {
- .name = "configuration0",
- .reg_bits = 8,
- .val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
- .max_register = 0x2F,
-};
-
#define CDCE925_I2C_COMMAND_BLOCK_TRANSFER 0x00
#define CDCE925_I2C_COMMAND_BYTE_TRANSFER 0x80
@@ -582,13 +615,19 @@ static int cdce925_probe(struct i2c_client *client,
struct clk_cdce925_chip *data;
struct device_node *node = client->dev.of_node;
const char *parent_name;
- const char *pll_clk_name[NUMBER_OF_PLLS] = {NULL,};
+ const char *pll_clk_name[MAX_NUMBER_OF_PLLS] = {NULL,};
struct clk_init_data init;
u32 value;
int i;
int err;
struct device_node *np_output;
char child_name[6];
+ struct regmap_config config = {
+ .name = "configuration0",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ };
dev_dbg(&client->dev, "%s\n", __func__);
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
@@ -596,8 +635,11 @@ static int cdce925_probe(struct i2c_client *client,
return -ENOMEM;
data->i2c_client = client;
+ data->chip_info = &clk_cdce925_chip_info_tbl[id->driver_data];
+ config.max_register = CDCE925_OFFSET_PLL +
+ data->chip_info->num_plls * 0x10 - 1;
data->regmap = devm_regmap_init(&client->dev, ®map_cdce925_bus,
- &client->dev, &cdce925_regmap_config);
+ &client->dev, &config);
if (IS_ERR(data->regmap)) {
dev_err(&client->dev, "failed to allocate register map\n");
return PTR_ERR(data->regmap);
@@ -626,7 +668,7 @@ static int cdce925_probe(struct i2c_client *client,
init.num_parents = parent_name ? 1 : 0;
/* Register PLL clocks */
- for (i = 0; i < NUMBER_OF_PLLS; ++i) {
+ for (i = 0; i < data->chip_info->num_plls; ++i) {
pll_clk_name[i] = kasprintf(GFP_KERNEL, "%s.pll%d",
client->dev.of_node->name, i);
init.name = pll_clk_name[i];
@@ -684,7 +726,7 @@ static int cdce925_probe(struct i2c_client *client,
init.ops = &cdce925_clk_ops;
init.flags = CLK_SET_RATE_PARENT;
init.num_parents = 1;
- for (i = 1; i < NUMBER_OF_OUTPUTS; ++i) {
+ for (i = 1; i < data->chip_info->num_outputs; ++i) {
init.name = kasprintf(GFP_KERNEL, "%s.Y%d",
client->dev.of_node->name, i+1);
data->clk[i].chip = data;
@@ -702,6 +744,16 @@ static int cdce925_probe(struct i2c_client *client,
/* Mux Y4/5 to PLL2 */
init.parent_names = &pll_clk_name[1];
break;
+ case 5:
+ case 6:
+ /* Mux Y6/7 to PLL3 */
+ init.parent_names = &pll_clk_name[2];
+ break;
+ case 7:
+ case 8:
+ /* Mux Y8/9 to PLL4 */
+ init.parent_names = &pll_clk_name[3];
+ break;
}
err = devm_clk_hw_register(&client->dev, &data->clk[i].hw);
kfree(init.name); /* clock framework made a copy of the name */
@@ -720,7 +772,7 @@ static int cdce925_probe(struct i2c_client *client,
err = 0;
error:
- for (i = 0; i < NUMBER_OF_PLLS; ++i)
+ for (i = 0; i < data->chip_info->num_plls; ++i)
/* clock framework made a copy of the name */
kfree(pll_clk_name[i]);
@@ -728,13 +780,19 @@ static int cdce925_probe(struct i2c_client *client,
}
static const struct i2c_device_id cdce925_id[] = {
- { "cdce925", 0 },
+ { "cdce913", CDCE913 },
+ { "cdce925", CDCE925 },
+ { "cdce937", CDCE937 },
+ { "cdce949", CDCE949 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cdce925_id);
static const struct of_device_id clk_cdce925_of_match[] = {
+ { .compatible = "ti,cdce913" },
{ .compatible = "ti,cdce925" },
+ { .compatible = "ti,cdce937" },
+ { .compatible = "ti,cdce949" },
{ },
};
MODULE_DEVICE_TABLE(of, clk_cdce925_of_match);
@@ -750,5 +808,5 @@ static struct i2c_driver cdce925_driver = {
module_i2c_driver(cdce925_driver);
MODULE_AUTHOR("Mike Looijmans <mike.looijmans-Oq418RWZeHk@public.gmane.org>");
-MODULE_DESCRIPTION("cdce925 driver");
+MODULE_DESCRIPTION("TI CDCE913/925/937/949 driver");
MODULE_LICENSE("GPL");
--
2.7.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
* Re: [PATCH v6 5/9] iio: multiplexer: new iio category and iio-mux driver
From: Jonathan Cameron @ 2016-12-31 16:21 UTC (permalink / raw)
To: Peter Rosin, linux-kernel
Cc: Wolfram Sang, Rob Herring, Mark Rutland, 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: <1480493823-21462-6-git-send-email-peda@axentia.se>
On 30/11/16 08:16, Peter Rosin wrote:
> 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>
Other than binding naming, I'm happy with this - so pending that changing...
Reviewed-by: Jonathan Cameron <jic23@kernel.org>
If it makes sense I can obviously take this via IIO once the core is in
place.
Jonathan
> ---
> 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 | 456 ++++++++++++++++++++++++++++++++++++++
> 6 files changed, 483 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..92dfee2dfed1
> --- /dev/null
> +++ b/drivers/iio/multiplexer/iio-mux.c
> @@ -0,0 +1,456 @@
> +/*
> + * 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,
> + u32 state, const char *label, 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;
> + char *page = NULL;
> + int num_ext_info;
> + int i;
> + int ret;
> +
> + chan->indexed = 1;
> + chan->output = pchan->output;
> + chan->datasheet_name = label;
> + 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);
> +
> + if (state >= mux->control->states) {
> + dev_err(dev, "too many channels\n");
> + 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;
> +}
> +
> +/*
> + * Same as of_property_for_each_string(), but also keeps track of the
> + * index of each string.
> + */
> +#define of_property_for_each_string_index(np, propname, prop, s, i) \
> + for (prop = of_find_property(np, propname, NULL), \
> + s = of_prop_next_string(prop, NULL), \
> + i = 0; \
> + s; \
> + s = of_prop_next_string(prop, s), \
> + i++)
> +
> +static int mux_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = pdev->dev.of_node;
> + struct iio_dev *indio_dev;
> + struct iio_channel *parent;
> + struct mux *mux;
> + struct property *prop;
> + const char *label;
> + u32 state;
> + 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;
> + of_property_for_each_string(np, "channels", prop, label) {
> + if (*label)
> + 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;
> + of_property_for_each_string_index(np, "channels", prop, label, state) {
> + if (!*label)
> + continue;
> +
> + ret = mux_configure_channel(dev, mux, state, label, i++);
> + if (ret < 0)
> + return ret;
> + }
> +
> + 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");
>
^ permalink raw reply
* Re: [PATCH v6 2/9] misc: minimal mux subsystem and gpio-based mux controller
From: Jonathan Cameron @ 2016-12-31 16:19 UTC (permalink / raw)
To: Peter Rosin, linux-kernel
Cc: Wolfram Sang, Rob Herring, Mark Rutland, 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: <1480493823-21462-3-git-send-email-peda@axentia.se>
On 30/11/16 08:16, Peter Rosin wrote:
> 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 initially comes with a single backend
> driver that controls gpio based multiplexers. Even though not needed by
> this initial driver, the mux controller subsystem is prepared to handle
> chips with multiple (independent) mux controllers.
>
> Signed-off-by: Peter Rosin <peda@axentia.se>
Few trivial bits inline + question of whether misc is the right location..
It's small, but not totally trivial as subsystems go, so perhaps it needs it's
own directory.
> ---
> Documentation/driver-model/devres.txt | 6 +-
> MAINTAINERS | 2 +
> drivers/misc/Kconfig | 30 ++++
> drivers/misc/Makefile | 2 +
> drivers/misc/mux-core.c | 311 ++++++++++++++++++++++++++++++++++
> drivers/misc/mux-gpio.c | 138 +++++++++++++++
> include/linux/mux.h | 197 +++++++++++++++++++++
> 7 files changed, 685 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 ca9d1eb46bc0..d64ede85b61b 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()
Technically should be in a separate cleanup patch...
> + devm_mfd_add_devices()
> +
> +MUX
> + devm_mux_control_get()
> + devm_mux_control_put()
>
> PER-CPU MEM
> devm_alloc_percpu()
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3d4d0efc2b64..dc7498682752 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8407,6 +8407,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..2ce675e410c5 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -766,6 +766,36 @@ 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.
>
> +menuconfig MULTIPLEXER
> + bool "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 unsure, say no.
> +
Fun question of the day. Is misc the place to put this or should it
have it's own directory. I'd go for own directory...
On the plus side, whilst it's in misc you get to pester Greg and Arnd
for review.
> +if MULTIPLEXER
> +
> +config MUX_GPIO
> + tristate "GPIO-controlled Multiplexer"
> + depends on OF && GPIOLIB
> + help
> + GPIO-controlled Multiplexer controller.
> +
> + The driver builds a single multiplexer controller using a number
> + of gpio pins. For N pins, there will be 2^N possible multiplexer
> + states. The GPIO pins can be connected (by the hardware) to several
> + multiplexers, which in that case will be operated in parallel.
> +
> + 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..cccaa7261a6e
> --- /dev/null
> +++ b/drivers/misc/mux-core.c
> @@ -0,0 +1,311 @@
> +/*
> + * Multiplexer subsystem
> + *
> + * 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.
> + */
> +
> +#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 class mux_class = {
> + .name = "mux",
> + .owner = THIS_MODULE,
> +};
> +
> +static int __init mux_init(void)
> +{
> + return class_register(&mux_class);
> +}
> +
> +static DEFINE_IDA(mux_ida);
> +
> +static void mux_chip_release(struct device *dev)
> +{
> + struct mux_chip *mux_chip = to_mux_chip(dev);
> +
> + ida_simple_remove(&mux_ida, mux_chip->id);
> + kfree(mux_chip);
> +}
> +
> +static struct device_type mux_type = {
> + .name = "mux-chip",
> + .release = mux_chip_release,
> +};
> +
> +struct mux_chip *mux_chip_alloc(struct device *dev,
> + unsigned int controllers, size_t sizeof_priv)
> +{
> + struct mux_chip *mux_chip;
> + int i;
> +
> + if (!dev || !controllers)
> + return NULL;
> +
> + mux_chip = kzalloc(sizeof(*mux_chip) +
> + controllers * sizeof(*mux_chip->mux) +
> + sizeof_priv, GFP_KERNEL);
> + if (!mux_chip)
> + return NULL;
> +
> + mux_chip->mux = (struct mux_control *)(mux_chip + 1);
> + mux_chip->dev.class = &mux_class;
> + mux_chip->dev.type = &mux_type;
> + mux_chip->dev.parent = dev;
> + mux_chip->dev.of_node = dev->of_node;
> + dev_set_drvdata(&mux_chip->dev, mux_chip);
> +
> + mux_chip->id = ida_simple_get(&mux_ida, 0, 0, GFP_KERNEL);
> + if (mux_chip->id < 0) {
> + pr_err("muxchipX failed to get a device id\n");
> + kfree(mux_chip);
> + return NULL;
> + }
> + dev_set_name(&mux_chip->dev, "muxchip%d", mux_chip->id);
> +
> + mux_chip->controllers = controllers;
> + for (i = 0; i < controllers; ++i) {
> + struct mux_control *mux = &mux_chip->mux[i];
> +
> + mux->chip = mux_chip;
> + init_rwsem(&mux->lock);
> + mux->cached_state = -1;
> + mux->idle_state = -1;
> + }
> +
> + device_initialize(&mux_chip->dev);
> +
> + return mux_chip;
> +}
> +EXPORT_SYMBOL_GPL(mux_chip_alloc);
> +
> +static int mux_control_set(struct mux_control *mux, int state)
> +{
> + int ret = mux->chip->ops->set(mux, state);
> +
> + mux->cached_state = ret < 0 ? -1 : state;
> +
> + return ret;
> +}
> +
> +int mux_chip_register(struct mux_chip *mux_chip)
> +{
> + int i;
> + int ret;
> +
> + for (i = 0; i < mux_chip->controllers; ++i) {
> + struct mux_control *mux = &mux_chip->mux[i];
> +
> + if (mux->idle_state == mux->cached_state)
> + continue;
> +
> + ret = mux_control_set(mux, mux->idle_state);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return device_add(&mux_chip->dev);
> +}
> +EXPORT_SYMBOL_GPL(mux_chip_register);
> +
> +void mux_chip_unregister(struct mux_chip *mux_chip)
> +{
> + device_del(&mux_chip->dev);
> +}
> +EXPORT_SYMBOL_GPL(mux_chip_unregister);
> +
> +void mux_chip_free(struct mux_chip *mux_chip)
> +{
> + if (!mux_chip)
> + return;
I'd drop a blank line in here. Slightly nicer to read.
> + put_device(&mux_chip->dev);
> +}
> +EXPORT_SYMBOL_GPL(mux_chip_free);
> +
> +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);
> +
> +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, const void *data)
> +{
> + return dev->of_node == data;
> +}
> +
> +static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
> +{
> + struct device *dev;
> +
> + dev = class_find_device(&mux_class, NULL, np, of_dev_node_match);
> +
> + return dev ? to_mux_chip(dev) : NULL;
> +}
> +
> +struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
> +{
> + struct device_node *np = dev->of_node;
> + struct of_phandle_args args;
> + struct mux_chip *mux_chip;
> + unsigned int controller;
> + int index = 0;
> + int ret;
> +
> + if (mux_name) {
> + index = of_property_match_string(np, "mux-control-names",
> + mux_name);
> + if (index < 0)
> + return ERR_PTR(index);
> + }
> +
> + ret = of_parse_phandle_with_args(np,
> + "mux-controls", "#mux-control-cells",
> + index, &args);
> + if (ret) {
> + dev_err(dev, "%s: failed to get mux-control %s(%i)\n",
> + np->full_name, mux_name ?: "", index);
> + return ERR_PTR(ret);
> + }
> +
> + mux_chip = of_find_mux_chip_by_node(args.np);
> + of_node_put(args.np);
> + if (!mux_chip)
> + return ERR_PTR(-EPROBE_DEFER);
> +
> + if (args.args_count > 1 ||
> + (!args.args_count && (mux_chip->controllers > 1))) {
> + dev_err(dev, "%s: wrong #mux-control-cells for %s\n",
> + np->full_name, args.np->full_name);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + controller = 0;
> + if (args.args_count)
> + controller = args.args[0];
> +
> + if (controller >= mux_chip->controllers)
> + return ERR_PTR(-EINVAL);
> +
> + get_device(&mux_chip->dev);
> + return &mux_chip->mux[controller];
> +}
> +EXPORT_SYMBOL_GPL(mux_control_get);
> +
> +void mux_control_put(struct mux_control *mux)
> +{
> + put_device(&mux->chip->dev);
> +}
> +EXPORT_SYMBOL_GPL(mux_control_put);
> +
> +static void devm_mux_control_release(struct device *dev, void *res)
> +{
> + struct mux_control *mux = *(struct mux_control **)res;
> +
> + mux_control_put(mux);
> +}
> +
> +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_release, 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;
> +}
> +
> +void devm_mux_control_put(struct device *dev, struct mux_control *mux)
> +{
> + WARN_ON(devres_release(dev, devm_mux_control_release,
> + devm_mux_control_match, mux));
> +}
> +EXPORT_SYMBOL_GPL(devm_mux_control_put);
> +
> +subsys_initcall(mux_init);
> +
> +MODULE_DESCRIPTION("Multiplexer subsystem");
> +MODULE_AUTHOR("Peter Rosin <peda@axentia.se");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/misc/mux-gpio.c b/drivers/misc/mux-gpio.c
> new file mode 100644
> index 000000000000..a107a9b96854
> --- /dev/null
> +++ b/drivers/misc/mux-gpio.c
> @@ -0,0 +1,138 @@
> +/*
> + * GPIO-controlled 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/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;
> + int *val;
> +};
> +
> +static int mux_gpio_set(struct mux_control *mux, int state)
> +{
> + struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
> + int i;
> +
> + for (i = 0; i < mux_gpio->gpios->ndescs; i++)
> + mux_gpio->val[i] = (state >> i) & 1;
> +
> + gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
> + mux_gpio->gpios->desc,
> + mux_gpio->val);
> +
> + 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_chip *mux_chip;
> + struct mux_gpio *mux_gpio;
> + int pins;
> + u32 idle_state;
> + int ret;
> +
> + if (!np)
> + return -ENODEV;
> +
> + pins = gpiod_count(dev, "mux");
> + if (pins < 0)
> + return pins;
> +
> + mux_chip = mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
> + pins * sizeof(*mux_gpio->val));
> + if (!mux_chip)
> + return -ENOMEM;
Rather feels like mux_chip_alloc is a good candidate for a managed
allocator. Might be worth doing the register as well then the remove
below would go away. I guess perhaps not that worthwhile as not many
mux types are likely to ever exist...
> +
> + mux_gpio = mux_chip_priv(mux_chip);
> + mux_gpio->val = (int *)(mux_gpio + 1);
> + mux_chip->ops = &mux_gpio_ops;
> +
> + platform_set_drvdata(pdev, mux_chip);
> +
> + mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
> + if (IS_ERR(mux_gpio->gpios)) {
> + ret = PTR_ERR(mux_gpio->gpios);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "failed to get gpios\n");
> + goto free_mux_chip;
> + }
> + WARN_ON(pins != mux_gpio->gpios->ndescs);
> + mux_chip->mux->states = 1 << pins;
> +
> + ret = of_property_read_u32(np, "idle-state", &idle_state);
> + if (ret >= 0) {
> + if (idle_state >= mux_chip->mux->states) {
> + dev_err(dev, "invalid idle-state %u\n", idle_state);
> + ret = -EINVAL;
> + goto free_mux_chip;
> + }
> +
> + mux_chip->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;
> + }
> +
> + dev_info(dev, "%u-way mux-controller registered\n",
> + mux_chip->mux->states);
> +
> + return 0;
> +
> +free_mux_chip:
> + mux_chip_free(mux_chip);
> + return ret;
> +}
> +
> +static int mux_gpio_remove(struct platform_device *pdev)
> +{
> + struct mux_chip *mux_chip = platform_get_drvdata(pdev);
> +
> + mux_chip_unregister(mux_chip);
> + mux_chip_free(mux_chip);
> +
> + 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_DESCRIPTION("GPIO-controlled multiplexer driver");
> +MODULE_AUTHOR("Peter Rosin <peda@axentia.se");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mux.h b/include/linux/mux.h
> new file mode 100644
> index 000000000000..88d607b7fde7
> --- /dev/null
> +++ b/include/linux/mux.h
> @@ -0,0 +1,197 @@
> +/*
> + * mux.h - definitions for the multiplexer interface
> + *
> + * 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.
> + */
> +
> +#ifndef _LINUX_MUX_H
> +#define _LINUX_MUX_H
> +
> +#include <linux/device.h>
> +#include <linux/rwsem.h>
> +
> +struct mux_chip;
> +struct mux_control;
> +struct platform_device;
> +
> +struct mux_control_ops {
> + int (*set)(struct mux_control *mux, int state);
> +};
> +
> +/**
> + * struct mux_control - Represents a mux controller.
> + * @lock: Protects the mux controller state.
> + * @chip: The mux chip that is handling this mux controller.
> + * @states: The number of mux controller states.
> + * @cached_state: The current mux controller state, or -1 if none.
> + * @idle_state: The mux controller state to use when inactive, or -1
> + * for none.
> + */
> +struct mux_control {
> + struct rw_semaphore lock; /* protects the state of the mux */
> +
> + struct mux_chip *chip;
> +
> + unsigned int states;
> + int cached_state;
> + int idle_state;
> +};
> +
> +/**
> + * struct mux_chip - Represents a chip holding mux controllers.
> + * @controllers: Number of mux controllers handled by the chip.
> + * @mux: Array of mux controllers that is handled.
> + * @dev: Device structure.
> + * @id: Used to identify the device internally.
> + * @ops: Mux controller operations.
> + */
> +struct mux_chip {
> + unsigned int controllers;
> + struct mux_control *mux;
> + struct device dev;
> + int id;
> +
> + const struct mux_control_ops *ops;
> +};
> +
> +#define to_mux_chip(x) container_of((x), struct mux_chip, dev)
> +
> +/**
> + * mux_chip_priv() - Get the extra memory reserved by mux_chip_alloc().
> + * @mux_chip: The mux-chip to get the private memory from.
> + *
> + * Return: Pointer to the private memory reserved by the allocator.
> + */
> +static inline void *mux_chip_priv(struct mux_chip *mux_chip)
> +{
> + return &mux_chip->mux[mux_chip->controllers];
> +}
> +
> +/**
> + * mux_chip_alloc() - Allocate a mux-chip.
> + * @dev: The parent device implementing the mux interface.
> + * @controllers: The number of mux controllers to allocate for this chip.
> + * @sizeof_priv: Size of extra memory area for private use by the caller.
> + *
> + * Return: A pointer to the new mux-chip, NULL on failure.
> + */
> +struct mux_chip *mux_chip_alloc(struct device *dev,
> + unsigned int controllers, size_t sizeof_priv);
> +
> +/**
> + * mux_chip_register() - Register a mux-chip, thus readying the controllers
> + * for use.
> + * @mux_chip: The mux-chip to register.
> + *
> + * Do not retry registration of the same mux-chip on failure. You should
> + * instead put it away with mux_chip_free() and allocate a new one, if you
> + * for some reason would like to retry registration.
> + *
> + * Return: Zero on success or a negative errno on error.
> + */
> +int mux_chip_register(struct mux_chip *mux_chip);
> +
> +/**
> + * mux_chip_unregister() - Take the mux-chip off-line.
> + * @mux_chip: The mux-chip to unregister.
> + *
> + * mux_chip_unregister() reverses the effects of mux_chip_register().
> + * But not completely, you should not try to call mux_chip_register()
> + * on a mux-chip that has been registered before.
> + */
> +void mux_chip_unregister(struct mux_chip *mux_chip);
> +
> +/**
> + * mux_chip_free() - Free the mux-chip for good.
> + * @mux_chip: The mux-chip to free.
> + *
> + * mux_chip_free() reverses the effects of mux_chip_alloc().
> + */
> +void mux_chip_free(struct mux_chip *mux_chip);
> +
> +/**
> + * mux_control_select() - Select the given multiplexer state.
> + * @mux: The mux-control to request a change of state from.
> + * @state: The new requested state.
> + *
> + * 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.
> + *
> + * Return: 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 perhaps not be making
> + * assumptions about.
> + */
> +int mux_control_select(struct mux_control *mux, int state);
> +
> +/**
> + * mux_control_deselect() - Deselect the previously selected multiplexer state.
> + * @mux: The mux-control to deselect.
> + *
> + * Return: 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);
> +
> +/**
> + * mux_control_get_index() - Get the index of the given mux controller
> + * @mux: The mux-control to the the index for.
> + *
> + * Return: The index of the mux controller within the mux chip the mux
> + * controller is a part of.
> + */
> +static inline unsigned int mux_control_get_index(struct mux_control *mux)
> +{
> + return mux - mux->chip->mux;
> +}
> +
> +/**
> + * mux_control_get() - Get the mux-control for a device.
> + * @dev: The device that needs a mux-control.
> + * @mux_name: The name identifying the mux-control.
> + *
> + * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
> + */
> +struct mux_control *mux_control_get(struct device *dev, const char *mux_name);
> +
> +/**
> + * mux_control_put() - Put away the mux-control for good.
> + * @mux: The mux-control to put away.
> + *
> + * mux_control_put() reverses the effects of mux_control_get().
> + */
> +void mux_control_put(struct mux_control *mux);
> +
> +/**
> + * devm_mux_control_get() - Get the mux-control for a device, with resource
> + * management.
> + * @dev: The device that needs a mux-control.
> + * @mux_name: The name identifying the mux-control.
> + *
> + * Return: Pointer to the mux-control, or an ERR_PTR with a negative errno.
> + */
> +struct mux_control *devm_mux_control_get(struct device *dev,
> + const char *mux_name);
> +
> +/**
> + * 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);
> +
> +#endif /* _LINUX_MUX_H */
>
^ permalink raw reply
* Re: [PATCH v6 1/9] dt-bindings: document devicetree bindings for mux-controllers and mux-gpio
From: Jonathan Cameron @ 2016-12-31 16:10 UTC (permalink / raw)
To: Peter Rosin, linux-kernel
Cc: Wolfram Sang, Rob Herring, Mark Rutland, 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: <1480493823-21462-2-git-send-email-peda@axentia.se>
On 30/11/16 08:16, Peter Rosin wrote:
> Signed-off-by: Peter Rosin <peda@axentia.se>
Bindings are still a bit of a black art to me ;)
Other than the naming of the iio-mux as discussed in the other patch
I'm happy with this. It feels like it has struck the right balance
between flexibility and complexity. Which probably means we'll
have an application it doesn't stretch to before the day is out...
Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> .../devicetree/bindings/misc/mux-controller.txt | 127 +++++++++++++++++++++
> .../devicetree/bindings/misc/mux-gpio.txt | 68 +++++++++++
> MAINTAINERS | 5 +
> 3 files changed, 200 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..19c36b73173e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/misc/mux-controller.txt
> @@ -0,0 +1,127 @@
> +Common multiplexer controller bindings
> +======================================
> +
> +A multiplexer (or mux) controller will have one, or several, consumer devices
> +that uses the mux controller. Thus, a mux controller can possibly control
> +several parallel multiplexers, presumably there will be at least one
> +multiplexer needed by each 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.
> +
> +
> +Consumers
> +---------
> +
> +Mux controller consumers should specify a list of mux controllers that they
> +want to use with a property containing a 'mux-ctrl-list':
> +
> + mux-ctrl-list ::= <single-mux-ctrl> [mux-ctrl-list]
> + single-mux-ctrl ::= <mux-ctrl-phandle> [mux-ctrl-specifier]
> + mux-ctrl-phandle : phandle to mux controller node
> + mux-ctrl-specifier : array of #mux-control-cells specifying the
> + given mux controller (controller specific)
> +
> +Mux controller properties should be named "mux-controls". The exact meaning of
> +each mux controller property must be documented in the device tree binding for
> +each consumer. An optional property "mux-control-names" may contain a list of
> +strings to label each of the mux controllers listed in the "mux-controls"
> +property.
> +
> +Drivers for devices that use more than a single mux controller can use the
> +"mux-control-names" property to map the name of the mux controller requested by
> +the mux_control_get() call to an index into the list given by the
> +"mux-controls" property.
> +
> +mux-ctrl-specifier typically encodes the chip-relative mux controller number.
> +If the mux controller chip only provides a single mux controller, the
> +mux-ctrl-specifier can typically be left out.
> +
> +Example:
> +
> + /* One consumer of a 2-way mux controller (one GPIO-line) */
> + mux: mux-controller {
> + compatible = "mux-gpio";
> + #mux-control-cells = <0>;
> +
> + mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>;
> + };
> +
> + adc-mux {
> + compatible = "iio-mux";
> + io-channels = <&adc 0>;
> + io-channel-names = "parent";
> + mux-controls = <&mux>;
> + mux-control-names = "adc";
> +
> + channels = "sync", "in";
> + };
> +
> +Note that in the example above, specifying the "mux-control-names" is redundant
> +because there is only one mux controller in the list.
> +
> + /*
> + * 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: 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";
> + mux-controls = <&mux>;
> +
> + channels = "sync-1", "in", "out", "sync-2";
> + };
> +
> + i2c-mux {
> + compatible = "i2c-mux-simple,mux-locked";
> + i2c-parent = <&i2c1>;
> + mux-controls = <&mux>;
> +
> + #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 {
> + /* ... */
> + };
> + };
> + };
> +
> +
> +Mux controller nodes
> +--------------------
> +
> +Mux controller nodes must specify the number of cells used for the
> +specifier using the '#mux-control-cells' property.
> +
> +An example mux controller might look like this:
> +
> + mux: adg792a@50 {
> + compatible = "adi,adg792a";
> + reg = <0x50>;
> + #mux-control-cells = <1>;
> + };
> diff --git a/Documentation/devicetree/bindings/misc/mux-gpio.txt b/Documentation/devicetree/bindings/misc/mux-gpio.txt
> new file mode 100644
> index 000000000000..2ff814f082c8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/misc/mux-gpio.txt
> @@ -0,0 +1,68 @@
> +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.
> +- #mux-control-cells : <0>
> +* 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. An active pin is a binary 1, an inactive pin is a binary 0.
> +
> +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";
> +
> + mux-controls = <&mux>;
> +
> + channels = "sync-1", "in", "out", "sync-2";
> + };
> +
> + i2c-mux {
> + compatible = "i2c-mux-simple,mux-locked";
> + i2c-parent = <&i2c1>;
> +
> + mux-controls = <&mux>;
> +
> + #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 d8eb3843dbd4..3d4d0efc2b64 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8403,6 +8403,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
>
^ permalink raw reply
* Re: [PATCH v6 4/9] dt-bindings: iio: iio-mux: document iio-mux bindings
From: Jonathan Cameron @ 2016-12-31 16:01 UTC (permalink / raw)
To: Peter Rosin, Rob Herring
Cc: linux-kernel, Wolfram Sang, Mark Rutland, 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: <2275a7ff-6a21-dd99-ab7d-b213d7fcd6e5@axentia.se>
On 12/12/16 12:18, Peter Rosin wrote:
> On 2016-12-10 19:21, Jonathan Cameron wrote:
>> On 06/12/16 09:18, Peter Rosin wrote:
>>> On 2016-12-06 00:26, Rob Herring wrote:
>>>> On Wed, Nov 30, 2016 at 09:16:58AM +0100, Peter Rosin wrote:
>>>>> Signed-off-by: Peter Rosin <peda@axentia.se>
>>>>> ---
>>>>> .../bindings/iio/multiplexer/iio-mux.txt | 40 ++++++++++++++++++++++
>>>>> MAINTAINERS | 6 ++++
>>>>> 2 files changed, 46 insertions(+)
>>>>> create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
>>>>
>>>> I'm still not convinced about this binding, but don't really have more
>>>> comments ATM. Sending 6 versions in 2 weeks or so doesn't really help
>>>> either.
>>>
>>> Sorry about the noise, I'll try to be more careful going forward. On
>>> the flip side, I haven't touched the code since v6.
>>>
>>> I don't see how bindings that are as flexible as the current (and
>>> original) phandle link between the mux consumer and the mux controller
>>> would look, and at the same time be simpler to understand. You need
>>> to be able to refer to a mux controller from several mux consumers, and
>>> you need to support several mux controllers in one node (the ADG792A
>>> case). And, AFAICT, the complex case wasn't really the problem, it was
>>> that it is overly complex to describe the simple case of one mux
>>> consumer and one mux controller. But in your comment for v2 [1] you
>>> said that I was working around limitations with shared GPIO pins. But
>>> solving that in the GPIO subsystem would not solve all that the
>>> phandle approach is solving, since you would not have support for
>>> ADG792A (or other non-GPIO controlled muxes). So, I think listing
>>> the gpio pins inside the mux consumer node is a non-starter, the mux
>>> controller has to live in its own node with its own compatible.
>>>
>>> Would you be happier if I managed to marry the phandle approach with
>>> the option of having the mux controller as a child node of the mux
>>> consumer for the simple case?
>>>
>>> I added an example at the end of this message (the same as the first
>>> example in v4 [2], at least in principle) for easy comparison between
>>> the phandle and the controller-in-child-node approaches. I can't say
>>> that I personally find the difference all that significant, and do not
>>> think it is worth it. As I see it, the "simple option" would just muddy
>>> the waters...
>>>
>>> [1] http://marc.info/?l=linux-kernel&m=147948334204795&w=2
>>> [2] http://marc.info/?l=linux-kernel&m=148001364904240&w=2
>>>
>>>>> 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..8080cf790d82
>>>>> --- /dev/null
>>>>> +++ b/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
>>>>> @@ -0,0 +1,40 @@
>>>>> +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"
>>>>
>>>> This is a Linuxism. perhaps "adc-mux".
>>>
>>> No, that's not general enough, it could just as well be used to mux a
>>> temperature sensor. Or whatever. Hmmm, given that "iio-mux" is bad, perhaps
>>> "io-channel-mux" is better? That matches the io-channels property used to
>>> refer to the parent channel.
>> analog-mux maybe? Makes more sense out of context (though with io-channels defined on
>> the next line you have plenty of context here ;)
>
> Not that I care all that much about the name, but that doesn't really
> fit if you take e.g. an IIO_INDEX channel. That sounds entirely non-analog
> to me, but what do I know? Maybe that example doesn't make sense for some
> reason, but I can't help but think that there will be some non-analog
> channel in the future, if there isn't one already.
>
> So, my preference is io-channel-mux, as that matches the previous dt
> naming for what is muxed. But that's just my opinion, if I'm told that
> it should be something else, then that's ok.
io-channel-mux works fine for me. It's some sort of input / output channel
and we are muxing it ;)
>
> I'm more worried about other aspects, such as how to get reviewers and who
> is going to take the core mux patches and what tree they are going to be
> merged into etc. That is, if this series is going anywhere at all or if
> someone is going to put up a road-block for some reason...
Whilst it is meeting some resistance, I'm not seeing any absolute blockers
(people tend to be rather explicit about that). The binding is still causing
the most friction I think and it may be that it just needs some more time for
Rob to mull it over. It's a fiddly thing to describe, so was never going
to drop straight in!
The core mux patches probably need to go one of a few possible routes.
1. Directly as a pull to linus with a good collection of Acks.
2. Via Greg KH perhaps as generic driver infrastructure, or Andrew Morton
as being in the category no one else will take.
3. Via either me or Wolfram (as a separate immutable branch) on the basis it's
core stuff but the users currently are IIO and I2C.
In any case, this needs at the very least Acks from Rob, Wolfram and myself.
Others would be most welcome, Arnd and/or Greg might be persuaded to take
a look for example...
Happy New Year,
Jonathan
>
> Cheers,
> peda
>
^ permalink raw reply
* Re: [PATCH v6 3/9] iio: inkern: api for manipulating ext_info of iio channels
From: Jonathan Cameron @ 2016-12-31 15:51 UTC (permalink / raw)
To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Wolfram Sang, Rob Herring, Mark Rutland, 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: <1480493823-21462-4-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
On 30/11/16 08:16, Peter Rosin wrote:
> Extend the inkern api with functions for reading and writing ext_info
> of iio channels.
>
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
It may make more sense to take this particular patch separately via
IIO, but as the churn on this file is fairly low I think it is probably
going to be easier to take it with the rest of the series if / when that
heads upstream.
Jonathan
> ---
> 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
>
^ permalink raw reply
* Re: [PATCH v2 1/2] devicetree: add Garmin vendor prefix
From: Jonathan Cameron @ 2016-12-31 15:19 UTC (permalink / raw)
To: Matt Ranostay
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring
In-Reply-To: <CAJ_EiSTQ6naDiRP6o1UOr33UyKDtXaan2j4UPFNMTRKReuR-PQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 31/12/16 05:55, Matt Ranostay wrote:
> On Fri, Dec 30, 2016 at 12:26 PM, Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>> On 29/12/16 05:13, Matt Ranostay wrote:
>>> Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>>> Signed-off-by: Matt Ranostay <matt-sk+viVC6FLCDq+mSdOJa79kegs52MxvZ@public.gmane.org>
>> Rob, just to check - is this the right way to go with prefixes?
>> It's awfully ugly in this case ;)
>
> Rob said the stock ticker (likely the US exchanges) is the way to go :)
Fair enough. Both applied to the togreg branch of iio.git and pushed
out as testing.
Thanks,
Jonathan
>
>>
>> Jonathan
>>> ---
>>> Changes from v1:
>>> * switch to stock ticker for Garmin Limited
>>>
>>> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>>> 1 file changed, 1 insertion(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>>> index 16d3b5e7f5d1..5749bfc5fc5b 100644
>>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>>> @@ -107,6 +107,7 @@ firefly Firefly
>>> focaltech FocalTech Systems Co.,Ltd
>>> friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd
>>> fsl Freescale Semiconductor
>>> +grmn Garmin Limited
>>> ge General Electric Company
>>> geekbuying GeekBuying
>>> gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
>>>
>>
^ permalink raw reply
* Re: [PATCH v5 3/4] clk: rockchip: add new pll-type for rk3328
From: Heiko Stuebner @ 2016-12-31 12:53 UTC (permalink / raw)
To: Elaine Zhang
Cc: mturquette, sboyd, xf, robh+dt, mark.rutland, linux-clk, huangtao,
xxx, cl, linux-rockchip, linux-kernel, devicetree,
linux-arm-kernel
In-Reply-To: <1482979511-6847-4-git-send-email-zhangqing@rock-chips.com>
Am Donnerstag, 29. Dezember 2016, 10:45:10 CET schrieb Elaine Zhang:
> The rk3328's pll and clock are similar with rk3036's,
> it different with pll_mode_mask, the rk3328 soc
> pll mode only one bit(rk3036 soc have two bits)
> so these should be independent and separate from
> the series of rk3328s.
>
> Changes in v4:
> adjust the pacth 3 and 4 order.
> move pll_rk3328 to patch 3.
> Changes in v3:
> fix up the pll type pll_rk3328 description and use
>
> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
applied to my clk-branch for 4.11
The clock controller itself also looks good now, I'll just give Rob or someone
else a bit of time for eventual comments after new years :-)
Heiko
^ permalink raw reply
* Re: [PATCH v2 1/2] devicetree: add Garmin vendor prefix
From: Matt Ranostay @ 2016-12-31 5:55 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring
In-Reply-To: <144bd616-1b5a-970a-edcb-13ebf67178f1-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On Fri, Dec 30, 2016 at 12:26 PM, Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On 29/12/16 05:13, Matt Ranostay wrote:
>> Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> Signed-off-by: Matt Ranostay <matt-sk+viVC6FLCDq+mSdOJa79kegs52MxvZ@public.gmane.org>
> Rob, just to check - is this the right way to go with prefixes?
> It's awfully ugly in this case ;)
Rob said the stock ticker (likely the US exchanges) is the way to go :)
>
> Jonathan
>> ---
>> Changes from v1:
>> * switch to stock ticker for Garmin Limited
>>
>> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index 16d3b5e7f5d1..5749bfc5fc5b 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -107,6 +107,7 @@ firefly Firefly
>> focaltech FocalTech Systems Co.,Ltd
>> friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd
>> fsl Freescale Semiconductor
>> +grmn Garmin Limited
>> ge General Electric Company
>> geekbuying GeekBuying
>> gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
>>
>
--
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] iio: adc: Add Renesas GyroADC driver
From: Marek Vasut @ 2016-12-30 21:49 UTC (permalink / raw)
To: Peter Meerwald-Stadler
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven,
Simon Horman
In-Reply-To: <alpine.DEB.2.02.1612302205210.3977-jW+XmwGofnusTnJN9+BGXg@public.gmane.org>
On 12/30/2016 10:09 PM, Peter Meerwald-Stadler wrote:
>
>>>> Add IIO driver for the Renesas RCar GyroADC block. This block is a
>>>> simple 4/8-channel ADC which samples 12/15/24 bits of data every
>
>>>> + if (iio_buffer_enabled(indio_dev))
>>>> + return -EBUSY;
>>>
>>> use iio_device_claim_direct_mode()
>>
>> Why ? Do I really need the mutex locking here ?
>
> no, not needed, since you do not support buffered mode yet; but neither do
> you need iio_buffer_enabled()
>
> p.
>
So drop the whole thing then ?
--
Best regards,
Marek Vasut
--
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 2/2] cfg80211: reg: support ieee80211-(min|max)-center-freq DT properties
From: Rafał Miłecki @ 2016-12-30 21:37 UTC (permalink / raw)
To: Arend van Spriel
Cc: Kalle Valo,
linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Martin Blumenstingl, Felix Fietkau, Arnd Bergmann,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Rafał Miłecki
In-Reply-To: <86a22b00-1a04-25e7-9d31-2c1fd9d04e48-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
On 30 December 2016 at 21:20, Arend van Spriel
<arend.vanspriel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> wrote:
> On 29-12-16 10:43, Rafał Miłecki wrote:
>> On 29 December 2016 at 09:57, Arend van Spriel
>> <arend.vanspriel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> wrote:
>>> On 28-12-16 22:30, Rafał Miłecki wrote:
>>>> On 28 December 2016 at 22:28, Rafał Miłecki <zajec5-Re5JQEeQqe8@public.gmane.orgm> wrote:
>>>>> On 28 December 2016 at 22:07, Arend van Spriel
>>>>> <arend.vanspriel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> wrote:
>>>>>> On 28-12-16 16:59, Rafał Miłecki wrote:
>>>>>>> From: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
>>>>>>>
>>>>>>> They allow specifying hardware limitations of supported channels. This
>>>>>>> may be useful for specifying single band devices or devices that support
>>>>>>> only some part of the whole band.
>>>>>>> E.g. some tri-band routers have separated radios for lower and higher
>>>>>>> part of 5 GHz band.
>>>>>>>
>>>>>>> Signed-off-by: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
>>>>>>> ---
>>>>>>> net/wireless/reg.c | 34 ++++++++++++++++++++++++++++++++++
>>>>>>> 1 file changed, 34 insertions(+)
>>>>>>>
>>>>>>> diff --git a/net/wireless/reg.c b/net/wireless/reg.c
>>>>>>> index 5dbac37..35ba5c7 100644
>>>>>>> --- a/net/wireless/reg.c
>>>>>>> +++ b/net/wireless/reg.c
>>>>>>> @@ -1123,6 +1123,26 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
>>>>>>> }
>>>>>>> EXPORT_SYMBOL(reg_initiator_name);
>>>>>>>
>>>>>>> +static bool reg_center_freq_of_valid(struct wiphy *wiphy,
>>>>>>> + struct ieee80211_channel *chan)
>>>>>>> +{
>>>>>>> + struct device_node *np = wiphy_dev(wiphy)->of_node;
>>>>>>> + u32 val;
>>>>>>> +
>>>>>>> + if (!np)
>>>>>>> + return true;
>>>>>>> +
>>>>>>> + if (!of_property_read_u32(np, "ieee80211-min-center-freq", &val) &&
>>>>>>> + chan->center_freq < KHZ_TO_MHZ(val))
>>>>>>> + return false;
>>>>>>> +
>>>>>>> + if (!of_property_read_u32(np, "ieee80211-max-center-freq", &val) &&
>>>>>>> + chan->center_freq > KHZ_TO_MHZ(val))
>>>>>>> + return false;
>>>>>>
>>>>>> I suspect these functions rely on CONFIG_OF. So might not build for
>>>>>> !CONFIG_OF.
>>>>>
>>>>> I compiled it with
>>>>> # CONFIG_OF is not set
>>>>>
>>>>> Can you test it and provide a non-working config if you see a
>>>>> compilation error, please?
>>>>
>>>> include/linux/of.h provides a lot of dummy static inline functions if
>>>> CONFIG_OF is not used (they also allow compiler to drop most of the
>>>> code).
>>>
>>> of_propeirty_read_u32 is static inline in of.h calling
>>> of_property_read_u32_array, which has a dummy variant in of.h returning
>>> -ENOSYS so -38. Pretty sure that is not what you want. At least it does
>>> not allow the compiler to drop any code so probably better to do:
>>>
>>> if (!IS_ENABLED(CONFIG_OF) || !np)
>>> return true;
>>
>> Please verify that using a compiler. If there is a problem I'll be
>> happy to work on it, but I need a proof it exists.
>
> I am on vacation right now so not having much more than email and web
> browser to use as review reference.
>
>> If compilers sees a:
>> if (!-ENOSYS && chan->center_freq < KHZ_TO_MHZ(val))
>> condition, it's pretty clear it can be dropped. With both conditional
>> blocks dropped function always returns "true" and... can be dropped.
>>
>> Let me see if I can convince you with the following test:
>
> No need to convince me. I made a mistake reviewing the code. Thanks for
> clarifying it.
>
>> $ grep -m 1 CONFIG_OF .config
>> # CONFIG_OF is not set
>> $ objdump --syms net/wireless/reg.o | grep -c reg_center_freq_of_valid
>> 0
>>
>> $ grep -m 1 CONFIG_OF .config
>> CONFIG_OF=y
>> $ objdump --syms net/wireless/reg.o | grep -c reg_center_freq_of_valid
>> 1
>>
>>
>>> So with this patch you change the channel to DISABLED. I am not very
>>> familiar with reg.c so do you know if this is done before or after
>>> calling regulatory notifier in the driver. brcmfmac will enable channels
>>> querying the device upon regulatory notifier call, which may undo the
>>> behavior introduced by your patch.
>>
>> I'm not regulatory export, so I hope someone will review this patch.
>> So far I can say it works for me after trying it on SR400ac with
>> BCM43602.
>
> But you probably do not have a mapping table for mapping country code
> received in notifier to firmware regulatory code/revision. Only if you
> have that, brcmfmac will update the channels in the bands.
Thanks, I'll redo my tests.
> Giving this some more consideration I am not sure if this is the proper
> place to handle this. ieee80211-(min|max)-center-freq is platform
> specific configuration allowing multiple cards to be used in different
> (sub)bands. This has nothing to do with regulatory. So probably better
> to move it to core.c or chan.c.
I can see point in this, I'll see if I can put this code in a more
proper place. Thanks for your comment!
--
Rafał
--
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 v6 6/8] IIO: add STM32 timer trigger driver
From: Jonathan Cameron @ 2016-12-30 21:12 UTC (permalink / raw)
To: Benjamin Gaignard, lee.jones-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
alexandre.torgue-qxv4g6HH51o, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
linux-pwm-u79uwXL29TY76Z2rM5mHXA, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: fabrice.gasnier-qxv4g6HH51o, gerald.baeza-qxv4g6HH51o,
arnaud.pouliquen-qxv4g6HH51o,
linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
linaro-kernel-cunTk1MwBs8s++Sfvej+rw, Benjamin Gaignard
In-Reply-To: <1481292919-26587-7-git-send-email-benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
On 09/12/16 14:15, Benjamin Gaignard wrote:
> Timers IPs can be used to generate triggers for other IPs like
> DAC, ADC or other timers.
> Each trigger may result of timer internals signals like counter enable,
> reset or edge, this configuration could be done through "master_mode"
> device attribute.
>
> A timer device could be triggered by other timers, we use the trigger
> name and is_stm32_iio_timer_trigger() function to distinguish them
> and configure IP input switch.
>
> Timer may also decide on which event (edge, level) they could
> be activated by a trigger, this configuration is done by writing in
> "slave_mode" device attribute.
>
> Since triggers could also be used by DAC or ADC their names are defined
> in include/ nux/iio/timer/stm32-timer-trigger.h so those IPs will be able
> to configure themselves in valid_trigger function
>
> Trigger have a "sampling_frequency" attribute which allow to configure
> timer sampling frequency without using PWM interface
>
> version 5:
> - simplify tables of triggers
> - only create an IIO device when needed
>
> version 4:
> - get triggers configuration from "reg" in DT
> - add tables of triggers
> - sampling frequency is enable/disable when writing in trigger
> sampling_frequency attribute
> - no more use of interruptions
>
> version 3:
> - change compatible to "st,stm32-timer-trigger"
> - fix attributes access right
> - use string instead of int for master_mode and slave_mode
> - document device attributes in sysfs-bus-iio-timer-stm32
>
> version 2:
> - keep only one compatible
> - use st,input-triggers-names and st,output-triggers-names
> to know which triggers are accepted and/or create by the device
Firstly, sorry it has taken me so long to get back to this.
I'm still not keen on this use of iio_device elements just to act as
glue between triggers. I think we need to work out a more light weight
way to do this. As you are only using them for validation and to provide
somewhere to hang the control attibutes off, there is nothing stopping us
moving that over to the iio_trigger instead which would avoid the messy
duality going on here.
I might still be missing something though!
You would only I think need 3 attributes
parrent_trigger
and something like your master_mode and slave_mode attributes.
The parrent_trigger would need some validation etc, but if we keep it
within this driver initially that won't be hard to do. Checking the device
parent matches will do most of it.
Jonathan
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
> ---
> .../ABI/testing/sysfs-bus-iio-timer-stm32 | 55 +++
> drivers/iio/Kconfig | 2 +-
> drivers/iio/Makefile | 1 +
> drivers/iio/timer/Kconfig | 13 +
> drivers/iio/timer/Makefile | 1 +
> drivers/iio/timer/stm32-timer-trigger.c | 466 +++++++++++++++++++++
> drivers/iio/trigger/Kconfig | 1 -
> include/linux/iio/timer/stm32-timer-trigger.h | 62 +++
> 8 files changed, 599 insertions(+), 2 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-timer-stm32
> create mode 100644 drivers/iio/timer/Kconfig
> create mode 100644 drivers/iio/timer/Makefile
> create mode 100644 drivers/iio/timer/stm32-timer-trigger.c
> create mode 100644 include/linux/iio/timer/stm32-timer-trigger.h
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32
> new file mode 100644
> index 0000000..26583dd
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32
> @@ -0,0 +1,55 @@
> +What: /sys/bus/iio/devices/iio:deviceX/master_mode_available
> +KernelVersion: 4.10
> +Contact: benjamin.gaignard-qxv4g6HH51o@public.gmane.org
> +Description:
> + Reading returns the list possible master modes which are:
> + - "reset" : The UG bit from the TIMx_EGR register is used as trigger output (TRGO).
> + - "enable" : The Counter Enable signal CNT_EN is used as trigger output.
> + - "update" : The update event is selected as trigger output.
> + For instance a master timer can then be used as a prescaler for a slave timer.
> + - "compare_pulse" : The trigger output send a positive pulse when the CC1IF flag is to be set.
> + - "OC1REF" : OC1REF signal is used as trigger output.
> + - "OC2REF" : OC2REF signal is used as trigger output.
> + - "OC3REF" : OC3REF signal is used as trigger output.
> + - "OC4REF" : OC4REF signal is used as trigger output.
> +
> +What: /sys/bus/iio/devices/iio:deviceX/master_mode
> +KernelVersion: 4.10
> +Contact: benjamin.gaignard-qxv4g6HH51o@public.gmane.org
> +Description:
> + Reading returns the current master modes.
> + Writing set the master mode
> +
> +What: /sys/bus/iio/devices/iio:deviceX/slave_mode_available
> +KernelVersion: 4.10
> +Contact: benjamin.gaignard-qxv4g6HH51o@public.gmane.org
> +Description:
> + Reading returns the list possible slave modes which are:
> + - "disabled" : The prescaler is clocked directly by the internal clock.
> + - "encoder_1" : Counter counts up/down on TI2FP1 edge depending on TI1FP2 level.
> + - "encoder_2" : Counter counts up/down on TI1FP2 edge depending on TI2FP1 level.
> + - "encoder_3" : Counter counts up/down on both TI1FP1 and TI2FP2 edges depending
> + on the level of the other input.
> + - "reset" : Rising edge of the selected trigger input reinitializes the counter
> + and generates an update of the registers.
> + - "gated" : The counter clock is enabled when the trigger input is high.
> + The counter stops (but is not reset) as soon as the trigger becomes low.
> + Both start and stop of the counter are controlled.
> + - "trigger" : The counter starts at a rising edge of the trigger TRGI (but it is not
> + reset). Only the start of the counter is controlled.
> + - "external_clock": Rising edges of the selected trigger (TRGI) clock the counter.
> +
> +What: /sys/bus/iio/devices/iio:deviceX/slave_mode
> +KernelVersion: 4.10
> +Contact: benjamin.gaignard-qxv4g6HH51o@public.gmane.org
> +Description:
> + Reading returns the current slave mode.
> + Writing set the slave mode
> +
> +What: /sys/bus/iio/devices/triggerX/sampling_frequency
> +KernelVersion: 4.10
> +Contact: benjamin.gaignard-qxv4g6HH51o@public.gmane.org
> +Description:
> + Reading returns the current sampling frequency.
> + Writing an value different of 0 set and start sampling.
> + Writing 0 stop sampling.
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 6743b18..2de2a80 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -90,5 +90,5 @@ source "drivers/iio/potentiometer/Kconfig"
> source "drivers/iio/pressure/Kconfig"
> source "drivers/iio/proximity/Kconfig"
> source "drivers/iio/temperature/Kconfig"
> -
> +source "drivers/iio/timer/Kconfig"
> endif # IIO
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 87e4c43..b797c08 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -32,4 +32,5 @@ obj-y += potentiometer/
> obj-y += pressure/
> obj-y += proximity/
> obj-y += temperature/
> +obj-y += timer/
> obj-y += trigger/
> diff --git a/drivers/iio/timer/Kconfig b/drivers/iio/timer/Kconfig
> new file mode 100644
> index 0000000..e3c21f2
> --- /dev/null
> +++ b/drivers/iio/timer/Kconfig
> @@ -0,0 +1,13 @@
> +#
> +# Timers drivers
> +
> +menu "Timers"
> +
> +config IIO_STM32_TIMER_TRIGGER
> + tristate "STM32 Timer Trigger"
> + depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST
> + select IIO_TRIGGERED_EVENT
> + help
> + Select this option to enable STM32 Timer Trigger
> +
> +endmenu
> diff --git a/drivers/iio/timer/Makefile b/drivers/iio/timer/Makefile
> new file mode 100644
> index 0000000..4ad95ec9
> --- /dev/null
> +++ b/drivers/iio/timer/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o
> diff --git a/drivers/iio/timer/stm32-timer-trigger.c b/drivers/iio/timer/stm32-timer-trigger.c
> new file mode 100644
> index 0000000..8d16e8f
> --- /dev/null
> +++ b/drivers/iio/timer/stm32-timer-trigger.c
> @@ -0,0 +1,466 @@
> +/*
> + * Copyright (C) STMicroelectronics 2016
> + *
> + * Author: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/timer/stm32-timer-trigger.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/triggered_event.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/stm32-timers.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#define MAX_TRIGGERS 6
> +#define MAX_VALIDS 5
> +
> +/* List the triggers created by each timer */
> +static const void *triggers_table[][MAX_TRIGGERS] = {
> + { TIM1_TRGO, TIM1_CH1, TIM1_CH2, TIM1_CH3, TIM1_CH4,},
> + { TIM2_TRGO, TIM2_CH1, TIM2_CH2, TIM2_CH3, TIM2_CH4,},
> + { TIM3_TRGO, TIM3_CH1, TIM3_CH2, TIM3_CH3, TIM3_CH4,},
> + { TIM4_TRGO, TIM4_CH1, TIM4_CH2, TIM4_CH3, TIM4_CH4,},
> + { TIM5_TRGO, TIM5_CH1, TIM5_CH2, TIM5_CH3, TIM5_CH4,},
> + { TIM6_TRGO,},
> + { TIM7_TRGO,},
> + { TIM8_TRGO, TIM8_CH1, TIM8_CH2, TIM8_CH3, TIM8_CH4,},
> + { TIM9_TRGO, TIM9_CH1, TIM9_CH2,},
> + { TIM12_TRGO, TIM12_CH1, TIM12_CH2,},
> +};
> +
> +/* List the triggers accepted by each timer */
> +static const void *valids_table[][MAX_VALIDS] = {
> + { TIM5_TRGO, TIM2_TRGO, TIM4_TRGO, TIM3_TRGO,},
> + { TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,},
> + { TIM1_TRGO, TIM8_TRGO, TIM5_TRGO, TIM4_TRGO,},
> + { TIM1_TRGO, TIM2_TRGO, TIM3_TRGO, TIM8_TRGO,},
> + { TIM2_TRGO, TIM3_TRGO, TIM4_TRGO, TIM8_TRGO,},
> + { }, /* timer 6 */
> + { }, /* timer 7 */
> + { TIM1_TRGO, TIM2_TRGO, TIM4_TRGO, TIM5_TRGO,},
> + { TIM2_TRGO, TIM3_TRGO,},
> + { TIM4_TRGO, TIM5_TRGO,},
> +};
> +
> +struct stm32_timer_trigger {
> + struct device *dev;
> + struct regmap *regmap;
> + struct clk *clk;
> + u32 max_arr;
> + const void *triggers;
> + const void *valids;
> +};
> +
> +static int stm32_timer_start(struct stm32_timer_trigger *priv,
> + unsigned int frequency)
> +{
> + unsigned long long prd, div;
> + int prescaler = 0;
> + u32 ccer, cr1;
> +
> + /* Period and prescaler values depends of clock rate */
> + div = (unsigned long long)clk_get_rate(priv->clk);
> +
> + do_div(div, frequency);
> +
> + prd = div;
> +
> + /*
> + * Increase prescaler value until we get a result that fit
> + * with auto reload register maximum value.
> + */
> + while (div > priv->max_arr) {
> + prescaler++;
> + div = prd;
> + do_div(div, (prescaler + 1));
> + }
> + prd = div;
> +
> + if (prescaler > MAX_TIM_PSC) {
> + dev_err(priv->dev, "prescaler exceeds the maximum value\n");
> + return -EINVAL;
> + }
> +
> + /* Check if nobody else use the timer */
> + regmap_read(priv->regmap, TIM_CCER, &ccer);
> + if (ccer & TIM_CCER_CCXE)
> + return -EBUSY;
> +
> + regmap_read(priv->regmap, TIM_CR1, &cr1);
> + if (!(cr1 & TIM_CR1_CEN))
> + clk_enable(priv->clk);
> +
> + regmap_write(priv->regmap, TIM_PSC, prescaler);
> + regmap_write(priv->regmap, TIM_ARR, prd - 1);
> + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE);
> +
> + /* Force master mode to update mode */
> + regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS, 0x20);
> +
> + /* Make sure that registers are updated */
> + regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
> +
> + /* Enable controller */
> + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
> +
> + return 0;
> +}
> +
> +static void stm32_timer_stop(struct stm32_timer_trigger *priv)
> +{
> + u32 ccer, cr1;
> +
> + regmap_read(priv->regmap, TIM_CCER, &ccer);
> + if (ccer & TIM_CCER_CCXE)
> + return;
> +
> + regmap_read(priv->regmap, TIM_CR1, &cr1);
> + if (cr1 & TIM_CR1_CEN)
> + clk_disable(priv->clk);
> +
> + /* Stop timer */
> + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
> + regmap_write(priv->regmap, TIM_PSC, 0);
> + regmap_write(priv->regmap, TIM_ARR, 0);
> +}
> +
> +static ssize_t stm32_tt_store_frequency(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_trigger *trig = to_iio_trigger(dev);
> + struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig);
> + unsigned int freq;
> + int ret;
> +
> + ret = kstrtouint(buf, 10, &freq);
> + if (ret)
> + return ret;
> +
> + if (freq == 0) {
> + stm32_timer_stop(priv);
> + } else {
> + ret = stm32_timer_start(priv, freq);
> + if (ret)
> + return ret;
> + }
> +
> + return len;
> +}
> +
> +static ssize_t stm32_tt_read_frequency(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct iio_trigger *trig = to_iio_trigger(dev);
> + struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig);
> + u32 psc, arr, cr1;
> + unsigned long long freq = 0;
> +
> + regmap_read(priv->regmap, TIM_CR1, &cr1);
> + regmap_read(priv->regmap, TIM_PSC, &psc);
> + regmap_read(priv->regmap, TIM_ARR, &arr);
> +
> + if (psc && arr && (cr1 & TIM_CR1_CEN)) {
> + freq = (unsigned long long)clk_get_rate(priv->clk);
> + do_div(freq, psc);
> + do_div(freq, arr);
> + }
> +
> + return sprintf(buf, "%d\n", (unsigned int)freq);
> +}
> +
> +static IIO_DEV_ATTR_SAMP_FREQ(0660,
> + stm32_tt_read_frequency,
> + stm32_tt_store_frequency);
> +
> +static struct attribute *stm32_trigger_attrs[] = {
> + &iio_dev_attr_sampling_frequency.dev_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group stm32_trigger_attr_group = {
> + .attrs = stm32_trigger_attrs,
> +};
> +
> +static const struct attribute_group *stm32_trigger_attr_groups[] = {
> + &stm32_trigger_attr_group,
> + NULL,
> +};
> +
> +static char *master_mode_table[] = {
> + "reset",
> + "enable",
> + "update",
> + "compare_pulse",
> + "OC1REF",
> + "OC2REF",
> + "OC3REF",
> + "OC4REF"
> +};
> +
> +static ssize_t stm32_tt_show_master_mode(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct stm32_timer_trigger *priv = iio_priv(indio_dev);
> + u32 cr2;
> +
> + regmap_read(priv->regmap, TIM_CR2, &cr2);
> + cr2 = (cr2 & TIM_CR2_MMS) >> TIM_CR2_MMS_SHIFT;
> +
> + return snprintf(buf, PAGE_SIZE, "%s\n", master_mode_table[cr2]);
> +}
> +
> +static ssize_t stm32_tt_store_master_mode(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct stm32_timer_trigger *priv = iio_priv(indio_dev);
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(master_mode_table); i++) {
> + if (!strncmp(master_mode_table[i], buf,
> + strlen(master_mode_table[i]))) {
> + regmap_update_bits(priv->regmap, TIM_CR2,
> + TIM_CR2_MMS, i << TIM_CR2_MMS_SHIFT);
> + return len;
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +static IIO_CONST_ATTR(master_mode_available,
> + "reset enable update compare_pulse OC1REF OC2REF OC3REF OC4REF");
> +
> +static IIO_DEVICE_ATTR(master_mode, 0660,
> + stm32_tt_show_master_mode,
> + stm32_tt_store_master_mode,
> + 0);
> +
> +static char *slave_mode_table[] = {
> + "disabled",
> + "encoder_1",
> + "encoder_2",
> + "encoder_3",
> + "reset",
> + "gated",
> + "trigger",
> + "external_clock",
> +};
> +
> +static ssize_t stm32_tt_show_slave_mode(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct stm32_timer_trigger *priv = iio_priv(indio_dev);
> + u32 smcr;
> +
> + regmap_read(priv->regmap, TIM_SMCR, &smcr);
> + smcr &= TIM_SMCR_SMS;
> +
> + return snprintf(buf, PAGE_SIZE, "%s\n", slave_mode_table[smcr]);
> +}
> +
> +static ssize_t stm32_tt_store_slave_mode(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct stm32_timer_trigger *priv = iio_priv(indio_dev);
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(slave_mode_table); i++) {
> + if (!strncmp(slave_mode_table[i], buf,
> + strlen(slave_mode_table[i]))) {
> + regmap_update_bits(priv->regmap,
> + TIM_SMCR, TIM_SMCR_SMS, i);
> + return len;
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +static IIO_CONST_ATTR(slave_mode_available,
> +"disabled encoder_1 encoder_2 encoder_3 reset gated trigger external_clock");
> +
> +static IIO_DEVICE_ATTR(slave_mode, 0660,
> + stm32_tt_show_slave_mode,
> + stm32_tt_store_slave_mode,
> + 0);
> +
> +static struct attribute *stm32_timer_attrs[] = {
> + &iio_dev_attr_master_mode.dev_attr.attr,
> + &iio_const_attr_master_mode_available.dev_attr.attr,
> + &iio_dev_attr_slave_mode.dev_attr.attr,
> + &iio_const_attr_slave_mode_available.dev_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group stm32_timer_attr_group = {
> + .attrs = stm32_timer_attrs,
> +};
> +
> +static const struct iio_trigger_ops timer_trigger_ops = {
> + .owner = THIS_MODULE,
> +};
> +
> +static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv)
> +{
> + int ret;
> + const char * const *cur = priv->triggers;
> +
> + while (cur && *cur) {
> + struct iio_trigger *trig;
> +
> + trig = devm_iio_trigger_alloc(priv->dev, "%s", *cur);
> + if (!trig)
> + return -ENOMEM;
> +
> + trig->dev.parent = priv->dev->parent;
> + trig->ops = &timer_trigger_ops;
> + trig->dev.groups = stm32_trigger_attr_groups;
> + iio_trigger_set_drvdata(trig, priv);
> +
> + ret = devm_iio_trigger_register(priv->dev, trig);
> + if (ret)
> + return ret;
> + cur++;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * is_stm32_timer_trigger
> + * @trig: trigger to be checked
> + *
> + * return true if the trigger is a valid stm32 iio timer trigger
> + * either return false
> + */
> +bool is_stm32_timer_trigger(struct iio_trigger *trig)
> +{
> + return (trig->ops == &timer_trigger_ops);
> +}
> +EXPORT_SYMBOL(is_stm32_timer_trigger);
> +
> +static int stm32_validate_trigger(struct iio_dev *indio_dev,
> + struct iio_trigger *trig)
> +{
> + struct stm32_timer_trigger *priv = iio_priv(indio_dev);
> + const char * const *cur = priv->valids;
> + unsigned int i = 0;
> +
> + if (!is_stm32_timer_trigger(trig))
> + return -EINVAL;
> +
> + while (cur && *cur) {
> + if (!strncmp(trig->name, *cur, strlen(trig->name))) {
> + regmap_update_bits(priv->regmap,
> + TIM_SMCR, TIM_SMCR_TS,
> + i << TIM_SMCR_TS_SHIFT);
> + return 0;
> + }
> + cur++;
> + i++;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const struct iio_info stm32_trigger_info = {
> + .driver_module = THIS_MODULE,
> + .validate_trigger = stm32_validate_trigger,
> + .attrs = &stm32_timer_attr_group,
> +};
> +
> +static struct stm32_timer_trigger *stm32_setup_iio_device(struct device *dev)
> +{
> + struct iio_dev *indio_dev;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(dev,
> + sizeof(struct stm32_timer_trigger));
> + if (!indio_dev)
> + return NULL;
> +
> + indio_dev->name = dev_name(dev);
> + indio_dev->dev.parent = dev;
> + indio_dev->info = &stm32_trigger_info;
> + indio_dev->modes = INDIO_EVENT_TRIGGERED;
> + indio_dev->num_channels = 0;
> + indio_dev->dev.of_node = dev->of_node;
> +
> + ret = devm_iio_device_register(dev, indio_dev);
> + if (ret)
> + return NULL;
> +
> + return iio_priv(indio_dev);
> +}
> +
> +static int stm32_timer_trigger_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct stm32_timer_trigger *priv;
> + struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent);
> + unsigned int index;
> + int ret;
> +
> + if (of_property_read_u32(dev->of_node, "reg", &index))
> + return -EINVAL;
> +
> + if (index >= ARRAY_SIZE(triggers_table))
> + return -EINVAL;
> +
> + /* Create an IIO device only if we have triggers to be validated */
> + if (*valids_table[index])
> + priv = stm32_setup_iio_device(dev);
I still don't like this. Really feels like we shouldn't be creating an
iio device with all the bagage that carries just to allow us to do the
trigger trees. We ought to have a much more light weight solution for this
functionality - we aren't typically even using the interrupt tree stuff
that the triggers for devices are all really about.
A simpler approach of allowing each trigger the option of a parent seems like
it would be cleaner. Could be done entirely within this driver in the first
instance. Basically it would just look like your master and slave attributes
but have those under triggerX not iio:deviceX.
We can work out how to make it more generic later - including perhaps the
option to trigger from triggers outside this driver, using some parallel
infrastructure to the device triggering.
> + else
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->dev = dev;
> + priv->regmap = ddata->regmap;
> + priv->clk = ddata->clk;
> + priv->max_arr = ddata->max_arr;
> + priv->triggers = triggers_table[index];
> + priv->valids = valids_table[index];
> +
> + ret = stm32_setup_iio_triggers(priv);
> + if (ret)
> + return ret;
> +
> + platform_set_drvdata(pdev, priv);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id stm32_trig_of_match[] = {
> + { .compatible = "st,stm32-timer-trigger", },
> + { /* end node */ },
> +};
> +MODULE_DEVICE_TABLE(of, stm32_trig_of_match);
> +
> +static struct platform_driver stm32_timer_trigger_driver = {
> + .probe = stm32_timer_trigger_probe,
> + .driver = {
> + .name = "stm32-timer-trigger",
> + .of_match_table = stm32_trig_of_match,
> + },
> +};
> +module_platform_driver(stm32_timer_trigger_driver);
> +
> +MODULE_ALIAS("platform: stm32-timer-trigger");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 Timer Trigger driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
> index 809b2e7..f2af4fe 100644
> --- a/drivers/iio/trigger/Kconfig
> +++ b/drivers/iio/trigger/Kconfig
> @@ -46,5 +46,4 @@ config IIO_SYSFS_TRIGGER
>
> To compile this driver as a module, choose M here: the
> module will be called iio-trig-sysfs.
> -
Clean this up.
> endmenu
> diff --git a/include/linux/iio/timer/stm32-timer-trigger.h b/include/linux/iio/timer/stm32-timer-trigger.h
> new file mode 100644
> index 0000000..55535ae
> --- /dev/null
> +++ b/include/linux/iio/timer/stm32-timer-trigger.h
> @@ -0,0 +1,62 @@
> +/*
> + * Copyright (C) STMicroelectronics 2016
> + *
> + * Author: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STM32_TIMER_TRIGGER_H_
> +#define _STM32_TIMER_TRIGGER_H_
> +
> +#define TIM1_TRGO "tim1_trgo"
> +#define TIM1_CH1 "tim1_ch1"
> +#define TIM1_CH2 "tim1_ch2"
> +#define TIM1_CH3 "tim1_ch3"
> +#define TIM1_CH4 "tim1_ch4"
> +
> +#define TIM2_TRGO "tim2_trgo"
> +#define TIM2_CH1 "tim2_ch1"
> +#define TIM2_CH2 "tim2_ch2"
> +#define TIM2_CH3 "tim2_ch3"
> +#define TIM2_CH4 "tim2_ch4"
> +
> +#define TIM3_TRGO "tim3_trgo"
> +#define TIM3_CH1 "tim3_ch1"
> +#define TIM3_CH2 "tim3_ch2"
> +#define TIM3_CH3 "tim3_ch3"
> +#define TIM3_CH4 "tim3_ch4"
> +
> +#define TIM4_TRGO "tim4_trgo"
> +#define TIM4_CH1 "tim4_ch1"
> +#define TIM4_CH2 "tim4_ch2"
> +#define TIM4_CH3 "tim4_ch3"
> +#define TIM4_CH4 "tim4_ch4"
> +
> +#define TIM5_TRGO "tim5_trgo"
> +#define TIM5_CH1 "tim5_ch1"
> +#define TIM5_CH2 "tim5_ch2"
> +#define TIM5_CH3 "tim5_ch3"
> +#define TIM5_CH4 "tim5_ch4"
> +
> +#define TIM6_TRGO "tim6_trgo"
> +
> +#define TIM7_TRGO "tim7_trgo"
> +
> +#define TIM8_TRGO "tim8_trgo"
> +#define TIM8_CH1 "tim8_ch1"
> +#define TIM8_CH2 "tim8_ch2"
> +#define TIM8_CH3 "tim8_ch3"
> +#define TIM8_CH4 "tim8_ch4"
> +
> +#define TIM9_TRGO "tim9_trgo"
> +#define TIM9_CH1 "tim9_ch1"
> +#define TIM9_CH2 "tim9_ch2"
> +
> +#define TIM12_TRGO "tim12_trgo"
> +#define TIM12_CH1 "tim12_ch1"
> +#define TIM12_CH2 "tim12_ch2"
> +
> +bool is_stm32_timer_trigger(struct iio_trigger *trig);
> +
> +#endif
>
^ permalink raw reply
* Re: [PATCH] iio: adc: Add Renesas GyroADC driver
From: Peter Meerwald-Stadler @ 2016-12-30 21:09 UTC (permalink / raw)
To: Marek Vasut
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven,
Simon Horman
In-Reply-To: <442f8085-110c-659d-d097-add788b8f291-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> >> Add IIO driver for the Renesas RCar GyroADC block. This block is a
> >> simple 4/8-channel ADC which samples 12/15/24 bits of data every
> >> + if (iio_buffer_enabled(indio_dev))
> >> + return -EBUSY;
> >
> > use iio_device_claim_direct_mode()
>
> Why ? Do I really need the mutex locking here ?
no, not needed, since you do not support buffered mode yet; but neither do
you need iio_buffer_enabled()
p.
--
Peter Meerwald-Stadler
+43-664-2444418 (mobile)
--
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] iio: adc: Add Renesas GyroADC driver
From: Marek Vasut @ 2016-12-30 20:52 UTC (permalink / raw)
To: Peter Meerwald-Stadler
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven,
Simon Horman
In-Reply-To: <alpine.DEB.2.02.1612302027480.3977-jW+XmwGofnusTnJN9+BGXg@public.gmane.org>
On 12/30/2016 08:50 PM, Peter Meerwald-Stadler wrote:
>
>> Add IIO driver for the Renesas RCar GyroADC block. This block is a
>> simple 4/8-channel ADC which samples 12/15/24 bits of data every
>> cycle from all channels.
>
> comments below
>
>> Signed-off-by: Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> Cc: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
>> Cc: Simon Horman <horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
>> ---
>> .../bindings/iio/adc/renesas,gyroadc.txt | 38 +++
>> MAINTAINERS | 6 +
>> drivers/iio/adc/Kconfig | 10 +
>> drivers/iio/adc/Makefile | 1 +
>> drivers/iio/adc/rcar_gyro_adc.c | 379 +++++++++++++++++++++
>> 5 files changed, 434 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
>> create mode 100644 drivers/iio/adc/rcar_gyro_adc.c
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
>> new file mode 100644
>> index 0000000..3fd5f57
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
>> @@ -0,0 +1,38 @@
>> +* Renesas RCar GyroADC device driver
>> +
>> +Required properties:
>> +- compatible: Should be "renesas,rcar-gyroadc" for regular GyroADC or
>> + "renesas,rcar-gyroadc-r8a7792" for GyroADC without interrupt
>> + block found in R8A7792.
>> +- reg: Address and length of the register set for the device
>> +- clocks: References to all the clocks specified in the clock-names
>> + property as specified in
>> + Documentation/devicetree/bindings/clock/clock-bindings.txt.
>> +- clock-names: Shall contain "fck" and "if". The "fck" are the GyroADC block
>
> "fck" is...
>
>> + clock, the "if" are the interface clock.
>
> "if" is ...
>
>> + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
>
> this is just an example and not appropriate here?
Oh, copy-paste error, thanks :)
>> +- power-domains: Must contain a reference to the PM domain, if available.
>> +- renesas,gyroadc-mode: GyroADC mode of operation, must be either of:
>> + 1 - MB88101A mode, 12bit sampling, 4 channels
>> + 2 - ADCS7476 mode, 15bit sampling, 8 channels
>> + 3 - MAX1162 mode, 16bit sampling, 8 channels
>> +- renesas,gyroadc-vref: Array of reference voltage values for each input to
>> + the GyroADC, in uV. Array must have 4 elemenets for
>
> elements
All spelling fixed.
[...]
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 99c0514..4a4cac7 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -408,6 +408,16 @@ config QCOM_SPMI_VADC
>> To compile this driver as a module, choose M here: the module will
>> be called qcom-spmi-vadc.
>>
>> +config RCAR_GYRO_ADC
>> + tristate "Renesas RCAR GyroADC driver"
>> + depends on ARCH_RCAR_GEN2 || (ARM && COMPILE_TEST)
>> + help
>> + Say yes here to build support for the GyroADC found in Renesas
>> + RCar Gen2 SoCs.
>> +
>> + To compile this driver as a module, choose M here: the
>> + module will be called rcar-gyroadc.
>
> called rcar_gyro_adc?
Why so ? The driver is really named rcar-gyroadc , I guess I should
rename either the file or the driver to keep things consistent. So
probably the file .
>> +
>> config ROCKCHIP_SARADC
>> tristate "Rockchip SARADC driver"
>> depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
[...]
>> +
>> +#define RCAR_GYROADC_CLOCK_LENGTH 0x08
>> +#define RCAR_GYROADC_1_25MS_LENGTH 0x0c
>> +
>> +#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4))
>> +#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4))
>> +#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4))
>> +
>> +#define RCAR_GYROADC_FIFO_STATUS 0x70
>> +#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch)))
>
> FIFO_STATUS_... is not used (yet)
Is it a problem to have a complete register layout here ?
> 4*ch looks suspicious for ch==8??
Well yes, channel is in range 0..7 :)
>> +#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch)))
>> +#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch)))
>> +
>> +#define RCAR_GYROADC_INTR 0x74
>> +#define RCAR_GYROADC_INTR_INT BIT(0)
>> +
>> +#define RCAR_GYROADC_INTENR 0x78
>> +#define RCAR_GYROADC_INTENR_INTEN BIT(0)
>> +
>> +#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */
[...]
>> +#define RCAR_GYROADC_CHAN(_idx, _chan_type, _realbits) { \
>> + .type = (_chan_type), \
>
> _chan_type is IIO_VOLTAGE always?
Yep, fixed.
>> + .indexed = 1, \
>> + .channel = (_idx), \
>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
>> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
>> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
>> + .scan_index = (_idx), \
>
> no buffered mode yet, so strictly no need for a scan_index and scan_type
OK
>> + .scan_type = { \
>> + .sign = 'u', \
>> + .realbits = (_realbits), \
>> + .storagebits = 16, \
>> + }, \
>> +}
>> +
>> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = {
>> + RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 12),
>> + RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 12),
>> + RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 12),
>> + RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 12),
>> +};
>> +
>> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = {
>> + RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(4, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(5, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(6, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(7, IIO_VOLTAGE, 15),
>> +};
>> +
>> +/*
>> + * NOTE: The data we receive in mode 3 from MAX1162 have MSByte = 0,
>> + * therefore we only use 16bit realbits here instead of 24.
>> + */
>> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
>> + RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(4, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(5, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(6, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(7, IIO_VOLTAGE, 16),
>> +};
>> +
>> +static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int *val, int *val2, long mask)
>> +{
>> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
>> + unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel);
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>> + if (chan->type != IIO_VOLTAGE)
>> + return -EINVAL;
>> +
>> + if (iio_buffer_enabled(indio_dev))
>> + return -EBUSY;
>
> use iio_device_claim_direct_mode()
Why ? Do I really need the mutex locking here ?
>> +
>> + *val = readl(priv->regs + datareg);
>> + *val &= BIT(chan->scan_type.realbits) - 1;
>> +
>> + return IIO_VAL_INT;
>> + case IIO_CHAN_INFO_SCALE:
>> + *val = 0;
>> + *val2 = (priv->vref_uv[chan->channel] * 1000) / 0x10000;
>> + return IIO_VAL_INT_PLUS_NANO;
>> + case IIO_CHAN_INFO_SAMP_FREQ:
>> + *val = RCAR_GYROADC_SAMPLE_RATE;
>> + *val2 = 0;
>
> *val2 = 0 not needed
OK
[...]
>> + indio_dev->name = dev_name(dev);
>> + indio_dev->dev.parent = dev;
>> + indio_dev->dev.of_node = pdev->dev.of_node;
>> + indio_dev->info = &rcar_gyroadc_iio_info;
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> + if (mode == 1) {
>
> maybe do the mode differentiation only once, any with a switch?
Done
[...]
--
Best regards,
Marek Vasut
^ permalink raw reply
* Re: [PATCH v6 2/8] MFD: add STM32 Timers driver
From: Jonathan Cameron @ 2016-12-30 20:38 UTC (permalink / raw)
To: Benjamin Gaignard, lee.jones, robh+dt, mark.rutland,
alexandre.torgue, devicetree, linux-kernel, thierry.reding,
linux-pwm, knaack.h, lars, pmeerw, linux-iio, linux-arm-kernel
Cc: fabrice.gasnier, gerald.baeza, arnaud.pouliquen, linus.walleij,
linaro-kernel, Benjamin Gaignard
In-Reply-To: <1481292919-26587-3-git-send-email-benjamin.gaignard@st.com>
On 09/12/16 14:15, Benjamin Gaignard wrote:
> This hardware block could at used at same time for PWM generation
> and IIO timers.
> PWM and IIO timer configuration are mixed in the same registers
> so we need a multi fonction driver to be able to share those registers.
fonction -> function
>
> version 6:
> - rename files to stm32-timers
> - rename functions to stm32_timers_xxx
>
> version 5:
> - fix Lee comments about detect function
> - add missing dependency on REGMAP_MMIO
>
> version 4:
> - add a function to detect Auto Reload Register (ARR) size
> - rename the structure shared with other drivers
>
> version 2:
> - rename driver "stm32-gptimer" to be align with SoC documentation
> - only keep one compatible
> - use of_platform_populate() instead of devm_mfd_add_devices()
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
> ---
> drivers/mfd/Kconfig | 11 ++++++
> drivers/mfd/Makefile | 2 +
> drivers/mfd/stm32-timers.c | 80 ++++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/stm32-timers.h | 71 +++++++++++++++++++++++++++++++++++
> 4 files changed, 164 insertions(+)
> create mode 100644 drivers/mfd/stm32-timers.c
> create mode 100644 include/linux/mfd/stm32-timers.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index c6df644..4ec1906 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> in various ST Microelectronics and ST-Ericsson embedded
> Nomadik series.
>
> +config MFD_STM32_TIMERS
> + tristate "Support for STM32 Timers"
> + depends on (ARCH_STM32 && OF) || COMPILE_TEST
> + select MFD_CORE
> + select REGMAP
> + select REGMAP_MMIO
> + help
> + Select this option to enable STM32 timers driver used
> + for PWM and IIO Timer. This driver allow to share the
> + registers between the others drivers.
> +
> menu "Multimedia Capabilities Port drivers"
> depends on ARCH_SA1100
>
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 9834e66..11a52f8 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
> obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
>
> obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
> +
> +obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
> diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c
> new file mode 100644
> index 0000000..68d115e
> --- /dev/null
> +++ b/drivers/mfd/stm32-timers.c
> @@ -0,0 +1,80 @@
> +/*
> + * Copyright (C) STMicroelectronics 2016
> + *
> + * Author: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/mfd/stm32-timers.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/reset.h>
> +
> +static const struct regmap_config stm32_timers_regmap_cfg = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = sizeof(u32),
> + .max_register = 0x400,
> +};
> +
> +static void stm32_timers_get_arr_size(struct stm32_timers *ddata)
> +{
> + /*
> + * Only the available bits will be written so when readback
> + * we get the maximum value of auto reload register
> + */
> + regmap_write(ddata->regmap, TIM_ARR, ~0L);
> + regmap_read(ddata->regmap, TIM_ARR, &ddata->max_arr);
> + regmap_write(ddata->regmap, TIM_ARR, 0x0);
> +}
> +
> +static int stm32_timers_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct stm32_timers *ddata;
> + struct resource *res;
> + void __iomem *mmio;
> +
> + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
> + if (!ddata)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + mmio = devm_ioremap_resource(dev, res);
> + if (IS_ERR(mmio))
> + return PTR_ERR(mmio);
> +
> + ddata->regmap = devm_regmap_init_mmio_clk(dev, "clk_int", mmio,
> + &stm32_timers_regmap_cfg);
> + if (IS_ERR(ddata->regmap))
> + return PTR_ERR(ddata->regmap);
> +
> + ddata->clk = devm_clk_get(dev, NULL);
> + if (IS_ERR(ddata->clk))
> + return PTR_ERR(ddata->clk);
> +
> + stm32_timers_get_arr_size(ddata);
> +
> + platform_set_drvdata(pdev, ddata);
> +
> + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
> +}
> +
> +static const struct of_device_id stm32_timers_of_match[] = {
> + { .compatible = "st,stm32-timers", },
> + { /* end node */ },
> +};
> +MODULE_DEVICE_TABLE(of, stm32_timers_of_match);
> +
> +static struct platform_driver stm32_timers_driver = {
> + .probe = stm32_timers_probe,
> + .driver = {
> + .name = "stm32-timers",
> + .of_match_table = stm32_timers_of_match,
> + },
> +};
> +module_platform_driver(stm32_timers_driver);
> +
> +MODULE_DESCRIPTION("STMicroelectronics STM32 Timers");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h
> new file mode 100644
> index 0000000..d030004
> --- /dev/null
> +++ b/include/linux/mfd/stm32-timers.h
> @@ -0,0 +1,71 @@
> +/*
> + * Copyright (C) STMicroelectronics 2016
> + *
> + * Author: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _LINUX_STM32_GPTIMER_H_
> +#define _LINUX_STM32_GPTIMER_H_
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +
> +#define TIM_CR1 0x00 /* Control Register 1 */
> +#define TIM_CR2 0x04 /* Control Register 2 */
> +#define TIM_SMCR 0x08 /* Slave mode control reg */
> +#define TIM_DIER 0x0C /* DMA/interrupt register */
> +#define TIM_SR 0x10 /* Status register */
> +#define TIM_EGR 0x14 /* Event Generation Reg */
> +#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */
> +#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */
> +#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */
> +#define TIM_PSC 0x28 /* Prescaler */
> +#define TIM_ARR 0x2c /* Auto-Reload Register */
> +#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */
> +#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */
> +#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */
> +#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */
> +#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */
> +
> +#define TIM_CR1_CEN BIT(0) /* Counter Enable */
> +#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */
> +#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */
> +#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */
> +#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */
> +#define TIM_DIER_UIE BIT(0) /* Update interrupt */
> +#define TIM_SR_UIF BIT(0) /* Update interrupt flag */
> +#define TIM_EGR_UG BIT(0) /* Update Generation */
> +#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */
> +#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */
> +#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */
> +#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */
> +#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */
> +#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */
> +#define TIM_CCER_CC2E BIT(4) /* Capt/Comp 2 out Ena */
> +#define TIM_CCER_CC3E BIT(8) /* Capt/Comp 3 out Ena */
> +#define TIM_CCER_CC4E BIT(12) /* Capt/Comp 4 out Ena */
> +#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12))
> +#define TIM_BDTR_BKE BIT(12) /* Break input enable */
> +#define TIM_BDTR_BKP BIT(13) /* Break input polarity */
> +#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */
> +#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */
> +#define TIM_BDTR_BKF (BIT(16) | BIT(17) | BIT(18) | BIT(19))
> +#define TIM_BDTR_BK2F (BIT(20) | BIT(21) | BIT(22) | BIT(23))
> +#define TIM_BDTR_BK2E BIT(24) /* Break 2 input enable */
> +#define TIM_BDTR_BK2P BIT(25) /* Break 2 input polarity */
> +
> +#define MAX_TIM_PSC 0xFFFF
> +#define TIM_CR2_MMS_SHIFT 4
> +#define TIM_SMCR_TS_SHIFT 4
> +#define TIM_BDTR_BKF_MASK 0xF
> +#define TIM_BDTR_BKF_SHIFT 16
> +#define TIM_BDTR_BK2F_SHIFT 20
> +
> +struct stm32_timers {
> + struct clk *clk;
> + struct regmap *regmap;
> + u32 max_arr;
> +};
> +#endif
>
^ permalink raw reply
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