All of lore.kernel.org
 help / color / mirror / Atom feed
From: Steve Longerbeam <slongerbeam@gmail.com>
To: linux-media@vger.kernel.org
Cc: Steve Longerbeam <steve_longerbeam@mentor.com>
Subject: [PATCH 18/28] clocksource/drivers/imx: add input capture support
Date: Wed,  6 Jul 2016 16:06:48 -0700	[thread overview]
Message-ID: <1467846418-12913-19-git-send-email-steve_longerbeam@mentor.com> (raw)
In-Reply-To: <1467846418-12913-1-git-send-email-steve_longerbeam@mentor.com>

This patch adds support for the input capture function in the
i.MX GPT. Output compare and input capture functions are mixed
in the same register block, so we need to modify the irq ack/enable/
disable primitives to not stomp on the other function.

The input capture API is modelled after request/free irq:

typedef void (*mxc_icap_handler_t)(int, void *, struct timespec *);

int mxc_request_input_capture(unsigned int chan,
			      mxc_icap_handler_t handler,
			      unsigned long capflags, void *dev_id);

    - chan: the channel number being requested (0 or 1).

    - handler: a callback when there is an input capture event. The
      handler is given the channel number, the dev_id, and a timespec
      marking the input capture event. The timespec is always reset at
      request time, that is, the first event after request will always
      have a timespec of 0, and will increase thereafter.

    - capflags: IRQF_TRIGGER_RISING and/or IRQF_TRIGGER_FALLING. If
      both are specified, events will be triggered on both rising and
      falling edges of the input capture signal.

    - dev_id: a context pointer given back to the handler.

void mxc_free_input_capture(unsigned int chan, void *dev_id);

    This disables the given input capture channel in the GPT.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/clocksource/timer-imx-gpt.c | 463 ++++++++++++++++++++++++++++++++----
 include/linux/mxc_icap.h            |  20 ++
 2 files changed, 437 insertions(+), 46 deletions(-)
 create mode 100644 include/linux/mxc_icap.h

diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c
index 99ec967..1f7f871 100644
--- a/drivers/clocksource/timer-imx-gpt.c
+++ b/drivers/clocksource/timer-imx-gpt.c
@@ -21,6 +21,7 @@
  * MA 02110-1301, USA.
  */
 
+#include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/clockchips.h>
@@ -32,6 +33,8 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/mxc_icap.h>
 #include <soc/imx/timer.h>
 
 /*
@@ -65,16 +68,53 @@
 #define V2_TCTL_CLK_PER		(2 << 6)
 #define V2_TCTL_CLK_OSC_DIV8	(5 << 6)
 #define V2_TCTL_FRR		(1 << 9)
+#define V2_TCTL_IM1_BIT		16
+#define V2_TCTL_IM2_BIT		18
+#define V2_IM_DISABLE		0
+#define V2_IM_RISING		1
+#define V2_IM_FALLING		2
+#define V2_IM_BOTH		3
 #define V2_TCTL_24MEN		(1 << 10)
 #define V2_TPRER_PRE24M		12
 #define V2_IR			0x0c
+#define V2_IR_OF1		(1 << 0)
+#define V2_IR_IF1		(1 << 3)
+#define V2_IR_IF2		(1 << 4)
 #define V2_TSTAT		0x08
 #define V2_TSTAT_OF1		(1 << 0)
+#define V2_TSTAT_IF1		(1 << 3)
+#define V2_TSTAT_IF2		(1 << 4)
 #define V2_TCN			0x24
 #define V2_TCMP			0x10
+#define V2_TCAP1		0x1c
+#define V2_TCAP2		0x20
 
 #define V2_TIMER_RATE_OSC_DIV8	3000000
 
+struct imx_timer;
+
+struct icap_channel {
+	struct imx_timer *imxtm;
+
+	int chan;
+
+	u32 cnt_reg;
+	u32 irqen_bit;
+	u32 status_bit;
+	u32 mode_bit;
+
+	mxc_icap_handler_t handler;
+	void *dev_id;
+
+	struct timespec ts;
+	cycles_t last_cycles;
+	bool first_event;
+};
+
+/* FIXME, for now can't find icap unless it's statically allocated */
+static struct icap_channel icap_channel[2];
+static DEFINE_SPINLOCK(icap_lock);
+
 struct imx_timer {
 	enum imx_gpt_type type;
 	void __iomem *base;
@@ -90,12 +130,20 @@ struct imx_gpt_data {
 	int reg_tstat;
 	int reg_tcn;
 	int reg_tcmp;
-	void (*gpt_setup_tctl)(struct imx_timer *imxtm);
-	void (*gpt_irq_enable)(struct imx_timer *imxtm);
-	void (*gpt_irq_disable)(struct imx_timer *imxtm);
-	void (*gpt_irq_acknowledge)(struct imx_timer *imxtm);
+	void (*gpt_oc_setup_tctl)(struct imx_timer *imxtm);
+	void (*gpt_oc_irq_enable)(struct imx_timer *imxtm);
+	void (*gpt_oc_irq_disable)(struct imx_timer *imxtm);
+	void (*gpt_oc_irq_acknowledge)(struct imx_timer *imxtm);
+	bool (*gpt_is_oc_irq)(unsigned int tstat);
 	int (*set_next_event)(unsigned long evt,
 			      struct clock_event_device *ced);
+
+	void (*gpt_ic_irq_enable)(struct icap_channel *ic);
+	void (*gpt_ic_irq_disable)(struct icap_channel *ic);
+	void (*gpt_ic_irq_acknowledge)(struct icap_channel *ic);
+	bool (*gpt_is_ic_irq)(unsigned int tstat);
+	void (*gpt_ic_enable)(struct icap_channel *ic, unsigned int mode);
+	void (*gpt_ic_disable)(struct icap_channel *ic);
 };
 
 static inline struct imx_timer *to_imx_timer(struct clock_event_device *ced)
@@ -103,52 +151,144 @@ static inline struct imx_timer *to_imx_timer(struct clock_event_device *ced)
 	return container_of(ced, struct imx_timer, ced);
 }
 
