All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/6] IIO pulse capture support for TI ECAP
@ 2014-02-05 19:01 ` Matt Porter
  0 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

Changes since v2:
	- Remove various unneeded field initialization
	- Call iio_triggered_buffer_cleanup() on remove

Changes since v1:
	- Rebased to 3.14-rc1
	- Renamed in_pulse_polarity to pulse_polarity
	- Added ABI entries for pulse devices and TI ECAP

This series adds support for PWM capture devices within IIO and
adds a TI ECAP IIO driver.

PWM capture devices are supported using a new IIO "pulse" channel type.

The IIO ECAP driver implements interrupt driven triggered buffer capture
only as raw sample reads are not applicable to this hardware.
Initially, the driver supports a single pulse width measurement with
configurable polarity. The ECAP hardware can support measurement of a
complete period and duty cycle but this is not yet implemented.

Matt Porter (6):
  iio: add support for pulse width capture devices
  iio: pulse: add TI ECAP driver
  iio: enable selection and build of pulse drivers
  iio: Add ABI docs for pulse capture devices
  pwm: enable TI PWMSS if the IIO tiecap driver is selected
  ARM: dts: AM33XX: Add ecap interrupt properties

 Documentation/ABI/testing/sysfs-bus-iio            |  18 +
 .../ABI/testing/sysfs-bus-iio-pulse-tiecap         |   9 +
 arch/arm/boot/dts/am33xx.dtsi                      |   6 +
 drivers/iio/Kconfig                                |   1 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/industrialio-core.c                    |   1 +
 drivers/iio/pulse/Kconfig                          |  20 +
 drivers/iio/pulse/Makefile                         |   6 +
 drivers/iio/pulse/tiecap.c                         | 491 +++++++++++++++++++++
 drivers/pwm/Kconfig                                |   2 +-
 include/linux/iio/types.h                          |   1 +
 11 files changed, 555 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
 create mode 100644 drivers/iio/pulse/Kconfig
 create mode 100644 drivers/iio/pulse/Makefile
 create mode 100644 drivers/iio/pulse/tiecap.c

-- 
1.8.4


^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 0/6] IIO pulse capture support for TI ECAP
@ 2014-02-05 19:01 ` Matt Porter
  0 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: linux-arm-kernel

Changes since v2:
	- Remove various unneeded field initialization
	- Call iio_triggered_buffer_cleanup() on remove

Changes since v1:
	- Rebased to 3.14-rc1
	- Renamed in_pulse_polarity to pulse_polarity
	- Added ABI entries for pulse devices and TI ECAP

This series adds support for PWM capture devices within IIO and
adds a TI ECAP IIO driver.

PWM capture devices are supported using a new IIO "pulse" channel type.

The IIO ECAP driver implements interrupt driven triggered buffer capture
only as raw sample reads are not applicable to this hardware.
Initially, the driver supports a single pulse width measurement with
configurable polarity. The ECAP hardware can support measurement of a
complete period and duty cycle but this is not yet implemented.

Matt Porter (6):
  iio: add support for pulse width capture devices
  iio: pulse: add TI ECAP driver
  iio: enable selection and build of pulse drivers
  iio: Add ABI docs for pulse capture devices
  pwm: enable TI PWMSS if the IIO tiecap driver is selected
  ARM: dts: AM33XX: Add ecap interrupt properties

 Documentation/ABI/testing/sysfs-bus-iio            |  18 +
 .../ABI/testing/sysfs-bus-iio-pulse-tiecap         |   9 +
 arch/arm/boot/dts/am33xx.dtsi                      |   6 +
 drivers/iio/Kconfig                                |   1 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/industrialio-core.c                    |   1 +
 drivers/iio/pulse/Kconfig                          |  20 +
 drivers/iio/pulse/Makefile                         |   6 +
 drivers/iio/pulse/tiecap.c                         | 491 +++++++++++++++++++++
 drivers/pwm/Kconfig                                |   2 +-
 include/linux/iio/types.h                          |   1 +
 11 files changed, 555 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
 create mode 100644 drivers/iio/pulse/Kconfig
 create mode 100644 drivers/iio/pulse/Makefile
 create mode 100644 drivers/iio/pulse/tiecap.c

-- 
1.8.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 1/6] iio: add support for pulse width capture devices
  2014-02-05 19:01 ` Matt Porter
  (?)
@ 2014-02-05 19:01   ` Matt Porter
  -1 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

Add a channel type to support pulse width capture devices.
These devices capture the timing of a PWM signal based on a
configurable trigger

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 drivers/iio/industrialio-core.c | 1 +
 include/linux/iio/types.h       | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index acc911a..6ea0cf8 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -70,6 +70,7 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_CCT] = "cct",
 	[IIO_PRESSURE] = "pressure",
 	[IIO_HUMIDITYRELATIVE] = "humidityrelative",
+	[IIO_PULSE] = "pulse",
 };
 
 static const char * const iio_modifier_names[] = {
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 084d882..4fa8840 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -30,6 +30,7 @@ enum iio_chan_type {
 	IIO_CCT,
 	IIO_PRESSURE,
 	IIO_HUMIDITYRELATIVE,
+	IIO_PULSE,
 };
 
 enum iio_modifier {
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 1/6] iio: add support for pulse width capture devices
@ 2014-02-05 19:01   ` Matt Porter
  0 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Devicetree List, Linux PWM List, Linux IIO List,
	Linux Kernel Mailing List, Linux OMAP List,
	Linux ARM Kernel List

Add a channel type to support pulse width capture devices.
These devices capture the timing of a PWM signal based on a
configurable trigger

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 drivers/iio/industrialio-core.c | 1 +
 include/linux/iio/types.h       | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index acc911a..6ea0cf8 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -70,6 +70,7 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_CCT] = "cct",
 	[IIO_PRESSURE] = "pressure",
 	[IIO_HUMIDITYRELATIVE] = "humidityrelative",
+	[IIO_PULSE] = "pulse",
 };
 
 static const char * const iio_modifier_names[] = {
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 084d882..4fa8840 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -30,6 +30,7 @@ enum iio_chan_type {
 	IIO_CCT,
 	IIO_PRESSURE,
 	IIO_HUMIDITYRELATIVE,
+	IIO_PULSE,
 };
 
 enum iio_modifier {
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 1/6] iio: add support for pulse width capture devices
@ 2014-02-05 19:01   ` Matt Porter
  0 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: linux-arm-kernel

Add a channel type to support pulse width capture devices.
These devices capture the timing of a PWM signal based on a
configurable trigger

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 drivers/iio/industrialio-core.c | 1 +
 include/linux/iio/types.h       | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index acc911a..6ea0cf8 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -70,6 +70,7 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_CCT] = "cct",
 	[IIO_PRESSURE] = "pressure",
 	[IIO_HUMIDITYRELATIVE] = "humidityrelative",
+	[IIO_PULSE] = "pulse",
 };
 
 static const char * const iio_modifier_names[] = {
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 084d882..4fa8840 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -30,6 +30,7 @@ enum iio_chan_type {
 	IIO_CCT,
 	IIO_PRESSURE,
 	IIO_HUMIDITYRELATIVE,
+	IIO_PULSE,
 };
 
 enum iio_modifier {
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 2/6] iio: pulse: add TI ECAP driver
  2014-02-05 19:01 ` Matt Porter
@ 2014-02-05 19:01   ` Matt Porter
  -1 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

Adds support for capturing PWM signals using the TI ECAP peripheral.
This driver supports triggered buffer capture of pulses on multiple
ECAP instances. In addition, the driver supports configurable polarity
of the signal to be captured.

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 drivers/iio/pulse/Kconfig  |  20 ++
 drivers/iio/pulse/Makefile |   6 +
 drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 517 insertions(+)
 create mode 100644 drivers/iio/pulse/Kconfig
 create mode 100644 drivers/iio/pulse/Makefile
 create mode 100644 drivers/iio/pulse/tiecap.c

diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
new file mode 100644
index 0000000..9864d4b
--- /dev/null
+++ b/drivers/iio/pulse/Kconfig
@@ -0,0 +1,20 @@
+#
+# Pulse Capture Devices
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Pulse Capture Devices"
+
+config IIO_TIECAP
+	tristate "TI ECAP Pulse Capture"
+	depends on SOC_AM33XX
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	 If you say yes here you get support for the TI ECAP peripheral
+	 in pulse capture mode.
+
+	 This driver can also be built as a module.  If so, the module
+	 will be called tiecap
+
+endmenu
diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
new file mode 100644
index 0000000..94d4b00
--- /dev/null
+++ b/drivers/iio/pulse/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for IIO PWM Capture Devices
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
new file mode 100644
index 0000000..fd96745
--- /dev/null
+++ b/drivers/iio/pulse/tiecap.c
@@ -0,0 +1,491 @@
+/*
+ * ECAP IIO pulse capture driver
+ *
+ * Copyright (C) 2014 Linaro Limited
+ * Author: Matt Porter <mporter@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "../../pwm/pwm-tipwmss.h"
+
+/* ECAP regs and bits */
+#define CAP1			0x08
+#define CAP2			0x0c
+#define ECCTL1			0x28
+#define ECCTL1_RUN_FREE		BIT(15)
+#define ECCTL1_CAPLDEN		BIT(8)
+#define ECCTL1_CAP2POL		BIT(2)
+#define ECCTL1_CTRRST1		BIT(1)
+#define ECCTL1_CAP1POL		BIT(0)
+#define ECCTL2			0x2a
+#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
+#define ECCTL2_TSCTR_FREERUN	BIT(4)
+#define ECCTL2_REARM		BIT(3)
+#define ECCTL2_STOP_WRAP_2	BIT(1)
+#define ECEINT			0x2c
+#define ECFLG			0x2e
+#define ECCLR			0x30
+#define ECINT_CTRCMP		BIT(7)
+#define ECINT_CTRPRD		BIT(6)
+#define ECINT_CTROVF		BIT(5)
+#define ECINT_CEVT4		BIT(4)
+#define ECINT_CEVT3		BIT(3)
+#define ECINT_CEVT2		BIT(2)
+#define ECINT_CEVT1		BIT(1)
+#define ECINT_ALL		(ECINT_CTRCMP |	\
+				ECINT_CTRPRD |	\
+				ECINT_CTROVF |	\
+				ECINT_CEVT4 |	\
+				ECINT_CEVT3 |	\
+				ECINT_CEVT2 |	\
+				ECINT_CEVT1)
+
+/* ECAP driver flags */
+#define ECAP_POLARITY_HIGH	BIT(1)
+#define ECAP_ENABLED		BIT(0)
+
+struct ecap_context {
+	u32	cap1;
+	u32	cap2;
+	u16	ecctl1;
+	u16	ecctl2;
+	u16	eceint;
+};
+
+struct ecap_state {
+	unsigned long		flags;
+	unsigned int		clk_rate;
+	void __iomem		*regs;
+	u32			*buf;
+	struct ecap_context	ctx;
+};
+
+#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
+
+static const struct iio_chan_spec ecap_channels[] = {
+	{
+		.type		= IIO_PULSE,
+		.info_mask_separate =
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index	= 0,
+		.scan_type = {
+			.sign		= 'u',
+			.realbits	= 32,
+			.storagebits	= 32,
+			.endianness	= IIO_LE,
+		},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(1)
+};
+
+static ssize_t ecap_attr_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct ecap_state *state = dev_to_ecap_state(dev);
+
+	return sprintf(buf, "%d\n",
+		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
+}
+
+static ssize_t ecap_attr_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf,
+			       size_t len)
+{
+	int ret;
+	bool val;
+	struct ecap_state *state = dev_to_ecap_state(dev);
+
+	if (test_bit(ECAP_ENABLED, &state->flags))
+		return -EINVAL;
+
+	ret = strtobool(buf, &val);
+	if (ret)
+		return ret;
+
+	if (val)
+		set_bit(ECAP_POLARITY_HIGH, &state->flags);
+	else
+		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
+	ecap_attr_show, ecap_attr_store, 0);
+
+static struct attribute *ecap_attributes[] = {
+	&iio_dev_attr_pulse_polarity.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ecap_attribute_group = {
+	.attrs = ecap_attributes,
+};
+
+static int ecap_read_raw(struct iio_dev *idev,
+			struct iio_chan_spec const *ch, int *val,
+			int *val2, long mask)
+{
+	struct ecap_state *state = iio_priv(idev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		/*
+		 * Always return 0 as a pulse width sample
+		 * is only valid in a triggered condition
+		 */
+		*val = 0;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = NSEC_PER_SEC / state->clk_rate;
+		return IIO_VAL_INT_PLUS_NANO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info ecap_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &ecap_attribute_group,
+	.read_raw = &ecap_read_raw,
+};
+
+static irqreturn_t ecap_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *idev = pf->indio_dev;
+	struct ecap_state *state = iio_priv(idev);
+
+	/* Read pulse counter value */
+	*state->buf = readl(state->regs + CAP2);
+
+	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
+
+	iio_trigger_notify_done(idev->trig);
+
+	return IRQ_HANDLED;
+};
+
+
+static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static irqreturn_t ecap_interrupt_handler(int irq, void *private)
+{
+	struct iio_dev *idev = private;
+	struct ecap_state *state = iio_priv(idev);
+	u16 ints;
+
+	iio_trigger_poll(idev->trig, 0);
+
+	/* Clear CAP2 interrupt */
+	ints = readw(state->regs + ECFLG);
+	if (ints & ECINT_CEVT2)
+		writew(ECINT_CEVT2, state->regs + ECCLR);
+	else
+		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
+			 ints);
+
+	return IRQ_HANDLED;
+}
+
+static int ecap_buffer_predisable(struct iio_dev *idev)
+{
+	struct ecap_state *state = iio_priv(idev);
+	int ret = 0;
+	u16 ecctl2;
+
+	/* Stop capture */
+	clear_bit(ECAP_ENABLED, &state->flags);
+	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
+	writew(ecctl2, state->regs + ECCTL2);
+
+	/* Disable and clear all interrupts */
+	writew(0, state->regs + ECEINT);
+	writew(ECINT_ALL, state->regs + ECCLR);
+
+	ret = iio_triggered_buffer_predisable(idev);
+
+	pm_runtime_put_sync(idev->dev.parent);
+
+	return ret;
+}
+
+static int ecap_buffer_postenable(struct iio_dev *idev)
+{
+	struct ecap_state *state = iio_priv(idev);
+	int ret = 0;
+	u16 ecctl1, ecctl2;
+
+	pm_runtime_get_sync(idev->dev.parent);
+
+	/* Configure pulse polarity */
+	ecctl1 = readw(state->regs + ECCTL1);
+	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
+		/* CAP1 rising, CAP2 falling */
+		ecctl1 |= ECCTL1_CAP2POL;
+		ecctl1 &= ~ECCTL1_CAP1POL;
+	} else {
+		/* CAP1 falling, CAP2 rising */
+		ecctl1 &= ~ECCTL1_CAP2POL;
+		ecctl1 |= ECCTL1_CAP1POL;
+	}
+	writew(ecctl1, state->regs + ECCTL1);
+
+	/* Enable CAP2 interrupt */
+	writew(ECINT_CEVT2, state->regs + ECEINT);
+
+	/* Enable capture */
+	ecctl2 = readw(state->regs + ECCTL2);
+	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
+	writew(ecctl2, state->regs + ECCTL2);
+	set_bit(ECAP_ENABLED, &state->flags);
+
+	ret = iio_triggered_buffer_postenable(idev);
+
+	return ret;
+}
+
+static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
+	.postenable = &ecap_buffer_postenable,
+	.predisable = &ecap_buffer_predisable,
+};
+
+static void ecap_init_hw(struct iio_dev *idev)
+{
+	struct ecap_state *state = iio_priv(idev);
+
+	clear_bit(ECAP_ENABLED, &state->flags);
+	set_bit(ECAP_POLARITY_HIGH, &state->flags);
+
+	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
+	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
+	       state->regs + ECCTL1);
+
+	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
+	       state->regs + ECCTL2);
+}
+
+static const struct of_device_id ecap_of_ids[] = {
+	{ .compatible	= "ti,am33xx-ecap" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ecap_of_ids);
+
+static int ecap_probe(struct platform_device *pdev)
+{
+	int irq, ret;
+	struct iio_dev *idev;
+	struct ecap_state *state;
+	struct resource *r;
+	struct clk *clk;
+	struct iio_trigger *trig;
+	u16 status;
+
+	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
+	if (!idev)
+		return -ENOMEM;
+
+	state = iio_priv(idev);
+
+	clk = devm_clk_get(&pdev->dev, "fck");
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "failed to get clock\n");
+		return PTR_ERR(clk);
+	}
+
+	state->clk_rate = clk_get_rate(clk);
+	if (!state->clk_rate) {
+		dev_err(&pdev->dev, "failed to get clock rate\n");
+		return -EINVAL;
+	}
+
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = dev_name(&pdev->dev);
+	idev->modes = INDIO_DIRECT_MODE;
+	idev->info = &ecap_info;
+	idev->channels = ecap_channels;
+	/* One h/w capture and one s/w timestamp channel per instance */
+	idev->num_channels = ARRAY_SIZE(ecap_channels);
+
+	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
+				      idev->name, idev->id);
+	if (!trig)
+		return -ENOMEM;
+	trig->dev.parent = idev->dev.parent;
+	iio_trigger_set_drvdata(trig, idev);
+	trig->ops = &iio_interrupt_trigger_ops;
+
+	ret = iio_trigger_register(trig);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register trigger\n");
+		return ret;
+	}
+
+	ret = iio_triggered_buffer_setup(idev, NULL,
+					 &ecap_trigger_handler,
+					 &ecap_buffer_setup_ops);
+	if (ret)
+		return ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq is specified\n");
+		return irq;
+	}
+	ret = devm_request_irq(&pdev->dev, irq,
+				&ecap_interrupt_handler,
+				0, dev_name(&pdev->dev), idev);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to request irq\n");
+		goto uninit_buffer;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	state->regs = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(state->regs)) {
+		dev_err(&pdev->dev, "unable to remap registers\n");
+		ret = PTR_ERR(state->regs);
+		goto uninit_buffer;
+	};
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to register device\n");
+		goto uninit_buffer;
+	}
+
+	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
+	if (!state->buf) {
+		ret = -ENOMEM;
+		goto uninit_buffer;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+
+	status = pwmss_submodule_state_change(pdev->dev.parent,
+			PWMSS_ECAPCLK_EN);
+	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
+		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
+		ret = -EINVAL;
+		goto pwmss_clk_failure;
+	}
+
+	ecap_init_hw(idev);
+
+	pm_runtime_put_sync(&pdev->dev);
+
+	return 0;
+
+pwmss_clk_failure:
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	iio_device_unregister(idev);
+
+uninit_buffer:
+	iio_triggered_buffer_cleanup(idev);
+
+	return ret;
+}
+
+static int ecap_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+
+	pm_runtime_get_sync(&pdev->dev);
+
+	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
+
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	iio_device_unregister(idev);
+	iio_triggered_buffer_cleanup(idev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ecap_suspend(struct device *dev)
+{
+	struct ecap_state *state = dev_to_ecap_state(dev);
+
+	pm_runtime_get_sync(dev);
+	state->ctx.cap1 = readl(state->regs + CAP1);
+	state->ctx.cap2 = readl(state->regs + CAP2);
+	state->ctx.eceint = readw(state->regs + ECEINT);
+	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
+	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
+	pm_runtime_put_sync(dev);
+
+	/* If capture was active, disable ECAP */
+	if (test_bit(ECAP_ENABLED, &state->flags))
+		pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int ecap_resume(struct device *dev)
+{
+	struct ecap_state *state = dev_to_ecap_state(dev);
+
+	/* If capture was active, enable ECAP */
+	if (test_bit(ECAP_ENABLED, &state->flags))
+		pm_runtime_get_sync(dev);
+
+	pm_runtime_get_sync(dev);
+	writel(state->ctx.cap1, state->regs + CAP1);
+	writel(state->ctx.cap2, state->regs + CAP2);
+	writew(state->ctx.eceint, state->regs + ECEINT);
+	writew(state->ctx.ecctl1, state->regs + ECCTL1);
+	writew(state->ctx.ecctl2, state->regs + ECCTL2);
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
+
+static struct platform_driver ecap_iio_driver = {
+	.driver = {
+		.name		= "ecap",
+		.owner		= THIS_MODULE,
+		.of_match_table = of_match_ptr(ecap_of_ids),
+		.pm		= &ecap_pm_ops,
+	},
+	.probe = ecap_probe,
+	.remove = ecap_remove,
+};
+
+module_platform_driver(ecap_iio_driver);
+
+MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
+MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
+MODULE_LICENSE("GPL");
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2014-02-05 19:01   ` Matt Porter
  0 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: linux-arm-kernel

Adds support for capturing PWM signals using the TI ECAP peripheral.
This driver supports triggered buffer capture of pulses on multiple
ECAP instances. In addition, the driver supports configurable polarity
of the signal to be captured.

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 drivers/iio/pulse/Kconfig  |  20 ++
 drivers/iio/pulse/Makefile |   6 +
 drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 517 insertions(+)
 create mode 100644 drivers/iio/pulse/Kconfig
 create mode 100644 drivers/iio/pulse/Makefile
 create mode 100644 drivers/iio/pulse/tiecap.c

diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
new file mode 100644
index 0000000..9864d4b
--- /dev/null
+++ b/drivers/iio/pulse/Kconfig
@@ -0,0 +1,20 @@
+#
+# Pulse Capture Devices
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Pulse Capture Devices"
+
+config IIO_TIECAP
+	tristate "TI ECAP Pulse Capture"
+	depends on SOC_AM33XX
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	 If you say yes here you get support for the TI ECAP peripheral
+	 in pulse capture mode.
+
+	 This driver can also be built as a module.  If so, the module
+	 will be called tiecap
+
+endmenu
diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
new file mode 100644
index 0000000..94d4b00
--- /dev/null
+++ b/drivers/iio/pulse/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for IIO PWM Capture Devices
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
new file mode 100644
index 0000000..fd96745
--- /dev/null
+++ b/drivers/iio/pulse/tiecap.c
@@ -0,0 +1,491 @@
+/*
+ * ECAP IIO pulse capture driver
+ *
+ * Copyright (C) 2014 Linaro Limited
+ * Author: Matt Porter <mporter@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "../../pwm/pwm-tipwmss.h"
+
+/* ECAP regs and bits */
+#define CAP1			0x08
+#define CAP2			0x0c
+#define ECCTL1			0x28
+#define ECCTL1_RUN_FREE		BIT(15)
+#define ECCTL1_CAPLDEN		BIT(8)
+#define ECCTL1_CAP2POL		BIT(2)
+#define ECCTL1_CTRRST1		BIT(1)
+#define ECCTL1_CAP1POL		BIT(0)
+#define ECCTL2			0x2a
+#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
+#define ECCTL2_TSCTR_FREERUN	BIT(4)
+#define ECCTL2_REARM		BIT(3)
+#define ECCTL2_STOP_WRAP_2	BIT(1)
+#define ECEINT			0x2c
+#define ECFLG			0x2e
+#define ECCLR			0x30
+#define ECINT_CTRCMP		BIT(7)
+#define ECINT_CTRPRD		BIT(6)
+#define ECINT_CTROVF		BIT(5)
+#define ECINT_CEVT4		BIT(4)
+#define ECINT_CEVT3		BIT(3)
+#define ECINT_CEVT2		BIT(2)
+#define ECINT_CEVT1		BIT(1)
+#define ECINT_ALL		(ECINT_CTRCMP |	\
+				ECINT_CTRPRD |	\
+				ECINT_CTROVF |	\
+				ECINT_CEVT4 |	\
+				ECINT_CEVT3 |	\
+				ECINT_CEVT2 |	\
+				ECINT_CEVT1)
+
+/* ECAP driver flags */
+#define ECAP_POLARITY_HIGH	BIT(1)
+#define ECAP_ENABLED		BIT(0)
+
+struct ecap_context {
+	u32	cap1;
+	u32	cap2;
+	u16	ecctl1;
+	u16	ecctl2;
+	u16	eceint;
+};
+
+struct ecap_state {
+	unsigned long		flags;
+	unsigned int		clk_rate;
+	void __iomem		*regs;
+	u32			*buf;
+	struct ecap_context	ctx;
+};
+
+#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
+
+static const struct iio_chan_spec ecap_channels[] = {
+	{
+		.type		= IIO_PULSE,
+		.info_mask_separate =
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index	= 0,
+		.scan_type = {
+			.sign		= 'u',
+			.realbits	= 32,
+			.storagebits	= 32,
+			.endianness	= IIO_LE,
+		},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(1)
+};
+
+static ssize_t ecap_attr_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct ecap_state *state = dev_to_ecap_state(dev);
+
+	return sprintf(buf, "%d\n",
+		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
+}
+
+static ssize_t ecap_attr_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf,
+			       size_t len)
+{
+	int ret;
+	bool val;
+	struct ecap_state *state = dev_to_ecap_state(dev);
+
+	if (test_bit(ECAP_ENABLED, &state->flags))
+		return -EINVAL;
+
+	ret = strtobool(buf, &val);
+	if (ret)
+		return ret;
+
+	if (val)
+		set_bit(ECAP_POLARITY_HIGH, &state->flags);
+	else
+		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
+	ecap_attr_show, ecap_attr_store, 0);
+
+static struct attribute *ecap_attributes[] = {
+	&iio_dev_attr_pulse_polarity.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ecap_attribute_group = {
+	.attrs = ecap_attributes,
+};
+
+static int ecap_read_raw(struct iio_dev *idev,
+			struct iio_chan_spec const *ch, int *val,
+			int *val2, long mask)
+{
+	struct ecap_state *state = iio_priv(idev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		/*
+		 * Always return 0 as a pulse width sample
+		 * is only valid in a triggered condition
+		 */
+		*val = 0;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = NSEC_PER_SEC / state->clk_rate;
+		return IIO_VAL_INT_PLUS_NANO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info ecap_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &ecap_attribute_group,
+	.read_raw = &ecap_read_raw,
+};
+
+static irqreturn_t ecap_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *idev = pf->indio_dev;
+	struct ecap_state *state = iio_priv(idev);
+
+	/* Read pulse counter value */
+	*state->buf = readl(state->regs + CAP2);
+
+	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
+
+	iio_trigger_notify_done(idev->trig);
+
+	return IRQ_HANDLED;
+};
+
+
+static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static irqreturn_t ecap_interrupt_handler(int irq, void *private)
+{
+	struct iio_dev *idev = private;
+	struct ecap_state *state = iio_priv(idev);
+	u16 ints;
+
+	iio_trigger_poll(idev->trig, 0);
+
+	/* Clear CAP2 interrupt */
+	ints = readw(state->regs + ECFLG);
+	if (ints & ECINT_CEVT2)
+		writew(ECINT_CEVT2, state->regs + ECCLR);
+	else
+		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
+			 ints);
+
+	return IRQ_HANDLED;
+}
+
+static int ecap_buffer_predisable(struct iio_dev *idev)
+{
+	struct ecap_state *state = iio_priv(idev);
+	int ret = 0;
+	u16 ecctl2;
+
+	/* Stop capture */
+	clear_bit(ECAP_ENABLED, &state->flags);
+	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
+	writew(ecctl2, state->regs + ECCTL2);
+
+	/* Disable and clear all interrupts */
+	writew(0, state->regs + ECEINT);
+	writew(ECINT_ALL, state->regs + ECCLR);
+
+	ret = iio_triggered_buffer_predisable(idev);
+
+	pm_runtime_put_sync(idev->dev.parent);
+
+	return ret;
+}
+
+static int ecap_buffer_postenable(struct iio_dev *idev)
+{
+	struct ecap_state *state = iio_priv(idev);
+	int ret = 0;
+	u16 ecctl1, ecctl2;
+
+	pm_runtime_get_sync(idev->dev.parent);
+
+	/* Configure pulse polarity */
+	ecctl1 = readw(state->regs + ECCTL1);
+	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
+		/* CAP1 rising, CAP2 falling */
+		ecctl1 |= ECCTL1_CAP2POL;
+		ecctl1 &= ~ECCTL1_CAP1POL;
+	} else {
+		/* CAP1 falling, CAP2 rising */
+		ecctl1 &= ~ECCTL1_CAP2POL;
+		ecctl1 |= ECCTL1_CAP1POL;
+	}
+	writew(ecctl1, state->regs + ECCTL1);
+
+	/* Enable CAP2 interrupt */
+	writew(ECINT_CEVT2, state->regs + ECEINT);
+
+	/* Enable capture */
+	ecctl2 = readw(state->regs + ECCTL2);
+	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
+	writew(ecctl2, state->regs + ECCTL2);
+	set_bit(ECAP_ENABLED, &state->flags);
+
+	ret = iio_triggered_buffer_postenable(idev);
+
+	return ret;
+}
+
+static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
+	.postenable = &ecap_buffer_postenable,
+	.predisable = &ecap_buffer_predisable,
+};
+
+static void ecap_init_hw(struct iio_dev *idev)
+{
+	struct ecap_state *state = iio_priv(idev);
+
+	clear_bit(ECAP_ENABLED, &state->flags);
+	set_bit(ECAP_POLARITY_HIGH, &state->flags);
+
+	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
+	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
+	       state->regs + ECCTL1);
+
+	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
+	       state->regs + ECCTL2);
+}
+
+static const struct of_device_id ecap_of_ids[] = {
+	{ .compatible	= "ti,am33xx-ecap" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ecap_of_ids);
+
+static int ecap_probe(struct platform_device *pdev)
+{
+	int irq, ret;
+	struct iio_dev *idev;
+	struct ecap_state *state;
+	struct resource *r;
+	struct clk *clk;
+	struct iio_trigger *trig;
+	u16 status;
+
+	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
+	if (!idev)
+		return -ENOMEM;
+
+	state = iio_priv(idev);
+
+	clk = devm_clk_get(&pdev->dev, "fck");
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "failed to get clock\n");
+		return PTR_ERR(clk);
+	}
+
+	state->clk_rate = clk_get_rate(clk);
+	if (!state->clk_rate) {
+		dev_err(&pdev->dev, "failed to get clock rate\n");
+		return -EINVAL;
+	}
+
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = dev_name(&pdev->dev);
+	idev->modes = INDIO_DIRECT_MODE;
+	idev->info = &ecap_info;
+	idev->channels = ecap_channels;
+	/* One h/w capture and one s/w timestamp channel per instance */
+	idev->num_channels = ARRAY_SIZE(ecap_channels);
+
+	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
+				      idev->name, idev->id);
+	if (!trig)
+		return -ENOMEM;
+	trig->dev.parent = idev->dev.parent;
+	iio_trigger_set_drvdata(trig, idev);
+	trig->ops = &iio_interrupt_trigger_ops;
+
+	ret = iio_trigger_register(trig);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register trigger\n");
+		return ret;
+	}
+
+	ret = iio_triggered_buffer_setup(idev, NULL,
+					 &ecap_trigger_handler,
+					 &ecap_buffer_setup_ops);
+	if (ret)
+		return ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq is specified\n");
+		return irq;
+	}
+	ret = devm_request_irq(&pdev->dev, irq,
+				&ecap_interrupt_handler,
+				0, dev_name(&pdev->dev), idev);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to request irq\n");
+		goto uninit_buffer;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	state->regs = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(state->regs)) {
+		dev_err(&pdev->dev, "unable to remap registers\n");
+		ret = PTR_ERR(state->regs);
+		goto uninit_buffer;
+	};
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to register device\n");
+		goto uninit_buffer;
+	}
+
+	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
+	if (!state->buf) {
+		ret = -ENOMEM;
+		goto uninit_buffer;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+
+	status = pwmss_submodule_state_change(pdev->dev.parent,
+			PWMSS_ECAPCLK_EN);
+	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
+		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
+		ret = -EINVAL;
+		goto pwmss_clk_failure;
+	}
+
+	ecap_init_hw(idev);
+
+	pm_runtime_put_sync(&pdev->dev);
+
+	return 0;
+
+pwmss_clk_failure:
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	iio_device_unregister(idev);
+
+uninit_buffer:
+	iio_triggered_buffer_cleanup(idev);
+
+	return ret;
+}
+
+static int ecap_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+
+	pm_runtime_get_sync(&pdev->dev);
+
+	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
+
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	iio_device_unregister(idev);
+	iio_triggered_buffer_cleanup(idev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ecap_suspend(struct device *dev)
+{
+	struct ecap_state *state = dev_to_ecap_state(dev);
+
+	pm_runtime_get_sync(dev);
+	state->ctx.cap1 = readl(state->regs + CAP1);
+	state->ctx.cap2 = readl(state->regs + CAP2);
+	state->ctx.eceint = readw(state->regs + ECEINT);
+	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
+	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
+	pm_runtime_put_sync(dev);
+
+	/* If capture was active, disable ECAP */
+	if (test_bit(ECAP_ENABLED, &state->flags))
+		pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int ecap_resume(struct device *dev)
+{
+	struct ecap_state *state = dev_to_ecap_state(dev);
+
+	/* If capture was active, enable ECAP */
+	if (test_bit(ECAP_ENABLED, &state->flags))
+		pm_runtime_get_sync(dev);
+
+	pm_runtime_get_sync(dev);
+	writel(state->ctx.cap1, state->regs + CAP1);
+	writel(state->ctx.cap2, state->regs + CAP2);
+	writew(state->ctx.eceint, state->regs + ECEINT);
+	writew(state->ctx.ecctl1, state->regs + ECCTL1);
+	writew(state->ctx.ecctl2, state->regs + ECCTL2);
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
+
+static struct platform_driver ecap_iio_driver = {
+	.driver = {
+		.name		= "ecap",
+		.owner		= THIS_MODULE,
+		.of_match_table = of_match_ptr(ecap_of_ids),
+		.pm		= &ecap_pm_ops,
+	},
+	.probe = ecap_probe,
+	.remove = ecap_remove,
+};
+
+module_platform_driver(ecap_iio_driver);
+
+MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
+MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
+MODULE_LICENSE("GPL");
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 3/6] iio: enable selection and build of pulse drivers
  2014-02-05 19:01 ` Matt Porter
@ 2014-02-05 19:01   ` Matt Porter
  -1 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

Add the pulse driver subdirectory when configuring and building
IIO.

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 drivers/iio/Kconfig  | 1 +
 drivers/iio/Makefile | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 5dd0e12..286acc3 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -74,6 +74,7 @@ if IIO_TRIGGER
    source "drivers/iio/trigger/Kconfig"
 endif #IIO_TRIGGER
 source "drivers/iio/pressure/Kconfig"
+source "drivers/iio/pulse/Kconfig"
 source "drivers/iio/temperature/Kconfig"
 
 endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 887d390..9a953c9 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -24,5 +24,6 @@ obj-y += light/
 obj-y += magnetometer/
 obj-y += orientation/
 obj-y += pressure/
+obj-y += pulse/
 obj-y += temperature/
 obj-y += trigger/
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 3/6] iio: enable selection and build of pulse drivers
@ 2014-02-05 19:01   ` Matt Porter
  0 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: linux-arm-kernel

Add the pulse driver subdirectory when configuring and building
IIO.

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 drivers/iio/Kconfig  | 1 +
 drivers/iio/Makefile | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 5dd0e12..286acc3 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -74,6 +74,7 @@ if IIO_TRIGGER
    source "drivers/iio/trigger/Kconfig"
 endif #IIO_TRIGGER
 source "drivers/iio/pressure/Kconfig"
+source "drivers/iio/pulse/Kconfig"
 source "drivers/iio/temperature/Kconfig"
 
 endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 887d390..9a953c9 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -24,5 +24,6 @@ obj-y += light/
 obj-y += magnetometer/
 obj-y += orientation/
 obj-y += pressure/
+obj-y += pulse/
 obj-y += temperature/
 obj-y += trigger/
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 4/6] iio: Add ABI docs for pulse capture devices
  2014-02-05 19:01 ` Matt Porter
@ 2014-02-05 19:01   ` Matt Porter
  -1 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

Add standard ABI entries for pulse capture devices. Also add
a separate ABI entry for the TI ECAP driver polarity option.

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 Documentation/ABI/testing/sysfs-bus-iio              | 18 ++++++++++++++++++
 Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap |  9 +++++++++
 2 files changed, 27 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 6e02c50..918a201 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -210,6 +210,14 @@ Contact:	linux-iio@vger.kernel.org
 Description:
 		Scaled humidity measurement in milli percent.
 
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_raw
+KernelVersion:	3.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Raw pulse measurement from channel Y. Units after
+		application of scale and offset are nanoseconds.
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_x_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
@@ -220,6 +228,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_tempY_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_temp_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_offset
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_offset
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_offset
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -251,6 +261,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_z_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_scale
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_scale
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_scale
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -784,6 +796,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_incli_x_en
 What:		/sys/.../iio:deviceX/scan_elements/in_incli_y_en
 What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_en
 What:		/sys/.../iio:deviceX/scan_elements/in_pressure_en
+What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_en
+What:		/sys/.../iio:deviceX/scan_elements/in_pulse_en
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -799,6 +813,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type
 What:		/sys/.../iio:deviceX/scan_elements/in_timestamp_type
 What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_type
 What:		/sys/.../iio:deviceX/scan_elements/in_pressure_type
+What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_type
+What:		/sys/.../iio:deviceX/scan_elements/in_pulse_type
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -845,6 +861,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_incli_y_index
 What:		/sys/.../iio:deviceX/scan_elements/in_timestamp_index
 What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_index
 What:		/sys/.../iio:deviceX/scan_elements/in_pressure_index
+What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_index
+What:		/sys/.../iio:deviceX/scan_elements/in_pulse_index
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap b/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
new file mode 100644
index 0000000..a9e4a9f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
@@ -0,0 +1,9 @@
+What:		/sys/bus/iio/devices/iio:deviceX/pulse_polarityY
+What:		/sys/bus/iio/devices/iio:deviceX/pulse_polarity
+Date:		January 2014
+KernelVersion:	3.15
+Contact:	Matt Porter <mporter@linaro.org>
+Description:
+		Get and set the polarity of the pulse signal to be captured
+		for channel Y.  1 indicates a high pulse signal and 0
+		indicates a low pulse signal.
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 4/6] iio: Add ABI docs for pulse capture devices
@ 2014-02-05 19:01   ` Matt Porter
  0 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: linux-arm-kernel

Add standard ABI entries for pulse capture devices. Also add
a separate ABI entry for the TI ECAP driver polarity option.

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 Documentation/ABI/testing/sysfs-bus-iio              | 18 ++++++++++++++++++
 Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap |  9 +++++++++
 2 files changed, 27 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 6e02c50..918a201 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -210,6 +210,14 @@ Contact:	linux-iio at vger.kernel.org
 Description:
 		Scaled humidity measurement in milli percent.
 
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_raw
+KernelVersion:	3.15
+Contact:	linux-iio at vger.kernel.org
+Description:
+		Raw pulse measurement from channel Y. Units after
+		application of scale and offset are nanoseconds.
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_x_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
@@ -220,6 +228,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_tempY_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_temp_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_offset
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_offset
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_offset
 KernelVersion:	2.6.35
 Contact:	linux-iio at vger.kernel.org
 Description:
@@ -251,6 +261,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_z_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_scale
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_scale
+What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_scale
 KernelVersion:	2.6.35
 Contact:	linux-iio at vger.kernel.org
 Description:
@@ -784,6 +796,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_incli_x_en
 What:		/sys/.../iio:deviceX/scan_elements/in_incli_y_en
 What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_en
 What:		/sys/.../iio:deviceX/scan_elements/in_pressure_en
+What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_en
+What:		/sys/.../iio:deviceX/scan_elements/in_pulse_en
 KernelVersion:	2.6.37
 Contact:	linux-iio at vger.kernel.org
 Description:
@@ -799,6 +813,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type
 What:		/sys/.../iio:deviceX/scan_elements/in_timestamp_type
 What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_type
 What:		/sys/.../iio:deviceX/scan_elements/in_pressure_type
+What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_type
+What:		/sys/.../iio:deviceX/scan_elements/in_pulse_type
 KernelVersion:	2.6.37
 Contact:	linux-iio at vger.kernel.org
 Description:
@@ -845,6 +861,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_incli_y_index
 What:		/sys/.../iio:deviceX/scan_elements/in_timestamp_index
 What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_index
 What:		/sys/.../iio:deviceX/scan_elements/in_pressure_index
+What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_index
+What:		/sys/.../iio:deviceX/scan_elements/in_pulse_index
 KernelVersion:	2.6.37
 Contact:	linux-iio at vger.kernel.org
 Description:
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap b/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
new file mode 100644
index 0000000..a9e4a9f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
@@ -0,0 +1,9 @@
+What:		/sys/bus/iio/devices/iio:deviceX/pulse_polarityY
+What:		/sys/bus/iio/devices/iio:deviceX/pulse_polarity
+Date:		January 2014
+KernelVersion:	3.15
+Contact:	Matt Porter <mporter@linaro.org>
+Description:
+		Get and set the polarity of the pulse signal to be captured
+		for channel Y.  1 indicates a high pulse signal and 0
+		indicates a low pulse signal.
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 5/6] pwm: enable TI PWMSS if the IIO tiecap driver is selected
  2014-02-05 19:01 ` Matt Porter
@ 2014-02-05 19:01   ` Matt Porter
  -1 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

The IIO TI ECAP driver depends on the TI PWMSS management
driver in this subsystem. Enable PWMSS when the IIO TI ECAP
driver is selected.

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 drivers/pwm/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 22f2f28..bd3cc65 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -219,7 +219,7 @@ config  PWM_TIEHRPWM
 
 config  PWM_TIPWMSS
 	bool
-	default y if SOC_AM33XX && (PWM_TIECAP || PWM_TIEHRPWM)
+	default y if SOC_AM33XX && (IIO_TIECAP || PWM_TIECAP || PWM_TIEHRPWM)
 	help
 	  PWM Subsystem driver support for AM33xx SOC.
 
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 5/6] pwm: enable TI PWMSS if the IIO tiecap driver is selected
@ 2014-02-05 19:01   ` Matt Porter
  0 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: linux-arm-kernel

The IIO TI ECAP driver depends on the TI PWMSS management
driver in this subsystem. Enable PWMSS when the IIO TI ECAP
driver is selected.

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 drivers/pwm/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 22f2f28..bd3cc65 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -219,7 +219,7 @@ config  PWM_TIEHRPWM
 
 config  PWM_TIPWMSS
 	bool
-	default y if SOC_AM33XX && (PWM_TIECAP || PWM_TIEHRPWM)
+	default y if SOC_AM33XX && (IIO_TIECAP || PWM_TIECAP || PWM_TIEHRPWM)
 	help
 	  PWM Subsystem driver support for AM33xx SOC.
 
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 6/6] ARM: dts: AM33XX: Add ecap interrupt properties
  2014-02-05 19:01 ` Matt Porter
@ 2014-02-05 19:01   ` Matt Porter
  -1 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

