Devicetree
 help / color / mirror / Atom feed
* Re: specifying order of /dev/mmcblk devices via device-tree?
From: Stefan Agner @ 2016-11-19  1:18 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Tim Harvey, Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Ulf Hansson, Fabio Estevam, Javier Martinez Canillas,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20161115235503.GC1041-l+eeeJia6m9URfEZ8mYm6t73F7V6hmMc@public.gmane.org>

On 2016-11-15 15:55, Russell King - ARM Linux wrote:
> On Tue, Nov 15, 2016 at 10:10:02PM +0000, Russell King - ARM Linux wrote:
>> On Tue, Nov 15, 2016 at 01:39:42PM -0800, Tim Harvey wrote:
>> > On Tue, Nov 15, 2016 at 1:35 PM, Russell King - ARM Linux
>> > <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org> wrote:
>> > > On Tue, Nov 15, 2016 at 12:27:53PM -0800, Tim Harvey wrote:
>> > >> On Mon, Nov 14, 2016 at 11:08 AM, Russell King - ARM Linux
>> > >> <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org> wrote:
>> > >> > So, someone merged a patch which makes mmcblk devices follow the
>> > >> > host controller numbering.
>> > >> >
>> > >> > Now my cubox-i fails to boot correctly because the SD card in the
>> > >> > _only_ SD card slot now gets called "mmcblk1" and not "mmcblk0".
>> > >> >
>> > >> > USDHC1 is wired to the on-microsom WiFi, and never has anything
>> > >> > remotely near a SD card or eMMC present.  So, this change is
>> > >> > confusing on these platforms.
>> > >> >
>> > >> > Moreover, this is _going_ to break SolidRun distros if people upgrade
>> > >> > their kernels.
>> > >> >
>> > >> > It may be appropriate for eMMC, but it's not appropriate everywhere.
>> > >> >
>> > >> > This is a user visible _regression_ in 4.9-rc.  Whoever did this,
>> > >> > please revert whatever change caused this, and next time limit it
>> > >> > to only eMMC.
>> > >> >
>> > >> > Thanks.
>> > >>
>> > >> I see the same thing on newer kernels, which is why I asked the
>> > >> question. I didn't expect (or even want honestly) a non mmcblk0 boot
>> > >> device and was looking for a way to control that via dt. Now I'm
>> > >> understanding that to avoid this kind of bootloader/kernel dependence
>> > >> issue I should be using UUID's to identify the boot device.
>> > >>
>> > >> >From my testing it looks like the change your looking for occurred
>> > >> some time ago and is somewhere between 4.5 and 4.6 and not a 4.9
>> > >> regression specifically.
>> > >
>> > > That depends how you look at it.  Yes, there's a change in 4.5 to 4.6
>> > > which ties the block device number to the host device index, but that's
>> > > really only part of the story here.
>> > >
>> > > 4.8 definitely identifies the SD card in iMX6 usdhc2 as "mmcblk0".
>> > > 4.9-rc identifies the SD card as "mmcblk1".  This makes it a 4.9 change
>> > > of behaviour - there can be no argument about that.
>> > >
>> > > Now, digging further into this today, it appears that:
>> > >
>> > > v4.8: usdhc2 was probed first, and is given mmc0.
>> > >       usdhc1 is probed second, and is given mmc1.
>> > >
>> > > v4.9-rc: usdhc1 is probed first, and is given mmc0.
>> > >          usdhc2 is probed second, and is given mmc1.
>> > >
>> > > I haven't yet been able to figure out why there's been this change
>> > > of probe order.  There's no change that I can see in the iMX6 DT
>> > > files that would account for this.
>> > >
>> >
>> > I bisected it and the commit your looking for is
>> > 9aaf3437aa72ed5370bf32c99580a3fa2c330e3d
>>
>> No it's not.
>>
>> Let me try and put it plainer:
>>
>> * Commit 9aaf3437aa72ed5370bf32c99580a3fa2c330e3d ties the mmc block
>>   device number (mmcblkN) to the mmc host interface number (mmcN).
>>   This change happened between 4.5 and 4.6.
>>
>> * The change I'm seeing happened between 4.8 and 4.9-rc.  I'm not
>>   seeing a change of behaviour between 4.5 and 4.6.
>>
>> * The change I'm seeing changes the order of the physical device
>>   associated with the hosts named mmc0 and mmc1 in the kernel.
>>
>> * Because physical devices associated with the mmc0 and mmc1 hosts
>>   swap over, the mmcblkN number changes due to the commit you point
>>   out.
>>
>> * So, the change that I'm seeing between 4.8 and 4.9-rc is not caused
>>   by commit 9aaf3437aa72ed5370bf32c99580a3fa2c330e3d, but by something
>>   else changing the order in which the two usdhc physical hardware
>>   blocks get probed.
>>
>> Does this make it clearer?
> 
> It turns out to be this commit:
> 
> commit 6eb1c9496b81680f2cd2e0eda06c531317e2e28d
> Author: Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
> Date:   Mon Sep 19 01:16:44 2016 +0900
> 
>     clk: probe common clock drivers earlier
> 
>     Several SoCs implement platform drivers for clocks rather than
>     CLK_OF_DECLARE().  Clocks should come earlier because they are
>     prerequisites for many of other drivers.  It will help to mitigate
>     EPROBE_DEFER issues.
> 
>     Also, drop the comment since it does not carry much value.
> 
>     Signed-off-by: Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
>     Acked-by: Michael Turquette <mturquette-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
>     Signed-off-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
> 
> which changes the init order.  In 4.8, we get:

Afaik, init order is not guaranteed, it never was. Usually the order
ends up to be in some order of the device tree, but one could also parse
the device tree in reverse order (which would be an interesting
experiment).

If we want to rely on ordering, we need to add alias support.

--
Stefan
--
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: specifying order of /dev/mmcblk devices via device-tree?
From: Stefan Agner @ 2016-11-19  1:23 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Tim Harvey,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <CAPDyKFrpqmvVAKsfQBP1m7vdOjPRx7puVkmmCe3yxe1gVFwgzw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On 2016-11-16 06:45, Ulf Hansson wrote:
> On 28 October 2016 at 17:37, Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org> wrote:
>> On Fri, Oct 28, 2016 at 08:23:04AM -0700, Tim Harvey wrote:
>>> Greetings,
>>>
>>> I have an IMX6 board that has the following:
>>> sdhc1: mmc0: sdio radio
>>> sdhc2: mmc1: /dev/mmcblk1: microSD connector
>>> sdhc3: mmc2: /dev/mmcblk2: on-board eMMC
>>>
>>> I would like to have sdhc3 registered as /dev/mmcblk0 and sdhc2
>>> registered as /dev/mmcblk1 so that permanent storage is the first
>>> mmcblk device as I think this is more intuitive however currently
>>> these get instanced in the order they appear in the imx6qdl.dtsi
>>> device-tree configuration and are not able to be mapped the way I want
>>> them in my dts file.
>>>
>>> Is there a way, or if not is there a desire for a way, to specify the
>>> order of /dev/mmcblk devices via device-tree?
>>
>> As with many other devices, there is no standard way of controlling the
>> Linux enumeration (and given the ID space is shared with other dynamic
>> devices it's not something that could generally work).
>>
>> These should be refererd to by UUID if possible.
>>
>> If not, we could cosider adding a by-dt-path or something like that.
> 
> So does that mean you think using "DT aliases" would be okay? As
> Javier pointed out, there have been some attempts [1] for that, but
> they didn't make it.
> Perhaps we need to re-consider, and if so please re-review the DT
> bindings patch from that series.

I really would like to see some sort of stable MMC block device
ordering.

by-dt-path solutions requires initramfs, which adds complexity and boot
time. Both not very welcome in simple embedded devices.

PARTUUID is relying on a particular instance of a partition, which is
not the same as to say boot from the second partition of device X.

The main problem I see in my patchset is that it works around the fact
MMC is a bus. Adding the mmc controller number to the block device name
would avoid that issue (mmc0blk0...), not sure if a such a drastic
change would be acceptable.

--
Stefan
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] mfd: cpcap: Add minimal support
From: Tony Lindgren @ 2016-11-19  1:27 UTC (permalink / raw)
  To: Lee Jones, Samuel Ortiz
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Marcel Partap, Mark Rutland,
	Michael Scott, Rob Herring

Many Motorola phones like droid 4 are using a custom PMIC called CPCAP
or 6556002. We can support it's core features quite easily with regmap_spi
and regmap_irq.

The children of cpcap, such as regulators, ADC and USB, can be just regular
device drivers and defined in the dts file. They get probed as we call
of_platform_populate() at the end of our probe, and then the children
can just call dev_get_regmap(dev.parent, NULL) to get the regmap.

Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Marcel Partap <mpartap-hi6Y0CQ0nG0@public.gmane.org>
Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
Cc: Michael Scott <michael.scott-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Signed-off-by: Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
---
 Documentation/devicetree/bindings/mfd/cpcap.txt |  36 ++++
 drivers/mfd/Kconfig                             |   8 +
 drivers/mfd/Makefile                            |   1 +
 drivers/mfd/cpcap.c                             | 255 ++++++++++++++++++++++++
 include/linux/mfd/cpcap.h                       | 238 ++++++++++++++++++++++
 5 files changed, 538 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/cpcap.txt
 create mode 100644 drivers/mfd/cpcap.c
 create mode 100644 include/linux/mfd/cpcap.h

diff --git a/Documentation/devicetree/bindings/mfd/cpcap.txt b/Documentation/devicetree/bindings/mfd/cpcap.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/cpcap.txt
@@ -0,0 +1,36 @@
+CPCAP PMIC device tree binding
+
+Required properties:
+- compatible		: Motorola device "motorola,cpcap", others "st,6556002"
+- reg			: Chip select and size
+- interrupt-parent	: The parent interrupt controller
+- interrupts		: The interrupt line the device is connected to
+- interrupt-controller	: Marks the device node as an interrupt controller
+- #interrupt-cells	: The number of cells to describe an IRQ, should be 2
+- #address-cells	: Child device offset number of cells, typically 1
+- #size-cells		: Child device size number of cells, typically 1
+- ranges		: Child device register range
+- spi-max-frequency	: Typically set to 3000000
+- spi-cs_high		: SPI chip select direction
+
+Example:
+
+&mcspi1 {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges;
+	cpcap: pmic@0 {
+		compatible = "motorola,cpcap", "st,6556002";
+		reg = <0 0>;	/* cs0, size 0 */
+		interrupt-parent = <&gpio1>;
+		interrupts = <7 IRQ_TYPE_EDGE_RISING>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0 0 0x8000>;
+		spi-max-frequency = <3000000>;
+		spi-cs-high;
+	};
+};
+
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -713,6 +713,14 @@ config EZX_PCAP
 	  This enables the PCAP ASIC present on EZX Phones. This is
 	  needed for MMC, TouchScreen, Sound, USB, etc..
 
+config MFD_CPCAP
+	tristate "Support for CPCAP"
+	depends on SPI && OF
+	help
+	  Say yes here if you want to include driver for CPCAP.
+	  It is used on many Motorola phones and tablets as a PMIC.
+	  At least Motorola Droid 4 is known to use CPCAP.
+
 config MFD_VIPERBOARD
         tristate "Nano River Technologies Viperboard"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
 obj-$(CONFIG_MFD_CORE)		+= mfd-core.o
 
 obj-$(CONFIG_EZX_PCAP)		+= ezx-pcap.o
+obj-$(CONFIG_MFD_CPCAP)		+= cpcap.o
 
 obj-$(CONFIG_MCP)		+= mcp-core.o
 obj-$(CONFIG_MCP_SA11X0)	+= mcp-sa11x0.o
diff --git a/drivers/mfd/cpcap.c b/drivers/mfd/cpcap.c
new file mode 100644
--- /dev/null
+++ b/drivers/mfd/cpcap.c
@@ -0,0 +1,255 @@
+/*
+ * 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/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+
+#include <linux/mfd/cpcap.h>
+#include <linux/spi/spi.h>
+
+#define CPCAP_NR_IRQ_BANKS	6
+#define CPCAP_NR_IRQ_DOMAINS	3
+
+struct cpcap_device {
+	struct spi_device *spi;
+	struct device *dev;
+	u16 vendor;
+	u16 revision;
+	const struct cpcap_platform_data *conf;
+	struct regmap_irq *irqs;
+	struct regmap_irq_chip_data *irqdata[CPCAP_NR_IRQ_DOMAINS];
+	const struct regmap_config *regmap_conf;
+	struct regmap *regmap;
+};
+
+static int cpcap_check_revision(struct cpcap_device *cpcap)
+{
+	unsigned int val;
+	int error;
+
+	error = regmap_read(cpcap->regmap, CPCAP_REG_VERSC1, &val);
+	if (error)
+		return error;
+
+	cpcap->vendor = (val >> 6) & 0x0007;
+	cpcap->revision = ((val >> 3) & 0x0007) | ((val << 3) & 0x0038);
+	dev_info(cpcap->dev, "CPCAP vendor: %s rev: %i.%i (%x)\n",
+		 cpcap->vendor ? "TI" : "ST", (cpcap->revision >> 4) + 1,
+		 cpcap->revision & 0xf, cpcap->revision);
+
+	if (cpcap->revision < CPCAP_REVISION_2_1) {
+		dev_info(cpcap->dev,
+			 "Please add old CPCAP revision support as needed\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ * First domain is the two private macro interrupt banks, the third
+ * domain is for banks 1 - 4 and is available for drivers to use.
+ */
+static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_DOMAINS] = {
+	{
+		.name = "cpcap-m2",
+		.num_regs = 1,
+		.status_base = CPCAP_REG_MI1,
+		.ack_base = CPCAP_REG_MI1,
+		.mask_base = CPCAP_REG_MIM1,
+		.use_ack = true,
+	},
+	{
+		.name = "cpcap-m2",
+		.num_regs = 1,
+		.status_base = CPCAP_REG_MI2,
+		.ack_base = CPCAP_REG_MI2,
+		.mask_base = CPCAP_REG_MIM2,
+		.use_ack = true,
+	},
+	{
+		.name = "cpcap1-4",
+		.num_regs = 4,
+		.status_base = CPCAP_REG_INT1,
+		.ack_base = CPCAP_REG_INT1,
+		.mask_base = CPCAP_REG_INTM1,
+		.type_base = CPCAP_REG_INTS1,
+		.use_ack = true,
+	},
+};
+
+static int cpcap_init_irq_bank(struct cpcap_device *cpcap, int irq_domain,
+			       int irq_start, int nr_irqs)
+{
+	struct regmap_irq_chip *domain = &cpcap_irq_chip[irq_domain];
+	int i, error;
+
+	for (i = irq_start; i < irq_start + nr_irqs; i++) {
+		struct regmap_irq *cpcap_irq = &cpcap->irqs[i];
+
+		cpcap_irq->reg_offset =
+			((i - irq_start) / cpcap->regmap_conf->val_bits) *
+			cpcap->regmap_conf->reg_stride;
+		cpcap_irq->mask = BIT(i % cpcap->regmap_conf->val_bits);
+	}
+	domain->irqs = &cpcap->irqs[irq_start];
+	domain->num_irqs = nr_irqs;
+	domain->irq_drv_data = cpcap;
+
+	error = devm_regmap_add_irq_chip(cpcap->dev, cpcap->regmap,
+					 cpcap->spi->irq,
+					 IRQF_TRIGGER_RISING |
+					 IRQF_SHARED, -1,
+					 domain, &cpcap->irqdata[irq_domain]);
+	if (error) {
+		dev_err(cpcap->dev, "could not add irq domain %i: %i\n",
+			irq_domain, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int cpcap_init_irq(struct cpcap_device *cpcap)
+{
+	int error;
+
+	cpcap->irqs = devm_kzalloc(cpcap->dev,
+				   sizeof(*cpcap->irqs) *
+				   CPCAP_NR_IRQ_BANKS *
+				   cpcap->regmap_conf->val_bits,
+				   GFP_KERNEL);
+	if (!cpcap->irqs)
+		return -ENOMEM;
+
+	error = cpcap_init_irq_bank(cpcap, 0, 0, 16);
+	if (error)
+		return error;
+
+	error = cpcap_init_irq_bank(cpcap, 1, 16, 16);
+	if (error)
+		return error;
+
+	error = cpcap_init_irq_bank(cpcap, 2, 32, 64);
+	if (error)
+		return error;
+
+	enable_irq_wake(cpcap->spi->irq);
+
+	return 0;
+}
+
+static const struct of_device_id cpcap_of_match[] = {
+	{
+		.compatible = "motorola,cpcap",
+	},
+	{
+		.compatible = "st,6556002",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, cpcap_of_match);
+
+static const struct regmap_config cpcap_regmap_config = {
+	.reg_bits = 16,
+	.reg_stride = 4,
+	.pad_bits = 0,
+	.val_bits = 16,
+	.write_flag_mask = 0x8000,
+	.max_register = CPCAP_REG_ST_TEST2,
+	.cache_type = REGCACHE_NONE,
+	.reg_format_endian = REGMAP_ENDIAN_LITTLE,
+	.val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static const struct of_device_id cpcap_dt_match_table[] = {
+	{ .compatible = "simple-bus", },
+	{ },
+};
+
+static int cpcap_probe(struct spi_device *spi)
+{
+	const struct of_device_id *match;
+	int error = -EINVAL;
+	struct cpcap_device *cpcap;
+
+	match = of_match_device(of_match_ptr(cpcap_of_match), &spi->dev);
+	if (!match)
+		return -ENODEV;
+
+	cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL);
+	if (!cpcap)
+		return -ENOMEM;
+
+	cpcap->conf = match->data;
+	cpcap->spi = spi;
+	cpcap->dev = &spi->dev;
+	spi_set_drvdata(spi, cpcap);
+
+	spi->bits_per_word = 16;
+	spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
+	error = spi_setup(spi);
+	if (error < 0)
+		return error;
+
+	cpcap->regmap_conf = &cpcap_regmap_config;
+	cpcap->regmap = devm_regmap_init_spi(spi, &cpcap_regmap_config);
+	if (IS_ERR(cpcap->regmap)) {
+		error = PTR_ERR(cpcap->regmap);
+		dev_err(cpcap->dev, "Failed to initialize regmap: %d\n",
+			error);
+
+		return error;
+	}
+
+	error = cpcap_check_revision(cpcap);
+	if (error)
+		return error;
+
+	error = cpcap_init_irq(cpcap);
+	if (error)
+		return error;
+
+	error = of_platform_populate(spi->dev.of_node,
+				     cpcap_dt_match_table,
+				     NULL, cpcap->dev);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static int cpcap_remove(struct spi_device *pdev)
+{
+	struct cpcap_device *cpcap = spi_get_drvdata(pdev);
+
+	of_platform_depopulate(cpcap->dev);
+
+	return 0;
+}
+
+static struct spi_driver cpcap_driver = {
+	.driver = {
+		.name = "cpcap-core",
+		.owner = THIS_MODULE,
+		.of_match_table = cpcap_of_match,
+	},
+	.probe = cpcap_probe,
+	.remove = cpcap_remove,
+};
+module_spi_driver(cpcap_driver);
+
+MODULE_ALIAS("platform:cpcap");
+MODULE_DESCRIPTION("CPCAP driver");
+MODULE_AUTHOR("Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/cpcap.h b/include/linux/mfd/cpcap.h
new file mode 100644
--- /dev/null
+++ b/include/linux/mfd/cpcap.h
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ *
+ * Note that the register defines are based on earlier cpcap.h in
+ * Motorola Linux kernel tree except rewritten for the real register
+ * addresses instead of enumeration so they are usable with regmap.
+ */
+
+#define CPCAP_VENDOR_ST		0
+#define CPCAP_VENDOR_TI		1
+
+#define CPCAP_REVISION_1_0	0x08
+#define CPCAP_REVISION_1_1	0x09
+#define CPCAP_REVISION_2_0	0x10
+#define CPCAP_REVISION_2_1	0x11
+
+/* CPCAP registers */
+#define CPCAP_REG_INT1		0x0000	/* Interrupt 1 */
+#define CPCAP_REG_INT2		0x0004	/* Interrupt 2 */
+#define CPCAP_REG_INT3		0x0008	/* Interrupt 3 */
+#define CPCAP_REG_INT4		0x000c	/* Interrupt 4 */
+#define CPCAP_REG_INTM1		0x0010	/* Interrupt Mask 1 */
+#define CPCAP_REG_INTM2		0x0014	/* Interrupt Mask 2 */
+#define CPCAP_REG_INTM3		0x0018	/* Interrupt Mask 3 */
+#define CPCAP_REG_INTM4		0x001c	/* Interrupt Mask 4 */
+#define CPCAP_REG_INTS1		0x0020	/* Interrupt Sense 1 */
+#define CPCAP_REG_INTS2		0x0024	/* Interrupt Sense 2 */
+#define CPCAP_REG_INTS3		0x0028	/* Interrupt Sense 3 */
+#define CPCAP_REG_INTS4		0x002c	/* Interrupt Sense 4 */
+#define CPCAP_REG_ASSIGN1	0x0030	/* Resource Assignment 1 */
+#define CPCAP_REG_ASSIGN2	0x0034	/* Resource Assignment 2 */
+#define CPCAP_REG_ASSIGN3	0x0038	/* Resource Assignment 3 */
+#define CPCAP_REG_ASSIGN4	0x003c	/* Resource Assignment 4 */
+#define CPCAP_REG_ASSIGN5	0x0040	/* Resource Assignment 5 */
+#define CPCAP_REG_ASSIGN6	0x0044	/* Resource Assignment 6 */
+#define CPCAP_REG_VERSC1	0x0048	/* Version Control 1 */
+#define CPCAP_REG_VERSC2	0x004c	/* Version Control 2 */
+
+#define CPCAP_REG_MI1		0x0200	/* Macro Interrupt 1 */
+#define CPCAP_REG_MIM1		0x0204	/* Macro Interrupt Mask 1 */
+#define CPCAP_REG_MI2		0x0208	/* Macro Interrupt 2 */
+#define CPCAP_REG_MIM2		0x020c	/* Macro Interrupt Mask 2 */
+#define CPCAP_REG_UCC1		0x0210	/* UC Control 1 */
+#define CPCAP_REG_UCC2		0x0214	/* UC Control 2 */
+
+#define CPCAP_REG_PC1		0x021c	/* Power Cut 1 */
+#define CPCAP_REG_PC2		0x0220	/* Power Cut 2 */
+#define CPCAP_REG_BPEOL		0x0224	/* BP and EOL */
+#define CPCAP_REG_PGC		0x0228	/* Power Gate and Control */
+#define CPCAP_REG_MT1		0x022c	/* Memory Transfer 1 */
+#define CPCAP_REG_MT2		0x0230	/* Memory Transfer 2 */
+#define CPCAP_REG_MT3		0x0234	/* Memory Transfer 3 */
+#define CPCAP_REG_PF		0x0238	/* Print Format */
+
+#define CPCAP_REG_SCC		0x0400	/* System Clock Control */
+#define CPCAP_REG_SW1		0x0404	/* Stop Watch 1 */
+#define CPCAP_REG_SW2		0x0408	/* Stop Watch 2 */
+#define CPCAP_REG_UCTM		0x040c	/* UC Turbo Mode */
+#define CPCAP_REG_TOD1		0x0410	/* Time of Day 1 */
+#define CPCAP_REG_TOD2		0x0414	/* Time of Day 2 */
+#define CPCAP_REG_TODA1		0x0418	/* Time of Day Alarm 1 */
+#define CPCAP_REG_TODA2		0x041c	/* Time of Day Alarm 2 */
+#define CPCAP_REG_DAY		0x0420	/* Day */
+#define CPCAP_REG_DAYA		0x0424	/* Day Alarm */
+#define CPCAP_REG_VAL1		0x0428	/* Validity 1 */
+#define CPCAP_REG_VAL2		0x042c	/* Validity 2 */
+
+#define CPCAP_REG_SDVSPLL	0x0600	/* Switcher DVS and PLL */
+#define CPCAP_REG_SI2CC1	0x0604	/* Switcher I2C Control 1 */
+#define CPCAP_REG_Si2CC2	0x0608	/* Switcher I2C Control 2 */
+#define CPCAP_REG_S1C1		0x060c	/* Switcher 1 Control 1 */
+#define CPCAP_REG_S1C2		0x0610	/* Switcher 1 Control 2 */
+#define CPCAP_REG_S2C1		0x0614	/* Switcher 2 Control 1 */
+#define CPCAP_REG_S2C2		0x0618	/* Switcher 2 Control 2 */
+#define CPCAP_REG_S3C		0x061c	/* Switcher 3 Control */
+#define CPCAP_REG_S4C1		0x0620	/* Switcher 4 Control 1 */
+#define CPCAP_REG_S4C2		0x0624	/* Switcher 4 Control 2 */
+#define CPCAP_REG_S5C		0x0628	/* Switcher 5 Control */
+#define CPCAP_REG_S6C		0x062c	/* Switcher 6 Control */
+#define CPCAP_REG_VCAMC		0x0630	/* VCAM Control */
+#define CPCAP_REG_VCSIC		0x0634	/* VCSI Control */
+#define CPCAP_REG_VDACC		0x0638	/* VDAC Control */
+#define CPCAP_REG_VDIGC		0x063c	/* VDIG Control */
+#define CPCAP_REG_VFUSEC	0x0640	/* VFUSE Control */
+#define CPCAP_REG_VHVIOC	0x0644	/* VHVIO Control */
+#define CPCAP_REG_VSDIOC	0x0648	/* VSDIO Control */
+#define CPCAP_REG_VPLLC		0x064c	/* VPLL Control */
+#define CPCAP_REG_VRF1C		0x0650	/* VRF1 Control */
+#define CPCAP_REG_VRF2C		0x0654	/* VRF2 Control */
+#define CPCAP_REG_VRFREFC	0x0658	/* VRFREF Control */
+#define CPCAP_REG_VWLAN1C	0x065c	/* VWLAN1 Control */
+#define CPCAP_REG_VWLAN2C	0x0660	/* VWLAN2 Control */
+#define CPCAP_REG_VSIMC		0x0664	/* VSIM Control */
+#define CPCAP_REG_VVIBC		0x0668	/* VVIB Control */
+#define CPCAP_REG_VUSBC		0x066c	/* VUSB Control */
+#define CPCAP_REG_VUSBINT1C	0x0670	/* VUSBINT1 Control */
+#define CPCAP_REG_VUSBINT2C	0x0674	/* VUSBINT2 Control */
+#define CPCAP_REG_URT		0x0678	/* Useroff Regulator Trigger */
+#define CPCAP_REG_URM1		0x067c	/* Useroff Regulator Mask 1 */
+#define CPCAP_REG_URM2		0x0680	/* Useroff Regulator Mask 2 */
+
+#define CPCAP_REG_VAUDIOC	0x0800	/* VAUDIO Control */
+#define CPCAP_REG_CC		0x0804	/* Codec Control */
+#define CPCAP_REG_CDI		0x0808	/* Codec Digital Interface */
+#define CPCAP_REG_SDAC		0x080c	/* Stereo DAC */
+#define CPCAP_REG_SDACDI	0x0810	/* Stereo DAC Digital Interface */
+#define CPCAP_REG_TXI		0x0814	/* TX Inputs */
+#define CPCAP_REG_TXMP		0x0818	/* TX MIC PGA's */
+#define CPCAP_REG_RXOA		0x081c	/* RX Output Amplifiers */
+#define CPCAP_REG_RXVC		0x0820	/* RX Volume Control */
+#define CPCAP_REG_RXCOA		0x0824	/* RX Codec to Output Amps */
+#define CPCAP_REG_RXSDOA	0x0828	/* RX Stereo DAC to Output Amps */
+#define CPCAP_REG_RXEPOA	0x082c	/* RX External PGA to Output Amps */
+#define CPCAP_REG_RXLL		0x0830	/* RX Low Latency */
+#define CPCAP_REG_A2LA		0x0834	/* A2 Loudspeaker Amplifier */
+#define CPCAP_REG_MIPIS1	0x0838	/* MIPI Slimbus 1 */
+#define CPCAP_REG_MIPIS2	0x083c	/* MIPI Slimbus 2 */
+#define CPCAP_REG_MIPIS3	0x0840	/* MIPI Slimbus 3. */
+#define CPCAP_REG_LVAB		0x0844	/* LMR Volume and A4 Balanced. */
+
+#define CPCAP_REG_CCC1		0x0a00	/* Coulomb Counter Control 1 */
+#define CPCAP_REG_CRM		0x0a04	/* Charger and Reverse Mode */
+#define CPCAP_REG_CCCC2		0x0a08	/* Coincell and Coulomb Ctr Ctrl 2 */
+#define CPCAP_REG_CCS1		0x0a0c	/* Coulomb Counter Sample 1 */
+#define CPCAP_REG_CCS2		0x0a10	/* Coulomb Counter Sample 2 */
+#define CPCAP_REG_CCA1		0x0a14	/* Coulomb Counter Accumulator 1 */
+#define CPCAP_REG_CCA2		0x0a18	/* Coulomb Counter Accumulator 2 */
+#define CPCAP_REG_CCM		0x0a1c	/* Coulomb Counter Mode */
+#define CPCAP_REG_CCO		0x0a20	/* Coulomb Counter Offset */
+#define CPCAP_REG_CCI		0x0a24	/* Coulomb Counter Integrator */
+
+#define CPCAP_REG_ADCC1		0x0c00	/* A/D Converter Configuration 1 */
+#define CPCAP_REG_ADCC2		0x0c04	/* A/D Converter Configuration 2 */
+#define CPCAP_REG_ADCD0		0x0c08	/* A/D Converter Data 0 */
+#define CPCAP_REG_ADCD1		0x0c0c	/* A/D Converter Data 1 */
+#define CPCAP_REG_ADCD2		0x0c10	/* A/D Converter Data 2 */
+#define CPCAP_REG_ADCD3		0x0c14	/* A/D Converter Data 3 */
+#define CPCAP_REG_ADCD4		0x0c18	/* A/D Converter Data 4 */
+#define CPCAP_REG_ADCD5		0x0c1c	/* A/D Converter Data 5 */
+#define CPCAP_REG_ADCD6		0x0c20	/* A/D Converter Data 6 */
+#define CPCAP_REG_ADCD7		0x0c24	/* A/D Converter Data 7 */
+#define CPCAP_REG_ADCAL1	0x0c28	/* A/D Converter Calibration 1 */
+#define CPCAP_REG_ADCAL2	0x0c2c	/* A/D Converter Calibration 2 */
+
+#define CPCAP_REG_USBC1		0x0e00	/* USB Control 1 */
+#define CPCAP_REG_USBC2		0x0e04	/* USB Control 2 */
+#define CPCAP_REG_USBC3		0x0e08	/* USB Control 3 */
+#define CPCAP_REG_UVIDL		0x0e0c	/* ULPI Vendor ID Low */
+#define CPCAP_REG_UVIDH		0x0e10	/* ULPI Vendor ID High */
+#define CPCAP_REG_UPIDL		0x0e14	/* ULPI Product ID Low */
+#define CPCAP_REG_UPIDH		0x0e18	/* ULPI Product ID High */
+#define CPCAP_REG_UFC1		0x0e1c	/* ULPI Function Control 1 */
+#define CPCAP_REG_UFC2		0x0e20	/* ULPI Function Control 2 */
+#define CPCAP_REG_UFC3		0x0e24	/* ULPI Function Control 3 */
+#define CPCAP_REG_UIC1		0x0e28	/* ULPI Interface Control 1 */
+#define CPCAP_REG_UIC2		0x0e2c	/* ULPI Interface Control 2 */
+#define CPCAP_REG_UIC3		0x0e30	/* ULPI Interface Control 3 */
+#define CPCAP_REG_USBOTG1	0x0e34	/* USB OTG Control 1 */
+#define CPCAP_REG_USBOTG2	0x0e38	/* USB OTG Control 2 */
+#define CPCAP_REG_USBOTG3	0x0e3c	/* USB OTG Control 3 */
+#define CPCAP_REG_UIER1		0x0e40	/* USB Interrupt Enable Rising 1 */
+#define CPCAP_REG_UIER2		0x0e44	/* USB Interrupt Enable Rising 2 */
+#define CPCAP_REG_UIER3		0x0e48	/* USB Interrupt Enable Rising 3 */
+#define CPCAP_REG_UIEF1		0x0e4c	/* USB Interrupt Enable Falling 1 */
+#define CPCAP_REG_UIEF2		0x0e50	/* USB Interrupt Enable Falling 1 */
+#define CPCAP_REG_UIEF3		0x0e54	/* USB Interrupt Enable Falling 1 */
+#define CPCAP_REG_UIS		0x0e58	/* USB Interrupt Status */
+#define CPCAP_REG_UIL		0x0e5c	/* USB Interrupt Latch */
+#define CPCAP_REG_USBD		0x0e60	/* USB Debug */
+#define CPCAP_REG_SCR1		0x0e64	/* Scratch 1 */
+#define CPCAP_REG_SCR2		0x0e68	/* Scratch 2 */
+#define CPCAP_REG_SCR3		0x0e6c	/* Scratch 3 */
+
+#define CPCAP_REG_VMC		0x0eac	/* Video Mux Control */
+#define CPCAP_REG_OWDC		0x0eb0	/* One Wire Device Control */
+#define CPCAP_REG_GPIO0		0x0eb4	/* GPIO 0 Control */
+
+#define CPCAP_REG_GPIO1		0x0ebc	/* GPIO 1 Control */
+
+#define CPCAP_REG_GPIO2		0x0ec4	/* GPIO 2 Control */
+
+#define CPCAP_REG_GPIO3		0x0ecc	/* GPIO 3 Control */
+
+#define CPCAP_REG_GPIO4		0x0ed4	/* GPIO 4 Control */
+
+#define CPCAP_REG_GPIO5		0x0edc	/* GPIO 5 Control */
+
+#define CPCAP_REG_GPIO6		0x0ee4	/* GPIO 6 Control */
+
+#define CPCAP_REG_MDLC		0x1000	/* Main Display Lighting Control */
+#define CPCAP_REG_KLC		0x1004	/* Keypad Lighting Control */
+#define CPCAP_REG_ADLC		0x1008	/* Aux Display Lighting Control */
+#define CPCAP_REG_REDC		0x100c	/* Red Triode Control */
+#define CPCAP_REG_GREENC	0x1010	/* Green Triode Control */
+#define CPCAP_REG_BLUEC		0x1014	/* Blue Triode Control */
+#define CPCAP_REG_CFC		0x1018	/* Camera Flash Control */
+#define CPCAP_REG_ABC		0x101c	/* Adaptive Boost Control */
+#define CPCAP_REG_BLEDC		0x1020	/* Bluetooth LED Control */
+#define CPCAP_REG_CLEDC		0x1024	/* Camera Privacy LED Control */
+
+#define CPCAP_REG_OW1C		0x1200	/* One Wire 1 Command */
+#define CPCAP_REG_OW1D		0x1204	/* One Wire 1 Data */
+#define CPCAP_REG_OW1I		0x1208	/* One Wire 1 Interrupt */
+#define CPCAP_REG_OW1IE		0x120c	/* One Wire 1 Interrupt Enable */
+
+#define CPCAP_REG_OW1		0x1214	/* One Wire 1 Control */
+
+#define CPCAP_REG_OW2C		0x1220	/* One Wire 2 Command */
+#define CPCAP_REG_OW2D		0x1224	/* One Wire 2 Data */
+#define CPCAP_REG_OW2I		0x1228	/* One Wire 2 Interrupt */
+#define CPCAP_REG_OW2IE		0x122c	/* One Wire 2 Interrupt Enable */
+
+#define CPCAP_REG_OW2		0x1234	/* One Wire 2 Control */
+
+#define CPCAP_REG_OW3C		0x1240	/* One Wire 3 Command */
+#define CPCAP_REG_OW3D		0x1244	/* One Wire 3 Data */
+#define CPCAP_REG_OW3I		0x1248	/* One Wire 3 Interrupt */
+#define CPCAP_REG_OW3IE		0x124c	/* One Wire 3 Interrupt Enable */
+
+#define CPCAP_REG_OW3		0x1254	/* One Wire 3 Control */
+#define CPCAP_REG_GCAIC		0x1258	/* GCAI Clock Control */
+#define CPCAP_REG_GCAIM		0x125c	/* GCAI GPIO Mode */
+#define CPCAP_REG_LGDIR		0x1260	/* LMR GCAI GPIO Direction */
+#define CPCAP_REG_LGPU		0x1264	/* LMR GCAI GPIO Pull-up */
+#define CPCAP_REG_LGPIN		0x1268	/* LMR GCAI GPIO Pin */
+#define CPCAP_REG_LGMASK	0x126c	/* LMR GCAI GPIO Mask */
+#define CPCAP_REG_LDEB		0x1270	/* LMR Debounce Settings */
+#define CPCAP_REG_LGDET		0x1274	/* LMR GCAI Detach Detect */
+#define CPCAP_REG_LMISC		0x1278	/* LMR Misc Bits */
+#define CPCAP_REG_LMACE		0x127c	/* LMR Mace IC Support */
+
+#define CPCAP_REG_TEST		0x7c00	/* Test */
+
+#define CPCAP_REG_ST_TEST1	0x7d08	/* ST Test1 */
+
+#define CPCAP_REG_ST_TEST2	0x7d18	/* ST Test2 */
-- 
2.10.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

* Re: [PATCH] arm64: dts: qcom: msm8996: Fixup smp2p node
From: Sarangdhar Joshi @ 2016-11-19  1:53 UTC (permalink / raw)
  To: Bjorn Andersson, Andy Gross
  Cc: linux-arm-msm, linux-soc, linux-kernel, linux-arm-kernel,
	devicetree
In-Reply-To: <1479499609-13336-1-git-send-email-bjorn.andersson@linaro.org>

On 11/18/2016 12:06 PM, Bjorn Andersson wrote:
> The SMEM state property name changes between the integration branch and
> mainline, update to use the correct one.
>
> Fixes: 2f45d9fcd531 ("arm64: dts: msm8996: Add SMP2P and APCS nodes")
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>

Thanks for the change and sorry for missing it earlier.

Reviewed-by: Sarangdhar Joshi <spjoshi@codeaurora.org>

^ permalink raw reply

* [PATCH v2 00/13] R-Car DU: Add support for LVDS mode selection
From: Laurent Pinchart @ 2016-11-19  3:28 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA, Tomi Valkeinen,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hello,

This patch series adds support for LVDS mode selection in the R-Car DU driver.

Compared to v1, the LVDS panel DT bindings (02/13) have been reworked to
document common panel properties in a common file (01/13), with Mitsubishi
panels bindings now properly documented (03/13). Compared to the existing DPI
panel bindings that are currently abused by the R-Car DU driver for LVDS
panel, these new bindings specify the LVDS more explicitly.

The bindings are based on the relevant standards I have been able to find, as
well as on existing LVDS-related code and DT bindings available in the
mainline kernel. Those include

- the LVDS formats MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
  MEDIA_BUS_FMT_RGB888_1X7X4_SPWG and MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA
  (Documentation/media/uapi/v4l/subdev-formats.rst)
- iMX display DT bindings available in
  (Documentation/devicetree/bindings/display/imx/ldb.txt)
- the drivers/gpu/drm/imx/ driver

In addition to the three modes specified in the LVDS panel DT bindings, the
Renesas R-Car DU also supports the following two modes.

Slot       0       1       2       3       4       5       6
       ________________                         _________________
Clock                  \_______________________/
         ______  ______  ______  ______  ______  ______  ______
DATA0  ><_CTL0_><__R7__><__R6__><__R5__><__R4__><__R3__><__R2__><
DATA1  ><_CTL1_><__G7__><__G6__><__G5__><__G4__><__G3__><__G2__><
DATA2  ><_CTL2_><__B7__><__B6__><__B5__><__B4__><__B3__><__B2__><
DATA3  ><_CTL3_><__B1__><__B0__><__G1__><__G0__><__R1__><__R0__><
         ______  ______  ______  ______  ______  ______  ______
DATA0  ><_CTL0_><__R5__><__R4__><__R3__><__R2__><__R1__><__R0__><
DATA1  ><_CTL1_><__G5__><__G4__><__G3__><__G2__><__G1__><__G0__><
DATA2  ><_CTL2_><__B5__><__B4__><__B3__><__B2__><__B1__><__B0__><
DATA3  ><_CTL3_><__B7__><__B6__><__G7__><__G6__><__R7__><__R6__><

and their mirrored version.

I haven't been able to find any standard defining those data mappings, nor any
panel using them. The control signals positions correspond to DC-balanced LVDS
(see figure 18 on page 19 of http://www.ti.com/lit/ds/symlink/ds90cf388.pdf),
but the R-Car DU doesn't support DC-balanced LVDS as far as I can tell, so
it's not a match. If anyone knows of other devices supporting these data
mappings or of standards defining them I would appreciate the information and
will update the bindings accordingly.

Patches 04/13 to 06/13 add a new DRM panel driver for LVDS panels compatible
with the DT bindings. Patches 07/13 to 09/13 update the device tree of the
R-Car H3 Salvator-X board to add panel backlight support, and patch 10/13
switches two Mitsubishi panels to use the new bindings. This series doesn't
strictly depend on them so they can be merged separately at a later point if
needed.

Patches 11/13 to 13/13 finally move the R-Car DU driver to use the DRM panel
API, and add support for LVDS mode selection.

Laurent Pinchart (13):
  devicetree/bindings: display: Document common panel properties
  devicetree/bindings: display: Add bindings for LVDS panels
  devicetree/bindings: display: Add bindings for two Mitsubishi panels
  drm: Add data mirror bus flag
  drm: panels: Constify device node argument to of_drm_find_panel()
  drm: panels: Add LVDS panel driver
  arm64: dts: r8a7795: Add PWM support
  arm64: dts: r8a7795: salvator-x: Add DU LVDS output endpoint
  arm64: dts: r8a7795: salvator-x: Add panel backlight support
  ARM: shmobile: dts: Switch to panel-lvds bindings for Mitsubishi
    panels
  drm: rcar-du: Switch to encoder .atomic_mode_set() helper function
  drm: rcar-du: Use the DRM panel API
  drm: rcar-du: Add support for LVDS mode selection

 .../display/panel/mitsubishi,aa104xd12.txt         |  47 ++++
 .../display/panel/mitsubishi,aa121td01.txt         |  47 ++++
 .../bindings/display/panel/panel-common.txt        |  91 +++++++
 .../bindings/display/panel/panel-lvds.txt          | 120 +++++++++
 arch/arm/boot/dts/r8a77xx-aa104xd12-panel.dtsi     |   3 +-
 arch/arm/boot/dts/r8a77xx-aa121td01-panel.dtsi     |   3 +-
 arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts |  26 ++
 arch/arm64/boot/dts/renesas/r8a7795.dtsi           |  63 +++++
 drivers/gpu/drm/drm_panel.c                        |   2 +-
 drivers/gpu/drm/panel/Kconfig                      |  10 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-lvds.c                 | 284 +++++++++++++++++++++
 drivers/gpu/drm/rcar-du/Kconfig                    |   1 +
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c          |  50 +++-
 drivers/gpu/drm/rcar-du/rcar_du_encoder.h          |   3 +
 drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c          |   8 +-
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c          |  68 ++---
 drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c          |  11 +-
 drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h          |  13 +
 include/drm/drm_connector.h                        |   2 +
 include/drm/drm_panel.h                            |   4 +-
 21 files changed, 798 insertions(+), 59 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/panel/mitsubishi,aa104xd12.txt
 create mode 100644 Documentation/devicetree/bindings/display/panel/mitsubishi,aa121td01.txt
 create mode 100644 Documentation/devicetree/bindings/display/panel/panel-common.txt
 create mode 100644 Documentation/devicetree/bindings/display/panel/panel-lvds.txt
 create mode 100644 drivers/gpu/drm/panel/panel-lvds.c

-- 
Regards,

Laurent Pinchart

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v2 01/13] devicetree/bindings: display: Document common panel properties
From: Laurent Pinchart @ 2016-11-19  3:28 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-renesas-soc, Tomi Valkeinen, devicetree
In-Reply-To: <1479526093-7014-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>

Document properties common to several display panels in a central
location that can be referenced by the panel device tree bindings.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 .../bindings/display/panel/panel-common.txt        | 91 ++++++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/panel-common.txt

diff --git a/Documentation/devicetree/bindings/display/panel/panel-common.txt b/Documentation/devicetree/bindings/display/panel/panel-common.txt
new file mode 100644
index 000000000000..ec52c472c845
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/panel-common.txt
@@ -0,0 +1,91 @@
+Common Properties for Display Panel
+===================================
+
+This document defines device tree properties common to several classes of
+display panels. It doesn't constitue a device tree binding specification by
+itself but is meant to be referenced by device tree bindings.
+
+When referenced from panel device tree bindings the properties defined in this
+document are defined as follows. The panel device tree bindings are
+responsible for defining whether each property is required or optional.
+
+
+Descriptive Properties
+----------------------
+
+- width-mm,
+- height-mm: The width-mm and height-mm specify the width and height of the
+  physical area where images are displayed. These properties are expressed in
+  millimeters and rounded to the closest unit.
+
+- label: The label property specifies a symbolic name for the panel as a
+  string suitable for use by humans. It typically contains a name inscribed on
+  the system (e.g. as an affixed label) or specified in the system's
+  documentation (e.g. in the user's manual).
+
+  If no such name exists, and unless the property is mandatory according to
+  device tree bindings, it shall rather be omitted than constructed of
+  non-descriptive information. For instance an LCD panel in a system that
+  contains a single panel shall not be labelled "LCD" if that name is not
+  inscribed on the system or used in a descriptive fashion in system
+  documentation.
+
+
+Display Timings
+---------------
+
+- panel-timing: Most display panels are restricted to a single resolution and
+  require specific display timings. The panel-timing subnode expresses those
+  timings as specified in the timing subnode section of the display timing
+  bindings defined in
+  Documentation/devicetree/bindings/display/display-timing.txt.
+
+
+Connectivity
+------------
+
+- ports: Panels receive video data through one or multiple connections. While
+  the nature of those connections is specific to the panel type, the
+  connectivity is expressed in a standard fashion using ports as specified in
+  the device graph bindings defined in
+  Documentation/devicetree/bindings/graph.txt.
+
+- ddc-i2c-bus: Some panels expose EDID information through an I2C-compatible
+  bus such as DDC2 or E-DDC. For such panels the ddc-i2c-bus contains a
+  phandle to the system I2C controller connected to that bus.
+
+
+Control I/Os
+------------
+
+Many display panels can be controlled through pins driven by GPIOs. The nature
+and timing of those control signals are device-specific and left for panel
+device tree bindings to specify. The following GPIO specifiers can however be
+used for panels that implement compatible control signals.
+
+- enable-gpios: Specifier for a GPIO connected to the panel enable control
+  signal. The enable signal is active high and enables operation of the panel.
+  This property can also be used for panels implementing an active low power
+  down signal, which is a negated version of the enable signal. Active low
+  enable signals (or active high power down signals) can be supported by
+  inverting the GPIO specifier polarity flag.
+
+  Note that the enable signal control panel operation only and must not be
+  confused with a backlight enable signal.
+
+- reset-gpios: Specifier for a GPIO coonnected to the panel reset control
+  signal. The reset signal is active low and resets the panel internal logic
+  while active. Active high reset signals can be supported by inverting the
+  GPIO specifier polarity flag.
+
+
+Backlight
+---------
+
+Most display panels include a backlight. Some of them also include a backlight
+controller exposed through a control bus such as I2C or DSI. Others expose
+backlight control through GPIO, PWM or other signals connected to an external
+backlight controller.
+
+- backlight: For panels whose backlight is controlled by an external backlight
+  controller, this property contains a phandle that references the controller.
-- 
Regards,

Laurent Pinchart

^ permalink raw reply related

* [PATCH v2 02/13] devicetree/bindings: display: Add bindings for LVDS panels
From: Laurent Pinchart @ 2016-11-19  3:28 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-renesas-soc, Tomi Valkeinen, devicetree
In-Reply-To: <1479526093-7014-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>

LVDS is a physical layer specification defined in ANSI/TIA/EIA-644-A.
Multiple incompatible data link layers have been used over time to
transmit image data to LVDS panels. This binding supports display panels
compatible with the JEIDA-59-1999, Open-LDI and VESA SWPG
specifications.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 .../bindings/display/panel/panel-lvds.txt          | 120 +++++++++++++++++++++
 1 file changed, 120 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/panel-lvds.txt

diff --git a/Documentation/devicetree/bindings/display/panel/panel-lvds.txt b/Documentation/devicetree/bindings/display/panel/panel-lvds.txt
new file mode 100644
index 000000000000..b938269f841e
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/panel-lvds.txt
@@ -0,0 +1,120 @@
+LVDS Display Panel
+==================
+
+LVDS is a physical layer specification defined in ANSI/TIA/EIA-644-A. Multiple
+incompatible data link layers have been used over time to transmit image data
+to LVDS panels. This bindings supports display panels compatible with the
+following specifications.
+
+[JEIDA] "Digital Interface Standards for Monitor", JEIDA-59-1999, February
+1999 (Version 1.0), Japan Electronic Industry Development Association (JEIDA)
+[LDI] "Open LVDS Display Interface", May 1999 (Version 0.95), National
+Semiconductor
+[VESA] "VESA Notebook Panel Standard", October 2007 (Version 1.0), Video
+Electronics Standards Association (VESA)
+
+Device compatible with those specifications have been marketed under the
+FPD-Link and FlatLink brands.
+
+
+Required properties:
+
+- compatible: Shall contain "panel-lvds" in addition to a mandatory
+  panel-specific compatible string defined in individual panel bindings. The
+  "panel-lvds" value shall never be used on its own.
+- width-mm: See panel-common.txt.
+- height-mm: See panel-common.txt.
+- data-mapping: The color signals mapping order, "jeida-18", "jeida-24"
+  or "vesa-24".
+
+Optional properties:
+
+- label: See panel-common.txt.
+- gpios: See panel-common.txt.
+- backlight: See panel-common.txt.
+- data-mirror: If set, reverse the bit order described in the data mappings
+  below on all data lanes, transmitting bits for slots 6 to 0 instead of
+  0 to 6.
+
+Required nodes:
+
+- panel-timing: See panel-common.txt.
+- ports: See panel-common.txt. These bindings require a single port subnode
+  corresponding to the panel LVDS input.
+
+
+LVDS data mappings are defined as follows.
+
+- "jeida-18" - 18-bit data mapping compatible with the [JEIDA], [LDI] and
+  [VESA] specifications. Data are transferred as follows on 3 LVDS lanes.
+
+Slot	    0       1       2       3       4       5       6
+	________________                         _________________
+Clock	                \_______________________/
+	  ______  ______  ______  ______  ______  ______  ______
+DATA0	><__G0__><__R5__><__R4__><__R3__><__R2__><__R1__><__R0__><
+DATA1	><__B1__><__B0__><__G5__><__G4__><__G3__><__G2__><__G1__><
+DATA2	><_CTL2_><_CTL1_><_CTL0_><__B5__><__B4__><__B3__><__B2__><
+
+- "jeida-24" - 24-bit data mapping compatible with the [DSIM] and [LDI]
+  specifications. Data are transferred as follows on 4 LVDS lanes.
+
+Slot	    0       1       2       3       4       5       6
+	________________                         _________________
+Clock	                \_______________________/
+	  ______  ______  ______  ______  ______  ______  ______
+DATA0	><__G2__><__R7__><__R6__><__R5__><__R4__><__R3__><__R2__><
+DATA1	><__B3__><__B2__><__G7__><__G6__><__G5__><__G4__><__G3__><
+DATA2	><_CTL2_><_CTL1_><_CTL0_><__B7__><__B6__><__B5__><__B4__><
+DATA3	><_CTL3_><__B1__><__B0__><__G1__><__G0__><__R1__><__R0__><
+
+- "vesa-24" - 24-bit data mapping compatible with the [VESA] specification.
+  Data are transferred as follows on 4 LVDS lanes.
+
+Slot	    0       1       2       3       4       5       6
+	________________                         _________________
+Clock	                \_______________________/
+	  ______  ______  ______  ______  ______  ______  ______
+DATA0	><__G0__><__R5__><__R4__><__R3__><__R2__><__R1__><__R0__><
+DATA1	><__B1__><__B0__><__G5__><__G4__><__G3__><__G2__><__G1__><
+DATA2	><_CTL2_><_CTL1_><_CTL0_><__B5__><__B4__><__B3__><__B2__><
+DATA3	><_CTL3_><__B7__><__B6__><__G7__><__G6__><__R7__><__R6__><
+
+Control signals are mapped as follows.
+
+CTL0: HSync
+CTL1: VSync
+CTL2: Data Enable
+CTL3: 0
+
+
+Example
+-------
+
+panel {
+	compatible = "mitsubishi,aa121td01", "panel-lvds";
+
+	width-mm = <261>;
+	height-mm = <163>;
+
+	data-mapping = "jeida-24";
+
+	panel-timing {
+		/* 1280x800 @60Hz */
+		clock-frequency = <71000000>;
+		hactive = <1280>;
+		vactive = <800>;
+		hsync-len = <70>;
+		hfront-porch = <20>;
+		hback-porch = <70>;
+		vsync-len = <5>;
+		vfront-porch = <3>;
+		vback-porch = <15>;
+	};
+
+	port {
+		panel_in: endpoint {
+			remote-endpoint = <&lvds_encoder>;
+		};
+	};
+};
-- 
Regards,

Laurent Pinchart

^ permalink raw reply related

* [PATCH v2 03/13] devicetree/bindings: display: Add bindings for two Mitsubishi panels
From: Laurent Pinchart @ 2016-11-19  3:28 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA, Tomi Valkeinen,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1479526093-7014-1-git-send-email-laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>

The AA104XD12 and AA121TD01 are LVDS display panels. Their bindings are
modelled on the the LVS panel bindings.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>
---
 .../display/panel/mitsubishi,aa104xd12.txt         | 47 ++++++++++++++++++++++
 .../display/panel/mitsubishi,aa121td01.txt         | 47 ++++++++++++++++++++++
 2 files changed, 94 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/mitsubishi,aa104xd12.txt
 create mode 100644 Documentation/devicetree/bindings/display/panel/mitsubishi,aa121td01.txt

diff --git a/Documentation/devicetree/bindings/display/panel/mitsubishi,aa104xd12.txt b/Documentation/devicetree/bindings/display/panel/mitsubishi,aa104xd12.txt
new file mode 100644
index 000000000000..ced0121aed7d
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/mitsubishi,aa104xd12.txt
@@ -0,0 +1,47 @@
+Mitsubishi AA204XD12 LVDS Display Panel
+=======================================
+
+The AA104XD12 is a 10.4" XGA TFT-LCD display panel.
+
+These DT bindings follow the LVDS panel bindings defined in panel-lvds.txt
+with the following device-specific properties.
+
+
+Required properties:
+
+- compatible: Shall contain "mitsubishi,aa121td01" and "panel-lvds", in that
+  order.
+- vcc-supply: Reference to the regulator powering the panel VCC pins.
+
+
+Example
+-------
+
+panel {
+	compatible = "mitsubishi,aa104xd12", "panel-lvds";
+	vcc-supply = <&vcc_3v3>;
+
+	width-mm = <210>;
+	height-mm = <158>;
+
+	data-mapping = "jeida-24";
+
+	panel-timing {
+		/* 1024x768 @65Hz */
+		clock-frequency = <65000000>;
+		hactive = <1024>;
+		vactive = <768>;
+		hsync-len = <136>;
+		hfront-porch = <20>;
+		hback-porch = <160>;
+		vfront-porch = <3>;
+		vback-porch = <29>;
+		vsync-len = <6>;
+	};
+
+	port {
+		panel_in: endpoint {
+			remote-endpoint = <&lvds_encoder>;
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/display/panel/mitsubishi,aa121td01.txt b/Documentation/devicetree/bindings/display/panel/mitsubishi,aa121td01.txt
new file mode 100644
index 000000000000..d6e1097504fe
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/mitsubishi,aa121td01.txt
@@ -0,0 +1,47 @@
+Mitsubishi AA121TD01 LVDS Display Panel
+=======================================
+
+The AA121TD01 is a 12.1" WXGA TFT-LCD display panel.
+
+These DT bindings follow the LVDS panel bindings defined in panel-lvds.txt
+with the following device-specific properties.
+
+
+Required properties:
+
+- compatible: Shall contain "mitsubishi,aa121td01" and "panel-lvds", in that
+  order.
+- vcc-supply: Reference to the regulator powering the panel VCC pins.
+
+
+Example
+-------
+
+panel {
+	compatible = "mitsubishi,aa121td01", "panel-lvds";
+	vcc-supply = <&vcc_3v3>;
+
+	width-mm = <261>;
+	height-mm = <163>;
+
+	data-mapping = "jeida-24";
+
+	panel-timing {
+		/* 1280x800 @60Hz */
+		clock-frequency = <71000000>;
+		hactive = <1280>;
+		vactive = <800>;
+		hsync-len = <70>;
+		hfront-porch = <20>;
+		hback-porch = <70>;
+		vsync-len = <5>;
+		vfront-porch = <3>;
+		vback-porch = <15>;
+	};
+
+	port {
+		panel_in: endpoint {
+			remote-endpoint = <&lvds_encoder>;
+		};
+	};
+};
-- 
Regards,

Laurent Pinchart

--
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 v2 0/9] R-Car DU: Use drm bridge API
From: Laurent Pinchart @ 2016-11-19  3:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-renesas-soc, devicetree

Hello,

This patch series replaces the custom external encoders support implementation
in the R-Car DU driver with code based on the DRM bridge API.

While the overall diffstat isn't impressive, the rcar-du-drm driver gets
notably thinner in the process:

	9 files changed, 57 insertions(+), 374 deletions(-)

This is offset by a reusable driver for LVDS encoders along with the
corresponding DT bindings (+ 204 lines).

Patches 1/9 and 2/9 add DT bindings for LVDS encoders with a corresponding
driver. It supports "dumb" LVDS encoders only, similarly to the dumb-vga-dac
driver. One notable difference, though, is that LVDS encoders can't be purely
passive, and thus require at least one power supply (and usually multiple of
them) and have a few control GPIOs (most notably to control reset, power down,
clock polarity and/or LVDS slew rate). However, on many systems those encoders
are integrated in such a way that the control pins are pulled up or down
appropriately and the power supplies are either always on or shared with other
display components that make them operate as if they were always on. For that
reason a common drivers for those systems is useful, with simple DT bindings
that don't try to cover any device-specific control pin or power supply.

To ensure backward compatibility most LVDS encoders should *not* use the
common simple "lvds-encoder" compatible string, but should instead define a
device-specific compatible string that can then be added to the lvds-encoder
driver (patch 4/9). This way, when the need to control pins or supplies will
arise, a new driver can be developed matching on the device-specific
compatible string, which will then be removed from the simple driver. Existing
systems will migrate transparently without requiring a change to their device
tree.

A similar reasoning applies to VGA DACs, leading to the addition of the
"adi,adv7123" compatible string to the dumb-vga-dac driver's OF match table in
patch 3/9.

Patch 5/9 adds a new type field to the drm_bridge structure to inform bridge
users of the bridge type. This is useful for display driver to create a DRM
encoder of the appropriate type without having to resort to heuristics.
Patches 6/9 and 7/9 update all bridge drivers to initialize the new field to
the appropriate value.

Patches 8/9 and 9/9 finally migrate the rcar-du-drm driver to the DRM bridge
API, removing the custom VGA DAC implementation in patch 8/9 and the table of
bridge compatible strings used to find the encoder type in patch 9/9.

Compared to v1,

- The patches have been rebased on top of the "[PATCH v2 00/13] R-Car DU: Add
  support for LVDS mode selection" series.
- The LVDS encoder DT bindings have been split from the LVDS encoder driver
  into a separate patch.
- The wording of the DRM bridge documentation new property has been updated.

Laurent Pinchart (9):
  drm: bridge: Add LVDS encoder DT bindings
  drm: bridge: Add LVDS encoder driver
  drm: bridge: vga-dac: Add adi,adv7123 compatible string
  drm: bridge: lvds-encoder: Add thine,thc63lvdm83d compatible string
  drm: Add encoder_type field to the drm_bridge structure
  drm: bridge: Set bridges' encoder type
  drm: Set on-chip bridges' encoder type
  drm: rcar-du: Replace manual bridge implementation with DRM bridge
  drm: rcar-du: Initialize encoder's type based on the bridge's type

 .../bindings/display/bridge/lvds-transmitter.txt   |  64 +++++++
 drivers/gpu/drm/bridge/Kconfig                     |   8 +
 drivers/gpu/drm/bridge/Makefile                    |   1 +
 drivers/gpu/drm/bridge/adv7511/adv7511_drv.c       |   1 +
 drivers/gpu/drm/bridge/analogix-anx78xx.c          |   1 +
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c |   1 +
 drivers/gpu/drm/bridge/dumb-vga-dac.c              |   2 +
 drivers/gpu/drm/bridge/dw-hdmi.c                   |   2 +
 drivers/gpu/drm/bridge/lvds-encoder.c              | 204 +++++++++++++++++++++
 drivers/gpu/drm/bridge/nxp-ptn3460.c               |   2 +
 drivers/gpu/drm/bridge/parade-ps8622.c             |   2 +
 drivers/gpu/drm/bridge/sii902x.c                   |   2 +
 drivers/gpu/drm/bridge/tc358767.c                  |   2 +
 drivers/gpu/drm/exynos/exynos_drm_mic.c            |   2 +
 drivers/gpu/drm/mediatek/mtk_hdmi.c                |   2 +
 drivers/gpu/drm/rcar-du/Kconfig                    |   6 -
 drivers/gpu/drm/rcar-du/Makefile                   |   5 +-
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c          | 103 ++++++-----
 drivers/gpu/drm/rcar-du/rcar_du_encoder.h          |   3 -
 drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c          | 137 --------------
 drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h          |  35 ----
 drivers/gpu/drm/rcar-du/rcar_du_kms.c              |  37 +---
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c           |  82 ---------
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.h           |  23 ---
 drivers/gpu/drm/sti/sti_dvo.c                      |   2 +
 include/drm/drm_bridge.h                           |   8 +
 26 files changed, 363 insertions(+), 374 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
 create mode 100644 drivers/gpu/drm/bridge/lvds-encoder.c
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.h

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* [PATCH v2 1/9] drm: bridge: Add LVDS encoder DT bindings
From: Laurent Pinchart @ 2016-11-19  3:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-renesas-soc, devicetree
In-Reply-To: <1479527302-7674-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>

The DT bindings support parallel to LVDS encoders that don't require any
configuration, similarly to the dumb VGA DAC DT bindings.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 .../bindings/display/bridge/lvds-transmitter.txt   | 64 ++++++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt

diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
new file mode 100644
index 000000000000..fd39ad34c383
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
@@ -0,0 +1,64 @@
+Parallel to LVDS Encoder
+------------------------
+
+This binding supports the parallel to LVDS encoders that don't require any
+configuration.
+
+LVDS is a physical layer specification defined in ANSI/TIA/EIA-644-A. Multiple
+incompatible data link layers have been used over time to transmit image data
+to LVDS panels. This binding targets devices compatible with the following
+specifications only.
+
+[JEIDA] "Digital Interface Standards for Monitor", JEIDA-59-1999, February
+1999 (Version 1.0), Japan Electronic Industry Development Association (JEIDA)
+[LDI] "Open LVDS Display Interface", May 1999 (Version 0.95), National
+Semiconductor
+[VESA] "VESA Notebook Panel Standard", October 2007 (Version 1.0), Video
+Electronics Standards Association (VESA)
+
+Those devices have been marketed under the FPD-Link and FlatLink brand names
+among others.
+
+
+Required properties:
+
+- compatible: Must be "lvds-encoder"
+
+Required nodes:
+
+This device has two video ports. Their connections are modeled using the OF
+graph bindings specified in Documentation/devicetree/bindings/graph.txt.
+
+- Video port 0 for parallel input
+- Video port 1 for LVDS output
+
+
+Example
+-------
+
+lvds-encoder {
+	compatible = "lvds-encoder";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+
+			lvds_enc_in: endpoint {
+				remote-endpoint = <&display_out_rgb>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			lvds_enc_out: endpoint {
+				remote-endpoint = <&lvds_panel_in>;
+			};
+		};
+	};
+};
-- 
Regards,

Laurent Pinchart

^ permalink raw reply related

* Re: [PATCH V8 2/6] thermal: bcm2835: add thermal driver for bcm2835 soc
From: Eduardo Valentin @ 2016-11-19  4:22 UTC (permalink / raw)
  To: kernel-TqfNSX0MhmxHKSADF0wUEw
  Cc: Zhang Rui, Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Lee Jones, Eric Anholt, Russell King, Florian Fainelli,
	Catalin Marinas, Will Deacon, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <7957B3CC-0E18-4B27-82EB-EF88B7695E28-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>

Hello Martin,

Thanks for your patience to take the time to explain to me how the
firmware/linux split is done in your platform. Still, one thing is not
clear to me.

On Fri, Nov 18, 2016 at 09:32:47AM +0100, kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org wrote:
> 

> The way that firmware works on the RPI is quite different from most others I guess.
> in principle you got 2 different CPUs on the bcm2835:
> * ARM, which runs the linux instance
> * VideoCore 4, which runs the firmware (loading from SD initially) and
>   then booting the ARM.
> 
> So this Firmware on VC4 is the one that I am talking about.
> Without the working firmware linux can not boot on arm.

Given that "without the working firmware linux can not boot on arm",

(...)

> As far as I understand the conversion is continuous (as soon as the HW is
> configured). This case is there primarily to handle the situation where
> we initialize the HW ourselves (see line 226 and below), and we immediately

and around line 226 we have the comment:
+       /*
+        * right now the FW does set up the HW-block, so we are not
+        * touching the configuration registers.
+        * But if the HW is not enabled, then set it up
+        * using "sane" values used by the firmware right now.
+        */


> want to read the ADC value before the first conversion is finished.
> 

then, does the firmware initializes the device or not?

What are the cases you would load this driver but still get an
uninitialized device? That looks like some bug workaround hidden
somewhere. Do system integrators/engineers need to be aware of this w/a?
Would the driver work right aways when the subsystem is loaded during
boot? How about module insertion?


Who has the ownership of this device?

> The above mentioned “configuration if not running” reflect the values that
> the FW is currently setting. We should not change those values as long as the
> Firmware is also reading the temperature on its own.

hmm.. that looks like racy to me. Again, How do you synchronize accesses to
this device? What if you configure the device and right after the
firmware updates the configs? How do you make sure the configs you are
writing here are the same used by the firmware? What if the firmware
version changes? What versions of the firmware does this driver support?

Would it make sense to simply always initialize the device? Do you have
a way to tell the firmware that it should not use the device?

Or, if you want to keep the device driver simply being a dummy reader,
would it make sense to simply avoid writing configurations to the
device, and simply retry to check if the firmware gets the device
initialized?

> 
> > 
> >> So do you need another version of the patchset that uses that new API?
> > 
> > I think the API usage is change that can be done together with
> > clarification for the above questions too: on hardware state,
> > firmware loading, maybe a master driver dependency, and the ADC
> > conversion sequence, which are not well clear to me on this driver. As long as
> > this is clarified and documented in the code (can be simple comments so
> > it is clear to whoever reads in the future), then I would be OK with
> > this driver. 
> 
> So how do you want this to get “documented” in the driver?
> The setup and Firmware is a generic feature of the SOC, so if we would put
> some clarifications in this driver, then we would need to put it in every
> bcm283X driver (which seems unreasonable).
> 

I think a simple comment explaining the firmware dependency and the
expected pre-conditions to get this driver working in a sane state would
do it.

A better device initialization would also be appreciated. Based on my
limited understanding of this platform, and your explanations, this
device seams to have a serious race condition with firmware while
accessing this device.

> Thanks,
> 	Martin
--
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] mtd/spi-nor: Add SPI memory controllers for Aspeed SoCs
From: Cédric Le Goater @ 2016-11-19  6:36 UTC (permalink / raw)
  To: Rob Herring
  Cc: Milton Miller, devicetree, linux-mtd, Joel Stanley, Brian Norris,
	David Woodhouse
In-Reply-To: <20161018163913.7z337iyc53atp4yd@rob-hp-laptop>

Hello Rob,

>> +Example:
>> +
>> +fmc: fmc@1e620000 {
>> +	compatible = "aspeed,ast2400-fmc";
>> +	reg = < 0x1e620000 0x94
>> +		0x20000000 0x02000000
>> +		0x22000000 0x02000000 >;
>> +	#address-cells = <1>;
>> +	#size-cells = <0>;
>> +	flash@0 {
>> +		reg = < 0 >;
>> +		compatible = "jedec,spi-nor" ;
>> +		label = "bmc";
> 
> label isn't really a defined property here. Belongs in jedec,spi-nor 
> binding if you want to add it.
> 

I am not sure I understand the suggestion.

Do you mean that we should rename the device node to something 
like "bmc@0" to identify a flash module on the system ?

This is a bit problematic for user space today as we need to sort 
out which flash modules are for the BMC or for the host, each having 
a primary and alternate. So the "label" was a practical way for 
identification.

Thanks,

C.

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply

* [PATCH] ARM: dts: msm8916: Add and enable wcnss node
From: Bjorn Andersson @ 2016-11-19  6:42 UTC (permalink / raw)
  To: Andy Gross, David Brown
  Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-soc-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

Add the wcnss remoteproc node the SMD edge and the wcnss ctrl, bluetooth
and wifi nodes specified and enable this on db410c.

Signed-off-by: Bjorn Andersson <bjorn.andersson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---

This still require the last wcn36xx and scm-interrupted patches to land, but as
those won't affect the dts I'm posting this anyway.

 arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi  |  4 ++
 arch/arm64/boot/dts/qcom/msm8916-pins.dtsi | 13 ++++++
 arch/arm64/boot/dts/qcom/msm8916.dtsi      | 73 +++++++++++++++++++++++++++++-
 3 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
index 08bd5ebafb4e..716d3ccbc309 100644
--- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
@@ -306,6 +306,10 @@
                                 };
                         };
                 };
+
+		wcnss@a21b000 {
+			status = "okay";
+		};
 	};
 
 	usb2513 {
diff --git a/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi b/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
index 10c83e11c272..4cb0b5834143 100644
--- a/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
@@ -720,4 +720,17 @@
 			};
 		};
 	};
+
+	wcnss_pin_a: wcnss-active {
+		pinmux {
+			pins = "gpio40", "gpio41", "gpio42", "gpio43", "gpio44";
+			function = "wcss_wlan";
+		};
+
+		pinconf {
+			pins = "gpio40", "gpio41", "gpio42", "gpio43", "gpio44";
+			drive-strength = <6>;
+			bias-pull-up;
+		};
+	};
 };
diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi
index 4221b7d2c0ce..2c692650ae43 100644
--- a/arch/arm64/boot/dts/qcom/msm8916.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi
@@ -14,6 +14,7 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/clock/qcom,gcc-msm8916.h>
 #include <dt-bindings/reset/qcom,gcc-msm8916.h>
+#include <dt-bindings/clock/qcom,rpmcc.h>
 
 / {
 	model = "Qualcomm Technologies, Inc. MSM8916";
@@ -82,7 +83,7 @@
 			no-map;
 		};
 
-		wcnss@89300000 {
+		wcnss_mem: wcnss@89300000 {
 			reg = <0x0 0x89300000 0x0 0x600000>;
 			no-map;
 		};
@@ -853,6 +854,76 @@
 				memory-region = <&mpss_mem>;
 			};
 		};
+
+		pronto: wcnss@a21b000 {
+			compatible = "qcom,pronto-v2-pil", "qcom,pronto";
+			reg = <0x0a204000 0x2000>, <0x0a202000 0x1000>, <0x0a21b000 0x3000>;
+			reg-names = "ccu", "dxe", "pmu";
+
+			memory-region = <&wcnss_mem>;
+
+			interrupts-extended = <&intc 0 149 IRQ_TYPE_EDGE_RISING>,
+					      <&wcnss_smp2p_in 0 IRQ_TYPE_EDGE_RISING>,
+					      <&wcnss_smp2p_in 1 IRQ_TYPE_EDGE_RISING>,
+					      <&wcnss_smp2p_in 2 IRQ_TYPE_EDGE_RISING>,
+					      <&wcnss_smp2p_in 3 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack";
+
+			vddmx-supply = <&pm8916_l3>;
+			vddpx-supply = <&pm8916_l7>;
+
+			qcom,state = <&wcnss_smp2p_out 0>;
+			qcom,state-names = "stop";
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&wcnss_pin_a>;
+
+			status = "disabled";
+
+			iris {
+				compatible = "qcom,wcn3620";
+
+				clocks = <&rpmcc RPM_SMD_RF_CLK2>;
+				clock-names = "xo";
+
+				vddxo-supply = <&pm8916_l7>;
+				vddrfa-supply = <&pm8916_s3>;
+				vddpa-supply = <&pm8916_l9>;
+				vdddig-supply = <&pm8916_l5>;
+			};
+
+			smd-edge {
+				interrupts = <0 142 1>;
+
+				qcom,ipc = <&apcs 8 17>;
+				qcom,smd-edge = <6>;
+				qcom,remote-pid = <4>;
+
+				label = "pronto";
+
+				wcnss {
+					compatible = "qcom,wcnss";
+					qcom,smd-channels = "WCNSS_CTRL";
+
+					qcom,mmio = <&pronto>;
+
+					bt {
+						compatible = "qcom,wcnss-bt";
+					};
+
+					wifi {
+						compatible = "qcom,wcnss-wlan";
+
+						interrupts = <0 145 IRQ_TYPE_LEVEL_HIGH>,
+							     <0 146 IRQ_TYPE_LEVEL_HIGH>;
+						interrupt-names = "tx", "rx";
+
+						qcom,smem-states = <&apps_smsm 10>, <&apps_smsm 9>;
+						qcom,smem-state-names = "tx-enable", "tx-rings-empty";
+					};
+				};
+			};
+		};
 	};
 
 	smd {
-- 
2.5.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

* Re: [PATCH v8] mwifiex: parse device tree node for PCIe
From: Kalle Valo @ 2016-11-19  6:56 UTC (permalink / raw)
  To: Rajat Jain
  Cc: Amitkumar Karwar, linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	Cathy Luo, Nishant Sarmukadam, Brian Norris, Dmitry Torokhov,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Xinming Hu
In-Reply-To: <CACK8Z6EAXTd07ZK3Y6Hy=7iGEY0fN91gO9Tz0NMh+shDAJoc+A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

Rajat Jain <rajatja-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org> writes:

> On Fri, Nov 18, 2016 at 8:21 AM, Kalle Valo <kvalo-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org> wrote:
>
>     > @@ -10,6 +10,8 @@ Required properties:
>     >    - compatible : should be one of the following:
>     >       * "marvell,sd8897"
>     >       * "marvell,sd8997"
>     > +     * "pci11ab,2b42"
>     > +     * "pci1b4b,2b42"
>
>     Are these correct? In v6 Rob said:
>
>     "I think I already said this, but you have the vendor and product IDs
>     reversed."
>
>     https://patchwork.kernel.org/patch/9390225/
>    
>
>
> I had responded to it:
>
>
> Hi Rob,
>
>> I think I already said this, but you have the vendor and product IDs
>> reversed.
>
> Actually Marvell has 2 vendor IDs assigned to it. In include/linux/pci_ids.h:
>
> #define PCI_VENDOR_ID_MARVELL           0x11ab
> #define PCI_VENDOR_ID_MARVELL_EXT       0x1b4b
>
> So in this case the compatible property describes a single product ID,
> with both possible vendor IDs.

Ok, I missed that.

BTW, please avoid using HTML. text/plain is very much preferred.

-- 
Kalle Valo

^ permalink raw reply

* Re: [v8] mwifiex: parse device tree node for PCIe
From: Kalle Valo @ 2016-11-19  7:11 UTC (permalink / raw)
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA, Cathy Luo,
	Nishant Sarmukadam, rajatja-hpIqsD4AKlfQT0dZR+AlfA,
	briannorris-hpIqsD4AKlfQT0dZR+AlfA,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Xinming Hu, Amitkumar Karwar
In-Reply-To: <1479472624-22340-1-git-send-email-akarwar-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>

Amitkumar Karwar <akarwar-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org> wrote:
> From: Xinming Hu <huxm-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
> 
> This patch derives device tree node from pcie bus layer framework.
> Device tree bindings file has been renamed(marvell-sd8xxx.txt ->
> marvell-8xxx.txt) to accommodate PCIe changes.
> 
> Signed-off-by: Xinming Hu <huxm-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
> Signed-off-by: Rajat Jain <rajatja-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
> Reviewed-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> Signed-off-by: Amitkumar Karwar <akarwar-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

Patch applied to wireless-drivers-next.git, thanks.

6b4480d109b0 mwifiex: parse device tree node for PCIe

-- 
https://patchwork.kernel.org/patch/9436393/

Documentation about submitting wireless patches and checking status
from patchwork:

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply

* Re: [PATCH v3 1/6] Documentation: dt-bindings: Document STM32 ADC DT bindings
From: Jonathan Cameron @ 2016-11-19 11:42 UTC (permalink / raw)
  To: Rob Herring, Fabrice Gasnier
  Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	mark.rutland-5wv7dgnIgG8, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
	knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg
In-Reply-To: <20161116151549.m6phclc6cqy4lj2y@rob-hp-laptop>

On 16/11/16 15:15, Rob Herring wrote:
> On Tue, Nov 15, 2016 at 04:30:56PM +0100, Fabrice Gasnier wrote:
>> This patch adds documentation of device tree bindings for the STM32 ADC.
>>
>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
>> ---
>>  .../devicetree/bindings/iio/adc/st,stm32-adc.txt   | 83 ++++++++++++++++++++++
>>  1 file changed, 83 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> 
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Applied to the togreg branch of iio.git.

Thanks,

Jonathan
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" 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: [RFC 2/6] drm/etnaviv: allow building etnaviv on omap devices
From: Joshua Clayton @ 2016-11-19 11:58 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Rob Herring, Robert Nelson, devicetree-u79uwXL29TY76Z2rM5mHXA,
	tony-4v6yS6AI5VpBDgjK7y7TUQ, Christian Gmeiner, Russell King,
	linux-omap-u79uwXL29TY76Z2rM5mHXA, Lucas Stach
In-Reply-To: <20161118163342.5t2tbsw54k2gmqe3@rob-hp-laptop>

On Friday, November 18, 2016 10:33:42 AM Rob Herring wrote:
> On Thu, Nov 17, 2016 at 08:44:32PM -0600, Robert Nelson wrote:
> > Signed-off-by: Robert Nelson <robertcnelson-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> > CC: Christian Gmeiner <christian.gmeiner-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> > CC: Russell King <rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
> > CC: Lucas Stach <l.stach-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> > ---
> >  drivers/gpu/drm/etnaviv/Kconfig | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig
> > index 2cde7a5..b776f41 100644
> > --- a/drivers/gpu/drm/etnaviv/Kconfig
> > +++ b/drivers/gpu/drm/etnaviv/Kconfig
> > @@ -2,7 +2,7 @@
> >  config DRM_ETNAVIV
> >  	tristate "ETNAVIV (DRM support for Vivante GPU IP cores)"
> >  	depends on DRM
> > -	depends on ARCH_MXC || ARCH_DOVE
> > +	depends on ARCH_MXC || ARCH_DOVE || ARCH_OMAP2PLUS
> 
> Why not just drop this line. Then it will get better build testing, too.
> 
> >  	select SHMEM
> >  	select TMPFS
> >  	select IOMMU_API
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
...building with ARCH=x86_64
            ^
drivers/gpu/drm/etnaviv/etnaviv_gpu.c: In function ‘etnaviv_gpu_init’:
drivers/gpu/drm/etnaviv/etnaviv_gpu.c:633:18: error: ‘PHYS_OFFSET’ undeclared (first use in this function)
   if (dma_mask < PHYS_OFFSET + SZ_2G)
                  ^
drivers/gpu/drm/etnaviv/etnaviv_gpu.c:633:18: note: each undeclared identifier is reported only once for each function it appears in
make[4]: *** [scripts/Makefile.build:290: drivers/gpu/drm/etnaviv/etnaviv_gpu.o] Error 1
make[3]: *** [scripts/Makefile.build:440: drivers/gpu/drm/etnaviv] Error 2
make[2]: *** [scripts/Makefile.build:440: drivers/gpu/drm] Error 2
make[1]: *** [scripts/Makefile.build:440: drivers/gpu] Error 2
make: *** [Makefile:968: drivers] Error 2


...looks like this snippit is the only (compile time) problem:
 
 622         /*
 623          * Set the GPU linear window to be at the end of the DMA window, where
 624          * the CMA area is likely to reside. This ensures that we are able to
 625          * map the command buffers while having the linear window overlap as
 626          * much RAM as possible, so we can optimize mappings for other buffers.
 627          *
 628          * For 3D cores only do this if MC2.0 is present, as with MC1.0 it leads
 629          * to different views of the memory on the individual engines.
 630          */
 631         if (!(gpu->identity.features & chipFeatures_PIPE_3D) ||
 632             (gpu->identity.minor_features0 & chipMinorFeatures0_MC20)) {
 633                 u32 dma_mask = (u32)dma_get_required_mask(gpu->dev);
 634                 if (dma_mask < PHYS_OFFSET + SZ_2G)
 635                         gpu->memory_base = PHYS_OFFSET;
 636                 else
 637                         gpu->memory_base = dma_mask - SZ_2G + 1;
 638         }

Joshua
--
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 v3 2/6] iio: adc: Add support for STM32 ADC core
From: Jonathan Cameron @ 2016-11-19 12:17 UTC (permalink / raw)
  To: Fabrice Gasnier, linux-iio, linux-arm-kernel, devicetree,
	linux-kernel
  Cc: linux, robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue,
	lars, knaack.h, pmeerw
In-Reply-To: <1479223861-21747-3-git-send-email-fabrice.gasnier@st.com>

On 15/11/16 15:30, Fabrice Gasnier wrote:
> Add core driver for STMicroelectronics STM32 ADC (Analog to Digital
> Converter). STM32 ADC can be composed of up to 3 ADCs with shared
> resources like clock prescaler, common interrupt line and analog
> reference voltage.
> This core driver basically manages shared resources.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>

There is nothing in here that demands selecting a fixed regulator.
I've also switched the select regulator over to depends on inline with
other drivers in IIO that have a hard dependency on regulators.
Other than that which showed up during build tests, looks good to me.
Shout if I've broken anything with this change.

Applied to the togreg branch of iio.git and pushed out as testing for
the autobuilders to play with it.

Thanks,

Jonathan
> ---
>  drivers/iio/adc/Kconfig          |  13 ++
>  drivers/iio/adc/Makefile         |   1 +
>  drivers/iio/adc/stm32-adc-core.c | 303 +++++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/stm32-adc-core.h |  52 +++++++
>  4 files changed, 369 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-adc-core.c
>  create mode 100644 drivers/iio/adc/stm32-adc-core.h
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 7edcf32..ff30239 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -419,6 +419,19 @@ config ROCKCHIP_SARADC
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called rockchip_saradc.
>  
> +config STM32_ADC_CORE
> +	tristate "STMicroelectronics STM32 adc core"
> +	depends on ARCH_STM32 || COMPILE_TEST
> +	depends on OF
> +	select REGULATOR
> +	select REGULATOR_FIXED_VOLTAGE
> +	help
> +	  Select this option to enable the core driver for STMicroelectronics
> +	  STM32 analog-to-digital converter (ADC).
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-adc-core.
> +
>  config STX104
>  	tristate "Apex Embedded Systems STX104 driver"
>  	depends on X86 && ISA_BUS_API
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04..a1e8f44 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -41,6 +41,7 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
>  obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
>  obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
> +obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>  obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
> new file mode 100644
> index 0000000..4214b0c
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-adc-core.c
> @@ -0,0 +1,303 @@
> +/*
> + * This file is part of STM32 ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
> + *
> + * Inspired from: fsl-imx25-tsadc
> + *
> + * License type: GPLv2
> + *
> + * 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.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdesc.h>
> +#include <linux/irqdomain.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +
> +#include "stm32-adc-core.h"
> +
> +/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
> +#define STM32F4_ADC_CSR			(STM32_ADCX_COMN_OFFSET + 0x00)
> +#define STM32F4_ADC_CCR			(STM32_ADCX_COMN_OFFSET + 0x04)
> +
> +/* STM32F4_ADC_CSR - bit fields */
> +#define STM32F4_EOC3			BIT(17)
> +#define STM32F4_EOC2			BIT(9)
> +#define STM32F4_EOC1			BIT(1)
> +
> +/* STM32F4_ADC_CCR - bit fields */
> +#define STM32F4_ADC_ADCPRE_SHIFT	16
> +#define STM32F4_ADC_ADCPRE_MASK		GENMASK(17, 16)
> +
> +/* STM32 F4 maximum analog clock rate (from datasheet) */
> +#define STM32F4_ADC_MAX_CLK_RATE	36000000
> +
> +/**
> + * struct stm32_adc_priv - stm32 ADC core private data
> + * @irq:		irq for ADC block
> + * @domain:		irq domain reference
> + * @aclk:		clock reference for the analog circuitry
> + * @vref:		regulator reference
> + * @common:		common data for all ADC instances
> + */
> +struct stm32_adc_priv {
> +	int				irq;
> +	struct irq_domain		*domain;
> +	struct clk			*aclk;
> +	struct regulator		*vref;
> +	struct stm32_adc_common		common;
> +};
> +
> +static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
> +{
> +	return container_of(com, struct stm32_adc_priv, common);
> +}
> +
> +/* STM32F4 ADC internal common clock prescaler division ratios */
> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
> +
> +/**
> + * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
> + * @priv: stm32 ADC core private data
> + * Select clock prescaler used for analog conversions, before using ADC.
> + */
> +static int stm32f4_adc_clk_sel(struct platform_device *pdev,
> +			       struct stm32_adc_priv *priv)
> +{
> +	unsigned long rate;
> +	u32 val;
> +	int i;
> +
> +	rate = clk_get_rate(priv->aclk);
> +	for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
> +		if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
> +			break;
> +	}
> +	if (i >= ARRAY_SIZE(stm32f4_pclk_div))
> +		return -EINVAL;
> +
> +	val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
> +	val &= ~STM32F4_ADC_ADCPRE_MASK;
> +	val |= i << STM32F4_ADC_ADCPRE_SHIFT;
> +	writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
> +
> +	dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
> +		rate / (stm32f4_pclk_div[i] * 1000));
> +
> +	return 0;
> +}
> +
> +/* ADC common interrupt for all instances */
> +static void stm32_adc_irq_handler(struct irq_desc *desc)
> +{
> +	struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	u32 status;
> +
> +	chained_irq_enter(chip, desc);
> +	status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
> +
> +	if (status & STM32F4_EOC1)
> +		generic_handle_irq(irq_find_mapping(priv->domain, 0));
> +
> +	if (status & STM32F4_EOC2)
> +		generic_handle_irq(irq_find_mapping(priv->domain, 1));
> +
> +	if (status & STM32F4_EOC3)
> +		generic_handle_irq(irq_find_mapping(priv->domain, 2));
> +
> +	chained_irq_exit(chip, desc);
> +};
> +
> +static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
> +				irq_hw_number_t hwirq)
> +{
> +	irq_set_chip_data(irq, d->host_data);
> +	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
> +
> +	return 0;
> +}
> +
> +static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
> +{
> +	irq_set_chip_and_handler(irq, NULL, NULL);
> +	irq_set_chip_data(irq, NULL);
> +}
> +
> +static const struct irq_domain_ops stm32_adc_domain_ops = {
> +	.map = stm32_adc_domain_map,
> +	.unmap  = stm32_adc_domain_unmap,
> +	.xlate = irq_domain_xlate_onecell,
> +};
> +
> +static int stm32_adc_irq_probe(struct platform_device *pdev,
> +			       struct stm32_adc_priv *priv)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +
> +	priv->irq = platform_get_irq(pdev, 0);
> +	if (priv->irq < 0) {
> +		dev_err(&pdev->dev, "failed to get irq\n");
> +		return priv->irq;
> +	}
> +
> +	priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
> +					     &stm32_adc_domain_ops,
> +					     priv);
> +	if (!priv->domain) {
> +		dev_err(&pdev->dev, "Failed to add irq domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
> +	irq_set_handler_data(priv->irq, priv);
> +
> +	return 0;
> +}
> +
> +static void stm32_adc_irq_remove(struct platform_device *pdev,
> +				 struct stm32_adc_priv *priv)
> +{
> +	int hwirq;
> +
> +	for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
> +		irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
> +	irq_domain_remove(priv->domain);
> +	irq_set_chained_handler(priv->irq, NULL);
> +}
> +
> +static int stm32_adc_probe(struct platform_device *pdev)
> +{
> +	struct stm32_adc_priv *priv;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct resource *res;
> +	int ret;
> +
> +	if (!pdev->dev.of_node)
> +		return -ENODEV;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->common.base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->common.base))
> +		return PTR_ERR(priv->common.base);
> +
> +	priv->vref = devm_regulator_get(&pdev->dev, "vref");
> +	if (IS_ERR(priv->vref)) {
> +		ret = PTR_ERR(priv->vref);
> +		dev_err(&pdev->dev, "vref get failed, %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = regulator_enable(priv->vref);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "vref enable failed\n");
> +		return ret;
> +	}
> +
> +	ret = regulator_get_voltage(priv->vref);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
> +		goto err_regulator_disable;
> +	}
> +	priv->common.vref_mv = ret / 1000;
> +	dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
> +
> +	priv->aclk = devm_clk_get(&pdev->dev, "adc");
> +	if (IS_ERR(priv->aclk)) {
> +		ret = PTR_ERR(priv->aclk);
> +		dev_err(&pdev->dev, "Can't get 'adc' clock\n");
> +		goto err_regulator_disable;
> +	}
> +
> +	ret = clk_prepare_enable(priv->aclk);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "adc clk enable failed\n");
> +		goto err_regulator_disable;
> +	}
> +
> +	ret = stm32f4_adc_clk_sel(pdev, priv);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "adc clk selection failed\n");
> +		goto err_clk_disable;
> +	}
> +
> +	ret = stm32_adc_irq_probe(pdev, priv);
> +	if (ret < 0)
> +		goto err_clk_disable;
> +
> +	platform_set_drvdata(pdev, &priv->common);
> +
> +	ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to populate DT children\n");
> +		goto err_irq_remove;
> +	}
> +
> +	return 0;
> +
> +err_irq_remove:
> +	stm32_adc_irq_remove(pdev, priv);
> +
> +err_clk_disable:
> +	clk_disable_unprepare(priv->aclk);
> +
> +err_regulator_disable:
> +	regulator_disable(priv->vref);
> +
> +	return ret;
> +}
> +
> +static int stm32_adc_remove(struct platform_device *pdev)
> +{
> +	struct stm32_adc_common *common = platform_get_drvdata(pdev);
> +	struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
> +
> +	of_platform_depopulate(&pdev->dev);
> +	stm32_adc_irq_remove(pdev, priv);
> +	clk_disable_unprepare(priv->aclk);
> +	regulator_disable(priv->vref);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id stm32_adc_of_match[] = {
> +	{ .compatible = "st,stm32f4-adc-core" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
> +
> +static struct platform_driver stm32_adc_driver = {
> +	.probe = stm32_adc_probe,
> +	.remove = stm32_adc_remove,
> +	.driver = {
> +		.name = "stm32-adc-core",
> +		.of_match_table = stm32_adc_of_match,
> +	},
> +};
> +module_platform_driver(stm32_adc_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC core driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:stm32-adc-core");
> diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
> new file mode 100644
> index 0000000..081fa5f
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-adc-core.h
> @@ -0,0 +1,52 @@
> +/*
> + * This file is part of STM32 ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
> + *
> + * License type: GPLv2
> + *
> + * 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.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __STM32_ADC_H
> +#define __STM32_ADC_H
> +
> +/*
> + * STM32 - ADC global register map
> + * ________________________________________________________
> + * | Offset |                 Register                    |
> + * --------------------------------------------------------
> + * | 0x000  |                Master ADC1                  |
> + * --------------------------------------------------------
> + * | 0x100  |                Slave ADC2                   |
> + * --------------------------------------------------------
> + * | 0x200  |                Slave ADC3                   |
> + * --------------------------------------------------------
> + * | 0x300  |         Master & Slave common regs          |
> + * --------------------------------------------------------
> + */
> +#define STM32_ADC_MAX_ADCS		3
> +#define STM32_ADCX_COMN_OFFSET		0x300
> +
> +/**
> + * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
> + * @base:		control registers base cpu addr
> + * @vref_mv:		vref voltage (mv)
> + */
> +struct stm32_adc_common {
> +	void __iomem			*base;
> +	int				vref_mv;
> +};
> +
> +#endif
> 

^ permalink raw reply

* Re: [PATCH v3 3/6] iio: adc: Add support for STM32 ADC
From: Jonathan Cameron @ 2016-11-19 12:19 UTC (permalink / raw)
  To: Fabrice Gasnier, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-I+IVW8TIWO2tmTQ+vhA3Yw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
	knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg
In-Reply-To: <1479223861-21747-4-git-send-email-fabrice.gasnier-qxv4g6HH51o@public.gmane.org>

On 15/11/16 15:30, Fabrice Gasnier wrote:
> This patch adds support for STMicroelectronics STM32 MCU's analog to
> digital converter.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it.

Very nice driver!

Thanks,

Jonathan
> ---
>  drivers/iio/adc/Kconfig     |  10 +
>  drivers/iio/adc/Makefile    |   1 +
>  drivers/iio/adc/stm32-adc.c | 518 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 529 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index ff30239..f93b990 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -432,6 +432,16 @@ config STM32_ADC_CORE
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc-core.
>  
> +config STM32_ADC
> +	tristate "STMicroelectronics STM32 adc"
> +	depends on STM32_ADC_CORE
> +	help
> +	  Say yes here to build support for STMicroelectronics stm32 Analog
> +	  to Digital Converter (ADC).
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-adc.
> +
>  config STX104
>  	tristate "Apex Embedded Systems STX104 driver"
>  	depends on X86 && ISA_BUS_API
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index a1e8f44..8e02a94 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -42,6 +42,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
>  obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
> +obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>  obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> new file mode 100644
> index 0000000..5715e79
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -0,0 +1,518 @@
> +/*
> + * This file is part of STM32 ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>.
> + *
> + * License type: GPLv2
> + *
> + * 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.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/iio/iio.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +
> +#include "stm32-adc-core.h"
> +
> +/* STM32F4 - Registers for each ADC instance */
> +#define STM32F4_ADC_SR			0x00
> +#define STM32F4_ADC_CR1			0x04
> +#define STM32F4_ADC_CR2			0x08
> +#define STM32F4_ADC_SMPR1		0x0C
> +#define STM32F4_ADC_SMPR2		0x10
> +#define STM32F4_ADC_HTR			0x24
> +#define STM32F4_ADC_LTR			0x28
> +#define STM32F4_ADC_SQR1		0x2C
> +#define STM32F4_ADC_SQR2		0x30
> +#define STM32F4_ADC_SQR3		0x34
> +#define STM32F4_ADC_JSQR		0x38
> +#define STM32F4_ADC_JDR1		0x3C
> +#define STM32F4_ADC_JDR2		0x40
> +#define STM32F4_ADC_JDR3		0x44
> +#define STM32F4_ADC_JDR4		0x48
> +#define STM32F4_ADC_DR			0x4C
> +
> +/* STM32F4_ADC_SR - bit fields */
> +#define STM32F4_STRT			BIT(4)
> +#define STM32F4_EOC			BIT(1)
> +
> +/* STM32F4_ADC_CR1 - bit fields */
> +#define STM32F4_SCAN			BIT(8)
> +#define STM32F4_EOCIE			BIT(5)
> +
> +/* STM32F4_ADC_CR2 - bit fields */
> +#define STM32F4_SWSTART			BIT(30)
> +#define STM32F4_EXTEN_MASK		GENMASK(29, 28)
> +#define STM32F4_EOCS			BIT(10)
> +#define STM32F4_ADON			BIT(0)
> +
> +/* STM32F4_ADC_SQR1 - bit fields */
> +#define STM32F4_L_SHIFT			20
> +#define STM32F4_L_MASK			GENMASK(23, 20)
> +
> +/* STM32F4_ADC_SQR3 - bit fields */
> +#define STM32F4_SQ1_SHIFT		0
> +#define STM32F4_SQ1_MASK		GENMASK(4, 0)
> +
> +#define STM32_ADC_TIMEOUT_US		100000
> +#define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
> +
> +/**
> + * struct stm32_adc - private data of each ADC IIO instance
> + * @common:		reference to ADC block common data
> + * @offset:		ADC instance register offset in ADC block
> + * @completion:		end of single conversion completion
> + * @buffer:		data buffer
> + * @clk:		clock for this adc instance
> + * @irq:		interrupt for this adc instance
> + * @lock:		spinlock
> + */
> +struct stm32_adc {
> +	struct stm32_adc_common	*common;
> +	u32			offset;
> +	struct completion	completion;
> +	u16			*buffer;
> +	struct clk		*clk;
> +	int			irq;
> +	spinlock_t		lock;		/* interrupt lock */
> +};
> +
> +/**
> + * struct stm32_adc_chan_spec - specification of stm32 adc channel
> + * @type:	IIO channel type
> + * @channel:	channel number (single ended)
> + * @name:	channel name (single ended)
> + */
> +struct stm32_adc_chan_spec {
> +	enum iio_chan_type	type;
> +	int			channel;
> +	const char		*name;
> +};
> +
> +/* Input definitions common for all STM32F4 instances */
> +static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
> +	{ IIO_VOLTAGE, 0, "in0" },
> +	{ IIO_VOLTAGE, 1, "in1" },
> +	{ IIO_VOLTAGE, 2, "in2" },
> +	{ IIO_VOLTAGE, 3, "in3" },
> +	{ IIO_VOLTAGE, 4, "in4" },
> +	{ IIO_VOLTAGE, 5, "in5" },
> +	{ IIO_VOLTAGE, 6, "in6" },
> +	{ IIO_VOLTAGE, 7, "in7" },
> +	{ IIO_VOLTAGE, 8, "in8" },
> +	{ IIO_VOLTAGE, 9, "in9" },
> +	{ IIO_VOLTAGE, 10, "in10" },
> +	{ IIO_VOLTAGE, 11, "in11" },
> +	{ IIO_VOLTAGE, 12, "in12" },
> +	{ IIO_VOLTAGE, 13, "in13" },
> +	{ IIO_VOLTAGE, 14, "in14" },
> +	{ IIO_VOLTAGE, 15, "in15" },
> +};
> +
> +/**
> + * STM32 ADC registers access routines
> + * @adc: stm32 adc instance
> + * @reg: reg offset in adc instance
> + *
> + * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
> + * for adc1, adc2 and adc3.
> + */
> +static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
> +{
> +	return readl_relaxed(adc->common->base + adc->offset + reg);
> +}
> +
> +static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
> +{
> +	return readw_relaxed(adc->common->base + adc->offset + reg);
> +}
> +
> +static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
> +{
> +	writel_relaxed(val, adc->common->base + adc->offset + reg);
> +}
> +
> +static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&adc->lock, flags);
> +	stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
> +	spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
> +static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&adc->lock, flags);
> +	stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
> +	spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
> +/**
> + * stm32_adc_conv_irq_enable() - Enable end of conversion interrupt
> + * @adc: stm32 adc instance
> + */
> +static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
> +{
> +	stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
> +};
> +
> +/**
> + * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
> + * @adc: stm32 adc instance
> + */
> +static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
> +{
> +	stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
> +}
> +
> +/**
> + * stm32_adc_start_conv() - Start conversions for regular channels.
> + * @adc: stm32 adc instance
> + */
> +static void stm32_adc_start_conv(struct stm32_adc *adc)
> +{
> +	stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
> +	stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
> +
> +	/* Wait for Power-up time (tSTAB from datasheet) */
> +	usleep_range(2, 3);
> +
> +	/* Software start ? (e.g. trigger detection disabled ?) */
> +	if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK))
> +		stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
> +}
> +
> +static void stm32_adc_stop_conv(struct stm32_adc *adc)
> +{
> +	stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
> +	stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
> +
> +	stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
> +	stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON);
> +}
> +
> +/**
> + * stm32_adc_single_conv() - Performs a single conversion
> + * @indio_dev: IIO device
> + * @chan: IIO channel
> + * @res: conversion result
> + *
> + * The function performs a single conversion on a given channel:
> + * - Program sequencer with one channel (e.g. in SQ1 with len = 1)
> + * - Use SW trigger
> + * - Start conversion, then wait for interrupt completion.
> + */
> +static int stm32_adc_single_conv(struct iio_dev *indio_dev,
> +				 const struct iio_chan_spec *chan,
> +				 int *res)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	long timeout;
> +	u32 val;
> +	u16 result;
> +	int ret;
> +
> +	reinit_completion(&adc->completion);
> +
> +	adc->buffer = &result;
> +
> +	/* Program chan number in regular sequence */
> +	val = stm32_adc_readl(adc, STM32F4_ADC_SQR3);
> +	val &= ~STM32F4_SQ1_MASK;
> +	val |= chan->channel << STM32F4_SQ1_SHIFT;
> +	stm32_adc_writel(adc, STM32F4_ADC_SQR3, val);
> +
> +	/* Set regular sequence len (0 for 1 conversion) */
> +	stm32_adc_clr_bits(adc, STM32F4_ADC_SQR1, STM32F4_L_MASK);
> +
> +	/* Trigger detection disabled (conversion can be launched in SW) */
> +	stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
> +
> +	stm32_adc_conv_irq_enable(adc);
> +
> +	stm32_adc_start_conv(adc);
> +
> +	timeout = wait_for_completion_interruptible_timeout(
> +					&adc->completion, STM32_ADC_TIMEOUT);
> +	if (timeout == 0) {
> +		ret = -ETIMEDOUT;
> +	} else if (timeout < 0) {
> +		ret = timeout;
> +	} else {
> +		*res = result;
> +		ret = IIO_VAL_INT;
> +	}
> +
> +	stm32_adc_stop_conv(adc);
> +
> +	stm32_adc_conv_irq_disable(adc);
> +
> +	return ret;
> +}
> +
> +static int stm32_adc_read_raw(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan,
> +			      int *val, int *val2, long mask)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = iio_device_claim_direct_mode(indio_dev);
> +		if (ret)
> +			return ret;
> +		if (chan->type == IIO_VOLTAGE)
> +			ret = stm32_adc_single_conv(indio_dev, chan, val);
> +		else
> +			ret = -EINVAL;
> +		iio_device_release_direct_mode(indio_dev);
> +		return ret;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = adc->common->vref_mv;
> +		*val2 = chan->scan_type.realbits;
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static irqreturn_t stm32_adc_isr(int irq, void *data)
> +{
> +	struct stm32_adc *adc = data;
> +	u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR);
> +
> +	if (status & STM32F4_EOC) {
> +		*adc->buffer = stm32_adc_readw(adc, STM32F4_ADC_DR);
> +		complete(&adc->completion);
> +		return IRQ_HANDLED;
> +	}
> +
> +	return IRQ_NONE;
> +}
> +
> +static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
> +			      const struct of_phandle_args *iiospec)
> +{
> +	int i;
> +
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		if (indio_dev->channels[i].channel == iiospec->args[0])
> +			return i;
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * stm32_adc_debugfs_reg_access - read or write register value
> + *
> + * To read a value from an ADC register:
> + *   echo [ADC reg offset] > direct_reg_access
> + *   cat direct_reg_access
> + *
> + * To write a value in a ADC register:
> + *   echo [ADC_reg_offset] [value] > direct_reg_access
> + */
> +static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
> +					unsigned reg, unsigned writeval,
> +					unsigned *readval)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +	if (!readval)
> +		stm32_adc_writel(adc, reg, writeval);
> +	else
> +		*readval = stm32_adc_readl(adc, reg);
> +
> +	return 0;
> +}
> +
> +static const struct iio_info stm32_adc_iio_info = {
> +	.read_raw = stm32_adc_read_raw,
> +	.debugfs_reg_access = stm32_adc_debugfs_reg_access,
> +	.of_xlate = stm32_adc_of_xlate,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
> +				    struct iio_chan_spec *chan,
> +				    const struct stm32_adc_chan_spec *channel,
> +				    int scan_index)
> +{
> +	chan->type = channel->type;
> +	chan->channel = channel->channel;
> +	chan->datasheet_name = channel->name;
> +	chan->scan_index = scan_index;
> +	chan->indexed = 1;
> +	chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> +	chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
> +	chan->scan_type.sign = 'u';
> +	chan->scan_type.realbits = 12;
> +	chan->scan_type.storagebits = 16;
> +}
> +
> +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
> +{
> +	struct device_node *node = indio_dev->dev.of_node;
> +	struct property *prop;
> +	const __be32 *cur;
> +	struct iio_chan_spec *channels;
> +	int scan_index = 0, num_channels;
> +	u32 val;
> +
> +	num_channels = of_property_count_u32_elems(node, "st,adc-channels");
> +	if (num_channels < 0 ||
> +	    num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
> +		return num_channels < 0 ? num_channels : -EINVAL;
> +	}
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_channels,
> +				sizeof(struct iio_chan_spec), GFP_KERNEL);
> +	if (!channels)
> +		return -ENOMEM;
> +
> +	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
> +		if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
> +			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
> +			return -EINVAL;
> +		}
> +		stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
> +					&stm32f4_adc123_channels[val],
> +					scan_index);
> +		scan_index++;
> +	}
> +
> +	indio_dev->num_channels = scan_index;
> +	indio_dev->channels = channels;
> +
> +	return 0;
> +}
> +
> +static int stm32_adc_probe(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev;
> +	struct stm32_adc *adc;
> +	int ret;
> +
> +	if (!pdev->dev.of_node)
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	adc = iio_priv(indio_dev);
> +	adc->common = dev_get_drvdata(pdev->dev.parent);
> +	spin_lock_init(&adc->lock);
> +	init_completion(&adc->completion);
> +
> +	indio_dev->name = dev_name(&pdev->dev);
> +	indio_dev->dev.parent = &pdev->dev;
> +	indio_dev->dev.of_node = pdev->dev.of_node;
> +	indio_dev->info = &stm32_adc_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	platform_set_drvdata(pdev, adc);
> +
> +	ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
> +	if (ret != 0) {
> +		dev_err(&pdev->dev, "missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	adc->irq = platform_get_irq(pdev, 0);
> +	if (adc->irq < 0) {
> +		dev_err(&pdev->dev, "failed to get irq\n");
> +		return adc->irq;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
> +			       0, pdev->name, adc);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to request IRQ\n");
> +		return ret;
> +	}
> +
> +	adc->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(adc->clk)) {
> +		dev_err(&pdev->dev, "Can't get clock\n");
> +		return PTR_ERR(adc->clk);
> +	}
> +
> +	ret = clk_prepare_enable(adc->clk);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "clk enable failed\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_adc_chan_of_init(indio_dev);
> +	if (ret < 0)
> +		goto err_clk_disable;
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "iio dev register failed\n");
> +		goto err_clk_disable;
> +	}
> +
> +	return 0;
> +
> +err_clk_disable:
> +	clk_disable_unprepare(adc->clk);
> +
> +	return ret;
> +}
> +
> +static int stm32_adc_remove(struct platform_device *pdev)
> +{
> +	struct stm32_adc *adc = platform_get_drvdata(pdev);
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +
> +	iio_device_unregister(indio_dev);
> +	clk_disable_unprepare(adc->clk);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id stm32_adc_of_match[] = {
> +	{ .compatible = "st,stm32f4-adc" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
> +
> +static struct platform_driver stm32_adc_driver = {
> +	.probe = stm32_adc_probe,
> +	.remove = stm32_adc_remove,
> +	.driver = {
> +		.name = "stm32-adc",
> +		.of_match_table = stm32_adc_of_match,
> +	},
> +};
> +module_platform_driver(stm32_adc_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:stm32-adc");
> 

^ permalink raw reply

* Re: [PATCH v3 4/6] ARM: configs: stm32: enable ADC driver
From: Jonathan Cameron @ 2016-11-19 12:21 UTC (permalink / raw)
  To: Fabrice Gasnier, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-I+IVW8TIWO2tmTQ+vhA3Yw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
	knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg
In-Reply-To: <1479223861-21747-5-git-send-email-fabrice.gasnier-qxv4g6HH51o@public.gmane.org>

On 15/11/16 15:30, Fabrice Gasnier wrote:
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
The driver is now on it's way in.  I'm assuming this and the two device tree patches
will go via the relevant route to arm-soc.

Thanks,

Jonathan
> ---
>  arch/arm/configs/stm32_defconfig | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
> index 1e5ec2a..5d241e0 100644
> --- a/arch/arm/configs/stm32_defconfig
> +++ b/arch/arm/configs/stm32_defconfig
> @@ -57,6 +57,9 @@ CONFIG_LEDS_TRIGGERS=y
>  CONFIG_LEDS_TRIGGER_HEARTBEAT=y
>  CONFIG_DMADEVICES=y
>  CONFIG_STM32_DMA=y
> +CONFIG_IIO=y
> +CONFIG_STM32_ADC_CORE=y
> +CONFIG_STM32_ADC=y
>  # CONFIG_FILE_LOCKING is not set
>  # CONFIG_DNOTIFY is not set
>  # CONFIG_INOTIFY_USER is not set
> 

^ permalink raw reply

* Re: [PATCH 1/2] staging: iio: ad7606: replace range/range_available with corresponding scale
From: Jonathan Cameron @ 2016-11-19 12:32 UTC (permalink / raw)
  To: Linus Walleij, Lars-Peter Clausen
  Cc: Eva Rachel Retuya,
	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Michael Hennerich, Hartmut Knaack, Peter Meerwald, Greg KH,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
	Mark Rutland
In-Reply-To: <CACRpkdbWqxUWc+40AYSY7XX2ZFYSZEBcU3FusOrunG+-GCZGwQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On 14/11/16 23:12, Linus Walleij wrote:
> On Mon, Nov 14, 2016 at 7:53 PM, Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org> wrote:
> 
>> It's about figuring out the setting of a "GPIO" that can't be changed from
>> software.
>>
>> Devices sometimes, instead of a configuration bus like I2C or SPI, use
>> simple input pins, that can either be set to high or low, to allow software
>> the state of the device. The GPIO API is typically used to configure these pins.
>>
>> This works fine as long as the pin is connected to a GPIO. But sometimes the
>> system designer decides that a settings does not need to be configurable, in
>> this case the pin will be tied to logic low or high directly on the PCB
>> without any GPIO controller being involved.
>>
>> Sometimes a driver wants to know how the pin is wired up so it can report to
>> userspace this part runs in the following mode and the mode can't be
>> changed. In a sense it is like a reverse GPIO hog.
>>
>> Considering that this is a common usecase the question was how this can be
>> implemented in a driver independent way to avoid code duplication and
>> slightly different variations of what is effectively the same DT/ACPI binding.
>>
>> E.g. lets say for a configurable pin you use
>>
>>         range-gpio = <&gpio ...>;
>>
>> and for a static pin
>>
>>         range-gpio-fixed = <1>;
>>
>> Or something similar.
> 
> Aha I understand.
> 
> Usually I feel we need not shoehorn stuff into GPIO because it is convenient,
> it might be best to leave the GPIO optional and if it is not there, look for
> a custom attribute that represents the "hogging" to 0/1. I think trying
> to extend GPIO bindings to cover it is overgeneralization, instead go
> for a local binding for this kind of devices.
> 
> But mainly it is a question to the DT bindings maintainers.
That's a reasonable approach, but I'd certainly like to see a generic binding
to describe it.  It's a pretty common situation.

Seems more likely we'll get a device tree maintainer response if we cc them ;)

So Mark, Rob and thoughts on this?

Thanks,

Jonathan
> 
> Yours,
> Linus Walleij
> 

--
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: [RFC PATCH v2 1/7] dt-bindings: document devicetree bindings for mux-gpio
From: Jonathan Cameron @ 2016-11-19 15:21 UTC (permalink / raw)
  To: Peter Rosin, Rob Herring
  Cc: linux-kernel, Wolfram Sang, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Arnd Bergmann,
	Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio
In-Reply-To: <a6a6610e-6827-a069-36e6-ca30a7110836@axentia.se>

On 18/11/16 16:59, Peter Rosin wrote:
> On 2016-11-18 16:35, Rob Herring wrote:
>> On Thu, Nov 17, 2016 at 10:48:03PM +0100, Peter Rosin wrote:
>>> ---
>>>  .../devicetree/bindings/misc/mux-gpio.txt          | 79 ++++++++++++++++++++++
>>>  1 file changed, 79 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/misc/mux-gpio.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/misc/mux-gpio.txt b/Documentation/devicetree/bindings/misc/mux-gpio.txt
>>> new file mode 100644
>>> index 000000000000..73699a37824f
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/misc/mux-gpio.txt
>>> @@ -0,0 +1,79 @@
>>> +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.
>>
>> I think this makes sense in your case, but I think it is too complicated 
>> for a non-shared case. Perhaps mux-gpios should be used directly (i.e. 
>> in the adc-mux node) and control-muxes only used for the shared case.
>>
>> Part of me feels like you are working around in DT the GPIO subsystem 
>> limitation that it can't share GPIO lines. Either this could be fixed 
>> in some way in the GPIO subsystem, or the mux subsys could deal with it. 
>> You just have to look up if you already have a mux registered for the 
>> same GPIOs. Of course, that may make the mux subsys pretty much GPIO 
>> only, but I'm having a hard time thinking how you would have shared 
>> muxes that are not GPIO controlled. Any other control would be 
>> integrated into the mux itself.
> 
> But if someone wants to mux an adc line with a mux that is some kind of
> integrated i2c device, you'd have to add extra code to the iio muxer
> driver to handle that case. Or fork it. Or build something like the
> i2c muxer infrastructure and separate out the mux control in small
> drivers and handle the generic iio muxing centrally. But then someone
> else uses that i2c device to instead mux an i2c bus, and you'd end up
> with code duplication when that same muxer control code is added under
> drivers/i2c/muxes.
> 
> With the proposed solution, this is unified.
> 
> I'd just hate to see drivers for muxers added under drivers/i2c/muxes
> that do little more that control a mux that happens to be used to mux
> an i2c bus, but are generic muxers that could equally well mux something
> else. Even if the control is integrated into the mux, what the mux is
> actually used for should perhaps not determine where its driver should
> live.
> 
> Anyway, I don't know what to make with your suggestion, I just don't
> see the path forward (not enough experience with the kernel and/or gpio
> code). And it would be a limited solution (GPIO only,a s you say) so it
> doesn't feel right.
Also worth pointing out here the possibility of multi pole muxes...
Relays are ultimately muxes as well (be it slow ones ;)

A quick google fed me:
TI SN74LS153 for example.  This one is digital only though...

Analog option (in both senses) is:
http://www.analog.com/media/en/technical-documentation/data-sheets/ADG888.pdf

So these 'look' the same as two single muxes wired to the same GPIOs.

> 
> Is there perhaps some way to keep the complicated shared case work as
> is (or equivalently, the exact details are not important), and also
> provide a simpler in-node thingy to glue a mux control to a consumer
> w/o pointing to it with a phandle, but still have the same mux driver
> handle both cases? No, I'm not a devicetree guru, so I don't see a
> solution for that either, but maybe someone else does?
> 
> Perhaps the consumer could look for the mux control in first the
> phandle, as in my proposal. If not found, it could also look for
> a mux provider bound to child node.
> 
> 	adc-mux {
> 		compatible = "iio-mux";
> 		io-channels = <&adc 0>;
> 		io-channel-names = "parent";
> 
> 		mux-control {
> 			compatible = "mux-gpio";
> 			mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
> 				    <&pioA 1 GPIO_ACTIVE_HIGH>;
> 		};
> 
> 		#address-cells = <1>;
> 		#size-cells = <0>;
> 
> 		sync-1@0 {
> 			reg = <0>;
> 		};
> 		/* ... */
> 	};
> 
> Or perhaps look in a parent node:
> 
> 	mux-control {
> 		compatible = "mux-gpio";
> 		mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
> 			    <&pioA 1 GPIO_ACTIVE_HIGH>;
> 
> 		adc-mux {
> 			compatible = "iio-mux";
> 			io-channels = <&adc 0>;
> 			io-channel-names = "parent";
> 
> 			#address-cells = <1>;
> 			#size-cells = <0>;
> 
> 			sync-1@0 {
> 				reg = <0>;
> 			};
> 			/* ... */
> 		};
> 	};
> 
> With the last suggestion, you could have multiple children of the
> mux-control node for the complicated case where it controls more
> than one mux. Not too bad? Hmm, what does the driver for the
> mux-control node have to do to have drivers tied to its children?
> 
> Maybe this last layout should be the only thing supported? Good
> enough for me anyway...
> 
> Cheers,
> Peter
> 
> PS. I will take care of the other comments for the next round.
> 

^ permalink raw reply

* Re: [RFC PATCH v2 2/7] misc: minimal mux subsystem and gpio-based mux controller
From: Jonathan Cameron @ 2016-11-19 15:34 UTC (permalink / raw)
  To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Arnd Bergmann,
	Greg Kroah-Hartman, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1479419289-17553-3-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

On 17/11/16 21:48, Peter Rosin wrote:
> When both the iio subsystem and the i2c subsystem wants to update
> the same mux, there needs to be some coordination. Invent a new
> minimal "mux" subsystem that handles this.
I'd probably put something more general in the description. Lots of things
may need the same infrastructure.  This is just an example.

Few bits inline.

Also, I suspect you will fairly rapidly have a need for a strobe signal
as well.  A lot of mux chips that are more than 2 way seem to have them to
allow multiple chips to be synchronized.
> 
> Add a single backend driver for this new subsystem that can
> control gpio based multiplexers.
> ---
>  drivers/misc/Kconfig    |   6 +
>  drivers/misc/Makefile   |   2 +
>  drivers/misc/mux-core.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/misc/mux-gpio.c | 115 +++++++++++++++++++
>  include/linux/mux.h     |  53 +++++++++
>  5 files changed, 475 insertions(+)
>  create mode 100644 drivers/misc/mux-core.c
>  create mode 100644 drivers/misc/mux-gpio.c
>  create mode 100644 include/linux/mux.h
> 
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index 64971baf11fa..9e119bb78d82 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -766,6 +766,12 @@ config PANEL_BOOT_MESSAGE
>  	  An empty message will only clear the display at driver init time. Any other
>  	  printf()-formatted message is valid with newline and escape codes.
>  
> +config MUX_GPIO
> +	tristate "GPIO-controlled MUX controller"
> +	depends on OF
> +	help
> +	  GPIO-controlled MUX controller
> +
>  source "drivers/misc/c2port/Kconfig"
>  source "drivers/misc/eeprom/Kconfig"
>  source "drivers/misc/cb710/Kconfig"
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index 31983366090a..92b547bcbac1 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -53,6 +53,8 @@ obj-$(CONFIG_ECHO)		+= echo/
>  obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
>  obj-$(CONFIG_CXL_BASE)		+= cxl/
>  obj-$(CONFIG_PANEL)             += panel.o
> +obj-$(CONFIG_MUX_GPIO)          += mux-core.o
> +obj-$(CONFIG_MUX_GPIO)          += mux-gpio.o
>  
>  lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
>  lkdtm-$(CONFIG_LKDTM)		+= lkdtm_bugs.o
> diff --git a/drivers/misc/mux-core.c b/drivers/misc/mux-core.c
> new file mode 100644
> index 000000000000..7a8bf003a92c
> --- /dev/null
> +++ b/drivers/misc/mux-core.c
> @@ -0,0 +1,299 @@
> +/*
> + * Multiplexer subsystem
> + *
> + * Copyright (C) 2016 Axentia Technologies AB
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#define pr_fmt(fmt) "mux-core: " fmt
> +
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/idr.h>
> +#include <linux/module.h>
> +#include <linux/mux.h>
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +
> +static struct bus_type mux_bus_type = {
> +	.name = "mux",
> +};
> +
> +static int __init mux_init(void)
> +{
> +	return bus_register(&mux_bus_type);
> +}
> +
> +static void __exit mux_exit(void)
> +{
> +	bus_unregister(&mux_bus_type);
> +}
> +
> +static DEFINE_IDA(mux_ida);
> +
> +static void mux_control_release(struct device *dev)
> +{
> +	struct mux_control *mux = to_mux_control(dev);
> +
> +	ida_simple_remove(&mux_ida, mux->id);
> +	kfree(mux);
> +}
> +
> +static struct device_type mux_control_type = {
> +	.name = "mux-control",
> +	.release = mux_control_release,
> +};
> +
> +/*
> + * Allocate a mux-control, plus an extra memory area for private use
> + * by the caller.
> + */
> +struct mux_control *mux_control_alloc(size_t sizeof_priv)
> +{
> +	struct mux_control *mux;
> +
Worth planning ahead for spi controlled muxes and others that need their
structures to be carefully aligned to avoid dma cacheline fun?
Easy enough to add later I guess.
> +	mux = kzalloc(sizeof(*mux) + sizeof_priv, GFP_KERNEL);
> +	if (!mux)
> +		return NULL;
> +
> +	mux->dev.bus = &mux_bus_type;
> +	mux->dev.type = &mux_control_type;
> +	device_initialize(&mux->dev);
> +	dev_set_drvdata(&mux->dev, mux);
> +
> +	init_rwsem(&mux->lock);
> +	mux->priv = mux + 1;
Needed?  Or just do it with a bit of pointer math where the access is needed?
> +
> +	mux->id = ida_simple_get(&mux_ida, 0, 0, GFP_KERNEL);
> +	if (mux->id < 0) {
> +		pr_err("mux-controlX failed to get device id\n");
> +		kfree(mux);
> +		return NULL;
> +	}
> +	dev_set_name(&mux->dev, "mux:control%d", mux->id);
> +
> +	mux->cached_state = -1;
> +	mux->idle_state = -1;
> +
> +	return mux;
> +}
> +EXPORT_SYMBOL_GPL(mux_control_alloc);
> +
> +/*
> + * Register the mux-control, thus readying it for use.
Either single line comment style - or perhaps kernel doc the lot...
> + */
> +int mux_control_register(struct mux_control *mux)
> +{
> +	/* If the calling driver did not initialize of_node, do it here */
> +	if (!mux->dev.of_node && mux->dev.parent)
> +		mux->dev.of_node = mux->dev.parent->of_node;
> +
> +	return device_add(&mux->dev);
> +}
> +EXPORT_SYMBOL_GPL(mux_control_register);
> +
> +/*
> + * Take the mux-control off-line.
> + */
> +void mux_control_unregister(struct mux_control *mux)
> +{
> +	device_del(&mux->dev);
> +}
> +EXPORT_SYMBOL_GPL(mux_control_unregister);
> +
> +/*
> + * Put away the mux-control for good.
> + */
> +void mux_control_put(struct mux_control *mux)
> +{
> +	if (!mux)
> +		return;
> +	put_device(&mux->dev);
> +}
> +EXPORT_SYMBOL_GPL(mux_control_put);
> +
> +static int mux_control_set(struct mux_control *mux, int state)
> +{
> +	int ret = mux->ops->set(mux, state);
> +
> +	mux->cached_state = ret < 0 ? -1 : state;
> +
> +	return ret;
> +}
> +
> +/*
> + * Select the given multiplexer channel. Call mux_control_deselect()
> + * when the operation is complete on the multiplexer channel, and the
> + * multiplexer is free for others to use.
> + */
> +int mux_control_select(struct mux_control *mux, int state)
> +{
> +	int ret;
> +
> +	if (down_read_trylock(&mux->lock)) {
> +		if (mux->cached_state == state)
> +			return 0;
> +
> +		/* Sigh, the mux needs updating... */
> +		up_read(&mux->lock);
> +	}
> +
> +	/* ...or it's just contended. */
> +	down_write(&mux->lock);
> +
> +	if (mux->cached_state == state) {
> +		/*
> +		 * Hmmm, someone else changed the mux to my liking.
> +		 * That makes me wonder how long I waited for nothing...
> +		 */
> +		downgrade_write(&mux->lock);
> +		return 0;
> +	}
> +
> +	ret = mux_control_set(mux, state);
> +	if (ret < 0) {
> +		if (mux->idle_state != -1)
> +			mux_control_set(mux, mux->idle_state);
> +
> +		up_write(&mux->lock);
> +		return ret;
> +	}
> +
> +	downgrade_write(&mux->lock);
> +
> +	return 1;
> +}
> +EXPORT_SYMBOL_GPL(mux_control_select);
> +
> +/*
> + * Deselect the previously selected multiplexer channel.
> + */
> +int mux_control_deselect(struct mux_control *mux)
> +{
> +	int ret = 0;
> +
> +	if (mux->idle_state != -1 && mux->cached_state != mux->idle_state)
> +		ret = mux_control_set(mux, mux->idle_state);
> +
> +	up_read(&mux->lock);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(mux_control_deselect);
> +
> +static int of_dev_node_match(struct device *dev, void *data)
> +{
> +	return dev->of_node == data;
> +}
> +
> +static struct mux_control *of_find_mux_by_node(struct device_node *np)
> +{
> +	struct device *dev;
> +
> +	dev = bus_find_device(&mux_bus_type, NULL, np, of_dev_node_match);
> +
> +	return dev ? to_mux_control(dev) : NULL;
> +}
> +
> +static struct mux_control *of_mux_control_get(struct device_node *np, int index)
> +{
> +	struct device_node *mux_np;
> +	struct mux_control *mux;
> +
> +	mux_np = of_parse_phandle(np, "control-muxes", index);
> +	if (!mux_np)
> +		return NULL;
> +
> +	mux = of_find_mux_by_node(mux_np);
> +	of_node_put(mux_np);
> +
> +	return mux;
> +}
> +
> +/*
> + * Get a named mux.
> + */
> +struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
> +{
> +	struct device_node *np = dev->of_node;
> +	struct mux_control *mux;
> +	int index;
> +
> +	index = of_property_match_string(np, "control-mux-names", mux_name);
> +	if (index < 0) {
> +		dev_err(dev, "failed to get control-mux %s:%s(%i)\n",
> +			np->full_name, mux_name ?: "", index);
> +		return ERR_PTR(index);
> +	}
> +
> +	mux = of_mux_control_get(np, index);
> +	if (!mux)
> +		return ERR_PTR(-EPROBE_DEFER);
> +
> +	return mux;
> +}
> +EXPORT_SYMBOL_GPL(mux_control_get);
> +
> +static void devm_mux_control_free(struct device *dev, void *res)
> +{
> +	struct mux_control *mux = *(struct mux_control **)res;
> +
> +	mux_control_put(mux);
> +}
> +
> +/*
> + * Get a named mux, with resource management.
> + */
Guess it might be elsewhere in patch set but remember to add this to
the global list of devm interfaces (in Documentation somewhere.. IIRC)
> +struct mux_control *devm_mux_control_get(struct device *dev,
> +					 const char *mux_name)
> +{
> +	struct mux_control **ptr, *mux;
> +
> +	ptr = devres_alloc(devm_mux_control_free, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	mux = mux_control_get(dev, mux_name);
> +	if (IS_ERR(mux)) {
> +		devres_free(ptr);
> +		return mux;
> +	}
> +
> +	*ptr = mux;
> +	devres_add(dev, ptr);
> +
> +	return mux;
> +}
> +EXPORT_SYMBOL_GPL(devm_mux_control_get);
> +
> +static int devm_mux_control_match(struct device *dev, void *res, void *data)
> +{
> +	struct mux_control **r = res;
> +
> +	if (!r || !*r) {
> +		WARN_ON(!r || !*r);
> +		return 0;
> +	}
> +
> +	return *r == data;
> +}
> +
> +/*
> + * Resource-managed version mux_control_put.
> + */
> +void devm_mux_control_put(struct device *dev, struct mux_control *mux)
> +{
> +	WARN_ON(devres_release(dev, devm_mux_control_free,
> +			       devm_mux_control_match, mux));
> +}
> +EXPORT_SYMBOL_GPL(devm_mux_control_put);
> +
> +subsys_initcall(mux_init);
> +module_exit(mux_exit);
> +
> +MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org");
> +MODULE_DESCRIPTION("MUX subsystem");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/misc/mux-gpio.c b/drivers/misc/mux-gpio.c
> new file mode 100644
> index 000000000000..2ddd7fb24078
> --- /dev/null
> +++ b/drivers/misc/mux-gpio.c
> @@ -0,0 +1,115 @@
> +/*
> + * GPIO-controlled multiplexer driver
> + *
> + * Copyright (C) 2016 Axentia Technologies AB
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/mux.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +struct mux_gpio {
> +	struct gpio_descs *gpios;
> +};
> +
> +static int mux_gpio_set(struct mux_control *mux, int val)
> +{
> +	struct mux_gpio *mux_gpio = mux->priv;
> +	int i;
> +
> +	for (i = 0; i < mux_gpio->gpios->ndescs; i++)
> +		gpiod_set_value_cansleep(mux_gpio->gpios->desc[i],
> +					 val & (1 << i));
> +
> +	return 0;
> +}
> +
> +static const struct mux_control_ops mux_gpio_ops = {
> +	.set = mux_gpio_set,
> +};
> +
> +static const struct of_device_id mux_gpio_dt_ids[] = {
> +	{ .compatible = "mux-gpio", },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids);
> +
> +static int mux_gpio_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct mux_control *mux;
> +	struct mux_gpio *mux_gpio;
> +	u32 idle_state;
> +	int ret;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	mux = mux_control_alloc(sizeof(*mux_gpio));
> +	if (!mux)
> +		return -ENOMEM;
> +	mux_gpio = mux->priv;
> +	mux->dev.parent = dev;
> +	mux->ops = &mux_gpio_ops;
> +
> +	platform_set_drvdata(pdev, mux);
> +
> +	mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
> +	if (IS_ERR(mux_gpio->gpios)) {
> +		if (PTR_ERR(mux_gpio->gpios) != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get gpios\n");
> +		mux_control_put(mux);
> +		return PTR_ERR(mux_gpio->gpios);
> +	}
> +
> +	ret = of_property_read_u32(np, "idle-state", &idle_state);
> +	if (ret >= 0) {
> +		if (idle_state >= (1 << mux_gpio->gpios->ndescs)) {
> +			dev_err(dev, "invalid idle-state %u\n", idle_state);
> +			return -EINVAL;
> +		}
> +		mux->idle_state = idle_state;
> +	}
> +
> +	ret = mux_control_register(mux);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to register mux_control\n");
> +		mux_control_put(mux);
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int mux_gpio_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mux_control *mux = to_mux_control(dev);
> +
> +	mux_control_unregister(mux);
> +	mux_control_put(mux);
> +	return 0;
> +}
> +
> +static struct platform_driver mux_gpio_driver = {
> +	.driver = {
> +		.name = "mux-gpio",
> +		.of_match_table	= of_match_ptr(mux_gpio_dt_ids),
> +	},
> +	.probe = mux_gpio_probe,
> +	.remove = mux_gpio_remove,
> +};
> +module_platform_driver(mux_gpio_driver);
> +
> +MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org");
> +MODULE_DESCRIPTION("GPIO-controlled multiplexer driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mux.h b/include/linux/mux.h
> new file mode 100644
> index 000000000000..5b21b8184056
> --- /dev/null
> +++ b/include/linux/mux.h
> @@ -0,0 +1,53 @@
> +/*
> + * mux.h - definitions for the multiplexer interface
> + *
> + * Copyright (C) 2016 Axentia Technologies AB
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef _LINUX_MUX_H
> +#define _LINUX_MUX_H
> +
> +#include <linux/device.h>
> +#include <linux/rwsem.h>
> +
> +struct mux_control;
> +
> +struct mux_control_ops {
> +	int (*set)(struct mux_control *mux, int reg);
> +};
> +
> +struct mux_control {
> +	struct rw_semaphore lock; /* protects the state of the mux */
> +
> +	struct device dev;
> +	int id;
> +
> +	int cached_state;
> +	int idle_state;
> +
> +	const struct mux_control_ops *ops;
> +
> +	void *priv;
> +};
> +
> +#define to_mux_control(x) container_of((x), struct mux_control, dev)
> +
> +struct mux_control *mux_control_alloc(size_t sizeof_priv);
> +int mux_control_register(struct mux_control *mux);
> +void mux_control_unregister(struct mux_control *mux);
> +void mux_control_put(struct mux_control *mux);
> +
> +int mux_control_select(struct mux_control *mux, int state);
> +int mux_control_deselect(struct mux_control *mux);
> +
> +struct mux_control *mux_control_get(struct device *dev,
> +				    const char *mux_name);
> +struct mux_control *devm_mux_control_get(struct device *dev,
> +					 const char *mux_name);
> +void devm_mux_control_put(struct device *dev, struct mux_control *mux);
> +
> +#endif /* _LINUX_MUX_H */
> 

^ permalink raw reply

* Re: [RFC PATCH v2 3/7] iio: inkern: api for manipulating ext_info of iio channels
From: Jonathan Cameron @ 2016-11-19 15:38 UTC (permalink / raw)
  To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Arnd Bergmann,
	Greg Kroah-Hartman, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1479419289-17553-4-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

On 17/11/16 21:48, Peter Rosin wrote:
> Extend the inkern api with functions for reading and writing ext_info
> of iio channels.
I'd like Lars' feedback on this one.

Superficially looks fine to me but I am not as familiar with this interface
as Lars is ;) (he wrote it IIRC:)
> ---
>  drivers/iio/inkern.c         | 55 ++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/iio/consumer.h |  6 +++++
>  2 files changed, 61 insertions(+)
> 
> diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
> index cfca17ba2535..a8099b164222 100644
> --- a/drivers/iio/inkern.c
> +++ b/drivers/iio/inkern.c
> @@ -850,3 +850,58 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(iio_write_channel_raw);
> +
> +int iio_get_channel_ext_info_count(struct iio_channel *chan)
> +{
> +	const struct iio_chan_spec_ext_info *ext_info;
> +	unsigned int i = 0;
> +
> +	if (!chan->channel->ext_info)
> +		return i;
> +
> +	for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++)
> +		++i;
> +
> +	return i;
> +}
> +EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);
> +
> +ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
> +				  const char *attr, char *buf)
> +{
> +	const struct iio_chan_spec_ext_info *ext_info;
> +
> +	if (!chan->channel->ext_info)
> +		return -EINVAL;
> +
> +	for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) {
> +		if (strcmp(attr, ext_info->name))
> +			continue;
> +
> +		return ext_info->read(chan->indio_dev, ext_info->private,
> +				      chan->channel, buf);
> +	}
> +
> +	return -EINVAL;
> +}
> +EXPORT_SYMBOL_GPL(iio_read_channel_ext_info);
> +
> +ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
> +				   const char *buf, size_t len)
> +{
> +	const struct iio_chan_spec_ext_info *ext_info;
> +
> +	if (!chan->channel->ext_info)
> +		return -EINVAL;
> +
> +	for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) {
> +		if (strcmp(attr, ext_info->name))
> +			continue;
> +
> +		return ext_info->write(chan->indio_dev, ext_info->private,
> +				       chan->channel, buf, len);
> +	}
> +
> +	return -EINVAL;
> +}
> +EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
> index 9a4f336d8b4a..471dece8729a 100644
> --- a/include/linux/iio/consumer.h
> +++ b/include/linux/iio/consumer.h
> @@ -299,4 +299,10 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val,
>  int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
>  	int *processed, unsigned int scale);
>  
> +int iio_get_channel_ext_info_count(struct iio_channel *chan);
> +ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
> +				  const char *attr, char *buf);
> +ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
> +				   const char *buf, size_t len);
> +
>  #endif
> 

^ permalink raw reply

* Re: [RFC PATCH v2 5/7] iio: multiplexer: new iio category and iio-mux driver
From: Jonathan Cameron @ 2016-11-19 15:49 UTC (permalink / raw)
  To: Peter Rosin, linux-kernel
  Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Arnd Bergmann,
	Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio
In-Reply-To: <1479419289-17553-6-git-send-email-peda@axentia.se>

On 17/11/16 21:48, Peter Rosin wrote:
> When a multiplexer changes how an iio device behaves (for example
> by feeding different signals to an ADC), this driver can be used
> create one virtual iio channel for each multiplexer state.
> 
> Depends on the generic multiplexer driver.
I'm not really following what all the ext info stuff in here is about.
Could you add a little more description of that?

Perhaps an example of how it is used and what the resulting interface
looks like?

Thanks,

Jonathan
> ---
>  drivers/iio/Kconfig               |   1 +
>  drivers/iio/Makefile              |   1 +
>  drivers/iio/multiplexer/Kconfig   |  17 ++
>  drivers/iio/multiplexer/Makefile  |   6 +
>  drivers/iio/multiplexer/iio-mux.c | 444 ++++++++++++++++++++++++++++++++++++++
>  5 files changed, 469 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/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 6743b18194fb..dcb541d0d70e 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -82,6 +82,7 @@ source "drivers/iio/humidity/Kconfig"
>  source "drivers/iio/imu/Kconfig"
>  source "drivers/iio/light/Kconfig"
>  source "drivers/iio/magnetometer/Kconfig"
> +source "drivers/iio/multiplexer/Kconfig"
>  source "drivers/iio/orientation/Kconfig"
>  if IIO_TRIGGER
>     source "drivers/iio/trigger/Kconfig"
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 87e4c4369e2f..f9879c29cf6f 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -27,6 +27,7 @@ obj-y += humidity/
>  obj-y += imu/
>  obj-y += light/
>  obj-y += magnetometer/
> +obj-y += multiplexer/
>  obj-y += orientation/
>  obj-y += potentiometer/
>  obj-y += pressure/
> diff --git a/drivers/iio/multiplexer/Kconfig b/drivers/iio/multiplexer/Kconfig
> new file mode 100644
> index 000000000000..31cbe4509366
> --- /dev/null
> +++ b/drivers/iio/multiplexer/Kconfig
> @@ -0,0 +1,17 @@
> +#
> +# Multiplexer drivers
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "Multiplexers"
> +
> +config IIO_MUX
> +	tristate "IIO multiplexer driver"
> +	depends on OF && MUX_GPIO
> +	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..2a8d00da990b
> --- /dev/null
> +++ b/drivers/iio/multiplexer/iio-mux.c
> @@ -0,0 +1,444 @@
> +/*
> + * IIO multiplexer driver
> + *
> + * Copyright (C) 2016 Axentia Technologies AB
> + *
> + * Author: Peter Rosin <peda@axentia.se>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/mux.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +struct mux_ext_info_cache {
> +	char *data;
> +	size_t size;
> +};
> +
> +struct mux_child {
> +	u32 state;
> +
> +	struct mux_ext_info_cache *ext_info_cache;
> +};
> +
> +struct mux {
> +	u32 cached_state;
> +	struct mux_control *control;
> +	struct iio_channel *parent;
> +	struct iio_dev *indio_dev;
> +	struct iio_chan_spec *c;
> +	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];
> +	int ret;
> +	int i;
> +
> +	ret = mux_control_select(mux->control, child->state);
> +	if (ret < 0) {
> +		mux->cached_state = -1;
> +		return ret;
> +	}
> +
> +	if (mux->cached_state == child->state)
> +		return 0;
> +
I don't follow what is going on here..  Perhaps you could explain
futher?
> +	if (mux->c[idx].ext_info) {
> +		for (i = 0; mux->c[idx].ext_info[i].name; ++i) {
> +			const char *attr = mux->c[idx].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 = child->state;
> +
> +	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->c;
> +	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->c;
> +	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->c;
> +	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->c;
> +	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->c;
> +	char *new;
> +	ssize_t ret;
> +
> +	ret = iio_mux_select(mux, idx);
> +	if (ret < 0)
> +		return ret;
> +
> +	new = devm_kmemdup(dev, buf, len + 1, GFP_KERNEL);
> +	if (!new) {
> +		iio_mux_deselect(mux);
> +		return -ENOMEM;
> +	}
> +
> +	new[len] = 0;
> +
> +	ret = iio_write_channel_ext_info(mux->parent,
> +					 mux->ext_info[private].name,
> +					 buf, len);
> +	if (ret < 0) {
> +		iio_mux_deselect(mux);
> +		devm_kfree(dev, new);
> +		return ret;
> +	}
> +
> +	devm_kfree(dev, mux->child[idx].ext_info_cache[private].data);
> +	mux->child[idx].ext_info_cache[private].data = new;
> +	mux->child[idx].ext_info_cache[private].size = len;
> +
> +	iio_mux_deselect(mux);
> +
> +	return ret;
> +}
> +
> +static int mux_configure_channel(struct device *dev, struct mux *mux,
> +				 struct device_node *child_np, int idx)
> +{
> +	struct mux_child *child = &mux->child[idx];
> +	struct iio_chan_spec *c = &mux->c[idx];
> +	const struct iio_chan_spec *pc = mux->parent->channel;
> +	char *page;
> +	int num_ext_info;
> +	int i;
> +	int ret;
> +
> +	c->indexed = 1;
> +	c->channel = idx;
> +	c->output = pc->output;
> +	c->datasheet_name = child_np->name;
> +	c->ext_info = mux->ext_info;
> +
> +	ret = iio_get_channel_type(mux->parent, &c->type);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to get parent channel type\n");
> +		return ret;
> +	}
> +
> +	if (iio_channel_has_info(pc, IIO_CHAN_INFO_RAW))
> +		c->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW);
> +	if (iio_channel_has_info(pc, IIO_CHAN_INFO_SCALE))
> +		c->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE);
> +
> +	if (iio_channel_has_available(pc, IIO_CHAN_INFO_RAW))
> +		c->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW);
> +
> +	ret = of_property_read_u32(child_np, "reg", &child->state);
> +	if (ret < 0) {
> +		dev_err(dev, "no reg property for node '%s'\n", child_np->name);
> +		return ret;
> +	}
> +
> +	for (i = 0; i < idx; ++i) {
> +		if (mux->child[i].state == child->state) {
> +			dev_err(dev, "double use of reg %d\n", child->state);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	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 (!pc->ext_info[i].write)
> +			continue;
> +		if (!pc->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",
> +				pc->ext_info[i].name);
> +			return ret;
> +		}
> +		if (ret >= PAGE_SIZE) {
> +			dev_err(dev, "too large ext_info '%s'\n",
> +				pc->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 (num_ext_info)
> +		devm_kfree(dev, page);
> +
> +	return 0;
> +}
> +
> +static int mux_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct device_node *child_np;
> +	struct iio_dev *indio_dev;
> +	struct iio_channel *parent;
> +	struct mux *mux;
> +	int sizeof_ext_info;
> +	int children;
> +	int sizeof_priv;
> +	int i;
> +	int ret;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	parent = devm_iio_channel_get(dev, "parent");
> +	if (IS_ERR(parent)) {
> +		if (PTR_ERR(parent) != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get parent channel\n");
> +		return PTR_ERR(parent);
> +	}
> +
> +	sizeof_ext_info = iio_get_channel_ext_info_count(parent);
> +	if (sizeof_ext_info) {
> +		sizeof_ext_info += 1; /* one extra entry for the sentinel */
> +		sizeof_ext_info *= sizeof(*mux->ext_info);
> +	}
> +
> +	children = of_get_child_count(np);
> +	if (children <= 0) {
> +		dev_err(dev, "not even a single child\n");
> +		return -EINVAL;
> +	}
> +
> +	sizeof_priv = sizeof(*mux);
> +	sizeof_priv += sizeof(*mux->child) * children;
> +	sizeof_priv += sizeof(*mux->c) * 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->c = (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->c;
> +	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;
> +		}
> +	}
> +
> +	i = 0;
> +	for_each_child_of_node(np, child_np) {
> +		ret = mux_configure_channel(dev, mux, child_np, i);
> +		if (ret < 0)
> +			return ret;
> +		i++;
> +	}
> +
> +	mux->control = devm_mux_control_get(dev, "mux");
> +	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);
> +	}
> +
> +	ret = devm_iio_device_register(dev, indio_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to register iio device\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id mux_match[] = {
> +	{ .compatible = "iio-mux" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mux_match);
> +
> +static struct platform_driver mux_driver = {
> +	.probe = mux_probe,
> +	.driver = {
> +		.name = "iio-mux",
> +		.of_match_table = mux_match,
> +	},
> +};
> +module_platform_driver(mux_driver);
> +
> +MODULE_DESCRIPTION("IIO multiplexer driver");
> +MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox