* [PATCH v13 07/10] dt-bindings: i2c: i2c-mux: document general purpose i2c-mux bindings
From: Peter Rosin @ 2017-04-13 16:43 UTC (permalink / raw)
To: linux-kernel, Greg Kroah-Hartman
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, linux-i2c, devicetree,
linux-iio, linux-doc, Andrew Morton, Colin Ian King,
Paul Gortmaker, Philipp Zabel, kernel
In-Reply-To: <1492101794-13444-1-git-send-email-peda@axentia.se>
Describe how a general purpose multiplexer controller is used to mux an
i2c bus.
Acked-by: Jonathan Cameron <jic23@kernel.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../devicetree/bindings/i2c/i2c-mux-gpmux.txt | 99 ++++++++++++++++++++++
1 file changed, 99 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt
diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt
new file mode 100644
index 000000000000..2907dab56298
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt
@@ -0,0 +1,99 @@
+General Purpose I2C Bus Mux
+
+This binding describes an I2C bus multiplexer that uses a mux controller
+from the mux subsystem to route the I2C signals.
+
+ .-----. .-----.
+ | dev | | dev |
+ .------------. '-----' '-----'
+ | SoC | | |
+ | | .--------+--------'
+ | .------. | .------+ child bus A, on MUX value set to 0
+ | | I2C |-|--| Mux |
+ | '------' | '--+---+ child bus B, on MUX value set to 1
+ | .------. | | '----------+--------+--------.
+ | | MUX- | | | | | |
+ | | Ctrl |-|-----+ .-----. .-----. .-----.
+ | '------' | | dev | | dev | | dev |
+ '------------' '-----' '-----' '-----'
+
+Required properties:
+- compatible: i2c-mux
+- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side
+ port is connected to.
+- mux-controls: The phandle of the mux controller to use for operating the
+ mux.
+* Standard I2C mux properties. See i2c-mux.txt in this directory.
+* I2C child bus nodes. See i2c-mux.txt in this directory. The sub-bus number
+ is also the mux-controller state described in ../mux/mux-controller.txt
+
+Optional properties:
+- mux-locked: If present, explicitly allow unrelated I2C transactions on the
+ parent I2C adapter at these times:
+ + during setup of the multiplexer
+ + between setup of the multiplexer and the child bus I2C transaction
+ + between the child bus I2C transaction and releasing of the multiplexer
+ + during releasing of the multiplexer
+ However, I2C transactions to devices behind all I2C multiplexers connected
+ to the same parent adapter that this multiplexer is connected to are blocked
+ for the full duration of the complete multiplexed I2C transaction (i.e.
+ including the times covered by the above list).
+ If mux-locked is not present, the multiplexer is assumed to be parent-locked.
+ This means that no unrelated I2C transactions are allowed on the parent I2C
+ adapter for the complete multiplexed I2C transaction.
+ The properties of mux-locked and parent-locked multiplexers are discussed
+ in more detail in Documentation/i2c/i2c-topology.
+
+For each i2c child node, an I2C child bus will be created. They will
+be numbered based on their order in the device tree.
+
+Whenever an access is made to a device on a child bus, the value set
+in the relevant node's reg property will be set as the state in the
+mux controller.
+
+Example:
+ mux: mux-controller {
+ compatible = "gpio-mux";
+ #mux-control-cells = <0>;
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+ };
+
+ i2c-mux {
+ compatible = "i2c-mux";
+ mux-locked;
+ i2c-parent = <&i2c1>;
+
+ mux-controls = <&mux>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ssd1307: oled@3c {
+ compatible = "solomon,ssd1307fb-i2c";
+ reg = <0x3c>;
+ pwms = <&pwm 4 3000>;
+ reset-gpios = <&gpio2 7 1>;
+ reset-active-low;
+ };
+ };
+
+ i2c@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pca9555: pca9555@20 {
+ compatible = "nxp,pca9555";
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x20>;
+ };
+ };
+ };
--
2.1.4
^ permalink raw reply related
* [PATCH v13 06/10] iio: multiplexer: new iio category and iio-mux driver
From: Peter Rosin @ 2017-04-13 16:43 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Greg Kroah-Hartman
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, Colin Ian King,
Paul Gortmaker, Philipp Zabel, kernel-bIcnvbaLZ9MEGnE8C9+IrQ
In-Reply-To: <1492101794-13444-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
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.
Reviewed-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---
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 | 459 ++++++++++++++++++++++++++++++++++++++
6 files changed, 486 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 4af912305d2c..23cfd5bc2158 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6365,6 +6365,7 @@ M: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.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-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.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..bab9e6902090
--- /dev/null
+++ b/drivers/iio/multiplexer/iio-mux.c
@@ -0,0 +1,459 @@
+/*
+ * IIO multiplexer driver
+ *
+ * Copyright (C) 2017 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/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;
+ ssize_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;
+
+ if (len >= PAGE_SIZE)
+ return -EINVAL;
+
+ 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 = "io-channel-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-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
--
2.1.4
^ permalink raw reply related
* [PATCH v13 05/10] dt-bindings: iio: io-channel-mux: document io-channel-mux bindings
From: Peter Rosin @ 2017-04-13 16:43 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Greg Kroah-Hartman
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, Colin Ian King,
Paul Gortmaker, Philipp Zabel, kernel-bIcnvbaLZ9MEGnE8C9+IrQ
In-Reply-To: <1492101794-13444-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Describe how a multiplexer can be used to select which signal is fed to
an io-channel.
Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---
.../bindings/iio/multiplexer/io-channel-mux.txt | 39 ++++++++++++++++++++++
MAINTAINERS | 6 ++++
2 files changed, 45 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
diff --git a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
new file mode 100644
index 000000000000..c82794002595
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
@@ -0,0 +1,39 @@
+I/O channel 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 : "io-channel-mux"
+- io-channels : Channel node of the parent channel that has multiplexed
+ input.
+- io-channel-names : Should be "parent".
+- #address-cells = <1>;
+- #size-cells = <0>;
+- mux-controls : Mux controller node to use for operating the mux
+- channels : List of strings, labeling the mux controller states.
+
+For each non-empty string in the channels property, an io-channel will
+be created. The number of this io-channel is the same as the index into
+the list of strings in the channels property, and also matches the mux
+controller state. The mux controller state is described in
+../mux/mux-controller.txt
+
+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 = "io-channel-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ mux-controls = <&mux>;
+
+ channels = "sync", "in", "system-regulator";
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 591eba737678..4af912305d2c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6360,6 +6360,12 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
F: drivers/iio/adc/envelope-detector.c
+IIO MULTIPLEXER
+M: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
+
IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
R: Hartmut Knaack <knaack.h-Mmb7MZpHnFY@public.gmane.org>
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v13 04/10] iio: inkern: api for manipulating ext_info of iio channels
From: Peter Rosin @ 2017-04-13 16:43 UTC (permalink / raw)
To: linux-kernel, Greg Kroah-Hartman
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, linux-i2c, devicetree,
linux-iio, linux-doc, Andrew Morton, Colin Ian King,
Paul Gortmaker, Philipp Zabel, kernel
In-Reply-To: <1492101794-13444-1-git-send-email-peda@axentia.se>
Extend the inkern api with functions for reading and writing ext_info
of iio channels.
Acked-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
drivers/iio/inkern.c | 60 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/iio/consumer.h | 37 +++++++++++++++++++++++++++
2 files changed, 97 insertions(+)
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index 7a13535dc3e9..8292ad4435ea 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -869,3 +869,63 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
return ret;
}
EXPORT_SYMBOL_GPL(iio_write_channel_raw);
+
+unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+ unsigned int i = 0;
+
+ if (!chan->channel->ext_info)
+ return i;
+
+ for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++)
+ ++i;
+
+ return i;
+}
+EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);
+
+static const struct iio_chan_spec_ext_info *iio_lookup_ext_info(
+ const struct iio_channel *chan,
+ const char *attr)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ if (!chan->channel->ext_info)
+ return NULL;
+
+ for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) {
+ if (!strcmp(attr, ext_info->name))
+ return ext_info;
+ }
+
+ return NULL;
+}
+
+ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
+ const char *attr, char *buf)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ ext_info = iio_lookup_ext_info(chan, attr);
+ if (!ext_info)
+ return -EINVAL;
+
+ return ext_info->read(chan->indio_dev, ext_info->private,
+ chan->channel, buf);
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_ext_info);
+
+ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
+ const char *buf, size_t len)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ ext_info = iio_lookup_ext_info(chan, attr);
+ if (!ext_info)
+ return -EINVAL;
+
+ return ext_info->write(chan->indio_dev, ext_info->private,
+ chan->channel, buf, len);
+}
+EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index 47eeec3218b5..5e347a9805fd 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -312,4 +312,41 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val,
int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
int *processed, unsigned int scale);
+/**
+ * iio_get_channel_ext_info_count() - get number of ext_info attributes
+ * connected to the channel.
+ * @chan: The channel being queried
+ *
+ * Returns the number of ext_info attributes
+ */
+unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan);
+
+/**
+ * iio_read_channel_ext_info() - read ext_info attribute from a given channel
+ * @chan: The channel being queried.
+ * @attr: The ext_info attribute to read.
+ * @buf: Where to store the attribute value. Assumed to hold
+ * at least PAGE_SIZE bytes.
+ *
+ * Returns the number of bytes written to buf (perhaps w/o zero termination;
+ * it need not even be a string), or an error code.
+ */
+ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
+ const char *attr, char *buf);
+
+/**
+ * iio_write_channel_ext_info() - write ext_info attribute from a given channel
+ * @chan: The channel being queried.
+ * @attr: The ext_info attribute to read.
+ * @buf: The new attribute value. Strings needs to be zero-
+ * terminated, but the terminator should not be included
+ * in the below len.
+ * @len: The size of the new attribute value.
+ *
+ * Returns the number of accepted bytes, which should be the same as len.
+ * An error code can also be returned.
+ */
+ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
+ const char *buf, size_t len);
+
#endif
--
2.1.4
^ permalink raw reply related
* [PATCH v13 03/10] mux: minimal mux subsystem and gpio-based mux controller
From: Peter Rosin @ 2017-04-13 16:43 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Greg Kroah-Hartman
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, Colin Ian King,
Paul Gortmaker, Philipp Zabel, kernel-bIcnvbaLZ9MEGnE8C9+IrQ
In-Reply-To: <1492101794-13444-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
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.
Reviewed-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---
Documentation/driver-model/devres.txt | 8 +
MAINTAINERS | 2 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/mux/Kconfig | 34 +++
drivers/mux/Makefile | 6 +
drivers/mux/mux-core.c | 422 ++++++++++++++++++++++++++++++++++
drivers/mux/mux-gpio.c | 114 +++++++++
include/linux/mux.h | 252 ++++++++++++++++++++
9 files changed, 841 insertions(+)
create mode 100644 drivers/mux/Kconfig
create mode 100644 drivers/mux/Makefile
create mode 100644 drivers/mux/mux-core.c
create mode 100644 drivers/mux/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 efb8200819d6..e2343d9cbec7 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -337,6 +337,14 @@ MEM
MFD
devm_mfd_add_devices()
+MUX
+ devm_mux_chip_alloc()
+ devm_mux_chip_free()
+ devm_mux_chip_register()
+ devm_mux_chip_unregister()
+ devm_mux_control_get()
+ devm_mux_control_put()
+
PER-CPU MEM
devm_alloc_percpu()
devm_free_percpu()
diff --git a/MAINTAINERS b/MAINTAINERS
index 7fc06739c8ad..591eba737678 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8563,6 +8563,8 @@ M: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
S: Maintained
F: Documentation/devicetree/bindings/mux/
F: include/linux/dt-bindings/mux/
+F: include/linux/mux.h
+F: drivers/mux/
MULTISOUND SOUND DRIVER
M: Andrew Veliath <andrewtv-Jdbf3xiKgS8@public.gmane.org>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 117ca14ccf85..a7ea13e1b869 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -204,4 +204,6 @@ source "drivers/fpga/Kconfig"
source "drivers/fsi/Kconfig"
+source "drivers/mux/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 2eced9afba53..c0436f6dd5a9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -177,3 +177,4 @@ obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_FSI) += fsi/
+obj-$(CONFIG_MULTIPLEXER) += mux/
diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
new file mode 100644
index 000000000000..41dfe08ead84
--- /dev/null
+++ b/drivers/mux/Kconfig
@@ -0,0 +1,34 @@
+#
+# Multiplexer devices
+#
+
+menuconfig MULTIPLEXER
+ tristate "Multiplexer subsystem"
+ help
+ Multiplexer controller subsystem. Multiplexers are used in a
+ variety of settings, and this subsystem abstracts their use
+ so that the rest of the kernel sees a common interface. When
+ multiple parallel multiplexers are controlled by one single
+ multiplexer controller, this subsystem also coordinates the
+ multiplexer accesses.
+
+ To compile the subsystem as a module, choose M here: the module will
+ be called mux-core.
+
+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 the driver as a module, choose M here: the module will
+ be called mux-gpio.
+
+endif
diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
new file mode 100644
index 000000000000..bb16953f6290
--- /dev/null
+++ b/drivers/mux/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for multiplexer devices.
+#
+
+obj-$(CONFIG_MULTIPLEXER) += mux-core.o
+obj-$(CONFIG_MUX_GPIO) += mux-gpio.o
diff --git a/drivers/mux/mux-core.c b/drivers/mux/mux-core.c
new file mode 100644
index 000000000000..66a8bccfc3d7
--- /dev/null
+++ b/drivers/mux/mux-core.c
@@ -0,0 +1,422 @@
+/*
+ * Multiplexer subsystem
+ *
+ * Copyright (C) 2017 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.
+ */
+
+#define pr_fmt(fmt) "mux-core: " fmt
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mux.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+/*
+ * The idle-as-is "state" is not an actual state that may be selected, it
+ * only implies that the state should not be changed. So, use that state
+ * as indication that the cached state of the multiplexer is unknown.
+ */
+#define MUX_CACHE_UNKNOWN MUX_IDLE_AS_IS
+
+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 (WARN_ON(!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 = MUX_CACHE_UNKNOWN;
+ mux->idle_state = MUX_IDLE_AS_IS;
+ }
+
+ 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 ? MUX_CACHE_UNKNOWN : 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) {
+ dev_err(&mux_chip->dev, "unable to set idle state\n");
+ return ret;
+ }
+ }
+
+ ret = device_add(&mux_chip->dev);
+ if (ret < 0)
+ dev_err(&mux_chip->dev,
+ "device_add failed in mux_chip_register: %d\n", ret);
+ return ret;
+}
+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;
+
+ put_device(&mux_chip->dev);
+}
+EXPORT_SYMBOL_GPL(mux_chip_free);
+
+static void devm_mux_chip_release(struct device *dev, void *res)
+{
+ struct mux_chip *mux_chip = *(struct mux_chip **)res;
+
+ mux_chip_free(mux_chip);
+}
+
+struct mux_chip *devm_mux_chip_alloc(struct device *dev,
+ unsigned int controllers,
+ size_t sizeof_priv)
+{
+ struct mux_chip **ptr, *mux_chip;
+
+ ptr = devres_alloc(devm_mux_chip_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return NULL;
+
+ mux_chip = mux_chip_alloc(dev, controllers, sizeof_priv);
+ if (!mux_chip) {
+ devres_free(ptr);
+ return NULL;
+ }
+
+ *ptr = mux_chip;
+ devres_add(dev, ptr);
+
+ return mux_chip;
+}
+EXPORT_SYMBOL_GPL(devm_mux_chip_alloc);
+
+static int devm_mux_chip_match(struct device *dev, void *res, void *data)
+{
+ struct mux_chip **r = res;
+
+ if (WARN_ON(!r || !*r))
+ return 0;
+
+ return *r == data;
+}
+
+void devm_mux_chip_free(struct device *dev, struct mux_chip *mux_chip)
+{
+ WARN_ON(devres_release(dev, devm_mux_chip_release,
+ devm_mux_chip_match, mux_chip));
+}
+EXPORT_SYMBOL_GPL(devm_mux_chip_free);
+
+static void devm_mux_chip_reg_release(struct device *dev, void *res)
+{
+ struct mux_chip *mux_chip = *(struct mux_chip **)res;
+
+ mux_chip_unregister(mux_chip);
+}
+
+int devm_mux_chip_register(struct device *dev,
+ struct mux_chip *mux_chip)
+{
+ struct mux_chip **ptr;
+ int res;
+
+ ptr = devres_alloc(devm_mux_chip_reg_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ res = mux_chip_register(mux_chip);
+ if (res) {
+ devres_free(ptr);
+ return res;
+ }
+
+ *ptr = mux_chip;
+ devres_add(dev, ptr);
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(devm_mux_chip_register);
+
+void devm_mux_chip_unregister(struct device *dev, struct mux_chip *mux_chip)
+{
+ WARN_ON(devres_release(dev, devm_mux_chip_reg_release,
+ devm_mux_chip_match, mux_chip));
+}
+EXPORT_SYMBOL_GPL(devm_mux_chip_unregister);
+
+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 != MUX_IDLE_AS_IS)
+ 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 != MUX_IDLE_AS_IS &&
+ mux->idle_state != mux->cached_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) {
+ dev_err(dev, "mux controller '%s' not found\n",
+ mux_name);
+ 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) {
+ dev_err(dev, "%s: bad mux controller %u specified in %s\n",
+ np->full_name, controller, args.np->full_name);
+ 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 (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);
+
+/*
+ * Using subsys_initcall instead of module_init here to ensure - for the
+ * non-modular case - that the subsystem is initialized when mux consumers
+ * and mux controllers start to use it /without/ relying on link order.
+ * For the modular case, the ordering is ensured with module dependencies.
+ */
+subsys_initcall(mux_init);
+
+MODULE_DESCRIPTION("Multiplexer subsystem");
+MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mux/mux-gpio.c b/drivers/mux/mux-gpio.c
new file mode 100644
index 000000000000..227d3572e6db
--- /dev/null
+++ b/drivers/mux/mux-gpio.c
@@ -0,0 +1,114 @@
+/*
+ * GPIO-controlled multiplexer driver
+ *
+ * Copyright (C) 2017 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mux.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.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 = "gpio-mux", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids);
+
+static int mux_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mux_chip *mux_chip;
+ struct mux_gpio *mux_gpio;
+ int pins;
+ s32 idle_state;
+ int ret;
+
+ pins = gpiod_count(dev, "mux");
+ if (pins < 0)
+ return pins;
+
+ mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
+ pins * sizeof(*mux_gpio->val));
+ if (!mux_chip)
+ return -ENOMEM;
+
+ mux_gpio = mux_chip_priv(mux_chip);
+ mux_gpio->val = (int *)(mux_gpio + 1);
+ mux_chip->ops = &mux_gpio_ops;
+
+ 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");
+ return ret;
+ }
+ WARN_ON(pins != mux_gpio->gpios->ndescs);
+ mux_chip->mux->states = 1 << pins;
+
+ ret = device_property_read_u32(dev, "idle-state", (u32 *)&idle_state);
+ if (ret >= 0 && idle_state != MUX_IDLE_AS_IS) {
+ if (idle_state < 0 || idle_state >= mux_chip->mux->states) {
+ dev_err(dev, "invalid idle-state %u\n", idle_state);
+ return -EINVAL;
+ }
+
+ mux_chip->mux->idle_state = idle_state;
+ }
+
+ ret = devm_mux_chip_register(dev, mux_chip);
+ if (ret < 0)
+ return ret;
+
+ dev_info(dev, "%u-way mux-controller registered\n",
+ mux_chip->mux->states);
+
+ return 0;
+}
+
+static struct platform_driver mux_gpio_driver = {
+ .driver = {
+ .name = "gpio-mux",
+ .of_match_table = of_match_ptr(mux_gpio_dt_ids),
+ },
+ .probe = mux_gpio_probe,
+};
+module_platform_driver(mux_gpio_driver);
+
+MODULE_DESCRIPTION("GPIO-controlled multiplexer driver");
+MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mux.h b/include/linux/mux.h
new file mode 100644
index 000000000000..febdde4246df
--- /dev/null
+++ b/include/linux/mux.h
@@ -0,0 +1,252 @@
+/*
+ * mux.h - definitions for the multiplexer interface
+ *
+ * Copyright (C) 2017 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.
+ */
+
+#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 - Mux controller operations for a mux chip.
+ * @set: Set the state of the given mux controller.
+ */
+struct mux_control_ops {
+ int (*set)(struct mux_control *mux, int state);
+};
+
+/* These defines match the constants from the dt-bindings. On purpose. */
+#define MUX_IDLE_AS_IS (-1)
+#define MUX_IDLE_DISCONNECT (-2)
+
+/**
+ * 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 one
+ * of MUX_IDLE_AS_IS and MUX_IDLE_DISCONNECT.
+ */
+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 are 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);
+
+/**
+ * devm_mux_chip_alloc() - Resource-managed version of mux_chip_alloc().
+ * @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.
+ *
+ * See mux_chip_alloc() for more details.
+ *
+ * Return: A pointer to the new mux-chip, NULL on failure.
+ */
+struct mux_chip *devm_mux_chip_alloc(struct device *dev,
+ unsigned int controllers,
+ size_t sizeof_priv);
+
+/**
+ * devm_mux_chip_register() - Resource-managed version mux_chip_register().
+ * @dev: The parent device implementing the mux interface.
+ * @mux_chip: The mux-chip to register.
+ *
+ * See mux_chip_register() for more details.
+ *
+ * Return: Zero on success or a negative errno on error.
+ */
+int devm_mux_chip_register(struct device *dev, struct mux_chip *mux_chip);
+
+/**
+ * devm_mux_chip_unregister() - Resource-managed version mux_chip_unregister().
+ * @dev: The device that originally registered the mux-chip.
+ * @mux_chip: The mux-chip to unregister.
+ *
+ * See mux_chip_unregister() for more details.
+ *
+ * Note that you do not normally need to call this function.
+ */
+void devm_mux_chip_unregister(struct device *dev, struct mux_chip *mux_chip);
+
+/**
+ * devm_mux_chip_free() - Resource-managed version mux_chip_free().
+ * @dev: The device that originally got the mux-chip.
+ * @mux_chip: The mux-chip to free.
+ *
+ * See mux_chip_free() for more details.
+ *
+ * Note that you do not normally need to call this function.
+ */
+void devm_mux_chip_free(struct device *dev, 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 negative
+ * 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 and is thus free for the next 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 get 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 */
--
2.1.4
^ permalink raw reply related
* [PATCH v13 02/10] dt-bindings: document devicetree bindings for mux-controllers and gpio-mux
From: Peter Rosin @ 2017-04-13 16:43 UTC (permalink / raw)
To: linux-kernel, Greg Kroah-Hartman
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, linux-i2c, devicetree,
linux-iio, linux-doc, Andrew Morton, Colin Ian King,
Paul Gortmaker, Philipp Zabel, kernel
In-Reply-To: <1492101794-13444-1-git-send-email-peda@axentia.se>
Allow specifying that a single multiplexer controller can be used to
control several parallel multiplexers, thus enabling sharing of the
multiplexer controller by different consumers.
Add a binding for a first mux controller in the form of a GPIO based mux
controller.
Acked-by: Jonathan Cameron <jic23@kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
Documentation/devicetree/bindings/mux/gpio-mux.txt | 69 +++++++++
.../devicetree/bindings/mux/mux-controller.txt | 157 +++++++++++++++++++++
MAINTAINERS | 6 +
include/dt-bindings/mux/mux.h | 16 +++
4 files changed, 248 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mux/gpio-mux.txt
create mode 100644 Documentation/devicetree/bindings/mux/mux-controller.txt
create mode 100644 include/dt-bindings/mux/mux.h
diff --git a/Documentation/devicetree/bindings/mux/gpio-mux.txt b/Documentation/devicetree/bindings/mux/gpio-mux.txt
new file mode 100644
index 000000000000..b8f746344d80
--- /dev/null
+++ b/Documentation/devicetree/bindings/mux/gpio-mux.txt
@@ -0,0 +1,69 @@
+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 : "gpio-mux"
+- 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
+ special state MUX_IDLE_AS_IS is the default.
+
+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 = "gpio-mux";
+ #mux-control-cells = <0>;
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+ };
+
+ adc-mux {
+ compatible = "io-channel-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ mux-controls = <&mux>;
+
+ channels = "sync-1", "in", "out", "sync-2";
+ };
+
+ i2c-mux {
+ compatible = "i2c-mux";
+ 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/Documentation/devicetree/bindings/mux/mux-controller.txt b/Documentation/devicetree/bindings/mux/mux-controller.txt
new file mode 100644
index 000000000000..4f47e4bd2fa0
--- /dev/null
+++ b/Documentation/devicetree/bindings/mux/mux-controller.txt
@@ -0,0 +1,157 @@
+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, but a single mux controller can of course
+control several multiplexers for a single 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 requested mux controller
+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 = "gpio-mux";
+ #mux-control-cells = <0>;
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>;
+ };
+
+ adc-mux {
+ compatible = "io-channel-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. However, if the driver
+for the consumer node in fact asks for a named mux controller, that name is of
+course still required.
+
+ /*
+ * 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 = "gpio-mux";
+ #mux-control-cells = <0>;
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+ };
+
+ adc-mux {
+ compatible = "io-channel-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ mux-controls = <&mux>;
+
+ channels = "sync-1", "in", "out", "sync-2";
+ };
+
+ i2c-mux {
+ compatible = "i2c-mux";
+ 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.
+
+Optionally, mux controller nodes can also specify the state the mux should
+have when it is idle. The idle-state property is used for this. If the
+idle-state is not present, the mux controller is typically left as is when
+it is idle. For multiplexer chips that expose several mux controllers, the
+idle-state property is an array with one idle state for each mux controller.
+
+The special value (-1) may be used to indicate that the mux should be left
+as is when it is idle. This is the default, but can still be useful for
+mux controller chips with more than one mux controller, particularly when
+there is a need to "step past" a mux controller and set some other idle
+state for a mux controller with a higher index.
+
+Some mux controllers have the ability to disconnect the input/output of the
+multiplexer. Using this disconnected high-impedance state as the idle state
+is indicated with idle state (-2).
+
+These constants are available in
+
+ #include <dt-bindings/mux/mux.h>
+
+as MUX_IDLE_AS_IS (-1) and MUX_IDLE_DISCONNECT (-2).
+
+An example mux controller node look like this (the adg972a chip is a triple
+4-way multiplexer):
+
+ mux: mux-controller@50 {
+ compatible = "adi,adg792a";
+ reg = <0x50>;
+ #mux-control-cells = <1>;
+
+ idle-state = <MUX_IDLE_DISCONNECT MUX_IDLE_AS_IS 2>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index c265a5fe4848..7fc06739c8ad 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8558,6 +8558,12 @@ 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/mux/
+F: include/linux/dt-bindings/mux/
+
MULTISOUND SOUND DRIVER
M: Andrew Veliath <andrewtv@usa.net>
S: Maintained
diff --git a/include/dt-bindings/mux/mux.h b/include/dt-bindings/mux/mux.h
new file mode 100644
index 000000000000..c8e855c4a609
--- /dev/null
+++ b/include/dt-bindings/mux/mux.h
@@ -0,0 +1,16 @@
+/*
+ * This header provides constants for most Multiplexer bindings.
+ *
+ * Most Multiplexer bindings specify an idle state. In most cases, the
+ * the multiplexer can be left as is when idle, and in some cases it can
+ * disconnect the input/output and leave the multiplexer in a high
+ * impedance state.
+ */
+
+#ifndef _DT_BINDINGS_MUX_MUX_H
+#define _DT_BINDINGS_MUX_MUX_H
+
+#define MUX_IDLE_AS_IS (-1)
+#define MUX_IDLE_DISCONNECT (-2)
+
+#endif
--
2.1.4
^ permalink raw reply related
* [PATCH v13 01/10] devres: trivial whitespace fix
From: Peter Rosin @ 2017-04-13 16:43 UTC (permalink / raw)
To: linux-kernel, Greg Kroah-Hartman
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, linux-i2c, devicetree,
linux-iio, linux-doc, Andrew Morton, Colin Ian King,
Paul Gortmaker, Philipp Zabel, kernel
In-Reply-To: <1492101794-13444-1-git-send-email-peda@axentia.se>
Everything else is indented with two spaces, so fix the odd one out.
Acked-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
Documentation/driver-model/devres.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index bf34d5b3a733..efb8200819d6 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -335,7 +335,7 @@ MEM
devm_kzalloc()
MFD
- devm_mfd_add_devices()
+ devm_mfd_add_devices()
PER-CPU MEM
devm_alloc_percpu()
--
2.1.4
^ permalink raw reply related
* [PATCH v13 00/10] mux controller abstraction and iio/i2c muxes
From: Peter Rosin @ 2017-04-13 16:43 UTC (permalink / raw)
To: linux-kernel, Greg Kroah-Hartman
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, linux-i2c, devicetree,
linux-iio, linux-doc, Andrew Morton, Colin Ian King,
Paul Gortmaker, Philipp Zabel, kernel
Hi Greg!
Please apply.
This adds a new mux controller subsystem with an interface for accessing
mux controllers, along with two drivers providing the interface (gpio
and adg792) and two consumers (iio and i2c). This is done in such a way
that several consumers can independently access the same mux controller
if one controller controls several multiplexers, thus allowing sharing.
But sharing is by no means required, of course. It is perfectly fine to
have a single consumer of a dedicated mux controller controlling only
one mux for said consumer.
The prediction is that the typical use case will be for gpio-based muxing
(which is also what drove the development), where the below schematics
show the flexibility with one gpio-based mux controller being shared by
the iio-mux and i2c-mux-gpmux drivers.
.----.
|GPO0|----------.
|GPO1|--------. |
| | | |
| | .-------.
| | |dg4052a|
| | | |
|ADC0|-----|X X0|---- signal X0
| | | X1|---- signal X1
| | | X2|---- signal X2
| | | X3|---- signal X3
| | | |
|SDA0|-----|Y Y0|---- i2c segment Y0
|SCL0|--. | Y1|---- i2c segment Y1
'----' | | Y2|---- i2c segment Y2
| | Y3|---- i2c segment Y3
| '-------'
| 0 1 2 3 (feed SCL0 to each of
| | | | | the 4 muxed segments)
'----------------+-+-+-'
GPO0 and GPO1 may also be fed to further parallel muxers, which is perhaps
desired in a real application to minimize digital noise from the i2c Y
channel leaking into the analog X channel. I.e. it might be a good idea
to separate the analog and digital signals...
And the below hypothetical schematics indicate something similar but using
the i2c-based adg792a multiplexer instead.
.----.
|SDA0|----------.
|SCL0|--------. |
| | | |
| | .-------.
| | |adg792a|
| | | |
|ADC0|-----|D1 S1A|---- signal S1A
| | | S1B|---- signal S1B
| | | S1C|---- signal S1C
| | | S1D|---- signal S1D
| | | |
|SDA1|---+-|D2 S2A|---- i2c segment S2A
|SCL1|-. | | S2B|---- i2c segment S2B
'----' | | | S2C|---- i2c segment S2C
| | | S2D|---- i2c segment S2D
| | | |
| '-|D3 S3A|---- i2c segment S3A
| | S3B|---- i2c segment S3B
| | S3C|---- i2c segment S3C
| | S3D|---- i2c segment S3D
| '-------'
| A B C D A B C D (feed SCL1 to each of
| | | | | | | | | the 8 muxed segments)
'-----------------+-+-+-+---+-+-+-'
v12 -> v13 changes
- Philipp Zabel noticed a bad compatible string in the gpio muxer.
I amended patch 3/10 with his patch.
- Fixed reversed sense of the reset bit in the adg792 muxer (patch 10/10).
v11 -> v12 changes
- patches 11 and 12 folded into patches 6 and 3 respectively.
v10 -> v11 changes
- added a fixes-tag and an ack from Jonathan on patch 11
- added a new patch (12) with a fix for messed up error path reported
by Paul Gortmaker.
- fixed some editorial nitpicks in the documentation comments in patch 3.
v9 -> v10 changes
- rebased onto v4.11-rc1
- added reviewed-by tags from Rob on patches 7 and 9
- added a new patch (11) with a fix for an unsigned compare with less than
zero detected by CoverityScan and reported by Colin Ian King
- allowed the mux core to be built as a module, after discussion with Paul
Gortmaker
- added explicit includes of linux/export.h and linux/init.h from the mux
core, also noted by Paul
- fixed trivial whitespace issue in drivers/mux/Makefile
- added trailing '>' after my mail address in MODULE_AUTHOR, which was missing
in all new modules in drivers/mux
v8 -> v9 changes
- dropped the suffix from the compatible string of the i2c-mux-simple
binding (was ,mux-locked or ,parent-locked) and add an optional
mux-locked property instead to change the desired locking behavior
from the default parent-locked
- add description of the difference between mux-locked and parent-locked
- renamed i2c-mux-simple to i2c-mux (bindings for this general purpose
i2c mux are in i2c-mux-gpmux.txt since i2c-mux.txt is already occupied
by the common i2c-mux bindings)
- changed compatible from mux-gpio to gpio-mux
- changed bindings for idle-state back to a single array, but add defines
for as-is and hi-Z thus avoiding magic numbers
- make use of the above defines in the code as well
- make idle-state a common mux property described in mux-controller.txt
instead of repeating the info in individual mux controller bindings
- drop the adi,parallel property from the adg792 bindings and piggy-back
on the #mux-control-cells property
- refrain from using the compatible string as node name
- dropped the simplified bindings for single-user gpio mux
- added acks on patches 2/10 and 5/10 from Rob
v7 -> v8 changes
- cleanup the implementation of the simplified gpio bindings, but still...
- ...reorder patches so that they appear last in the series (patches 11
and 12 were patches 4 and 5 in v7) since Jonathan convinced me that
they were perhaps not such a good idea after all. But I still wanted
to show the last version I had and I'm still a bit undecided...
- added some words to the remaining otherwise empty commit messages
- added various acks/reviews from Jonathan and Wolfram.
- move mux last in drivers/Kconfig and drivers/Makefile
- bump copyright years
- centralize error reporting of common operatinons to the mux core
- add WARN_ON for faulty usage of mux_chip_register
- simplify code for other WARN_ON call sites
v6 -> v7 changes
- move from drivers/misc/mux-* to drivers/mux/
[I will remove Cc:s to Arnd and Greg for v8, when/if that happens]
- add managed versions of mux_chip_alloc and mux_chip_register
- use the above in mux-gpio.c and mux-adg792a.c
- also use them to support a simplified binding of a gpio mux for the common
case where there is a single consumer (and no specific idle requirements)
- new binding for describing idle behaviour of mux-adg792a
- add bindings for the gpo-pins on the mux-adg792g
- use device_property_read_u32 instead of of_property_read_u32 in mux-gpio.c
- rename iio mux compatible to io-channel-mux (was iio-mux)
- remove linuxism in the bindings (it was mentioning a function name)
- add missing quote in the example in the io-channel-mux binding
- factor out preparatory cleanup of devres docs to its own patch
- add blank line in mux_chip_free
- use SIMPLE_{PARENT,MUX}_LOCKED instead of magic numbers {0,1} in
i2c-mux-simple.c
- add some acks and a reviewed-by from Jonathan
v5 -> v6 changes
- fix stupidity in mux_chip_priv, mux_gpio_remove and adg792a_remove.
- change the devicetree bindings for the iio-mux to use a list of strings
(channels property) instead of a list children.
v4 -> v5 changes
- remove support for fancier dt layouts and go back to the phandle
approach from v2 and before, killing the horrible non-working
refcounting crap from v4 and avoiding a bunch of life-time issues
in v3.
- introduce the concept of a mux-chip, that can hold one or more
mux-controllers (inspired by the pwm subsystem).
- add dt #mux-control-cells property needed to get to the desired
mux controller if a mux chip provides more than one.
- take away the option to build the mux-core as a module.
- if the mux controller has an idle state, make sure the mux controller
is set up in the idle state initially (when it should be idle).
- do not use a variable length array on the stack in mux_gpio_set to
temporarily store the gpio state, preallocate space instead.
- fix resource leak on one failure path in mux_gpio_probe.
- driver for Analog Devices ADG792A/G, literally the first mux chip
I found on the Internet with an i2c interface (that was not a
dedicated i2c multiplexer like PCA9547) which I used to verify
that the abstractions in the mux core are up to the task. Untested,
just proof of concept that at least looks pretty and compiles...
- various touch-ups.
v3 -> v4 changes
- rebased onto next-20161122 (depends on recent _available iio changes).
- added support for having the mux-controller in a child node of a
mux-consumer if it is a sole consumer, to hopefully even further satisfy
the complaint from Rob (and later Lars-Peter) about dt complexity.
- the above came at the cost of some rather horrible refcounting code,
please review and suggest how it should be done...
- changed to register a device class instead of a bus.
- pass in the parent device into mux_control_alloc and require less
work from mux-control drivers.
- changed device names from mux:control%d to mux%d
- move kernel-doc from mux-core.c to mux.h (and add some bits).
- give the gpio driver a chance to update all mux pins at once.
- factor out iio ext_info lookup into new helper function. /Lars-Peter
- use an unsigned type for the iio ext_info count. /Lars-Peter
- unified "brag strings" in the file headers.
v2 -> v3 changes
- have the mux-controller in the parent node of any mux-controller consumer,
to hopefully satisfy complaint from Rob about dt complexity.
- improve commit message of the mux subsystem commit, making it more
general, as requested by Jonathan.
- remove priv member from struct mux_control and calculate it on the
fly. /Jonathan
- make the function comments in mux-core.c kernel doc. /Jonathan
- add devm_mux_control_* to Documentation/driver.model/devres.txt. /Jonathan
- add common dt bindings for mux-controllers, refer to them from the
mux-gpio bindings. /Rob
- clarify how the gpio pins map to the mux state. /Rob
- separate CONFIG_ variables for the mux core and the mux gpio driver.
- improve Kconfig help texts.
- make CONFIG_MUX_GPIO depend on CONFIG_GPIOLIB.
- keep track of the number of mux states in the mux core.
- since the iio channel number is used as mux state, it was possible
to drop the state member from the mux_child struct.
- cleanup dt bindings for i2c-mux-simple, it had some of copy-paste
problems from ots origin (i2c-mux-gpio).
- select the mux control subsystem in config for the i2c-mux-simple driver.
- add entries to MAINTAINERS and my sign-off, I'm now satisfied and know
nothing in this to be ashamed of.
v1 -> v2 changes
- fixup export of mux_control_put reported by kbuild
- drop devicetree iio-ext-info property as noted by Lars-Peter,
and replace the functionality by exposing all ext_info
attributes of the parent channel for each of the muxed
channels. A cache on top of that and each muxed channel
gets its own view of the ext_info of the parent channel.
- implement idle-state for muxes
- clear out the cache on failure in order to force a mux
update on the following use
- cleanup the probe of i2c-mux-simple driver
- fix a bug in the i2c-mux-simple driver, where failure in
the selection of the mux caused a deadlock when the mux
was later unconditionally deselected.
Old background:
I have a piece of hardware that is using the same 3 GPIO pins
to control four 8-way muxes. Three of them control ADC lines
to an ADS1015 chip with an iio driver, and the last one
controls the SDA line of an i2c bus. We have some deployed
code to handle this, but you do not want to see it or ever
hear about it. I'm not sure why I even mention it. Anyway,
the situation has nagged me to no end for quite some time.
So, after first getting more intimate with the i2c muxing code
and later discovering the drivers/iio/inkern.c file and
writing a couple of drivers making use of it, I came up with
what I think is an acceptable solution; add a generic mux
controller driver (and subsystem) that is shared between all
instances, and combine that with an iio mux driver and a new
general purpose i2c mux driver that is only hooking the i2c
muxing and the new mux controller.
One thing that I would like to do, but don't see a solution
for, is to move the mux control code that is present in
various drivers in drivers/i2c/muxes to this new minimalistic
muxing subsystem, thus converting all present i2c muxes (but
perhaps not gates and arbitrators).
I'm using an rwsem to lock a mux, but that isn't really a
perfect fit. Is there a better locking primitive that I don't
know about that fits better? I had a mutex at one point, but
that didn't allow any concurrent accesses at all. At least
the rwsem allows concurrent access as long as all users
agree on the mux state, but I suspect that the rwsem will
degrade to the mutex situation pretty quickly if there is
any contention.
Also, the "mux" name feels a bit ambitious, there are many muxes
in the world, and this tiny bit of code is probably not good
enough to be a nice fit for all...
Cheers,
peda
Peter Rosin (10):
devres: trivial whitespace fix
dt-bindings: document devicetree bindings for mux-controllers and
gpio-mux
mux: minimal mux subsystem and gpio-based mux controller
iio: inkern: api for manipulating ext_info of iio channels
dt-bindings: iio: io-channel-mux: document io-channel-mux bindings
iio: multiplexer: new iio category and iio-mux driver
dt-bindings: i2c: i2c-mux: document general purpose i2c-mux bindings
i2c: i2c-mux-gpmux: new driver
dt-bindings: mux-adg792a: document devicetree bindings for ADG792A/G
mux
mux: adg792a: add mux controller driver for ADG792A/G
.../devicetree/bindings/i2c/i2c-mux-gpmux.txt | 99 +++++
.../bindings/iio/multiplexer/io-channel-mux.txt | 39 ++
.../devicetree/bindings/mux/adi,adg792a.txt | 75 ++++
Documentation/devicetree/bindings/mux/gpio-mux.txt | 69 ++++
.../devicetree/bindings/mux/mux-controller.txt | 157 +++++++
Documentation/driver-model/devres.txt | 10 +-
MAINTAINERS | 15 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/i2c/muxes/Kconfig | 13 +
drivers/i2c/muxes/Makefile | 1 +
drivers/i2c/muxes/i2c-mux-gpmux.c | 173 ++++++++
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/inkern.c | 60 +++
drivers/iio/multiplexer/Kconfig | 18 +
drivers/iio/multiplexer/Makefile | 6 +
drivers/iio/multiplexer/iio-mux.c | 459 +++++++++++++++++++++
drivers/mux/Kconfig | 46 +++
drivers/mux/Makefile | 7 +
drivers/mux/mux-adg792a.c | 141 +++++++
drivers/mux/mux-core.c | 422 +++++++++++++++++++
drivers/mux/mux-gpio.c | 114 +++++
include/dt-bindings/mux/mux.h | 16 +
include/linux/iio/consumer.h | 37 ++
include/linux/mux.h | 252 +++++++++++
26 files changed, 2233 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt
create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
create mode 100644 Documentation/devicetree/bindings/mux/adi,adg792a.txt
create mode 100644 Documentation/devicetree/bindings/mux/gpio-mux.txt
create mode 100644 Documentation/devicetree/bindings/mux/mux-controller.txt
create mode 100644 drivers/i2c/muxes/i2c-mux-gpmux.c
create mode 100644 drivers/iio/multiplexer/Kconfig
create mode 100644 drivers/iio/multiplexer/Makefile
create mode 100644 drivers/iio/multiplexer/iio-mux.c
create mode 100644 drivers/mux/Kconfig
create mode 100644 drivers/mux/Makefile
create mode 100644 drivers/mux/mux-adg792a.c
create mode 100644 drivers/mux/mux-core.c
create mode 100644 drivers/mux/mux-gpio.c
create mode 100644 include/dt-bindings/mux/mux.h
create mode 100644 include/linux/mux.h
--
2.1.4
^ permalink raw reply
* [PATCH v2] arm64: allwinner: h5: add support for Orange Pi Prime board
From: Icenowy Zheng @ 2017-04-13 16:38 UTC (permalink / raw)
To: Maxime Ripard, Chen-Yu Tsai
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
Orange Pi Prime is a new Allwinner H5-based SBC by Xunlong.
It's like a Orange Pi Plus 2E with H3 replaced with H5, eMMC replaced
with onboard SPI NOR Flash and wireless card changed to Realtek
RTL8723BS (with Bluetooth functionality).
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
---
Changes since v1:
- Fixed copyright holder.
arch/arm64/boot/dts/allwinner/Makefile | 1 +
.../dts/allwinner/sun50i-h5-orangepi-prime.dts | 205 +++++++++++++++++++++
2 files changed, 206 insertions(+)
create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts
diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile
index 244e8b7565f9..92a84eea6b96 100644
--- a/arch/arm64/boot/dts/allwinner/Makefile
+++ b/arch/arm64/boot/dts/allwinner/Makefile
@@ -1,6 +1,7 @@
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-bananapi-m64.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pine64-plus.dtb sun50i-a64-pine64.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-orangepi-pc2.dtb
+dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-orangepi-prime.dtb
always := $(dtb-y)
subdir-y := $(dts-dirs)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts
new file mode 100644
index 000000000000..28d92a612329
--- /dev/null
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
+ *
+ * Based on sun50i-h5-orangepi-pc2.dts, which is:
+ * Copyright (C) 2016 ARM Ltd.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include "sun50i-h5.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+ model = "Xunlong Orange Pi Prime";
+ compatible = "xunlong,orangepi-prime", "allwinner,sun50i-h5";
+
+ reg_vcc3v3: vcc3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ pwr {
+ label = "orangepi:green:pwr";
+ gpios = <&r_pio 0 10 GPIO_ACTIVE_HIGH>;
+ default-state = "on";
+ };
+
+ status {
+ label = "orangepi:red:status";
+ gpios = <&pio 0 20 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ r-gpio-keys {
+ compatible = "gpio-keys";
+
+ sw4 {
+ label = "sw4";
+ linux,code = <BTN_0>;
+ gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ reg_usb0_vbus: usb0-vbus {
+ compatible = "regulator-fixed";
+ regulator-name = "usb0-vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&r_pio 0 2 GPIO_ACTIVE_HIGH>; /* PL2 */
+ status = "okay";
+ };
+
+ wifi_pwrseq: wifi_pwrseq {
+ compatible = "mmc-pwrseq-simple";
+ reset-gpios = <&pio 2 14 GPIO_ACTIVE_LOW>; /* PC14 */
+ };
+};
+
+&codec {
+ allwinner,audio-routing =
+ "Line Out", "LINEOUT",
+ "MIC1", "Mic",
+ "Mic", "MBIAS";
+ status = "okay";
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ehci2 {
+ status = "okay";
+};
+
+&ehci3 {
+ status = "okay";
+};
+
+&ir {
+ pinctrl-names = "default";
+ pinctrl-0 = <&ir_pins_a>;
+ status = "okay";
+};
+
+&mmc0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>;
+ vmmc-supply = <®_vcc3v3>;
+ bus-width = <4>;
+ cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */
+ status = "okay";
+};
+
+&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins_a>;
+ vmmc-supply = <®_vcc3v3>;
+ mmc-pwrseq = <&wifi_pwrseq>;
+ bus-width = <4>;
+ non-removable;
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&ohci2 {
+ status = "okay";
+};
+
+&ohci3 {
+ status = "okay";
+};
+
+&uart0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pins_a>;
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart1_pins>;
+ status = "disabled";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_pins>;
+ status = "disabled";
+};
+
+&usb_otg {
+ dr_mode = "otg";
+ status = "okay";
+};
+
+&usbphy {
+ /* USB Type-A ports' VBUS is always on */
+ usb0_id_det-gpios = <&pio 0 21 GPIO_ACTIVE_HIGH>; /* PA21 */
+ usb0_vbus-supply = <®_usb0_vbus>;
+ status = "okay";
+};
--
2.12.2
--
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 v2 2/4] gpio - Add EXAR XRA1403 SPI GPIO expander driver
From: kbuild test robot @ 2017-04-13 16:22 UTC (permalink / raw)
Cc: kbuild-all, gregkh, davem, geert, mchehab, daniel.vetter,
linus.walleij, gnurou, robh+dt, mark.rutland, linux-gpio,
devicetree, linux-kernel, Nandor Han, Semi Malinen
In-Reply-To: <f28eef264102786357ac5ed7205813619c508fb4.1492077070.git.nandor.han@ge.com>
[-- Attachment #1: Type: text/plain, Size: 1935 bytes --]
Hi Nandor,
[auto build test ERROR on gpio/for-next]
[also build test ERROR on v4.11-rc6 next-20170413]
[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/Nandor-Han/XRA1403-gpio-add-XRA1403-gpio-expander-driver/20170413-215739
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git for-next
config: frv-allyesconfig (attached as .config)
compiler: frv-linux-gcc (GCC) 6.2.0
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=frv
All errors (new ones prefixed by >>):
drivers//gpio/gpio-xra1403.c: In function 'xra1403_dbg_show':
>> drivers//gpio/gpio-xra1403.c:133:2: error: implicit declaration of function 'seq_puts' [-Werror=implicit-function-declaration]
seq_puts(s, "xra reg:");
^~~~~~~~
>> drivers//gpio/gpio-xra1403.c:135:3: error: implicit declaration of function 'seq_printf' [-Werror=implicit-function-declaration]
seq_printf(s, " %2.2x", reg);
^~~~~~~~~~
cc1: some warnings being treated as errors
vim +/seq_puts +133 drivers//gpio/gpio-xra1403.c
127 struct xra1403 *xra = gpiochip_get_data(chip);
128 int value[xra1403_regmap_cfg.max_register];
129 int i;
130 unsigned int gcr;
131 unsigned int gsr;
132
> 133 seq_puts(s, "xra reg:");
134 for (reg = 0; reg <= xra1403_regmap_cfg.max_register; reg++)
> 135 seq_printf(s, " %2.2x", reg);
136 seq_puts(s, "\n value:");
137 for (reg = 0; reg < xra1403_regmap_cfg.max_register; reg++) {
138 regmap_read(xra->regmap, reg, &value[reg]);
---
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: 47568 bytes --]
^ permalink raw reply
* Re: [PATCH] ASoC: cs35l35: Allow user to configure IMON SCALE
From: Brian Austin @ 2017-04-13 16:03 UTC (permalink / raw)
To: Charles Keepax
Cc: mark.rutland, devicetree, brian.austin, alsa-devel, patches,
lgirdwood, Paul.Handrigan, robh+dt, broonie
In-Reply-To: <1492098729-30491-1-git-send-email-ckeepax@opensource.wolfsonmicro.com>
On Thu, 13 Apr 2017, Charles Keepax wrote:
> On the chip the IMON signal is a full 24-bits however normally only
> some of the bits will be sent over the bus. The chip provides a field
> to select which bits of the IMON will be sent back, this is the only
> feedback signal that has this feature.
>
> Add an additional entry to the cirrus,imon device tree property to
> allow the IMON scale parameter to be passed.
>
> Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Acked-by: Brian Austin <brian.austin@cirrus.com>
^ permalink raw reply
* [PATCH] ASoC: cs35l35: Allow user to configure IMON SCALE
From: Charles Keepax @ 2017-04-13 15:52 UTC (permalink / raw)
To: broonie
Cc: mark.rutland, devicetree, brian.austin, alsa-devel, patches,
lgirdwood, Paul.Handrigan, robh+dt
On the chip the IMON signal is a full 24-bits however normally only
some of the bits will be sent over the bus. The chip provides a field
to select which bits of the IMON will be sent back, this is the only
feedback signal that has this feature.
Add an additional entry to the cirrus,imon device tree property to
allow the IMON scale parameter to be passed.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
---
I realise this is changing the binding slightly, but thought
we might be able to sneak it before cs35l35 ships as it hasn't
shipped in a kernel yet. However, it would be relatively simple
(although makes the code a little messier) to support both the 3
and 4 entry versions of the binding if people prefer?
Thanks,
Charles
.../devicetree/bindings/sound/cs35l35.txt | 4 ++--
include/sound/cs35l35.h | 1 +
sound/soc/codecs/cs35l35.c | 22 +++++++++++++++-------
sound/soc/codecs/cs35l35.h | 3 +++
4 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/cs35l35.txt b/Documentation/devicetree/bindings/sound/cs35l35.txt
index 457d176..016b768 100644
--- a/Documentation/devicetree/bindings/sound/cs35l35.txt
+++ b/Documentation/devicetree/bindings/sound/cs35l35.txt
@@ -118,8 +118,8 @@ Optional Monitor Signal Format sub-node:
Sections 7.44 - 7.53 lists values for the depth, location, and frame
for each monitoring signal.
- - cirrus,imon : 3 8 bit values to set the depth, location, and frame
- of the IMON monitor signal.
+ - cirrus,imon : 4 8 bit values to set the depth, location, frame and ADC
+ scale of the IMON monitor signal.
- cirrus,vmon : 3 8 bit values to set the depth, location, and frame
of the VMON monitor signal.
diff --git a/include/sound/cs35l35.h b/include/sound/cs35l35.h
index 88744bb..29da899 100644
--- a/include/sound/cs35l35.h
+++ b/include/sound/cs35l35.h
@@ -57,6 +57,7 @@ struct monitor_cfg {
u8 imon_dpth;
u8 imon_loc;
u8 imon_frm;
+ u8 imon_scale;
u8 vmon_dpth;
u8 vmon_loc;
u8 vmon_frm;
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
index 6ecb7dd..88c48e2 100644
--- a/sound/soc/codecs/cs35l35.c
+++ b/sound/soc/codecs/cs35l35.c
@@ -918,6 +918,11 @@ static int cs35l35_codec_probe(struct snd_soc_codec *codec)
CS35L35_MON_FRM_MASK,
monitor_config->imon_frm <<
CS35L35_MON_FRM_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_SCALE_CTL,
+ CS35L35_IMON_SCALE_MASK,
+ monitor_config->imon_scale <<
+ CS35L35_IMON_SCALE_SHIFT);
}
if (monitor_config->vpmon_specs) {
regmap_update_bits(cs35l35->regmap,
@@ -1161,7 +1166,9 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
struct classh_cfg *classh_config = &pdata->classh_algo;
struct monitor_cfg *monitor_config = &pdata->mon_cfg;
unsigned int val32 = 0;
- u8 monitor_array[3];
+ u8 monitor_array[4];
+ const int imon_array_size = ARRAY_SIZE(monitor_array);
+ const int mon_array_size = imon_array_size - 1;
int ret = 0;
if (!np)
@@ -1302,15 +1309,16 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
monitor_config->is_present = signal_format ? true : false;
if (monitor_config->is_present) {
ret = of_property_read_u8_array(signal_format, "cirrus,imon",
- monitor_array, ARRAY_SIZE(monitor_array));
+ monitor_array, imon_array_size);
if (!ret) {
monitor_config->imon_specs = true;
monitor_config->imon_dpth = monitor_array[0];
monitor_config->imon_loc = monitor_array[1];
monitor_config->imon_frm = monitor_array[2];
+ monitor_config->imon_scale = monitor_array[3];
}
ret = of_property_read_u8_array(signal_format, "cirrus,vmon",
- monitor_array, ARRAY_SIZE(monitor_array));
+ monitor_array, mon_array_size);
if (!ret) {
monitor_config->vmon_specs = true;
monitor_config->vmon_dpth = monitor_array[0];
@@ -1318,7 +1326,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
monitor_config->vmon_frm = monitor_array[2];
}
ret = of_property_read_u8_array(signal_format, "cirrus,vpmon",
- monitor_array, ARRAY_SIZE(monitor_array));
+ monitor_array, mon_array_size);
if (!ret) {
monitor_config->vpmon_specs = true;
monitor_config->vpmon_dpth = monitor_array[0];
@@ -1326,7 +1334,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
monitor_config->vpmon_frm = monitor_array[2];
}
ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon",
- monitor_array, ARRAY_SIZE(monitor_array));
+ monitor_array, mon_array_size);
if (!ret) {
monitor_config->vbstmon_specs = true;
monitor_config->vbstmon_dpth = monitor_array[0];
@@ -1334,7 +1342,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
monitor_config->vbstmon_frm = monitor_array[2];
}
ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat",
- monitor_array, ARRAY_SIZE(monitor_array));
+ monitor_array, mon_array_size);
if (!ret) {
monitor_config->vpbrstat_specs = true;
monitor_config->vpbrstat_dpth = monitor_array[0];
@@ -1342,7 +1350,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
monitor_config->vpbrstat_frm = monitor_array[2];
}
ret = of_property_read_u8_array(signal_format, "cirrus,zerofill",
- monitor_array, ARRAY_SIZE(monitor_array));
+ monitor_array, mon_array_size);
if (!ret) {
monitor_config->zerofill_specs = true;
monitor_config->zerofill_dpth = monitor_array[0];
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
index 54e9ac5..5a6e43a 100644
--- a/sound/soc/codecs/cs35l35.h
+++ b/sound/soc/codecs/cs35l35.h
@@ -148,6 +148,9 @@
#define CS35L35_MON_FRM_MASK 0x80
#define CS35L35_MON_FRM_SHIFT 7
+#define CS35L35_IMON_SCALE_MASK 0xF8
+#define CS35L35_IMON_SCALE_SHIFT 3
+
#define CS35L35_MS_MASK 0x80
#define CS35L35_MS_SHIFT 7
#define CS35L35_SPMODE_MASK 0x40
--
2.1.4
^ permalink raw reply related
* [RFC 2/2] mux: mmio-based syscon mux controller
From: Philipp Zabel @ 2017-04-13 15:48 UTC (permalink / raw)
To: Peter Rosin
Cc: Rob Herring, Mark Rutland, Sakari Ailus, Steve Longerbeam,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Philipp Zabel
In-Reply-To: <20170413154812.19597-1-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
This adds a driver for mmio-based syscon multiplexers controlled by a
single bitfield in a syscon register range.
Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
drivers/mux/Kconfig | 13 +++++
drivers/mux/Makefile | 1 +
drivers/mux/mux-syscon.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 144 insertions(+)
create mode 100644 drivers/mux/mux-syscon.c
diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
index 86668b4d2fc52..a5e6a3b01ac24 100644
--- a/drivers/mux/Kconfig
+++ b/drivers/mux/Kconfig
@@ -43,4 +43,17 @@ config MUX_GPIO
To compile the driver as a module, choose M here: the module will
be called mux-gpio.
+config MUX_SYSCON
+ tristate "MMIO bitfield-controlled Multiplexer"
+ depends on OF && MFD_SYSCON
+ help
+ MMIO bitfield-controlled Multiplexer controller.
+
+ The driver builds a single multiplexer controller using a bitfield
+ in a syscon register. For N bit wide bitfields, there will be 2^N
+ possible multiplexer states.
+
+ To compile the driver as a module, choose M here: the module will
+ be called mux-syscon.
+
endif
diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
index b00a7d37d2fbe..234309f6655f7 100644
--- a/drivers/mux/Makefile
+++ b/drivers/mux/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_MULTIPLEXER) += mux-core.o
obj-$(CONFIG_MUX_ADG792A) += mux-adg792a.o
obj-$(CONFIG_MUX_GPIO) += mux-gpio.o
+obj-$(CONFIG_MUX_SYSCON) += mux-syscon.o
diff --git a/drivers/mux/mux-syscon.c b/drivers/mux/mux-syscon.c
new file mode 100644
index 0000000000000..31cacc61f1439
--- /dev/null
+++ b/drivers/mux/mux-syscon.c
@@ -0,0 +1,130 @@
+/*
+ * syscon bitfield-controlled multiplexer driver
+ *
+ * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mux.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+struct mux_syscon {
+ struct regmap_field *field;
+};
+
+static int mux_syscon_set(struct mux_control *mux, int state)
+{
+ struct mux_syscon *mux_syscon = mux_chip_priv(mux->chip);
+
+ return regmap_field_write(mux_syscon->field, state);
+}
+
+static const struct mux_control_ops mux_syscon_ops = {
+ .set = mux_syscon_set,
+};
+
+static const struct of_device_id mux_syscon_dt_ids[] = {
+ { .compatible = "mmio-mux", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mux_syscon_dt_ids);
+
+static int of_get_reg_field(struct device_node *node, struct reg_field *field)
+{
+ u32 bit_mask;
+ int ret;
+
+ ret = of_property_read_u32(node, "reg", &field->reg);
+ if (ret < 0)
+ return ret;
+
+ ret = of_property_read_u32(node, "bit-mask", &bit_mask);
+ if (ret < 0)
+ return ret;
+
+ ret = of_property_read_u32(node, "bit-shift", &field->lsb);
+ if (ret < 0)
+ return ret;
+
+ field->msb = field->lsb + fls(bit_mask) - 1;
+
+ return 0;
+}
+
+static int mux_syscon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mux_chip *mux_chip;
+ struct mux_syscon *mux_syscon;
+ struct regmap *regmap;
+ struct reg_field field;
+ int bits;
+ s32 idle_state;
+ int ret;
+
+ ret = of_get_reg_field(pdev->dev.of_node, &field);
+ if (ret) {
+ dev_err(&pdev->dev, "missing bit-field properties: %d\n", ret);
+ return ret;
+ }
+
+ regmap = syscon_node_to_regmap(pdev->dev.of_node->parent);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&pdev->dev, "failed to get syscon regmap: %d\n", ret);
+ return ret;
+ }
+
+ mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_syscon));
+ if (!mux_chip)
+ return -ENOMEM;
+
+ mux_syscon = mux_chip_priv(mux_chip);
+ mux_chip->ops = &mux_syscon_ops;
+
+ mux_syscon->field = devm_regmap_field_alloc(&pdev->dev, regmap, field);
+ if (IS_ERR(mux_syscon->field)) {
+ ret = PTR_ERR(mux_syscon->field);
+ dev_err(&pdev->dev, "failed to regmap bit-field: %d\n", ret);
+ return ret;
+ }
+ bits = 1 + field.msb - field.lsb;
+
+ mux_chip->mux->states = 1 << bits;
+
+ ret = device_property_read_u32(dev, "idle-state", (u32 *)&idle_state);
+ if (ret >= 0 && idle_state != MUX_IDLE_AS_IS) {
+ if (idle_state < 0 || idle_state >= mux_chip->mux->states) {
+ dev_err(dev, "invalid idle-state %u\n", idle_state);
+ return -EINVAL;
+ }
+
+ mux_chip->mux->idle_state = idle_state;
+ }
+
+ regmap_field_read(mux_syscon->field, &mux_chip->mux->cached_state);
+
+ return devm_mux_chip_register(dev, mux_chip);
+}
+
+static struct platform_driver mux_syscon_driver = {
+ .driver = {
+ .name = "mmio-mux",
+ .of_match_table = of_match_ptr(mux_syscon_dt_ids),
+ },
+ .probe = mux_syscon_probe,
+};
+module_platform_driver(mux_syscon_driver);
+
+MODULE_DESCRIPTION("MMIO bitfield-controlled multiplexer driver");
+MODULE_AUTHOR("Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
--
2.11.0
--
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
* [RFC 1/2] dt-bindings: add mmio-based syscon mux controller DT bindings
From: Philipp Zabel @ 2017-04-13 15:48 UTC (permalink / raw)
To: Peter Rosin
Cc: Rob Herring, Mark Rutland, Sakari Ailus, Steve Longerbeam,
devicetree, linux-kernel, kernel, Philipp Zabel
This adds device tree binding documentation for mmio-based syscon
multiplexers controlled by a single bitfield in a syscon register
range.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Documentation/devicetree/bindings/mux/mmio-mux.txt | 56 ++++++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mux/mmio-mux.txt
diff --git a/Documentation/devicetree/bindings/mux/mmio-mux.txt b/Documentation/devicetree/bindings/mux/mmio-mux.txt
new file mode 100644
index 0000000000000..11d96f5d98583
--- /dev/null
+++ b/Documentation/devicetree/bindings/mux/mmio-mux.txt
@@ -0,0 +1,56 @@
+MMIO bitfield-based multiplexer controller bindings
+
+Define a syscon bitfield to be used to control a multiplexer. The parent
+device tree node must be a syscon node to provide register access.
+
+Required properties:
+- compatible : "gpio-mux"
+- reg : register base of the register containing the control bitfield
+- bit-mask : bitmask of the control bitfield in the control register
+- bit-shift : bit offset of the control bitfield in the control register
+- #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
+ special state MUX_IDLE_AS_IS is the default.
+
+The multiplexer state is defined as the value of the bitfield described
+by the reg, bit-mask, and bit-shift properties, accessed through the parent
+syscon.
+
+Example:
+
+ syscon {
+ compatible = "syscon";
+
+ mux: mux-controller@3 {
+ compatible = "mmio-mux";
+ reg = <0x3>;
+ bit-mask = <0x1>;
+ bit-shift = <5>;
+ #mux-control-cells = <0>;
+ };
+ };
+
+ video-mux {
+ compatible = "video-mux";
+ mux-controls = <&mux>;
+
+ ports {
+ /* input 0 */
+ port@0 {
+ reg = <0>;
+ };
+
+ /* input 1 */
+ port@1 {
+ reg = <1>;
+ };
+
+ /* output */
+ port@2 {
+ reg = <2>;
+ };
+ };
+ };
--
2.11.0
^ permalink raw reply related
* [PATCH v3 4/4] arm64: dts: hikey: add WL1835 Bluetooth device node
From: Rob Herring @ 2017-04-13 15:03 UTC (permalink / raw)
To: Marcel Holtmann, linux-bluetooth-u79uwXL29TY76Z2rM5mHXA
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Gustavo Padovan, Johan Hedberg, Mark Rutland, Wei Xu, Eyal Reizer,
Satish Patel, netdev-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170413150353.7389-1-robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
This adds the serial slave device for the WL1835 Bluetooth interface.
Signed-off-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Wei Xu <xuwei5-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
---
v3:
- rebase on bluetooth-next
arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts
index dba3c131c62c..9b4ba7169210 100644
--- a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts
+++ b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts
@@ -98,6 +98,11 @@
assigned-clocks = <&sys_ctrl HI6220_UART1_SRC>;
assigned-clock-rates = <150000000>;
status = "ok";
+
+ bluetooth {
+ compatible = "ti,wl1835-st";
+ enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
+ };
};
uart2: uart@f7112000 {
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 3/4] bluetooth: hci_uart: add LL protocol serdev driver support
From: Rob Herring @ 2017-04-13 15:03 UTC (permalink / raw)
To: Marcel Holtmann, linux-bluetooth
Cc: linux-arm-kernel, Gustavo Padovan, Johan Hedberg, Mark Rutland,
Wei Xu, Eyal Reizer, Satish Patel, netdev, devicetree
In-Reply-To: <20170413150353.7389-1-robh@kernel.org>
Turns out that the LL protocol and the TI-ST are the same thing AFAICT.
The TI-ST adds firmware loading, GPIO control, and shared access for
NFC, FM radio, etc. For now, we're only implementing what is needed for
BT. This mirrors other drivers like BCM and Intel, but uses the new
serdev bus.
The firmware loading is greatly simplified by using existing
infrastructure to send commands. It may be a bit slower than the
original code using synchronous functions, but the real bottleneck is
likely doing firmware load at 115.2kbps.
Signed-off-by: Rob Herring <robh@kernel.org>
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Gustavo Padovan <gustavo@padovan.org>
Cc: Johan Hedberg <johan.hedberg@gmail.com>
Cc: linux-bluetooth@vger.kernel.org
---
v3:
- rebase on bluetooth-next
- Add explicit of.h include
v2:
- Use IS_ENABLED() to fix module build
drivers/bluetooth/hci_ll.c | 262 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 261 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index 02692fe30279..485e8eb04542 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -34,20 +34,24 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
+#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/poll.h>
#include <linux/slab.h>
-#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/signal.h>
#include <linux/ioctl.h>
+#include <linux/of.h>
+#include <linux/serdev.h>
#include <linux/skbuff.h>
+#include <linux/ti_wilink_st.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <linux/gpio/consumer.h>
#include "hci_uart.h"
@@ -76,6 +80,12 @@ struct hcill_cmd {
u8 cmd;
} __packed;
+struct ll_device {
+ struct hci_uart hu;
+ struct serdev_device *serdev;
+ struct gpio_desc *enable_gpio;
+};
+
struct ll_struct {
unsigned long rx_state;
unsigned long rx_count;
@@ -136,6 +146,9 @@ static int ll_open(struct hci_uart *hu)
hu->priv = ll;
+ if (hu->serdev)
+ serdev_device_open(hu->serdev);
+
return 0;
}
@@ -164,6 +177,13 @@ static int ll_close(struct hci_uart *hu)
kfree_skb(ll->rx_skb);
+ if (hu->serdev) {
+ struct ll_device *lldev = serdev_device_get_drvdata(hu->serdev);
+ gpiod_set_value_cansleep(lldev->enable_gpio, 0);
+
+ serdev_device_close(hu->serdev);
+ }
+
hu->priv = NULL;
kfree(ll);
@@ -505,9 +525,245 @@ static struct sk_buff *ll_dequeue(struct hci_uart *hu)
return skb_dequeue(&ll->txq);
}
+#if IS_ENABLED(CONFIG_SERIAL_DEV_BUS)
+static int read_local_version(struct hci_dev *hdev)
+{
+ int err = 0;
+ unsigned short version = 0;
+ struct sk_buff *skb;
+ struct hci_rp_read_local_version *ver;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Reading TI version information failed (%ld)",
+ PTR_ERR(skb));
+ err = PTR_ERR(skb);
+ goto out;
+ }
+ if (skb->len != sizeof(*ver)) {
+ err = -EILSEQ;
+ goto out;
+ }
+
+ ver = (struct hci_rp_read_local_version *)skb->data;
+ if (le16_to_cpu(ver->manufacturer) != 13) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ version = le16_to_cpu(ver->lmp_subver);
+
+out:
+ if (err) bt_dev_err(hdev, "Failed to read TI version info: %d", err);
+ kfree_skb(skb);
+ return err ? err : version;
+}
+
+/**
+ * download_firmware -
+ * internal function which parses through the .bts firmware
+ * script file intreprets SEND, DELAY actions only as of now
+ */
+static int download_firmware(struct ll_device *lldev)
+{
+ unsigned short chip, min_ver, maj_ver;
+ int version, err, len;
+ unsigned char *ptr, *action_ptr;
+ unsigned char bts_scr_name[40]; /* 40 char long bts scr name? */
+ const struct firmware *fw;
+ struct sk_buff *skb;
+ struct hci_command *cmd;
+
+ version = read_local_version(lldev->hu.hdev);
+ if (version < 0)
+ return version;
+
+ chip = (version & 0x7C00) >> 10;
+ min_ver = (version & 0x007F);
+ maj_ver = (version & 0x0380) >> 7;
+ if (version & 0x8000)
+ maj_ver |= 0x0008;
+
+ snprintf(bts_scr_name, sizeof(bts_scr_name),
+ "ti-connectivity/TIInit_%d.%d.%d.bts",
+ chip, maj_ver, min_ver);
+
+ err = request_firmware(&fw, bts_scr_name, &lldev->serdev->dev);
+ if (err || !fw->data || !fw->size) {
+ bt_dev_err(lldev->hu.hdev, "request_firmware failed(errno %d) for %s",
+ err, bts_scr_name);
+ return -EINVAL;
+ }
+ ptr = (void *)fw->data;
+ len = fw->size;
+ /* bts_header to remove out magic number and
+ * version
+ */
+ ptr += sizeof(struct bts_header);
+ len -= sizeof(struct bts_header);
+
+ while (len > 0 && ptr) {
+ bt_dev_dbg(lldev->hu.hdev, " action size %d, type %d ",
+ ((struct bts_action *)ptr)->size,
+ ((struct bts_action *)ptr)->type);
+
+ action_ptr = &(((struct bts_action *)ptr)->data[0]);
+
+ switch (((struct bts_action *)ptr)->type) {
+ case ACTION_SEND_COMMAND: /* action send */
+ bt_dev_dbg(lldev->hu.hdev, "S");
+ cmd = (struct hci_command *)action_ptr;
+ if (cmd->opcode == 0xff36) {
+ /* ignore remote change
+ * baud rate HCI VS command */
+ bt_dev_warn(lldev->hu.hdev, "change remote baud rate command in firmware");
+ break;
+ }
+ if (cmd->prefix != 1)
+ bt_dev_dbg(lldev->hu.hdev, "command type %d\n", cmd->prefix);
+
+ skb = __hci_cmd_sync(lldev->hu.hdev, cmd->opcode, cmd->plen, &cmd->speed, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(lldev->hu.hdev, "send command failed\n");
+ goto out_rel_fw;
+ }
+ kfree_skb(skb);
+ break;
+ case ACTION_WAIT_EVENT: /* wait */
+ /* no need to wait as command was synchronous */
+ bt_dev_dbg(lldev->hu.hdev, "W");
+ break;
+ case ACTION_DELAY: /* sleep */
+ bt_dev_info(lldev->hu.hdev, "sleep command in scr");
+ mdelay(((struct bts_action_delay *)action_ptr)->msec);
+ break;
+ }
+ len -= (sizeof(struct bts_action) +
+ ((struct bts_action *)ptr)->size);
+ ptr += sizeof(struct bts_action) +
+ ((struct bts_action *)ptr)->size;
+ }
+
+out_rel_fw:
+ /* fw download complete */
+ release_firmware(fw);
+ return err;
+}
+
+static int ll_setup(struct hci_uart *hu)
+{
+ int err, retry = 3;
+ struct ll_device *lldev;
+ struct serdev_device *serdev = hu->serdev;
+ u32 speed;
+
+ if (!serdev)
+ return 0;
+
+ lldev = serdev_device_get_drvdata(serdev);
+
+ serdev_device_set_flow_control(serdev, true);
+
+ do {
+ /* Configure BT_EN to HIGH state */
+ gpiod_set_value_cansleep(lldev->enable_gpio, 0);
+ msleep(5);
+ gpiod_set_value_cansleep(lldev->enable_gpio, 1);
+ msleep(100);
+
+ err = download_firmware(lldev);
+ if (!err)
+ break;
+
+ /* Toggle BT_EN and retry */
+ bt_dev_err(hu->hdev, "download firmware failed, retrying...");
+ } while (retry--);
+
+ if (err)
+ return err;
+
+ /* Operational speed if any */
+ if (hu->oper_speed)
+ speed = hu->oper_speed;
+ else if (hu->proto->oper_speed)
+ speed = hu->proto->oper_speed;
+ else
+ speed = 0;
+
+ if (speed) {
+ struct sk_buff *skb = __hci_cmd_sync(hu->hdev, 0xff36, sizeof(speed), &speed, HCI_INIT_TIMEOUT);
+ if (!IS_ERR(skb)) {
+ kfree_skb(skb);
+ serdev_device_set_baudrate(serdev, speed);
+ }
+ }
+
+ return 0;
+}
+
+static const struct hci_uart_proto llp;
+
+static int hci_ti_probe(struct serdev_device *serdev)
+{
+ struct hci_uart *hu;
+ struct ll_device *lldev;
+ u32 max_speed = 3000000;
+
+ lldev = devm_kzalloc(&serdev->dev, sizeof(struct ll_device), GFP_KERNEL);
+ if (!lldev)
+ return -ENOMEM;
+ hu = &lldev->hu;
+
+ serdev_device_set_drvdata(serdev, lldev);
+ lldev->serdev = hu->serdev = serdev;
+
+ lldev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(lldev->enable_gpio))
+ return PTR_ERR(lldev->enable_gpio);
+
+ of_property_read_u32(serdev->dev.of_node, "max-speed", &max_speed);
+ hci_uart_set_speeds(hu, 115200, max_speed);
+
+ return hci_uart_register_device(hu, &llp);
+}
+
+static void hci_ti_remove(struct serdev_device *serdev)
+{
+ struct ll_device *lldev = serdev_device_get_drvdata(serdev);
+ struct hci_uart *hu = &lldev->hu;
+ struct hci_dev *hdev = hu->hdev;
+
+ cancel_work_sync(&hu->write_work);
+
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+ hu->proto->close(hu);
+}
+
+static const struct of_device_id hci_ti_of_match[] = {
+ { .compatible = "ti,wl1831-st" },
+ { .compatible = "ti,wl1835-st" },
+ { .compatible = "ti,wl1837-st" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hci_ti_of_match);
+
+static struct serdev_device_driver hci_ti_drv = {
+ .driver = {
+ .name = "hci-ti",
+ .of_match_table = of_match_ptr(hci_ti_of_match),
+ },
+ .probe = hci_ti_probe,
+ .remove = hci_ti_remove,
+};
+#else
+#define ll_setup NULL
+#endif
+
static const struct hci_uart_proto llp = {
.id = HCI_UART_LL,
.name = "LL",
+ .setup = ll_setup,
.open = ll_open,
.close = ll_close,
.recv = ll_recv,
@@ -518,10 +774,14 @@ static const struct hci_uart_proto llp = {
int __init ll_init(void)
{
+ serdev_device_driver_register(&hci_ti_drv);
+
return hci_uart_register_proto(&llp);
}
int __exit ll_deinit(void)
{
+ serdev_device_driver_unregister(&hci_ti_drv);
+
return hci_uart_unregister_proto(&llp);
}
--
2.11.0
^ permalink raw reply related
* [PATCH v3 2/4] bluetooth: hci_uart: remove unused hci_uart_init_tty
From: Rob Herring @ 2017-04-13 15:03 UTC (permalink / raw)
To: Marcel Holtmann, linux-bluetooth
Cc: linux-arm-kernel, Gustavo Padovan, Johan Hedberg, Mark Rutland,
Wei Xu, Eyal Reizer, Satish Patel, netdev, devicetree
In-Reply-To: <20170413150353.7389-1-robh@kernel.org>
There are no users of hci_uart_init_tty, so remove it.
Signed-off-by: Rob Herring <robh@kernel.org>
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Gustavo Padovan <gustavo@padovan.org>
Cc: Johan Hedberg <johan.hedberg@gmail.com>
Cc: linux-bluetooth@vger.kernel.org
---
v3:
- rebase on bluetooth-next
drivers/bluetooth/hci_ldisc.c | 19 -------------------
drivers/bluetooth/hci_uart.h | 1 -
2 files changed, 20 deletions(-)
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 17bcbc13623f..cec4438ede01 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -319,25 +319,6 @@ void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
hu->oper_speed = oper_speed;
}
-void hci_uart_init_tty(struct hci_uart *hu)
-{
- struct tty_struct *tty = hu->tty;
- struct ktermios ktermios;
-
- /* Bring the UART into a known 8 bits no parity hw fc state */
- ktermios = tty->termios;
- ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
- INLCR | IGNCR | ICRNL | IXON);
- ktermios.c_oflag &= ~OPOST;
- ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
- ktermios.c_cflag &= ~(CSIZE | PARENB);
- ktermios.c_cflag |= CS8;
- ktermios.c_cflag |= CRTSCTS;
-
- /* tty_set_termios() return not checked as it is always 0 */
- tty_set_termios(tty, &ktermios);
-}
-
void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed)
{
struct tty_struct *tty = hu->tty;
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 1b41c661bbb8..2b05e557fad0 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -114,7 +114,6 @@ int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p
int hci_uart_tx_wakeup(struct hci_uart *hu);
int hci_uart_init_ready(struct hci_uart *hu);
-void hci_uart_init_tty(struct hci_uart *hu);
void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed);
void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
--
2.11.0
^ permalink raw reply related
* [PATCH v3 1/4] dt-bindings: net: Add TI WiLink shared transport binding
From: Rob Herring @ 2017-04-13 15:03 UTC (permalink / raw)
To: Marcel Holtmann, linux-bluetooth
Cc: linux-arm-kernel, Gustavo Padovan, Johan Hedberg, Mark Rutland,
Wei Xu, Eyal Reizer, Satish Patel, netdev, devicetree
In-Reply-To: <20170413150353.7389-1-robh@kernel.org>
Add serial slave device binding for the TI WiLink series of Bluetooth/FM/GPS
devices.
Signed-off-by: Rob Herring <robh@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: netdev@vger.kernel.org
Cc: devicetree@vger.kernel.org
---
v3:
- rebase on bluetooth-next
.../devicetree/bindings/net/ti,wilink-st.txt | 35 ++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/ti,wilink-st.txt
diff --git a/Documentation/devicetree/bindings/net/ti,wilink-st.txt b/Documentation/devicetree/bindings/net/ti,wilink-st.txt
new file mode 100644
index 000000000000..cbad73a84ac4
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/ti,wilink-st.txt
@@ -0,0 +1,35 @@
+TI WiLink 7/8 (wl12xx/wl18xx) Shared Transport BT/FM/GPS devices
+
+TI WiLink devices have a UART interface for providing Bluetooth, FM radio,
+and GPS over what's called "shared transport". The shared transport is
+standard BT HCI protocol with additional channels for the other functions.
+
+These devices also have a separate WiFi interface as described in
+wireless/ti,wlcore.txt.
+
+This bindings follows the UART slave device binding in
+../serial/slave-device.txt.
+
+Required properties:
+ - compatible: should be one of the following:
+ "ti,wl1271-st"
+ "ti,wl1273-st"
+ "ti,wl1831-st"
+ "ti,wl1835-st"
+ "ti,wl1837-st"
+
+Optional properties:
+ - enable-gpios : GPIO signal controlling enabling of BT. Active high.
+ - vio-supply : Vio input supply (1.8V)
+ - vbat-supply : Vbat input supply (2.9-4.8V)
+
+Example:
+
+&serial0 {
+ compatible = "ns16550a";
+ ...
+ bluetooth {
+ compatible = "ti,wl1835-st";
+ enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
+ };
+};
--
2.11.0
^ permalink raw reply related
* [PATCH v3 0/4] TI Bluetooth serdev support
From: Rob Herring @ 2017-04-13 15:03 UTC (permalink / raw)
To: Marcel Holtmann, linux-bluetooth
Cc: linux-arm-kernel, Gustavo Padovan, Johan Hedberg, Mark Rutland,
Wei Xu, Eyal Reizer, Satish Patel, netdev, devicetree
This series adds serdev support to the HCI LL protocol used on TI BT
modules and enables support on HiKey board with with the WL1835 module.
With this the custom TI UIM daemon and btattach are no longer needed.
The series is available on this git branch[1]. This version is rebased on
bluetooth-next tree containing its dependencies.
Rob
[1] git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git ti-bluetooth
Rob Herring (4):
dt-bindings: net: Add TI WiLink shared transport binding
bluetooth: hci_uart: remove unused hci_uart_init_tty
bluetooth: hci_uart: add LL protocol serdev driver support
arm64: dts: hikey: add WL1835 Bluetooth device node
.../devicetree/bindings/net/ti,wilink-st.txt | 35 +++
arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts | 5 +
drivers/bluetooth/hci_ldisc.c | 19 --
drivers/bluetooth/hci_ll.c | 262 ++++++++++++++++++++-
drivers/bluetooth/hci_uart.h | 1 -
5 files changed, 301 insertions(+), 21 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/ti,wilink-st.txt
--
2.11.0
^ permalink raw reply
* Re: [PATCH 0/6] mvmdio updates
From: David Miller @ 2017-04-13 14:59 UTC (permalink / raw)
To: linux
Cc: mw, stefanc, andrew, thomas.petazzoni, devicetree, mark.rutland,
netdev, robh+dt
In-Reply-To: <20170410152728.GT17774@n2100.armlinux.org.uk>
From: Russell King - ARM Linux <linux@armlinux.org.uk>
Date: Mon, 10 Apr 2017 16:27:28 +0100
> This series of patches update mvmdio for Armada 8k CP110. A number of
> issues were found:
...
Series applied to net-next, thanks.
^ permalink raw reply
* RE: [PATCH 4/5] mtd: nand: add support for Micron on-die ECC
From: Bean Huo (beanhuo) @ 2017-04-13 14:08 UTC (permalink / raw)
To: Boris Brezillon
Cc: Thomas Petazzoni,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
pawel.moll-5wv7dgnIgG8@public.gmane.org, Campbell,
richard-/L3Ra7n9ekc@public.gmane.org, Mark Rutland,
marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org, Rob Herring,
linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
galak-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org, Cyrille Pitchen,
computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
In-Reply-To: <20170412090313.539f81b2@bbrezillon>
Hi, Boris
>One last question. Is it dangerous to call
>SET_FEATURE(disable/enable-ECC) on a NAND that has ECC enabled by default?
>We could use that to detect whether on-die ECC can be turned off or not and
>adjust the chip->ecc init steps accordingly.
>
No any danger exist, NAND just gives response that set feature failed.
Also, it is a good solution to detect whether on-die ECC support or not through
SET_FEATURE(disable/enable-ECC) successful or not.
>Thanks,
>
>Boris
Happy Easter!
//Bean
--
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 17/39] platform: add video-multiplexer subdevice driver
From: Philipp Zabel @ 2017-04-13 13:52 UTC (permalink / raw)
To: Sakari Ailus
Cc: Steve Longerbeam, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, shawnguo-DgEjT+Ai2ygdnm+yROfE0A,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, fabio.estevam-3arQi8VN3Tc,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, mchehab-DgEjT+Ai2ygdnm+yROfE0A,
hverkuil-qWit8jRvyhVmR6Xm/wNWPw, nick-gcszYUEDH4VrovVCs/uTlw,
markus.heiser-O6JHGLzbNUwb1SvskN2V4Q,
laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw,
bparrot-l0cyMroinI0, geert-Td1EMuHUCqxL1ZNQvxDV9g,
arnd-r2nGTMty4D4, sudipm.mukherjee-Re5JQEeQqe8AvxtiuMwx3w,
minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w,
tiffany.lin-NuS5LvNUpcJWk0Htik3J/w,
jean-christophe.trotin-qxv4g6HH51o,
horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ,
niklas.soderlund+renesas-1zkq55x86MTxsAP9Fp7wbw,
robert.jarzmik-GANU6spQydw, songjun.wu-UWL1GkI3JZL3oGB3hsPCZA,
andrew-ct.chen-NuS5LvNUpcJWk0Htik3J/w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
shuah-DgEjT+Ai2ygdnm+yROfE0A, sakari.ailus-VuQAYsv1563Yd54FQh9/CA,
pavel-+ZI9xUNit7I, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWMMKQXVYFwzLw
In-Reply-To: <20170404124732.GD3288-S+BSfZ9RZZmRSg0ZkenSGLdO1Tsj/99ntUK59QYPAWc@public.gmane.org>
Hi Sakari,
thank you for the review.
On Tue, 2017-04-04 at 15:47 +0300, Sakari Ailus wrote:
> Hi Steve, Philipp and Pavel,
>
> On Mon, Mar 27, 2017 at 05:40:34PM -0700, Steve Longerbeam wrote:
> > From: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> >
> > This driver can handle SoC internal and external video bus multiplexers,
> > controlled either by register bit fields or by a GPIO. The subdevice
> > passes through frame interval and mbus configuration of the active input
> > to the output side.
>
> The MUX framework is already in linux-next. Could you use that instead of
> adding new driver + bindings that are not compliant with the MUX framework?
> I don't think it'd be much of a change in terms of code, using the MUX
> framework appears quite simple.
It is not quite clear to me how to design the DT bindings for this. Just
splitting the video-multiplexer driver from the mux-mmio / mux-gpio
would make it necessary to keep the video-multiplexer node to describe
the of-graph bindings. But then we have two different nodes in the DT
that describe the same hardware:
mux: mux {
compatible = "mux-gpio";
mux-gpios = <&gpio 0>, <&gpio 1>;
#mux-control-cells = <0>;
}
video-multiplexer {
compatible = "video-multiplexer"
mux-controls = <&mux>;
ports {
/* ... */
}
}
It would feel more natural to have the ports in the mux node, but then
how would the video-multiplexer driver be instanciated, and how would it
get to the of-graph nodes?
> In general the driver looks pretty good, especially regarding the user space
> API implementation which is important for use with other drivers.
>
> I have some more detailed comments below.
>
> >
> > Signed-off-by: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> >
> > - fixed a cut&paste error in vidsw_remove(): v4l2_async_register_subdev()
> > should be unregister.
> >
> > - added media_entity_cleanup() to vidsw_remove().
> >
> > - added missing MODULE_DEVICE_TABLE().
> > Suggested-by: Javier Martinez Canillas <javier-0uQlZySMnqxg9hUCZPvPmw@public.gmane.org>
> >
> > - there was a line left over from a previous iteration that negated
> > the new way of determining the pad count just before it which
> > has been removed (num_pads = of_get_child_count(np)).
> >
> > - removed [gs]_frame_interval ops. timeperframe is not used anywhwere
> > in this subdev, and currently it has no control over frame rate.
> >
> > - add link_validate to media_entity_operations.
> >
> > - moved devicetree binding doc to a separate commit.
> >
> > - Philipp Zabel has developed a set of patches that allow adding
> > to the subdev async notifier waiting list using a chaining method
> > from the async registered callbacks (v4l2_of_subdev_registered()
> > and the prep patches for that). For now, I've removed the use of
> > v4l2_of_subdev_registered() for the vidmux driver's registered
> > callback. This doesn't affect the functionality of this driver,
> > but allows for it to be merged now, before adding the chaining
> > support.
> >
> > Signed-off-by: Steve Longerbeam <steve_longerbeam-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
> > ---
> > drivers/media/platform/Kconfig | 8 +
> > drivers/media/platform/Makefile | 2 +
> > drivers/media/platform/video-multiplexer.c | 451 +++++++++++++++++++++++++++++
> > 3 files changed, 461 insertions(+)
> > create mode 100644 drivers/media/platform/video-multiplexer.c
> >
> > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> > index ab0bb48..c9b8d9c 100644
> > --- a/drivers/media/platform/Kconfig
> > +++ b/drivers/media/platform/Kconfig
> > @@ -74,6 +74,14 @@ config VIDEO_M32R_AR_M64278
> > To compile this driver as a module, choose M here: the
> > module will be called arv.
> >
> > +config VIDEO_MULTIPLEXER
> > + tristate "Video Multiplexer"
> > + depends on VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
> > + help
> > + This driver provides support for SoC internal N:1 video bus
> > + multiplexers controlled by register bitfields as well as external
> > + 2:1 video multiplexers controlled by a single GPIO.
> > +
> > config VIDEO_OMAP3
> > tristate "OMAP 3 Camera support"
> > depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
> > diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> > index 8959f6e..d418add 100644
> > --- a/drivers/media/platform/Makefile
> > +++ b/drivers/media/platform/Makefile
> > @@ -27,6 +27,8 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o
> >
> > obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
> >
> > +obj-$(CONFIG_VIDEO_MULTIPLEXER) += video-multiplexer.o
> > +
> > obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/
> > obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/
> > obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
> > diff --git a/drivers/media/platform/video-multiplexer.c b/drivers/media/platform/video-multiplexer.c
> > new file mode 100644
> > index 0000000..b18c317
> > --- /dev/null
> > +++ b/drivers/media/platform/video-multiplexer.c
> > @@ -0,0 +1,451 @@
> > +/*
> > + * video stream multiplexer controlled via gpio or syscon
> > + *
> > + * Copyright (C) 2013 Pengutronix, Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > + * Copyright (C) 2016 Pengutronix, Philipp Zabel <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@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
> > + * as published by the Free Software Foundation; either version 2
> > + * of the License, or (at your option) any later version.
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_graph.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +#include <media/v4l2-async.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-subdev.h>
> > +#include <media/v4l2-of.h>
> > +
> > +struct vidsw {
> > + struct v4l2_subdev subdev;
> > + unsigned int num_pads;
>
> You could use subdev.entity.num_pads instead of caching the value locally.
Ok, I'll change this.
> > + struct media_pad *pads;
> > + struct v4l2_mbus_framefmt *format_mbus;
> > + struct v4l2_of_endpoint *endpoint;
> > + struct regmap_field *field;
> > + struct gpio_desc *gpio;
> > + int active;
> > +};
> > +
> > +static inline struct vidsw *v4l2_subdev_to_vidsw(struct v4l2_subdev *sd)
> > +{
> > + return container_of(sd, struct vidsw, subdev);
> > +}
> > +
> > +static void vidsw_set_active(struct vidsw *vidsw, int active)
> > +{
> > + vidsw->active = active;
> > + if (active < 0)
> > + return;
> > +
> > + dev_dbg(vidsw->subdev.dev, "setting %d active\n", active);
> > +
> > + if (vidsw->field)
> > + regmap_field_write(vidsw->field, active);
> > + else if (vidsw->gpio)
> > + gpiod_set_value(vidsw->gpio, active);
> > +}
> > +
> > +static int vidsw_link_setup(struct media_entity *entity,
> > + const struct media_pad *local,
> > + const struct media_pad *remote, u32 flags)
> > +{
> > + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> > + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> > +
> > + /* We have no limitations on enabling or disabling our output link */
> > + if (local->index == vidsw->num_pads - 1)
> > + return 0;
> > +
> > + dev_dbg(sd->dev, "link setup %s -> %s", remote->entity->name,
> > + local->entity->name);
> > +
> > + if (!(flags & MEDIA_LNK_FL_ENABLED)) {
> > + if (local->index == vidsw->active) {
> > + dev_dbg(sd->dev, "going inactive\n");
> > + vidsw->active = -1;
> > + }
> > + return 0;
> > + }
> > +
> > + if (vidsw->active >= 0) {
> > + struct media_pad *pad;
> > +
> > + if (vidsw->active == local->index)
> > + return 0;
> > +
> > + pad = media_entity_remote_pad(&vidsw->pads[vidsw->active]);
> > + if (pad) {
> > + struct media_link *link;
> > + int ret;
> > +
> > + link = media_entity_find_link(pad,
> > + &vidsw->pads[vidsw->active]);
> > + if (link) {
> > + ret = __media_entity_setup_link(link, 0);
>
> I wouldn't implicitly disable a link, even if only one can be active at a
> given time. No other drivers do that either.
>
> Perhaps returning an error might be a better thing to do: if you're
> reconfiguring the pipeline anyway, there are likely issues elsewhere in it.
>
> We could also change the behaviour later to allow implicit changes but we
> can't later on go the other way without breaking the user space.
Fair enough, I'll drop this.
> > + if (ret)
> > + return ret;
> > + }
> > + }
> > + }
> > +
> > + vidsw_set_active(vidsw, local->index);
> > +
> > + return 0;
> > +}
> > +
> > +static struct media_entity_operations vidsw_ops = {
> > + .link_setup = vidsw_link_setup,
> > + .link_validate = v4l2_subdev_link_validate,
> > +};
> > +
> > +static bool vidsw_endpoint_disabled(struct device_node *ep)
> > +{
> > + struct device_node *rpp;
> > +
> > + if (!of_device_is_available(ep))
>
> ep here is the endpoint, whereas the argument to of_device_is_available()
> should correspond to the actual device.
Well, this was a convenience to allow adding status = "disabled" to
endpoints to turn them off (see the function name). I
> > + return true;
> > +
> > + rpp = of_graph_get_remote_port_parent(ep);
> > + if (!rpp)
> > + return true;
> > +
> > + return !of_device_is_available(rpp);
> > +}
> > +
> > +static int vidsw_async_init(struct vidsw *vidsw, struct device_node *node)
>
> I think I'd arrange this closer to probe as it's related to probe directly.
> Up to you.
I'll change this to
rpp = of_graph_get_remote_port_parent(ep);
return !of_device_is_available(rpp);
as Steve suggested.
> > +{
> > + struct device_node *ep;
> > + u32 portno;
> > + int numports;
> > + int ret;
> > + int i;
> > + bool active_link = false;
> > +
> > + numports = vidsw->num_pads;
> > +
> > + for (i = 0; i < numports - 1; i++)
> > + vidsw->pads[i].flags = MEDIA_PAD_FL_SINK;
> > + vidsw->pads[numports - 1].flags = MEDIA_PAD_FL_SOURCE;
> > +
> > + vidsw->subdev.entity.function = MEDIA_ENT_F_VID_MUX;
> > + ret = media_entity_pads_init(&vidsw->subdev.entity, numports,
> > + vidsw->pads);
> > + if (ret < 0)
> > + return ret;
> > +
> > + vidsw->subdev.entity.ops = &vidsw_ops;
> > +
> > + for_each_endpoint_of_node(node, ep) {
> > + struct v4l2_of_endpoint endpoint;
> > +
> > + v4l2_of_parse_endpoint(ep, &endpoint);
> > +
> > + portno = endpoint.base.port;
> > + if (portno >= numports - 1)
> > + continue;
> > +
> > + if (vidsw_endpoint_disabled(ep)) {
> > + dev_dbg(vidsw->subdev.dev,
> > + "port %d disabled\n", portno);
> > + continue;
> > + }
> > +
> > + vidsw->endpoint[portno] = endpoint;
> > +
> > + if (portno == vidsw->active)
> > + active_link = true;
> > + }
> > +
> > + for (portno = 0; portno < numports - 1; portno++) {
> > + if (!vidsw->endpoint[portno].base.local_node)
> > + continue;
> > +
> > + /* If the active input is not connected, use another */
> > + if (!active_link) {
> > + vidsw_set_active(vidsw, portno);
> > + active_link = true;
> > + }
> > + }
> > +
> > + return v4l2_async_register_subdev(&vidsw->subdev);
> > +}
> > +
> > +int vidsw_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg)
>
> We should get rid of g_mbus_config() in the long run, but as we don't have
> the alternative (frame descriptors) isn't up to the job yet I guess it's ok.
> I don't think we'll have too many users for the video switch right now.
Ok, we can add a comment to mark this as undesirable.
> > +{
> > + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> > + struct media_pad *pad;
> > + int ret;
> > +
> > + if (vidsw->active == -1) {
> > + dev_err(sd->dev, "no configuration for inactive mux\n");
> > + return -EINVAL;
> > + }
> > +
> > + /*
> > + * Retrieve media bus configuration from the entity connected to the
> > + * active input
> > + */
> > + pad = media_entity_remote_pad(&vidsw->pads[vidsw->active]);
> > + if (pad) {
> > + sd = media_entity_to_v4l2_subdev(pad->entity);
> > + ret = v4l2_subdev_call(sd, video, g_mbus_config, cfg);
> > + if (ret == -ENOIOCTLCMD)
> > + pad = NULL;
> > + else if (ret < 0) {
> > + dev_err(sd->dev, "failed to get source configuration\n");
> > + return ret;
> > + }
> > + }
> > + if (!pad) {
> > + /* Mirror the input side on the output side */
> > + cfg->type = vidsw->endpoint[vidsw->active].bus_type;
> > + if (cfg->type == V4L2_MBUS_PARALLEL ||
> > + cfg->type == V4L2_MBUS_BT656)
> > + cfg->flags = vidsw->endpoint[vidsw->active].bus.parallel.flags;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int vidsw_s_stream(struct v4l2_subdev *sd, int enable)
> > +{
> > + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> > + struct v4l2_subdev *upstream_sd;
> > + struct media_pad *pad;
> > +
> > + if (vidsw->active == -1) {
> > + dev_err(sd->dev, "Can not start streaming on inactive mux\n");
> > + return -EINVAL;
> > + }
> > +
> > + pad = media_entity_remote_pad(&sd->entity.pads[vidsw->active]);
> > + if (!pad) {
> > + dev_err(sd->dev, "Failed to find remote source pad\n");
> > + return -ENOLINK;
> > + }
> > +
> > + if (!is_media_entity_v4l2_subdev(pad->entity)) {
> > + dev_err(sd->dev, "Upstream entity is not a v4l2 subdev\n");
> > + return -ENODEV;
> > + }
> > +
> > + upstream_sd = media_entity_to_v4l2_subdev(pad->entity);
> > +
> > + return v4l2_subdev_call(upstream_sd, video, s_stream, enable);
>
> Now that we'll have more than two drivers involved in the same pipeline it
> becomes necessary to define the behaviour of s_stream() throughout the
> pipeline --- i.e. whose responsibility is it to call s_stream() on the
> sub-devices in the pipeline?
>
> I can submit a patch for that. I think the way you do it here is good, as it
> enables the caller to choose the appropriate behaviour, i.e. start the local
> device before or after the upstream sub-device.
Please do, this should be decided before any more complex inter-driver
pipelines show up.
> > +}
> > +
> > +static const struct v4l2_subdev_video_ops vidsw_subdev_video_ops = {
> > + .g_mbus_config = vidsw_g_mbus_config,
> > + .s_stream = vidsw_s_stream,
> > +};
> > +
> > +static struct v4l2_mbus_framefmt *
> > +__vidsw_get_pad_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + unsigned int pad, u32 which)
> > +{
> > + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> > +
> > + switch (which) {
> > + case V4L2_SUBDEV_FORMAT_TRY:
> > + return v4l2_subdev_get_try_format(sd, cfg, pad);
> > + case V4L2_SUBDEV_FORMAT_ACTIVE:
> > + return &vidsw->format_mbus[pad];
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +static int vidsw_get_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *sdformat)
> > +{
> > + sdformat->format = *__vidsw_get_pad_format(sd, cfg, sdformat->pad,
> > + sdformat->which);
> > + return 0;
> > +}
> > +
> > +static int vidsw_set_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *sdformat)
> > +{
> > + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> > + struct v4l2_mbus_framefmt *mbusformat;
> > +
> > + if (sdformat->pad >= vidsw->num_pads)
> > + return -EINVAL;
>
> This check is already performed in v4l2-subdev.c.
Excellent.
> > +
> > + mbusformat = __vidsw_get_pad_format(sd, cfg, sdformat->pad,
> > + sdformat->which);
> > + if (!mbusformat)
> > + return -EINVAL;
> > +
> > + /* Output pad mirrors active input pad, no limitations on input pads */
>
> Source and sink pads.
Ok.
> > + if (sdformat->pad == (vidsw->num_pads - 1) && vidsw->active >= 0)
>
> I think it'd be cleaner to test for the pad flag instead of the number. Or,
> add a macro to obtain the source pad number.
>
> > + sdformat->format = vidsw->format_mbus[vidsw->active];
> > +
> > + *mbusformat = sdformat->format;
> > +
> > + return 0;
> > +}
> > +
> > +static struct v4l2_subdev_pad_ops vidsw_pad_ops = {
> > + .get_fmt = vidsw_get_format,
> > + .set_fmt = vidsw_set_format,
> > +};
> > +
> > +static struct v4l2_subdev_ops vidsw_subdev_ops = {
> > + .pad = &vidsw_pad_ops,
> > + .video = &vidsw_subdev_video_ops,
> > +};
> > +
> > +static int of_get_reg_field(struct device_node *node, struct reg_field *field)
> > +{
> > + u32 bit_mask;
> > + int ret;
> > +
> > + ret = of_property_read_u32(node, "reg", &field->reg);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = of_property_read_u32(node, "bit-mask", &bit_mask);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = of_property_read_u32(node, "bit-shift", &field->lsb);
> > + if (ret < 0)
> > + return ret;
>
> I think the above would look nice in a MUX driver. :-)
I'll submit an MMIO / syscon / regmap bitfield mux driver.
> > +
> > + field->msb = field->lsb + fls(bit_mask) - 1;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidsw_probe(struct platform_device *pdev)
> > +{
> > + struct device_node *np = pdev->dev.of_node;
> > + struct of_endpoint endpoint;
> > + struct device_node *ep;
> > + struct reg_field field;
> > + struct vidsw *vidsw;
> > + struct regmap *map;
> > + unsigned int num_pads;
> > + int ret;
> > +
> > + vidsw = devm_kzalloc(&pdev->dev, sizeof(*vidsw), GFP_KERNEL);
> > + if (!vidsw)
> > + return -ENOMEM;
> > +
> > + platform_set_drvdata(pdev, vidsw);
> > +
> > + v4l2_subdev_init(&vidsw->subdev, &vidsw_subdev_ops);
> > + snprintf(vidsw->subdev.name, sizeof(vidsw->subdev.name), "%s",
> > + np->name);
> > + vidsw->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > + vidsw->subdev.dev = &pdev->dev;
> > +
> > + /*
> > + * The largest numbered port is the output port. It determines
> > + * total number of pads
> > + */
> > + num_pads = 0;
>
> You can initialise num_pads in variable declaration.
>
> > + for_each_endpoint_of_node(np, ep) {
> > + of_graph_parse_endpoint(ep, &endpoint);
> > + num_pads = max(num_pads, endpoint.port + 1);
>
> Port numbers come directly from DT.
>
> Shouldn't num_pads be only the number of pads that have links with actual
> physical connections? I.e. if a device is disabled, it shouldn't be
> counted here.
If we only create media pads for the connected DT ports with available
remote device, there is no 1:1 correspondence between pad number and the
physical mux setting anymore, complicating the driver.
> > + }
> > +
> > + if (num_pads < 2) {
> > + dev_err(&pdev->dev, "Not enough ports %d\n", num_pads);
> > + return -EINVAL;
> > + }
> > +
> > + ret = of_get_reg_field(np, &field);
> > + if (ret == 0) {
> > + map = syscon_node_to_regmap(np->parent);
> > + if (!map) {
> > + dev_err(&pdev->dev, "Failed to get syscon register map\n");
> > + return PTR_ERR(map);
> > + }
> > +
> > + vidsw->field = devm_regmap_field_alloc(&pdev->dev, map, field);
> > + if (IS_ERR(vidsw->field)) {
> > + dev_err(&pdev->dev, "Failed to allocate regmap field\n");
> > + return PTR_ERR(vidsw->field);
> > + }
> > +
> > + regmap_field_read(vidsw->field, &vidsw->active);
> > + } else {
> > + if (num_pads > 3) {
> > + dev_err(&pdev->dev, "Too many ports %d\n", num_pads);
> > + return -EINVAL;
> > + }
> > +
> > + vidsw->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
> > + if (IS_ERR(vidsw->gpio)) {
> > + dev_warn(&pdev->dev,
> > + "could not request control gpio: %d\n", ret);
> > + vidsw->gpio = NULL;
> > + }
> > +
> > + vidsw->active = gpiod_get_value(vidsw->gpio) ? 1 : 0;
> > + }
> > +
> > + vidsw->num_pads = num_pads;
> > + vidsw->pads = devm_kzalloc(&pdev->dev, sizeof(*vidsw->pads) * num_pads,
> > + GFP_KERNEL);
> > + vidsw->format_mbus = devm_kzalloc(&pdev->dev,
> > + sizeof(*vidsw->format_mbus) * num_pads, GFP_KERNEL);
> > + vidsw->endpoint = devm_kzalloc(&pdev->dev,
> > + sizeof(*vidsw->endpoint) * (num_pads - 1), GFP_KERNEL);
> > +
> > + ret = vidsw_async_init(vidsw, np);
> > + if (ret)
> > + return ret;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidsw_remove(struct platform_device *pdev)
> > +{
> > + struct vidsw *vidsw = platform_get_drvdata(pdev);
> > + struct v4l2_subdev *sd = &vidsw->subdev;
> > +
> > + v4l2_async_unregister_subdev(sd);
> > + media_entity_cleanup(&sd->entity);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id vidsw_dt_ids[] = {
> > + { .compatible = "video-multiplexer", },
> > + { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, vidsw_dt_ids);
> > +
> > +static struct platform_driver vidsw_driver = {
> > + .probe = vidsw_probe,
> > + .remove = vidsw_remove,
> > + .driver = {
> > + .of_match_table = vidsw_dt_ids,
> > + .name = "video-multiplexer",
> > + },
> > +};
> > +
> > +module_platform_driver(vidsw_driver);
> > +
> > +MODULE_DESCRIPTION("video stream multiplexer");
> > +MODULE_AUTHOR("Sascha Hauer, Pengutronix");
> > +MODULE_AUTHOR("Philipp Zabel, Pengutronix");
> > +MODULE_LICENSE("GPL");
regards
Philipp
--
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 V4 2/9] PM / Domains: Use OPP tables for power-domains
From: Sudeep Holla @ 2017-04-13 13:45 UTC (permalink / raw)
To: Viresh Kumar
Cc: Sudeep Holla, Rafael Wysocki, ulf.hansson-QSEj5FYQhm4dnm+yROfE0A,
Kevin Hilman, linaro-kernel-cunTk1MwBs8s++Sfvej+rw,
linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Vincent Guittot,
Stephen Boyd, Nishanth Menon, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
lina.iyer-QSEj5FYQhm4dnm+yROfE0A, rnayak-sgV2jX0FEOL9JmXXK+q4OQ,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170413060342.GO5910@vireshk-i7>
On 13/04/17 07:03, Viresh Kumar wrote:
> On 12-04-17, 17:58, Sudeep Holla wrote:
>>
>>
>> On 20/03/17 09:32, Viresh Kumar wrote:
>>> The OPP table bindings contains all the necessary fields to support
>>> power-domains now. Update the power-domain bindings to allow
>>> "operating-points-v2" to be present within the power-domain node.
>>>
>>> Also allow consumer devices, that don't use OPP tables, to specify the
>>> parent power-domain's performance level using the
>>> "domain-performance-state" property.
>>>
>>> Signed-off-by: Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>>> ---
>>> .../devicetree/bindings/power/power_domain.txt | 42 ++++++++++++++++++++++
>>> 1 file changed, 42 insertions(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
>>> index 723e1ad937da..5db112fa5d7c 100644
>>> --- a/Documentation/devicetree/bindings/power/power_domain.txt
>>> +++ b/Documentation/devicetree/bindings/power/power_domain.txt
>>> @@ -38,6 +38,9 @@ phandle arguments (so called PM domain specifiers) of length specified by the
>>> domain's idle states. In the absence of this property, the domain would be
>>> considered as capable of being powered-on or powered-off.
>>>
>>> +- operating-points-v2 : This describes the performance states of a PM domain.
>>> + Refer to ../opp/opp.txt for more information.
>>> +
>>> Example:
>>>
>>> power: power-controller@12340000 {
>>> @@ -118,4 +121,43 @@ The node above defines a typical PM domain consumer device, which is located
>>> inside a PM domain with index 0 of a power controller represented by a node
>>> with the label "power".
>>>
>>> +Optional properties:
>>> +- domain-performance-state: A positive integer value representing the minimum
>>> + power-domain performance level required by the consumer device. The integer
>>> + value '0' represents the lowest performance level and the higher values
>>> + represent higher performance levels. The value of "domain-performance-state"
>>> + field should match the "domain-performance-state" field of one of the OPP
>>> + nodes in the parent power-domain's OPP table.
>>> +
>>> +
>>> +
>>> +Example:
>>> +
>>> + domain_opp_table: opp_table {
>>> + compatible = "operating-points-v2";
>>> +
>>> + opp@1 {
>>> + domain-performance-state = <1>;
>>> + opp-microvolt = <975000 970000 985000>;
>>> + };
>>> + opp@2 {
>>> + domain-performance-state = <2>;
>>> + opp-microvolt = <1075000 1000000 1085000>;
>>> + };
>>> + };
>>> +
>>> + parent: power-controller@12340000 {
>>> + compatible = "foo,power-controller";
>>> + reg = <0x12340000 0x1000>;
>>> + #power-domain-cells = <0>;
>>> + operating-points-v2 = <&domain_opp_table>;
>>
>> As mentioned in the other email, it would be good to consider
>> scalability with multiple power domains in a PM domain provider.
>> i.e case of #power-domain-cells = <1> or more
>
> Yeah, but that isn't supported for devices today. So no point
> considering that today.
>
Do you mean we don't support power controllers with multiple power
domains ? If yes, we do support #power-domain-cells=<1 or more> clearly
from the binding and this change simple doesn't scale with such power
controllers/power-domain providers.
--
Regards,
Sudeep
--
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 V4 1/9] PM / OPP: Allow OPP table to be used for power-domains
From: Sudeep Holla @ 2017-04-13 13:43 UTC (permalink / raw)
To: Viresh Kumar
Cc: Sudeep Holla, Rafael Wysocki, ulf.hansson-QSEj5FYQhm4dnm+yROfE0A,
Kevin Hilman, Viresh Kumar, Nishanth Menon, Stephen Boyd,
linaro-kernel-cunTk1MwBs8s++Sfvej+rw,
linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Vincent Guittot,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, lina.iyer-QSEj5FYQhm4dnm+yROfE0A,
rnayak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170413055049.GN5910@vireshk-i7>
On 13/04/17 06:50, Viresh Kumar wrote:
> On 12-04-17, 18:05, Sudeep Holla wrote:
>>
>>
>> On 20/03/17 09:32, Viresh Kumar wrote:
[...]
>>
>> Thinking more about this above example, I think you need more
>> explanation. So in the above case you have cpu with clock controller,
>> power-domain and the OPP table info, I can think of few things that need
>> to be explicit:
>>
>> 1. How does the precedence look like ?
>
> Just think of the power-domain as a regulator here. If we are
> increasing frequency of the device, power-domain needs to be
> programmed first followed by the clock.
>
Interesting. My understand of power domain and in particular power
domain performance was that it would control both. The abstract number
you introduce would hide clocks and regulators.
But if the concept treats it just as yet another regulator, we do we
need these at all. Why don't we relate this performance to regulator
values and be done with it ?
Sorry if I am missing to understand something here. I would look this as
replacement for both clocks and regulators, something similar to ACPI
CPPC. If not, it looks unnecessary to me with the information I have got
so far.
>> 2. Since power-domains with OPP table control the performance state, do
>
> They control performance state of the domains, not the devices.
>
>> we ignore clock and operating-points-v2 in the above case completely?
>
> No. They are separate.
>
Understood now, but still trying to understand the complexity introduced
here.
>>
>> 3. Will the power-domain drive the OPP ?
>
> power-domain will driver its own state using its own OPP table.
> Devices may fine tune within those states.
>
I fail to understand here. This makes me think this power domain is same
as regulators as you pointed out earlier. So, we do we need all these
extra things. I was hoping this to be something like ACPI CPPC that hide
away clock and regulators.
--
Regards,
Sudeep
--
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 V4 1/9] PM / OPP: Allow OPP table to be used for power-domains
From: Sudeep Holla @ 2017-04-13 13:42 UTC (permalink / raw)
To: Viresh Kumar
Cc: Sudeep Holla, Rafael Wysocki, ulf.hansson, Kevin Hilman,
Viresh Kumar, Nishanth Menon, Stephen Boyd, linaro-kernel,
linux-pm, linux-kernel, Vincent Guittot, robh+dt, lina.iyer,
rnayak, devicetree
In-Reply-To: <20170413053736.GM5910@vireshk-i7>
On 13/04/17 06:37, Viresh Kumar wrote:
> On 12-04-17, 17:49, Sudeep Holla wrote:
>> On 20/03/17 09:32, Viresh Kumar wrote:
>>> diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt
>>> index 63725498bd20..d0b95c9e1011 100644
>>> --- a/Documentation/devicetree/bindings/opp/opp.txt
>>> +++ b/Documentation/devicetree/bindings/opp/opp.txt
>>> @@ -76,10 +76,9 @@ This describes the OPPs belonging to a device. This node can have following
>>> This defines voltage-current-frequency combinations along with other related
>>> properties.
>>>
>>> -Required properties:
>>> +Optional properties:
>>> - opp-hz: Frequency in Hz, expressed as a 64-bit big-endian integer.
>>>
>>> -Optional properties:
>>> - opp-microvolt: voltage in micro Volts.
>>>
>>> A single regulator's voltage is specified with an array of size one or three.
>>> @@ -154,6 +153,19 @@ properties.
>>>
>>> - status: Marks the node enabled/disabled.
>>>
>>> +- domain-performance-state: A positive integer value representing the minimum
>>> + power-domain performance level required by the device for the OPP node. The
>>
>> So the above definition is when this field in in the device node rather
>> than the OPP table entry, right ?
>
> No. We are updating the opp.txt file here and so it is not about the
> device node. The OPP node entries will contain this field for two
> cases:
> - The OPP table belongs to a power domain
> - The OPP table belongs to a device whose power domain supports
> performance-states.
>
Understood.
>> For simplicity why not have the
>> properties named slightly different or just use phandle to an entry in
>> the device node for this purpose.
>
> We really need a value here. For example, in case where the OPP table
> defines the states of the power-domain itself, we don't have any
> phandles to point to.
>
OK
>>> + The integer value '0' represents the lowest performance level and the higher
>>> + values represent higher performance levels.
>>
>> needs to be changed as OPP table entry.
>
> Not sure I understood what change you are looking for :(
>
Looks like I commented the same thing below, just redundant comment
here. Sorry about that.
>>> When present in the OPP table of a
>>> + power-domain, it represents the performance level of the domain. When present
>>
>> again "performance level of the domain corresponding to that OPP entry"
>> on something similar
>
> Ok.
>
>>> + in the OPP table of a normal device, it represents the performance level of
>>
>> what do you mean by normal device ? needs description as that's
>> something new introduced here.
>
> It should be non-power-domain node.
>
OK
>>> + the parent power-domain. The OPP table can contain the
>>> + "domain-performance-state" property, only if the device node contains the
>>> + "power-domains" or "#power-domain-cells" property.
>>
>> Why such a restriction ?
>
> Why would we use it for non-power-domain cases? That's not what we
> are looking for..
>
OK I was imagining that this would abstract clocks and regulators and
hence thinking of other possibilities.
>>> The OPP nodes aren't
>>> + allowed to contain the "domain-performance-state" property partially, i.e.
>>> + Either all OPP nodes in the OPP table have the "domain-performance-state"
>>> + property or none of them have it.
>>> +
>>> Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
>>>
>>> / {
>>> @@ -528,3 +540,60 @@ Example 5: opp-supported-hw
>>> };
>>> };
>>> };
>>> +
>>> +Example 7: domain-Performance-state:
>>> +(example: For 1GHz require domain state 1 and for 1.1 & 1.2 GHz require state 2)
>>> +
>>> +/ {
>>> + domain_opp_table: opp_table0 {
>>> + compatible = "operating-points-v2";
>>> +
>>> + opp@1 {
>>> + domain-performance-state = <1>;
>>> + opp-microvolt = <975000 970000 985000>;
>>> + };
>>> + opp@2 {
>>> + domain-performance-state = <2>;
>>> + opp-microvolt = <1075000 1000000 1085000>;
>>> + };
>>> + };
>>> +
>>> + foo_domain: power-controller@12340000 {
>>> + compatible = "foo,power-controller";
>>> + reg = <0x12340000 0x1000>;
>>> + #power-domain-cells = <0>;
>>> + operating-points-v2 = <&domain_opp_table>;
>>
>> How does it scale with power domain providers with multiple power domain ?
>
> Devices can't have multiple power domains today. Will see this when
> that support is added.
>
Agreed and I see some working already happening on that, so yes we can
add that later.
What I was referring is about power domain provider with multiple power
domains(simply #power-domain-cells=<1> case as explained in the
power-domain specification.
> Note that only the power domains can have multiple parent power
> domains today.
>
>>> + }
>>> +
>>> + cpu0_opp_table: opp_table1 {
>>> + compatible = "operating-points-v2";
>>> + opp-shared;
>>> +
>>> + opp@1000000000 {
>>> + opp-hz = /bits/ 64 <1000000000>;
>>> + domain-performance-state = <1>;
>>> + };
>>> + opp@1100000000 {
>>> + opp-hz = /bits/ 64 <1100000000>;
>>> + domain-performance-state = <2>;
>>> + };
>>> + opp@1200000000 {
>>> + opp-hz = /bits/ 64 <1200000000>;
>>> + domain-performance-state = <2>;
>>> + };
>>> + };
>>> +
>>> + cpus {
>>> + #address-cells = <1>;
>>> + #size-cells = <0>;
>>> +
>>> + cpu@0 {
>>> + compatible = "arm,cortex-a9";
>>> + reg = <0>;
>>> + clocks = <&clk_controller 0>;
>>> + clock-names = "cpu";
>>> + operating-points-v2 = <&cpu0_opp_table>;
>>
>> Do we ignore operating-points-v2 above as this device/cpu node contains
>> power domain which has operating-points-v2 property ? In other words
>> how do they correlate ?
>
> Devices and their power domains can both have their performance
> states. Just that to get the device in a particular state, we may need
> to get its power domain to a particular state first.
>
Yes. To simplify what not we just have power-domain for a device and
change state of that domain to change the performance of that device.
Then put this in the hierarchy. Some thing similar to what we already
have with new domain-idle states. In that way, we can move any
performance control to the domain and abstract the clocks and regulators
from the devices as the first step and from the OSPM view if there's
firmware support.
If we are looking this power-domains with performance as just some
*advanced regulators*, I don't like the complexity added.
I am also looking at how does this align with other specifications like
ACPI and SCMI that are trying to solve similar issues.
--
Regards,
Sudeep
^ 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