Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 05/14] MIPS: ingenic: Enable pinctrl for all ingenic SoCs
From: Paul Cercueil @ 2017-01-22 14:49 UTC (permalink / raw)
  To: Linus Walleij, Rob Herring, Mark Rutland, Ralf Baechle,
	Ulf Hansson
  Cc: Boris Brezillon, Thierry Reding, Bartlomiej Zolnierkiewicz,
	Maarten ter Huurne, Lars-Peter Clausen, Paul Burton, linux-gpio,
	devicetree, linux-kernel, linux-mips, linux-mmc, linux-mtd,
	linux-pwm, linux-fbdev, james.hogan, Paul Cercueil
In-Reply-To: <20170122144947.16158-1-paul@crapouillou.net>

There is a pinctrl driver for each of the Ingenic SoCs supported by the
upstream Linux kernel. In order to switch away from the old GPIO
platform code, we now enable the pinctrl drivers by default for the
Ingenic SoCs.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---
 arch/mips/Kconfig | 1 +
 1 file changed, 1 insertion(+)

v2: No changes

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index b3c5bde43d34..fc720e37661e 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -359,6 +359,7 @@ config MACH_INGENIC
 	select SYS_SUPPORTS_ZBOOT_UART16550
 	select DMA_NONCOHERENT
 	select IRQ_MIPS_CPU
+	select PINCTRL
 	select GPIOLIB
 	select COMMON_CLK
 	select GENERIC_IRQ_CHIP
-- 
2.11.0

^ permalink raw reply related

* [PATCH v2 04/14] GPIO: Add gpio-ingenic driver
From: Paul Cercueil @ 2017-01-22 14:49 UTC (permalink / raw)
  To: Linus Walleij, Rob Herring, Mark Rutland, Ralf Baechle,
	Ulf Hansson
  Cc: Boris Brezillon, Thierry Reding, Bartlomiej Zolnierkiewicz,
	Maarten ter Huurne, Lars-Peter Clausen, Paul Burton, linux-gpio,
	devicetree, linux-kernel, linux-mips, linux-mmc, linux-mtd,
	linux-pwm, linux-fbdev, james.hogan, Paul Cercueil
In-Reply-To: <20170122144947.16158-1-paul@crapouillou.net>

This driver handles the GPIOs of all the Ingenic JZ47xx SoCs
currently supported by the upsteam Linux kernel.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---
 drivers/gpio/Kconfig        |  10 ++
 drivers/gpio/Makefile       |   1 +
 drivers/gpio/gpio-ingenic.c | 367 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 378 insertions(+)
 create mode 100644 drivers/gpio/gpio-ingenic.c

v2: Consider it's a new patch. Completely rewritten from v1.

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d5d36549ecc1..21992ca29342 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -225,6 +225,16 @@ config GPIO_ICH
 
 	  If unsure, say N.
 
+config GPIO_INGENIC
+	tristate "Ingenic JZ47xx SoCs GPIO support"
+	depends on MACH_INGENIC || COMPILE_TEST
+	select GPIOLIB_IRQCHIP
+	help
+	  Say yes here to support the GPIO functionality present on the
+	  JZ4740 and JZ4780 SoCs from Ingenic.
+
+	  If unsure, say N.
+
 config GPIO_IOP
 	tristate "Intel IOP GPIO"
 	depends on ARCH_IOP32X || ARCH_IOP33X || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index a7676b82de6f..3c5412ae56f0 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_GPIO_GPIO_MM)	+= gpio-gpio-mm.o
 obj-$(CONFIG_GPIO_GRGPIO)	+= gpio-grgpio.o
 obj-$(CONFIG_HTC_EGPIO)		+= gpio-htc-egpio.o
 obj-$(CONFIG_GPIO_ICH)		+= gpio-ich.o
+obj-$(CONFIG_GPIO_INGENIC)	+= gpio-ingenic.o
 obj-$(CONFIG_GPIO_IOP)		+= gpio-iop.o
 obj-$(CONFIG_GPIO_IT87)		+= gpio-it87.o
 obj-$(CONFIG_GPIO_JANZ_TTL)	+= gpio-janz-ttl.o
diff --git a/drivers/gpio/gpio-ingenic.c b/drivers/gpio/gpio-ingenic.c
new file mode 100644
index 000000000000..aa9e0cb794c3
--- /dev/null
+++ b/drivers/gpio/gpio-ingenic.c
@@ -0,0 +1,367 @@
+/*
+ * Ingenic JZ47xx GPIO driver
+ *
+ * Copyright (c) 2017 Paul Cercueil <paul@crapouillou.net>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#define GPIO_PIN	0x00
+#define GPIO_MSK	0x20
+
+#define JZ4740_GPIO_DATA	0x10
+#define JZ4740_GPIO_SELECT	0x50
+#define JZ4740_GPIO_DIR		0x60
+#define JZ4740_GPIO_TRIG	0x70
+#define JZ4740_GPIO_FLAG	0x80
+
+#define JZ4780_GPIO_INT		0x10
+#define JZ4780_GPIO_PAT1	0x30
+#define JZ4780_GPIO_PAT0	0x40
+#define JZ4780_GPIO_FLAG	0x50
+
+#define REG_SET(x) ((x) + 0x4)
+#define REG_CLEAR(x) ((x) + 0x8)
+
+enum jz_version {
+	ID_JZ4740,
+	ID_JZ4780,
+};
+
+struct ingenic_gpio_chip {
+	void __iomem *base;
+	struct gpio_chip gc;
+	struct irq_chip irq_chip;
+	unsigned int irq;
+	enum jz_version version;
+};
+
+static inline bool gpio_get_value(struct ingenic_gpio_chip *jzgc, u8 offset)
+{
+	if (jzgc->version >= ID_JZ4780)
+		return readl(jzgc->base + GPIO_PIN) & BIT(offset);
+	else
+		return readl(jzgc->base + JZ4740_GPIO_DATA) & BIT(offset);
+}
+
+static void gpio_set_value(struct ingenic_gpio_chip *jzgc, u8 offset, int value)
+{
+	u8 reg;
+
+	if (jzgc->version >= ID_JZ4780)
+		reg = JZ4780_GPIO_PAT0;
+	else
+		reg = JZ4740_GPIO_DATA;
+
+	if (value)
+		writel(BIT(offset), jzgc->base + REG_SET(reg));
+	else
+		writel(BIT(offset), jzgc->base + REG_CLEAR(reg));
+}
+
+static void irq_set_type(struct ingenic_gpio_chip *jzgc,
+		u8 offset, unsigned int type)
+{
+	u8 reg1, reg2;
+
+	if (jzgc->version >= ID_JZ4780) {
+		reg1 = JZ4780_GPIO_PAT1;
+		reg2 = JZ4780_GPIO_PAT0;
+	} else {
+		reg1 = JZ4740_GPIO_TRIG;
+		reg2 = JZ4740_GPIO_DIR;
+	}
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		writel(BIT(offset), jzgc->base + REG_SET(reg2));
+		writel(BIT(offset), jzgc->base + REG_SET(reg1));
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		writel(BIT(offset), jzgc->base + REG_CLEAR(reg2));
+		writel(BIT(offset), jzgc->base + REG_SET(reg1));
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		writel(BIT(offset), jzgc->base + REG_SET(reg2));
+		writel(BIT(offset), jzgc->base + REG_CLEAR(reg1));
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+	default:
+		writel(BIT(offset), jzgc->base + REG_CLEAR(reg2));
+		writel(BIT(offset), jzgc->base + REG_CLEAR(reg1));
+		break;
+	};
+
+}
+
+static void ingenic_gpio_irq_mask(struct irq_data *irqd)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+	struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+	writel(BIT(irqd->hwirq), jzgc->base + REG_SET(GPIO_MSK));
+}
+
+static void ingenic_gpio_irq_unmask(struct irq_data *irqd)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+	struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+	writel(BIT(irqd->hwirq), jzgc->base + REG_CLEAR(GPIO_MSK));
+}
+
+static void ingenic_gpio_irq_enable(struct irq_data *irqd)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+	struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+	int irq = irqd->hwirq;
+
+	if (jzgc->version >= ID_JZ4780)
+		writel(BIT(irq), jzgc->base + REG_SET(JZ4780_GPIO_INT));
+	else
+		writel(BIT(irq), jzgc->base + REG_SET(JZ4740_GPIO_SELECT));
+
+	ingenic_gpio_irq_unmask(irqd);
+}
+
+static void ingenic_gpio_irq_disable(struct irq_data *irqd)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+	struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+	int irq = irqd->hwirq;
+
+	ingenic_gpio_irq_mask(irqd);
+
+	if (jzgc->version >= ID_JZ4780)
+		writel(BIT(irq), jzgc->base + REG_CLEAR(JZ4780_GPIO_INT));
+	else
+		writel(BIT(irq), jzgc->base + REG_CLEAR(JZ4740_GPIO_SELECT));
+}
+
+static void ingenic_gpio_irq_ack(struct irq_data *irqd)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+	struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+	int irq = irqd->hwirq;
+	bool high;
+
+	if (irqd_get_trigger_type(irqd) == IRQ_TYPE_EDGE_BOTH) {
+		/*
+		 * Switch to an interrupt for the opposite edge to the one that
+		 * triggered the interrupt being ACKed.
+		 */
+		high = gpio_get_value(jzgc, irq);
+		if (high)
+			irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_FALLING);
+		else
+			irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_RISING);
+	}
+
+	if (jzgc->version >= ID_JZ4780)
+		writel(BIT(irq), jzgc->base + REG_CLEAR(JZ4780_GPIO_FLAG));
+	else
+		writel(BIT(irq), jzgc->base + REG_SET(JZ4740_GPIO_DATA));
+}
+
+static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+	struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_BOTH:
+	case IRQ_TYPE_EDGE_RISING:
+	case IRQ_TYPE_EDGE_FALLING:
+		irq_set_handler_locked(irqd, handle_edge_irq);
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+	case IRQ_TYPE_LEVEL_LOW:
+		irq_set_handler_locked(irqd, handle_level_irq);
+		break;
+	default:
+		irq_set_handler_locked(irqd, handle_bad_irq);
+	}
+
+	if (type == IRQ_TYPE_EDGE_BOTH) {
+		/*
+		 * The hardware does not support interrupts on both edges. The
+		 * best we can do is to set up a single-edge interrupt and then
+		 * switch to the opposing edge when ACKing the interrupt.
+		 */
+		bool high = gpio_get_value(jzgc, irqd->hwirq);
+
+		type = high ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
+	}
+
+	irq_set_type(jzgc, irqd->hwirq, type);
+	return 0;
+}
+
+static int ingenic_gpio_irq_set_wake(struct irq_data *irqd, unsigned int on)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+	struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+	return irq_set_irq_wake(jzgc->irq, on);
+}
+
+static void ingenic_gpio_irq_handler(struct irq_desc *desc)
+{
+	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+	struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+	struct irq_chip *irq_chip = irq_data_get_irq_chip(&desc->irq_data);
+	unsigned long flag, i;
+
+	chained_irq_enter(irq_chip, desc);
+
+	if (jzgc->version >= ID_JZ4780)
+		flag = readl(jzgc->base + JZ4780_GPIO_FLAG);
+	else
+		flag = readl(jzgc->base + JZ4740_GPIO_FLAG);
+
+	for_each_set_bit(i, &flag, 32)
+		generic_handle_irq(irq_linear_revmap(gc->irqdomain, i));
+	chained_irq_exit(irq_chip, desc);
+}
+
+static void ingenic_gpio_set(struct gpio_chip *gc,
+		unsigned int offset, int value)
+{
+	struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+	gpio_set_value(jzgc, offset, value);
+}
+
+static int ingenic_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+	struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+	return (int) gpio_get_value(jzgc, offset);
+}
+
+static int ingenic_gpio_direction_input(struct gpio_chip *gc,
+		unsigned int offset)
+{
+	return pinctrl_gpio_direction_input(gc->base + offset);
+}
+
+static int ingenic_gpio_direction_output(struct gpio_chip *gc,
+		unsigned int offset, int value)
+{
+	ingenic_gpio_set(gc, offset, value);
+	return pinctrl_gpio_direction_output(gc->base + offset);
+}
+
+static const struct of_device_id ingenic_gpio_of_match[] = {
+	{ .compatible = "ingenic,jz4740-gpio", .data = (void *)ID_JZ4740 },
+	{ .compatible = "ingenic,jz4780-gpio", .data = (void *)ID_JZ4780 },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ingenic_gpio_of_match);
+
+static int ingenic_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *of_id = of_match_device(
+			ingenic_gpio_of_match, dev);
+	struct ingenic_gpio_chip *jzgc;
+	int err;
+
+	jzgc = devm_kzalloc(dev, sizeof(*jzgc), GFP_KERNEL);
+	if (!jzgc)
+		return -ENOMEM;
+
+	jzgc->base = of_iomap(dev->of_node, 0);
+	if (!jzgc->base) {
+		dev_err(dev, "failed to map IO memory\n");
+		return -ENXIO;
+	}
+
+	jzgc->gc.base = -1;
+	jzgc->gc.ngpio = 32;
+	jzgc->gc.parent = dev;
+	jzgc->gc.of_node = dev->of_node;
+	jzgc->gc.label = "";
+	jzgc->gc.owner = THIS_MODULE;
+	jzgc->version = (enum jz_version)of_id->data;
+
+	jzgc->gc.set = ingenic_gpio_set;
+	jzgc->gc.get = ingenic_gpio_get;
+	jzgc->gc.direction_input = ingenic_gpio_direction_input;
+	jzgc->gc.direction_output = ingenic_gpio_direction_output;
+
+	if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
+		jzgc->gc.request = gpiochip_generic_request;
+		jzgc->gc.free = gpiochip_generic_free;
+	}
+
+	of_property_read_u32(dev->of_node, "base", &jzgc->gc.base);
+
+	err = devm_gpiochip_add_data(dev, &jzgc->gc, jzgc);
+	if (err)
+		return err;
+
+	if (!of_property_read_bool(dev->of_node, "interrupt-controller"))
+		return 0;
+
+	jzgc->irq = irq_of_parse_and_map(dev->of_node, 0);
+	if (!jzgc->irq)
+		return -EINVAL;
+
+	jzgc->irq_chip.name = dev->of_node->name;
+	jzgc->irq_chip.irq_enable = ingenic_gpio_irq_enable;
+	jzgc->irq_chip.irq_disable = ingenic_gpio_irq_disable;
+	jzgc->irq_chip.irq_unmask = ingenic_gpio_irq_unmask;
+	jzgc->irq_chip.irq_mask = ingenic_gpio_irq_mask;
+	jzgc->irq_chip.irq_ack = ingenic_gpio_irq_ack;
+	jzgc->irq_chip.irq_set_type = ingenic_gpio_irq_set_type;
+	jzgc->irq_chip.irq_set_wake = ingenic_gpio_irq_set_wake;
+	jzgc->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND;
+
+	err = gpiochip_irqchip_add(&jzgc->gc, &jzgc->irq_chip, 0,
+			handle_level_irq, IRQ_TYPE_NONE);
+	if (err)
+		return err;
+
+	gpiochip_set_chained_irqchip(&jzgc->gc, &jzgc->irq_chip,
+			jzgc->irq, ingenic_gpio_irq_handler);
+	return 0;
+}
+
+static int ingenic_gpio_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver ingenic_gpio_driver = {
+	.driver = {
+		.name = "gpio-ingenic",
+		.of_match_table = of_match_ptr(ingenic_gpio_of_match),
+	},
+	.probe = ingenic_gpio_probe,
+	.remove = ingenic_gpio_remove,
+};
+
+static int __init ingenic_gpio_drv_register(void)
+{
+	return platform_driver_register(&ingenic_gpio_driver);
+}
+subsys_initcall(ingenic_gpio_drv_register);
+
+static void __exit ingenic_gpio_drv_unregister(void)
+{
+	platform_driver_unregister(&ingenic_gpio_driver);
+}
+module_exit(ingenic_gpio_drv_unregister);
+
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_DESCRIPTION("Ingenic JZ47xx GPIO driver");
+MODULE_LICENSE("GPL");
-- 
2.11.0


^ permalink raw reply related

* [PATCH v2 03/14] pinctrl-ingenic: add a pinctrl driver for the Ingenic jz47xx SoCs
From: Paul Cercueil @ 2017-01-22 14:49 UTC (permalink / raw)
  To: Linus Walleij, Rob Herring, Mark Rutland, Ralf Baechle,
	Ulf Hansson
  Cc: Boris Brezillon, Thierry Reding, Bartlomiej Zolnierkiewicz,
	Maarten ter Huurne, Lars-Peter Clausen, Paul Burton,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA, Paul Cercueil
In-Reply-To: <20170122144947.16158-1-paul-icTtO2rgO2OTuSrc4Mpeew@public.gmane.org>

This driver handles pin configuration and pin muxing for the
JZ4740 and JZ4780 SoCs from Ingenic.

Signed-off-by: Paul Cercueil <paul-icTtO2rgO2OTuSrc4Mpeew@public.gmane.org>
---
 drivers/pinctrl/Kconfig           |   8 +
 drivers/pinctrl/Makefile          |   1 +
 drivers/pinctrl/pinctrl-ingenic.c | 488 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 497 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-ingenic.c

v2: Consider it's a new patch. Completely rewritten from v1.

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 8f8c2af45781..2312e21ca48d 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -285,6 +285,14 @@ config PINCTRL_ZYNQ
 	help
 	  This selects the pinctrl driver for Xilinx Zynq.
 
+config PINCTRL_INGENIC
+	bool "Pinctrl driver for the Ingenic JZ47xx SoCs"
+	default y
+	depends on MACH_INGENIC || COMPILE_TEST
+	select GENERIC_PINCONF
+	select GENERIC_PINCTRL_GROUPS
+	select GENERIC_PINMUX_FUNCTIONS
+
 source "drivers/pinctrl/aspeed/Kconfig"
 source "drivers/pinctrl/bcm/Kconfig"
 source "drivers/pinctrl/berlin/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index a251f439626f..80f327239d4b 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_PINCTRL_LPC18XX)	+= pinctrl-lpc18xx.o
 obj-$(CONFIG_PINCTRL_TB10X)	+= pinctrl-tb10x.o
 obj-$(CONFIG_PINCTRL_ST) 	+= pinctrl-st.o
 obj-$(CONFIG_PINCTRL_ZYNQ)	+= pinctrl-zynq.o
+obj-$(CONFIG_PINCTRL_INGENIC)	+= pinctrl-ingenic.o
 
 obj-$(CONFIG_ARCH_ASPEED)	+= aspeed/
 obj-y				+= bcm/
diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c
new file mode 100644
index 000000000000..ce36cf509eb1
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-ingenic.c
@@ -0,0 +1,488 @@
+/*
+ * Ingenic SoCs pinctrl driver
+ *
+ * Copyright (c) 2017 Paul Cercueil <paul-icTtO2rgO2OTuSrc4Mpeew@public.gmane.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/compiler.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "core.h"
+#include "pinconf.h"
+#include "pinmux.h"
+
+#define JZ4740_GPIO_DATA	0x10
+#define JZ4740_GPIO_PULL_DIS	0x30
+#define JZ4740_GPIO_FUNC	0x40
+#define JZ4740_GPIO_SELECT	0x50
+#define JZ4740_GPIO_DIR		0x60
+#define JZ4740_GPIO_TRIG	0x70
+#define JZ4740_GPIO_FLAG	0x80
+
+#define JZ4780_GPIO_INT		0x10
+#define JZ4780_GPIO_MSK		0x20
+#define JZ4780_GPIO_PAT1	0x30
+#define JZ4780_GPIO_PAT0	0x40
+#define JZ4780_GPIO_FLAG	0x50
+#define JZ4780_GPIO_PEN		0x70
+
+#define REG_SET(x) ((x) + 0x4)
+#define REG_CLEAR(x) ((x) + 0x8)
+
+#define PINS_PER_GPIO_CHIP 32
+#define NUM_MAX_GPIO_CHIPS 6
+
+enum jz_version {
+	ID_JZ4740,
+	ID_JZ4780,
+};
+
+struct ingenic_pinctrl {
+	struct device *dev;
+	void __iomem *base;
+	struct pinctrl_dev *pctl;
+	struct pinctrl_pin_desc *pdesc;
+	enum jz_version version;
+
+	u32 pull_ups[NUM_MAX_GPIO_CHIPS];
+	u32 pull_downs[NUM_MAX_GPIO_CHIPS];
+};
+
+static inline void ingenic_config_pin(struct ingenic_pinctrl *jzpc,
+		unsigned int pin, u8 reg, bool set)
+{
+	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
+	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
+
+	writel(BIT(idx), jzpc->base + offt * 0x100 +
+			(set ? REG_SET(reg) : REG_CLEAR(reg)));
+}
+
+static inline bool ingenic_get_pin_config(struct ingenic_pinctrl *jzpc,
+		unsigned int pin, u8 reg)
+{
+	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
+	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
+
+	return readl(jzpc->base + offt * 0x100 + reg) & BIT(idx);
+}
+
+static struct pinctrl_ops ingenic_pctlops = {
+	.get_groups_count = pinctrl_generic_get_group_count,
+	.get_group_name = pinctrl_generic_get_group_name,
+	.get_group_pins = pinctrl_generic_get_group_pins,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+	.dt_free_map = pinconf_generic_dt_free_map,
+};
+
+static int ingenic_pinmux_set_pin_fn(struct ingenic_pinctrl *jzpc,
+		int pin, int func)
+{
+	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
+	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
+
+	dev_dbg(jzpc->dev, "set pin P%c%u to function %u\n",
+			'A' + offt, idx, func);
+
+	if (jzpc->version >= ID_JZ4780) {
+		ingenic_config_pin(jzpc, pin, JZ4780_GPIO_INT, false);
+		ingenic_config_pin(jzpc, pin, JZ4780_GPIO_MSK, false);
+		ingenic_config_pin(jzpc, pin, JZ4780_GPIO_PAT1, func & 0x2);
+		ingenic_config_pin(jzpc, pin, JZ4780_GPIO_PAT0, func & 0x1);
+	} else {
+		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_FUNC, true);
+		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_TRIG, func & 0x2);
+		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, func > 0);
+	}
+
+	return 0;
+}
+
+static int ingenic_pinmux_set_mux(struct pinctrl_dev *pctldev,
+		unsigned int selector, unsigned int group)
+{
+	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
+	struct function_desc *func;
+	struct group_desc *grp;
+	unsigned int i;
+
+	func = pinmux_generic_get_function(pctldev, selector);
+	if (!func)
+		return -EINVAL;
+
+	grp = pinctrl_generic_get_group(pctldev, group);
+	if (!grp)
+		return -EINVAL;
+
+	dev_dbg(pctldev->dev, "enable function %s group %s\n",
+		func->name, grp->name);
+
+	for (i = 0; i < grp->num_pins; i++) {
+		int *pin_modes = grp->data;
+
+		ingenic_pinmux_set_pin_fn(jzpc, grp->pins[i], pin_modes[i]);
+	}
+
+	return 0;
+}
+
+static int ingenic_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
+		struct pinctrl_gpio_range *range,
+		unsigned int pin, bool input)
+{
+	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
+	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
+	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
+
+	dev_dbg(pctldev->dev, "set pin P%c%u to %sput\n",
+			'A' + offt, idx, input ? "in" : "out");
+
+	if (jzpc->version >= ID_JZ4780) {
+		ingenic_config_pin(jzpc, pin, JZ4780_GPIO_INT, false);
+		ingenic_config_pin(jzpc, pin, JZ4780_GPIO_MSK, true);
+		ingenic_config_pin(jzpc, pin, JZ4780_GPIO_PAT1, input);
+	} else {
+		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, false);
+		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_DIR, input);
+		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_FUNC, false);
+	}
+
+	return 0;
+}
+
+static struct pinmux_ops ingenic_pmxops = {
+	.get_functions_count = pinmux_generic_get_function_count,
+	.get_function_name = pinmux_generic_get_function_name,
+	.get_function_groups = pinmux_generic_get_function_groups,
+	.set_mux = ingenic_pinmux_set_mux,
+	.gpio_set_direction = ingenic_pinmux_gpio_set_direction,
+};
+
+static int ingenic_pinconf_get(struct pinctrl_dev *pctldev,
+		unsigned int pin, unsigned long *config)
+{
+	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
+	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
+	bool pull;
+
+	if (jzpc->version >= ID_JZ4780)
+		pull = !ingenic_get_pin_config(jzpc, pin, JZ4780_GPIO_PEN);
+	else
+		pull = !ingenic_get_pin_config(jzpc, pin, JZ4740_GPIO_PULL_DIS);
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+		if (pull)
+			return -EINVAL;
+		break;
+
+	case PIN_CONFIG_BIAS_PULL_UP:
+		if (!pull || !(jzpc->pull_ups[offt] & BIT(idx)))
+			return -EINVAL;
+		break;
+
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		if (!pull || !(jzpc->pull_downs[offt] & BIT(idx)))
+			return -EINVAL;
+		break;
+
+	default:
+		return -ENOTSUPP;
+	}
+
+	*config = pinconf_to_config_packed(param, 1);
+	return 0;
+}
+
+static void ingenic_set_bias(struct ingenic_pinctrl *jzpc,
+		unsigned int pin, bool enabled)
+{
+	if (jzpc->version >= ID_JZ4780)
+		ingenic_config_pin(jzpc, pin, JZ4780_GPIO_PEN, !enabled);
+	else
+		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_PULL_DIS, !enabled);
+}
+
+static int ingenic_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+		unsigned long *configs, unsigned int num_configs)
+{
+	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
+	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
+	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
+	unsigned int cfg;
+
+	for (cfg = 0; cfg < num_configs; cfg++) {
+		switch (pinconf_to_config_param(configs[cfg])) {
+		case PIN_CONFIG_BIAS_DISABLE:
+		case PIN_CONFIG_BIAS_PULL_UP:
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			continue;
+		default:
+			return -ENOTSUPP;
+		}
+	}
+
+	for (cfg = 0; cfg < num_configs; cfg++) {
+		switch (pinconf_to_config_param(configs[cfg])) {
+		case PIN_CONFIG_BIAS_DISABLE:
+			dev_dbg(jzpc->dev, "disable pull-over for pin P%c%u\n",
+					'A' + offt, idx);
+			ingenic_set_bias(jzpc, pin, false);
+			break;
+
+		case PIN_CONFIG_BIAS_PULL_UP:
+			if (!(jzpc->pull_ups[offt] & BIT(idx)))
+				return -EINVAL;
+			dev_dbg(jzpc->dev, "set pull-up for pin P%c%u\n",
+					'A' + offt, idx);
+			ingenic_set_bias(jzpc, pin, true);
+			break;
+
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			if (!(jzpc->pull_downs[offt] & BIT(idx)))
+				return -EINVAL;
+			dev_dbg(jzpc->dev, "set pull-down for pin P%c%u\n",
+					'A' + offt, idx);
+			ingenic_set_bias(jzpc, pin, true);
+			break;
+
+		default:
+			unreachable();
+		}
+	}
+
+	return 0;
+}
+
+static int ingenic_pinconf_group_get(struct pinctrl_dev *pctldev,
+		unsigned int group, unsigned long *config)
+{
+	const unsigned *pins;
+	unsigned int i, npins, old = 0;
+	int ret;
+
+	ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < npins; i++) {
+		if (ingenic_pinconf_get(pctldev, pins[i], config))
+			return -ENOTSUPP;
+
+		/* configs do not match between two pins */
+		if (i && (old != *config))
+			return -ENOTSUPP;
+
+		old = *config;
+	}
+
+	return 0;
+}
+
+static int ingenic_pinconf_group_set(struct pinctrl_dev *pctldev,
+		unsigned int group, unsigned long *configs,
+		unsigned int num_configs)
+{
+	const unsigned *pins;
+	unsigned int i, npins;
+	int ret;
+
+	ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < npins; i++) {
+		ret = ingenic_pinconf_set(pctldev,
+				pins[i], configs, num_configs);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct pinconf_ops ingenic_confops = {
+	.is_generic = true,
+	.pin_config_get = ingenic_pinconf_get,
+	.pin_config_set = ingenic_pinconf_set,
+	.pin_config_group_get = ingenic_pinconf_group_get,
+	.pin_config_group_set = ingenic_pinconf_group_set,
+};
+
+static int ingenic_pinctrl_parse_dt_func(struct ingenic_pinctrl *jzpc,
+		struct device_node *np)
+{
+	unsigned int num_groups;
+	struct device_node *group_node;
+	unsigned int i, j;
+	int err, npins, *pins, *confs;
+	const char **groups;
+
+	num_groups = of_get_child_count(np);
+	groups = devm_kzalloc(jzpc->dev,
+			sizeof(*groups) * num_groups, GFP_KERNEL);
+	if (!groups)
+		return -ENOMEM;
+
+	i = 0;
+	for_each_child_of_node(np, group_node) {
+		groups[i++] = group_node->name;
+
+		npins = of_property_count_elems_of_size(group_node,
+				"ingenic,pins", 8);
+		if (npins < 0)
+			return npins;
+
+		pins = devm_kzalloc(jzpc->dev,
+				sizeof(*pins) * npins, GFP_KERNEL);
+		confs = devm_kzalloc(jzpc->dev,
+				sizeof(*confs) * npins, GFP_KERNEL);
+		if (!pins || !confs)
+			return -ENOMEM;
+
+		for (j = 0; j < npins; j++) {
+			of_property_read_u32_index(group_node,
+					"ingenic,pins", j * 2, &pins[j]);
+
+			of_property_read_u32_index(group_node,
+					"ingenic,pins", j * 2 + 1, &confs[j]);
+		}
+
+		err = pinctrl_generic_add_group(jzpc->pctl, group_node->name,
+				pins, npins, confs);
+		if (err)
+			return err;
+	}
+
+	return pinmux_generic_add_function(jzpc->pctl, np->name,
+			groups, num_groups, NULL);
+}
+
+static const struct of_device_id ingenic_pinctrl_of_match[] = {
+	{ .compatible = "ingenic,jz4740-pinctrl", .data = (void *) ID_JZ4740 },
+	{ .compatible = "ingenic,jz4780-pinctrl", .data = (void *) ID_JZ4780 },
+	{},
+};
+
+int ingenic_pinctrl_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ingenic_pinctrl *jzpc;
+	struct pinctrl_desc *pctl_desc;
+	struct device_node *np, *functions_node;
+	const struct of_device_id *of_id = of_match_device(
+			ingenic_pinctrl_of_match, dev);
+	unsigned int i, num_chips;
+	int err;
+
+	jzpc = devm_kzalloc(dev, sizeof(*jzpc), GFP_KERNEL);
+	if (!jzpc)
+		return -ENOMEM;
+
+	jzpc->base = of_iomap(dev->of_node, 0);
+	if (!jzpc->base) {
+		dev_err(dev, "failed to map IO memory\n");
+		return -ENXIO;
+	}
+
+	jzpc->dev = dev;
+	dev_set_drvdata(dev, jzpc);
+
+	jzpc->version = (enum jz_version)of_id->data;
+
+	if (jzpc->version >= ID_JZ4780)
+		num_chips = 6;
+	else
+		num_chips = 4;
+
+	/*
+	 * Read the ingenic,pull-ups and ingenic,pull-downs arrays if present in
+	 * the devicetree. Otherwise set all bits to 0xff to consider that
+	 * pull-over resistors are available on all pins.
+	 */
+	err = of_property_read_u32_array(dev->of_node, "ingenic,pull-ups",
+			jzpc->pull_ups, num_chips);
+	if (err)
+		memset(jzpc->pull_ups, 0xff, sizeof(jzpc->pull_ups));
+
+	err = of_property_read_u32_array(dev->of_node, "ingenic,pull-downs",
+			jzpc->pull_downs, num_chips);
+	if (err)
+		memset(jzpc->pull_downs, 0xff, sizeof(jzpc->pull_downs));
+
+	functions_node = of_find_node_by_name(dev->of_node, "functions");
+	if (!functions_node) {
+		dev_err(dev, "Missing \"functions\" devicetree node\n");
+		return -EINVAL;
+	}
+
+	pctl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctl_desc), GFP_KERNEL);
+	if (!pctl_desc)
+		return -ENOMEM;
+
+	/* fill in pinctrl_desc structure */
+	pctl_desc->name = dev_name(dev);
+	pctl_desc->owner = THIS_MODULE;
+	pctl_desc->pctlops = &ingenic_pctlops;
+	pctl_desc->pmxops = &ingenic_pmxops;
+	pctl_desc->confops = &ingenic_confops;
+	pctl_desc->npins = num_chips * PINS_PER_GPIO_CHIP;
+	pctl_desc->pins = jzpc->pdesc = devm_kzalloc(&pdev->dev,
+			sizeof(*jzpc->pdesc) * pctl_desc->npins, GFP_KERNEL);
+	if (!jzpc->pdesc)
+		return -ENOMEM;
+
+	for (i = 0; i < pctl_desc->npins; i++) {
+		jzpc->pdesc[i].number = i;
+		jzpc->pdesc[i].name = kasprintf(GFP_KERNEL, "P%c%d",
+						'A' + (i / PINS_PER_GPIO_CHIP),
+						i % PINS_PER_GPIO_CHIP);
+	}
+
+	jzpc->pctl = devm_pinctrl_register(dev, pctl_desc, jzpc);
+	if (!jzpc->pctl) {
+		dev_err(dev, "Failed pinctrl registration\n");
+		return -EINVAL;
+	}
+
+	for_each_child_of_node(functions_node, np) {
+		err = ingenic_pinctrl_parse_dt_func(jzpc, np);
+		if (err) {
+			dev_err(dev, "failed to parse function %s\n",
+					np->full_name);
+			continue;
+		}
+	}
+
+	return 0;
+}
+
+static struct platform_driver ingenic_pinctrl_driver = {
+	.driver = {
+		.name = "pinctrl-ingenic",
+		.of_match_table = of_match_ptr(ingenic_pinctrl_of_match),
+		.suppress_bind_attrs = true,
+	},
+	.probe = ingenic_pinctrl_probe,
+};
+
+static int __init ingenic_pinctrl_drv_register(void)
+{
+	return platform_driver_register(&ingenic_pinctrl_driver);
+}
+postcore_initcall(ingenic_pinctrl_drv_register);
-- 
2.11.0

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

^ permalink raw reply related

* [PATCH v2 02/14] Documentation: dt/bindings: Document pinctrl-gpio
From: Paul Cercueil @ 2017-01-22 14:49 UTC (permalink / raw)
  To: Linus Walleij, Rob Herring, Mark Rutland, Ralf Baechle,
	Ulf Hansson
  Cc: Boris Brezillon, Thierry Reding, Bartlomiej Zolnierkiewicz,
	Maarten ter Huurne, Lars-Peter Clausen, Paul Burton, linux-gpio,
	devicetree, linux-kernel, linux-mips, linux-mmc, linux-mtd,
	linux-pwm, linux-fbdev, james.hogan, Paul Cercueil
In-Reply-To: <20170122144947.16158-1-paul@crapouillou.net>

This commit adds documentation for the devicetree bidings of the
pinctrl-gpio driver, which handles GPIOs of the Ingenic SoCs
currently supported by the Linux kernel.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---
 .../devicetree/bindings/gpio/ingenic,gpio.txt      | 45 ++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/ingenic,gpio.txt

v2: New patch

diff --git a/Documentation/devicetree/bindings/gpio/ingenic,gpio.txt b/Documentation/devicetree/bindings/gpio/ingenic,gpio.txt
new file mode 100644
index 000000000000..b2eb20494365
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/ingenic,gpio.txt
@@ -0,0 +1,45 @@
+Ingenic jz47xx GPIO controller
+
+Required properties:
+  - compatible:
+    - "ingenic,jz4740-gpio" for the JZ4740 SoC
+    - "ingenic,jz4780-gpio" for the JZ4780 SoC
+
+  - reg: Base address and length of each memory resource used by the GPIO
+    controller hardware module.
+
+  - gpio-controller: Marks the device node as a GPIO controller.
+  - #gpio-cells: Should be 2. The first cell is the GPIO number and the second
+    cell specifies GPIO flags, as defined in <dt-bindings/gpio/gpio.h>. Only the
+    GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported.
+  - gpio-ranges: Range of pins managed by the GPIO controller.
+
+Optional properties:
+  - base: The GPIO number to use as the base for this driver.
+  - interrupt-controller: Marks the device node as an interrupt controller.
+  - interrupts: Interrupt specifier for the controllers interrupt.
+    Required if 'interrupt-controller' is specified.
+
+Please refer to gpio.txt in this directory for details of gpio-ranges property
+and the common GPIO bindings used by client devices.
+
+The GPIO controller also acts as an interrupt controller. It uses the default
+two cells specifier as described in Documentation/devicetree/bindings/
+interrupt-controller/interrupts.txt.
+
+Example:
+
+gpa: gpio-controller@10010000 {
+	compatible = "ingenic,jz4740-gpio";
+	reg = <0x10010000 0x100>;
+
+	gpio-controller;
+	gpio-ranges = <&pinctrl 0 0 32>;
+	#gpio-cells = <2>;
+
+	interrupt-controller;
+	#interrupt-cells = <2>;
+
+	interrupt-parent = <&intc>;
+	interrupts = <28>;
+};
-- 
2.11.0

^ permalink raw reply related

* [PATCH v2 01/14] Documentation: dt/bindings: Document pinctrl-ingenic
From: Paul Cercueil @ 2017-01-22 14:49 UTC (permalink / raw)
  To: Linus Walleij, Rob Herring, Mark Rutland, Ralf Baechle,
	Ulf Hansson
  Cc: Boris Brezillon, Thierry Reding, Bartlomiej Zolnierkiewicz,
	Maarten ter Huurne, Lars-Peter Clausen, Paul Burton, linux-gpio,
	devicetree, linux-kernel, linux-mips, linux-mmc, linux-mtd,
	linux-pwm, linux-fbdev, james.hogan, Paul Cercueil
In-Reply-To: <20170122144947.16158-1-paul@crapouillou.net>

This commit adds documentation for the devicetree bidings of the
pinctrl-ingenic driver, which handles pin configuration and pin
muxing of the Ingenic SoCs currently supported by the Linux kernel.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---
 .../bindings/pinctrl/ingenic,pinctrl.txt           | 77 ++++++++++++++++++++++
 1 file changed, 77 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt

v2: Rewrote the documentation for the new pinctrl-ingenic driver

diff --git a/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt
new file mode 100644
index 000000000000..ead5b01ad471
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt
@@ -0,0 +1,77 @@
+Ingenic jz47xx pin controller
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+For the jz47xx SoCs, pin control is tightly bound with GPIO ports. All pins may
+be used as GPIOs, multiplexed device functions are configured within the
+GPIO port configuration registers and it is typical to refer to pins using the
+naming scheme "PxN" where x is a character identifying the GPIO port with
+which the pin is associated and N is an integer from 0 to 31 identifying the
+pin within that GPIO port. For example PA0 is the first pin in GPIO port A, and
+PB31 is the last pin in GPIO port B. The jz4740 contains 4 GPIO ports, PA to
+PD, for a total of 128 pins. The jz4780 contains 6 GPIO ports, PA to PF, for a
+total of 192 pins.
+
+
+Pin controller node
+===================
+
+Required properties:
+- compatible: One of:
+  - "ingenic,jz4740-pinctrl"
+  - "ingenic,jz4780-pinctrl"
+
+Optional properties:
+- ingenic,pull-ups: A list of 32-bit bit fields, where each bit set tells the
+  driver that a pull-up resistor is available for this pin.
+  By default, the driver considers that all pins feature a pull-up resistor.
+- ingenic,pull-downs: A list of 32-bit bit fields, where each bit set tells
+  the driver that a pull-down resistor is available for this pin.
+  By default, the driver considers that all pins feature a pull-down
+  resistor.
+
+
+'functions' sub-node
+====================
+
+The 'functions' node will contain sub-nodes that correspond to pin function
+nodes, and no properties. Pin function nodes will contain sub-nodes that
+correspond to pin groups, and no properties.
+
+The names of the pin function nodes will end up being the available functions
+provided by the pinctrl driver.
+The names of the pin group nodes will end up being the available groups
+provided by the pinctrl driver.
+
+Required properties for pin groups:
+- ingenic,pins: <pin mode [pin mode ...]>;
+  where 'pin' is the number of the pin, and 'mode' is the function mode of the
+  pin that should be enabled for this group.
+
+
+Example:
+=======
+
+pinctrl: ingenic-pinctrl@10010000 {
+	compatible = "ingenic,jz4740-pinctrl";
+	reg = <0x10010000 0x400>;
+
+	ingenic,pull-ups   = <0xffffffff 0xffffffff 0xffffffff 0xdfffffff>;
+	ingenic,pull-downs = <0x00000000 0x00000000 0x00000000 0x00000000>;
+
+	functions {
+		mmc {
+			mmc-1bit {
+				/* CLK, CMD, D0 */
+				ingenic,pins = <0x69 0 0x68 0 0x6a 0>;
+			};
+
+			mmc-4bit {
+				/* D1, D2, D3 */
+				ingenic,pins = <0x6b 0 0x6c 0 0x6d 0>;
+			};
+		};
+	};
+};
-- 
2.11.0

^ permalink raw reply related

* [PATCH v2 00/14] Ingenic JZ4740 / JZ4780 pinctrl driver
From: Paul Cercueil @ 2017-01-22 14:49 UTC (permalink / raw)
  To: Linus Walleij, Rob Herring, Mark Rutland, Ralf Baechle,
	Ulf Hansson
  Cc: Boris Brezillon, Thierry Reding, Bartlomiej Zolnierkiewicz,
	Maarten ter Huurne, Lars-Peter Clausen, Paul Burton, linux-gpio,
	devicetree, linux-kernel, linux-mips, linux-mmc, linux-mtd,
	linux-pwm, linux-fbdev, james.hogan
In-Reply-To: <27071da2f01d48141e8ac3dfaa13255d@mail.crapouillou.net>

Hi,

This is the v2 of my ingenic pinctrl patch series.
Huge changes in there, the pinctrl driver was completely rewritten, and the
GPIO code was split into a separate driver.

It now uses the generic functions to handle pin groups, as well as generic
devicetree bindings.

Best regards,
- Paul


^ permalink raw reply

* [PATCH 5/5] ARM: tegra: nyan-blaze: Proper pinmux for TPM i2c
From: Paul Kocialkowski @ 2017-01-22 14:44 UTC (permalink / raw)
  To: linux-tegra
  Cc: devicetree, Mikko Perttunen, Stephen Warren, Jerome Coste,
	linux-kernel, Jon Hunter, Thierry Reding, Peter Rosin,
	Alexandre Courbot, linux-arm-kernel
In-Reply-To: <20170122144415.31208-1-contact@paulk.fr>

From: Jerome Coste <jerome.coste@etu.utc.fr>

This corrects the pinmux for accessing the TPM over the i2c line.
Thus, it allows correctly probing the module, that previously failed with i2c
errors.
---
 arch/arm/boot/dts/tegra124-nyan-blaze.dts | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/arm/boot/dts/tegra124-nyan-blaze.dts b/arch/arm/boot/dts/tegra124-nyan-blaze.dts