-static void imx1_gpt_irq_disable(struct imx_timer *imxtm)
+static void imx1_gpt_oc_irq_disable(struct imx_timer *imxtm)
 {
 	unsigned int tmp;
 
 	tmp = readl_relaxed(imxtm->base + MXC_TCTL);
 	writel_relaxed(tmp & ~MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
 }
-#define imx21_gpt_irq_disable imx1_gpt_irq_disable
+#define imx21_gpt_oc_irq_disable imx1_gpt_oc_irq_disable
 
-static void imx31_gpt_irq_disable(struct imx_timer *imxtm)
+static void imx31_gpt_oc_irq_disable(struct imx_timer *imxtm)
 {
-	writel_relaxed(0, imxtm->base + V2_IR);
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	writel_relaxed(tmp & ~V2_IR_OF1, imxtm->base + V2_IR);
 }
-#define imx6dl_gpt_irq_disable imx31_gpt_irq_disable
+#define imx6dl_gpt_oc_irq_disable imx31_gpt_oc_irq_disable
 
-static void imx1_gpt_irq_enable(struct imx_timer *imxtm)
+static void imx1_gpt_oc_irq_enable(struct imx_timer *imxtm)
 {
 	unsigned int tmp;
 
 	tmp = readl_relaxed(imxtm->base + MXC_TCTL);
 	writel_relaxed(tmp | MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
 }
-#define imx21_gpt_irq_enable imx1_gpt_irq_enable
+#define imx21_gpt_oc_irq_enable imx1_gpt_oc_irq_enable
 
-static void imx31_gpt_irq_enable(struct imx_timer *imxtm)
+static void imx31_gpt_oc_irq_enable(struct imx_timer *imxtm)
 {
-	writel_relaxed(1<<0, imxtm->base + V2_IR);
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	writel_relaxed(tmp | V2_IR_OF1, imxtm->base + V2_IR);
 }
-#define imx6dl_gpt_irq_enable imx31_gpt_irq_enable
+#define imx6dl_gpt_oc_irq_enable imx31_gpt_oc_irq_enable
 
-static void imx1_gpt_irq_acknowledge(struct imx_timer *imxtm)
+static void imx1_gpt_oc_irq_acknowledge(struct imx_timer *imxtm)
 {
 	writel_relaxed(0, imxtm->base + MX1_2_TSTAT);
 }
 
-static void imx21_gpt_irq_acknowledge(struct imx_timer *imxtm)
+static void imx21_gpt_oc_irq_acknowledge(struct imx_timer *imxtm)
 {
 	writel_relaxed(MX2_TSTAT_CAPT | MX2_TSTAT_COMP,
 				imxtm->base + MX1_2_TSTAT);
 }
 
-static void imx31_gpt_irq_acknowledge(struct imx_timer *imxtm)
+static bool imx1_gpt_is_oc_irq(unsigned int tstat)
+{
+	return true;
+}
+
+static bool imx21_gpt_is_oc_irq(unsigned int tstat)
+{
+	return (tstat & MX2_TSTAT_COMP) != 0;
+}
+
+static bool imx31_gpt_is_oc_irq(unsigned int tstat)
+{
+	return (tstat & V2_TSTAT_OF1) != 0;
+}
+#define imx6dl_gpt_is_oc_irq imx31_gpt_is_oc_irq
+
+static void imx31_gpt_oc_irq_acknowledge(struct imx_timer *imxtm)
 {
 	writel_relaxed(V2_TSTAT_OF1, imxtm->base + V2_TSTAT);
 }
-#define imx6dl_gpt_irq_acknowledge imx31_gpt_irq_acknowledge
+#define imx6dl_gpt_oc_irq_acknowledge imx31_gpt_oc_irq_acknowledge
+
+static void imx31_gpt_ic_irq_disable(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	tmp &= ~ic->irqen_bit;
+	writel_relaxed(tmp, imxtm->base + V2_IR);
+}
+#define imx6dl_gpt_ic_irq_disable imx31_gpt_ic_irq_disable
+
+static void imx31_gpt_ic_irq_enable(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	tmp |= ic->irqen_bit;
+	writel_relaxed(tmp, imxtm->base + V2_IR);
+}
+#define imx6dl_gpt_ic_irq_enable imx31_gpt_ic_irq_enable
+
+static void imx31_gpt_ic_irq_acknowledge(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+
+	writel_relaxed(ic->status_bit, imxtm->base + V2_TSTAT);
+}
+#define imx6dl_gpt_ic_irq_acknowledge imx31_gpt_ic_irq_acknowledge
+
+static bool imx1_gpt_is_ic_irq(unsigned int tstat)
+{
+	return false;
+}
+#define imx21_gpt_is_ic_irq imx1_gpt_is_ic_irq
+
+static bool imx31_gpt_is_ic_irq(unsigned int tstat)
+{
+	return (tstat & (V2_TSTAT_IF1 | V2_TSTAT_IF2)) != 0;
+}
+#define imx6dl_gpt_is_ic_irq imx31_gpt_is_ic_irq
+
+static void imx31_gpt_ic_enable(struct icap_channel *ic, unsigned int mode)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tctl, mask;
+
+	mask = 0x3 << ic->mode_bit;
+	mode <<= ic->mode_bit;
+
+	tctl = readl_relaxed(imxtm->base + MXC_TCTL);
+	tctl &= ~mask;
+	tctl |= mode;
+	writel_relaxed(tctl, imxtm->base + MXC_TCTL);
+}
+#define imx6dl_gpt_ic_enable imx31_gpt_ic_enable
+
+static void imx31_gpt_ic_disable(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tctl, mask;
+
+	mask = 0x3 << ic->mode_bit;
+
+	tctl = readl_relaxed(imxtm->base + MXC_TCTL);
+	tctl &= ~mask;
+	writel_relaxed(tctl, imxtm->base + MXC_TCTL);
+}
+#define imx6dl_gpt_ic_disable imx31_gpt_ic_disable
 
 static void __iomem *sched_clock_reg;
 
@@ -164,6 +304,32 @@ static unsigned long imx_read_current_timer(void)
 	return readl_relaxed(sched_clock_reg);
 }
 
+static void mxc_update_icap_ts(struct icap_channel *ic, struct timespec *ts)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	cycles_t cycles;
+	u64 diff;
+	u32 rem;
+
+	cycles = readl_relaxed(imxtm->base + ic->cnt_reg);
+	if (!ic->first_event) {
+		if (cycles >= ic->last_cycles)
+			diff = cycles - ic->last_cycles;
+		else
+			diff = ((u64)1 << 32) - ic->last_cycles + cycles;
+
+		diff *= NSEC_PER_SEC;
+		diff += imx_delay_timer.freq / 2;
+
+		rem = do_div(diff, imx_delay_timer.freq);
+
+		timespec_add_ns(&ic->ts, diff);
+	}
+
+	*ts = ic->ts;
+	ic->last_cycles = cycles;
+}
+
 static int __init mxc_clocksource_init(struct imx_timer *imxtm)
 {
 	unsigned int c = clk_get_rate(imxtm->clk_per);
@@ -224,14 +390,14 @@ static int mxc_shutdown(struct clock_event_device *ced)
 	local_irq_save(flags);
 
 	/* Disable interrupt in GPT module */
-	imxtm->gpt->gpt_irq_disable(imxtm);
+	imxtm->gpt->gpt_oc_irq_disable(imxtm);
 
 	tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn);
 	/* Set event time into far-far future */
 	writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp);
 
 	/* Clear pending interrupt */
-	imxtm->gpt->gpt_irq_acknowledge(imxtm);
+	imxtm->gpt->gpt_oc_irq_acknowledge(imxtm);
 
 #ifdef DEBUG
 	printk(KERN_INFO "%s: changing mode\n", __func__);
@@ -254,7 +420,7 @@ static int mxc_set_oneshot(struct clock_event_device *ced)
 	local_irq_save(flags);
 
 	/* Disable interrupt in GPT module */
-	imxtm->gpt->gpt_irq_disable(imxtm);
+	imxtm->gpt->gpt_oc_irq_disable(imxtm);
 
 	if (!clockevent_state_oneshot(ced)) {
 		u32 tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn);
@@ -262,7 +428,7 @@ static int mxc_set_oneshot(struct clock_event_device *ced)
 		writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp);
 
 		/* Clear pending interrupt */
-		imxtm->gpt->gpt_irq_acknowledge(imxtm);
+		imxtm->gpt->gpt_oc_irq_acknowledge(imxtm);
 	}
 
 #ifdef DEBUG
@@ -275,7 +441,7 @@ static int mxc_set_oneshot(struct clock_event_device *ced)
 	 * to call mxc_set_next_event() or shutdown clock after
 	 * mode switching
 	 */
-	imxtm->gpt->gpt_irq_enable(imxtm);
+	imxtm->gpt->gpt_oc_irq_enable(imxtm);
 	local_irq_restore(flags);
 
 	return 0;
@@ -292,9 +458,31 @@ static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
 
 	tstat = readl_relaxed(imxtm->base + imxtm->gpt->reg_tstat);
 
-	imxtm->gpt->gpt_irq_acknowledge(imxtm);
+	if (imxtm->gpt->gpt_is_ic_irq(tstat)) {
+		struct icap_channel *ic;
+		struct timespec ts;
+		int i;
+
+		for (i = 0; i < 2; i++) {
+			ic = &icap_channel[i];
+
+			if (!(tstat & ic->status_bit))
+				continue;
 
-	ced->event_handler(ced);
+			imxtm->gpt->gpt_ic_irq_acknowledge(ic);
+
+			mxc_update_icap_ts(ic, &ts);
+			ic->first_event = false;
+
+			if (ic->handler)
+				ic->handler(ic->chan, ic->dev_id, &ts);
+		}
+	}
+
+	if (imxtm->gpt->gpt_is_oc_irq(tstat)) {
+		imxtm->gpt->gpt_oc_irq_acknowledge(imxtm);
+		ced->event_handler(ced);
+	}
 
 	return IRQ_HANDLED;
 }
@@ -324,16 +512,16 @@ static int __init mxc_clockevent_init(struct imx_timer *imxtm)
 	return setup_irq(imxtm->irq, act);
 }
 
-static void imx1_gpt_setup_tctl(struct imx_timer *imxtm)
+static void imx1_gpt_oc_setup_tctl(struct imx_timer *imxtm)
 {
 	u32 tctl_val;
 
 	tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN;
 	writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
 }
-#define imx21_gpt_setup_tctl imx1_gpt_setup_tctl
+#define imx21_gpt_oc_setup_tctl imx1_gpt_oc_setup_tctl
 
-static void imx31_gpt_setup_tctl(struct imx_timer *imxtm)
+static void imx31_gpt_oc_setup_tctl(struct imx_timer *imxtm)
 {
 	u32 tctl_val;
 
@@ -346,7 +534,7 @@ static void imx31_gpt_setup_tctl(struct imx_timer *imxtm)
 	writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
 }
 