Add missing interrupt properties to the ecap0, ecap1, and ecap2
nodes.

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 arch/arm/boot/dts/am33xx.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 6d95d3d..b4139ba 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -582,6 +582,8 @@
 				compatible = "ti,am33xx-ecap";
 				#pwm-cells = <3>;
 				reg = <0x48300100 0x80>;
+				interrupts = <31>;
+				interrupt-names = "ecap0";
 				ti,hwmods = "ecap0";
 				status = "disabled";
 			};
@@ -610,6 +612,8 @@
 				compatible = "ti,am33xx-ecap";
 				#pwm-cells = <3>;
 				reg = <0x48302100 0x80>;
+				interrupts = <47>;
+				interrupt-names = "ecap1";
 				ti,hwmods = "ecap1";
 				status = "disabled";
 			};
@@ -638,6 +642,8 @@
 				compatible = "ti,am33xx-ecap";
 				#pwm-cells = <3>;
 				reg = <0x48304100 0x80>;
+				interrupts = <61>;
+				interrupt-names = "ecap2";
 				ti,hwmods = "ecap2";
 				status = "disabled";
 			};
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v3 6/6] ARM: dts: AM33XX: Add ecap interrupt properties
@ 2014-02-05 19:01   ` Matt Porter
  0 siblings, 0 replies; 42+ messages in thread
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
  To: linux-arm-kernel

Add missing interrupt properties to the ecap0, ecap1, and ecap2
nodes.

Signed-off-by: Matt Porter <mporter@linaro.org>
---
 arch/arm/boot/dts/am33xx.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 6d95d3d..b4139ba 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -582,6 +582,8 @@
 				compatible = "ti,am33xx-ecap";
 				#pwm-cells = <3>;
 				reg = <0x48300100 0x80>;
+				interrupts = <31>;
+				interrupt-names = "ecap0";
 				ti,hwmods = "ecap0";
 				status = "disabled";
 			};
@@ -610,6 +612,8 @@
 				compatible = "ti,am33xx-ecap";
 				#pwm-cells = <3>;
 				reg = <0x48302100 0x80>;
+				interrupts = <47>;
+				interrupt-names = "ecap1";
 				ti,hwmods = "ecap1";
 				status = "disabled";
 			};
@@ -638,6 +642,8 @@
 				compatible = "ti,am33xx-ecap";
 				#pwm-cells = <3>;
 				reg = <0x48304100 0x80>;
+				interrupts = <61>;
+				interrupt-names = "ecap2";
 				ti,hwmods = "ecap2";
 				status = "disabled";
 			};
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 0/6] IIO pulse capture support for TI ECAP
  2014-02-05 19:01 ` Matt Porter
@ 2014-02-05 20:23   ` Sergei Shtylyov
  -1 siblings, 0 replies; 42+ messages in thread
From: Sergei Shtylyov @ 2014-02-05 20:23 UTC (permalink / raw)
  To: Matt Porter, Jonathan Cameron, Grant Likely, Rob Herring,
	Benoît Cousson, Tony Lindgren, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Thierry Reding
  Cc: Devicetree List, Linux PWM List, Linux IIO List,
	Linux Kernel Mailing List, Linux OMAP List,
	Linux ARM Kernel List

Hello.

On 02/05/2014 10:01 PM, Matt Porter wrote:

[...]

> This series adds support for PWM capture devices within IIO and
> adds a TI ECAP IIO driver.

> PWM capture devices are supported using a new IIO "pulse" channel type.

> The IIO ECAP driver implements interrupt driven triggered buffer capture
> only as raw sample reads are not applicable to this hardware.
> Initially, the driver supports a single pulse width measurement with
> configurable polarity. The ECAP hardware can support measurement of a
> complete period and duty cycle but this is not yet implemented.

    How about pulse counting? I have the hardware that can also count pulses 
in addition to measuring the periods, so I'm interested in this work 
(initially I supported it in driver/misc/ but it got turned down for iio).

WBR, Sergei


^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 0/6] IIO pulse capture support for TI ECAP
@ 2014-02-05 20:23   ` Sergei Shtylyov
  0 siblings, 0 replies; 42+ messages in thread
From: Sergei Shtylyov @ 2014-02-05 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

Hello.

On 02/05/2014 10:01 PM, Matt Porter wrote:

[...]

> This series adds support for PWM capture devices within IIO and
> adds a TI ECAP IIO driver.

> PWM capture devices are supported using a new IIO "pulse" channel type.

> The IIO ECAP driver implements interrupt driven triggered buffer capture
> only as raw sample reads are not applicable to this hardware.
> Initially, the driver supports a single pulse width measurement with
> configurable polarity. The ECAP hardware can support measurement of a
> complete period and duty cycle but this is not yet implemented.

    How about pulse counting? I have the hardware that can also count pulses 
in addition to measuring the periods, so I'm interested in this work 
(initially I supported it in driver/misc/ but it got turned down for iio).

WBR, Sergei

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 4/6] iio: Add ABI docs for pulse capture devices
  2014-02-05 19:01   ` Matt Porter
@ 2014-02-15 11:40     ` Jonathan Cameron
  -1 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 11:40 UTC (permalink / raw)
  To: Matt Porter, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List,
	Lars-Peter Clausen

On 05/02/14 19:01, Matt Porter wrote:
> Add standard ABI entries for pulse capture devices. Also add
> a separate ABI entry for the TI ECAP driver polarity option.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>
Lars, I've cc'd you for the various Analog devices dds and frequency devices.
Ideally we want a consistent interface for devices capturing info about these
waveforms and devices generating them.  Please CC anyone else who might have
opinions.  This interface is not obvious, so we need to spend some time getting
it right.
> ---
>   Documentation/ABI/testing/sysfs-bus-iio              | 18 ++++++++++++++++++
>   Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap |  9 +++++++++
>   2 files changed, 27 insertions(+)
>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 6e02c50..918a201 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -210,6 +210,14 @@ Contact:	linux-iio@vger.kernel.org
>   Description:
>   		Scaled humidity measurement in milli percent.
>
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_raw
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_raw
> +KernelVersion:	3.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Raw pulse measurement from channel Y. Units after
> +		application of scale and offset are nanoseconds.
> +
I wonder if we are being a little bit too specific in this interface.  What other things
might we want to capture about a pulse?  Right now you have it either capturing the
'high width' or the 'low width'.  Presumably there are devices that will capture
both via different 'channels'?

So lets list what we might capture about a wave - including values computable from
the base quantities.
* High time
* Low time
* Mark Space Ratio
* Period (rising edge to rising edge  or falling edge to falling edge).
(This might be treated as instantaneous frequency - as would be done in
frequency modulation)
* Peak to peak Amplitude.

For imperfect waves
* Rise time,
* Fall time,

Funnily enough this is pretty much what an oscilloscope will allow you to automatically
measure on a square wave...

So next question is base units for the time measurements.
I'd prefer the base to be seconds rather than nano seconds
for consistency with the other time measurements already in the ABI.  All it will do is
change the type for scale factors output in sysfs.  Userspace can trivially convert
back to nano seconds if that is what it wants.  If we need to futher expand our small
units, then that is fine.

Next, the question is do we want to cover other waveform types. The dds drivers that
are most relevant are still in staging, so we can mess around with their interfaces
without 'too' much pain.  They allow specifying of a waveform 'type' (square, triangle,
sine so far I think).

So do we want to have an iio type corresponding to a time feature of a waveform then
use modifiers for the various characteristics. I'll describe this in the sysfs attribute
names (they translate directly into the buffer channels, but are easier to read!)


We do have a few modifiers that lead to different units already so there is precedence
e.g. IIO_MOD_SUM_SQUARED_X_Y_Z, (often (m/s^2)^2
for the following (as long as well documented!)
in_waveformX_type - (Square, triangle etc).

in_waveformX_hightime_raw
in_waveformX_lowtime_raw
in_waveformX_period_raw
in_waveformX_markspace_raw
in_waveformX_peaktopeak_raw (similar to existing peak abi elements - these are not typically
                             read from a buffer and hence we have rushed them in as a modifier).
in_waveformX_risetime_raw
in_waveformX_falltime_raw


Would this work for both this sort of capture device and for synthesis chips?



>   What:		/sys/bus/iio/devices/iio:deviceX/in_accel_offset
>   What:		/sys/bus/iio/devices/iio:deviceX/in_accel_x_offset
>   What:		/sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
> @@ -220,6 +228,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_tempY_offset
>   What:		/sys/bus/iio/devices/iio:deviceX/in_temp_offset
>   What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
>   What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_offset
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_offset
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_offset
>   KernelVersion:	2.6.35
>   Contact:	linux-iio@vger.kernel.org
>   Description:
> @@ -251,6 +261,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
>   What:		/sys/bus/iio/devices/iio:deviceX/in_magn_z_scale
>   What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
>   What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_scale
>   KernelVersion:	2.6.35
>   Contact:	linux-iio@vger.kernel.org
>   Description:
> @@ -784,6 +796,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_incli_x_en
>   What:		/sys/.../iio:deviceX/scan_elements/in_incli_y_en
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_en
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressure_en
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_en
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulse_en
>   KernelVersion:	2.6.37
>   Contact:	linux-iio@vger.kernel.org
>   Description:
> @@ -799,6 +813,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type
>   What:		/sys/.../iio:deviceX/scan_elements/in_timestamp_type
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_type
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressure_type
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_type
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulse_type
>   KernelVersion:	2.6.37
>   Contact:	linux-iio@vger.kernel.org
>   Description:
> @@ -845,6 +861,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_incli_y_index
>   What:		/sys/.../iio:deviceX/scan_elements/in_timestamp_index
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_index
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressure_index
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_index
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulse_index
>   KernelVersion:	2.6.37
>   Contact:	linux-iio@vger.kernel.org
>   Description:
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap b/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
> new file mode 100644
> index 0000000..a9e4a9f
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
> @@ -0,0 +1,9 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/pulse_polarityY
> +What:		/sys/bus/iio/devices/iio:deviceX/pulse_polarity
> +Date:		January 2014
> +KernelVersion:	3.15
> +Contact:	Matt Porter <mporter@linaro.org>
> +Description:
> +		Get and set the polarity of the pulse signal to be captured
> +		for channel Y.  1 indicates a high pulse signal and 0
> +		indicates a low pulse signal.
>


^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 4/6] iio: Add ABI docs for pulse capture devices
@ 2014-02-15 11:40     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 11:40 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/02/14 19:01, Matt Porter wrote:
> Add standard ABI entries for pulse capture devices. Also add
> a separate ABI entry for the TI ECAP driver polarity option.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>
Lars, I've cc'd you for the various Analog devices dds and frequency devices.
Ideally we want a consistent interface for devices capturing info about these
waveforms and devices generating them.  Please CC anyone else who might have
opinions.  This interface is not obvious, so we need to spend some time getting
it right.
> ---
>   Documentation/ABI/testing/sysfs-bus-iio              | 18 ++++++++++++++++++
>   Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap |  9 +++++++++
>   2 files changed, 27 insertions(+)
>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 6e02c50..918a201 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -210,6 +210,14 @@ Contact:	linux-iio at vger.kernel.org
>   Description:
>   		Scaled humidity measurement in milli percent.
>
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_raw
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_raw
> +KernelVersion:	3.15
> +Contact:	linux-iio at vger.kernel.org
> +Description:
> +		Raw pulse measurement from channel Y. Units after
> +		application of scale and offset are nanoseconds.
> +
I wonder if we are being a little bit too specific in this interface.  What other things
might we want to capture about a pulse?  Right now you have it either capturing the
'high width' or the 'low width'.  Presumably there are devices that will capture
both via different 'channels'?

So lets list what we might capture about a wave - including values computable from
the base quantities.
* High time
* Low time
* Mark Space Ratio
* Period (rising edge to rising edge  or falling edge to falling edge).
(This might be treated as instantaneous frequency - as would be done in
frequency modulation)
* Peak to peak Amplitude.

For imperfect waves
* Rise time,
* Fall time,

Funnily enough this is pretty much what an oscilloscope will allow you to automatically
measure on a square wave...

So next question is base units for the time measurements.
I'd prefer the base to be seconds rather than nano seconds
for consistency with the other time measurements already in the ABI.  All it will do is
change the type for scale factors output in sysfs.  Userspace can trivially convert
back to nano seconds if that is what it wants.  If we need to futher expand our small
units, then that is fine.

Next, the question is do we want to cover other waveform types. The dds drivers that
are most relevant are still in staging, so we can mess around with their interfaces
without 'too' much pain.  They allow specifying of a waveform 'type' (square, triangle,
sine so far I think).

So do we want to have an iio type corresponding to a time feature of a waveform then
use modifiers for the various characteristics. I'll describe this in the sysfs attribute
names (they translate directly into the buffer channels, but are easier to read!)


We do have a few modifiers that lead to different units already so there is precedence
e.g. IIO_MOD_SUM_SQUARED_X_Y_Z, (often (m/s^2)^2
for the following (as long as well documented!)
in_waveformX_type - (Square, triangle etc).

in_waveformX_hightime_raw
in_waveformX_lowtime_raw
in_waveformX_period_raw
in_waveformX_markspace_raw
in_waveformX_peaktopeak_raw (similar to existing peak abi elements - these are not typically
                             read from a buffer and hence we have rushed them in as a modifier).
in_waveformX_risetime_raw
in_waveformX_falltime_raw


Would this work for both this sort of capture device and for synthesis chips?



>   What:		/sys/bus/iio/devices/iio:deviceX/in_accel_offset
>   What:		/sys/bus/iio/devices/iio:deviceX/in_accel_x_offset
>   What:		/sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
> @@ -220,6 +228,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_tempY_offset
>   What:		/sys/bus/iio/devices/iio:deviceX/in_temp_offset
>   What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
>   What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_offset
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_offset
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_offset
>   KernelVersion:	2.6.35
>   Contact:	linux-iio at vger.kernel.org
>   Description:
> @@ -251,6 +261,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
>   What:		/sys/bus/iio/devices/iio:deviceX/in_magn_z_scale
>   What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
>   What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulseY_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pulse_scale
>   KernelVersion:	2.6.35
>   Contact:	linux-iio at vger.kernel.org
>   Description:
> @@ -784,6 +796,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_incli_x_en
>   What:		/sys/.../iio:deviceX/scan_elements/in_incli_y_en
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_en
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressure_en
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_en
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulse_en
>   KernelVersion:	2.6.37
>   Contact:	linux-iio at vger.kernel.org
>   Description:
> @@ -799,6 +813,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type
>   What:		/sys/.../iio:deviceX/scan_elements/in_timestamp_type
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_type
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressure_type
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_type
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulse_type
>   KernelVersion:	2.6.37
>   Contact:	linux-iio at vger.kernel.org
>   Description:
> @@ -845,6 +861,8 @@ What:		/sys/.../iio:deviceX/scan_elements/in_incli_y_index
>   What:		/sys/.../iio:deviceX/scan_elements/in_timestamp_index
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressureY_index
>   What:		/sys/.../iio:deviceX/scan_elements/in_pressure_index
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulseY_index
> +What:		/sys/.../iio:deviceX/scan_elements/in_pulse_index
>   KernelVersion:	2.6.37
>   Contact:	linux-iio at vger.kernel.org
>   Description:
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap b/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
> new file mode 100644
> index 0000000..a9e4a9f
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
> @@ -0,0 +1,9 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/pulse_polarityY
> +What:		/sys/bus/iio/devices/iio:deviceX/pulse_polarity
> +Date:		January 2014
> +KernelVersion:	3.15
> +Contact:	Matt Porter <mporter@linaro.org>
> +Description:
> +		Get and set the polarity of the pulse signal to be captured
> +		for channel Y.  1 indicates a high pulse signal and 0
> +		indicates a low pulse signal.
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 3/6] iio: enable selection and build of pulse drivers
@ 2014-02-15 11:41     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 11:41 UTC (permalink / raw)
  To: Matt Porter, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

On 05/02/14 19:01, Matt Porter wrote:
> Add the pulse driver subdirectory when configuring and building
> IIO.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>
Hi Matt,

This wants rolling into the driver patch before it.  No point it giving it
it's own patch.  This will just lead to confusion on git bisections etc.


Jonathan
> ---
>   drivers/iio/Kconfig  | 1 +
>   drivers/iio/Makefile | 1 +
>   2 files changed, 2 insertions(+)
>
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 5dd0e12..286acc3 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -74,6 +74,7 @@ if IIO_TRIGGER
>      source "drivers/iio/trigger/Kconfig"
>   endif #IIO_TRIGGER
>   source "drivers/iio/pressure/Kconfig"
> +source "drivers/iio/pulse/Kconfig"
>   source "drivers/iio/temperature/Kconfig"
>
>   endif # IIO
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 887d390..9a953c9 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -24,5 +24,6 @@ obj-y += light/
>   obj-y += magnetometer/
>   obj-y += orientation/
>   obj-y += pressure/
> +obj-y += pulse/
>   obj-y += temperature/
>   obj-y += trigger/
>


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 3/6] iio: enable selection and build of pulse drivers
@ 2014-02-15 11:41     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 11:41 UTC (permalink / raw)
  To: Matt Porter, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

On 05/02/14 19:01, Matt Porter wrote:
> Add the pulse driver subdirectory when configuring and building
> IIO.
>
> Signed-off-by: Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Hi Matt,

This wants rolling into the driver patch before it.  No point it giving it
it's own patch.  This will just lead to confusion on git bisections etc.


Jonathan
> ---
>   drivers/iio/Kconfig  | 1 +
>   drivers/iio/Makefile | 1 +
>   2 files changed, 2 insertions(+)
>
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 5dd0e12..286acc3 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -74,6 +74,7 @@ if IIO_TRIGGER
>      source "drivers/iio/trigger/Kconfig"
>   endif #IIO_TRIGGER
>   source "drivers/iio/pressure/Kconfig"
> +source "drivers/iio/pulse/Kconfig"
>   source "drivers/iio/temperature/Kconfig"
>
>   endif # IIO
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 887d390..9a953c9 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -24,5 +24,6 @@ obj-y += light/
>   obj-y += magnetometer/
>   obj-y += orientation/
>   obj-y += pressure/
> +obj-y += pulse/
>   obj-y += temperature/
>   obj-y += trigger/
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 3/6] iio: enable selection and build of pulse drivers
@ 2014-02-15 11:41     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 11:41 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/02/14 19:01, Matt Porter wrote:
> Add the pulse driver subdirectory when configuring and building
> IIO.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>
Hi Matt,

This wants rolling into the driver patch before it.  No point it giving it
it's own patch.  This will just lead to confusion on git bisections etc.


Jonathan
> ---
>   drivers/iio/Kconfig  | 1 +
>   drivers/iio/Makefile | 1 +
>   2 files changed, 2 insertions(+)
>
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 5dd0e12..286acc3 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -74,6 +74,7 @@ if IIO_TRIGGER
>      source "drivers/iio/trigger/Kconfig"
>   endif #IIO_TRIGGER
>   source "drivers/iio/pressure/Kconfig"
> +source "drivers/iio/pulse/Kconfig"
>   source "drivers/iio/temperature/Kconfig"
>
>   endif # IIO
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 887d390..9a953c9 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -24,5 +24,6 @@ obj-y += light/
>   obj-y += magnetometer/
>   obj-y += orientation/
>   obj-y += pressure/
> +obj-y += pulse/
>   obj-y += temperature/
>   obj-y += trigger/
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 6/6] ARM: dts: AM33XX: Add ecap interrupt properties
  2014-02-05 19:01   ` Matt Porter
@ 2014-02-15 11:43     ` Jonathan Cameron
  -1 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 11:43 UTC (permalink / raw)
  To: Matt Porter, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

On 05/02/14 19:01, Matt Porter wrote:
> Add missing interrupt properties to the ecap0, ecap1, and ecap2
> nodes.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>
This one is unconnected from the rest of the series really so should go
into the relevant arch tree whenever it make sense.

Jonathan
> ---
>   arch/arm/boot/dts/am33xx.dtsi | 6 ++++++
>   1 file changed, 6 insertions(+)
>
> diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
> index 6d95d3d..b4139ba 100644
> --- a/arch/arm/boot/dts/am33xx.dtsi
> +++ b/arch/arm/boot/dts/am33xx.dtsi
> @@ -582,6 +582,8 @@
>   				compatible = "ti,am33xx-ecap";
>   				#pwm-cells = <3>;
>   				reg = <0x48300100 0x80>;
> +				interrupts = <31>;
> +				interrupt-names = "ecap0";
>   				ti,hwmods = "ecap0";
>   				status = "disabled";
>   			};
> @@ -610,6 +612,8 @@
>   				compatible = "ti,am33xx-ecap";
>   				#pwm-cells = <3>;
>   				reg = <0x48302100 0x80>;
> +				interrupts = <47>;
> +				interrupt-names = "ecap1";
>   				ti,hwmods = "ecap1";
>   				status = "disabled";
>   			};
> @@ -638,6 +642,8 @@
>   				compatible = "ti,am33xx-ecap";
>   				#pwm-cells = <3>;
>   				reg = <0x48304100 0x80>;
> +				interrupts = <61>;
> +				interrupt-names = "ecap2";
>   				ti,hwmods = "ecap2";
>   				status = "disabled";
>   			};
>


^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 6/6] ARM: dts: AM33XX: Add ecap interrupt properties
@ 2014-02-15 11:43     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 11:43 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/02/14 19:01, Matt Porter wrote:
> Add missing interrupt properties to the ecap0, ecap1, and ecap2
> nodes.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>
This one is unconnected from the rest of the series really so should go
into the relevant arch tree whenever it make sense.

Jonathan
> ---
>   arch/arm/boot/dts/am33xx.dtsi | 6 ++++++
>   1 file changed, 6 insertions(+)
>
> diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
> index 6d95d3d..b4139ba 100644
> --- a/arch/arm/boot/dts/am33xx.dtsi
> +++ b/arch/arm/boot/dts/am33xx.dtsi
> @@ -582,6 +582,8 @@
>   				compatible = "ti,am33xx-ecap";
>   				#pwm-cells = <3>;
>   				reg = <0x48300100 0x80>;
> +				interrupts = <31>;
> +				interrupt-names = "ecap0";
>   				ti,hwmods = "ecap0";
>   				status = "disabled";
>   			};
> @@ -610,6 +612,8 @@
>   				compatible = "ti,am33xx-ecap";
>   				#pwm-cells = <3>;
>   				reg = <0x48302100 0x80>;
> +				interrupts = <47>;
> +				interrupt-names = "ecap1";
>   				ti,hwmods = "ecap1";
>   				status = "disabled";
>   			};
> @@ -638,6 +642,8 @@
>   				compatible = "ti,am33xx-ecap";
>   				#pwm-cells = <3>;
>   				reg = <0x48304100 0x80>;
> +				interrupts = <61>;
> +				interrupt-names = "ecap2";
>   				ti,hwmods = "ecap2";
>   				status = "disabled";
>   			};
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 0/6] IIO pulse capture support for TI ECAP
@ 2014-02-15 11:48     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 11:48 UTC (permalink / raw)
  To: Sergei Shtylyov, Matt Porter, Grant Likely, Rob Herring,
	Benoît Cousson, Tony Lindgren, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Thierry Reding
  Cc: Devicetree List, Linux PWM List, Linux IIO List,
	Linux Kernel Mailing List, Linux OMAP List,
	Linux ARM Kernel List

On 05/02/14 20:23, Sergei Shtylyov wrote:
> Hello.
>
> On 02/05/2014 10:01 PM, Matt Porter wrote:
>
> [...]
>
>> This series adds support for PWM capture devices within IIO and
>> adds a TI ECAP IIO driver.
>
>> PWM capture devices are supported using a new IIO "pulse" channel type.
>
>> The IIO ECAP driver implements interrupt driven triggered buffer capture
>> only as raw sample reads are not applicable to this hardware.
>> Initially, the driver supports a single pulse width measurement with
>> configurable polarity. The ECAP hardware can support measurement of a
>> complete period and duty cycle but this is not yet implemented.
>

> How about pulse counting? I have the hardware that can also
> countpulses in addition to measuring the periods, so I'm interested
> in this work (initially I supported it in driver/misc/ but it got
> turned down for iio).
>
I'm afraid I'd forgotten this completely if you can dig out some links
to that discussion it would be great. Only one I can find right now
is a request that you post an ABI introducing this as a generic pulse
counter rather than a speed measurement.

Good point though - under my previous sketched outline, perhaps

in_waveform_cyclecount_input?  (I would imagine that it doesn't come
out needing scaling!)

> WBR, Sergei
>
> --
> 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	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 0/6] IIO pulse capture support for TI ECAP
@ 2014-02-15 11:48     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 11:48 UTC (permalink / raw)
  To: Sergei Shtylyov, Matt Porter, Grant Likely, Rob Herring,
	Benoît Cousson, Tony Lindgren, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Thierry Reding
  Cc: Devicetree List, Linux PWM List, Linux IIO List,
	Linux Kernel Mailing List, Linux OMAP List,
	Linux ARM Kernel List

On 05/02/14 20:23, Sergei Shtylyov wrote:
> Hello.
>
> On 02/05/2014 10:01 PM, Matt Porter wrote:
>
> [...]
>
>> This series adds support for PWM capture devices within IIO and
>> adds a TI ECAP IIO driver.
>
>> PWM capture devices are supported using a new IIO "pulse" channel type.
>
>> The IIO ECAP driver implements interrupt driven triggered buffer capture
>> only as raw sample reads are not applicable to this hardware.
>> Initially, the driver supports a single pulse width measurement with
>> configurable polarity. The ECAP hardware can support measurement of a
>> complete period and duty cycle but this is not yet implemented.
>

> How about pulse counting? I have the hardware that can also
> countpulses in addition to measuring the periods, so I'm interested
> in this work (initially I supported it in driver/misc/ but it got
> turned down for iio).
>
I'm afraid I'd forgotten this completely if you can dig out some links
to that discussion it would be great. Only one I can find right now
is a request that you post an ABI introducing this as a generic pulse
counter rather than a speed measurement.

Good point though - under my previous sketched outline, perhaps

in_waveform_cyclecount_input?  (I would imagine that it doesn't come
out needing scaling!)

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

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 0/6] IIO pulse capture support for TI ECAP
@ 2014-02-15 11:48     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 11:48 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/02/14 20:23, Sergei Shtylyov wrote:
> Hello.
>
> On 02/05/2014 10:01 PM, Matt Porter wrote:
>
> [...]
>
>> This series adds support for PWM capture devices within IIO and
>> adds a TI ECAP IIO driver.
>
>> PWM capture devices are supported using a new IIO "pulse" channel type.
>
>> The IIO ECAP driver implements interrupt driven triggered buffer capture
>> only as raw sample reads are not applicable to this hardware.
>> Initially, the driver supports a single pulse width measurement with
>> configurable polarity. The ECAP hardware can support measurement of a
>> complete period and duty cycle but this is not yet implemented.
>

> How about pulse counting? I have the hardware that can also
> countpulses in addition to measuring the periods, so I'm interested
> in this work (initially I supported it in driver/misc/ but it got
> turned down for iio).
>
I'm afraid I'd forgotten this completely if you can dig out some links
to that discussion it would be great. Only one I can find right now
is a request that you post an ABI introducing this as a generic pulse
counter rather than a speed measurement.

Good point though - under my previous sketched outline, perhaps

in_waveform_cyclecount_input?  (I would imagine that it doesn't come
out needing scaling!)

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

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2014-02-15 12:19     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 12:19 UTC (permalink / raw)
  To: Matt Porter, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

On 05/02/14 19:01, Matt Porter wrote:
> Adds support for capturing PWM signals using the TI ECAP peripheral.
> This driver supports triggered buffer capture of pulses on multiple
> ECAP instances. In addition, the driver supports configurable polarity
> of the signal to be captured.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>
Ignoring the ABI questions - there are a few bits and bobs inline.

Sorry again that it took me so long to take a look at this.
Ouch, still far too much unread email in my inbox.
> ---
>   drivers/iio/pulse/Kconfig  |  20 ++
>   drivers/iio/pulse/Makefile |   6 +
>   drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 517 insertions(+)
>   create mode 100644 drivers/iio/pulse/Kconfig
>   create mode 100644 drivers/iio/pulse/Makefile
>   create mode 100644 drivers/iio/pulse/tiecap.c
>
> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
> new file mode 100644
> index 0000000..9864d4b
> --- /dev/null
> +++ b/drivers/iio/pulse/Kconfig
> @@ -0,0 +1,20 @@
> +#
> +# Pulse Capture Devices
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "Pulse Capture Devices"
> +
> +config IIO_TIECAP
> +	tristate "TI ECAP Pulse Capture"
> +	depends on SOC_AM33XX
Looks like it rather more specifically depends on the pwm supplied on
that soc.  I haven't checked but a dependency on that would seem
more logical to me.  Pitty it doesn't have a generic interface, because
then we could relax the dependency and it would allow you to get a lot
more build coverage.
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	 If you say yes here you get support for the TI ECAP peripheral
> +	 in pulse capture mode.
> +
> +	 This driver can also be built as a module.  If so, the module
> +	 will be called tiecap
> +
> +endmenu
> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
> new file mode 100644
> index 0000000..94d4b00
> --- /dev/null
> +++ b/drivers/iio/pulse/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for IIO PWM Capture Devices
> +#
> +
> +# When adding new entries keep the list in alphabetical order
> +obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
> new file mode 100644
> index 0000000..fd96745
> --- /dev/null
> +++ b/drivers/iio/pulse/tiecap.c
> @@ -0,0 +1,491 @@
> +/*
> + * ECAP IIO pulse capture driver
> + *
> + * Copyright (C) 2014 Linaro Limited
> + * Author: Matt Porter <mporter@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "../../pwm/pwm-tipwmss.h"
If we need functions from in there, surely it should be in
include/linux/pwm instead of burried in the driver tree?
> +
> +/* ECAP regs and bits */
Please add prefixes to all of these.  i.e. #define ECAP_CAP1
etc.

This is to avoid the possibility of a naming clash if these rather
short names ever turn in in one of the headers included above.

> +#define CAP1			0x08
> +#define CAP2			0x0c
> +#define ECCTL1			0x28
> +#define ECCTL1_RUN_FREE		BIT(15)
> +#define ECCTL1_CAPLDEN		BIT(8)
> +#define ECCTL1_CAP2POL		BIT(2)
> +#define ECCTL1_CTRRST1		BIT(1)
> +#define ECCTL1_CAP1POL		BIT(0)
> +#define ECCTL2			0x2a
> +#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
> +#define ECCTL2_TSCTR_FREERUN	BIT(4)
> +#define ECCTL2_REARM		BIT(3)
> +#define ECCTL2_STOP_WRAP_2	BIT(1)
> +#define ECEINT			0x2c
> +#define ECFLG			0x2e
> +#define ECCLR			0x30
> +#define ECINT_CTRCMP		BIT(7)
> +#define ECINT_CTRPRD		BIT(6)
> +#define ECINT_CTROVF		BIT(5)
> +#define ECINT_CEVT4		BIT(4)
> +#define ECINT_CEVT3		BIT(3)
> +#define ECINT_CEVT2		BIT(2)
> +#define ECINT_CEVT1		BIT(1)
> +#define ECINT_ALL		(ECINT_CTRCMP |	\
> +				ECINT_CTRPRD |	\
> +				ECINT_CTROVF |	\
> +				ECINT_CEVT4 |	\
> +				ECINT_CEVT3 |	\
> +				ECINT_CEVT2 |	\
> +				ECINT_CEVT1)
> +
> +/* ECAP driver flags */
> +#define ECAP_POLARITY_HIGH	BIT(1)
> +#define ECAP_ENABLED		BIT(0)
> +
> +struct ecap_context {
> +	u32	cap1;
> +	u32	cap2;
> +	u16	ecctl1;
> +	u16	ecctl2;
> +	u16	eceint;
> +};
> +
> +struct ecap_state {
> +	unsigned long		flags;
> +	unsigned int		clk_rate;
> +	void __iomem		*regs;
> +	u32			*buf;
> +	struct ecap_context	ctx;
> +};
> +
> +#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
> +
> +static const struct iio_chan_spec ecap_channels[] = {
> +	{
> +		.type		= IIO_PULSE,
> +		.info_mask_separate =
> +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> +		.scan_index	= 0,
> +		.scan_type = {
> +			.sign		= 'u',
> +			.realbits	= 32,
> +			.storagebits	= 32,
> +			.endianness	= IIO_LE,
> +		},
> +	},
> +	IIO_CHAN_SOFT_TIMESTAMP(1)
> +};
> +
> +static ssize_t ecap_attr_show(struct device *dev,
> +			      struct device_attribute *attr, char *buf)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	return sprintf(buf, "%d\n",
> +		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
> +}
> +
This function needs a name indicating what attr.  There may be only one
now, but who knows in future.
> +static ssize_t ecap_attr_store(struct device *dev,
> +			       struct device_attribute *attr,
> +			       const char *buf,
> +			       size_t len)
> +{
> +	int ret;
> +	bool val;
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		return -EINVAL;
> +
Not obvious how a boolean value corresponds to a polarity. If we change to
the abi I suggested earlier, I'd have this as in_waveformX_inverted.
> +	ret = strtobool(buf, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val)
> +		set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +	else
> +		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
> +	ecap_attr_show, ecap_attr_store, 0);
> +
> +static struct attribute *ecap_attributes[] = {
> +	&iio_dev_attr_pulse_polarity.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group ecap_attribute_group = {
> +	.attrs = ecap_attributes,
> +};
> +
> +static int ecap_read_raw(struct iio_dev *idev,
> +			struct iio_chan_spec const *ch, int *val,
> +			int *val2, long mask)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
Just don't provide the BIT(IIO_CHAN_INFO_RAW) in the info_mask_separate
and there will be no userspace interface. Thus indicating it can't be
read better than returning a 'fake' value.
> +		/*
> +		 * Always return 0 as a pulse width sample
> +		 * is only valid in a triggered condition
> +		 */
> +		*val = 0;
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = 0;
> +		*val2 = NSEC_PER_SEC / state->clk_rate;
> +		return IIO_VAL_INT_PLUS_NANO;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info ecap_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &ecap_attribute_group,
> +	.read_raw = &ecap_read_raw,
> +};
> +
> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
> +{
> +	struct iio_poll_func *pf = private;
> +	struct iio_dev *idev = pf->indio_dev;
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	/* Read pulse counter value */
> +	*state->buf = readl(state->regs + CAP2);
> +
To get more accurate timestamps, you could grab that in the top half handler.
See how ad7887 does it for example using the utility function
iio_pollfunc_store_time.

> +	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
> +
> +	iio_trigger_notify_done(idev->trig);
> +
> +	return IRQ_HANDLED;
> +};
> +
> +
> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
> +{
> +	struct iio_dev *idev = private;
> +	struct ecap_state *state = iio_priv(idev);
> +	u16 ints;
> +
> +	iio_trigger_poll(idev->trig, 0);
> +
Do you want to clear the interrupt here, or in the try renable callback
for the trigger?  That would only be called after the data has been grabbed.


> +	/* Clear CAP2 interrupt */
> +	ints = readw(state->regs + ECFLG);
> +	if (ints & ECINT_CEVT2)
> +		writew(ECINT_CEVT2, state->regs + ECCLR);
> +	else
> +		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
> +			 ints);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int ecap_buffer_predisable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl2;
> +
> +	/* Stop capture */
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
> +	writew(ecctl2, state->regs + ECCTL2);
> +
> +	/* Disable and clear all interrupts */
> +	writew(0, state->regs + ECEINT);
> +	writew(ECINT_ALL, state->regs + ECCLR);
> +
By ordering convention this should be in reverse order of the postenable,
hence this predisable call should be first in the function.
Not sure it matters in reality but is more 'obviously' correct which
is always a good thing.

> +	ret = iio_triggered_buffer_predisable(idev);
> +
> +	pm_runtime_put_sync(idev->dev.parent);
> +
> +	return ret;
> +}
> +
> +static int ecap_buffer_postenable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl1, ecctl2;
> +
> +	pm_runtime_get_sync(idev->dev.parent);
> +
> +	/* Configure pulse polarity */
> +	ecctl1 = readw(state->regs + ECCTL1);
> +	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
> +		/* CAP1 rising, CAP2 falling */
> +		ecctl1 |= ECCTL1_CAP2POL;
> +		ecctl1 &= ~ECCTL1_CAP1POL;
> +	} else {
> +		/* CAP1 falling, CAP2 rising */
> +		ecctl1 &= ~ECCTL1_CAP2POL;
> +		ecctl1 |= ECCTL1_CAP1POL;
> +	}
> +	writew(ecctl1, state->regs + ECCTL1);
> +
> +	/* Enable CAP2 interrupt */
> +	writew(ECINT_CEVT2, state->regs + ECEINT);
> +
> +	/* Enable capture */
> +	ecctl2 = readw(state->regs + ECCTL2);
> +	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
> +	writew(ecctl2, state->regs + ECCTL2);
> +	set_bit(ECAP_ENABLED, &state->flags);
> +
> +	ret = iio_triggered_buffer_postenable(idev);
> +
> +	return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
> +	.postenable = &ecap_buffer_postenable,
> +	.predisable = &ecap_buffer_predisable,
> +};
> +
> +static void ecap_init_hw(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
> +	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
> +	       state->regs + ECCTL1);
> +
> +	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
> +	       state->regs + ECCTL2);
> +}
> +
> +static const struct of_device_id ecap_of_ids[] = {
> +	{ .compatible	= "ti,am33xx-ecap" },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
> +
> +static int ecap_probe(struct platform_device *pdev)
> +{
> +	int irq, ret;
> +	struct iio_dev *idev;
> +	struct ecap_state *state;
> +	struct resource *r;
> +	struct clk *clk;
> +	struct iio_trigger *trig;
> +	u16 status;
> +
> +	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
> +	if (!idev)
> +		return -ENOMEM;
> +
> +	state = iio_priv(idev);
> +
> +	clk = devm_clk_get(&pdev->dev, "fck");
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "failed to get clock\n");
> +		return PTR_ERR(clk);
> +	}
> +
> +	state->clk_rate = clk_get_rate(clk);
> +	if (!state->clk_rate) {
> +		dev_err(&pdev->dev, "failed to get clock rate\n");
> +		return -EINVAL;
> +	}
> +
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = dev_name(&pdev->dev);
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &ecap_info;
> +	idev->channels = ecap_channels;
> +	/* One h/w capture and one s/w timestamp channel per instance */

Note that the timestamp may or may not be enabled...  Technically it's
possible to enable the timestamp and not the channel - that's just rather
odd though funnily enough it would give access to the period of the waveform
in this case...

> +	idev->num_channels = ARRAY_SIZE(ecap_channels);
> +
> +	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
> +				      idev->name, idev->id);
> +	if (!trig)
> +		return -ENOMEM;
> +	trig->dev.parent = idev->dev.parent;
> +	iio_trigger_set_drvdata(trig, idev);
> +	trig->ops = &iio_interrupt_trigger_ops;
> +
> +	ret = iio_trigger_register(trig);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register trigger\n");
> +		return ret;
> +	}
> +
> +	ret = iio_triggered_buffer_setup(idev, NULL,
> +					 &ecap_trigger_handler,
> +					 &ecap_buffer_setup_ops);
> +	if (ret)
> +		return ret;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq is specified\n");
> +		return irq;
> +	}
> +	ret = devm_request_irq(&pdev->dev, irq,
> +				&ecap_interrupt_handler,
> +				0, dev_name(&pdev->dev), idev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to request irq\n");
> +		goto uninit_buffer;
> +	}
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	state->regs = devm_ioremap_resource(&pdev->dev, r);
> +	if (IS_ERR(state->regs)) {
> +		dev_err(&pdev->dev, "unable to remap registers\n");
> +		ret = PTR_ERR(state->regs);
> +		goto uninit_buffer;
> +	};
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "unable to register device\n");
> +		goto uninit_buffer;
> +	}
> +

Unless I missed something scan_bytes won't contain the correct size until
the channels have been enabled from userspace.

There would be nothing to stop you allocating 'enough' space here. But then
if you do that, you might as well do it directly in the state structure
rather than with this separate allocation.

> +	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
> +	if (!state->buf) {
> +		ret = -ENOMEM;
> +		goto uninit_buffer;
> +	}
> +
> +	pm_runtime_enable(&pdev->dev);
> +	pm_runtime_get_sync(&pdev->dev);
> +
Does this initialize capture?
> +	status = pwmss_submodule_state_change(pdev->dev.parent,
> +			PWMSS_ECAPCLK_EN);
> +	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
> +		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
> +		ret = -EINVAL;
> +		goto pwmss_clk_failure;
> +	}
> +
> +	ecap_init_hw(idev);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +
> +	return 0;
> +
> +pwmss_clk_failure:
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	iio_device_unregister(idev);
> +
> +uninit_buffer:
> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return ret;
> +}
> +
> +static int ecap_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	iio_device_unregister(idev);

I'd normally expect the device unregister to be the first element of remove
(and the last of probe) given it is responsible for providing the user
space interfaces.  Can you explain why this doesn't make sense here?

> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ecap_suspend(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	state->ctx.cap1 = readl(state->regs + CAP1);
> +	state->ctx.cap2 = readl(state->regs + CAP2);
> +	state->ctx.eceint = readw(state->regs + ECEINT);
> +	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
> +	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	/* If capture was active, disable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int ecap_resume(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	/* If capture was active, enable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_get_sync(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	writel(state->ctx.cap1, state->regs + CAP1);
> +	writel(state->ctx.cap2, state->regs + CAP2);
> +	writew(state->ctx.eceint, state->regs + ECEINT);
> +	writew(state->ctx.ecctl1, state->regs + ECCTL1);
> +	writew(state->ctx.ecctl2, state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
> +
> +static struct platform_driver ecap_iio_driver = {
> +	.driver = {
> +		.name		= "ecap",
> +		.owner		= THIS_MODULE,
> +		.of_match_table = of_match_ptr(ecap_of_ids),
> +		.pm		= &ecap_pm_ops,
> +	},
> +	.probe = ecap_probe,
> +	.remove = ecap_remove,
> +};
> +
> +module_platform_driver(ecap_iio_driver);
> +
> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
> +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
> +MODULE_LICENSE("GPL");
>


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2014-02-15 12:19     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 12:19 UTC (permalink / raw)
  To: Matt Porter, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

On 05/02/14 19:01, Matt Porter wrote:
> Adds support for capturing PWM signals using the TI ECAP peripheral.
> This driver supports triggered buffer capture of pulses on multiple
> ECAP instances. In addition, the driver supports configurable polarity
> of the signal to be captured.
>
> Signed-off-by: Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Ignoring the ABI questions - there are a few bits and bobs inline.

Sorry again that it took me so long to take a look at this.
Ouch, still far too much unread email in my inbox.
> ---
>   drivers/iio/pulse/Kconfig  |  20 ++
>   drivers/iio/pulse/Makefile |   6 +
>   drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 517 insertions(+)
>   create mode 100644 drivers/iio/pulse/Kconfig
>   create mode 100644 drivers/iio/pulse/Makefile
>   create mode 100644 drivers/iio/pulse/tiecap.c
>
> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
> new file mode 100644
> index 0000000..9864d4b
> --- /dev/null
> +++ b/drivers/iio/pulse/Kconfig
> @@ -0,0 +1,20 @@
> +#
> +# Pulse Capture Devices
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "Pulse Capture Devices"
> +
> +config IIO_TIECAP
> +	tristate "TI ECAP Pulse Capture"
> +	depends on SOC_AM33XX
Looks like it rather more specifically depends on the pwm supplied on
that soc.  I haven't checked but a dependency on that would seem
more logical to me.  Pitty it doesn't have a generic interface, because
then we could relax the dependency and it would allow you to get a lot
more build coverage.
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	 If you say yes here you get support for the TI ECAP peripheral
> +	 in pulse capture mode.
> +
> +	 This driver can also be built as a module.  If so, the module
> +	 will be called tiecap
> +
> +endmenu
> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
> new file mode 100644
> index 0000000..94d4b00
> --- /dev/null
> +++ b/drivers/iio/pulse/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for IIO PWM Capture Devices
> +#
> +
> +# When adding new entries keep the list in alphabetical order
> +obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
> new file mode 100644
> index 0000000..fd96745
> --- /dev/null
> +++ b/drivers/iio/pulse/tiecap.c
> @@ -0,0 +1,491 @@
> +/*
> + * ECAP IIO pulse capture driver
> + *
> + * Copyright (C) 2014 Linaro Limited
> + * Author: Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "../../pwm/pwm-tipwmss.h"
If we need functions from in there, surely it should be in
include/linux/pwm instead of burried in the driver tree?
> +
> +/* ECAP regs and bits */
Please add prefixes to all of these.  i.e. #define ECAP_CAP1
etc.

This is to avoid the possibility of a naming clash if these rather
short names ever turn in in one of the headers included above.

> +#define CAP1			0x08
> +#define CAP2			0x0c
> +#define ECCTL1			0x28
> +#define ECCTL1_RUN_FREE		BIT(15)
> +#define ECCTL1_CAPLDEN		BIT(8)
> +#define ECCTL1_CAP2POL		BIT(2)
> +#define ECCTL1_CTRRST1		BIT(1)
> +#define ECCTL1_CAP1POL		BIT(0)
> +#define ECCTL2			0x2a
> +#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
> +#define ECCTL2_TSCTR_FREERUN	BIT(4)
> +#define ECCTL2_REARM		BIT(3)
> +#define ECCTL2_STOP_WRAP_2	BIT(1)
> +#define ECEINT			0x2c
> +#define ECFLG			0x2e
> +#define ECCLR			0x30
> +#define ECINT_CTRCMP		BIT(7)
> +#define ECINT_CTRPRD		BIT(6)
> +#define ECINT_CTROVF		BIT(5)
> +#define ECINT_CEVT4		BIT(4)
> +#define ECINT_CEVT3		BIT(3)
> +#define ECINT_CEVT2		BIT(2)
> +#define ECINT_CEVT1		BIT(1)
> +#define ECINT_ALL		(ECINT_CTRCMP |	\
> +				ECINT_CTRPRD |	\
> +				ECINT_CTROVF |	\
> +				ECINT_CEVT4 |	\
> +				ECINT_CEVT3 |	\
> +				ECINT_CEVT2 |	\
> +				ECINT_CEVT1)
> +
> +/* ECAP driver flags */
> +#define ECAP_POLARITY_HIGH	BIT(1)
> +#define ECAP_ENABLED		BIT(0)
> +
> +struct ecap_context {
> +	u32	cap1;
> +	u32	cap2;
> +	u16	ecctl1;
> +	u16	ecctl2;
> +	u16	eceint;
> +};
> +
> +struct ecap_state {
> +	unsigned long		flags;
> +	unsigned int		clk_rate;
> +	void __iomem		*regs;
> +	u32			*buf;
> +	struct ecap_context	ctx;
> +};
> +
> +#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
> +
> +static const struct iio_chan_spec ecap_channels[] = {
> +	{
> +		.type		= IIO_PULSE,
> +		.info_mask_separate =
> +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> +		.scan_index	= 0,
> +		.scan_type = {
> +			.sign		= 'u',
> +			.realbits	= 32,
> +			.storagebits	= 32,
> +			.endianness	= IIO_LE,
> +		},
> +	},
> +	IIO_CHAN_SOFT_TIMESTAMP(1)
> +};
> +
> +static ssize_t ecap_attr_show(struct device *dev,
> +			      struct device_attribute *attr, char *buf)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	return sprintf(buf, "%d\n",
> +		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
> +}
> +
This function needs a name indicating what attr.  There may be only one
now, but who knows in future.
> +static ssize_t ecap_attr_store(struct device *dev,
> +			       struct device_attribute *attr,
> +			       const char *buf,
> +			       size_t len)
> +{
> +	int ret;
> +	bool val;
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		return -EINVAL;
> +
Not obvious how a boolean value corresponds to a polarity. If we change to
the abi I suggested earlier, I'd have this as in_waveformX_inverted.
> +	ret = strtobool(buf, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val)
> +		set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +	else
> +		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
> +	ecap_attr_show, ecap_attr_store, 0);
> +
> +static struct attribute *ecap_attributes[] = {
> +	&iio_dev_attr_pulse_polarity.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group ecap_attribute_group = {
> +	.attrs = ecap_attributes,
> +};
> +
> +static int ecap_read_raw(struct iio_dev *idev,
> +			struct iio_chan_spec const *ch, int *val,
> +			int *val2, long mask)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
Just don't provide the BIT(IIO_CHAN_INFO_RAW) in the info_mask_separate
and there will be no userspace interface. Thus indicating it can't be
read better than returning a 'fake' value.
> +		/*
> +		 * Always return 0 as a pulse width sample
> +		 * is only valid in a triggered condition
> +		 */
> +		*val = 0;
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = 0;
> +		*val2 = NSEC_PER_SEC / state->clk_rate;
> +		return IIO_VAL_INT_PLUS_NANO;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info ecap_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &ecap_attribute_group,
> +	.read_raw = &ecap_read_raw,
> +};
> +
> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
> +{
> +	struct iio_poll_func *pf = private;
> +	struct iio_dev *idev = pf->indio_dev;
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	/* Read pulse counter value */
> +	*state->buf = readl(state->regs + CAP2);
> +
To get more accurate timestamps, you could grab that in the top half handler.
See how ad7887 does it for example using the utility function
iio_pollfunc_store_time.

> +	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
> +
> +	iio_trigger_notify_done(idev->trig);
> +
> +	return IRQ_HANDLED;
> +};
> +
> +
> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
> +{
> +	struct iio_dev *idev = private;
> +	struct ecap_state *state = iio_priv(idev);
> +	u16 ints;
> +
> +	iio_trigger_poll(idev->trig, 0);
> +
Do you want to clear the interrupt here, or in the try renable callback
for the trigger?  That would only be called after the data has been grabbed.


> +	/* Clear CAP2 interrupt */
> +	ints = readw(state->regs + ECFLG);
> +	if (ints & ECINT_CEVT2)
> +		writew(ECINT_CEVT2, state->regs + ECCLR);
> +	else
> +		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
> +			 ints);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int ecap_buffer_predisable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl2;
> +
> +	/* Stop capture */
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
> +	writew(ecctl2, state->regs + ECCTL2);
> +
> +	/* Disable and clear all interrupts */
> +	writew(0, state->regs + ECEINT);
> +	writew(ECINT_ALL, state->regs + ECCLR);
> +
By ordering convention this should be in reverse order of the postenable,
hence this predisable call should be first in the function.
Not sure it matters in reality but is more 'obviously' correct which
is always a good thing.

> +	ret = iio_triggered_buffer_predisable(idev);
> +
> +	pm_runtime_put_sync(idev->dev.parent);
> +
> +	return ret;
> +}
> +
> +static int ecap_buffer_postenable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl1, ecctl2;
> +
> +	pm_runtime_get_sync(idev->dev.parent);
> +
> +	/* Configure pulse polarity */
> +	ecctl1 = readw(state->regs + ECCTL1);
> +	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
> +		/* CAP1 rising, CAP2 falling */
> +		ecctl1 |= ECCTL1_CAP2POL;
> +		ecctl1 &= ~ECCTL1_CAP1POL;
> +	} else {
> +		/* CAP1 falling, CAP2 rising */
> +		ecctl1 &= ~ECCTL1_CAP2POL;
> +		ecctl1 |= ECCTL1_CAP1POL;
> +	}
> +	writew(ecctl1, state->regs + ECCTL1);
> +
> +	/* Enable CAP2 interrupt */
> +	writew(ECINT_CEVT2, state->regs + ECEINT);
> +
> +	/* Enable capture */
> +	ecctl2 = readw(state->regs + ECCTL2);
> +	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
> +	writew(ecctl2, state->regs + ECCTL2);
> +	set_bit(ECAP_ENABLED, &state->flags);
> +
> +	ret = iio_triggered_buffer_postenable(idev);
> +
> +	return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
> +	.postenable = &ecap_buffer_postenable,
> +	.predisable = &ecap_buffer_predisable,
> +};
> +
> +static void ecap_init_hw(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
> +	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
> +	       state->regs + ECCTL1);
> +
> +	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
> +	       state->regs + ECCTL2);
> +}
> +
> +static const struct of_device_id ecap_of_ids[] = {
> +	{ .compatible	= "ti,am33xx-ecap" },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
> +
> +static int ecap_probe(struct platform_device *pdev)
> +{
> +	int irq, ret;
> +	struct iio_dev *idev;
> +	struct ecap_state *state;
> +	struct resource *r;
> +	struct clk *clk;
> +	struct iio_trigger *trig;
> +	u16 status;
> +
> +	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
> +	if (!idev)
> +		return -ENOMEM;
> +
> +	state = iio_priv(idev);
> +
> +	clk = devm_clk_get(&pdev->dev, "fck");
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "failed to get clock\n");
> +		return PTR_ERR(clk);
> +	}
> +
> +	state->clk_rate = clk_get_rate(clk);
> +	if (!state->clk_rate) {
> +		dev_err(&pdev->dev, "failed to get clock rate\n");
> +		return -EINVAL;
> +	}
> +
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = dev_name(&pdev->dev);
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &ecap_info;
> +	idev->channels = ecap_channels;
> +	/* One h/w capture and one s/w timestamp channel per instance */

Note that the timestamp may or may not be enabled...  Technically it's
possible to enable the timestamp and not the channel - that's just rather
odd though funnily enough it would give access to the period of the waveform
in this case...

> +	idev->num_channels = ARRAY_SIZE(ecap_channels);
> +
> +	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
> +				      idev->name, idev->id);
> +	if (!trig)
> +		return -ENOMEM;
> +	trig->dev.parent = idev->dev.parent;
> +	iio_trigger_set_drvdata(trig, idev);
> +	trig->ops = &iio_interrupt_trigger_ops;
> +
> +	ret = iio_trigger_register(trig);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register trigger\n");
> +		return ret;
> +	}
> +
> +	ret = iio_triggered_buffer_setup(idev, NULL,
> +					 &ecap_trigger_handler,
> +					 &ecap_buffer_setup_ops);
> +	if (ret)
> +		return ret;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq is specified\n");
> +		return irq;
> +	}
> +	ret = devm_request_irq(&pdev->dev, irq,
> +				&ecap_interrupt_handler,
> +				0, dev_name(&pdev->dev), idev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to request irq\n");
> +		goto uninit_buffer;
> +	}
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	state->regs = devm_ioremap_resource(&pdev->dev, r);
> +	if (IS_ERR(state->regs)) {
> +		dev_err(&pdev->dev, "unable to remap registers\n");
> +		ret = PTR_ERR(state->regs);
> +		goto uninit_buffer;
> +	};
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "unable to register device\n");
> +		goto uninit_buffer;
> +	}
> +

Unless I missed something scan_bytes won't contain the correct size until
the channels have been enabled from userspace.

There would be nothing to stop you allocating 'enough' space here. But then
if you do that, you might as well do it directly in the state structure
rather than with this separate allocation.

> +	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
> +	if (!state->buf) {
> +		ret = -ENOMEM;
> +		goto uninit_buffer;
> +	}
> +
> +	pm_runtime_enable(&pdev->dev);
> +	pm_runtime_get_sync(&pdev->dev);
> +
Does this initialize capture?
> +	status = pwmss_submodule_state_change(pdev->dev.parent,
> +			PWMSS_ECAPCLK_EN);
> +	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
> +		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
> +		ret = -EINVAL;
> +		goto pwmss_clk_failure;
> +	}
> +
> +	ecap_init_hw(idev);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +
> +	return 0;
> +
> +pwmss_clk_failure:
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	iio_device_unregister(idev);
> +
> +uninit_buffer:
> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return ret;
> +}
> +
> +static int ecap_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	iio_device_unregister(idev);

I'd normally expect the device unregister to be the first element of remove
(and the last of probe) given it is responsible for providing the user
space interfaces.  Can you explain why this doesn't make sense here?

> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ecap_suspend(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	state->ctx.cap1 = readl(state->regs + CAP1);
> +	state->ctx.cap2 = readl(state->regs + CAP2);
> +	state->ctx.eceint = readw(state->regs + ECEINT);
> +	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
> +	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	/* If capture was active, disable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int ecap_resume(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	/* If capture was active, enable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_get_sync(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	writel(state->ctx.cap1, state->regs + CAP1);
> +	writel(state->ctx.cap2, state->regs + CAP2);
> +	writew(state->ctx.eceint, state->regs + ECEINT);
> +	writew(state->ctx.ecctl1, state->regs + ECCTL1);
> +	writew(state->ctx.ecctl2, state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
> +
> +static struct platform_driver ecap_iio_driver = {
> +	.driver = {
> +		.name		= "ecap",
> +		.owner		= THIS_MODULE,
> +		.of_match_table = of_match_ptr(ecap_of_ids),
> +		.pm		= &ecap_pm_ops,
> +	},
> +	.probe = ecap_probe,
> +	.remove = ecap_remove,
> +};
> +
> +module_platform_driver(ecap_iio_driver);
> +
> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
> +MODULE_AUTHOR("Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
> +MODULE_LICENSE("GPL");
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2014-02-15 12:19     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2014-02-15 12:19 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/02/14 19:01, Matt Porter wrote:
> Adds support for capturing PWM signals using the TI ECAP peripheral.
> This driver supports triggered buffer capture of pulses on multiple
> ECAP instances. In addition, the driver supports configurable polarity
> of the signal to be captured.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>
Ignoring the ABI questions - there are a few bits and bobs inline.

Sorry again that it took me so long to take a look at this.
Ouch, still far too much unread email in my inbox.
> ---
>   drivers/iio/pulse/Kconfig  |  20 ++
>   drivers/iio/pulse/Makefile |   6 +
>   drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 517 insertions(+)
>   create mode 100644 drivers/iio/pulse/Kconfig
>   create mode 100644 drivers/iio/pulse/Makefile
>   create mode 100644 drivers/iio/pulse/tiecap.c
>
> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
> new file mode 100644
> index 0000000..9864d4b
> --- /dev/null
> +++ b/drivers/iio/pulse/Kconfig
> @@ -0,0 +1,20 @@
> +#
> +# Pulse Capture Devices
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "Pulse Capture Devices"
> +
> +config IIO_TIECAP
> +	tristate "TI ECAP Pulse Capture"
> +	depends on SOC_AM33XX
Looks like it rather more specifically depends on the pwm supplied on
that soc.  I haven't checked but a dependency on that would seem
more logical to me.  Pitty it doesn't have a generic interface, because
then we could relax the dependency and it would allow you to get a lot
more build coverage.
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	 If you say yes here you get support for the TI ECAP peripheral
> +	 in pulse capture mode.
> +
> +	 This driver can also be built as a module.  If so, the module
> +	 will be called tiecap
> +
> +endmenu
> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
> new file mode 100644
> index 0000000..94d4b00
> --- /dev/null
> +++ b/drivers/iio/pulse/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for IIO PWM Capture Devices
> +#
> +
> +# When adding new entries keep the list in alphabetical order
> +obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
> new file mode 100644
> index 0000000..fd96745
> --- /dev/null
> +++ b/drivers/iio/pulse/tiecap.c
> @@ -0,0 +1,491 @@
> +/*
> + * ECAP IIO pulse capture driver
> + *
> + * Copyright (C) 2014 Linaro Limited
> + * Author: Matt Porter <mporter@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "../../pwm/pwm-tipwmss.h"
If we need functions from in there, surely it should be in
include/linux/pwm instead of burried in the driver tree?
> +
> +/* ECAP regs and bits */
Please add prefixes to all of these.  i.e. #define ECAP_CAP1
etc.

This is to avoid the possibility of a naming clash if these rather
short names ever turn in in one of the headers included above.

> +#define CAP1			0x08
> +#define CAP2			0x0c
> +#define ECCTL1			0x28
> +#define ECCTL1_RUN_FREE		BIT(15)
> +#define ECCTL1_CAPLDEN		BIT(8)
> +#define ECCTL1_CAP2POL		BIT(2)
> +#define ECCTL1_CTRRST1		BIT(1)
> +#define ECCTL1_CAP1POL		BIT(0)
> +#define ECCTL2			0x2a
> +#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
> +#define ECCTL2_TSCTR_FREERUN	BIT(4)
> +#define ECCTL2_REARM		BIT(3)
> +#define ECCTL2_STOP_WRAP_2	BIT(1)
> +#define ECEINT			0x2c
> +#define ECFLG			0x2e
> +#define ECCLR			0x30
> +#define ECINT_CTRCMP		BIT(7)
> +#define ECINT_CTRPRD		BIT(6)
> +#define ECINT_CTROVF		BIT(5)
> +#define ECINT_CEVT4		BIT(4)
> +#define ECINT_CEVT3		BIT(3)
> +#define ECINT_CEVT2		BIT(2)
> +#define ECINT_CEVT1		BIT(1)
> +#define ECINT_ALL		(ECINT_CTRCMP |	\
> +				ECINT_CTRPRD |	\
> +				ECINT_CTROVF |	\
> +				ECINT_CEVT4 |	\
> +				ECINT_CEVT3 |	\
> +				ECINT_CEVT2 |	\
> +				ECINT_CEVT1)
> +
> +/* ECAP driver flags */
> +#define ECAP_POLARITY_HIGH	BIT(1)
> +#define ECAP_ENABLED		BIT(0)
> +
> +struct ecap_context {
> +	u32	cap1;
> +	u32	cap2;
> +	u16	ecctl1;
> +	u16	ecctl2;
> +	u16	eceint;
> +};
> +
> +struct ecap_state {
> +	unsigned long		flags;
> +	unsigned int		clk_rate;
> +	void __iomem		*regs;
> +	u32			*buf;
> +	struct ecap_context	ctx;
> +};
> +
> +#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
> +
> +static const struct iio_chan_spec ecap_channels[] = {
> +	{
> +		.type		= IIO_PULSE,
> +		.info_mask_separate =
> +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> +		.scan_index	= 0,
> +		.scan_type = {
> +			.sign		= 'u',
> +			.realbits	= 32,
> +			.storagebits	= 32,
> +			.endianness	= IIO_LE,
> +		},
> +	},
> +	IIO_CHAN_SOFT_TIMESTAMP(1)
> +};
> +
> +static ssize_t ecap_attr_show(struct device *dev,
> +			      struct device_attribute *attr, char *buf)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	return sprintf(buf, "%d\n",
> +		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
> +}
> +
This function needs a name indicating what attr.  There may be only one
now, but who knows in future.
> +static ssize_t ecap_attr_store(struct device *dev,
> +			       struct device_attribute *attr,
> +			       const char *buf,
> +			       size_t len)
> +{
> +	int ret;
> +	bool val;
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		return -EINVAL;
> +
Not obvious how a boolean value corresponds to a polarity. If we change to
the abi I suggested earlier, I'd have this as in_waveformX_inverted.
> +	ret = strtobool(buf, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val)
> +		set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +	else
> +		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
> +	ecap_attr_show, ecap_attr_store, 0);
> +
> +static struct attribute *ecap_attributes[] = {
> +	&iio_dev_attr_pulse_polarity.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group ecap_attribute_group = {
> +	.attrs = ecap_attributes,
> +};
> +
> +static int ecap_read_raw(struct iio_dev *idev,
> +			struct iio_chan_spec const *ch, int *val,
> +			int *val2, long mask)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
Just don't provide the BIT(IIO_CHAN_INFO_RAW) in the info_mask_separate
and there will be no userspace interface. Thus indicating it can't be
read better than returning a 'fake' value.
> +		/*
> +		 * Always return 0 as a pulse width sample
> +		 * is only valid in a triggered condition
> +		 */
> +		*val = 0;
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = 0;
> +		*val2 = NSEC_PER_SEC / state->clk_rate;
> +		return IIO_VAL_INT_PLUS_NANO;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info ecap_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &ecap_attribute_group,
> +	.read_raw = &ecap_read_raw,
> +};
> +
> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
> +{
> +	struct iio_poll_func *pf = private;
> +	struct iio_dev *idev = pf->indio_dev;
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	/* Read pulse counter value */
> +	*state->buf = readl(state->regs + CAP2);
> +
To get more accurate timestamps, you could grab that in the top half handler.
See how ad7887 does it for example using the utility function
iio_pollfunc_store_time.

> +	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
> +
> +	iio_trigger_notify_done(idev->trig);
> +
> +	return IRQ_HANDLED;
> +};
> +
> +
> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
> +{
> +	struct iio_dev *idev = private;
> +	struct ecap_state *state = iio_priv(idev);
> +	u16 ints;
> +
> +	iio_trigger_poll(idev->trig, 0);
> +
Do you want to clear the interrupt here, or in the try renable callback
for the trigger?  That would only be called after the data has been grabbed.


> +	/* Clear CAP2 interrupt */
> +	ints = readw(state->regs + ECFLG);
> +	if (ints & ECINT_CEVT2)
> +		writew(ECINT_CEVT2, state->regs + ECCLR);
> +	else
> +		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
> +			 ints);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int ecap_buffer_predisable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl2;
> +
> +	/* Stop capture */
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
> +	writew(ecctl2, state->regs + ECCTL2);
> +
> +	/* Disable and clear all interrupts */
> +	writew(0, state->regs + ECEINT);
> +	writew(ECINT_ALL, state->regs + ECCLR);
> +
By ordering convention this should be in reverse order of the postenable,
hence this predisable call should be first in the function.
Not sure it matters in reality but is more 'obviously' correct which
is always a good thing.

> +	ret = iio_triggered_buffer_predisable(idev);
> +
> +	pm_runtime_put_sync(idev->dev.parent);
> +
> +	return ret;
> +}
> +
> +static int ecap_buffer_postenable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl1, ecctl2;
> +
> +	pm_runtime_get_sync(idev->dev.parent);
> +
> +	/* Configure pulse polarity */
> +	ecctl1 = readw(state->regs + ECCTL1);
> +	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
> +		/* CAP1 rising, CAP2 falling */
> +		ecctl1 |= ECCTL1_CAP2POL;
> +		ecctl1 &= ~ECCTL1_CAP1POL;
> +	} else {
> +		/* CAP1 falling, CAP2 rising */
> +		ecctl1 &= ~ECCTL1_CAP2POL;
> +		ecctl1 |= ECCTL1_CAP1POL;
> +	}
> +	writew(ecctl1, state->regs + ECCTL1);
> +
> +	/* Enable CAP2 interrupt */
> +	writew(ECINT_CEVT2, state->regs + ECEINT);
> +
> +	/* Enable capture */
> +	ecctl2 = readw(state->regs + ECCTL2);
> +	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
> +	writew(ecctl2, state->regs + ECCTL2);
> +	set_bit(ECAP_ENABLED, &state->flags);
> +
> +	ret = iio_triggered_buffer_postenable(idev);
> +
> +	return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
> +	.postenable = &ecap_buffer_postenable,
> +	.predisable = &ecap_buffer_predisable,
> +};
> +
> +static void ecap_init_hw(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
> +	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
> +	       state->regs + ECCTL1);
> +
> +	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
> +	       state->regs + ECCTL2);
> +}
> +
> +static const struct of_device_id ecap_of_ids[] = {
> +	{ .compatible	= "ti,am33xx-ecap" },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
> +
> +static int ecap_probe(struct platform_device *pdev)
> +{
> +	int irq, ret;
> +	struct iio_dev *idev;
> +	struct ecap_state *state;
> +	struct resource *r;
> +	struct clk *clk;
> +	struct iio_trigger *trig;
> +	u16 status;
> +
> +	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
> +	if (!idev)
> +		return -ENOMEM;
> +
> +	state = iio_priv(idev);
> +
> +	clk = devm_clk_get(&pdev->dev, "fck");
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "failed to get clock\n");
> +		return PTR_ERR(clk);
> +	}
> +
> +	state->clk_rate = clk_get_rate(clk);
> +	if (!state->clk_rate) {
> +		dev_err(&pdev->dev, "failed to get clock rate\n");
> +		return -EINVAL;
> +	}
> +
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = dev_name(&pdev->dev);
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &ecap_info;
> +	idev->channels = ecap_channels;
> +	/* One h/w capture and one s/w timestamp channel per instance */

Note that the timestamp may or may not be enabled...  Technically it's
possible to enable the timestamp and not the channel - that's just rather
odd though funnily enough it would give access to the period of the waveform
in this case...

> +	idev->num_channels = ARRAY_SIZE(ecap_channels);
> +
> +	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
> +				      idev->name, idev->id);
> +	if (!trig)
> +		return -ENOMEM;
> +	trig->dev.parent = idev->dev.parent;
> +	iio_trigger_set_drvdata(trig, idev);
> +	trig->ops = &iio_interrupt_trigger_ops;
> +
> +	ret = iio_trigger_register(trig);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register trigger\n");
> +		return ret;
> +	}
> +
> +	ret = iio_triggered_buffer_setup(idev, NULL,
> +					 &ecap_trigger_handler,
> +					 &ecap_buffer_setup_ops);
> +	if (ret)
> +		return ret;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq is specified\n");
> +		return irq;
> +	}
> +	ret = devm_request_irq(&pdev->dev, irq,
> +				&ecap_interrupt_handler,
> +				0, dev_name(&pdev->dev), idev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to request irq\n");
> +		goto uninit_buffer;
> +	}
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	state->regs = devm_ioremap_resource(&pdev->dev, r);
> +	if (IS_ERR(state->regs)) {
> +		dev_err(&pdev->dev, "unable to remap registers\n");
> +		ret = PTR_ERR(state->regs);
> +		goto uninit_buffer;
> +	};
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "unable to register device\n");
> +		goto uninit_buffer;
> +	}
> +

Unless I missed something scan_bytes won't contain the correct size until
the channels have been enabled from userspace.

There would be nothing to stop you allocating 'enough' space here. But then
if you do that, you might as well do it directly in the state structure
rather than with this separate allocation.

> +	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
> +	if (!state->buf) {
> +		ret = -ENOMEM;
> +		goto uninit_buffer;
> +	}
> +
> +	pm_runtime_enable(&pdev->dev);
> +	pm_runtime_get_sync(&pdev->dev);
> +
Does this initialize capture?
> +	status = pwmss_submodule_state_change(pdev->dev.parent,
> +			PWMSS_ECAPCLK_EN);
> +	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
> +		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
> +		ret = -EINVAL;
> +		goto pwmss_clk_failure;
> +	}
> +
> +	ecap_init_hw(idev);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +
> +	return 0;
> +
> +pwmss_clk_failure:
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	iio_device_unregister(idev);
> +
> +uninit_buffer:
> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return ret;
> +}
> +
> +static int ecap_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	iio_device_unregister(idev);

I'd normally expect the device unregister to be the first element of remove
(and the last of probe) given it is responsible for providing the user
space interfaces.  Can you explain why this doesn't make sense here?

> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ecap_suspend(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	state->ctx.cap1 = readl(state->regs + CAP1);
> +	state->ctx.cap2 = readl(state->regs + CAP2);
> +	state->ctx.eceint = readw(state->regs + ECEINT);
> +	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
> +	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	/* If capture was active, disable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int ecap_resume(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	/* If capture was active, enable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_get_sync(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	writel(state->ctx.cap1, state->regs + CAP1);
> +	writel(state->ctx.cap2, state->regs + CAP2);
> +	writew(state->ctx.eceint, state->regs + ECEINT);
> +	writew(state->ctx.ecctl1, state->regs + ECCTL1);
> +	writew(state->ctx.ecctl2, state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
> +
> +static struct platform_driver ecap_iio_driver = {
> +	.driver = {
> +		.name		= "ecap",
> +		.owner		= THIS_MODULE,
> +		.of_match_table = of_match_ptr(ecap_of_ids),
> +		.pm		= &ecap_pm_ops,
> +	},
> +	.probe = ecap_probe,
> +	.remove = ecap_remove,
> +};
> +
> +module_platform_driver(ecap_iio_driver);
> +
> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
> +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
> +MODULE_LICENSE("GPL");
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 5/6] pwm: enable TI PWMSS if the IIO tiecap driver is selected
@ 2014-02-26 14:15     ` Thierry Reding
  0 siblings, 0 replies; 42+ messages in thread
From: Thierry Reding @ 2014-02-26 14:15 UTC (permalink / raw)
  To: Matt Porter
  Cc: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Linux IIO List, Linux Kernel Mailing List,
	Devicetree List, Linux PWM List, Linux OMAP List,
	Linux ARM Kernel List

[-- Attachment #1: Type: text/plain, Size: 893 bytes --]

On Wed, Feb 05, 2014 at 02:01:40PM -0500, Matt Porter wrote:
> The IIO TI ECAP driver depends on the TI PWMSS management
> driver in this subsystem. Enable PWMSS when the IIO TI ECAP
> driver is selected.
> 
> Signed-off-by: Matt Porter <mporter@linaro.org>
> ---
>  drivers/pwm/Kconfig | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 22f2f28..bd3cc65 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -219,7 +219,7 @@ config  PWM_TIEHRPWM
>  
>  config  PWM_TIPWMSS
>  	bool
> -	default y if SOC_AM33XX && (PWM_TIECAP || PWM_TIEHRPWM)
> +	default y if SOC_AM33XX && (IIO_TIECAP || PWM_TIECAP || PWM_TIEHRPWM)
>  	help
>  	  PWM Subsystem driver support for AM33xx SOC.

Perhaps this module should move out of drivers/pwm if it's no longer a
PWM specific module.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 5/6] pwm: enable TI PWMSS if the IIO tiecap driver is selected
@ 2014-02-26 14:15     ` Thierry Reding
  0 siblings, 0 replies; 42+ messages in thread
From: Thierry Reding @ 2014-02-26 14:15 UTC (permalink / raw)
  To: Matt Porter
  Cc: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Linux IIO List, Linux Kernel Mailing List,
	Devicetree List, Linux PWM List, Linux OMAP List,
	Linux ARM Kernel List

[-- Attachment #1: Type: text/plain, Size: 922 bytes --]

On Wed, Feb 05, 2014 at 02:01:40PM -0500, Matt Porter wrote:
> The IIO TI ECAP driver depends on the TI PWMSS management
> driver in this subsystem. Enable PWMSS when the IIO TI ECAP
> driver is selected.
> 
> Signed-off-by: Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  drivers/pwm/Kconfig | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 22f2f28..bd3cc65 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -219,7 +219,7 @@ config  PWM_TIEHRPWM
>  
>  config  PWM_TIPWMSS
>  	bool
> -	default y if SOC_AM33XX && (PWM_TIECAP || PWM_TIEHRPWM)
> +	default y if SOC_AM33XX && (IIO_TIECAP || PWM_TIECAP || PWM_TIEHRPWM)
>  	help
>  	  PWM Subsystem driver support for AM33xx SOC.

Perhaps this module should move out of drivers/pwm if it's no longer a
PWM specific module.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 5/6] pwm: enable TI PWMSS if the IIO tiecap driver is selected
@ 2014-02-26 14:15     ` Thierry Reding
  0 siblings, 0 replies; 42+ messages in thread
From: Thierry Reding @ 2014-02-26 14:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Feb 05, 2014 at 02:01:40PM -0500, Matt Porter wrote:
> The IIO TI ECAP driver depends on the TI PWMSS management
> driver in this subsystem. Enable PWMSS when the IIO TI ECAP
> driver is selected.
> 
> Signed-off-by: Matt Porter <mporter@linaro.org>
> ---
>  drivers/pwm/Kconfig | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 22f2f28..bd3cc65 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -219,7 +219,7 @@ config  PWM_TIEHRPWM
>  
>  config  PWM_TIPWMSS
>  	bool
> -	default y if SOC_AM33XX && (PWM_TIECAP || PWM_TIEHRPWM)
> +	default y if SOC_AM33XX && (IIO_TIECAP || PWM_TIECAP || PWM_TIEHRPWM)
>  	help
>  	  PWM Subsystem driver support for AM33xx SOC.

Perhaps this module should move out of drivers/pwm if it's no longer a
PWM specific module.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140226/670f7d0f/attachment.sig>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 2/6] iio: pulse: add TI ECAP driver
  2014-02-05 19:01   ` Matt Porter
  (?)
  (?)
@ 2014-03-22  3:33   ` Matt Ranostay
  -1 siblings, 0 replies; 42+ messages in thread
From: Matt Ranostay @ 2014-03-22  3:33 UTC (permalink / raw)
  To: Matt Porter
  Cc: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding, Linux IIO List,
	Linux Kernel Mailing List, Devicetree List, Linux PWM List,
	Linux OMAP List, Linux ARM Kernel List

[-- Attachment #1: Type: text/plain, Size: 18352 bytes --]

On Wed, Feb 5, 2014 at 11:01 AM, Matt Porter <mporter@linaro.org> wrote:

> Adds support for capturing PWM signals using the TI ECAP peripheral.
> This driver supports triggered buffer capture of pulses on multiple
> ECAP instances. In addition, the driver supports configurable polarity
> of the signal to be captured.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>
>

Tested-by: Matt Ranostay <mranostay@gmail.com>


> ---
>  drivers/iio/pulse/Kconfig  |  20 ++
>  drivers/iio/pulse/Makefile |   6 +
>  drivers/iio/pulse/tiecap.c | 491
> +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 517 insertions(+)
>  create mode 100644 drivers/iio/pulse/Kconfig
>  create mode 100644 drivers/iio/pulse/Makefile
>  create mode 100644 drivers/iio/pulse/tiecap.c
>
> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
> new file mode 100644
> index 0000000..9864d4b
> --- /dev/null
> +++ b/drivers/iio/pulse/Kconfig
> @@ -0,0 +1,20 @@
> +#
> +# Pulse Capture Devices
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "Pulse Capture Devices"
> +
> +config IIO_TIECAP
> +       tristate "TI ECAP Pulse Capture"
> +       depends on SOC_AM33XX
> +       select IIO_BUFFER
> +       select IIO_TRIGGERED_BUFFER
> +       help
> +        If you say yes here you get support for the TI ECAP peripheral
> +        in pulse capture mode.
> +
> +        This driver can also be built as a module.  If so, the module
> +        will be called tiecap
> +
> +endmenu
> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
> new file mode 100644
> index 0000000..94d4b00
> --- /dev/null
> +++ b/drivers/iio/pulse/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for IIO PWM Capture Devices
> +#
> +
> +# When adding new entries keep the list in alphabetical order
> +obj-$(CONFIG_IIO_TIECAP)       += tiecap.o
> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
> new file mode 100644
> index 0000000..fd96745
> --- /dev/null
> +++ b/drivers/iio/pulse/tiecap.c
> @@ -0,0 +1,491 @@
> +/*
> + * ECAP IIO pulse capture driver
> + *
> + * Copyright (C) 2014 Linaro Limited
> + * Author: Matt Porter <mporter@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "../../pwm/pwm-tipwmss.h"
> +
> +/* ECAP regs and bits */
> +#define CAP1                   0x08
> +#define CAP2                   0x0c
> +#define ECCTL1                 0x28
> +#define ECCTL1_RUN_FREE                BIT(15)
> +#define ECCTL1_CAPLDEN         BIT(8)
> +#define ECCTL1_CAP2POL         BIT(2)
> +#define ECCTL1_CTRRST1         BIT(1)
> +#define ECCTL1_CAP1POL         BIT(0)
> +#define ECCTL2                 0x2a
> +#define ECCTL2_SYNCO_SEL_DIS   BIT(7)
> +#define ECCTL2_TSCTR_FREERUN   BIT(4)
> +#define ECCTL2_REARM           BIT(3)
> +#define ECCTL2_STOP_WRAP_2     BIT(1)
> +#define ECEINT                 0x2c
> +#define ECFLG                  0x2e
> +#define ECCLR                  0x30
> +#define ECINT_CTRCMP           BIT(7)
> +#define ECINT_CTRPRD           BIT(6)
> +#define ECINT_CTROVF           BIT(5)
> +#define ECINT_CEVT4            BIT(4)
> +#define ECINT_CEVT3            BIT(3)
> +#define ECINT_CEVT2            BIT(2)
> +#define ECINT_CEVT1            BIT(1)
> +#define ECINT_ALL              (ECINT_CTRCMP | \
> +                               ECINT_CTRPRD |  \
> +                               ECINT_CTROVF |  \
> +                               ECINT_CEVT4 |   \
> +                               ECINT_CEVT3 |   \
> +                               ECINT_CEVT2 |   \
> +                               ECINT_CEVT1)
> +
> +/* ECAP driver flags */
> +#define ECAP_POLARITY_HIGH     BIT(1)
> +#define ECAP_ENABLED           BIT(0)
> +
> +struct ecap_context {
> +       u32     cap1;
> +       u32     cap2;
> +       u16     ecctl1;
> +       u16     ecctl2;
> +       u16     eceint;
> +};
> +
> +struct ecap_state {
> +       unsigned long           flags;
> +       unsigned int            clk_rate;
> +       void __iomem            *regs;
> +       u32                     *buf;
> +       struct ecap_context     ctx;
> +};
> +
> +#define dev_to_ecap_state(d)   iio_priv(dev_to_iio_dev(d))
> +
> +static const struct iio_chan_spec ecap_channels[] = {
> +       {
> +               .type           = IIO_PULSE,
> +               .info_mask_separate =
> +                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> +               .scan_index     = 0,
> +               .scan_type = {
> +                       .sign           = 'u',
> +                       .realbits       = 32,
> +                       .storagebits    = 32,
> +                       .endianness     = IIO_LE,
> +               },
> +       },
> +       IIO_CHAN_SOFT_TIMESTAMP(1)
> +};
> +
> +static ssize_t ecap_attr_show(struct device *dev,
> +                             struct device_attribute *attr, char *buf)
> +{
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       return sprintf(buf, "%d\n",
> +                      test_bit(ECAP_POLARITY_HIGH, &state->flags));
> +}
> +
> +static ssize_t ecap_attr_store(struct device *dev,
> +                              struct device_attribute *attr,
> +                              const char *buf,
> +                              size_t len)
> +{
> +       int ret;
> +       bool val;
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       if (test_bit(ECAP_ENABLED, &state->flags))
> +               return -EINVAL;
> +
> +       ret = strtobool(buf, &val);
> +       if (ret)
> +               return ret;
> +
> +       if (val)
> +               set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +       else
> +               clear_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +       return len;
> +}
> +
> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
> +       ecap_attr_show, ecap_attr_store, 0);
> +
> +static struct attribute *ecap_attributes[] = {
> +       &iio_dev_attr_pulse_polarity.dev_attr.attr,
> +       NULL,
> +};
> +
> +static struct attribute_group ecap_attribute_group = {
> +       .attrs = ecap_attributes,
> +};
> +
> +static int ecap_read_raw(struct iio_dev *idev,
> +                       struct iio_chan_spec const *ch, int *val,
> +                       int *val2, long mask)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               /*
> +                * Always return 0 as a pulse width sample
> +                * is only valid in a triggered condition
> +                */
> +               *val = 0;
> +               return IIO_VAL_INT;
> +       case IIO_CHAN_INFO_SCALE:
> +               *val = 0;
> +               *val2 = NSEC_PER_SEC / state->clk_rate;
> +               return IIO_VAL_INT_PLUS_NANO;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static const struct iio_info ecap_info = {
> +       .driver_module = THIS_MODULE,
> +       .attrs = &ecap_attribute_group,
> +       .read_raw = &ecap_read_raw,
> +};
> +
> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
> +{
> +       struct iio_poll_func *pf = private;
> +       struct iio_dev *idev = pf->indio_dev;
> +       struct ecap_state *state = iio_priv(idev);
> +
> +       /* Read pulse counter value */
> +       *state->buf = readl(state->regs + CAP2);
> +
> +       iio_push_to_buffers_with_timestamp(idev, state->buf,
> iio_get_time_ns());
> +
> +       iio_trigger_notify_done(idev->trig);
> +
> +       return IRQ_HANDLED;
> +};
> +
> +
> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
> +       .owner = THIS_MODULE,
> +};
> +
> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
> +{
> +       struct iio_dev *idev = private;
> +       struct ecap_state *state = iio_priv(idev);
> +       u16 ints;
> +
> +       iio_trigger_poll(idev->trig, 0);
> +
> +       /* Clear CAP2 interrupt */
> +       ints = readw(state->regs + ECFLG);
> +       if (ints & ECINT_CEVT2)
> +               writew(ECINT_CEVT2, state->regs + ECCLR);
> +       else
> +               dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
> +                        ints);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int ecap_buffer_predisable(struct iio_dev *idev)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +       int ret = 0;
> +       u16 ecctl2;
> +
> +       /* Stop capture */
> +       clear_bit(ECAP_ENABLED, &state->flags);
> +       ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
> +       writew(ecctl2, state->regs + ECCTL2);
> +
> +       /* Disable and clear all interrupts */
> +       writew(0, state->regs + ECEINT);
> +       writew(ECINT_ALL, state->regs + ECCLR);
> +
> +       ret = iio_triggered_buffer_predisable(idev);
> +
> +       pm_runtime_put_sync(idev->dev.parent);
> +
> +       return ret;
> +}
> +
> +static int ecap_buffer_postenable(struct iio_dev *idev)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +       int ret = 0;
> +       u16 ecctl1, ecctl2;
> +
> +       pm_runtime_get_sync(idev->dev.parent);
> +
> +       /* Configure pulse polarity */
> +       ecctl1 = readw(state->regs + ECCTL1);
> +       if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
> +               /* CAP1 rising, CAP2 falling */
> +               ecctl1 |= ECCTL1_CAP2POL;
> +               ecctl1 &= ~ECCTL1_CAP1POL;
> +       } else {
> +               /* CAP1 falling, CAP2 rising */
> +               ecctl1 &= ~ECCTL1_CAP2POL;
> +               ecctl1 |= ECCTL1_CAP1POL;
> +       }
> +       writew(ecctl1, state->regs + ECCTL1);
> +
> +       /* Enable CAP2 interrupt */
> +       writew(ECINT_CEVT2, state->regs + ECEINT);
> +
> +       /* Enable capture */
> +       ecctl2 = readw(state->regs + ECCTL2);
> +       ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
> +       writew(ecctl2, state->regs + ECCTL2);
> +       set_bit(ECAP_ENABLED, &state->flags);
> +
> +       ret = iio_triggered_buffer_postenable(idev);
> +
> +       return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
> +       .postenable = &ecap_buffer_postenable,
> +       .predisable = &ecap_buffer_predisable,
> +};
> +
> +static void ecap_init_hw(struct iio_dev *idev)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +
> +       clear_bit(ECAP_ENABLED, &state->flags);
> +       set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +       writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
> +              ECCTL1_CAP2POL | ECCTL1_CTRRST1,
> +              state->regs + ECCTL1);
> +
> +       writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
> +              state->regs + ECCTL2);
> +}
> +
> +static const struct of_device_id ecap_of_ids[] = {
> +       { .compatible   = "ti,am33xx-ecap" },
> +       { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
> +
> +static int ecap_probe(struct platform_device *pdev)
> +{
> +       int irq, ret;
> +       struct iio_dev *idev;
> +       struct ecap_state *state;
> +       struct resource *r;
> +       struct clk *clk;
> +       struct iio_trigger *trig;
> +       u16 status;
> +
> +       idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct
> ecap_state));
> +       if (!idev)
> +               return -ENOMEM;
> +
> +       state = iio_priv(idev);
> +
> +       clk = devm_clk_get(&pdev->dev, "fck");
> +       if (IS_ERR(clk)) {
> +               dev_err(&pdev->dev, "failed to get clock\n");
> +               return PTR_ERR(clk);
> +       }
> +
> +       state->clk_rate = clk_get_rate(clk);
> +       if (!state->clk_rate) {
> +               dev_err(&pdev->dev, "failed to get clock rate\n");
> +               return -EINVAL;
> +       }
> +
> +       platform_set_drvdata(pdev, idev);
> +
> +       idev->dev.parent = &pdev->dev;
> +       idev->name = dev_name(&pdev->dev);
> +       idev->modes = INDIO_DIRECT_MODE;
> +       idev->info = &ecap_info;
> +       idev->channels = ecap_channels;
> +       /* One h/w capture and one s/w timestamp channel per instance */
> +       idev->num_channels = ARRAY_SIZE(ecap_channels);
> +
> +       trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
> +                                     idev->name, idev->id);
> +       if (!trig)
> +               return -ENOMEM;
> +       trig->dev.parent = idev->dev.parent;
> +       iio_trigger_set_drvdata(trig, idev);
> +       trig->ops = &iio_interrupt_trigger_ops;
> +
> +       ret = iio_trigger_register(trig);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to register trigger\n");
> +               return ret;
> +       }
> +
> +       ret = iio_triggered_buffer_setup(idev, NULL,
> +                                        &ecap_trigger_handler,
> +                                        &ecap_buffer_setup_ops);
> +       if (ret)
> +               return ret;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0) {
> +               dev_err(&pdev->dev, "no irq is specified\n");
> +               return irq;
> +       }
> +       ret = devm_request_irq(&pdev->dev, irq,
> +                               &ecap_interrupt_handler,
> +                               0, dev_name(&pdev->dev), idev);
> +       if (ret) {
> +               dev_err(&pdev->dev, "unable to request irq\n");
> +               goto uninit_buffer;
> +       }
> +
> +       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       state->regs = devm_ioremap_resource(&pdev->dev, r);
> +       if (IS_ERR(state->regs)) {
> +               dev_err(&pdev->dev, "unable to remap registers\n");
> +               ret = PTR_ERR(state->regs);
> +               goto uninit_buffer;
> +       };
> +
> +       ret = iio_device_register(idev);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "unable to register device\n");
> +               goto uninit_buffer;
> +       }
> +
> +       state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes,
> GFP_KERNEL);
> +       if (!state->buf) {
> +               ret = -ENOMEM;
> +               goto uninit_buffer;
> +       }
> +
> +       pm_runtime_enable(&pdev->dev);
> +       pm_runtime_get_sync(&pdev->dev);
> +
> +       status = pwmss_submodule_state_change(pdev->dev.parent,
> +                       PWMSS_ECAPCLK_EN);
> +       if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
> +               dev_err(&pdev->dev, "failed to enable PWMSS config space
> clock\n");
> +               ret = -EINVAL;
> +               goto pwmss_clk_failure;
> +       }
> +
> +       ecap_init_hw(idev);
> +
> +       pm_runtime_put_sync(&pdev->dev);
> +
> +       return 0;
> +
> +pwmss_clk_failure:
> +       pm_runtime_put_sync(&pdev->dev);
> +       pm_runtime_disable(&pdev->dev);
> +       iio_device_unregister(idev);
> +
> +uninit_buffer:
> +       iio_triggered_buffer_cleanup(idev);
> +
> +       return ret;
> +}
> +
> +static int ecap_remove(struct platform_device *pdev)
> +{
> +       struct iio_dev *idev = platform_get_drvdata(pdev);
> +
> +       pm_runtime_get_sync(&pdev->dev);
> +
> +       pwmss_submodule_state_change(pdev->dev.parent,
> PWMSS_ECAPCLK_STOP_REQ);
> +
> +       pm_runtime_put_sync(&pdev->dev);
> +       pm_runtime_disable(&pdev->dev);
> +
> +       iio_device_unregister(idev);
> +       iio_triggered_buffer_cleanup(idev);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ecap_suspend(struct device *dev)
> +{
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       pm_runtime_get_sync(dev);
> +       state->ctx.cap1 = readl(state->regs + CAP1);
> +       state->ctx.cap2 = readl(state->regs + CAP2);
> +       state->ctx.eceint = readw(state->regs + ECEINT);
> +       state->ctx.ecctl1 = readw(state->regs + ECCTL1);
> +       state->ctx.ecctl2 = readw(state->regs + ECCTL2);
> +       pm_runtime_put_sync(dev);
> +
> +       /* If capture was active, disable ECAP */
> +       if (test_bit(ECAP_ENABLED, &state->flags))
> +               pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +
> +static int ecap_resume(struct device *dev)
> +{
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       /* If capture was active, enable ECAP */
> +       if (test_bit(ECAP_ENABLED, &state->flags))
> +               pm_runtime_get_sync(dev);
> +
> +       pm_runtime_get_sync(dev);
> +       writel(state->ctx.cap1, state->regs + CAP1);
> +       writel(state->ctx.cap2, state->regs + CAP2);
> +       writew(state->ctx.eceint, state->regs + ECEINT);
> +       writew(state->ctx.ecctl1, state->regs + ECCTL1);
> +       writew(state->ctx.ecctl2, state->regs + ECCTL2);
> +       pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
> +
> +static struct platform_driver ecap_iio_driver = {
> +       .driver = {
> +               .name           = "ecap",
> +               .owner          = THIS_MODULE,
> +               .of_match_table = of_match_ptr(ecap_of_ids),
> +               .pm             = &ecap_pm_ops,
> +       },
> +       .probe = ecap_probe,
> +       .remove = ecap_remove,
> +};
> +
> +module_platform_driver(ecap_iio_driver);
> +
> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
> +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
> +MODULE_LICENSE("GPL");
> --
> 1.8.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>

[-- Attachment #2: Type: text/html, Size: 21708 bytes --]

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 2/6] iio: pulse: add TI ECAP driver
  2014-02-05 19:01   ` Matt Porter
@ 2014-03-22  3:35     ` Matt Ranostay
  -1 siblings, 0 replies; 42+ messages in thread
From: Matt Ranostay @ 2014-03-22  3:35 UTC (permalink / raw)
  To: Matt Porter
  Cc: Jonathan Cameron, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding, Linux IIO List,
	Linux Kernel Mailing List, Devicetree List, Linux PWM List,
	Linux OMAP List, Linux ARM Kernel List

On Wed, Feb 5, 2014 at 11:01 AM, Matt Porter <mporter@linaro.org> wrote:
> Adds support for capturing PWM signals using the TI ECAP peripheral.
> This driver supports triggered buffer capture of pulses on multiple
> ECAP instances. In addition, the driver supports configurable polarity
> of the signal to be captured.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>

Tested-by: Matt Ranostay <mranostay@gmail.com>

> ---
>  drivers/iio/pulse/Kconfig  |  20 ++
>  drivers/iio/pulse/Makefile |   6 +
>  drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 517 insertions(+)
>  create mode 100644 drivers/iio/pulse/Kconfig
>  create mode 100644 drivers/iio/pulse/Makefile
>  create mode 100644 drivers/iio/pulse/tiecap.c
>
> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
> new file mode 100644
> index 0000000..9864d4b
> --- /dev/null
> +++ b/drivers/iio/pulse/Kconfig
> @@ -0,0 +1,20 @@
> +#
> +# Pulse Capture Devices
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "Pulse Capture Devices"
> +
> +config IIO_TIECAP
> +       tristate "TI ECAP Pulse Capture"
> +       depends on SOC_AM33XX
> +       select IIO_BUFFER
> +       select IIO_TRIGGERED_BUFFER
> +       help
> +        If you say yes here you get support for the TI ECAP peripheral
> +        in pulse capture mode.
> +
> +        This driver can also be built as a module.  If so, the module
> +        will be called tiecap
> +
> +endmenu
> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
> new file mode 100644
> index 0000000..94d4b00
> --- /dev/null
> +++ b/drivers/iio/pulse/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for IIO PWM Capture Devices
> +#
> +
> +# When adding new entries keep the list in alphabetical order
> +obj-$(CONFIG_IIO_TIECAP)       += tiecap.o
> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
> new file mode 100644
> index 0000000..fd96745
> --- /dev/null
> +++ b/drivers/iio/pulse/tiecap.c
> @@ -0,0 +1,491 @@
> +/*
> + * ECAP IIO pulse capture driver
> + *
> + * Copyright (C) 2014 Linaro Limited
> + * Author: Matt Porter <mporter@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "../../pwm/pwm-tipwmss.h"
> +
> +/* ECAP regs and bits */
> +#define CAP1                   0x08
> +#define CAP2                   0x0c
> +#define ECCTL1                 0x28
> +#define ECCTL1_RUN_FREE                BIT(15)
> +#define ECCTL1_CAPLDEN         BIT(8)
> +#define ECCTL1_CAP2POL         BIT(2)
> +#define ECCTL1_CTRRST1         BIT(1)
> +#define ECCTL1_CAP1POL         BIT(0)
> +#define ECCTL2                 0x2a
> +#define ECCTL2_SYNCO_SEL_DIS   BIT(7)
> +#define ECCTL2_TSCTR_FREERUN   BIT(4)
> +#define ECCTL2_REARM           BIT(3)
> +#define ECCTL2_STOP_WRAP_2     BIT(1)
> +#define ECEINT                 0x2c
> +#define ECFLG                  0x2e
> +#define ECCLR                  0x30
> +#define ECINT_CTRCMP           BIT(7)
> +#define ECINT_CTRPRD           BIT(6)
> +#define ECINT_CTROVF           BIT(5)
> +#define ECINT_CEVT4            BIT(4)
> +#define ECINT_CEVT3            BIT(3)
> +#define ECINT_CEVT2            BIT(2)
> +#define ECINT_CEVT1            BIT(1)
> +#define ECINT_ALL              (ECINT_CTRCMP | \
> +                               ECINT_CTRPRD |  \
> +                               ECINT_CTROVF |  \
> +                               ECINT_CEVT4 |   \
> +                               ECINT_CEVT3 |   \
> +                               ECINT_CEVT2 |   \
> +                               ECINT_CEVT1)
> +
> +/* ECAP driver flags */
> +#define ECAP_POLARITY_HIGH     BIT(1)
> +#define ECAP_ENABLED           BIT(0)
> +
> +struct ecap_context {
> +       u32     cap1;
> +       u32     cap2;
> +       u16     ecctl1;
> +       u16     ecctl2;
> +       u16     eceint;
> +};
> +
> +struct ecap_state {
> +       unsigned long           flags;
> +       unsigned int            clk_rate;
> +       void __iomem            *regs;
> +       u32                     *buf;
> +       struct ecap_context     ctx;
> +};
> +
> +#define dev_to_ecap_state(d)   iio_priv(dev_to_iio_dev(d))
> +
> +static const struct iio_chan_spec ecap_channels[] = {
> +       {
> +               .type           = IIO_PULSE,
> +               .info_mask_separate =
> +                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> +               .scan_index     = 0,
> +               .scan_type = {
> +                       .sign           = 'u',
> +                       .realbits       = 32,
> +                       .storagebits    = 32,
> +                       .endianness     = IIO_LE,
> +               },
> +       },
> +       IIO_CHAN_SOFT_TIMESTAMP(1)
> +};
> +
> +static ssize_t ecap_attr_show(struct device *dev,
> +                             struct device_attribute *attr, char *buf)
> +{
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       return sprintf(buf, "%d\n",
> +                      test_bit(ECAP_POLARITY_HIGH, &state->flags));
> +}
> +
> +static ssize_t ecap_attr_store(struct device *dev,
> +                              struct device_attribute *attr,
> +                              const char *buf,
> +                              size_t len)
> +{
> +       int ret;
> +       bool val;
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       if (test_bit(ECAP_ENABLED, &state->flags))
> +               return -EINVAL;
> +
> +       ret = strtobool(buf, &val);
> +       if (ret)
> +               return ret;
> +
> +       if (val)
> +               set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +       else
> +               clear_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +       return len;
> +}
> +
> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
> +       ecap_attr_show, ecap_attr_store, 0);
> +
> +static struct attribute *ecap_attributes[] = {
> +       &iio_dev_attr_pulse_polarity.dev_attr.attr,
> +       NULL,
> +};
> +
> +static struct attribute_group ecap_attribute_group = {
> +       .attrs = ecap_attributes,
> +};
> +
> +static int ecap_read_raw(struct iio_dev *idev,
> +                       struct iio_chan_spec const *ch, int *val,
> +                       int *val2, long mask)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               /*
> +                * Always return 0 as a pulse width sample
> +                * is only valid in a triggered condition
> +                */
> +               *val = 0;
> +               return IIO_VAL_INT;
> +       case IIO_CHAN_INFO_SCALE:
> +               *val = 0;
> +               *val2 = NSEC_PER_SEC / state->clk_rate;
> +               return IIO_VAL_INT_PLUS_NANO;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static const struct iio_info ecap_info = {
> +       .driver_module = THIS_MODULE,
> +       .attrs = &ecap_attribute_group,
> +       .read_raw = &ecap_read_raw,
> +};
> +
> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
> +{
> +       struct iio_poll_func *pf = private;
> +       struct iio_dev *idev = pf->indio_dev;
> +       struct ecap_state *state = iio_priv(idev);
> +
> +       /* Read pulse counter value */
> +       *state->buf = readl(state->regs + CAP2);
> +
> +       iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
> +
> +       iio_trigger_notify_done(idev->trig);
> +
> +       return IRQ_HANDLED;
> +};
> +
> +
> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
> +       .owner = THIS_MODULE,
> +};
> +
> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
> +{
> +       struct iio_dev *idev = private;
> +       struct ecap_state *state = iio_priv(idev);
> +       u16 ints;
> +
> +       iio_trigger_poll(idev->trig, 0);
> +
> +       /* Clear CAP2 interrupt */
> +       ints = readw(state->regs + ECFLG);
> +       if (ints & ECINT_CEVT2)
> +               writew(ECINT_CEVT2, state->regs + ECCLR);
> +       else
> +               dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
> +                        ints);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int ecap_buffer_predisable(struct iio_dev *idev)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +       int ret = 0;
> +       u16 ecctl2;
> +
> +       /* Stop capture */
> +       clear_bit(ECAP_ENABLED, &state->flags);
> +       ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
> +       writew(ecctl2, state->regs + ECCTL2);
> +
> +       /* Disable and clear all interrupts */
> +       writew(0, state->regs + ECEINT);
> +       writew(ECINT_ALL, state->regs + ECCLR);
> +
> +       ret = iio_triggered_buffer_predisable(idev);
> +
> +       pm_runtime_put_sync(idev->dev.parent);
> +
> +       return ret;
> +}
> +
> +static int ecap_buffer_postenable(struct iio_dev *idev)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +       int ret = 0;
> +       u16 ecctl1, ecctl2;
> +
> +       pm_runtime_get_sync(idev->dev.parent);
> +
> +       /* Configure pulse polarity */
> +       ecctl1 = readw(state->regs + ECCTL1);
> +       if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
> +               /* CAP1 rising, CAP2 falling */
> +               ecctl1 |= ECCTL1_CAP2POL;
> +               ecctl1 &= ~ECCTL1_CAP1POL;
> +       } else {
> +               /* CAP1 falling, CAP2 rising */
> +               ecctl1 &= ~ECCTL1_CAP2POL;
> +               ecctl1 |= ECCTL1_CAP1POL;
> +       }
> +       writew(ecctl1, state->regs + ECCTL1);
> +
> +       /* Enable CAP2 interrupt */
> +       writew(ECINT_CEVT2, state->regs + ECEINT);
> +
> +       /* Enable capture */
> +       ecctl2 = readw(state->regs + ECCTL2);
> +       ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
> +       writew(ecctl2, state->regs + ECCTL2);
> +       set_bit(ECAP_ENABLED, &state->flags);
> +
> +       ret = iio_triggered_buffer_postenable(idev);
> +
> +       return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
> +       .postenable = &ecap_buffer_postenable,
> +       .predisable = &ecap_buffer_predisable,
> +};
> +
> +static void ecap_init_hw(struct iio_dev *idev)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +
> +       clear_bit(ECAP_ENABLED, &state->flags);
> +       set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +       writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
> +              ECCTL1_CAP2POL | ECCTL1_CTRRST1,
> +              state->regs + ECCTL1);
> +
> +       writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
> +              state->regs + ECCTL2);
> +}
> +
> +static const struct of_device_id ecap_of_ids[] = {
> +       { .compatible   = "ti,am33xx-ecap" },
> +       { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
> +
> +static int ecap_probe(struct platform_device *pdev)
> +{
> +       int irq, ret;
> +       struct iio_dev *idev;
> +       struct ecap_state *state;
> +       struct resource *r;
> +       struct clk *clk;
> +       struct iio_trigger *trig;
> +       u16 status;
> +
> +       idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
> +       if (!idev)
> +               return -ENOMEM;
> +
> +       state = iio_priv(idev);
> +
> +       clk = devm_clk_get(&pdev->dev, "fck");
> +       if (IS_ERR(clk)) {
> +               dev_err(&pdev->dev, "failed to get clock\n");
> +               return PTR_ERR(clk);
> +       }
> +
> +       state->clk_rate = clk_get_rate(clk);
> +       if (!state->clk_rate) {
> +               dev_err(&pdev->dev, "failed to get clock rate\n");
> +               return -EINVAL;
> +       }
> +
> +       platform_set_drvdata(pdev, idev);
> +
> +       idev->dev.parent = &pdev->dev;
> +       idev->name = dev_name(&pdev->dev);
> +       idev->modes = INDIO_DIRECT_MODE;
> +       idev->info = &ecap_info;
> +       idev->channels = ecap_channels;
> +       /* One h/w capture and one s/w timestamp channel per instance */
> +       idev->num_channels = ARRAY_SIZE(ecap_channels);
> +
> +       trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
> +                                     idev->name, idev->id);
> +       if (!trig)
> +               return -ENOMEM;
> +       trig->dev.parent = idev->dev.parent;
> +       iio_trigger_set_drvdata(trig, idev);
> +       trig->ops = &iio_interrupt_trigger_ops;
> +
> +       ret = iio_trigger_register(trig);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to register trigger\n");
> +               return ret;
> +       }
> +
> +       ret = iio_triggered_buffer_setup(idev, NULL,
> +                                        &ecap_trigger_handler,
> +                                        &ecap_buffer_setup_ops);
> +       if (ret)
> +               return ret;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0) {
> +               dev_err(&pdev->dev, "no irq is specified\n");
> +               return irq;
> +       }
> +       ret = devm_request_irq(&pdev->dev, irq,
> +                               &ecap_interrupt_handler,
> +                               0, dev_name(&pdev->dev), idev);
> +       if (ret) {
> +               dev_err(&pdev->dev, "unable to request irq\n");
> +               goto uninit_buffer;
> +       }
> +
> +       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       state->regs = devm_ioremap_resource(&pdev->dev, r);
> +       if (IS_ERR(state->regs)) {
> +               dev_err(&pdev->dev, "unable to remap registers\n");
> +               ret = PTR_ERR(state->regs);
> +               goto uninit_buffer;
> +       };
> +
> +       ret = iio_device_register(idev);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "unable to register device\n");
> +               goto uninit_buffer;
> +       }
> +
> +       state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
> +       if (!state->buf) {
> +               ret = -ENOMEM;
> +               goto uninit_buffer;
> +       }
> +
> +       pm_runtime_enable(&pdev->dev);
> +       pm_runtime_get_sync(&pdev->dev);
> +
> +       status = pwmss_submodule_state_change(pdev->dev.parent,
> +                       PWMSS_ECAPCLK_EN);
> +       if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
> +               dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
> +               ret = -EINVAL;
> +               goto pwmss_clk_failure;
> +       }
> +
> +       ecap_init_hw(idev);
> +
> +       pm_runtime_put_sync(&pdev->dev);
> +
> +       return 0;
> +
> +pwmss_clk_failure:
> +       pm_runtime_put_sync(&pdev->dev);
> +       pm_runtime_disable(&pdev->dev);
> +       iio_device_unregister(idev);
> +
> +uninit_buffer:
> +       iio_triggered_buffer_cleanup(idev);
> +
> +       return ret;
> +}
> +
> +static int ecap_remove(struct platform_device *pdev)
> +{
> +       struct iio_dev *idev = platform_get_drvdata(pdev);
> +
> +       pm_runtime_get_sync(&pdev->dev);
> +
> +       pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
> +
> +       pm_runtime_put_sync(&pdev->dev);
> +       pm_runtime_disable(&pdev->dev);
> +
> +       iio_device_unregister(idev);
> +       iio_triggered_buffer_cleanup(idev);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ecap_suspend(struct device *dev)
> +{
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       pm_runtime_get_sync(dev);
> +       state->ctx.cap1 = readl(state->regs + CAP1);
> +       state->ctx.cap2 = readl(state->regs + CAP2);
> +       state->ctx.eceint = readw(state->regs + ECEINT);
> +       state->ctx.ecctl1 = readw(state->regs + ECCTL1);
> +       state->ctx.ecctl2 = readw(state->regs + ECCTL2);
> +       pm_runtime_put_sync(dev);
> +
> +       /* If capture was active, disable ECAP */
> +       if (test_bit(ECAP_ENABLED, &state->flags))
> +               pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +
> +static int ecap_resume(struct device *dev)
> +{
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       /* If capture was active, enable ECAP */
> +       if (test_bit(ECAP_ENABLED, &state->flags))
> +               pm_runtime_get_sync(dev);
> +
> +       pm_runtime_get_sync(dev);
> +       writel(state->ctx.cap1, state->regs + CAP1);
> +       writel(state->ctx.cap2, state->regs + CAP2);
> +       writew(state->ctx.eceint, state->regs + ECEINT);
> +       writew(state->ctx.ecctl1, state->regs + ECCTL1);
> +       writew(state->ctx.ecctl2, state->regs + ECCTL2);
> +       pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
> +
> +static struct platform_driver ecap_iio_driver = {
> +       .driver = {
> +               .name           = "ecap",
> +               .owner          = THIS_MODULE,
> +               .of_match_table = of_match_ptr(ecap_of_ids),
> +               .pm             = &ecap_pm_ops,
> +       },
> +       .probe = ecap_probe,
> +       .remove = ecap_remove,
> +};
> +
> +module_platform_driver(ecap_iio_driver);
> +
> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
> +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
> +MODULE_LICENSE("GPL");
> --
> 1.8.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2014-03-22  3:35     ` Matt Ranostay
  0 siblings, 0 replies; 42+ messages in thread
From: Matt Ranostay @ 2014-03-22  3:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Feb 5, 2014 at 11:01 AM, Matt Porter <mporter@linaro.org> wrote:
> Adds support for capturing PWM signals using the TI ECAP peripheral.
> This driver supports triggered buffer capture of pulses on multiple
> ECAP instances. In addition, the driver supports configurable polarity
> of the signal to be captured.
>
> Signed-off-by: Matt Porter <mporter@linaro.org>

Tested-by: Matt Ranostay <mranostay@gmail.com>

> ---
>  drivers/iio/pulse/Kconfig  |  20 ++
>  drivers/iio/pulse/Makefile |   6 +
>  drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 517 insertions(+)
>  create mode 100644 drivers/iio/pulse/Kconfig
>  create mode 100644 drivers/iio/pulse/Makefile
>  create mode 100644 drivers/iio/pulse/tiecap.c
>
> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
> new file mode 100644
> index 0000000..9864d4b
> --- /dev/null
> +++ b/drivers/iio/pulse/Kconfig
> @@ -0,0 +1,20 @@
> +#
> +# Pulse Capture Devices
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "Pulse Capture Devices"
> +
> +config IIO_TIECAP
> +       tristate "TI ECAP Pulse Capture"
> +       depends on SOC_AM33XX
> +       select IIO_BUFFER
> +       select IIO_TRIGGERED_BUFFER
> +       help
> +        If you say yes here you get support for the TI ECAP peripheral
> +        in pulse capture mode.
> +
> +        This driver can also be built as a module.  If so, the module
> +        will be called tiecap
> +
> +endmenu
> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
> new file mode 100644
> index 0000000..94d4b00
> --- /dev/null
> +++ b/drivers/iio/pulse/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for IIO PWM Capture Devices
> +#
> +
> +# When adding new entries keep the list in alphabetical order
> +obj-$(CONFIG_IIO_TIECAP)       += tiecap.o
> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
> new file mode 100644
> index 0000000..fd96745
> --- /dev/null
> +++ b/drivers/iio/pulse/tiecap.c
> @@ -0,0 +1,491 @@
> +/*
> + * ECAP IIO pulse capture driver
> + *
> + * Copyright (C) 2014 Linaro Limited
> + * Author: Matt Porter <mporter@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "../../pwm/pwm-tipwmss.h"
> +
> +/* ECAP regs and bits */
> +#define CAP1                   0x08
> +#define CAP2                   0x0c
> +#define ECCTL1                 0x28
> +#define ECCTL1_RUN_FREE                BIT(15)
> +#define ECCTL1_CAPLDEN         BIT(8)
> +#define ECCTL1_CAP2POL         BIT(2)
> +#define ECCTL1_CTRRST1         BIT(1)
> +#define ECCTL1_CAP1POL         BIT(0)
> +#define ECCTL2                 0x2a
> +#define ECCTL2_SYNCO_SEL_DIS   BIT(7)
> +#define ECCTL2_TSCTR_FREERUN   BIT(4)
> +#define ECCTL2_REARM           BIT(3)
> +#define ECCTL2_STOP_WRAP_2     BIT(1)
> +#define ECEINT                 0x2c
> +#define ECFLG                  0x2e
> +#define ECCLR                  0x30
> +#define ECINT_CTRCMP           BIT(7)
> +#define ECINT_CTRPRD           BIT(6)
> +#define ECINT_CTROVF           BIT(5)
> +#define ECINT_CEVT4            BIT(4)
> +#define ECINT_CEVT3            BIT(3)
> +#define ECINT_CEVT2            BIT(2)
> +#define ECINT_CEVT1            BIT(1)
> +#define ECINT_ALL              (ECINT_CTRCMP | \
> +                               ECINT_CTRPRD |  \
> +                               ECINT_CTROVF |  \
> +                               ECINT_CEVT4 |   \
> +                               ECINT_CEVT3 |   \
> +                               ECINT_CEVT2 |   \
> +                               ECINT_CEVT1)
> +
> +/* ECAP driver flags */
> +#define ECAP_POLARITY_HIGH     BIT(1)
> +#define ECAP_ENABLED           BIT(0)
> +
> +struct ecap_context {
> +       u32     cap1;
> +       u32     cap2;
> +       u16     ecctl1;
> +       u16     ecctl2;
> +       u16     eceint;
> +};
> +
> +struct ecap_state {
> +       unsigned long           flags;
> +       unsigned int            clk_rate;
> +       void __iomem            *regs;
> +       u32                     *buf;
> +       struct ecap_context     ctx;
> +};
> +
> +#define dev_to_ecap_state(d)   iio_priv(dev_to_iio_dev(d))
> +
> +static const struct iio_chan_spec ecap_channels[] = {
> +       {
> +               .type           = IIO_PULSE,
> +               .info_mask_separate =
> +                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> +               .scan_index     = 0,
> +               .scan_type = {
> +                       .sign           = 'u',
> +                       .realbits       = 32,
> +                       .storagebits    = 32,
> +                       .endianness     = IIO_LE,
> +               },
> +       },
> +       IIO_CHAN_SOFT_TIMESTAMP(1)
> +};
> +
> +static ssize_t ecap_attr_show(struct device *dev,
> +                             struct device_attribute *attr, char *buf)
> +{
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       return sprintf(buf, "%d\n",
> +                      test_bit(ECAP_POLARITY_HIGH, &state->flags));
> +}
> +
> +static ssize_t ecap_attr_store(struct device *dev,
> +                              struct device_attribute *attr,
> +                              const char *buf,
> +                              size_t len)
> +{
> +       int ret;
> +       bool val;
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       if (test_bit(ECAP_ENABLED, &state->flags))
> +               return -EINVAL;
> +
> +       ret = strtobool(buf, &val);
> +       if (ret)
> +               return ret;
> +
> +       if (val)
> +               set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +       else
> +               clear_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +       return len;
> +}
> +
> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
> +       ecap_attr_show, ecap_attr_store, 0);
> +
> +static struct attribute *ecap_attributes[] = {
> +       &iio_dev_attr_pulse_polarity.dev_attr.attr,
> +       NULL,
> +};
> +
> +static struct attribute_group ecap_attribute_group = {
> +       .attrs = ecap_attributes,
> +};
> +
> +static int ecap_read_raw(struct iio_dev *idev,
> +                       struct iio_chan_spec const *ch, int *val,
> +                       int *val2, long mask)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               /*
> +                * Always return 0 as a pulse width sample
> +                * is only valid in a triggered condition
> +                */
> +               *val = 0;
> +               return IIO_VAL_INT;
> +       case IIO_CHAN_INFO_SCALE:
> +               *val = 0;
> +               *val2 = NSEC_PER_SEC / state->clk_rate;
> +               return IIO_VAL_INT_PLUS_NANO;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static const struct iio_info ecap_info = {
> +       .driver_module = THIS_MODULE,
> +       .attrs = &ecap_attribute_group,
> +       .read_raw = &ecap_read_raw,
> +};
> +
> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
> +{
> +       struct iio_poll_func *pf = private;
> +       struct iio_dev *idev = pf->indio_dev;
> +       struct ecap_state *state = iio_priv(idev);
> +
> +       /* Read pulse counter value */
> +       *state->buf = readl(state->regs + CAP2);
> +
> +       iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
> +
> +       iio_trigger_notify_done(idev->trig);
> +
> +       return IRQ_HANDLED;
> +};
> +
> +
> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
> +       .owner = THIS_MODULE,
> +};
> +
> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
> +{
> +       struct iio_dev *idev = private;
> +       struct ecap_state *state = iio_priv(idev);
> +       u16 ints;
> +
> +       iio_trigger_poll(idev->trig, 0);
> +
> +       /* Clear CAP2 interrupt */
> +       ints = readw(state->regs + ECFLG);
> +       if (ints & ECINT_CEVT2)
> +               writew(ECINT_CEVT2, state->regs + ECCLR);
> +       else
> +               dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
> +                        ints);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int ecap_buffer_predisable(struct iio_dev *idev)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +       int ret = 0;
> +       u16 ecctl2;
> +
> +       /* Stop capture */
> +       clear_bit(ECAP_ENABLED, &state->flags);
> +       ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
> +       writew(ecctl2, state->regs + ECCTL2);
> +
> +       /* Disable and clear all interrupts */
> +       writew(0, state->regs + ECEINT);
> +       writew(ECINT_ALL, state->regs + ECCLR);
> +
> +       ret = iio_triggered_buffer_predisable(idev);
> +
> +       pm_runtime_put_sync(idev->dev.parent);
> +
> +       return ret;
> +}
> +
> +static int ecap_buffer_postenable(struct iio_dev *idev)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +       int ret = 0;
> +       u16 ecctl1, ecctl2;
> +
> +       pm_runtime_get_sync(idev->dev.parent);
> +
> +       /* Configure pulse polarity */
> +       ecctl1 = readw(state->regs + ECCTL1);
> +       if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
> +               /* CAP1 rising, CAP2 falling */
> +               ecctl1 |= ECCTL1_CAP2POL;
> +               ecctl1 &= ~ECCTL1_CAP1POL;
> +       } else {
> +               /* CAP1 falling, CAP2 rising */
> +               ecctl1 &= ~ECCTL1_CAP2POL;
> +               ecctl1 |= ECCTL1_CAP1POL;
> +       }
> +       writew(ecctl1, state->regs + ECCTL1);
> +
> +       /* Enable CAP2 interrupt */
> +       writew(ECINT_CEVT2, state->regs + ECEINT);
> +
> +       /* Enable capture */
> +       ecctl2 = readw(state->regs + ECCTL2);
> +       ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
> +       writew(ecctl2, state->regs + ECCTL2);
> +       set_bit(ECAP_ENABLED, &state->flags);
> +
> +       ret = iio_triggered_buffer_postenable(idev);
> +
> +       return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
> +       .postenable = &ecap_buffer_postenable,
> +       .predisable = &ecap_buffer_predisable,
> +};
> +
> +static void ecap_init_hw(struct iio_dev *idev)
> +{
> +       struct ecap_state *state = iio_priv(idev);
> +
> +       clear_bit(ECAP_ENABLED, &state->flags);
> +       set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +       writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
> +              ECCTL1_CAP2POL | ECCTL1_CTRRST1,
> +              state->regs + ECCTL1);
> +
> +       writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
> +              state->regs + ECCTL2);
> +}
> +
> +static const struct of_device_id ecap_of_ids[] = {
> +       { .compatible   = "ti,am33xx-ecap" },
> +       { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
> +
> +static int ecap_probe(struct platform_device *pdev)
> +{
> +       int irq, ret;
> +       struct iio_dev *idev;
> +       struct ecap_state *state;
> +       struct resource *r;
> +       struct clk *clk;
> +       struct iio_trigger *trig;
> +       u16 status;
> +
> +       idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
> +       if (!idev)
> +               return -ENOMEM;
> +
> +       state = iio_priv(idev);
> +
> +       clk = devm_clk_get(&pdev->dev, "fck");
> +       if (IS_ERR(clk)) {
> +               dev_err(&pdev->dev, "failed to get clock\n");
> +               return PTR_ERR(clk);
> +       }
> +
> +       state->clk_rate = clk_get_rate(clk);
> +       if (!state->clk_rate) {
> +               dev_err(&pdev->dev, "failed to get clock rate\n");
> +               return -EINVAL;
> +       }
> +
> +       platform_set_drvdata(pdev, idev);
> +
> +       idev->dev.parent = &pdev->dev;
> +       idev->name = dev_name(&pdev->dev);
> +       idev->modes = INDIO_DIRECT_MODE;
> +       idev->info = &ecap_info;
> +       idev->channels = ecap_channels;
> +       /* One h/w capture and one s/w timestamp channel per instance */
> +       idev->num_channels = ARRAY_SIZE(ecap_channels);
> +
> +       trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
> +                                     idev->name, idev->id);
> +       if (!trig)
> +               return -ENOMEM;
> +       trig->dev.parent = idev->dev.parent;
> +       iio_trigger_set_drvdata(trig, idev);
> +       trig->ops = &iio_interrupt_trigger_ops;
> +
> +       ret = iio_trigger_register(trig);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to register trigger\n");
> +               return ret;
> +       }
> +
> +       ret = iio_triggered_buffer_setup(idev, NULL,
> +                                        &ecap_trigger_handler,
> +                                        &ecap_buffer_setup_ops);
> +       if (ret)
> +               return ret;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0) {
> +               dev_err(&pdev->dev, "no irq is specified\n");
> +               return irq;
> +       }
> +       ret = devm_request_irq(&pdev->dev, irq,
> +                               &ecap_interrupt_handler,
> +                               0, dev_name(&pdev->dev), idev);
> +       if (ret) {
> +               dev_err(&pdev->dev, "unable to request irq\n");
> +               goto uninit_buffer;
> +       }
> +
> +       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       state->regs = devm_ioremap_resource(&pdev->dev, r);
> +       if (IS_ERR(state->regs)) {
> +               dev_err(&pdev->dev, "unable to remap registers\n");
> +               ret = PTR_ERR(state->regs);
> +               goto uninit_buffer;
> +       };
> +
> +       ret = iio_device_register(idev);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "unable to register device\n");
> +               goto uninit_buffer;
> +       }
> +
> +       state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
> +       if (!state->buf) {
> +               ret = -ENOMEM;
> +               goto uninit_buffer;
> +       }
> +
> +       pm_runtime_enable(&pdev->dev);
> +       pm_runtime_get_sync(&pdev->dev);
> +
> +       status = pwmss_submodule_state_change(pdev->dev.parent,
> +                       PWMSS_ECAPCLK_EN);
> +       if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
> +               dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
> +               ret = -EINVAL;
> +               goto pwmss_clk_failure;
> +       }
> +
> +       ecap_init_hw(idev);
> +
> +       pm_runtime_put_sync(&pdev->dev);
> +
> +       return 0;
> +
> +pwmss_clk_failure:
> +       pm_runtime_put_sync(&pdev->dev);
> +       pm_runtime_disable(&pdev->dev);
> +       iio_device_unregister(idev);
> +
> +uninit_buffer:
> +       iio_triggered_buffer_cleanup(idev);
> +
> +       return ret;
> +}
> +
> +static int ecap_remove(struct platform_device *pdev)
> +{
> +       struct iio_dev *idev = platform_get_drvdata(pdev);
> +
> +       pm_runtime_get_sync(&pdev->dev);
> +
> +       pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
> +
> +       pm_runtime_put_sync(&pdev->dev);
> +       pm_runtime_disable(&pdev->dev);
> +
> +       iio_device_unregister(idev);
> +       iio_triggered_buffer_cleanup(idev);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ecap_suspend(struct device *dev)
> +{
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       pm_runtime_get_sync(dev);
> +       state->ctx.cap1 = readl(state->regs + CAP1);
> +       state->ctx.cap2 = readl(state->regs + CAP2);
> +       state->ctx.eceint = readw(state->regs + ECEINT);
> +       state->ctx.ecctl1 = readw(state->regs + ECCTL1);
> +       state->ctx.ecctl2 = readw(state->regs + ECCTL2);
> +       pm_runtime_put_sync(dev);
> +
> +       /* If capture was active, disable ECAP */
> +       if (test_bit(ECAP_ENABLED, &state->flags))
> +               pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +
> +static int ecap_resume(struct device *dev)
> +{
> +       struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +       /* If capture was active, enable ECAP */
> +       if (test_bit(ECAP_ENABLED, &state->flags))
> +               pm_runtime_get_sync(dev);
> +
> +       pm_runtime_get_sync(dev);
> +       writel(state->ctx.cap1, state->regs + CAP1);
> +       writel(state->ctx.cap2, state->regs + CAP2);
> +       writew(state->ctx.eceint, state->regs + ECEINT);
> +       writew(state->ctx.ecctl1, state->regs + ECCTL1);
> +       writew(state->ctx.ecctl2, state->regs + ECCTL2);
> +       pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
> +
> +static struct platform_driver ecap_iio_driver = {
> +       .driver = {
> +               .name           = "ecap",
> +               .owner          = THIS_MODULE,
> +               .of_match_table = of_match_ptr(ecap_of_ids),
> +               .pm             = &ecap_pm_ops,
> +       },
> +       .probe = ecap_probe,
> +       .remove = ecap_remove,
> +};
> +
> +module_platform_driver(ecap_iio_driver);
> +
> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
> +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
> +MODULE_LICENSE("GPL");
> --
> 1.8.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2016-03-28 15:30     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2016-03-28 15:30 UTC (permalink / raw)
  To: Matt Porter, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

On 05/02/14 19:01, Matt Porter wrote:
> Adds support for capturing PWM signals using the TI ECAP peripheral.
> This driver supports triggered buffer capture of pulses on multiple
> ECAP instances. In addition, the driver supports configurable polarity
> of the signal to be captured.
> 
> Signed-off-by: Matt Porter <mporter@linaro.org>
Hi Matt,

Given I had a personal message asking about this a few weeks ago, I thought I'd
ask... Any plans to resurrect this?
> ---
>  drivers/iio/pulse/Kconfig  |  20 ++
>  drivers/iio/pulse/Makefile |   6 +
>  drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 517 insertions(+)
>  create mode 100644 drivers/iio/pulse/Kconfig
>  create mode 100644 drivers/iio/pulse/Makefile
>  create mode 100644 drivers/iio/pulse/tiecap.c
> 
> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
> new file mode 100644
> index 0000000..9864d4b
> --- /dev/null
> +++ b/drivers/iio/pulse/Kconfig
> @@ -0,0 +1,20 @@
> +#
> +# Pulse Capture Devices
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "Pulse Capture Devices"
> +
> +config IIO_TIECAP
> +	tristate "TI ECAP Pulse Capture"
> +	depends on SOC_AM33XX
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	 If you say yes here you get support for the TI ECAP peripheral
> +	 in pulse capture mode.
> +
> +	 This driver can also be built as a module.  If so, the module
> +	 will be called tiecap
> +
> +endmenu
> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
> new file mode 100644
> index 0000000..94d4b00
> --- /dev/null
> +++ b/drivers/iio/pulse/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for IIO PWM Capture Devices
> +#
> +
> +# When adding new entries keep the list in alphabetical order
> +obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
> new file mode 100644
> index 0000000..fd96745
> --- /dev/null
> +++ b/drivers/iio/pulse/tiecap.c
> @@ -0,0 +1,491 @@
> +/*
> + * ECAP IIO pulse capture driver
> + *
> + * Copyright (C) 2014 Linaro Limited
> + * Author: Matt Porter <mporter@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "../../pwm/pwm-tipwmss.h"
> +
> +/* ECAP regs and bits */
> +#define CAP1			0x08
> +#define CAP2			0x0c
> +#define ECCTL1			0x28
> +#define ECCTL1_RUN_FREE		BIT(15)
> +#define ECCTL1_CAPLDEN		BIT(8)
> +#define ECCTL1_CAP2POL		BIT(2)
> +#define ECCTL1_CTRRST1		BIT(1)
> +#define ECCTL1_CAP1POL		BIT(0)
> +#define ECCTL2			0x2a
> +#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
> +#define ECCTL2_TSCTR_FREERUN	BIT(4)
> +#define ECCTL2_REARM		BIT(3)
> +#define ECCTL2_STOP_WRAP_2	BIT(1)
> +#define ECEINT			0x2c
> +#define ECFLG			0x2e
> +#define ECCLR			0x30
> +#define ECINT_CTRCMP		BIT(7)
> +#define ECINT_CTRPRD		BIT(6)
> +#define ECINT_CTROVF		BIT(5)
> +#define ECINT_CEVT4		BIT(4)
> +#define ECINT_CEVT3		BIT(3)
> +#define ECINT_CEVT2		BIT(2)
> +#define ECINT_CEVT1		BIT(1)
> +#define ECINT_ALL		(ECINT_CTRCMP |	\
> +				ECINT_CTRPRD |	\
> +				ECINT_CTROVF |	\
> +				ECINT_CEVT4 |	\
> +				ECINT_CEVT3 |	\
> +				ECINT_CEVT2 |	\
> +				ECINT_CEVT1)
> +
> +/* ECAP driver flags */
> +#define ECAP_POLARITY_HIGH	BIT(1)
> +#define ECAP_ENABLED		BIT(0)
> +
> +struct ecap_context {
> +	u32	cap1;
> +	u32	cap2;
> +	u16	ecctl1;
> +	u16	ecctl2;
> +	u16	eceint;
> +};
> +
> +struct ecap_state {
> +	unsigned long		flags;
> +	unsigned int		clk_rate;
> +	void __iomem		*regs;
> +	u32			*buf;
> +	struct ecap_context	ctx;
> +};
> +
> +#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
> +
> +static const struct iio_chan_spec ecap_channels[] = {
> +	{
> +		.type		= IIO_PULSE,
> +		.info_mask_separate =
> +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> +		.scan_index	= 0,
> +		.scan_type = {
> +			.sign		= 'u',
> +			.realbits	= 32,
> +			.storagebits	= 32,
> +			.endianness	= IIO_LE,
> +		},
> +	},
> +	IIO_CHAN_SOFT_TIMESTAMP(1)
> +};
> +
> +static ssize_t ecap_attr_show(struct device *dev,
> +			      struct device_attribute *attr, char *buf)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	return sprintf(buf, "%d\n",
> +		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
> +}
> +
> +static ssize_t ecap_attr_store(struct device *dev,
> +			       struct device_attribute *attr,
> +			       const char *buf,
> +			       size_t len)
> +{
> +	int ret;
> +	bool val;
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		return -EINVAL;
> +
> +	ret = strtobool(buf, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val)
> +		set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +	else
> +		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
> +	ecap_attr_show, ecap_attr_store, 0);
> +
> +static struct attribute *ecap_attributes[] = {
> +	&iio_dev_attr_pulse_polarity.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group ecap_attribute_group = {
> +	.attrs = ecap_attributes,
> +};
> +
> +static int ecap_read_raw(struct iio_dev *idev,
> +			struct iio_chan_spec const *ch, int *val,
> +			int *val2, long mask)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		/*
> +		 * Always return 0 as a pulse width sample
> +		 * is only valid in a triggered condition
> +		 */
> +		*val = 0;
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = 0;
> +		*val2 = NSEC_PER_SEC / state->clk_rate;
> +		return IIO_VAL_INT_PLUS_NANO;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info ecap_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &ecap_attribute_group,
> +	.read_raw = &ecap_read_raw,
> +};
> +
> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
> +{
> +	struct iio_poll_func *pf = private;
> +	struct iio_dev *idev = pf->indio_dev;
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	/* Read pulse counter value */
> +	*state->buf = readl(state->regs + CAP2);
> +
> +	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
> +
> +	iio_trigger_notify_done(idev->trig);
> +
> +	return IRQ_HANDLED;
> +};
> +
> +
> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
> +{
> +	struct iio_dev *idev = private;
> +	struct ecap_state *state = iio_priv(idev);
> +	u16 ints;
> +
> +	iio_trigger_poll(idev->trig, 0);
> +
> +	/* Clear CAP2 interrupt */
> +	ints = readw(state->regs + ECFLG);
> +	if (ints & ECINT_CEVT2)
> +		writew(ECINT_CEVT2, state->regs + ECCLR);
> +	else
> +		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
> +			 ints);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int ecap_buffer_predisable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl2;
> +
> +	/* Stop capture */
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
> +	writew(ecctl2, state->regs + ECCTL2);
> +
> +	/* Disable and clear all interrupts */
> +	writew(0, state->regs + ECEINT);
> +	writew(ECINT_ALL, state->regs + ECCLR);
> +
> +	ret = iio_triggered_buffer_predisable(idev);
> +
> +	pm_runtime_put_sync(idev->dev.parent);
> +
> +	return ret;
> +}
> +
> +static int ecap_buffer_postenable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl1, ecctl2;
> +
> +	pm_runtime_get_sync(idev->dev.parent);
> +
> +	/* Configure pulse polarity */
> +	ecctl1 = readw(state->regs + ECCTL1);
> +	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
> +		/* CAP1 rising, CAP2 falling */
> +		ecctl1 |= ECCTL1_CAP2POL;
> +		ecctl1 &= ~ECCTL1_CAP1POL;
> +	} else {
> +		/* CAP1 falling, CAP2 rising */
> +		ecctl1 &= ~ECCTL1_CAP2POL;
> +		ecctl1 |= ECCTL1_CAP1POL;
> +	}
> +	writew(ecctl1, state->regs + ECCTL1);
> +
> +	/* Enable CAP2 interrupt */
> +	writew(ECINT_CEVT2, state->regs + ECEINT);
> +
> +	/* Enable capture */
> +	ecctl2 = readw(state->regs + ECCTL2);
> +	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
> +	writew(ecctl2, state->regs + ECCTL2);
> +	set_bit(ECAP_ENABLED, &state->flags);
> +
> +	ret = iio_triggered_buffer_postenable(idev);
> +
> +	return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
> +	.postenable = &ecap_buffer_postenable,
> +	.predisable = &ecap_buffer_predisable,
> +};
> +
> +static void ecap_init_hw(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
> +	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
> +	       state->regs + ECCTL1);
> +
> +	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
> +	       state->regs + ECCTL2);
> +}
> +
> +static const struct of_device_id ecap_of_ids[] = {
> +	{ .compatible	= "ti,am33xx-ecap" },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
> +
> +static int ecap_probe(struct platform_device *pdev)
> +{
> +	int irq, ret;
> +	struct iio_dev *idev;
> +	struct ecap_state *state;
> +	struct resource *r;
> +	struct clk *clk;
> +	struct iio_trigger *trig;
> +	u16 status;
> +
> +	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
> +	if (!idev)
> +		return -ENOMEM;
> +
> +	state = iio_priv(idev);
> +
> +	clk = devm_clk_get(&pdev->dev, "fck");
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "failed to get clock\n");
> +		return PTR_ERR(clk);
> +	}
> +
> +	state->clk_rate = clk_get_rate(clk);
> +	if (!state->clk_rate) {
> +		dev_err(&pdev->dev, "failed to get clock rate\n");
> +		return -EINVAL;
> +	}
> +
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = dev_name(&pdev->dev);
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &ecap_info;
> +	idev->channels = ecap_channels;
> +	/* One h/w capture and one s/w timestamp channel per instance */
> +	idev->num_channels = ARRAY_SIZE(ecap_channels);
> +
> +	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
> +				      idev->name, idev->id);
> +	if (!trig)
> +		return -ENOMEM;
> +	trig->dev.parent = idev->dev.parent;
> +	iio_trigger_set_drvdata(trig, idev);
> +	trig->ops = &iio_interrupt_trigger_ops;
> +
> +	ret = iio_trigger_register(trig);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register trigger\n");
> +		return ret;
> +	}
> +
> +	ret = iio_triggered_buffer_setup(idev, NULL,
> +					 &ecap_trigger_handler,
> +					 &ecap_buffer_setup_ops);
> +	if (ret)
> +		return ret;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq is specified\n");
> +		return irq;
> +	}
> +	ret = devm_request_irq(&pdev->dev, irq,
> +				&ecap_interrupt_handler,
> +				0, dev_name(&pdev->dev), idev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to request irq\n");
> +		goto uninit_buffer;
> +	}
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	state->regs = devm_ioremap_resource(&pdev->dev, r);
> +	if (IS_ERR(state->regs)) {
> +		dev_err(&pdev->dev, "unable to remap registers\n");
> +		ret = PTR_ERR(state->regs);
> +		goto uninit_buffer;
> +	};
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "unable to register device\n");
> +		goto uninit_buffer;
> +	}
> +
> +	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
> +	if (!state->buf) {
> +		ret = -ENOMEM;
> +		goto uninit_buffer;
> +	}
> +
> +	pm_runtime_enable(&pdev->dev);
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	status = pwmss_submodule_state_change(pdev->dev.parent,
> +			PWMSS_ECAPCLK_EN);
> +	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
> +		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
> +		ret = -EINVAL;
> +		goto pwmss_clk_failure;
> +	}
> +
> +	ecap_init_hw(idev);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +
> +	return 0;
> +
> +pwmss_clk_failure:
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	iio_device_unregister(idev);
> +
> +uninit_buffer:
> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return ret;
> +}
> +
> +static int ecap_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	iio_device_unregister(idev);
> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ecap_suspend(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	state->ctx.cap1 = readl(state->regs + CAP1);
> +	state->ctx.cap2 = readl(state->regs + CAP2);
> +	state->ctx.eceint = readw(state->regs + ECEINT);
> +	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
> +	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	/* If capture was active, disable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int ecap_resume(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	/* If capture was active, enable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_get_sync(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	writel(state->ctx.cap1, state->regs + CAP1);
> +	writel(state->ctx.cap2, state->regs + CAP2);
> +	writew(state->ctx.eceint, state->regs + ECEINT);
> +	writew(state->ctx.ecctl1, state->regs + ECCTL1);
> +	writew(state->ctx.ecctl2, state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
> +
> +static struct platform_driver ecap_iio_driver = {
> +	.driver = {
> +		.name		= "ecap",
> +		.owner		= THIS_MODULE,
> +		.of_match_table = of_match_ptr(ecap_of_ids),
> +		.pm		= &ecap_pm_ops,
> +	},
> +	.probe = ecap_probe,
> +	.remove = ecap_remove,
> +};
> +
> +module_platform_driver(ecap_iio_driver);
> +
> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
> +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
> +MODULE_LICENSE("GPL");
> 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2016-03-28 15:30     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2016-03-28 15:30 UTC (permalink / raw)
  To: Matt Porter, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List

On 05/02/14 19:01, Matt Porter wrote:
> Adds support for capturing PWM signals using the TI ECAP peripheral.
> This driver supports triggered buffer capture of pulses on multiple
> ECAP instances. In addition, the driver supports configurable polarity
> of the signal to be captured.
> 
> Signed-off-by: Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Hi Matt,

Given I had a personal message asking about this a few weeks ago, I thought I'd
ask... Any plans to resurrect this?
> ---
>  drivers/iio/pulse/Kconfig  |  20 ++
>  drivers/iio/pulse/Makefile |   6 +
>  drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 517 insertions(+)
>  create mode 100644 drivers/iio/pulse/Kconfig
>  create mode 100644 drivers/iio/pulse/Makefile
>  create mode 100644 drivers/iio/pulse/tiecap.c
> 
> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
> new file mode 100644
> index 0000000..9864d4b
> --- /dev/null
> +++ b/drivers/iio/pulse/Kconfig
> @@ -0,0 +1,20 @@
> +#
> +# Pulse Capture Devices
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "Pulse Capture Devices"
> +
> +config IIO_TIECAP
> +	tristate "TI ECAP Pulse Capture"
> +	depends on SOC_AM33XX
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	 If you say yes here you get support for the TI ECAP peripheral
> +	 in pulse capture mode.
> +
> +	 This driver can also be built as a module.  If so, the module
> +	 will be called tiecap
> +
> +endmenu
> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
> new file mode 100644
> index 0000000..94d4b00
> --- /dev/null
> +++ b/drivers/iio/pulse/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for IIO PWM Capture Devices
> +#
> +
> +# When adding new entries keep the list in alphabetical order
> +obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
> new file mode 100644
> index 0000000..fd96745
> --- /dev/null
> +++ b/drivers/iio/pulse/tiecap.c
> @@ -0,0 +1,491 @@
> +/*
> + * ECAP IIO pulse capture driver
> + *
> + * Copyright (C) 2014 Linaro Limited
> + * Author: Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "../../pwm/pwm-tipwmss.h"
> +
> +/* ECAP regs and bits */
> +#define CAP1			0x08
> +#define CAP2			0x0c
> +#define ECCTL1			0x28
> +#define ECCTL1_RUN_FREE		BIT(15)
> +#define ECCTL1_CAPLDEN		BIT(8)
> +#define ECCTL1_CAP2POL		BIT(2)
> +#define ECCTL1_CTRRST1		BIT(1)
> +#define ECCTL1_CAP1POL		BIT(0)
> +#define ECCTL2			0x2a
> +#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
> +#define ECCTL2_TSCTR_FREERUN	BIT(4)
> +#define ECCTL2_REARM		BIT(3)
> +#define ECCTL2_STOP_WRAP_2	BIT(1)
> +#define ECEINT			0x2c
> +#define ECFLG			0x2e
> +#define ECCLR			0x30
> +#define ECINT_CTRCMP		BIT(7)
> +#define ECINT_CTRPRD		BIT(6)
> +#define ECINT_CTROVF		BIT(5)
> +#define ECINT_CEVT4		BIT(4)
> +#define ECINT_CEVT3		BIT(3)
> +#define ECINT_CEVT2		BIT(2)
> +#define ECINT_CEVT1		BIT(1)
> +#define ECINT_ALL		(ECINT_CTRCMP |	\
> +				ECINT_CTRPRD |	\
> +				ECINT_CTROVF |	\
> +				ECINT_CEVT4 |	\
> +				ECINT_CEVT3 |	\
> +				ECINT_CEVT2 |	\
> +				ECINT_CEVT1)
> +
> +/* ECAP driver flags */
> +#define ECAP_POLARITY_HIGH	BIT(1)
> +#define ECAP_ENABLED		BIT(0)
> +
> +struct ecap_context {
> +	u32	cap1;
> +	u32	cap2;
> +	u16	ecctl1;
> +	u16	ecctl2;
> +	u16	eceint;
> +};
> +
> +struct ecap_state {
> +	unsigned long		flags;
> +	unsigned int		clk_rate;
> +	void __iomem		*regs;
> +	u32			*buf;
> +	struct ecap_context	ctx;
> +};
> +
> +#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
> +
> +static const struct iio_chan_spec ecap_channels[] = {
> +	{
> +		.type		= IIO_PULSE,
> +		.info_mask_separate =
> +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> +		.scan_index	= 0,
> +		.scan_type = {
> +			.sign		= 'u',
> +			.realbits	= 32,
> +			.storagebits	= 32,
> +			.endianness	= IIO_LE,
> +		},
> +	},
> +	IIO_CHAN_SOFT_TIMESTAMP(1)
> +};
> +
> +static ssize_t ecap_attr_show(struct device *dev,
> +			      struct device_attribute *attr, char *buf)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	return sprintf(buf, "%d\n",
> +		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
> +}
> +
> +static ssize_t ecap_attr_store(struct device *dev,
> +			       struct device_attribute *attr,
> +			       const char *buf,
> +			       size_t len)
> +{
> +	int ret;
> +	bool val;
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		return -EINVAL;
> +
> +	ret = strtobool(buf, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val)
> +		set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +	else
> +		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
> +	ecap_attr_show, ecap_attr_store, 0);
> +
> +static struct attribute *ecap_attributes[] = {
> +	&iio_dev_attr_pulse_polarity.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group ecap_attribute_group = {
> +	.attrs = ecap_attributes,
> +};
> +
> +static int ecap_read_raw(struct iio_dev *idev,
> +			struct iio_chan_spec const *ch, int *val,
> +			int *val2, long mask)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		/*
> +		 * Always return 0 as a pulse width sample
> +		 * is only valid in a triggered condition
> +		 */
> +		*val = 0;
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = 0;
> +		*val2 = NSEC_PER_SEC / state->clk_rate;
> +		return IIO_VAL_INT_PLUS_NANO;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info ecap_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &ecap_attribute_group,
> +	.read_raw = &ecap_read_raw,
> +};
> +
> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
> +{
> +	struct iio_poll_func *pf = private;
> +	struct iio_dev *idev = pf->indio_dev;
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	/* Read pulse counter value */
> +	*state->buf = readl(state->regs + CAP2);
> +
> +	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
> +
> +	iio_trigger_notify_done(idev->trig);
> +
> +	return IRQ_HANDLED;
> +};
> +
> +
> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
> +{
> +	struct iio_dev *idev = private;
> +	struct ecap_state *state = iio_priv(idev);
> +	u16 ints;
> +
> +	iio_trigger_poll(idev->trig, 0);
> +
> +	/* Clear CAP2 interrupt */
> +	ints = readw(state->regs + ECFLG);
> +	if (ints & ECINT_CEVT2)
> +		writew(ECINT_CEVT2, state->regs + ECCLR);
> +	else
> +		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
> +			 ints);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int ecap_buffer_predisable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl2;
> +
> +	/* Stop capture */
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
> +	writew(ecctl2, state->regs + ECCTL2);
> +
> +	/* Disable and clear all interrupts */
> +	writew(0, state->regs + ECEINT);
> +	writew(ECINT_ALL, state->regs + ECCLR);
> +
> +	ret = iio_triggered_buffer_predisable(idev);
> +
> +	pm_runtime_put_sync(idev->dev.parent);
> +
> +	return ret;
> +}
> +
> +static int ecap_buffer_postenable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl1, ecctl2;
> +
> +	pm_runtime_get_sync(idev->dev.parent);
> +
> +	/* Configure pulse polarity */
> +	ecctl1 = readw(state->regs + ECCTL1);
> +	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
> +		/* CAP1 rising, CAP2 falling */
> +		ecctl1 |= ECCTL1_CAP2POL;
> +		ecctl1 &= ~ECCTL1_CAP1POL;
> +	} else {
> +		/* CAP1 falling, CAP2 rising */
> +		ecctl1 &= ~ECCTL1_CAP2POL;
> +		ecctl1 |= ECCTL1_CAP1POL;
> +	}
> +	writew(ecctl1, state->regs + ECCTL1);
> +
> +	/* Enable CAP2 interrupt */
> +	writew(ECINT_CEVT2, state->regs + ECEINT);
> +
> +	/* Enable capture */
> +	ecctl2 = readw(state->regs + ECCTL2);
> +	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
> +	writew(ecctl2, state->regs + ECCTL2);
> +	set_bit(ECAP_ENABLED, &state->flags);
> +
> +	ret = iio_triggered_buffer_postenable(idev);
> +
> +	return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
> +	.postenable = &ecap_buffer_postenable,
> +	.predisable = &ecap_buffer_predisable,
> +};
> +
> +static void ecap_init_hw(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
> +	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
> +	       state->regs + ECCTL1);
> +
> +	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
> +	       state->regs + ECCTL2);
> +}
> +
> +static const struct of_device_id ecap_of_ids[] = {
> +	{ .compatible	= "ti,am33xx-ecap" },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
> +
> +static int ecap_probe(struct platform_device *pdev)
> +{
> +	int irq, ret;
> +	struct iio_dev *idev;
> +	struct ecap_state *state;
> +	struct resource *r;
> +	struct clk *clk;
> +	struct iio_trigger *trig;
> +	u16 status;
> +
> +	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
> +	if (!idev)
> +		return -ENOMEM;
> +
> +	state = iio_priv(idev);
> +
> +	clk = devm_clk_get(&pdev->dev, "fck");
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "failed to get clock\n");
> +		return PTR_ERR(clk);
> +	}
> +
> +	state->clk_rate = clk_get_rate(clk);
> +	if (!state->clk_rate) {
> +		dev_err(&pdev->dev, "failed to get clock rate\n");
> +		return -EINVAL;
> +	}
> +
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = dev_name(&pdev->dev);
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &ecap_info;
> +	idev->channels = ecap_channels;
> +	/* One h/w capture and one s/w timestamp channel per instance */
> +	idev->num_channels = ARRAY_SIZE(ecap_channels);
> +
> +	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
> +				      idev->name, idev->id);
> +	if (!trig)
> +		return -ENOMEM;
> +	trig->dev.parent = idev->dev.parent;
> +	iio_trigger_set_drvdata(trig, idev);
> +	trig->ops = &iio_interrupt_trigger_ops;
> +
> +	ret = iio_trigger_register(trig);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register trigger\n");
> +		return ret;
> +	}
> +
> +	ret = iio_triggered_buffer_setup(idev, NULL,
> +					 &ecap_trigger_handler,
> +					 &ecap_buffer_setup_ops);
> +	if (ret)
> +		return ret;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq is specified\n");
> +		return irq;
> +	}
> +	ret = devm_request_irq(&pdev->dev, irq,
> +				&ecap_interrupt_handler,
> +				0, dev_name(&pdev->dev), idev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to request irq\n");
> +		goto uninit_buffer;
> +	}
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	state->regs = devm_ioremap_resource(&pdev->dev, r);
> +	if (IS_ERR(state->regs)) {
> +		dev_err(&pdev->dev, "unable to remap registers\n");
> +		ret = PTR_ERR(state->regs);
> +		goto uninit_buffer;
> +	};
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "unable to register device\n");
> +		goto uninit_buffer;
> +	}
> +
> +	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
> +	if (!state->buf) {
> +		ret = -ENOMEM;
> +		goto uninit_buffer;
> +	}
> +
> +	pm_runtime_enable(&pdev->dev);
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	status = pwmss_submodule_state_change(pdev->dev.parent,
> +			PWMSS_ECAPCLK_EN);
> +	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
> +		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
> +		ret = -EINVAL;
> +		goto pwmss_clk_failure;
> +	}
> +
> +	ecap_init_hw(idev);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +
> +	return 0;
> +
> +pwmss_clk_failure:
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	iio_device_unregister(idev);
> +
> +uninit_buffer:
> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return ret;
> +}
> +
> +static int ecap_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	iio_device_unregister(idev);
> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ecap_suspend(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	state->ctx.cap1 = readl(state->regs + CAP1);
> +	state->ctx.cap2 = readl(state->regs + CAP2);
> +	state->ctx.eceint = readw(state->regs + ECEINT);
> +	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
> +	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	/* If capture was active, disable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int ecap_resume(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	/* If capture was active, enable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_get_sync(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	writel(state->ctx.cap1, state->regs + CAP1);
> +	writel(state->ctx.cap2, state->regs + CAP2);
> +	writew(state->ctx.eceint, state->regs + ECEINT);
> +	writew(state->ctx.ecctl1, state->regs + ECCTL1);
> +	writew(state->ctx.ecctl2, state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
> +
> +static struct platform_driver ecap_iio_driver = {
> +	.driver = {
> +		.name		= "ecap",
> +		.owner		= THIS_MODULE,
> +		.of_match_table = of_match_ptr(ecap_of_ids),
> +		.pm		= &ecap_pm_ops,
> +	},
> +	.probe = ecap_probe,
> +	.remove = ecap_remove,
> +};
> +
> +module_platform_driver(ecap_iio_driver);
> +
> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
> +MODULE_AUTHOR("Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
> +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	[flat|nested] 42+ messages in thread

* [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2016-03-28 15:30     ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2016-03-28 15:30 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/02/14 19:01, Matt Porter wrote:
> Adds support for capturing PWM signals using the TI ECAP peripheral.
> This driver supports triggered buffer capture of pulses on multiple
> ECAP instances. In addition, the driver supports configurable polarity
> of the signal to be captured.
> 
> Signed-off-by: Matt Porter <mporter@linaro.org>
Hi Matt,

Given I had a personal message asking about this a few weeks ago, I thought I'd
ask... Any plans to resurrect this?
> ---
>  drivers/iio/pulse/Kconfig  |  20 ++
>  drivers/iio/pulse/Makefile |   6 +
>  drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 517 insertions(+)
>  create mode 100644 drivers/iio/pulse/Kconfig
>  create mode 100644 drivers/iio/pulse/Makefile
>  create mode 100644 drivers/iio/pulse/tiecap.c
> 
> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
> new file mode 100644
> index 0000000..9864d4b
> --- /dev/null
> +++ b/drivers/iio/pulse/Kconfig
> @@ -0,0 +1,20 @@
> +#
> +# Pulse Capture Devices
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "Pulse Capture Devices"
> +
> +config IIO_TIECAP
> +	tristate "TI ECAP Pulse Capture"
> +	depends on SOC_AM33XX
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	 If you say yes here you get support for the TI ECAP peripheral
> +	 in pulse capture mode.
> +
> +	 This driver can also be built as a module.  If so, the module
> +	 will be called tiecap
> +
> +endmenu
> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
> new file mode 100644
> index 0000000..94d4b00
> --- /dev/null
> +++ b/drivers/iio/pulse/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for IIO PWM Capture Devices
> +#
> +
> +# When adding new entries keep the list in alphabetical order
> +obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
> new file mode 100644
> index 0000000..fd96745
> --- /dev/null
> +++ b/drivers/iio/pulse/tiecap.c
> @@ -0,0 +1,491 @@
> +/*
> + * ECAP IIO pulse capture driver
> + *
> + * Copyright (C) 2014 Linaro Limited
> + * Author: Matt Porter <mporter@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "../../pwm/pwm-tipwmss.h"
> +
> +/* ECAP regs and bits */
> +#define CAP1			0x08
> +#define CAP2			0x0c
> +#define ECCTL1			0x28
> +#define ECCTL1_RUN_FREE		BIT(15)
> +#define ECCTL1_CAPLDEN		BIT(8)
> +#define ECCTL1_CAP2POL		BIT(2)
> +#define ECCTL1_CTRRST1		BIT(1)
> +#define ECCTL1_CAP1POL		BIT(0)
> +#define ECCTL2			0x2a
> +#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
> +#define ECCTL2_TSCTR_FREERUN	BIT(4)
> +#define ECCTL2_REARM		BIT(3)
> +#define ECCTL2_STOP_WRAP_2	BIT(1)
> +#define ECEINT			0x2c
> +#define ECFLG			0x2e
> +#define ECCLR			0x30
> +#define ECINT_CTRCMP		BIT(7)
> +#define ECINT_CTRPRD		BIT(6)
> +#define ECINT_CTROVF		BIT(5)
> +#define ECINT_CEVT4		BIT(4)
> +#define ECINT_CEVT3		BIT(3)
> +#define ECINT_CEVT2		BIT(2)
> +#define ECINT_CEVT1		BIT(1)
> +#define ECINT_ALL		(ECINT_CTRCMP |	\
> +				ECINT_CTRPRD |	\
> +				ECINT_CTROVF |	\
> +				ECINT_CEVT4 |	\
> +				ECINT_CEVT3 |	\
> +				ECINT_CEVT2 |	\
> +				ECINT_CEVT1)
> +
> +/* ECAP driver flags */
> +#define ECAP_POLARITY_HIGH	BIT(1)
> +#define ECAP_ENABLED		BIT(0)
> +
> +struct ecap_context {
> +	u32	cap1;
> +	u32	cap2;
> +	u16	ecctl1;
> +	u16	ecctl2;
> +	u16	eceint;
> +};
> +
> +struct ecap_state {
> +	unsigned long		flags;
> +	unsigned int		clk_rate;
> +	void __iomem		*regs;
> +	u32			*buf;
> +	struct ecap_context	ctx;
> +};
> +
> +#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
> +
> +static const struct iio_chan_spec ecap_channels[] = {
> +	{
> +		.type		= IIO_PULSE,
> +		.info_mask_separate =
> +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> +		.scan_index	= 0,
> +		.scan_type = {
> +			.sign		= 'u',
> +			.realbits	= 32,
> +			.storagebits	= 32,
> +			.endianness	= IIO_LE,
> +		},
> +	},
> +	IIO_CHAN_SOFT_TIMESTAMP(1)
> +};
> +
> +static ssize_t ecap_attr_show(struct device *dev,
> +			      struct device_attribute *attr, char *buf)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	return sprintf(buf, "%d\n",
> +		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
> +}
> +
> +static ssize_t ecap_attr_store(struct device *dev,
> +			       struct device_attribute *attr,
> +			       const char *buf,
> +			       size_t len)
> +{
> +	int ret;
> +	bool val;
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		return -EINVAL;
> +
> +	ret = strtobool(buf, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val)
> +		set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +	else
> +		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
> +	ecap_attr_show, ecap_attr_store, 0);
> +
> +static struct attribute *ecap_attributes[] = {
> +	&iio_dev_attr_pulse_polarity.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group ecap_attribute_group = {
> +	.attrs = ecap_attributes,
> +};
> +
> +static int ecap_read_raw(struct iio_dev *idev,
> +			struct iio_chan_spec const *ch, int *val,
> +			int *val2, long mask)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		/*
> +		 * Always return 0 as a pulse width sample
> +		 * is only valid in a triggered condition
> +		 */
> +		*val = 0;
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = 0;
> +		*val2 = NSEC_PER_SEC / state->clk_rate;
> +		return IIO_VAL_INT_PLUS_NANO;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info ecap_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &ecap_attribute_group,
> +	.read_raw = &ecap_read_raw,
> +};
> +
> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
> +{
> +	struct iio_poll_func *pf = private;
> +	struct iio_dev *idev = pf->indio_dev;
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	/* Read pulse counter value */
> +	*state->buf = readl(state->regs + CAP2);
> +
> +	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
> +
> +	iio_trigger_notify_done(idev->trig);
> +
> +	return IRQ_HANDLED;
> +};
> +
> +
> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
> +{
> +	struct iio_dev *idev = private;
> +	struct ecap_state *state = iio_priv(idev);
> +	u16 ints;
> +
> +	iio_trigger_poll(idev->trig, 0);
> +
> +	/* Clear CAP2 interrupt */
> +	ints = readw(state->regs + ECFLG);
> +	if (ints & ECINT_CEVT2)
> +		writew(ECINT_CEVT2, state->regs + ECCLR);
> +	else
> +		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
> +			 ints);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int ecap_buffer_predisable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl2;
> +
> +	/* Stop capture */
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
> +	writew(ecctl2, state->regs + ECCTL2);
> +
> +	/* Disable and clear all interrupts */
> +	writew(0, state->regs + ECEINT);
> +	writew(ECINT_ALL, state->regs + ECCLR);
> +
> +	ret = iio_triggered_buffer_predisable(idev);
> +
> +	pm_runtime_put_sync(idev->dev.parent);
> +
> +	return ret;
> +}
> +
> +static int ecap_buffer_postenable(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +	int ret = 0;
> +	u16 ecctl1, ecctl2;
> +
> +	pm_runtime_get_sync(idev->dev.parent);
> +
> +	/* Configure pulse polarity */
> +	ecctl1 = readw(state->regs + ECCTL1);
> +	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
> +		/* CAP1 rising, CAP2 falling */
> +		ecctl1 |= ECCTL1_CAP2POL;
> +		ecctl1 &= ~ECCTL1_CAP1POL;
> +	} else {
> +		/* CAP1 falling, CAP2 rising */
> +		ecctl1 &= ~ECCTL1_CAP2POL;
> +		ecctl1 |= ECCTL1_CAP1POL;
> +	}
> +	writew(ecctl1, state->regs + ECCTL1);
> +
> +	/* Enable CAP2 interrupt */
> +	writew(ECINT_CEVT2, state->regs + ECEINT);
> +
> +	/* Enable capture */
> +	ecctl2 = readw(state->regs + ECCTL2);
> +	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
> +	writew(ecctl2, state->regs + ECCTL2);
> +	set_bit(ECAP_ENABLED, &state->flags);
> +
> +	ret = iio_triggered_buffer_postenable(idev);
> +
> +	return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
> +	.postenable = &ecap_buffer_postenable,
> +	.predisable = &ecap_buffer_predisable,
> +};
> +
> +static void ecap_init_hw(struct iio_dev *idev)
> +{
> +	struct ecap_state *state = iio_priv(idev);
> +
> +	clear_bit(ECAP_ENABLED, &state->flags);
> +	set_bit(ECAP_POLARITY_HIGH, &state->flags);
> +
> +	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
> +	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
> +	       state->regs + ECCTL1);
> +
> +	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
> +	       state->regs + ECCTL2);
> +}
> +
> +static const struct of_device_id ecap_of_ids[] = {
> +	{ .compatible	= "ti,am33xx-ecap" },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
> +
> +static int ecap_probe(struct platform_device *pdev)
> +{
> +	int irq, ret;
> +	struct iio_dev *idev;
> +	struct ecap_state *state;
> +	struct resource *r;
> +	struct clk *clk;
> +	struct iio_trigger *trig;
> +	u16 status;
> +
> +	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
> +	if (!idev)
> +		return -ENOMEM;
> +
> +	state = iio_priv(idev);
> +
> +	clk = devm_clk_get(&pdev->dev, "fck");
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "failed to get clock\n");
> +		return PTR_ERR(clk);
> +	}
> +
> +	state->clk_rate = clk_get_rate(clk);
> +	if (!state->clk_rate) {
> +		dev_err(&pdev->dev, "failed to get clock rate\n");
> +		return -EINVAL;
> +	}
> +
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = dev_name(&pdev->dev);
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &ecap_info;
> +	idev->channels = ecap_channels;
> +	/* One h/w capture and one s/w timestamp channel per instance */
> +	idev->num_channels = ARRAY_SIZE(ecap_channels);
> +
> +	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
> +				      idev->name, idev->id);
> +	if (!trig)
> +		return -ENOMEM;
> +	trig->dev.parent = idev->dev.parent;
> +	iio_trigger_set_drvdata(trig, idev);
> +	trig->ops = &iio_interrupt_trigger_ops;
> +
> +	ret = iio_trigger_register(trig);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register trigger\n");
> +		return ret;
> +	}
> +
> +	ret = iio_triggered_buffer_setup(idev, NULL,
> +					 &ecap_trigger_handler,
> +					 &ecap_buffer_setup_ops);
> +	if (ret)
> +		return ret;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq is specified\n");
> +		return irq;
> +	}
> +	ret = devm_request_irq(&pdev->dev, irq,
> +				&ecap_interrupt_handler,
> +				0, dev_name(&pdev->dev), idev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to request irq\n");
> +		goto uninit_buffer;
> +	}
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	state->regs = devm_ioremap_resource(&pdev->dev, r);
> +	if (IS_ERR(state->regs)) {
> +		dev_err(&pdev->dev, "unable to remap registers\n");
> +		ret = PTR_ERR(state->regs);
> +		goto uninit_buffer;
> +	};
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "unable to register device\n");
> +		goto uninit_buffer;
> +	}
> +
> +	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
> +	if (!state->buf) {
> +		ret = -ENOMEM;
> +		goto uninit_buffer;
> +	}
> +
> +	pm_runtime_enable(&pdev->dev);
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	status = pwmss_submodule_state_change(pdev->dev.parent,
> +			PWMSS_ECAPCLK_EN);
> +	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
> +		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
> +		ret = -EINVAL;
> +		goto pwmss_clk_failure;
> +	}
> +
> +	ecap_init_hw(idev);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +
> +	return 0;
> +
> +pwmss_clk_failure:
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	iio_device_unregister(idev);
> +
> +uninit_buffer:
> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return ret;
> +}
> +
> +static int ecap_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	iio_device_unregister(idev);
> +	iio_triggered_buffer_cleanup(idev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ecap_suspend(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	state->ctx.cap1 = readl(state->regs + CAP1);
> +	state->ctx.cap2 = readl(state->regs + CAP2);
> +	state->ctx.eceint = readw(state->regs + ECEINT);
> +	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
> +	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	/* If capture was active, disable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int ecap_resume(struct device *dev)
> +{
> +	struct ecap_state *state = dev_to_ecap_state(dev);
> +
> +	/* If capture was active, enable ECAP */
> +	if (test_bit(ECAP_ENABLED, &state->flags))
> +		pm_runtime_get_sync(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	writel(state->ctx.cap1, state->regs + CAP1);
> +	writel(state->ctx.cap2, state->regs + CAP2);
> +	writew(state->ctx.eceint, state->regs + ECEINT);
> +	writew(state->ctx.ecctl1, state->regs + ECCTL1);
> +	writew(state->ctx.ecctl2, state->regs + ECCTL2);
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
> +
> +static struct platform_driver ecap_iio_driver = {
> +	.driver = {
> +		.name		= "ecap",
> +		.owner		= THIS_MODULE,
> +		.of_match_table = of_match_ptr(ecap_of_ids),
> +		.pm		= &ecap_pm_ops,
> +	},
> +	.probe = ecap_probe,
> +	.remove = ecap_remove,
> +};
> +
> +module_platform_driver(ecap_iio_driver);
> +
> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
> +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
> +MODULE_LICENSE("GPL");
> 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2016-04-03 10:41       ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2016-04-03 10:41 UTC (permalink / raw)
  To: Matt Porter, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List,
	Daniel Baluta, Lars-Peter Clausen, Peter Meerwald,
	Hartmut Knaack, Michael Welling

On 28/03/16 16:30, Jonathan Cameron wrote:
> On 05/02/14 19:01, Matt Porter wrote:
>> Adds support for capturing PWM signals using the TI ECAP peripheral.
>> This driver supports triggered buffer capture of pulses on multiple
>> ECAP instances. In addition, the driver supports configurable polarity
>> of the signal to be captured.
>>
>> Signed-off-by: Matt Porter <mporter@linaro.org>
> Hi Matt,
> 
> Given I had a personal message asking about this a few weeks ago, I thought I'd
> ask... Any plans to resurrect this?
Guess that's a no :( 

If anyone wants a a driver to get kernel driver merging experience with this would
be a good one.  Hardware is readily available I think as the beaglebone black has one
of these exposed - there is no support for it in the TI tree either that I can see.

I would cc'd the beagleboard google group but can't actually figure out which is
the right one.  Michael, if you know could you reply, with such a group cc'd?
(or anyone else for that matter)

I'd kind of like to pick this one up but will be a while before I clear enough time
to do so (possibly another couple of years!)

Jonathan



>> ---
>>  drivers/iio/pulse/Kconfig  |  20 ++
>>  drivers/iio/pulse/Makefile |   6 +
>>  drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 517 insertions(+)
>>  create mode 100644 drivers/iio/pulse/Kconfig
>>  create mode 100644 drivers/iio/pulse/Makefile
>>  create mode 100644 drivers/iio/pulse/tiecap.c
>>
>> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
>> new file mode 100644
>> index 0000000..9864d4b
>> --- /dev/null
>> +++ b/drivers/iio/pulse/Kconfig
>> @@ -0,0 +1,20 @@
>> +#
>> +# Pulse Capture Devices
>> +#
>> +# When adding new entries keep the list in alphabetical order
>> +
>> +menu "Pulse Capture Devices"
>> +
>> +config IIO_TIECAP
>> +	tristate "TI ECAP Pulse Capture"
>> +	depends on SOC_AM33XX
>> +	select IIO_BUFFER
>> +	select IIO_TRIGGERED_BUFFER
>> +	help
>> +	 If you say yes here you get support for the TI ECAP peripheral
>> +	 in pulse capture mode.
>> +
>> +	 This driver can also be built as a module.  If so, the module
>> +	 will be called tiecap
>> +
>> +endmenu
>> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
>> new file mode 100644
>> index 0000000..94d4b00
>> --- /dev/null
>> +++ b/drivers/iio/pulse/Makefile
>> @@ -0,0 +1,6 @@
>> +#
>> +# Makefile for IIO PWM Capture Devices
>> +#
>> +
>> +# When adding new entries keep the list in alphabetical order
>> +obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
>> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
>> new file mode 100644
>> index 0000000..fd96745
>> --- /dev/null
>> +++ b/drivers/iio/pulse/tiecap.c
>> @@ -0,0 +1,491 @@
>> +/*
>> + * ECAP IIO pulse capture driver
>> + *
>> + * Copyright (C) 2014 Linaro Limited
>> + * Author: Matt Porter <mporter@linaro.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +
>> +#include "../../pwm/pwm-tipwmss.h"
>> +
>> +/* ECAP regs and bits */
>> +#define CAP1			0x08
>> +#define CAP2			0x0c
>> +#define ECCTL1			0x28
>> +#define ECCTL1_RUN_FREE		BIT(15)
>> +#define ECCTL1_CAPLDEN		BIT(8)
>> +#define ECCTL1_CAP2POL		BIT(2)
>> +#define ECCTL1_CTRRST1		BIT(1)
>> +#define ECCTL1_CAP1POL		BIT(0)
>> +#define ECCTL2			0x2a
>> +#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
>> +#define ECCTL2_TSCTR_FREERUN	BIT(4)
>> +#define ECCTL2_REARM		BIT(3)
>> +#define ECCTL2_STOP_WRAP_2	BIT(1)
>> +#define ECEINT			0x2c
>> +#define ECFLG			0x2e
>> +#define ECCLR			0x30
>> +#define ECINT_CTRCMP		BIT(7)
>> +#define ECINT_CTRPRD		BIT(6)
>> +#define ECINT_CTROVF		BIT(5)
>> +#define ECINT_CEVT4		BIT(4)
>> +#define ECINT_CEVT3		BIT(3)
>> +#define ECINT_CEVT2		BIT(2)
>> +#define ECINT_CEVT1		BIT(1)
>> +#define ECINT_ALL		(ECINT_CTRCMP |	\
>> +				ECINT_CTRPRD |	\
>> +				ECINT_CTROVF |	\
>> +				ECINT_CEVT4 |	\
>> +				ECINT_CEVT3 |	\
>> +				ECINT_CEVT2 |	\
>> +				ECINT_CEVT1)
>> +
>> +/* ECAP driver flags */
>> +#define ECAP_POLARITY_HIGH	BIT(1)
>> +#define ECAP_ENABLED		BIT(0)
>> +
>> +struct ecap_context {
>> +	u32	cap1;
>> +	u32	cap2;
>> +	u16	ecctl1;
>> +	u16	ecctl2;
>> +	u16	eceint;
>> +};
>> +
>> +struct ecap_state {
>> +	unsigned long		flags;
>> +	unsigned int		clk_rate;
>> +	void __iomem		*regs;
>> +	u32			*buf;
>> +	struct ecap_context	ctx;
>> +};
>> +
>> +#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
>> +
>> +static const struct iio_chan_spec ecap_channels[] = {
>> +	{
>> +		.type		= IIO_PULSE,
>> +		.info_mask_separate =
>> +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
>> +		.scan_index	= 0,
>> +		.scan_type = {
>> +			.sign		= 'u',
>> +			.realbits	= 32,
>> +			.storagebits	= 32,
>> +			.endianness	= IIO_LE,
>> +		},
>> +	},
>> +	IIO_CHAN_SOFT_TIMESTAMP(1)
>> +};
>> +
>> +static ssize_t ecap_attr_show(struct device *dev,
>> +			      struct device_attribute *attr, char *buf)
>> +{
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	return sprintf(buf, "%d\n",
>> +		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
>> +}
>> +
>> +static ssize_t ecap_attr_store(struct device *dev,
>> +			       struct device_attribute *attr,
>> +			       const char *buf,
>> +			       size_t len)
>> +{
>> +	int ret;
>> +	bool val;
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	if (test_bit(ECAP_ENABLED, &state->flags))
>> +		return -EINVAL;
>> +
>> +	ret = strtobool(buf, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (val)
>> +		set_bit(ECAP_POLARITY_HIGH, &state->flags);
>> +	else
>> +		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
>> +
>> +	return len;
>> +}
>> +
>> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
>> +	ecap_attr_show, ecap_attr_store, 0);
>> +
>> +static struct attribute *ecap_attributes[] = {
>> +	&iio_dev_attr_pulse_polarity.dev_attr.attr,
>> +	NULL,
>> +};
>> +
>> +static struct attribute_group ecap_attribute_group = {
>> +	.attrs = ecap_attributes,
>> +};
>> +
>> +static int ecap_read_raw(struct iio_dev *idev,
>> +			struct iio_chan_spec const *ch, int *val,
>> +			int *val2, long mask)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		/*
>> +		 * Always return 0 as a pulse width sample
>> +		 * is only valid in a triggered condition
>> +		 */
>> +		*val = 0;
>> +		return IIO_VAL_INT;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		*val = 0;
>> +		*val2 = NSEC_PER_SEC / state->clk_rate;
>> +		return IIO_VAL_INT_PLUS_NANO;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static const struct iio_info ecap_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.attrs = &ecap_attribute_group,
>> +	.read_raw = &ecap_read_raw,
>> +};
>> +
>> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
>> +{
>> +	struct iio_poll_func *pf = private;
>> +	struct iio_dev *idev = pf->indio_dev;
>> +	struct ecap_state *state = iio_priv(idev);
>> +
>> +	/* Read pulse counter value */
>> +	*state->buf = readl(state->regs + CAP2);
>> +
>> +	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
>> +
>> +	iio_trigger_notify_done(idev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +};
>> +
>> +
>> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
>> +	.owner = THIS_MODULE,
>> +};
>> +
>> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
>> +{
>> +	struct iio_dev *idev = private;
>> +	struct ecap_state *state = iio_priv(idev);
>> +	u16 ints;
>> +
>> +	iio_trigger_poll(idev->trig, 0);
>> +
>> +	/* Clear CAP2 interrupt */
>> +	ints = readw(state->regs + ECFLG);
>> +	if (ints & ECINT_CEVT2)
>> +		writew(ECINT_CEVT2, state->regs + ECCLR);
>> +	else
>> +		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
>> +			 ints);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int ecap_buffer_predisable(struct iio_dev *idev)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +	int ret = 0;
>> +	u16 ecctl2;
>> +
>> +	/* Stop capture */
>> +	clear_bit(ECAP_ENABLED, &state->flags);
>> +	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
>> +	writew(ecctl2, state->regs + ECCTL2);
>> +
>> +	/* Disable and clear all interrupts */
>> +	writew(0, state->regs + ECEINT);
>> +	writew(ECINT_ALL, state->regs + ECCLR);
>> +
>> +	ret = iio_triggered_buffer_predisable(idev);
>> +
>> +	pm_runtime_put_sync(idev->dev.parent);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ecap_buffer_postenable(struct iio_dev *idev)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +	int ret = 0;
>> +	u16 ecctl1, ecctl2;
>> +
>> +	pm_runtime_get_sync(idev->dev.parent);
>> +
>> +	/* Configure pulse polarity */
>> +	ecctl1 = readw(state->regs + ECCTL1);
>> +	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
>> +		/* CAP1 rising, CAP2 falling */
>> +		ecctl1 |= ECCTL1_CAP2POL;
>> +		ecctl1 &= ~ECCTL1_CAP1POL;
>> +	} else {
>> +		/* CAP1 falling, CAP2 rising */
>> +		ecctl1 &= ~ECCTL1_CAP2POL;
>> +		ecctl1 |= ECCTL1_CAP1POL;
>> +	}
>> +	writew(ecctl1, state->regs + ECCTL1);
>> +
>> +	/* Enable CAP2 interrupt */
>> +	writew(ECINT_CEVT2, state->regs + ECEINT);
>> +
>> +	/* Enable capture */
>> +	ecctl2 = readw(state->regs + ECCTL2);
>> +	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
>> +	writew(ecctl2, state->regs + ECCTL2);
>> +	set_bit(ECAP_ENABLED, &state->flags);
>> +
>> +	ret = iio_triggered_buffer_postenable(idev);
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
>> +	.postenable = &ecap_buffer_postenable,
>> +	.predisable = &ecap_buffer_predisable,
>> +};
>> +
>> +static void ecap_init_hw(struct iio_dev *idev)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +
>> +	clear_bit(ECAP_ENABLED, &state->flags);
>> +	set_bit(ECAP_POLARITY_HIGH, &state->flags);
>> +
>> +	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
>> +	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
>> +	       state->regs + ECCTL1);
>> +
>> +	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
>> +	       state->regs + ECCTL2);
>> +}
>> +
>> +static const struct of_device_id ecap_of_ids[] = {
>> +	{ .compatible	= "ti,am33xx-ecap" },
>> +	{ /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
>> +
>> +static int ecap_probe(struct platform_device *pdev)
>> +{
>> +	int irq, ret;
>> +	struct iio_dev *idev;
>> +	struct ecap_state *state;
>> +	struct resource *r;
>> +	struct clk *clk;
>> +	struct iio_trigger *trig;
>> +	u16 status;
>> +
>> +	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
>> +	if (!idev)
>> +		return -ENOMEM;
>> +
>> +	state = iio_priv(idev);
>> +
>> +	clk = devm_clk_get(&pdev->dev, "fck");
>> +	if (IS_ERR(clk)) {
>> +		dev_err(&pdev->dev, "failed to get clock\n");
>> +		return PTR_ERR(clk);
>> +	}
>> +
>> +	state->clk_rate = clk_get_rate(clk);
>> +	if (!state->clk_rate) {
>> +		dev_err(&pdev->dev, "failed to get clock rate\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = dev_name(&pdev->dev);
>> +	idev->modes = INDIO_DIRECT_MODE;
>> +	idev->info = &ecap_info;
>> +	idev->channels = ecap_channels;
>> +	/* One h/w capture and one s/w timestamp channel per instance */
>> +	idev->num_channels = ARRAY_SIZE(ecap_channels);
>> +
>> +	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
>> +				      idev->name, idev->id);
>> +	if (!trig)
>> +		return -ENOMEM;
>> +	trig->dev.parent = idev->dev.parent;
>> +	iio_trigger_set_drvdata(trig, idev);
>> +	trig->ops = &iio_interrupt_trigger_ops;
>> +
>> +	ret = iio_trigger_register(trig);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "failed to register trigger\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = iio_triggered_buffer_setup(idev, NULL,
>> +					 &ecap_trigger_handler,
>> +					 &ecap_buffer_setup_ops);
>> +	if (ret)
>> +		return ret;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq < 0) {
>> +		dev_err(&pdev->dev, "no irq is specified\n");
>> +		return irq;
>> +	}
>> +	ret = devm_request_irq(&pdev->dev, irq,
>> +				&ecap_interrupt_handler,
>> +				0, dev_name(&pdev->dev), idev);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "unable to request irq\n");
>> +		goto uninit_buffer;
>> +	}
>> +
>> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	state->regs = devm_ioremap_resource(&pdev->dev, r);
>> +	if (IS_ERR(state->regs)) {
>> +		dev_err(&pdev->dev, "unable to remap registers\n");
>> +		ret = PTR_ERR(state->regs);
>> +		goto uninit_buffer;
>> +	};
>> +
>> +	ret = iio_device_register(idev);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "unable to register device\n");
>> +		goto uninit_buffer;
>> +	}
>> +
>> +	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
>> +	if (!state->buf) {
>> +		ret = -ENOMEM;
>> +		goto uninit_buffer;
>> +	}
>> +
>> +	pm_runtime_enable(&pdev->dev);
>> +	pm_runtime_get_sync(&pdev->dev);
>> +
>> +	status = pwmss_submodule_state_change(pdev->dev.parent,
>> +			PWMSS_ECAPCLK_EN);
>> +	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
>> +		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
>> +		ret = -EINVAL;
>> +		goto pwmss_clk_failure;
>> +	}
>> +
>> +	ecap_init_hw(idev);
>> +
>> +	pm_runtime_put_sync(&pdev->dev);
>> +
>> +	return 0;
>> +
>> +pwmss_clk_failure:
>> +	pm_runtime_put_sync(&pdev->dev);
>> +	pm_runtime_disable(&pdev->dev);
>> +	iio_device_unregister(idev);
>> +
>> +uninit_buffer:
>> +	iio_triggered_buffer_cleanup(idev);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ecap_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +
>> +	pm_runtime_get_sync(&pdev->dev);
>> +
>> +	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
>> +
>> +	pm_runtime_put_sync(&pdev->dev);
>> +	pm_runtime_disable(&pdev->dev);
>> +
>> +	iio_device_unregister(idev);
>> +	iio_triggered_buffer_cleanup(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int ecap_suspend(struct device *dev)
>> +{
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	pm_runtime_get_sync(dev);
>> +	state->ctx.cap1 = readl(state->regs + CAP1);
>> +	state->ctx.cap2 = readl(state->regs + CAP2);
>> +	state->ctx.eceint = readw(state->regs + ECEINT);
>> +	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
>> +	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
>> +	pm_runtime_put_sync(dev);
>> +
>> +	/* If capture was active, disable ECAP */
>> +	if (test_bit(ECAP_ENABLED, &state->flags))
>> +		pm_runtime_put_sync(dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ecap_resume(struct device *dev)
>> +{
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	/* If capture was active, enable ECAP */
>> +	if (test_bit(ECAP_ENABLED, &state->flags))
>> +		pm_runtime_get_sync(dev);
>> +
>> +	pm_runtime_get_sync(dev);
>> +	writel(state->ctx.cap1, state->regs + CAP1);
>> +	writel(state->ctx.cap2, state->regs + CAP2);
>> +	writew(state->ctx.eceint, state->regs + ECEINT);
>> +	writew(state->ctx.ecctl1, state->regs + ECCTL1);
>> +	writew(state->ctx.ecctl2, state->regs + ECCTL2);
>> +	pm_runtime_put_sync(dev);
>> +
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
>> +
>> +static struct platform_driver ecap_iio_driver = {
>> +	.driver = {
>> +		.name		= "ecap",
>> +		.owner		= THIS_MODULE,
>> +		.of_match_table = of_match_ptr(ecap_of_ids),
>> +		.pm		= &ecap_pm_ops,
>> +	},
>> +	.probe = ecap_probe,
>> +	.remove = ecap_remove,
>> +};
>> +
>> +module_platform_driver(ecap_iio_driver);
>> +
>> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
>> +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
>> +MODULE_LICENSE("GPL");
>>
> 
> --
> 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	[flat|nested] 42+ messages in thread

* Re: [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2016-04-03 10:41       ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2016-04-03 10:41 UTC (permalink / raw)
  To: Matt Porter, Grant Likely, Rob Herring, Benoît Cousson,
	Tony Lindgren, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Thierry Reding
  Cc: Linux IIO List, Linux Kernel Mailing List, Devicetree List,
	Linux PWM List, Linux OMAP List, Linux ARM Kernel List,
	Daniel Baluta, Lars-Peter Clausen, Peter Meerwald,
	Hartmut Knaack, Michael Welling

On 28/03/16 16:30, Jonathan Cameron wrote:
> On 05/02/14 19:01, Matt Porter wrote:
>> Adds support for capturing PWM signals using the TI ECAP peripheral.
>> This driver supports triggered buffer capture of pulses on multiple
>> ECAP instances. In addition, the driver supports configurable polarity
>> of the signal to be captured.
>>
>> Signed-off-by: Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Hi Matt,
> 
> Given I had a personal message asking about this a few weeks ago, I thought I'd
> ask... Any plans to resurrect this?
Guess that's a no :( 

If anyone wants a a driver to get kernel driver merging experience with this would
be a good one.  Hardware is readily available I think as the beaglebone black has one
of these exposed - there is no support for it in the TI tree either that I can see.

I would cc'd the beagleboard google group but can't actually figure out which is
the right one.  Michael, if you know could you reply, with such a group cc'd?
(or anyone else for that matter)

I'd kind of like to pick this one up but will be a while before I clear enough time
to do so (possibly another couple of years!)

Jonathan



>> ---
>>  drivers/iio/pulse/Kconfig  |  20 ++
>>  drivers/iio/pulse/Makefile |   6 +
>>  drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 517 insertions(+)
>>  create mode 100644 drivers/iio/pulse/Kconfig
>>  create mode 100644 drivers/iio/pulse/Makefile
>>  create mode 100644 drivers/iio/pulse/tiecap.c
>>
>> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
>> new file mode 100644
>> index 0000000..9864d4b
>> --- /dev/null
>> +++ b/drivers/iio/pulse/Kconfig
>> @@ -0,0 +1,20 @@
>> +#
>> +# Pulse Capture Devices
>> +#
>> +# When adding new entries keep the list in alphabetical order
>> +
>> +menu "Pulse Capture Devices"
>> +
>> +config IIO_TIECAP
>> +	tristate "TI ECAP Pulse Capture"
>> +	depends on SOC_AM33XX
>> +	select IIO_BUFFER
>> +	select IIO_TRIGGERED_BUFFER
>> +	help
>> +	 If you say yes here you get support for the TI ECAP peripheral
>> +	 in pulse capture mode.
>> +
>> +	 This driver can also be built as a module.  If so, the module
>> +	 will be called tiecap
>> +
>> +endmenu
>> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
>> new file mode 100644
>> index 0000000..94d4b00
>> --- /dev/null
>> +++ b/drivers/iio/pulse/Makefile
>> @@ -0,0 +1,6 @@
>> +#
>> +# Makefile for IIO PWM Capture Devices
>> +#
>> +
>> +# When adding new entries keep the list in alphabetical order
>> +obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
>> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
>> new file mode 100644
>> index 0000000..fd96745
>> --- /dev/null
>> +++ b/drivers/iio/pulse/tiecap.c
>> @@ -0,0 +1,491 @@
>> +/*
>> + * ECAP IIO pulse capture driver
>> + *
>> + * Copyright (C) 2014 Linaro Limited
>> + * Author: Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +
>> +#include "../../pwm/pwm-tipwmss.h"
>> +
>> +/* ECAP regs and bits */
>> +#define CAP1			0x08
>> +#define CAP2			0x0c
>> +#define ECCTL1			0x28
>> +#define ECCTL1_RUN_FREE		BIT(15)
>> +#define ECCTL1_CAPLDEN		BIT(8)
>> +#define ECCTL1_CAP2POL		BIT(2)
>> +#define ECCTL1_CTRRST1		BIT(1)
>> +#define ECCTL1_CAP1POL		BIT(0)
>> +#define ECCTL2			0x2a
>> +#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
>> +#define ECCTL2_TSCTR_FREERUN	BIT(4)
>> +#define ECCTL2_REARM		BIT(3)
>> +#define ECCTL2_STOP_WRAP_2	BIT(1)
>> +#define ECEINT			0x2c
>> +#define ECFLG			0x2e
>> +#define ECCLR			0x30
>> +#define ECINT_CTRCMP		BIT(7)
>> +#define ECINT_CTRPRD		BIT(6)
>> +#define ECINT_CTROVF		BIT(5)
>> +#define ECINT_CEVT4		BIT(4)
>> +#define ECINT_CEVT3		BIT(3)
>> +#define ECINT_CEVT2		BIT(2)
>> +#define ECINT_CEVT1		BIT(1)
>> +#define ECINT_ALL		(ECINT_CTRCMP |	\
>> +				ECINT_CTRPRD |	\
>> +				ECINT_CTROVF |	\
>> +				ECINT_CEVT4 |	\
>> +				ECINT_CEVT3 |	\
>> +				ECINT_CEVT2 |	\
>> +				ECINT_CEVT1)
>> +
>> +/* ECAP driver flags */
>> +#define ECAP_POLARITY_HIGH	BIT(1)
>> +#define ECAP_ENABLED		BIT(0)
>> +
>> +struct ecap_context {
>> +	u32	cap1;
>> +	u32	cap2;
>> +	u16	ecctl1;
>> +	u16	ecctl2;
>> +	u16	eceint;
>> +};
>> +
>> +struct ecap_state {
>> +	unsigned long		flags;
>> +	unsigned int		clk_rate;
>> +	void __iomem		*regs;
>> +	u32			*buf;
>> +	struct ecap_context	ctx;
>> +};
>> +
>> +#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
>> +
>> +static const struct iio_chan_spec ecap_channels[] = {
>> +	{
>> +		.type		= IIO_PULSE,
>> +		.info_mask_separate =
>> +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
>> +		.scan_index	= 0,
>> +		.scan_type = {
>> +			.sign		= 'u',
>> +			.realbits	= 32,
>> +			.storagebits	= 32,
>> +			.endianness	= IIO_LE,
>> +		},
>> +	},
>> +	IIO_CHAN_SOFT_TIMESTAMP(1)
>> +};
>> +
>> +static ssize_t ecap_attr_show(struct device *dev,
>> +			      struct device_attribute *attr, char *buf)
>> +{
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	return sprintf(buf, "%d\n",
>> +		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
>> +}
>> +
>> +static ssize_t ecap_attr_store(struct device *dev,
>> +			       struct device_attribute *attr,
>> +			       const char *buf,
>> +			       size_t len)
>> +{
>> +	int ret;
>> +	bool val;
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	if (test_bit(ECAP_ENABLED, &state->flags))
>> +		return -EINVAL;
>> +
>> +	ret = strtobool(buf, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (val)
>> +		set_bit(ECAP_POLARITY_HIGH, &state->flags);
>> +	else
>> +		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
>> +
>> +	return len;
>> +}
>> +
>> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
>> +	ecap_attr_show, ecap_attr_store, 0);
>> +
>> +static struct attribute *ecap_attributes[] = {
>> +	&iio_dev_attr_pulse_polarity.dev_attr.attr,
>> +	NULL,
>> +};
>> +
>> +static struct attribute_group ecap_attribute_group = {
>> +	.attrs = ecap_attributes,
>> +};
>> +
>> +static int ecap_read_raw(struct iio_dev *idev,
>> +			struct iio_chan_spec const *ch, int *val,
>> +			int *val2, long mask)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		/*
>> +		 * Always return 0 as a pulse width sample
>> +		 * is only valid in a triggered condition
>> +		 */
>> +		*val = 0;
>> +		return IIO_VAL_INT;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		*val = 0;
>> +		*val2 = NSEC_PER_SEC / state->clk_rate;
>> +		return IIO_VAL_INT_PLUS_NANO;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static const struct iio_info ecap_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.attrs = &ecap_attribute_group,
>> +	.read_raw = &ecap_read_raw,
>> +};
>> +
>> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
>> +{
>> +	struct iio_poll_func *pf = private;
>> +	struct iio_dev *idev = pf->indio_dev;
>> +	struct ecap_state *state = iio_priv(idev);
>> +
>> +	/* Read pulse counter value */
>> +	*state->buf = readl(state->regs + CAP2);
>> +
>> +	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
>> +
>> +	iio_trigger_notify_done(idev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +};
>> +
>> +
>> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
>> +	.owner = THIS_MODULE,
>> +};
>> +
>> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
>> +{
>> +	struct iio_dev *idev = private;
>> +	struct ecap_state *state = iio_priv(idev);
>> +	u16 ints;
>> +
>> +	iio_trigger_poll(idev->trig, 0);
>> +
>> +	/* Clear CAP2 interrupt */
>> +	ints = readw(state->regs + ECFLG);
>> +	if (ints & ECINT_CEVT2)
>> +		writew(ECINT_CEVT2, state->regs + ECCLR);
>> +	else
>> +		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
>> +			 ints);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int ecap_buffer_predisable(struct iio_dev *idev)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +	int ret = 0;
>> +	u16 ecctl2;
>> +
>> +	/* Stop capture */
>> +	clear_bit(ECAP_ENABLED, &state->flags);
>> +	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
>> +	writew(ecctl2, state->regs + ECCTL2);
>> +
>> +	/* Disable and clear all interrupts */
>> +	writew(0, state->regs + ECEINT);
>> +	writew(ECINT_ALL, state->regs + ECCLR);
>> +
>> +	ret = iio_triggered_buffer_predisable(idev);
>> +
>> +	pm_runtime_put_sync(idev->dev.parent);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ecap_buffer_postenable(struct iio_dev *idev)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +	int ret = 0;
>> +	u16 ecctl1, ecctl2;
>> +
>> +	pm_runtime_get_sync(idev->dev.parent);
>> +
>> +	/* Configure pulse polarity */
>> +	ecctl1 = readw(state->regs + ECCTL1);
>> +	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
>> +		/* CAP1 rising, CAP2 falling */
>> +		ecctl1 |= ECCTL1_CAP2POL;
>> +		ecctl1 &= ~ECCTL1_CAP1POL;
>> +	} else {
>> +		/* CAP1 falling, CAP2 rising */
>> +		ecctl1 &= ~ECCTL1_CAP2POL;
>> +		ecctl1 |= ECCTL1_CAP1POL;
>> +	}
>> +	writew(ecctl1, state->regs + ECCTL1);
>> +
>> +	/* Enable CAP2 interrupt */
>> +	writew(ECINT_CEVT2, state->regs + ECEINT);
>> +
>> +	/* Enable capture */
>> +	ecctl2 = readw(state->regs + ECCTL2);
>> +	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
>> +	writew(ecctl2, state->regs + ECCTL2);
>> +	set_bit(ECAP_ENABLED, &state->flags);
>> +
>> +	ret = iio_triggered_buffer_postenable(idev);
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
>> +	.postenable = &ecap_buffer_postenable,
>> +	.predisable = &ecap_buffer_predisable,
>> +};
>> +
>> +static void ecap_init_hw(struct iio_dev *idev)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +
>> +	clear_bit(ECAP_ENABLED, &state->flags);
>> +	set_bit(ECAP_POLARITY_HIGH, &state->flags);
>> +
>> +	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
>> +	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
>> +	       state->regs + ECCTL1);
>> +
>> +	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
>> +	       state->regs + ECCTL2);
>> +}
>> +
>> +static const struct of_device_id ecap_of_ids[] = {
>> +	{ .compatible	= "ti,am33xx-ecap" },
>> +	{ /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
>> +
>> +static int ecap_probe(struct platform_device *pdev)
>> +{
>> +	int irq, ret;
>> +	struct iio_dev *idev;
>> +	struct ecap_state *state;
>> +	struct resource *r;
>> +	struct clk *clk;
>> +	struct iio_trigger *trig;
>> +	u16 status;
>> +
>> +	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
>> +	if (!idev)
>> +		return -ENOMEM;
>> +
>> +	state = iio_priv(idev);
>> +
>> +	clk = devm_clk_get(&pdev->dev, "fck");
>> +	if (IS_ERR(clk)) {
>> +		dev_err(&pdev->dev, "failed to get clock\n");
>> +		return PTR_ERR(clk);
>> +	}
>> +
>> +	state->clk_rate = clk_get_rate(clk);
>> +	if (!state->clk_rate) {
>> +		dev_err(&pdev->dev, "failed to get clock rate\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = dev_name(&pdev->dev);
>> +	idev->modes = INDIO_DIRECT_MODE;
>> +	idev->info = &ecap_info;
>> +	idev->channels = ecap_channels;
>> +	/* One h/w capture and one s/w timestamp channel per instance */
>> +	idev->num_channels = ARRAY_SIZE(ecap_channels);
>> +
>> +	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
>> +				      idev->name, idev->id);
>> +	if (!trig)
>> +		return -ENOMEM;
>> +	trig->dev.parent = idev->dev.parent;
>> +	iio_trigger_set_drvdata(trig, idev);
>> +	trig->ops = &iio_interrupt_trigger_ops;
>> +
>> +	ret = iio_trigger_register(trig);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "failed to register trigger\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = iio_triggered_buffer_setup(idev, NULL,
>> +					 &ecap_trigger_handler,
>> +					 &ecap_buffer_setup_ops);
>> +	if (ret)
>> +		return ret;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq < 0) {
>> +		dev_err(&pdev->dev, "no irq is specified\n");
>> +		return irq;
>> +	}
>> +	ret = devm_request_irq(&pdev->dev, irq,
>> +				&ecap_interrupt_handler,
>> +				0, dev_name(&pdev->dev), idev);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "unable to request irq\n");
>> +		goto uninit_buffer;
>> +	}
>> +
>> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	state->regs = devm_ioremap_resource(&pdev->dev, r);
>> +	if (IS_ERR(state->regs)) {
>> +		dev_err(&pdev->dev, "unable to remap registers\n");
>> +		ret = PTR_ERR(state->regs);
>> +		goto uninit_buffer;
>> +	};
>> +
>> +	ret = iio_device_register(idev);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "unable to register device\n");
>> +		goto uninit_buffer;
>> +	}
>> +
>> +	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
>> +	if (!state->buf) {
>> +		ret = -ENOMEM;
>> +		goto uninit_buffer;
>> +	}
>> +
>> +	pm_runtime_enable(&pdev->dev);
>> +	pm_runtime_get_sync(&pdev->dev);
>> +
>> +	status = pwmss_submodule_state_change(pdev->dev.parent,
>> +			PWMSS_ECAPCLK_EN);
>> +	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
>> +		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
>> +		ret = -EINVAL;
>> +		goto pwmss_clk_failure;
>> +	}
>> +
>> +	ecap_init_hw(idev);
>> +
>> +	pm_runtime_put_sync(&pdev->dev);
>> +
>> +	return 0;
>> +
>> +pwmss_clk_failure:
>> +	pm_runtime_put_sync(&pdev->dev);
>> +	pm_runtime_disable(&pdev->dev);
>> +	iio_device_unregister(idev);
>> +
>> +uninit_buffer:
>> +	iio_triggered_buffer_cleanup(idev);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ecap_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +
>> +	pm_runtime_get_sync(&pdev->dev);
>> +
>> +	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
>> +
>> +	pm_runtime_put_sync(&pdev->dev);
>> +	pm_runtime_disable(&pdev->dev);
>> +
>> +	iio_device_unregister(idev);
>> +	iio_triggered_buffer_cleanup(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int ecap_suspend(struct device *dev)
>> +{
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	pm_runtime_get_sync(dev);
>> +	state->ctx.cap1 = readl(state->regs + CAP1);
>> +	state->ctx.cap2 = readl(state->regs + CAP2);
>> +	state->ctx.eceint = readw(state->regs + ECEINT);
>> +	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
>> +	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
>> +	pm_runtime_put_sync(dev);
>> +
>> +	/* If capture was active, disable ECAP */
>> +	if (test_bit(ECAP_ENABLED, &state->flags))
>> +		pm_runtime_put_sync(dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ecap_resume(struct device *dev)
>> +{
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	/* If capture was active, enable ECAP */
>> +	if (test_bit(ECAP_ENABLED, &state->flags))
>> +		pm_runtime_get_sync(dev);
>> +
>> +	pm_runtime_get_sync(dev);
>> +	writel(state->ctx.cap1, state->regs + CAP1);
>> +	writel(state->ctx.cap2, state->regs + CAP2);
>> +	writew(state->ctx.eceint, state->regs + ECEINT);
>> +	writew(state->ctx.ecctl1, state->regs + ECCTL1);
>> +	writew(state->ctx.ecctl2, state->regs + ECCTL2);
>> +	pm_runtime_put_sync(dev);
>> +
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
>> +
>> +static struct platform_driver ecap_iio_driver = {
>> +	.driver = {
>> +		.name		= "ecap",
>> +		.owner		= THIS_MODULE,
>> +		.of_match_table = of_match_ptr(ecap_of_ids),
>> +		.pm		= &ecap_pm_ops,
>> +	},
>> +	.probe = ecap_probe,
>> +	.remove = ecap_remove,
>> +};
>> +
>> +module_platform_driver(ecap_iio_driver);
>> +
>> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
>> +MODULE_AUTHOR("Matt Porter <mporter-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
>> +MODULE_LICENSE("GPL");
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v3 2/6] iio: pulse: add TI ECAP driver
@ 2016-04-03 10:41       ` Jonathan Cameron
  0 siblings, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2016-04-03 10:41 UTC (permalink / raw)
  To: linux-arm-kernel

On 28/03/16 16:30, Jonathan Cameron wrote:
> On 05/02/14 19:01, Matt Porter wrote:
>> Adds support for capturing PWM signals using the TI ECAP peripheral.
>> This driver supports triggered buffer capture of pulses on multiple
>> ECAP instances. In addition, the driver supports configurable polarity
>> of the signal to be captured.
>>
>> Signed-off-by: Matt Porter <mporter@linaro.org>
> Hi Matt,
> 
> Given I had a personal message asking about this a few weeks ago, I thought I'd
> ask... Any plans to resurrect this?
Guess that's a no :( 

If anyone wants a a driver to get kernel driver merging experience with this would
be a good one.  Hardware is readily available I think as the beaglebone black has one
of these exposed - there is no support for it in the TI tree either that I can see.

I would cc'd the beagleboard google group but can't actually figure out which is
the right one.  Michael, if you know could you reply, with such a group cc'd?
(or anyone else for that matter)

I'd kind of like to pick this one up but will be a while before I clear enough time
to do so (possibly another couple of years!)

Jonathan



>> ---
>>  drivers/iio/pulse/Kconfig  |  20 ++
>>  drivers/iio/pulse/Makefile |   6 +
>>  drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 517 insertions(+)
>>  create mode 100644 drivers/iio/pulse/Kconfig
>>  create mode 100644 drivers/iio/pulse/Makefile
>>  create mode 100644 drivers/iio/pulse/tiecap.c
>>
>> diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
>> new file mode 100644
>> index 0000000..9864d4b
>> --- /dev/null
>> +++ b/drivers/iio/pulse/Kconfig
>> @@ -0,0 +1,20 @@
>> +#
>> +# Pulse Capture Devices
>> +#
>> +# When adding new entries keep the list in alphabetical order
>> +
>> +menu "Pulse Capture Devices"
>> +
>> +config IIO_TIECAP
>> +	tristate "TI ECAP Pulse Capture"
>> +	depends on SOC_AM33XX
>> +	select IIO_BUFFER
>> +	select IIO_TRIGGERED_BUFFER
>> +	help
>> +	 If you say yes here you get support for the TI ECAP peripheral
>> +	 in pulse capture mode.
>> +
>> +	 This driver can also be built as a module.  If so, the module
>> +	 will be called tiecap
>> +
>> +endmenu
>> diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
>> new file mode 100644
>> index 0000000..94d4b00
>> --- /dev/null
>> +++ b/drivers/iio/pulse/Makefile
>> @@ -0,0 +1,6 @@
>> +#
>> +# Makefile for IIO PWM Capture Devices
>> +#
>> +
>> +# When adding new entries keep the list in alphabetical order
>> +obj-$(CONFIG_IIO_TIECAP)	+= tiecap.o
>> diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
>> new file mode 100644
>> index 0000000..fd96745
>> --- /dev/null
>> +++ b/drivers/iio/pulse/tiecap.c
>> @@ -0,0 +1,491 @@
>> +/*
>> + * ECAP IIO pulse capture driver
>> + *
>> + * Copyright (C) 2014 Linaro Limited
>> + * Author: Matt Porter <mporter@linaro.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +
>> +#include "../../pwm/pwm-tipwmss.h"
>> +
>> +/* ECAP regs and bits */
>> +#define CAP1			0x08
>> +#define CAP2			0x0c
>> +#define ECCTL1			0x28
>> +#define ECCTL1_RUN_FREE		BIT(15)
>> +#define ECCTL1_CAPLDEN		BIT(8)
>> +#define ECCTL1_CAP2POL		BIT(2)
>> +#define ECCTL1_CTRRST1		BIT(1)
>> +#define ECCTL1_CAP1POL		BIT(0)
>> +#define ECCTL2			0x2a
>> +#define ECCTL2_SYNCO_SEL_DIS	BIT(7)
>> +#define ECCTL2_TSCTR_FREERUN	BIT(4)
>> +#define ECCTL2_REARM		BIT(3)
>> +#define ECCTL2_STOP_WRAP_2	BIT(1)
>> +#define ECEINT			0x2c
>> +#define ECFLG			0x2e
>> +#define ECCLR			0x30
>> +#define ECINT_CTRCMP		BIT(7)
>> +#define ECINT_CTRPRD		BIT(6)
>> +#define ECINT_CTROVF		BIT(5)
>> +#define ECINT_CEVT4		BIT(4)
>> +#define ECINT_CEVT3		BIT(3)
>> +#define ECINT_CEVT2		BIT(2)
>> +#define ECINT_CEVT1		BIT(1)
>> +#define ECINT_ALL		(ECINT_CTRCMP |	\
>> +				ECINT_CTRPRD |	\
>> +				ECINT_CTROVF |	\
>> +				ECINT_CEVT4 |	\
>> +				ECINT_CEVT3 |	\
>> +				ECINT_CEVT2 |	\
>> +				ECINT_CEVT1)
>> +
>> +/* ECAP driver flags */
>> +#define ECAP_POLARITY_HIGH	BIT(1)
>> +#define ECAP_ENABLED		BIT(0)
>> +
>> +struct ecap_context {
>> +	u32	cap1;
>> +	u32	cap2;
>> +	u16	ecctl1;
>> +	u16	ecctl2;
>> +	u16	eceint;
>> +};
>> +
>> +struct ecap_state {
>> +	unsigned long		flags;
>> +	unsigned int		clk_rate;
>> +	void __iomem		*regs;
>> +	u32			*buf;
>> +	struct ecap_context	ctx;
>> +};
>> +
>> +#define dev_to_ecap_state(d)	iio_priv(dev_to_iio_dev(d))
>> +
>> +static const struct iio_chan_spec ecap_channels[] = {
>> +	{
>> +		.type		= IIO_PULSE,
>> +		.info_mask_separate =
>> +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
>> +		.scan_index	= 0,
>> +		.scan_type = {
>> +			.sign		= 'u',
>> +			.realbits	= 32,
>> +			.storagebits	= 32,
>> +			.endianness	= IIO_LE,
>> +		},
>> +	},
>> +	IIO_CHAN_SOFT_TIMESTAMP(1)
>> +};
>> +
>> +static ssize_t ecap_attr_show(struct device *dev,
>> +			      struct device_attribute *attr, char *buf)
>> +{
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	return sprintf(buf, "%d\n",
>> +		       test_bit(ECAP_POLARITY_HIGH, &state->flags));
>> +}
>> +
>> +static ssize_t ecap_attr_store(struct device *dev,
>> +			       struct device_attribute *attr,
>> +			       const char *buf,
>> +			       size_t len)
>> +{
>> +	int ret;
>> +	bool val;
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	if (test_bit(ECAP_ENABLED, &state->flags))
>> +		return -EINVAL;
>> +
>> +	ret = strtobool(buf, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (val)
>> +		set_bit(ECAP_POLARITY_HIGH, &state->flags);
>> +	else
>> +		clear_bit(ECAP_POLARITY_HIGH, &state->flags);
>> +
>> +	return len;
>> +}
>> +
>> +static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
>> +	ecap_attr_show, ecap_attr_store, 0);
>> +
>> +static struct attribute *ecap_attributes[] = {
>> +	&iio_dev_attr_pulse_polarity.dev_attr.attr,
>> +	NULL,
>> +};
>> +
>> +static struct attribute_group ecap_attribute_group = {
>> +	.attrs = ecap_attributes,
>> +};
>> +
>> +static int ecap_read_raw(struct iio_dev *idev,
>> +			struct iio_chan_spec const *ch, int *val,
>> +			int *val2, long mask)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		/*
>> +		 * Always return 0 as a pulse width sample
>> +		 * is only valid in a triggered condition
>> +		 */
>> +		*val = 0;
>> +		return IIO_VAL_INT;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		*val = 0;
>> +		*val2 = NSEC_PER_SEC / state->clk_rate;
>> +		return IIO_VAL_INT_PLUS_NANO;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static const struct iio_info ecap_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.attrs = &ecap_attribute_group,
>> +	.read_raw = &ecap_read_raw,
>> +};
>> +
>> +static irqreturn_t ecap_trigger_handler(int irq, void *private)
>> +{
>> +	struct iio_poll_func *pf = private;
>> +	struct iio_dev *idev = pf->indio_dev;
>> +	struct ecap_state *state = iio_priv(idev);
>> +
>> +	/* Read pulse counter value */
>> +	*state->buf = readl(state->regs + CAP2);
>> +
>> +	iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
>> +
>> +	iio_trigger_notify_done(idev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +};
>> +
>> +
>> +static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
>> +	.owner = THIS_MODULE,
>> +};
>> +
>> +static irqreturn_t ecap_interrupt_handler(int irq, void *private)
>> +{
>> +	struct iio_dev *idev = private;
>> +	struct ecap_state *state = iio_priv(idev);
>> +	u16 ints;
>> +
>> +	iio_trigger_poll(idev->trig, 0);
>> +
>> +	/* Clear CAP2 interrupt */
>> +	ints = readw(state->regs + ECFLG);
>> +	if (ints & ECINT_CEVT2)
>> +		writew(ECINT_CEVT2, state->regs + ECCLR);
>> +	else
>> +		dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
>> +			 ints);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int ecap_buffer_predisable(struct iio_dev *idev)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +	int ret = 0;
>> +	u16 ecctl2;
>> +
>> +	/* Stop capture */
>> +	clear_bit(ECAP_ENABLED, &state->flags);
>> +	ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
>> +	writew(ecctl2, state->regs + ECCTL2);
>> +
>> +	/* Disable and clear all interrupts */
>> +	writew(0, state->regs + ECEINT);
>> +	writew(ECINT_ALL, state->regs + ECCLR);
>> +
>> +	ret = iio_triggered_buffer_predisable(idev);
>> +
>> +	pm_runtime_put_sync(idev->dev.parent);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ecap_buffer_postenable(struct iio_dev *idev)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +	int ret = 0;
>> +	u16 ecctl1, ecctl2;
>> +
>> +	pm_runtime_get_sync(idev->dev.parent);
>> +
>> +	/* Configure pulse polarity */
>> +	ecctl1 = readw(state->regs + ECCTL1);
>> +	if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
>> +		/* CAP1 rising, CAP2 falling */
>> +		ecctl1 |= ECCTL1_CAP2POL;
>> +		ecctl1 &= ~ECCTL1_CAP1POL;
>> +	} else {
>> +		/* CAP1 falling, CAP2 rising */
>> +		ecctl1 &= ~ECCTL1_CAP2POL;
>> +		ecctl1 |= ECCTL1_CAP1POL;
>> +	}
>> +	writew(ecctl1, state->regs + ECCTL1);
>> +
>> +	/* Enable CAP2 interrupt */
>> +	writew(ECINT_CEVT2, state->regs + ECEINT);
>> +
>> +	/* Enable capture */
>> +	ecctl2 = readw(state->regs + ECCTL2);
>> +	ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
>> +	writew(ecctl2, state->regs + ECCTL2);
>> +	set_bit(ECAP_ENABLED, &state->flags);
>> +
>> +	ret = iio_triggered_buffer_postenable(idev);
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
>> +	.postenable = &ecap_buffer_postenable,
>> +	.predisable = &ecap_buffer_predisable,
>> +};
>> +
>> +static void ecap_init_hw(struct iio_dev *idev)
>> +{
>> +	struct ecap_state *state = iio_priv(idev);
>> +
>> +	clear_bit(ECAP_ENABLED, &state->flags);
>> +	set_bit(ECAP_POLARITY_HIGH, &state->flags);
>> +
>> +	writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
>> +	       ECCTL1_CAP2POL | ECCTL1_CTRRST1,
>> +	       state->regs + ECCTL1);
>> +
>> +	writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
>> +	       state->regs + ECCTL2);
>> +}
>> +
>> +static const struct of_device_id ecap_of_ids[] = {
>> +	{ .compatible	= "ti,am33xx-ecap" },
>> +	{ /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, ecap_of_ids);
>> +
>> +static int ecap_probe(struct platform_device *pdev)
>> +{
>> +	int irq, ret;
>> +	struct iio_dev *idev;
>> +	struct ecap_state *state;
>> +	struct resource *r;
>> +	struct clk *clk;
>> +	struct iio_trigger *trig;
>> +	u16 status;
>> +
>> +	idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
>> +	if (!idev)
>> +		return -ENOMEM;
>> +
>> +	state = iio_priv(idev);
>> +
>> +	clk = devm_clk_get(&pdev->dev, "fck");
>> +	if (IS_ERR(clk)) {
>> +		dev_err(&pdev->dev, "failed to get clock\n");
>> +		return PTR_ERR(clk);
>> +	}
>> +
>> +	state->clk_rate = clk_get_rate(clk);
>> +	if (!state->clk_rate) {
>> +		dev_err(&pdev->dev, "failed to get clock rate\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = dev_name(&pdev->dev);
>> +	idev->modes = INDIO_DIRECT_MODE;
>> +	idev->info = &ecap_info;
>> +	idev->channels = ecap_channels;
>> +	/* One h/w capture and one s/w timestamp channel per instance */
>> +	idev->num_channels = ARRAY_SIZE(ecap_channels);
>> +
>> +	trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
>> +				      idev->name, idev->id);
>> +	if (!trig)
>> +		return -ENOMEM;
>> +	trig->dev.parent = idev->dev.parent;
>> +	iio_trigger_set_drvdata(trig, idev);
>> +	trig->ops = &iio_interrupt_trigger_ops;
>> +
>> +	ret = iio_trigger_register(trig);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "failed to register trigger\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = iio_triggered_buffer_setup(idev, NULL,
>> +					 &ecap_trigger_handler,
>> +					 &ecap_buffer_setup_ops);
>> +	if (ret)
>> +		return ret;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq < 0) {
>> +		dev_err(&pdev->dev, "no irq is specified\n");
>> +		return irq;
>> +	}
>> +	ret = devm_request_irq(&pdev->dev, irq,
>> +				&ecap_interrupt_handler,
>> +				0, dev_name(&pdev->dev), idev);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "unable to request irq\n");
>> +		goto uninit_buffer;
>> +	}
>> +
>> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	state->regs = devm_ioremap_resource(&pdev->dev, r);
>> +	if (IS_ERR(state->regs)) {
>> +		dev_err(&pdev->dev, "unable to remap registers\n");
>> +		ret = PTR_ERR(state->regs);
>> +		goto uninit_buffer;
>> +	};
>> +
>> +	ret = iio_device_register(idev);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "unable to register device\n");
>> +		goto uninit_buffer;
>> +	}
>> +
>> +	state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
>> +	if (!state->buf) {
>> +		ret = -ENOMEM;
>> +		goto uninit_buffer;
>> +	}
>> +
>> +	pm_runtime_enable(&pdev->dev);
>> +	pm_runtime_get_sync(&pdev->dev);
>> +
>> +	status = pwmss_submodule_state_change(pdev->dev.parent,
>> +			PWMSS_ECAPCLK_EN);
>> +	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
>> +		dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
>> +		ret = -EINVAL;
>> +		goto pwmss_clk_failure;
>> +	}
>> +
>> +	ecap_init_hw(idev);
>> +
>> +	pm_runtime_put_sync(&pdev->dev);
>> +
>> +	return 0;
>> +
>> +pwmss_clk_failure:
>> +	pm_runtime_put_sync(&pdev->dev);
>> +	pm_runtime_disable(&pdev->dev);
>> +	iio_device_unregister(idev);
>> +
>> +uninit_buffer:
>> +	iio_triggered_buffer_cleanup(idev);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ecap_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +
>> +	pm_runtime_get_sync(&pdev->dev);
>> +
>> +	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
>> +
>> +	pm_runtime_put_sync(&pdev->dev);
>> +	pm_runtime_disable(&pdev->dev);
>> +
>> +	iio_device_unregister(idev);
>> +	iio_triggered_buffer_cleanup(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int ecap_suspend(struct device *dev)
>> +{
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	pm_runtime_get_sync(dev);
>> +	state->ctx.cap1 = readl(state->regs + CAP1);
>> +	state->ctx.cap2 = readl(state->regs + CAP2);
>> +	state->ctx.eceint = readw(state->regs + ECEINT);
>> +	state->ctx.ecctl1 = readw(state->regs + ECCTL1);
>> +	state->ctx.ecctl2 = readw(state->regs + ECCTL2);
>> +	pm_runtime_put_sync(dev);
>> +
>> +	/* If capture was active, disable ECAP */
>> +	if (test_bit(ECAP_ENABLED, &state->flags))
>> +		pm_runtime_put_sync(dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ecap_resume(struct device *dev)
>> +{
>> +	struct ecap_state *state = dev_to_ecap_state(dev);
>> +
>> +	/* If capture was active, enable ECAP */
>> +	if (test_bit(ECAP_ENABLED, &state->flags))
>> +		pm_runtime_get_sync(dev);
>> +
>> +	pm_runtime_get_sync(dev);
>> +	writel(state->ctx.cap1, state->regs + CAP1);
>> +	writel(state->ctx.cap2, state->regs + CAP2);
>> +	writew(state->ctx.eceint, state->regs + ECEINT);
>> +	writew(state->ctx.ecctl1, state->regs + ECCTL1);
>> +	writew(state->ctx.ecctl2, state->regs + ECCTL2);
>> +	pm_runtime_put_sync(dev);
>> +
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
>> +
>> +static struct platform_driver ecap_iio_driver = {
>> +	.driver = {
>> +		.name		= "ecap",
>> +		.owner		= THIS_MODULE,
>> +		.of_match_table = of_match_ptr(ecap_of_ids),
>> +		.pm		= &ecap_pm_ops,
>> +	},
>> +	.probe = ecap_probe,
>> +	.remove = ecap_remove,
>> +};
>> +
>> +module_platform_driver(ecap_iio_driver);
>> +
>> +MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
>> +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
>> +MODULE_LICENSE("GPL");
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2016-04-03 10:41 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-05 19:01 [PATCH v3 0/6] IIO pulse capture support for TI ECAP Matt Porter
2014-02-05 19:01 ` Matt Porter
2014-02-05 19:01 ` [PATCH v3 1/6] iio: add support for pulse width capture devices Matt Porter
2014-02-05 19:01   ` Matt Porter
2014-02-05 19:01   ` Matt Porter
2014-02-05 19:01 ` [PATCH v3 2/6] iio: pulse: add TI ECAP driver Matt Porter
2014-02-05 19:01   ` Matt Porter
2014-02-15 12:19   ` Jonathan Cameron
2014-02-15 12:19     ` Jonathan Cameron
2014-02-15 12:19     ` Jonathan Cameron
2014-03-22  3:33   ` Matt Ranostay
2014-03-22  3:35   ` Matt Ranostay
2014-03-22  3:35     ` Matt Ranostay
2016-03-28 15:30   ` Jonathan Cameron
2016-03-28 15:30     ` Jonathan Cameron
2016-03-28 15:30     ` Jonathan Cameron
2016-04-03 10:41     ` Jonathan Cameron
2016-04-03 10:41       ` Jonathan Cameron
2016-04-03 10:41       ` Jonathan Cameron
2014-02-05 19:01 ` [PATCH v3 3/6] iio: enable selection and build of pulse drivers Matt Porter
2014-02-05 19:01   ` Matt Porter
2014-02-15 11:41   ` Jonathan Cameron
2014-02-15 11:41     ` Jonathan Cameron
2014-02-15 11:41     ` Jonathan Cameron
2014-02-05 19:01 ` [PATCH v3 4/6] iio: Add ABI docs for pulse capture devices Matt Porter
2014-02-05 19:01   ` Matt Porter
2014-02-15 11:40   ` Jonathan Cameron
2014-02-15 11:40     ` Jonathan Cameron
2014-02-05 19:01 ` [PATCH v3 5/6] pwm: enable TI PWMSS if the IIO tiecap driver is selected Matt Porter
2014-02-05 19:01   ` Matt Porter
2014-02-26 14:15   ` Thierry Reding
2014-02-26 14:15     ` Thierry Reding
2014-02-26 14:15     ` Thierry Reding
2014-02-05 19:01 ` [PATCH v3 6/6] ARM: dts: AM33XX: Add ecap interrupt properties Matt Porter
2014-02-05 19:01   ` Matt Porter
2014-02-15 11:43   ` Jonathan Cameron
2014-02-15 11:43     ` Jonathan Cameron
2014-02-05 20:23 ` [PATCH v3 0/6] IIO pulse capture support for TI ECAP Sergei Shtylyov
2014-02-05 20:23   ` Sergei Shtylyov
2014-02-15 11:48   ` Jonathan Cameron
2014-02-15 11:48     ` Jonathan Cameron
2014-02-15 11:48     ` Jonathan Cameron

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.