index c9582361c26e..8bb97e1baf90 100644
--- a/arch/arm/boot/dts/tegra124-nyan-blaze.dts
+++ b/arch/arm/boot/dts/tegra124-nyan-blaze.dts
@@ -1094,19 +1094,19 @@
 			};
 			cam_i2c_scl_pbb1 {
 				nvidia,pins = "cam_i2c_scl_pbb1";
-				nvidia,function = "rsvd3";
-				nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
-				nvidia,tristate = <TEGRA_PIN_ENABLE>;
-				nvidia,enable-input = <TEGRA_PIN_DISABLE>;
-				nvidia,open-drain = <TEGRA_PIN_DISABLE>;
+				nvidia,function = "i2c3";
+				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
+				nvidia,tristate = <TEGRA_PIN_DISABLE>;
+				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
+				nvidia,open-drain = <TEGRA_PIN_ENABLE>;
 			};
 			cam_i2c_sda_pbb2 {
 				nvidia,pins = "cam_i2c_sda_pbb2";
-				nvidia,function = "rsvd3";
-				nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
-				nvidia,tristate = <TEGRA_PIN_ENABLE>;
-				nvidia,enable-input = <TEGRA_PIN_DISABLE>;
-				nvidia,open-drain = <TEGRA_PIN_DISABLE>;
+				nvidia,function = "i2c3";
+				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
+				nvidia,tristate = <TEGRA_PIN_DISABLE>;
+				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
+				nvidia,open-drain = <TEGRA_PIN_ENABLE>;
 			};
 			pbb3 {
 				nvidia,pins = "pbb3";
-- 
2.11.0

^ permalink raw reply related

* [PATCH 4/5] ARM: tegra: nyan-big: Proper pinmux for TPM i2c
From: Paul Kocialkowski @ 2017-01-22 14:44 UTC (permalink / raw)
  To: linux-tegra
  Cc: devicetree, Mikko Perttunen, Stephen Warren, Paul Kocialkowski,
	linux-kernel, Jon Hunter, Thierry Reding, Peter Rosin,
	Alexandre Courbot, linux-arm-kernel
In-Reply-To: <20170122144415.31208-1-contact@paulk.fr>

This corrects the pinmux for accessing the TPM over the i2c line.
Thus, it allows correctly probing the module, that previously failed with i2c
errors.

Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
---
 arch/arm/boot/dts/tegra124-nyan-big.dts | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/arm/boot/dts/tegra124-nyan-big.dts b/arch/arm/boot/dts/tegra124-nyan-big.dts
index f12ece9b76a6..03eedf86dc48 100644
--- a/arch/arm/boot/dts/tegra124-nyan-big.dts
+++ b/arch/arm/boot/dts/tegra124-nyan-big.dts
@@ -1103,19 +1103,19 @@
 			};
 			cam_i2c_scl_pbb1 {
 				nvidia,pins = "cam_i2c_scl_pbb1";
-				nvidia,function = "rsvd3";
-				nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
-				nvidia,tristate = <TEGRA_PIN_ENABLE>;
-				nvidia,enable-input = <TEGRA_PIN_DISABLE>;
-				nvidia,open-drain = <TEGRA_PIN_DISABLE>;
+				nvidia,function = "i2c3";
+				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
+				nvidia,tristate = <TEGRA_PIN_DISABLE>;
+				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
+				nvidia,open-drain = <TEGRA_PIN_ENABLE>;
 			};
 			cam_i2c_sda_pbb2 {
 				nvidia,pins = "cam_i2c_sda_pbb2";
-				nvidia,function = "rsvd3";
-				nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
-				nvidia,tristate = <TEGRA_PIN_ENABLE>;
-				nvidia,enable-input = <TEGRA_PIN_DISABLE>;
-				nvidia,open-drain = <TEGRA_PIN_DISABLE>;
+				nvidia,function = "i2c3";
+				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
+				nvidia,tristate = <TEGRA_PIN_DISABLE>;
+				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
+				nvidia,open-drain = <TEGRA_PIN_ENABLE>;
 			};
 			pbb3 {
 				nvidia,pins = "pbb3";
-- 
2.11.0

^ permalink raw reply related

* Re: [PATCH 4/6] rtc: sun6i: Force the mux to the external oscillator
From: Alexandre Belloni @ 2017-01-22 14:44 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Maxime Ripard, Alessandro Zummo, linux-arm-kernel,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, Rob Herring, devicetree
In-Reply-To: <CAGb2v650f-4RZ6RrRBGcmPz8mJ4KbJwTTj6=w3HeSWUZtLAKcw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On 21/01/2017 at 11:53:08 +0800, Chen-Yu Tsai wrote :
> Hi,
> 
> On Fri, Jan 20, 2017 at 11:56 PM, Maxime Ripard
> <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:
> > The internal oscillator is way too inaccurate to do something useful with
> > it. Switch to the external oscillator if it is available.
> >
> > Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > ---
> >  drivers/rtc/rtc-sun6i.c | 12 ++++++++++++
> >  1 file changed, 12 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
> > index edd5627da10f..1695fae24cd5 100644
> > --- a/drivers/rtc/rtc-sun6i.c
> > +++ b/drivers/rtc/rtc-sun6i.c
> > @@ -493,6 +493,7 @@ static const struct rtc_class_ops sun6i_rtc_ops = {
> >  static int sun6i_rtc_probe(struct platform_device *pdev)
> >  {
> >         struct sun6i_rtc_dev *chip = sun6i_rtc;
> > +       struct clk *parent;
> >         int ret;
> >
> >         if (!chip)
> > @@ -540,6 +541,17 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
> >         /* disable alarm wakeup */
> >         writel(0, chip->base + SUN6I_ALARM_CONFIG);
> >
> > +       parent = clk_get(&pdev->dev, NULL);
> > +       if (!IS_ERR(parent)) {
> > +               ret = clk_set_parent(chip->losc, parent);
> > +               clk_put(parent);
> > +
> > +               if (ret) {
> > +                       dev_err(&pdev->dev,
> > +                               "Failed to reparent the RTC to the external oscillator\n");
> > +                       return ret;
> > +               }
> > +       }
> 
> Following what I mentioned in patch 1, maybe it is easier to force
> the mux before the clocks are registered by writing directly to
> the registers? We could also backport the changes to stable?
> 

I'd say that the risk to break existing platforms is low enough to
backport that patch to stable.
Maxime as you are certainly the one that will handle the potential
breakage, I'll let you choose what you want to do.


-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply

* [PATCH 3/5] ARM: tegra: nyan-big: Include compatible revisions for proper detection
From: Paul Kocialkowski @ 2017-01-22 14:44 UTC (permalink / raw)
  To: linux-tegra
  Cc: devicetree, Mikko Perttunen, Stephen Warren, Paul Kocialkowski,
	linux-kernel, Jon Hunter, Thierry Reding, Peter Rosin,
	Alexandre Courbot, linux-arm-kernel
In-Reply-To: <20170122144415.31208-1-contact@paulk.fr>

Depthcharge (the payload used with cros devices) will attempt to detect
boards using their revision. This includes all the known revisions for
the nyan-big board so that the dtb can be selected preferably.

Defining compatibly revisions allows depthcharge to select the kernel
via the revision it detects instead of using the default kernel. This
allows having a FIT image with multiple kernels for multiple devices.

Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
---
 arch/arm/boot/dts/tegra124-nyan-big.dts | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/tegra124-nyan-big.dts b/arch/arm/boot/dts/tegra124-nyan-big.dts
index 67d7cfb32541..f12ece9b76a6 100644
--- a/arch/arm/boot/dts/tegra124-nyan-big.dts
+++ b/arch/arm/boot/dts/tegra124-nyan-big.dts
@@ -6,7 +6,12 @@
 
 / {
 	model = "Acer Chromebook 13 CB5-311";
-	compatible = "google,nyan-big", "nvidia,tegra124";
+	compatible = "google,nyan-big-rev7", "google,nyan-big-rev6",
+			"google,nyan-big-rev5", "google,nyan-big-rev4",
+			"google,nyan-big-rev3", "google,nyan-big-rev2",
+			"google,nyan-big-rev5", "google,nyan-big-rev4",
+			"google,nyan-big-rev1", "google,nyan-big-rev0",
+			"google,nyan-big", "google,nyan", "nvidia,tegra124";
 
 	panel: panel {
 		compatible = "auo,b133xtn01";
-- 
2.11.0

^ permalink raw reply related

* [PATCH 2/5] ARM: tegra: nyan: Use external control for bq24735 charger
From: Paul Kocialkowski @ 2017-01-22 14:44 UTC (permalink / raw)
  To: linux-tegra
  Cc: devicetree, Mikko Perttunen, Stephen Warren, Paul Kocialkowski,
	linux-kernel, Jon Hunter, Thierry Reding, Peter Rosin,
	Alexandre Courbot, linux-arm-kernel
In-Reply-To: <20170122144415.31208-1-contact@paulk.fr>

Nyan boards come with an embedded controller that controls when to
enable and disable the charge. Thus, it should not be left up to the
kernel to handle that.

Using the ti,external-control property allows specifying this use-case.

Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
---
 arch/arm/boot/dts/tegra124-nyan.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/tegra124-nyan.dtsi b/arch/arm/boot/dts/tegra124-nyan.dtsi
index ef430aa30152..5cf987b5401e 100644
--- a/arch/arm/boot/dts/tegra124-nyan.dtsi
+++ b/arch/arm/boot/dts/tegra124-nyan.dtsi
@@ -335,6 +335,7 @@
 					ti,ac-detect-gpios = <&gpio
 							TEGRA_GPIO(J, 0)
 							GPIO_ACTIVE_HIGH>;
+					ti,external-control;
 				};
 
 				battery: sbs-battery@b {
-- 
2.11.0

^ permalink raw reply related

* [PATCH 1/5] ARM: tegra: nyan: Use proper IRQ type definitions
From: Paul Kocialkowski @ 2017-01-22 14:44 UTC (permalink / raw)
  To: linux-tegra
  Cc: devicetree, Mikko Perttunen, Stephen Warren, Paul Kocialkowski,
	linux-kernel, Jon Hunter, Thierry Reding, Peter Rosin,
	Alexandre Courbot, linux-arm-kernel

This switches a few interrupt definitions that were using
GPIO_ACTIVE_HIGH as IRQ type, which is invalid.

This is mostly a cosmetic change, that doesn't affect any driver.

Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
---
 arch/arm/boot/dts/tegra124-nyan.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/tegra124-nyan.dtsi b/arch/arm/boot/dts/tegra124-nyan.dtsi
index eabfa655a3cd..ef430aa30152 100644
--- a/arch/arm/boot/dts/tegra124-nyan.dtsi
+++ b/arch/arm/boot/dts/tegra124-nyan.dtsi
@@ -65,7 +65,7 @@
 			compatible = "maxim,max98090";
 			reg = <0x10>;
 			interrupt-parent = <&gpio>;
-			interrupts = <TEGRA_GPIO(H, 4) GPIO_ACTIVE_HIGH>;
+			interrupts = <TEGRA_GPIO(H, 4) IRQ_TYPE_EDGE_FALLING>;
 		};
 
 		temperature-sensor@4c {
@@ -331,7 +331,7 @@
 					reg = <0x9>;
 					interrupt-parent = <&gpio>;
 					interrupts = <TEGRA_GPIO(J, 0)
-							GPIO_ACTIVE_HIGH>;
+							IRQ_TYPE_EDGE_BOTH>;
 					ti,ac-detect-gpios = <&gpio
 							TEGRA_GPIO(J, 0)
 							GPIO_ACTIVE_HIGH>;
-- 
2.11.0

^ permalink raw reply related

* Re: [PATCH 09/22] mtd: add DT bindings for the Cortina Systems Gemini Flash
From: Marek Vasut @ 2017-01-22 14:40 UTC (permalink / raw)
  To: Linus Walleij, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Hans Ulli Kroll, Florian Fainelli, David Woodhouse, Brian Norris,
	Boris Brezillon, Richard Weinberger, Cyrille Pitchen
  Cc: Janos Laube, Paulius Zaleckas,
	openwrt-devel-p3rKhJxN3npAfugRpC6u6w, Arnd Bergmann,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170122122119.10510-1-linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

On 01/22/2017 01:21 PM, Linus Walleij wrote:
> This adds device tree bindings for the Cortina systems Gemini
> flash controller, a simple physmap which however need a few
> syscon bits to be poked to operate properly.
> 
> Cc: Janos Laube <janos.dev-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Cc: Paulius Zaleckas <paulius.zaleckas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Cc: Hans Ulli Kroll <ulli.kroll-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
> Cc: Florian Fainelli <f.fainelli-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Signed-off-by: Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Looks pretty OK to me:
Reviewed-by: Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

> ---
> MTD maintainers: please just apply this to the MTD tree when
> you are happy with it. It is functionally orthogonal to the rest
> of the series and is just in the series for context.
> ---
>  .../bindings/mtd/cortina,gemini-flash.txt          | 24 ++++++++++++++++++++++
>  1 file changed, 24 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mtd/cortina,gemini-flash.txt
> 
> diff --git a/Documentation/devicetree/bindings/mtd/cortina,gemini-flash.txt b/Documentation/devicetree/bindings/mtd/cortina,gemini-flash.txt
> new file mode 100644
> index 000000000000..3fa1b34d69ad
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/cortina,gemini-flash.txt
> @@ -0,0 +1,24 @@
> +Flash device on Cortina Systems Gemini SoC
> +
> +This flash is regular CFI compatible (Intel or AMD extended) flash chips with
> +some special bits that can be controlled by the machine's system controller.
> +
> +Required properties:
> +- compatible : must be "cortina,gemini-flash", "cfi-flash";
> +- reg : memory address for the flash chip
> +- syscon : must be a phandle to the system controller
> +- bank-width : width in bytes of flash interface, should be <2>
> +
> +For the rest of the properties, see mtd-physmap.txt.
> +
> +The device tree may optionally contain sub-nodes describing partitions of the
> +address space. See partition.txt for more detail.
> +
> +Example:
> +
> +flash@30000000 {
> +	compatible = "cortina,gemini-flash", "cfi-flash";
> +	reg = <0x30000000 0x01000000>;
> +	syscon = <&syscon>;
> +	bank-width = <2>;
> +};
> 


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

^ permalink raw reply

* Re: [PATCH 2/2] Documentation: dt: iio: imu: st_lsm6dsx: add st,drdy-int-pin property
From: Jonathan Cameron @ 2017-01-22 14:35 UTC (permalink / raw)
  To: Lorenzo Bianconi
  Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.bianconi-qxv4g6HH51o
In-Reply-To: <20170122103052.8769-3-lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>

On 22/01/17 10:30, Lorenzo Bianconi wrote:
> Add st,drdy-int-pin property to select interrupt pin of the package
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
Looks good and lines up with existing st-sensors binding.
> ---
>  Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt
> index ed3cdac..cf81afd 100644
> --- a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt
> +++ b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt
> @@ -7,6 +7,8 @@ Required properties:
>  - reg: i2c address of the sensor / spi cs line
>  
>  Optional properties:
> +- st,drdy-int-pin: the pin on the package that will be used to signal
> +  "data ready" (valid values: 1 or 2).
>  - interrupt-parent: should be the phandle for the interrupt controller
>  - interrupts: interrupt mapping for IRQ. It should be configured with
>    flags IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_EDGE_RISING.
> 

--
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 1/2] iio: imu: st_lsm6dsx: add possibility to select drdy pin
From: Jonathan Cameron @ 2017-01-22 14:34 UTC (permalink / raw)
  To: Lorenzo Bianconi
  Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.bianconi-qxv4g6HH51o
In-Reply-To: <20170122103052.8769-2-lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>

On 22/01/17 10:30, Lorenzo Bianconi wrote:
> Add capability to route data ready signal on pin 1 or pin 2 of the package
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
> ---
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 66 +++++++++++++++++++++++++---
>  1 file changed, 60 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> index f869dfa..bc50164 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> @@ -37,11 +37,14 @@
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
>  
> +#include <linux/platform_data/st_sensors_pdata.h>
> +
>  #include "st_lsm6dsx.h"
>  
>  #define ST_LSM6DSX_REG_ACC_DEC_MASK		GENMASK(2, 0)
>  #define ST_LSM6DSX_REG_GYRO_DEC_MASK		GENMASK(5, 3)
>  #define ST_LSM6DSX_REG_INT1_ADDR		0x0d
> +#define ST_LSM6DSX_REG_INT2_ADDR		0x0e
>  #define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK	BIT(3)
>  #define ST_LSM6DSX_REG_WHOAMI_ADDR		0x0f
>  #define ST_LSM6DSX_REG_RESET_ADDR		0x12
> @@ -532,10 +535,63 @@ static const struct iio_info st_lsm6dsx_gyro_info = {
>  
>  static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
>  
> +#ifdef CONFIG_OF
Perhaps rely on the -ENOSYS return from
of_property_read_u32 stub instead of having the ifdefs around something so
simple.

> +static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
> +{
> +	struct device_node *np = hw->dev->of_node;
> +	int err;
> +
> +	if (!np)
> +		return -EINVAL;
> +
> +	err = of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
> +	if (err == -ENODATA) {
> +		/* if the property has not been specified use default value */
> +		*drdy_pin = 1;
> +		err = 0;
> +	}
> +
> +	return err;
> +}
> +#else
> +static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
> +{
> +	return -EINVAL;
> +}
> +#endif /* CONFIG_OF */
> +
> +static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
> +{
> +	int err = 0, drdy_pin;
> +
> +	if (st_lsm6dsx_of_get_drdy_pin(hw, &drdy_pin) < 0) {
> +		struct st_sensors_platform_data *pdata;
> +		struct device *dev = hw->dev;
> +
> +		pdata = (struct st_sensors_platform_data *)dev->platform_data;
> +		drdy_pin = pdata ? pdata->drdy_int_pin : 1;
> +	}
> +
> +	switch (drdy_pin) {
> +	case 1:
> +		*drdy_reg = ST_LSM6DSX_REG_INT1_ADDR;
> +		break;
> +	case 2:
> +		*drdy_reg = ST_LSM6DSX_REG_INT2_ADDR;
> +		break;
> +	default:
> +		dev_err(hw->dev, "unsupported data ready pin\n");
> +		err = -EINVAL;
> +		break;
> +	}
> +
> +	return err;
> +}
> +
>  static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
>  {
> +	u8 data, drdy_int_reg;
>  	int err;
> -	u8 data;
>  
>  	data = ST_LSM6DSX_REG_RESET_MASK;
>  	err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_RESET_ADDR, sizeof(data),
> @@ -563,14 +619,12 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
>  		return err;
>  
>  	/* enable FIFO watermak interrupt */
> -	err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_INT1_ADDR,
> -					 ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, 1);
> +	err = st_lsm6dsx_get_drdy_reg(hw, &drdy_int_reg);
>  	if (err < 0)
>  		return err;
>  
> -	/* redirect INT2 on INT1 */
> -	return st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_INT2_ON_INT1_ADDR,
> -					  ST_LSM6DSX_REG_INT2_ON_INT1_MASK, 1);
Does removing this potentially break existing setups?

> +	return st_lsm6dsx_write_with_mask(hw, drdy_int_reg,
> +					  ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, 1);
>  }
>  
>  static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
> 

--
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 0/6] rtc: sun6i: Fix the RTC accuracy
From: Alexandre Belloni @ 2017-01-22 14:32 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Alessandro Zummo, Chen-Yu Tsai,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.180d5ae6bc36ead9625d208824f5cbd7114cf92e.1484927680.git-series.maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

On 20/01/2017 at 16:56:37 +0100, Maxime Ripard wrote :
> Hi,
> 
> The RTC used in the A31 and later SoC has an accuracy issue, which is
> already significant even after a couple of hours.
> 
> This is due to the fact that the oscillator used by default is an internal
> and very inaccurate one.
> 
> A first attempt at fixing that by switching to the external oscillator was
> done in the patch "rtc: sun6i: Switch to the external oscillator". However,
> it turned out to be problematic since it was tracked properly in the clock
> framework, which might lead to some clocks being disabled, even though
> their devices were not notified.
> 
> This is a second attempt, this time by making it part of the CCF. It
> turned out to be a bit more complicated than one would expect since the mux
> found inside the RTC also controls one of the input of the main clock unit.
> Therefore, it needs to be probed before the main clock unit driver.
> 
> Let me know what you think,
> Maxime
> 
> Maxime Ripard (6):
>   rtc: sun6i: Expose the 32kHz oscillator
>   rtc: sun6i: Add some locking
>   rtc: sun6i: Disable the build as a module
>   rtc: sun6i: Force the mux to the external oscillator
>   ARM: sun8i: a23/a33: Enable the real LOSC and use it
>   ARM: sun8i: a23/a33: Add the oscillators accuracy
> 

As Chen-Yu mentioned, maybe you can reorder some patches so they can go
in stable.

>  Documentation/devicetree/bindings/rtc/sun6i-rtc.txt |   8 +-
>  arch/arm/boot/dts/sun8i-a23-a33.dtsi                |  15 +-
>  drivers/rtc/Kconfig                                 |   2 +-
>  drivers/rtc/rtc-sun6i.c                             | 189 +++++++++++--
>  4 files changed, 181 insertions(+), 33 deletions(-)
> 
> base-commit: 99cef370ac9939df2aeb16c96d07e842b2fa8201
> -- 
> git-series 0.8.11

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply

* Re: [PATCH 1/6] rtc: sun6i: Expose the 32kHz oscillator
From: Alexandre Belloni @ 2017-01-22 14:17 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Alessandro Zummo, Chen-Yu Tsai,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <f1b2a5053cf143dccf110ab0d40c082903a958a7.1484927680.git-series.maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

Hi,

On 20/01/2017 at 16:56:38 +0100, Maxime Ripard wrote :
> +
> +	rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
> +	if (!rtc)
> +		pr_crit("Can't allocate RTC structure\n");
> +

The message is unnecessary but you probably want to stop there and
return.

> +	rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node));
> +	if (!rtc->base) {
> +		pr_crit("Can't map RTC registers");
> +		return;
> +	}
> +
> +	rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL,
> +								"rtc-int-osc",
> +								NULL, 0,
> +								667000,
> +								300000000);
> +	if (IS_ERR(rtc->int_osc)) {
> +		pr_crit("Couldn't register the internal oscillator\n");
> +		return;
> +	}
> +
> +	/*
> +	 * Due to the missing clocks property we had to express the
> +	 * parenthood with the external oscillator that cannot fix (or
> +	 * at least expect to have) in the old DTs, we have to be a
> +	 * bit smart here.
> +	 *
> +	 * We deal with that by simply registering the internal
> +	 * oscillator as our parent in all cases, and try to get the
> +	 * external oscillator from the DT.
> +	 *
> +	 * In the case where we don't have it, of_clk_get_parent_name
> +	 * will return NULL, which is ok, and of_clk_get_parent_count
> +	 * will return 0, which is fine too. We will just register a
> +	 * single parent, everything works.
> +	 */
> +	parents[0] = clk_hw_get_name(rtc->int_osc);
> +	parents[1] = of_clk_get_parent_name(node, 0);
> +
> +	rtc->hw.init = &init;
> +
> +	init.parent_names = parents;
> +	init.num_parents = of_clk_get_parent_count(node) + 1;
> +	init.name = "rtc-osc";
> +	of_property_read_string(node, "clock-output-names", &init.name);
> +
> +	rtc->losc = clk_register(NULL, &rtc->hw);
> +	if (IS_ERR(rtc->losc)) {
> +		pr_crit("Couldn't register the LOSC clock\n");
> +		return;
> +	}
> +
> +	of_clk_add_hw_provider(node, of_clk_hw_simple_get, &rtc->hw);
> +
> +	/* Yes, I know, this is ugly. */
> +	sun6i_rtc = rtc;
> +}
> +CLK_OF_DECLARE_DRIVER(sun6i_rtc_clk, "allwinner,sun6i-a31-rtc",
> +		      sun6i_rtc_clk_init);
> +
>  static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
>  {
>  	struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
> @@ -349,22 +476,15 @@ static const struct rtc_class_ops sun6i_rtc_ops = {
>  
>  static int sun6i_rtc_probe(struct platform_device *pdev)
>  {
> -	struct sun6i_rtc_dev *chip;
> -	struct resource *res;
> +	struct sun6i_rtc_dev *chip = sun6i_rtc;
>  	int ret;
>  
> -	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
>  	if (!chip)
> -		return -ENOMEM;
> +		return -ENODEV;
>  
>  	platform_set_drvdata(pdev, chip);
>  	chip->dev = &pdev->dev;
>  
> -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -	chip->base = devm_ioremap_resource(&pdev->dev, res);
> -	if (IS_ERR(chip->base))
> -		return PTR_ERR(chip->base);
> -
>  	chip->irq = platform_get_irq(pdev, 0);
>  	if (chip->irq < 0) {
>  		dev_err(&pdev->dev, "No IRQ resource\n");
> @@ -404,6 +524,8 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
>  	/* disable alarm wakeup */
>  	writel(0, chip->base + SUN6I_ALARM_CONFIG);
>  
> +	clk_prepare_enable(chip->losc);
> +
>  	chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
>  					&sun6i_rtc_ops, THIS_MODULE);
>  	if (IS_ERR(chip->rtc)) {
> -- 
> git-series 0.8.11

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply

* RE: [PATCH v2 2/3] ARM: dts: r7s72100: add ostm to device tree
From: Chris Brandt @ 2017-01-22 14:13 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Simon Horman, Magnus Damm, Rob Herring, Mark Rutland,
	Russell King, Thomas Gleixner, Geert Uytterhoeven,
	devicetree@vger.kernel.org, Linux-Renesas
In-Reply-To: <CAMuHMdXQCqs4bCR8o+BC5ak0rE64bQOEz65udFntcQSRkU6+cw@mail.gmail.com>

Hello Geert,

On Sunday, January 22, 2017, Geert Uytterhoeven wrote:
> > diff --git a/arch/arm/boot/dts/r7s72100.dtsi
> > b/arch/arm/boot/dts/r7s72100.dtsi index d5946df..47b2165 100644
> > --- a/arch/arm/boot/dts/r7s72100.dtsi
> > +++ b/arch/arm/boot/dts/r7s72100.dtsi
> > @@ -505,4 +505,20 @@
> >                 cap-sdio-irq;
> >                 status = "disabled";
> >         };
> > +
> > +       ostm0: ostm@fcfec000 {
> > +               compatible = "renesas,r7s72100-ostm", "renesas,ostm";
> > +               reg = <0xfcfec000 0x30>;
> > +               interrupts = <GIC_SPI 102 IRQ_TYPE_EDGE_RISING>;
> > +               clocks = <&mstp5_clks R7S72100_CLK_OSTM0>;
> 
> Missing power-domains property:
> 
>         power-domains = <&cpg_clocks>;
> 
> > +               status = "disabled";
> > +       };
> > +
> > +       ostm1: ostm@fcfec400 {
> > +               compatible = "renesas,r7s72100-ostm", "renesas,ostm";
> > +               reg = <0xfcfec400 0x30>;
> > +               interrupts = <GIC_SPI 103 IRQ_TYPE_EDGE_RISING>;
> > +               clocks = <&mstp5_clks R7S72100_CLK_OSTM1>;
> 
> Likewise.

OK. Thank you.


> BTW, they're also missing for the last added device nodes (MMC and SDHI).

OK, I will submit patches for them as well.

Thank you for the review.

Chris


^ permalink raw reply

* RE: [PATCH v2 2/3] ARM: dts: r7s72100: add ostm to device tree
From: Chris Brandt @ 2017-01-22 14:08 UTC (permalink / raw)
  To: Sergei Shtylyov, Simon Horman, Magnus Damm, Rob Herring,
	Mark Rutland, Russell King, Thomas Gleixner, Geert Uytterhoeven
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <4db44120-aa9f-2cb4-aad7-2baa8fcfe7c0-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 983 bytes --]

Hello Sergei,

On Saturday, January 21, 2017, Sergei Shtylyov wrote:
> > diff --git a/arch/arm/boot/dts/r7s72100.dtsi
> > b/arch/arm/boot/dts/r7s72100.dtsi index d5946df..47b2165 100644
> > --- a/arch/arm/boot/dts/r7s72100.dtsi
> > +++ b/arch/arm/boot/dts/r7s72100.dtsi
> > @@ -505,4 +505,20 @@
> >  		cap-sdio-irq;
> >  		status = "disabled";
> >  	};
> > +
> > +	ostm0: ostm@fcfec000 {
> 
>     According to devicetree.org, the node name should be generic, i.e.
> "timer@...".
> 
> > +		compatible = "renesas,r7s72100-ostm", "renesas,ostm";
> > +		reg = <0xfcfec000 0x30>;
> > +		interrupts = <GIC_SPI 102 IRQ_TYPE_EDGE_RISING>;
> > +		clocks = <&mstp5_clks R7S72100_CLK_OSTM0>;
> > +		status = "disabled";
> > +	};
> > +
> > +	ostm1: ostm@fcfec400 {
> 
>     Likewise.
> 

OK. I will change it.
Thank you!

Chris
N‹§²æìr¸›yúèšØb²X¬¶Ç§vØ^–)Þº{.nÇ+‰·zøœzÚÞz)í…æèw*\x1fjg¬±¨\x1e¶‰šŽŠÝ¢j.ïÛ°\½½MŽúgjÌæa×\x02››–' ™©Þ¢¸\f¢·¦j:+v‰¨ŠwèjØm¶Ÿÿ¾\a«‘êçzZ+ƒùšŽŠÝ¢j"ú!¶i

^ permalink raw reply

* Re: [PATCH v3 2/3] iio: distance: srf08: add IIO driver for us ranger
From: Jonathan Cameron @ 2017-01-22 13:46 UTC (permalink / raw)
  To: Andreas Klinger, knaack.h-Mmb7MZpHnFY,
	lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, ktsai-GubuWUlQtMwciDkP5Hr2oA,
	wsa-z923LK4zBo2bacvFa/9K2g, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, trivial-DgEjT+Ai2ygdnm+yROfE0A,
	mranostay-Re5JQEeQqe8AvxtiuMwx3w,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170117134938.GA22892@andreas>

On 17/01/17 13:49, Andreas Klinger wrote:
> This is the IIO driver for devantech srf08 ultrasonic ranger which can be
> used to measure the distances to an object.
> 
> The sensor supports I2C with some registers.
> 
> Supported Features include:
>  - read the distance in ranging mode in centimeters
>  - output of the driver is directly the read value
>  - together with the scale the driver delivers the distance in meters
>  - only the first echo of the nearest object is delivered
>  - set sensitivity as analog value in the range of 0-31  means setting 
>    gain register on device
>  - set range registers; userspace enters max. range in millimeters in 
>    43 mm steps
> 
> Features not supported by this driver:
>  - ranging mode in inches or in microseconds
>  - ANN mode
>  - change I2C address through this driver
>  - light sensor
> 
> The driver was added in the directory "proximity" of the iio subsystem and
> the menu in den config is now called "Proximity and distance sensors"
> 
> Signed-off-by: Andreas Klinger <ak-n176/SwNRljddJNmlsFzeA@public.gmane.org>
One suggestion inline. Otherwise, just the units of your new max_range
attribute as per comment on ABI docs.

Looking good.

Jonathan
> ---
>  drivers/iio/proximity/Kconfig  |  13 +-
>  drivers/iio/proximity/Makefile |   1 +
>  drivers/iio/proximity/srf08.c  | 405 +++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 418 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/iio/proximity/srf08.c
> 
> diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
> index ef4c73db5b53..ab96cb7a0054 100644
> --- a/drivers/iio/proximity/Kconfig
> +++ b/drivers/iio/proximity/Kconfig
> @@ -18,7 +18,7 @@ config AS3935
>  
>  endmenu
>  
> -menu "Proximity sensors"
> +menu "Proximity and distance sensors"
>  
>  config LIDAR_LITE_V2
>  	tristate "PulsedLight LIDAR sensor"
> @@ -45,4 +45,15 @@ config SX9500
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called sx9500.
>  
> +config SRF08
> +	tristate "Devantech SRF08 ultrasonic ranger sensor"
> +	depends on I2C
> +	help
> +	  Say Y here to build a driver for Devantech SRF08 ultrasonic
> +	  ranger sensor. This driver can be used to measure the distance
> +	  of objects.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called srf08.
> +
>  endmenu
> diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
> index 9aadd9a8ee99..e914c2a5dd49 100644
> --- a/drivers/iio/proximity/Makefile
> +++ b/drivers/iio/proximity/Makefile
> @@ -5,4 +5,5 @@
>  # When adding new entries keep the list in alphabetical order
>  obj-$(CONFIG_AS3935)		+= as3935.o
>  obj-$(CONFIG_LIDAR_LITE_V2)	+= pulsedlight-lidar-lite-v2.o
> +obj-$(CONFIG_SRF08)		+= srf08.o
>  obj-$(CONFIG_SX9500)		+= sx9500.o
> diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c
> new file mode 100644
> index 000000000000..faab6cf3f1a2
> --- /dev/null
> +++ b/drivers/iio/proximity/srf08.c
> @@ -0,0 +1,405 @@
> +/*
> + * srf08.c - Support for Devantech SRF08 ultrasonic ranger
> + *
> + * Copyright (c) 2016 Andreas Klinger <ak-n176/SwNRljddJNmlsFzeA@public.gmane.org>
> + *
> + * This file is subject to the terms and conditions of version 2 of
> + * the GNU General Public License.  See the file COPYING in the main
> + * directory of this archive for more details.
> + *
> + * For details about the device see:
> + * http://www.robot-electronics.co.uk/htm/srf08tech.html
> + */
> +
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/bitops.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +/* registers of SRF08 device */
> +#define SRF08_WRITE_COMMAND	0x00	/* Command Register */
> +#define SRF08_WRITE_MAX_GAIN	0x01	/* Max Gain Register: 0 .. 31 */
> +#define SRF08_WRITE_RANGE	0x02	/* Range Register: 0 .. 255 */
> +#define SRF08_READ_SW_REVISION	0x00	/* Software Revision */
> +#define SRF08_READ_LIGHT	0x01	/* Light Sensor during last echo */
> +#define SRF08_READ_ECHO_1_HIGH	0x02	/* Range of first echo received */
> +#define SRF08_READ_ECHO_1_LOW	0x03	/* Range of first echo received */
> +
> +#define SRF08_CMD_RANGING_CM	0x51	/* Ranging Mode - Result in cm */
> +
> +#define SRF08_DEFAULT_GAIN	1025	/* default analogue value of Gain */
> +#define SRF08_DEFAULT_RANGE	6020	/* default value of Range in mm */
> +
> +struct srf08_data {
> +	struct i2c_client	*client;
> +	int			sensitivity;		/* Gain */
> +	int			range_mm;		/* max. Range in mm */
> +	struct mutex		lock;
> +};
> +
> +/*
> + * in the documentation one can read about the "Gain" of the device
> + * which is used here for amplifying the signal and filtering out unwanted
> + * ones.
> + * But with ADC's this term is already used differently and that's why it
> + * is called "Sensitivity" here.
> + */
> +static const int srf08_sensitivity[] = {
> +	 94,  97, 100, 103, 107, 110, 114, 118,
> +	123, 128, 133, 139, 145, 152, 159, 168,
> +	177, 187, 199, 212, 227, 245, 265, 288,
> +	317, 352, 395, 450, 524, 626, 777, 1025 };
> +
> +static int srf08_read_ranging(struct srf08_data *data)
> +{
> +	struct i2c_client *client = data->client;
> +	int ret, i;
> +	int waittime;
> +
> +	mutex_lock(&data->lock);
> +
> +	ret = i2c_smbus_write_byte_data(data->client,
> +			SRF08_WRITE_COMMAND, SRF08_CMD_RANGING_CM);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "write command - err: %d\n", ret);
> +		mutex_unlock(&data->lock);
> +		return ret;
> +	}
> +
> +	/*
> +	 * we read here until a correct version number shows up as
> +	 * suggested by the documentation
> +	 *
> +	 * with an ultrasonic speed of 343 m/s and a roundtrip of it
> +	 * sleep the expected duration and try to read from the device
> +	 * if nothing useful is read try it in a shorter grid
> +	 *
> +	 * polling for not more than 20 ms should be enough
> +	 */
> +	waittime = 1 + data->range_mm / 172;
> +	msleep(waittime);
> +	for (i = 0; i < 4; i++) {
> +		ret = i2c_smbus_read_byte_data(data->client,
> +						SRF08_READ_SW_REVISION);
> +
> +		/* check if a valid version number is read */
> +		if (ret < 255 && ret > 0)
> +			break;
> +		msleep(5);
> +	}
> +
> +	if (ret >= 255 || ret <= 0) {
> +		dev_err(&client->dev, "device not ready\n");
> +		mutex_unlock(&data->lock);
> +		return -EIO;
> +	}
> +
> +	ret = i2c_smbus_read_word_swapped(data->client,
> +						SRF08_READ_ECHO_1_HIGH);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "cannot read distance: ret=%d\n", ret);
> +		mutex_unlock(&data->lock);
> +		return ret;
> +	}
> +
> +	mutex_unlock(&data->lock);
> +
> +	return ret;
> +}
> +
> +static int srf08_read_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *channel, int *val,
> +			    int *val2, long mask)
> +{
> +	struct srf08_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (channel->type != IIO_DISTANCE)
> +		return -EINVAL;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = srf08_read_ranging(data);
> +		if (ret < 0)
> +			return ret;
> +		*val = ret;
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		/* 1 LSB is 1 cm */
> +		*val = 0;
> +		*val2 = 10000;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static ssize_t srf08_show_range_mm_available(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	int i, len = 0;
> +
> +	for (i = 0; i < 256; i++)
> +		len += scnprintf(buf + len, PAGE_SIZE - len,
> +							"%d ", (i + 1) * 43);
Does this fit in the page length? Guess it probably does.  See comment below.
> +
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sensor_max_range_available, S_IRUGO,
> +				srf08_show_range_mm_available, NULL, 0);
> +
> +static ssize_t srf08_show_range_mm(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct srf08_data *data = iio_priv(indio_dev);
> +
> +	return sprintf(buf, "%d\n", data->range_mm);
> +}
> +
> +/*
> + * set the range of the sensor to an even multiple of 43 mm
> + * which corresponds to 1 LSB in the register
> + *
> + * register value    corresponding range
> + *         0x00             43 mm
> + *         0x01             86 mm
> + *         0x02            129 mm
> + *         ...
> + *         0xFF          11008 mm
> + */
> +static ssize_t srf08_write_range_mm(struct srf08_data *data, unsigned int val)
> +{
> +	int ret;
> +	struct i2c_client *client = data->client;
> +	unsigned int mod;
> +	u8 regval;
> +
> +	ret = val / 43 - 1;
> +	mod = val % 43;
> +
> +	if (mod || (ret < 0) || (ret > 255))
> +		return -EINVAL;
> +
> +	regval = ret;
> +
> +	mutex_lock(&data->lock);
> +
> +	ret = i2c_smbus_write_byte_data(client, SRF08_WRITE_RANGE, regval);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "write_range - err: %d\n", ret);
> +		mutex_unlock(&data->lock);
> +		return ret;
> +	}
> +
> +	data->range_mm = val;
> +
> +	mutex_unlock(&data->lock);
> +
> +	return 0;
> +}
> +
> +static ssize_t srf08_store_range_mm(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct srf08_data *data = iio_priv(indio_dev);
> +	int ret;
> +	unsigned int val;
> +
> +	ret = kstrtouint(buf, 10, &val);
> +	if (ret)
> +		return ret;
> +
> +	ret = srf08_write_range_mm(data, val);
> +	if (ret < 0)
> +		return ret;
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sensor_max_range, S_IRUGO | S_IWUSR,
> +			srf08_show_range_mm, srf08_store_range_mm, 0);
> +
> +static ssize_t srf08_show_sensitivity_available(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	int i, len = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(srf08_sensitivity); i++)
> +		len += sprintf(buf + len, "%d ", srf08_sensitivity[i]);
> +
> +	len += sprintf(buf + len, "\n");
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sensor_sensitivity_available, S_IRUGO,
> +				srf08_show_sensitivity_available, NULL, 0);
> +
> +static ssize_t srf08_show_sensitivity(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct srf08_data *data = iio_priv(indio_dev);
> +	int len;
> +
> +	len = sprintf(buf, "%d\n", data->sensitivity);
> +
> +	return len;
> +}
> +
> +static ssize_t srf08_write_sensitivity(struct srf08_data *data,
> +							unsigned int val)
> +{
> +	struct i2c_client *client = data->client;
> +	int ret, i;
> +	u8 regval;
> +
> +	for (i = 0; i < ARRAY_SIZE(srf08_sensitivity); i++)
> +		if (val == srf08_sensitivity[i]) {
> +			regval = i;
> +			break;
> +		}
> +
> +	if (i >= ARRAY_SIZE(srf08_sensitivity))
> +		return -EINVAL;
> +
> +	mutex_lock(&data->lock);
> +
> +	ret = i2c_smbus_write_byte_data(client,
> +						SRF08_WRITE_MAX_GAIN, regval);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "write_sensitivity - err: %d\n", ret);
> +		mutex_unlock(&data->lock);
> +		return ret;
> +	}
> +
> +	data->sensitivity = val;
> +
> +	mutex_unlock(&data->lock);
> +
> +	return 0;
> +}
> +
> +static ssize_t srf08_store_sensitivity(struct device *dev,
> +						struct device_attribute *attr,
> +						const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct srf08_data *data = iio_priv(indio_dev);
> +	int ret;
> +	unsigned int val;
> +
> +	ret = kstrtouint(buf, 10, &val);
> +	if (ret)
> +		return ret;
> +
> +	ret = srf08_write_sensitivity(data, val);
> +	if (ret < 0)
> +		return ret;
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR,
> +			srf08_show_sensitivity, srf08_store_sensitivity, 0);
> +
> +static struct attribute *srf08_attributes[] = {
> +	&iio_dev_attr_sensor_max_range.dev_attr.attr,
> +	&iio_dev_attr_sensor_max_range_available.dev_attr.attr,
Might be useful to note we now allow more complex available descriptions
though they are not well documented at the moment (my fault - had a busy
few weeks).  See Documentation/ABI/testing/sysfs-bus-iio-dac-dpot for
a simplex example.

> +	&iio_dev_attr_sensor_sensitivity.dev_attr.attr,
> +	&iio_dev_attr_sensor_sensitivity_available.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group srf08_attribute_group = {
> +	.attrs = srf08_attributes,
> +};
> +
> +static const struct iio_chan_spec srf08_channels[] = {
> +	{
> +		.type = IIO_DISTANCE,
> +		.info_mask_separate =
> +				BIT(IIO_CHAN_INFO_RAW) |
> +				BIT(IIO_CHAN_INFO_SCALE),
> +	},
> +};
> +
> +static const struct iio_info srf08_info = {
> +	.read_raw = srf08_read_raw,
> +	.attrs = &srf08_attribute_group,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int srf08_probe(struct i2c_client *client,
> +					 const struct i2c_device_id *id)
> +{
> +	struct iio_dev *indio_dev;
> +	struct srf08_data *data;
> +	int ret;
> +
> +	if (!i2c_check_functionality(client->adapter,
> +					I2C_FUNC_SMBUS_READ_BYTE_DATA |
> +					I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
> +					I2C_FUNC_SMBUS_READ_WORD_DATA))
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	data = iio_priv(indio_dev);
> +	i2c_set_clientdata(client, indio_dev);
> +	data->client = client;
> +
> +	indio_dev->name = "srf08";
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->info = &srf08_info;
> +	indio_dev->channels = srf08_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(srf08_channels);
> +
> +	mutex_init(&data->lock);
> +
> +	/*
> +	 * set default values of device here
> +	 * these register values cannot be read from the hardware
> +	 * therefore set driver specific default values
> +	 */
> +	ret = srf08_write_sensitivity(data, SRF08_DEFAULT_GAIN);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = srf08_write_range_mm(data, SRF08_DEFAULT_RANGE);
> +	if (ret < 0)
> +		return ret;
> +
> +	return devm_iio_device_register(&client->dev, indio_dev);
> +}
> +
> +static const struct i2c_device_id srf08_id[] = {
> +	{ "srf08", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, srf08_id);
> +
> +static struct i2c_driver srf08_driver = {
> +	.driver = {
> +		.name	= "srf08",
> +	},
> +	.probe = srf08_probe,
> +	.id_table = srf08_id,
> +};
> +module_i2c_driver(srf08_driver);
> +
> +MODULE_AUTHOR("Andreas Klinger <ak-n176/SwNRljddJNmlsFzeA@public.gmane.org>");
> +MODULE_DESCRIPTION("Devantech SRF08 ultrasonic ranger driver");
> +MODULE_LICENSE("GPL");
> 

--
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 3/3] iio: distance: srf08: add driver ABI documentation
From: Jonathan Cameron @ 2017-01-22 13:41 UTC (permalink / raw)
  To: Andreas Klinger, knaack.h-Mmb7MZpHnFY,
	lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, ktsai-GubuWUlQtMwciDkP5Hr2oA,
	wsa-z923LK4zBo2bacvFa/9K2g, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, trivial-DgEjT+Ai2ygdnm+yROfE0A,
	mranostay-Re5JQEeQqe8AvxtiuMwx3w,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170117135006.GA22903@andreas>

On 17/01/17 13:50, Andreas Klinger wrote:
> Add sysfs-bus-iio-distance-srf08 for individual attributes of the driver,
> especially:
>  - sensitivity which the device documentation calls gain for amplifying the
>    signal
>  - max_range for limiting the maximum distance for expected echos and
>    therefore limiting the time waiting for telegrams
> 
> Signed-off-by: Andreas Klinger <ak-n176/SwNRljddJNmlsFzeA@public.gmane.org>
> ---
>  .../ABI/testing/sysfs-bus-iio-distance-srf08       | 27 ++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
> new file mode 100644
> index 000000000000..e96c28064748
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
> @@ -0,0 +1,27 @@
> +What		/sys/bus/iio/devices/iio:deviceX/in_distance_raw
> +Date:		January 2017
> +KernelVersion:	4.11
> +Contact:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +Description:
> +		Get the current distance in meters between the sensor and
> +		the first object recognized
Generally no need to document what are effectively standard elements.
Just leads to the possibility of disagreements in the docs! 
Still no harm here really.
> +
> +What		/sys/bus/iio/devices/iio:deviceX/sensor_sensitivity
> +Date:		January 2017
> +KernelVersion:	4.11
> +Contact:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +Description:
> +		Show or set the gain boost of the amp, from 0-31 range.
> +		default 31
> +
> +What		/sys/bus/iio/devices/iio:deviceX/sensor_max_range
> +Date:		January 2017
> +KernelVersion:	4.11
> +Contact:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +Description:
> +		Show or set the maximum range between the sensor and the
> +		first object echoed in millimeters.
> +		This setting limits the time the driver is waiting for a
> +		echo.
> +		Can be set between 43 and 11008 in a grid of 43 mm.
> +		default 6020
This needs to be in the same units as the range - so m.
I'm not 100% sure this is the best ABI we can do for this. However, supporting
this as a legacy abi if we come up with something more general will be a very
small burden, so lets not stall the driver on this!

Jonathan
> 

^ permalink raw reply

* Re: [PATCH v8 12/12] mux: support simplified bindings for single-user gpio mux
From: Jonathan Cameron @ 2017-01-22 13:30 UTC (permalink / raw)
  To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jonathan Corbet,
	Andrew Morton, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1484755035-25927-13-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

On 18/01/17 15:57, Peter Rosin wrote:
> Allow bindings for a GPIO controlled mux to be specified in the
> mux consumer node.
> 
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Code is good as far as I am concerned. Only question is whether this
is worth the hassle given the normal bindings don't give that high
a burden in complexity!

I don't really care either way:)

> ---
>  drivers/mux/Kconfig    |  5 +----
>  drivers/mux/mux-core.c | 23 +++++++++++++++++++++--
>  drivers/mux/mux-gpio.c | 28 +++++++++++++++++++++-------
>  drivers/mux/mux-gpio.h | 13 +++++++++++++
>  4 files changed, 56 insertions(+), 13 deletions(-)
>  create mode 100644 drivers/mux/mux-gpio.h
> 
> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
> index d13fcf98958e..cd161c228537 100644
> --- a/drivers/mux/Kconfig
> +++ b/drivers/mux/Kconfig
> @@ -29,7 +29,7 @@ config MUX_ADG792A
>  	  be called mux-adg792a.
>  
>  config MUX_GPIO
> -	tristate "GPIO-controlled Multiplexer"
> +	bool "GPIO-controlled Multiplexer"
>  	depends on OF && GPIOLIB
>  	help
>  	  GPIO-controlled Multiplexer controller.
> @@ -39,7 +39,4 @@ config MUX_GPIO
>  	  states. The GPIO pins can be connected (by the hardware) to several
>  	  multiplexers, which in that case will be operated in parallel.
>  
> -	  To compile the driver as a module, choose M here: the module will
> -	  be called mux-gpio.
> -
>  endif
> diff --git a/drivers/mux/mux-core.c b/drivers/mux/mux-core.c
> index 16a61253d164..0caafd6f5a77 100644
> --- a/drivers/mux/mux-core.c
> +++ b/drivers/mux/mux-core.c
> @@ -21,6 +21,8 @@
>  #include <linux/of_platform.h>
>  #include <linux/slab.h>
>  
> +#include "mux-gpio.h"
> +
>  static struct class mux_class = {
>  	.name = "mux",
>  	.owner = THIS_MODULE,
> @@ -314,9 +316,26 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
>  	ret = of_parse_phandle_with_args(np,
>  					 "mux-controls", "#mux-control-cells",
>  					 index, &args);
> +
> +#ifdef CONFIG_MUX_GPIO
> +	if (ret == -ENOENT && !mux_name) {
> +		mux_chip = mux_gpio_alloc(dev);
> +		if (!IS_ERR(mux_chip)) {
> +			ret = devm_mux_chip_register(dev, mux_chip);
> +			if (ret < 0)
> +				return ERR_PTR(ret);
> +			get_device(&mux_chip->dev);
> +			return mux_chip->mux;
> +		}
> +
> +		ret = PTR_ERR(mux_chip);
> +	}
> +#endif
> +
>  	if (ret) {
> -		dev_err(dev, "%s: failed to get mux-control %s(%i)\n",
> -			np->full_name, mux_name ?: "", index);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "%s: failed to get mux-control %s(%i)\n",
> +				np->full_name, mux_name ?: "", index);
>  		return ERR_PTR(ret);
>  	}
>  
> diff --git a/drivers/mux/mux-gpio.c b/drivers/mux/mux-gpio.c
> index 48ca4d6b4fc7..2ab8735e3415 100644
> --- a/drivers/mux/mux-gpio.c
> +++ b/drivers/mux/mux-gpio.c
> @@ -18,6 +18,8 @@
>  #include <linux/platform_device.h>
>  #include <linux/property.h>
>  
> +#include "mux-gpio.h"
> +
>  struct mux_gpio {
>  	struct gpio_descs *gpios;
>  	int *val;
> @@ -48,24 +50,21 @@ static const struct of_device_id mux_gpio_dt_ids[] = {
>  };
>  MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids);
>  
> -static int mux_gpio_probe(struct platform_device *pdev)
> +struct mux_chip *mux_gpio_alloc(struct device *dev)
>  {
> -	struct device *dev = &pdev->dev;
> -	struct device_node *np = dev->of_node;
>  	struct mux_chip *mux_chip;
>  	struct mux_gpio *mux_gpio;
>  	int pins;
> -	u32 idle_state;
>  	int ret;
>  
>  	pins = gpiod_count(dev, "mux");
>  	if (pins < 0)
> -		return pins;
> +		return ERR_PTR(pins);
>  
>  	mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
>  				       pins * sizeof(*mux_gpio->val));
>  	if (!mux_chip)
> -		return -ENOMEM;
> +		return ERR_PTR(-ENOMEM);
>  
>  	mux_gpio = mux_chip_priv(mux_chip);
>  	mux_gpio->val = (int *)(mux_gpio + 1);
> @@ -76,11 +75,26 @@ static int mux_gpio_probe(struct platform_device *pdev)
>  		ret = PTR_ERR(mux_gpio->gpios);
>  		if (ret != -EPROBE_DEFER)
>  			dev_err(dev, "failed to get gpios\n");
> -		return ret;
> +		return ERR_PTR(ret);
>  	}
>  	WARN_ON(pins != mux_gpio->gpios->ndescs);
>  	mux_chip->mux->states = 1 << pins;
>  
> +	return mux_chip;
> +}
> +EXPORT_SYMBOL_GPL(mux_gpio_alloc);
> +
> +static int mux_gpio_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mux_chip *mux_chip;
> +	u32 idle_state;
> +	int ret;
> +
> +	mux_chip = mux_gpio_alloc(dev);
> +	if (IS_ERR(mux_chip))
> +		return PTR_ERR(mux_chip);
> +
>  	ret = device_property_read_u32(dev, "idle-state", &idle_state);
>  	if (ret >= 0) {
>  		if (idle_state >= mux_chip->mux->states) {
> diff --git a/drivers/mux/mux-gpio.h b/drivers/mux/mux-gpio.h
> new file mode 100644
> index 000000000000..fe3e8d0173aa
> --- /dev/null
> +++ b/drivers/mux/mux-gpio.h
> @@ -0,0 +1,13 @@
> +/*
> + * GPIO-controlled multiplexer driver interface
> + *
> + * Copyright (C) 2017 Axentia Technologies AB
> + *
> + * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +struct mux_chip *mux_gpio_alloc(struct device *dev);
> 

--
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: [PATCHv5 2/2] dt-bindings: iio: document MAX11100 ADC
From: Jonathan Cameron @ 2017-01-22 13:21 UTC (permalink / raw)
  To: Rob Herring, Jacopo Mondi
  Cc: wsa+renesas, magnus.damm, knaack.h, lars, pmeerw, marek.vasut,
	geert, mark.rutland, linux-iio, devicetree, linux-renesas-soc
In-Reply-To: <20170121203307.3ylfbh3xlhp5mkjs@rob-hp-laptop>

On 21/01/17 20:33, Rob Herring wrote:
> On Wed, Jan 18, 2017 at 05:30:53PM +0100, Jacopo Mondi wrote:
>> Add device tree bindings documentation for Maxim MAX11100 single-channel
>> ADC
>>
>> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
>> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
>> Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
>> ---
>>  .../devicetree/bindings/iio/adc/max11100.txt          | 19 +++++++++++++++++++
>>  1 file changed, 19 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/max11100.txt
> 
> Acked-by: Rob Herring <robh@kernel.org>
Thanks and added.

Jonathan
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply

* Re: [PATCH 5/7] iio: adc: stm32: add optional dma support
From: Jonathan Cameron @ 2017-01-22 13:14 UTC (permalink / raw)
  To: Fabrice Gasnier, linux, robh+dt, linux-arm-kernel, devicetree,
	linux-kernel
  Cc: linux-iio, mark.rutland, mcoquelin.stm32, alexandre.torgue, lars,
	knaack.h, pmeerw, benjamin.gaignard, benjamin.gaignard
In-Reply-To: <1484832854-6314-6-git-send-email-fabrice.gasnier@st.com>

On 19/01/17 13:34, Fabrice Gasnier wrote:
> Add optional DMA support to STM32 ADC.
> Use dma cyclic mode with at least two periods.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
What is the point going forward in supporting non dma buffered reads at all?
Is there hardware that doesn't have DMA support?
Just strikes me that the driver would be slight simpler if we dropped that
support.

Various comments inline.  Mostly around crossing the boundary to fetch
from the IIO specific buffer (indio_dev->buffer).  That should never be
used directly as we can have multiple consumers of the datastream so
the numbers you get from that may represent only part of what is going on.


> ---
>  drivers/iio/adc/Kconfig          |   2 +
>  drivers/iio/adc/stm32-adc-core.c |   1 +
>  drivers/iio/adc/stm32-adc-core.h |   2 +
>  drivers/iio/adc/stm32-adc.c      | 209 ++++++++++++++++++++++++++++++++++++---
>  4 files changed, 202 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 9a7b090..2a2ef78 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -444,12 +444,14 @@ config ROCKCHIP_SARADC
>  config STM32_ADC_CORE
>  	tristate "STMicroelectronics STM32 adc core"
>  	depends on ARCH_STM32 || COMPILE_TEST
> +	depends on HAS_DMA
>  	depends on OF
>  	depends on REGULATOR
>  	select IIO_BUFFER
>  	select MFD_STM32_TIMERS
>  	select IIO_STM32_TIMER_TRIGGER
>  	select IIO_TRIGGERED_BUFFER
> +	select IRQ_WORK
>  	help
>  	  Select this option to enable the core driver for STMicroelectronics
>  	  STM32 analog-to-digital converter (ADC).
> diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
> index 4214b0c..22b7c93 100644
> --- a/drivers/iio/adc/stm32-adc-core.c
> +++ b/drivers/iio/adc/stm32-adc-core.c
> @@ -201,6 +201,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
>  	priv->common.base = devm_ioremap_resource(&pdev->dev, res);
>  	if (IS_ERR(priv->common.base))
>  		return PTR_ERR(priv->common.base);
> +	priv->common.phys_base = res->start;
>  
>  	priv->vref = devm_regulator_get(&pdev->dev, "vref");
>  	if (IS_ERR(priv->vref)) {
> diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
> index 081fa5f..2ec7abb 100644
> --- a/drivers/iio/adc/stm32-adc-core.h
> +++ b/drivers/iio/adc/stm32-adc-core.h
> @@ -42,10 +42,12 @@
>  /**
>   * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
>   * @base:		control registers base cpu addr
> + * @phys_base:		control registers base physical addr
>   * @vref_mv:		vref voltage (mv)
>   */
>  struct stm32_adc_common {
>  	void __iomem			*base;
> +	phys_addr_t			phys_base;
>  	int				vref_mv;
>  };
>  
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 9753c39..3439f4c 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -21,6 +21,8 @@
>  
>  #include <linux/clk.h>
>  #include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dmaengine.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/buffer.h>
>  #include <linux/iio/timer/stm32-timer-trigger.h>
> @@ -29,6 +31,7 @@
>  #include <linux/iio/triggered_buffer.h>
>  #include <linux/interrupt.h>
>  #include <linux/io.h>
> +#include <linux/irq_work.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
>  #include <linux/of.h>
> @@ -69,6 +72,8 @@
>  #define STM32F4_EXTSEL_SHIFT		24
>  #define STM32F4_EXTSEL_MASK		GENMASK(27, 24)
>  #define STM32F4_EOCS			BIT(10)
> +#define STM32F4_DDS			BIT(9)
> +#define STM32F4_DMA			BIT(8)
>  #define STM32F4_ADON			BIT(0)
>  
>  /* STM32F4_ADC_SQR1 - bit fields */
> @@ -165,6 +170,11 @@ struct stm32_adc_trig_info {
>   * @bufi:		data buffer index
>   * @num_conv:		expected number of scan conversions
>   * @exten:		external trigger config (enable/polarity)
> + * @work:		irq work used to call trigger poll routine
> + * @dma_chan:		dma channel
> + * @rx_buf:		dma rx buffer cpu address
> + * @rx_dma_buf:		dma rx buffer bus address
> + * @rx_buf_sz:		dma rx buffer size
>   */
>  struct stm32_adc {
>  	struct stm32_adc_common	*common;
> @@ -177,6 +187,11 @@ struct stm32_adc {
>  	int			bufi;
>  	int			num_conv;
>  	enum stm32_adc_exten	exten;
> +	struct irq_work		work;
> +	struct dma_chan		*dma_chan;
> +	u8			*rx_buf;
> +	dma_addr_t		rx_dma_buf;
> +	int			rx_buf_sz;
>  };
>  
>  /**
> @@ -332,10 +347,20 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
>  /**
>   * stm32_adc_start_conv() - Start conversions for regular channels.
>   * @adc: stm32 adc instance
> + *
> + * Start conversions for regular channels.
> + * Also take care of normal or DMA mode. DMA is used in circular mode for
> + * regular conversions, in IIO buffer modes. Rely on rx_buf as raw
> + * read doesn't use dma, but direct DR read.
>   */
>  static void stm32_adc_start_conv(struct stm32_adc *adc)
>  {
>  	stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
> +
> +	if (adc->rx_buf)
> +		stm32_adc_set_bits(adc, STM32F4_ADC_CR2,
> +				   STM32F4_DMA | STM32F4_DDS);
> +
>  	stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
>  
>  	/* Wait for Power-up time (tSTAB from datasheet) */
> @@ -353,6 +378,10 @@ static void stm32_adc_stop_conv(struct stm32_adc *adc)
>  
>  	stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
>  	stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON);
> +
> +	if (adc->rx_buf)
> +		stm32_adc_clr_bits(adc, STM32F4_ADC_CR2,
> +				   STM32F4_DMA | STM32F4_DDS);
>  }
>  
>  /**
> @@ -689,19 +718,138 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
>  	.driver_module = THIS_MODULE,
>  };
>  
> +static int stm32_adc_dma_residue(struct stm32_adc *adc)
> +{
> +	struct dma_tx_state state;
> +	enum dma_status status;
> +
> +	if (!adc->rx_buf)
> +		return 0;
> +
> +	status = dmaengine_tx_status(adc->dma_chan,
> +				     adc->dma_chan->cookie,
> +				     &state);
> +	if (status == DMA_IN_PROGRESS) {
> +		/* Residue is size in bytes from end of buffer */
> +		int i = adc->rx_buf_sz - state.residue;
> +		int size;
> +
> +		/* Return available bytes */
> +		if (i >= adc->bufi)
> +			size = i - adc->bufi;
> +		else
> +			size = adc->rx_buf_sz - adc->bufi + i;
> +
> +		return size;
> +	}
> +
> +	return 0;
> +}
> +
> +static void stm32_adc_dma_irq_work(struct irq_work *work)
> +{
> +	struct stm32_adc *adc = container_of(work, struct stm32_adc, work);
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +
> +	/**
> +	 * iio_trigger_poll calls generic_handle_irq(). So, it requires hard
> +	 * irq context, and cannot be called directly from dma callback,
> +	 * dma cb has to schedule this work instead.
> +	 */
> +	iio_trigger_poll(indio_dev->trig);
This is nasty ;)  iio_trigger_poll_chained is your friend. It is missnamed.
If you only need to call the threaded part of the pollfunc (and I think you
can construct it so you do) then it will call it without needing to bounce
back into interrupt context.

> +}
> +
> +static void stm32_adc_dma_buffer_done(void *data)
> +{
> +	struct stm32_adc *adc = data;
> +
> +	/* invoques iio_trigger_poll() from hard irq context */
> +	irq_work_queue(&adc->work);
> +}
> +
>  static int stm32_adc_buffer_preenable(struct iio_dev *indio_dev)
>  {
>  	struct stm32_adc *adc = iio_priv(indio_dev);
> +	struct dma_async_tx_descriptor *desc;
> +	struct dma_slave_config config;
> +	dma_cookie_t cookie;
> +	int ret, size, watermark;
>  
>  	/* Reset adc buffer index */
>  	adc->bufi = 0;
>  
> -	/* Allocate adc buffer */
> -	adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
> -	if (!adc->buffer)
> +	if (!adc->dma_chan) {
> +		/* Allocate adc buffer */
> +		adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
> +		if (!adc->buffer)
> +			return -ENOMEM;
> +
> +		return 0;
> +	}
> +
> +	/*
> +	 * Allocate at least twice the buffer size for dma cyclic transfers, so
> +	 * we can work with at least two dma periods. There should be :
> +	 * - always one buffer (period) dma is working on
> +	 * - one buffer (period) driver can push with iio_trigger_poll().
> +	 */
> +	size = indio_dev->buffer->bytes_per_datum * indio_dev->buffer->length;
> +	size = max(indio_dev->scan_bytes * 2, size);
Hmm. There is a bit of a weird mix going on here. Firstly, you may have more
than one consumer buffer, the one you are checking is only the one directly
associated with the IIO userspace interface.

So scan_bytes is the right value to use for both of these statements.
The buffer length is typically not knowable either or relevant here.
If you are ultimately going to deal with watermarks there is an interface
to guide read sizes based on that but this isn't it.

So basically device should never know anything at all about the software
buffer. All info should pass through the core which knows about all the
consumers hanging off this interface (and the demux that is going on to
feed them all).

Some drivers provide additional info to allow the modifying of the
precise hardware watermark being used.  That's an acceptable form of
'tweak'.

> +
> +	adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
> +					 PAGE_ALIGN(size), &adc->rx_dma_buf,
> +					 GFP_KERNEL);
> +	if (!adc->rx_buf)
>  		return -ENOMEM;
> +	adc->rx_buf_sz = size;
> +	watermark = indio_dev->buffer->bytes_per_datum
> +		* indio_dev->buffer->watermark;
> +	watermark = max(indio_dev->scan_bytes, watermark);
> +	watermark = rounddown(watermark, indio_dev->scan_bytes);
> +
> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, size,
> +		watermark);
> +
> +	/* Configure DMA channel to read data register */
> +	memset(&config, 0, sizeof(config));
> +	config.src_addr = (dma_addr_t)adc->common->phys_base;
> +	config.src_addr += adc->offset + STM32F4_ADC_DR;
> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
> +
> +	ret = dmaengine_slave_config(adc->dma_chan, &config);
> +	if (ret)
> +		goto config_err;
> +
> +	/* Prepare a DMA cyclic transaction */
> +	desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
> +					 adc->rx_dma_buf,
> +					 size, watermark,
> +					 DMA_DEV_TO_MEM,
> +					 DMA_PREP_INTERRUPT);
> +	if (!desc) {
> +		ret = -ENODEV;
> +		goto config_err;
> +	}
> +
> +	desc->callback = stm32_adc_dma_buffer_done;
> +	desc->callback_param = adc;
> +
> +	cookie = dmaengine_submit(desc);
> +	if (dma_submit_error(cookie)) {
> +		ret = dma_submit_error(cookie);
> +		goto config_err;
> +	}
> +
> +	/* Issue pending DMA requests */
> +	dma_async_issue_pending(adc->dma_chan);
>  
>  	return 0;
> +
> +config_err:
> +	dma_free_coherent(adc->dma_chan->device->dev, PAGE_ALIGN(size),
> +			  adc->rx_buf, adc->rx_dma_buf);
> +
> +	return ret;
>  }
>  
>  static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
> @@ -719,7 +867,8 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
>  	if (ret < 0)
>  		return ret;
>  
> -	stm32_adc_conv_irq_enable(adc);
> +	if (!adc->dma_chan)
> +		stm32_adc_conv_irq_enable(adc);
>  	stm32_adc_start_conv(adc);
>  
>  	return 0;
> @@ -731,7 +880,8 @@ static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
>  	int ret;
>  
>  	stm32_adc_stop_conv(adc);
> -	stm32_adc_conv_irq_disable(adc);
> +	if (!adc->dma_chan)
> +		stm32_adc_conv_irq_disable(adc);
>  
>  	ret = iio_triggered_buffer_predisable(indio_dev);
>  	if (ret < 0)
> @@ -748,7 +898,16 @@ static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev)
>  {
>  	struct stm32_adc *adc = iio_priv(indio_dev);
>  
> -	kfree(adc->buffer);
> +	if (!adc->dma_chan) {
> +		kfree(adc->buffer);
> +	} else {
> +		dmaengine_terminate_all(adc->dma_chan);
> +		irq_work_sync(&adc->work);
> +		dma_free_coherent(adc->dma_chan->device->dev,
> +				  PAGE_ALIGN(adc->rx_buf_sz),
> +				  adc->rx_buf, adc->rx_dma_buf);
> +		adc->rx_buf = NULL;
> +	}
>  	adc->buffer = NULL;
>  
>  	return 0;
> @@ -769,15 +928,31 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
>  
>  	dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
>  
> -	/* reset buffer index */
> -	adc->bufi = 0;
> -	iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
> -					   pf->timestamp);
> +	if (!adc->dma_chan) {
> +		/* reset buffer index */
> +		adc->bufi = 0;
> +		iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
> +						   pf->timestamp);
> +	} else {
> +		int residue = stm32_adc_dma_residue(adc);
> +
> +		while (residue >= indio_dev->scan_bytes) {
> +			adc->buffer = (u16 *)&adc->rx_buf[adc->bufi];
> +			iio_push_to_buffers_with_timestamp(indio_dev,
> +							   adc->buffer,
> +							   pf->timestamp);
> +			residue -= indio_dev->scan_bytes;
> +			adc->bufi += indio_dev->scan_bytes;
> +			if (adc->bufi >= adc->rx_buf_sz)
> +				adc->bufi = 0;
> +		}
> +	}
>  
>  	iio_trigger_notify_done(indio_dev->trig);
>  
>  	/* re-enable eoc irq */
> -	stm32_adc_conv_irq_enable(adc);
> +	if (!adc->dma_chan)
> +		stm32_adc_conv_irq_enable(adc);
>  
>  	return IRQ_HANDLED;
>  }
> @@ -910,13 +1085,17 @@ static int stm32_adc_probe(struct platform_device *pdev)
>  	if (ret < 0)
>  		goto err_clk_disable;
>  
> +	adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
> +	if (adc->dma_chan)
> +		init_irq_work(&adc->work, stm32_adc_dma_irq_work);
> +
>  	ret = iio_triggered_buffer_setup(indio_dev,
>  					 &iio_pollfunc_store_time,
>  					 &stm32_adc_trigger_handler,
>  					 &stm32_adc_buffer_setup_ops);
>  	if (ret) {
>  		dev_err(&pdev->dev, "buffer setup failed\n");
> -		goto err_clk_disable;
> +		goto err_dma_disable;
>  	}
>  
>  	ret = iio_device_register(indio_dev);
> @@ -930,6 +1109,10 @@ static int stm32_adc_probe(struct platform_device *pdev)
>  err_buffer_cleanup:
>  	iio_triggered_buffer_cleanup(indio_dev);
>  
> +err_dma_disable:
> +	if (adc->dma_chan)
> +		dma_release_channel(adc->dma_chan);
> +
>  err_clk_disable:
>  	clk_disable_unprepare(adc->clk);
>  
> @@ -943,6 +1126,8 @@ static int stm32_adc_remove(struct platform_device *pdev)
>  
>  	iio_device_unregister(indio_dev);
>  	iio_triggered_buffer_cleanup(indio_dev);
> +	if (adc->dma_chan)
> +		dma_release_channel(adc->dma_chan);
>  	clk_disable_unprepare(adc->clk);
>  
>  	return 0;
> 

