[PATCH 1/2] plat-omap: dmtimer: Add support for interrupts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Added the posibility to have interrupts from any timer.
A client module can register a callback for each timer using the
new API function omap_dm_timer_set_isr_callback.
The callback will be called from the timer ISR having as parameters
the timer handle and the event that triggered the interrupt. This
way the client module will know the timer that produced the interrupt
and also the interrupt type (match, overflow or capture) so that it can
take some action on it.
Added also statistics on the diffent interrupt types and a function
that prints them.

Signed-off-by: Andrei Varvara <andrei.varvara@xxxxxxxx>
---
 arch/arm/plat-omap/dmtimer.c              | 121 ++++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/plat/dmtimer.h |  28 ++++++-
 2 files changed, 148 insertions(+), 1 deletion(-)

diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index 8ca94d3..b13e0d2 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -45,6 +45,7 @@
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/dmtimer-omap.h>
+#include <linux/interrupt.h>
 
 #include <plat/dmtimer.h>
 
@@ -713,6 +714,33 @@ int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask)
 }
 EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_disable);
 
+/**
+ * omap_dm_timer_set_isr_callback - sets a callback called from the timer isr
+ * @timer:  pointer to timer handle
+ * @cb:     callback to be called from the timer ISR
+ *
+ * Returns 0 on success and error code otherwise
+ */
+int omap_dm_timer_set_isr_callback(struct omap_dm_timer *timer,
+				   omap_dm_timer_isr_callback_t cb)
+{
+	if (unlikely(!timer)) {
+		pr_err("%s: Invalid timer handle.\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!cb) {
+		pr_err("%s: invalid callback pointer\n", __func__);
+		return -EINVAL;
+	}
+
+	timer->isr_callback = cb;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(omap_dm_timer_set_isr_callback);
+
+
 unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
 {
 	unsigned int l;
@@ -782,8 +810,84 @@ int omap_dm_timers_active(void)
 }
 EXPORT_SYMBOL_GPL(omap_dm_timers_active);
 
+int print_timer_irq_statistics(struct omap_dm_timer *timer)
+{
+	u32 capture, overflow, match;
+	u64 all;
+
+	if (!timer) {
+		pr_err("Invalid timer handle.\n");
+		return -EINVAL;
+	}
+
+	raw_spin_lock(&timer->raw_lock);
+
+	capture = timer->irq_stats.capture;
+	overflow = timer->irq_stats.overflow;
+	match = timer->irq_stats.match;
+	all = timer->irq_stats.all;
+
+	raw_spin_unlock(&timer->raw_lock);
+
+	pr_info("dmtimer: %s irq statistics:\n", timer->pdev->name);
+	pr_info("capture %u\n", capture);
+	pr_info("overflow_irqs %u\n", overflow);
+	pr_info("match %u\n", match);
+	pr_info("all %llu\n", all);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(print_timer_irq_statistics);
+
 static const struct of_device_id omap_timer_match[];
 
+static irqreturn_t timer_irq_handler(int irq_no, void *data)
+{
+	struct omap_dm_timer *timer = (struct omap_dm_timer *) data;
+	unsigned int status;
+
+	if (!data)
+		return IRQ_NONE;
+
+	raw_spin_lock(&timer->raw_lock);
+
+	status = readl_relaxed(timer->irq_stat);
+
+	if (status & OMAP_TIMER_INT_CAPTURE) {
+		timer->context.tcar1 = __omap_dm_timer_read(timer,
+					OMAP_TIMER_CAPTURE_REG, timer->posted);
+		timer->context.tcar2 = __omap_dm_timer_read(timer,
+					OMAP_TIMER_CAPTURE2_REG, timer->posted);
+		timer->irq_stats.capture++;
+		if (timer->isr_callback)
+			timer->isr_callback(timer, CAPTURE);
+	}
+
+	if (status & OMAP_TIMER_INT_OVERFLOW) {
+		timer->irq_stats.overflow++;
+		if (timer->isr_callback)
+			timer->isr_callback(timer, OVERFLOW);
+	}
+
+	if (status & OMAP_TIMER_INT_MATCH) {
+		timer->irq_stats.match++;
+		if (timer->isr_callback)
+			timer->isr_callback(timer, MATCH);
+	}
+
+	timer->irq_stats.all++;
+
+	/*
+	 * Instruct timer to deassert the interrupt, also it will trigger
+	 * another capture process.
+	 */
+	writel_relaxed(status, timer->irq_stat);
+
+	raw_spin_unlock(&timer->raw_lock);
+
+	return IRQ_HANDLED;
+}
+
 /**
  * omap_dm_timer_probe - probe function called for every registered device
  * @pdev:	pointer to current timer platform device
@@ -854,6 +958,23 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
 	timer->irq = irq->start;
 	timer->pdev = pdev;
 
+	/*
+	 * Initialize the non preemptible spinlock that protects the captured
+	 * timer values.
+	 */
+	raw_spin_lock_init(&timer->raw_lock);
+
+	/* set up irq for timer */
+	pr_info("dmtimer: %s request irq by no: %d\n",
+		dev_name(dev), timer->irq);
+	ret = request_irq(timer->irq, timer_irq_handler, IRQF_TIMER,
+			  pdev->name, timer);
+	if (ret) {
+		dev_err(dev, "Failed to reserve irq line %d for timer %s\n",
+			timer->irq, pdev->name);
+		return ret;
+	}
+
 	/* Skip pm_runtime_enable for OMAP1 */
 	if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) {
 		pm_runtime_enable(dev);
diff --git a/arch/arm/plat-omap/include/plat/dmtimer.h b/arch/arm/plat-omap/include/plat/dmtimer.h
index dd79f30..4e7daf9 100644
--- a/arch/arm/plat-omap/include/plat/dmtimer.h
+++ b/arch/arm/plat-omap/include/plat/dmtimer.h
@@ -99,6 +99,24 @@ struct timer_regs {
 	u32 towr;
 };
 
+struct timer_irq_stats {
+	u32 capture;
+	u32 overflow;
+	u32 match;
+	u64 all;
+};
+
+enum timer_irq_event {
+	MATCH = 1,
+	OVERFLOW,
+	CAPTURE
+};
+
+struct omap_dm_timer;
+
+typedef void (*omap_dm_timer_isr_callback_t)(struct omap_dm_timer *timer,
+					     enum timer_irq_event irq_event);
+
 struct omap_dm_timer {
 	int id;
 	int irq;
@@ -115,6 +133,7 @@ struct omap_dm_timer {
 	unsigned reserved:1;
 	unsigned posted:1;
 	struct timer_regs context;
+	struct timer_irq_stats irq_stats;
 	int (*get_context_loss_count)(struct device *);
 	int ctx_loss_count;
 	int revision;
@@ -122,6 +141,10 @@ struct omap_dm_timer {
 	u32 errata;
 	struct platform_device *pdev;
 	struct list_head node;
+	omap_dm_timer_isr_callback_t isr_callback;
+
+	/* protects tcar1, tcar2 and timer_irq_stats from concurent access */
+	raw_spinlock_t raw_lock;
 };
 
 int omap_dm_timer_reserve_systimer(int id);
@@ -151,7 +174,8 @@ int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler);
 
 int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, unsigned int value);
 int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask);
-
+int omap_dm_timer_set_isr_callback(struct omap_dm_timer *timer,
+				   omap_dm_timer_isr_callback_t cb);
 unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer);
 int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value);
 unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer);
@@ -159,6 +183,8 @@ int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
 
 int omap_dm_timers_active(void);
 
+int print_timer_irq_statistics(struct omap_dm_timer *timer);
+
 /*
  * Do not use the defines below, they are not needed. They should be only
  * used by dmtimer.c and sys_timer related code.
-- 
1.9.1

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



[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux