All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Lechner <david@lechnology.com>
To: linux-iio@vger.kernel.org
Cc: David Lechner <david@lechnology.com>,
	William Breathitt Gray <vilhelm.gray@gmail.com>,
	Robert Nelson <robertcnelson@gmail.com>,
	linux-kernel@vger.kernel.org
Subject: [PATCH 3/8] counter/ti-eqep: add support for unit timer
Date: Sat, 16 Oct 2021 20:33:38 -0500	[thread overview]
Message-ID: <20211017013343.3385923-4-david@lechnology.com> (raw)
In-Reply-To: <20211017013343.3385923-1-david@lechnology.com>

This adds support to the TI eQEP counter driver for the Unit Timer.
The Unit Timer is a device-level extension that provides a timer to be
used for speed calculations. The sysfs interface for the Unit Timer is
new and will be documented in a later commit. It contains a R/W time
attribute for the current time, a R/W period attribute for the timeout
period and a R/W enable attribute to start/stop the timer. It also
implements a timeout event on the chrdev interface that is triggered
each time the period timeout is reached.

Signed-off-by: David Lechner <david@lechnology.com>
---
 drivers/counter/ti-eqep.c    | 132 ++++++++++++++++++++++++++++++++++-
 include/uapi/linux/counter.h |   2 +
 2 files changed, 133 insertions(+), 1 deletion(-)

diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c
index 9881e5115da6..a4a5a4486329 100644
--- a/drivers/counter/ti-eqep.c
+++ b/drivers/counter/ti-eqep.c
@@ -6,6 +6,7 @@
  */
 
 #include <linux/bitops.h>
+#include <linux/clk.h>
 #include <linux/counter.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
@@ -131,6 +132,7 @@ enum ti_eqep_count_func {
 
 struct ti_eqep_cnt {
 	struct counter_device counter;
+	unsigned long sysclkout_rate;
 	struct regmap *regmap32;
 	struct regmap *regmap16;
 };
@@ -298,6 +300,9 @@ static int ti_eqep_events_configure(struct counter_device *counter)
 		case COUNTER_EVENT_DIRECTION_CHANGE:
 			qeint |= QEINT_QDC;
 			break;
+		case COUNTER_EVENT_TIMEOUT:
+			qeint |= QEINT_UTO;
+			break;
 		}
 	}
 
@@ -311,6 +316,7 @@ static int ti_eqep_watch_validate(struct counter_device *counter,
 	case COUNTER_EVENT_OVERFLOW:
 	case COUNTER_EVENT_UNDERFLOW:
 	case COUNTER_EVENT_DIRECTION_CHANGE:
+	case COUNTER_EVENT_TIMEOUT:
 		return 0;
 	default:
 		return -EINVAL;
@@ -457,6 +463,106 @@ static struct counter_count ti_eqep_counts[] = {
 	},
 };
 
+static int ti_eqep_unit_timer_time_read(struct counter_device *counter,
+				       u64 *value)
+{
+	struct ti_eqep_cnt *priv = counter->priv;
+	u32 qutmr;
+
+	regmap_read(priv->regmap32, QUTMR, &qutmr);
+
+	/* convert timer ticks to nanoseconds */
+	*value = mul_u64_u32_div(qutmr, NSEC_PER_SEC, priv->sysclkout_rate);
+
+	return 0;
+}
+
+static int ti_eqep_unit_timer_time_write(struct counter_device *counter,
+					u64 value)
+{
+	struct ti_eqep_cnt *priv = counter->priv;
+	u32 qutmr;
+
+	/* convert nanoseconds to timer ticks */
+	qutmr = value = mul_u64_u32_div(value, priv->sysclkout_rate, NSEC_PER_SEC);
+	if (qutmr != value)
+		return -ERANGE;
+
+	regmap_write(priv->regmap32, QUTMR, qutmr);
+
+	return 0;
+}
+
+static int ti_eqep_unit_timer_period_read(struct counter_device *counter,
+					  u64 *value)
+{
+	struct ti_eqep_cnt *priv = counter->priv;
+	u32 quprd;
+
+	regmap_read(priv->regmap32, QUPRD, &quprd);
+
+	/* convert timer ticks to nanoseconds */
+	*value = mul_u64_u32_div(quprd, NSEC_PER_SEC, priv->sysclkout_rate);
+
+	return 0;
+}
+
+static int ti_eqep_unit_timer_period_write(struct counter_device *counter,
+					   u64 value)
+{
+	struct ti_eqep_cnt *priv = counter->priv;
+	u32 quprd;
+
+	/* convert nanoseconds to timer ticks */
+	quprd = value = mul_u64_u32_div(value, priv->sysclkout_rate, NSEC_PER_SEC);
+	if (quprd != value)
+		return -ERANGE;
+
+	/* protect against infinite unit timeout interrupts */
+	if (quprd == 0)
+		return -EINVAL;
+
+	regmap_write(priv->regmap32, QUPRD, quprd);
+
+	return 0;
+}
+
+static int ti_eqep_unit_timer_enable_read(struct counter_device *counter,
+					  u8 *value)
+{
+	struct ti_eqep_cnt *priv = counter->priv;
+	u32 qepctl;
+
+	regmap_read(priv->regmap16, QEPCTL, &qepctl);
+	*value = !!(qepctl & QEPCTL_UTE);
+
+	return 0;
+}
+
+static int ti_eqep_unit_timer_enable_write(struct counter_device *counter,
+					   u8 value)
+{
+	struct ti_eqep_cnt *priv = counter->priv;
+
+	if (value)
+		regmap_set_bits(priv->regmap16, QEPCTL, QEPCTL_UTE);
+	else
+		regmap_clear_bits(priv->regmap16, QEPCTL, QEPCTL_UTE);
+
+	return 0;
+}
+
+static struct counter_comp ti_eqep_device_ext[] = {
+	COUNTER_COMP_DEVICE_U64("unit_timer_time", ti_eqep_unit_timer_time_read,
+				ti_eqep_unit_timer_time_write),
+	COUNTER_COMP_DEVICE_U64("unit_timer_period",
+				ti_eqep_unit_timer_period_read,
+				ti_eqep_unit_timer_period_write),
+	COUNTER_COMP_DEVICE_BOOL("unit_timer_enable",
+				 ti_eqep_unit_timer_enable_read,
+				 ti_eqep_unit_timer_enable_write),
+};
+
 static irqreturn_t ti_eqep_irq_handler(int irq, void *dev_id)
 {
 	struct ti_eqep_cnt *priv = dev_id;
@@ -474,6 +580,8 @@ static irqreturn_t ti_eqep_irq_handler(int irq, void *dev_id)
 	if (qflg & QFLG_QDC)
 		counter_push_event(counter, COUNTER_EVENT_DIRECTION_CHANGE, 0);
 
+	if (qflg & QFLG_UTO)
+		counter_push_event(counter, COUNTER_EVENT_TIMEOUT, 0);
 
 	regmap_set_bits(priv->regmap16, QCLR, ~0);
 
@@ -500,6 +608,7 @@ static int ti_eqep_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct ti_eqep_cnt *priv;
+	struct clk *clk;
 	void __iomem *base;
 	int err;
 	int irq;
@@ -508,6 +617,24 @@ static int ti_eqep_probe(struct platform_device *pdev)
 	if (!priv)
 		return -ENOMEM;
 
+	clk = devm_clk_get(dev, "sysclkout");
+	if (IS_ERR(clk)) {
+		if (PTR_ERR(clk) != -EPROBE_DEFER)
+			dev_err(dev, "failed to get sysclkout");
+		return PTR_ERR(clk);
+	}
+
+	priv->sysclkout_rate = clk_get_rate(clk);
+	if (priv->sysclkout_rate == 0) {
+		dev_err(dev, "failed to get sysclkout rate");
+		/* prevent divide by zero */
+		priv->sysclkout_rate = 1;
+		/*
+		 * This error is not expected and the driver is mostly usable
+		 * without clock rate anyway, so don't exit here.
+		 */
+	}
+
 	base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(base))
 		return PTR_ERR(base);
@@ -536,6 +663,8 @@ static int ti_eqep_probe(struct platform_device *pdev)
 	priv->counter.ops = &ti_eqep_counter_ops;
 	priv->counter.counts = ti_eqep_counts;
 	priv->counter.num_counts = ARRAY_SIZE(ti_eqep_counts);
+	priv->counter.ext = ti_eqep_device_ext;
+	priv->counter.num_ext = ARRAY_SIZE(ti_eqep_device_ext);
 	priv->counter.signals = ti_eqep_signals;
 	priv->counter.num_signals = ARRAY_SIZE(ti_eqep_signals);
 	priv->counter.priv = priv;
@@ -552,10 +681,11 @@ static int ti_eqep_probe(struct platform_device *pdev)
 
 	/*
 	 * We can end up with an interupt infinite loop (interrupts triggered
-	 * as soon as they are cleared) if we leave this at the default value
+	 * as soon as they are cleared) if we leave these at the default value
 	 * of 0 and events are enabled.
 	 */
 	regmap_write(priv->regmap32, QPOSMAX, UINT_MAX);
+	regmap_write(priv->regmap32, QUPRD, UINT_MAX);
 
 	err = counter_register(&priv->counter);
 	if (err < 0) {
diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h
index 36dd3b474d09..640d9719b88c 100644
--- a/include/uapi/linux/counter.h
+++ b/include/uapi/linux/counter.h
@@ -63,6 +63,8 @@ enum counter_event_type {
 	COUNTER_EVENT_INDEX,
 	/* Direction change detected */
 	COUNTER_EVENT_DIRECTION_CHANGE,
+	/* Timer exceeded timeout */
+	COUNTER_EVENT_TIMEOUT,
 };
 
 /**
-- 
2.25.1


  parent reply	other threads:[~2021-10-17  1:56 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-10-17  1:33 [PATCH 0/8] counter: ti-eqep: implement features for speed measurement David Lechner
2021-10-17  1:33 ` [PATCH 1/8] counter/ti-eqep: implement over/underflow events David Lechner
2021-10-17 11:10   ` Jonathan Cameron
2021-10-25  7:13   ` William Breathitt Gray
2021-10-27 15:23     ` David Lechner
2021-10-28  6:41       ` William Breathitt Gray
2021-10-17  1:33 ` [PATCH 2/8] counter/ti-eqep: add support for direction David Lechner
2021-10-17 11:11   ` Jonathan Cameron
2021-10-25  7:29   ` William Breathitt Gray
2021-10-17  1:33 ` David Lechner [this message]
2021-10-17 11:20   ` [PATCH 3/8] counter/ti-eqep: add support for unit timer Jonathan Cameron
2021-10-25  8:48   ` William Breathitt Gray
2021-10-27 15:28     ` David Lechner
2021-10-28  7:48       ` William Breathitt Gray
2021-10-28 13:42         ` David Lechner
2021-10-30  8:35           ` William Breathitt Gray
2021-10-17  1:33 ` [PATCH 4/8] docs: counter: add unit timer sysfs attributes David Lechner
2021-10-17 11:23   ` Jonathan Cameron
2021-10-27  6:46   ` William Breathitt Gray
2021-10-27 15:30     ` David Lechner
2021-10-28  7:59       ` William Breathitt Gray
2021-10-30 16:40         ` David Lechner
2021-11-01  4:08           ` William Breathitt Gray
2021-11-01  5:27             ` William Breathitt Gray
2021-10-17  1:33 ` [PATCH 5/8] counter/ti-eqep: add support for latched position David Lechner
2021-10-27  7:44   ` William Breathitt Gray
2021-10-27 15:40     ` David Lechner
2021-10-28  8:12       ` William Breathitt Gray
2021-10-17  1:33 ` [PATCH 6/8] docs: counter: add latch_mode and latched_count sysfs attributes David Lechner
2021-10-17 11:26   ` Jonathan Cameron
2021-10-27  7:54   ` William Breathitt Gray
2021-10-27 17:00     ` David Lechner
2021-10-30  1:32       ` William Breathitt Gray
2021-10-30 14:39         ` Jonathan Cameron
2021-11-01  5:11           ` William Breathitt Gray
2021-10-17  1:33 ` [PATCH 7/8] counter/ti-eqep: add support for edge capture unit David Lechner
2021-10-17 11:29   ` Jonathan Cameron
2021-10-27  8:23   ` William Breathitt Gray
2021-10-27 17:28     ` David Lechner
2021-10-17  1:33 ` [PATCH 8/8] docs: counter: add edge_capture_unit_* attributes David Lechner
2021-10-27  8:26   ` William Breathitt Gray

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211017013343.3385923-4-david@lechnology.com \
    --to=david@lechnology.com \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=robertcnelson@gmail.com \
    --cc=vilhelm.gray@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.