^ permalink raw reply

* Re: [PATCH 3/7] iio: adc: stm32: add trigger polarity extended attribute
From: Jonathan Cameron @ 2017-01-22 12:58 UTC (permalink / raw)
  To: Fabrice Gasnier, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
	knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	benjamin.gaignard-QSEj5FYQhm4dnm+yROfE0A,
	benjamin.gaignard-qxv4g6HH51o
In-Reply-To: <1484832854-6314-4-git-send-email-fabrice.gasnier-qxv4g6HH51o@public.gmane.org>

On 19/01/17 13:34, Fabrice Gasnier wrote:
> Define extended attribute so that user may choose rising, falling or both
> edges for external trigger sources.
> Default to rising edge in case it isn't set.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
Creates a custom attibute.  Documentation please!
/Documentation/ABI/testing/sysfs-bus-iio-stm32 or similar.

The approach seems reasonable, but easier to discuss when it's
formally described.

I'd also not care about characters saved and go with 
trigger_polarity rather than the short form (unless this you have
a good reason for it!)

Jonathan
> ---
>  drivers/iio/adc/stm32-adc.c | 51 ++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 50 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 30708bc..9753c39 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -164,6 +164,7 @@ struct stm32_adc_trig_info {
>   * @lock:		spinlock
>   * @bufi:		data buffer index
>   * @num_conv:		expected number of scan conversions
> + * @exten:		external trigger config (enable/polarity)
>   */
>  struct stm32_adc {
>  	struct stm32_adc_common	*common;
> @@ -175,6 +176,7 @@ struct stm32_adc {
>  	spinlock_t		lock;		/* interrupt lock */
>  	int			bufi;
>  	int			num_conv;
> +	enum stm32_adc_exten	exten;
>  };
>  
>  /**
> @@ -449,7 +451,9 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
>  
>  		/* set trigger source, default to rising edge */
>  		extsel = ret;
> -		exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
> +		if (adc->exten == STM32_EXTEN_SWTRIG)
> +			adc->exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
> +		exten = adc->exten;
>  	}
>  
>  	spin_lock_irqsave(&adc->lock, flags);
> @@ -463,6 +467,39 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
>  	return 0;
>  }
>  
> +static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev,
> +				  const struct iio_chan_spec *chan,
> +				  unsigned int type)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +	adc->exten = type;
> +
> +	return 0;
> +}
> +
> +static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev,
> +				  const struct iio_chan_spec *chan)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +	return adc->exten;
> +}
> +
> +static const char * const stm32_trig_pol_items[] = {
> +	[STM32_EXTEN_SWTRIG] = "swtrig",
> +	[STM32_EXTEN_HWTRIG_RISING_EDGE] = "rising-edge",
> +	[STM32_EXTEN_HWTRIG_FALLING_EDGE] = "falling-edge",
> +	[STM32_EXTEN_HWTRIG_BOTH_EDGES] = "both-edges",
> +};
> +
> +const struct iio_enum stm32_adc_trig_pol = {
> +	.items = stm32_trig_pol_items,
> +	.num_items = ARRAY_SIZE(stm32_trig_pol_items),
> +	.get = stm32_adc_get_trig_pol,
> +	.set = stm32_adc_set_trig_pol,
> +};
> +
>  /**
>   * stm32_adc_single_conv() - Performs a single conversion
>   * @indio_dev: IIO device
> @@ -745,6 +782,17 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
>  	return IRQ_HANDLED;
>  }
>  
> +static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = {
> +	IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
> +	{
> +		.name = "trigger_pol_available",
> +		.shared = IIO_SHARED_BY_ALL,
> +		.read = iio_enum_available_read,
> +		.private = (uintptr_t)&stm32_adc_trig_pol,
> +	},
> +	{},
> +};
> +
>  static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>  				    struct iio_chan_spec *chan,
>  				    const struct stm32_adc_chan_spec *channel,
> @@ -760,6 +808,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>  	chan->scan_type.sign = 'u';
>  	chan->scan_type.realbits = 12;
>  	chan->scan_type.storagebits = 16;
> +	chan->ext_info = stm32_adc_ext_info;
>  }
>  
>  static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
> 

^ 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