-static void imx6dl_gpt_setup_tctl(struct imx_timer *imxtm)
+static void imx6dl_gpt_oc_setup_tctl(struct imx_timer *imxtm)
 {
 	u32 tctl_val;
 
@@ -367,10 +555,12 @@ static const struct imx_gpt_data imx1_gpt_data = {
 	.reg_tstat = MX1_2_TSTAT,
 	.reg_tcn = MX1_2_TCN,
 	.reg_tcmp = MX1_2_TCMP,
-	.gpt_irq_enable = imx1_gpt_irq_enable,
-	.gpt_irq_disable = imx1_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx1_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx1_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx1_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx1_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx1_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx1_gpt_is_oc_irq,
+	.gpt_is_ic_irq = imx1_gpt_is_ic_irq,
+	.gpt_oc_setup_tctl = imx1_gpt_oc_setup_tctl,
 	.set_next_event = mx1_2_set_next_event,
 };
 
@@ -378,10 +568,12 @@ static const struct imx_gpt_data imx21_gpt_data = {
 	.reg_tstat = MX1_2_TSTAT,
 	.reg_tcn = MX1_2_TCN,
 	.reg_tcmp = MX1_2_TCMP,
-	.gpt_irq_enable = imx21_gpt_irq_enable,
-	.gpt_irq_disable = imx21_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx21_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx21_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx21_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx21_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx21_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx21_gpt_is_oc_irq,
+	.gpt_is_ic_irq = imx21_gpt_is_ic_irq,
+	.gpt_oc_setup_tctl = imx21_gpt_oc_setup_tctl,
 	.set_next_event = mx1_2_set_next_event,
 };
 
@@ -389,26 +581,136 @@ static const struct imx_gpt_data imx31_gpt_data = {
 	.reg_tstat = V2_TSTAT,
 	.reg_tcn = V2_TCN,
 	.reg_tcmp = V2_TCMP,
-	.gpt_irq_enable = imx31_gpt_irq_enable,
-	.gpt_irq_disable = imx31_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx31_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx31_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx31_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx31_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx31_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx31_gpt_is_oc_irq,
+	.gpt_oc_setup_tctl = imx31_gpt_oc_setup_tctl,
 	.set_next_event = v2_set_next_event,
+
+	/* input capture methods */
+	.gpt_ic_irq_enable = imx31_gpt_ic_irq_enable,
+	.gpt_ic_irq_disable = imx31_gpt_ic_irq_disable,
+	.gpt_ic_irq_acknowledge = imx31_gpt_ic_irq_acknowledge,
+	.gpt_is_ic_irq = imx31_gpt_is_ic_irq,
+	.gpt_ic_enable = imx31_gpt_ic_enable,
+	.gpt_ic_disable = imx31_gpt_ic_disable,
 };
 
 static const struct imx_gpt_data imx6dl_gpt_data = {
 	.reg_tstat = V2_TSTAT,
 	.reg_tcn = V2_TCN,
 	.reg_tcmp = V2_TCMP,
-	.gpt_irq_enable = imx6dl_gpt_irq_enable,
-	.gpt_irq_disable = imx6dl_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx6dl_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx6dl_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx6dl_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx6dl_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx6dl_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx6dl_gpt_is_oc_irq,
+	.gpt_oc_setup_tctl = imx6dl_gpt_oc_setup_tctl,
 	.set_next_event = v2_set_next_event,
+
+	/* input capture methods */
+	.gpt_ic_irq_enable = imx6dl_gpt_ic_irq_enable,
+	.gpt_ic_irq_disable = imx6dl_gpt_ic_irq_disable,
+	.gpt_ic_irq_acknowledge = imx6dl_gpt_ic_irq_acknowledge,
+	.gpt_is_ic_irq = imx6dl_gpt_is_ic_irq,
+	.gpt_ic_enable = imx6dl_gpt_ic_enable,
+	.gpt_ic_disable = imx6dl_gpt_ic_disable,
 };
 
+int mxc_request_input_capture(unsigned int chan, mxc_icap_handler_t handler,
+			      unsigned long capflags, void *dev_id)
+{
+	struct imx_timer *imxtm;
+	struct icap_channel *ic;
+	unsigned long flags;
+	int ret = 0;
+	u32 mode;
+
+	/* we only care about rising and falling flags */
+	capflags &= (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING);
+
+	if (chan > 1 || !handler || !capflags)
+		return -EINVAL;
+
+	ic = &icap_channel[chan];
+	imxtm = ic->imxtm;
+
+	if (!imxtm->gpt->gpt_ic_enable)
+		return -ENODEV;
+
+	spin_lock_irqsave(&icap_lock, flags);
+
+	if (ic->handler) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ic->handler = handler;
+	ic->dev_id = dev_id;
+
+	switch (capflags) {
+	case IRQF_TRIGGER_RISING:
+		mode = V2_IM_RISING;
+		break;
+	case IRQF_TRIGGER_FALLING:
+		mode = V2_IM_FALLING;
+		break;
+	default:
+		mode = V2_IM_BOTH;
+		break;
+	}
+
+	/* ack any pending input capture interrupt before enabling */
+	imxtm->gpt->gpt_ic_irq_acknowledge(ic);
+
+	/* initialize timespec */
+	ic->ts.tv_sec = ic->ts.tv_nsec = 0;
+	ic->first_event = true;
+
+	imxtm->gpt->gpt_ic_enable(ic, mode);
+	imxtm->gpt->gpt_ic_irq_enable(ic);
+
+out:
+	spin_unlock_irqrestore(&icap_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mxc_request_input_capture);
+
+void mxc_free_input_capture(unsigned int chan, void *dev_id)
+{
+	struct imx_timer *imxtm;
+	struct icap_channel *ic;
+	unsigned long flags;
+
+	if (chan > 1)
+		return;
+
+	ic = &icap_channel[chan];
+	imxtm = ic->imxtm;
+
+	if (!imxtm->gpt->gpt_ic_disable)
+		return;
+
+	spin_lock_irqsave(&icap_lock, flags);
+
+	if (!ic->handler || dev_id != ic->dev_id)
+		goto out;
+
+	imxtm->gpt->gpt_ic_irq_disable(ic);
+	imxtm->gpt->gpt_ic_disable(ic);
+
+	ic->handler = NULL;
+	ic->dev_id = NULL;
+out:
+	spin_unlock_irqrestore(&icap_lock, flags);
+}
+EXPORT_SYMBOL_GPL(mxc_free_input_capture);
+
 static void __init _mxc_timer_init(struct imx_timer *imxtm)
 {
+	struct icap_channel *ic;
+	int i;
+
 	switch (imxtm->type) {
 	case GPT_TYPE_IMX1:
 		imxtm->gpt = &imx1_gpt_data;
@@ -443,11 +745,16 @@ static void __init _mxc_timer_init(struct imx_timer *imxtm)
 	writel_relaxed(0, imxtm->base + MXC_TCTL);
 	writel_relaxed(0, imxtm->base + MXC_TPRER); /* see datasheet note */
 
-	imxtm->gpt->gpt_setup_tctl(imxtm);
+	imxtm->gpt->gpt_oc_setup_tctl(imxtm);
 
 	/* init and register the timer to the framework */
 	mxc_clocksource_init(imxtm);
 	mxc_clockevent_init(imxtm);
+
+	for (i = 0; i < 2; i++) {
+		ic = &icap_channel[i];
+		ic->imxtm = imxtm;
+	}
 }
 
 void __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type)
@@ -469,6 +776,70 @@ void __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type)
 	_mxc_timer_init(imxtm);
 }
 
+/*
+ * a platform driver is needed in order to acquire pinmux
+ * for input capture pins. The probe call is also useful
+ * for setting up the input capture channel structures.
+ */
+static int mxc_timer_probe(struct platform_device *pdev)
+{
+	struct icap_channel *ic;
+	int i;
+
+	/* setup the input capture channels */
+	for (i = 0; i < 2; i++) {
+		ic = &icap_channel[i];
+		ic->chan = i;
+		if (i == 0) {
+			ic->cnt_reg = V2_TCAP1;
+			ic->irqen_bit = V2_IR_IF1;
+			ic->status_bit = V2_TSTAT_IF1;
+			ic->mode_bit = V2_TCTL_IM1_BIT;
+		} else {
+			ic->cnt_reg = V2_TCAP2;
+			ic->irqen_bit = V2_IR_IF2;
+			ic->status_bit = V2_TSTAT_IF2;
+			ic->mode_bit = V2_TCTL_IM2_BIT;
+		}
+	}
+
+	return 0;
+}
+
+static int mxc_timer_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id timer_of_match[] = {
+	{ .compatible = "fsl,imx1-gpt" },
+	{ .compatible = "fsl,imx21-gpt" },
+	{ .compatible = "fsl,imx27-gpt" },
+	{ .compatible = "fsl,imx31-gpt" },
+	{ .compatible = "fsl,imx25-gpt" },
+	{ .compatible = "fsl,imx50-gpt" },
+	{ .compatible = "fsl,imx51-gpt" },
+	{ .compatible = "fsl,imx53-gpt" },
+	{ .compatible = "fsl,imx6q-gpt" },
+	{ .compatible = "fsl,imx6dl-gpt" },
+	{ .compatible = "fsl,imx6sl-gpt" },
+	{ .compatible = "fsl,imx6sx-gpt" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, timer_of_match);
+
+static struct platform_driver mxc_timer_pdrv = {
+	.probe		= mxc_timer_probe,
+	.remove		= mxc_timer_remove,
+	.driver		= {
+		.name	= "mxc-timer",
+		.owner	= THIS_MODULE,
+		.of_match_table	= timer_of_match,
+	},
+};
+
+module_platform_driver(mxc_timer_pdrv);
+
 static void __init mxc_timer_init_dt(struct device_node *np,  enum imx_gpt_type type)
 {
 	struct imx_timer *imxtm;
diff --git a/include/linux/mxc_icap.h b/include/linux/mxc_icap.h
new file mode 100644
index 0000000..a829f11
--- /dev/null
+++ b/include/linux/mxc_icap.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2015 Mentor Graphics, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __MXC_ICAP_H__
+#define __MXC_ICAP_H__
+
+typedef void (*mxc_icap_handler_t)(int, void *, struct timespec *);
+
+int mxc_request_input_capture(unsigned int chan, mxc_icap_handler_t handler,
+			      unsigned long capflags, void *dev_id);
+void mxc_free_input_capture(unsigned int chan, void *dev_id);
+
+#endif
-- 
1.9.1


  parent reply	other threads:[~2016-07-06 23:07 UTC|newest]

Thread overview: 105+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
2016-06-14 22:48 ` [PATCH 01/38] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
2016-06-14 22:48 ` [PATCH 02/38] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
2016-06-14 22:48 ` [PATCH 03/38] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
2016-06-14 22:49 ` [PATCH 04/38] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
2016-06-14 22:49 ` [PATCH 05/38] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
2016-06-14 22:49 ` [PATCH 06/38] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
2016-06-14 22:49 ` [PATCH 07/38] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
2016-06-14 22:49 ` [PATCH 08/38] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
2016-06-14 22:49 ` [PATCH 09/38] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
2016-06-14 22:49 ` [PATCH 10/38] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
2016-06-14 22:49 ` [PATCH 11/38] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
2016-06-14 22:49 ` [PATCH 12/38] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
2016-06-14 22:49 ` [PATCH 13/38] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
2016-06-14 22:49 ` [PATCH 14/38] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
2016-06-14 22:49 ` [PATCH 15/38] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
2016-06-14 22:49 ` [PATCH 16/38] gpu: ipu-v3: rename CSI client device Steve Longerbeam
2016-06-14 22:49 ` [PATCH 17/38] ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node Steve Longerbeam
2016-06-14 22:49 ` [PATCH 18/38] ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections Steve Longerbeam
2016-06-14 22:49 ` [PATCH 19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections Steve Longerbeam
2016-06-16  8:32   ` [19/38] " Gary Bisson
2016-06-17 15:18     ` Gary Bisson
2016-06-17 16:09       ` Jack Mitchell
2016-06-17 19:00       ` tchellRe: " Steve Longerbeam
2016-06-18  9:05         ` Hans Verkuil
2016-06-17 19:01       ` Steve Longerbeam
2016-06-20  9:33         ` Gary Bisson
2016-06-20  9:44           ` Jack Mitchell
2016-06-20 10:16             ` Gary Bisson
2016-06-20 10:44               ` Jack Mitchell
2016-06-21 17:17                 ` Steve Longerbeam
2016-06-14 22:49 ` [PATCH 20/38] ARM: dts: imx6-sabresd: " Steve Longerbeam
2016-06-14 22:49 ` [PATCH 21/38] ARM: dts: imx6-sabreauto: create i2cmux for i2c3 Steve Longerbeam
2016-06-14 22:49 ` [PATCH 22/38] ARM: dts: imx6-sabreauto: add reset-gpios property for max7310 Steve Longerbeam
2016-06-14 22:49 ` [PATCH 23/38] ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture Steve Longerbeam
2016-06-14 22:49 ` [PATCH 24/38] ARM: dts: imx6-sabreauto: add video capture ports and connections Steve Longerbeam
2016-06-14 22:49 ` [PATCH 25/38] ARM: dts: imx6qdl: add mem2mem device for sabre* boards Steve Longerbeam
2016-06-14 22:49 ` [PATCH 26/38] gpio: pca953x: Add reset-gpios property Steve Longerbeam
2016-06-14 22:49 ` [PATCH 27/38] clocksource/drivers/imx: add input capture support Steve Longerbeam
2016-06-14 22:49 ` [PATCH 28/38] v4l: Add signal lock status to source change events Steve Longerbeam
2016-07-01  7:24   ` Hans Verkuil
2016-07-02  3:59     ` Steve Longerbeam
2016-06-14 22:49 ` [PATCH 29/38] media: Add camera interface driver for i.MX5/6 Steve Longerbeam
2016-06-14 22:49 ` [PATCH 30/38] media: imx: Add MIPI CSI-2 Receiver driver Steve Longerbeam
2016-06-14 22:49 ` [PATCH 31/38] media: imx: Add video switch Steve Longerbeam
2016-06-16 16:13   ` Ian Arkver
2016-06-17 20:14     ` Steve Longerbeam
2016-06-14 22:49 ` [PATCH 32/38] media: imx: Add support for MIPI CSI-2 OV5640 Steve Longerbeam
2016-06-14 22:49 ` [PATCH 33/38] media: imx: Add support for Parallel OV5642 Steve Longerbeam
2016-06-14 22:49 ` [PATCH 34/38] media: imx: Add support for ADV7180 Video Decoder Steve Longerbeam
2016-06-16 11:33   ` Lars-Peter Clausen
2016-06-17 20:33     ` Steve Longerbeam
2016-06-14 22:49 ` [PATCH 35/38] media: adv7180: add power pin control Steve Longerbeam
2016-06-15 16:05   ` Lars-Peter Clausen
2016-06-16  1:34     ` Steve Longerbeam
2016-06-14 22:49 ` [PATCH 36/38] media: adv7180: implement g_parm Steve Longerbeam
2016-06-14 22:49 ` [PATCH 37/38] media: Add i.MX5/6 mem2mem driver Steve Longerbeam
2016-06-14 22:49 ` [PATCH 38/38] ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers Steve Longerbeam
2016-06-15 10:43 ` [PATCH 00/38] i.MX5/6 Video Capture Jack Mitchell
2016-06-15 10:47   ` Hans Verkuil
2016-06-16  1:37   ` Steve Longerbeam
2016-06-16  9:49     ` Jack Mitchell
2016-06-16 17:02       ` Steve Longerbeam
2016-06-16 17:43         ` Nicolas Dufresne
2016-06-16 17:43         ` Nicolas Dufresne
2016-06-17  7:10         ` Hans Verkuil
2016-06-17 17:35           ` Steve Longerbeam
2016-06-18  9:01             ` Hans Verkuil
2016-06-28 18:54 ` Tim Harvey
2016-06-28 20:10   ` Hans Verkuil
2016-07-01  7:19     ` Hans Verkuil
2016-07-02  3:38   ` Steve Longerbeam
2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
2016-07-06 23:06   ` [PATCH 01/28] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
2016-07-06 23:06   ` [PATCH 02/28] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 03/28] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 04/28] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 05/28] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
2016-07-06 23:06   ` [PATCH 06/28] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 07/28] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
2016-07-06 23:06   ` [PATCH 08/28] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 09/28] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 10/28] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
2016-07-06 23:06   ` [PATCH 11/28] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
2016-07-06 23:06   ` [PATCH 12/28] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
2016-07-06 23:06   ` [PATCH 13/28] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
2016-07-06 23:06   ` [PATCH 14/28] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
2016-07-13 18:58     ` Mauro Carvalho Chehab
2016-07-13 19:06       ` Mauro Carvalho Chehab
2016-07-13 22:24       ` Steve Longerbeam
2016-07-06 23:06   ` [PATCH 15/28] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
2016-07-06 23:06   ` [PATCH 16/28] gpu: ipu-v3: rename CSI client device Steve Longerbeam
2016-07-06 23:06   ` [PATCH 17/28] gpio: pca953x: Add optional reset gpio control Steve Longerbeam
2016-07-06 23:06   ` Steve Longerbeam [this message]
2016-07-06 23:06   ` [PATCH 19/28] media: Add i.MX5/6 camera interface driver Steve Longerbeam
2016-07-06 23:06   ` [PATCH 20/28] media: imx: Add MIPI CSI-2 Receiver driver Steve Longerbeam
2016-07-06 23:06   ` [PATCH 21/28] media: imx: Add video switch Steve Longerbeam
2016-07-06 23:06   ` [PATCH 22/28] media: imx: Add support for MIPI CSI-2 OV5640 Steve Longerbeam
2016-07-06 23:06   ` [PATCH 23/28] media: imx: Add support for Parallel OV5642 Steve Longerbeam
2016-07-06 23:06   ` [PATCH 24/28] media: Add i.MX5/6 mem2mem driver Steve Longerbeam
2016-07-06 23:06   ` [PATCH 25/28] ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node Steve Longerbeam
2016-07-06 23:06   ` [PATCH 26/28] ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections Steve Longerbeam
2016-07-06 23:11   ` [PATCH 27/28] ARM: dts: imx6qdl: add mem2mem devices Steve Longerbeam
2016-07-06 23:11     ` [PATCH 28/28] ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers Steve Longerbeam
2016-07-07 13:31   ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Tim Harvey

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=1467846418-12913-19-git-send-email-steve_longerbeam@mentor.com \
    --to=slongerbeam@gmail.com \
    --cc=linux-media@vger.kernel.org \
    --cc=steve_longerbeam@mentor.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.