From: Steve Longerbeam <slongerbeam@xxxxxxxxx> The input capture API is modelled after request/free irq: typedef void (*mxc_icap_handler_t)(int, void *, ktime_t); 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 ktime_t marking the input capture event. - 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 <slongerbeam@xxxxxxxxx> --- drivers/clocksource/timer-imx-gpt.c | 479 +++++++++++++++++++++++++--- include/linux/mxc_icap.h | 16 + 2 files changed, 445 insertions(+), 50 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 7b2c70f2f353..c6aa1462e441 100644 --- a/drivers/clocksource/timer-imx-gpt.c +++ b/drivers/clocksource/timer-imx-gpt.c @@ -5,9 +5,11 @@ // Copyright (C) 2006-2007 Pavel Pisa (ppisa@xxxxxxxxxx) // Copyright (C) 2008 Juergen Beisert (kernel@xxxxxxxxxxxxxx) +#include <linux/module.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/clockchips.h> +#include <linux/timecounter.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> @@ -16,6 +18,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> /* @@ -49,16 +53,52 @@ #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 cyclecounter cc; + struct timecounter tc; +}; + +/* 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; @@ -73,12 +113,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)(struct imx_timer *imxtm, 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)(struct icap_channel *ic, 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) @@ -86,52 +134,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(struct imx_timer *imxtm, unsigned int tstat) +{ + return true; +} + +static bool imx21_gpt_is_oc_irq(struct imx_timer *imxtm, unsigned int tstat) +{ + return (tstat & MX2_TSTAT_COMP) != 0; +} + +static bool imx31_gpt_is_oc_irq(struct imx_timer *imxtm, 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(struct icap_channel *ic, unsigned int tstat) +{ + return false; +} +#define imx21_gpt_is_ic_irq imx1_gpt_is_ic_irq + +static bool imx31_gpt_is_ic_irq(struct icap_channel *ic, unsigned int tstat) +{ + return (tstat & ic->status_bit) != 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; @@ -149,6 +289,19 @@ static unsigned long imx_read_current_timer(void) } #endif +static u64 mxc_clocksource_read(struct clocksource *cs) +{ + return mxc_read_sched_clock(); +} + +static struct clocksource clocksource_mxc = { + .name = "mxc_timer1", + .rating = 200, + .mask = CLOCKSOURCE_MASK(32), + .read = mxc_clocksource_read, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + static int __init mxc_clocksource_init(struct imx_timer *imxtm) { unsigned int c = clk_get_rate(imxtm->clk_per); @@ -163,8 +316,7 @@ static int __init mxc_clocksource_init(struct imx_timer *imxtm) sched_clock_reg = reg; sched_clock_register(mxc_read_sched_clock, 32, c); - return clocksource_mmio_init(reg, "mxc_timer1", c, 200, 32, - clocksource_mmio_readl_up); + return clocksource_register_hz(&clocksource_mxc, c); } /* clock event */ @@ -204,14 +356,14 @@ static int mxc_shutdown(struct clock_event_device *ced) u32 tcn; /* 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__); @@ -225,7 +377,7 @@ static int mxc_set_oneshot(struct clock_event_device *ced) struct imx_timer *imxtm = to_imx_timer(ced); /* 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); @@ -233,7 +385,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 @@ -246,7 +398,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); return 0; } @@ -259,12 +411,29 @@ static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id) struct clock_event_device *ced = dev_id; struct imx_timer *imxtm = to_imx_timer(ced); uint32_t tstat; + int i; tstat = readl_relaxed(imxtm->base + imxtm->gpt->reg_tstat); - imxtm->gpt->gpt_irq_acknowledge(imxtm); + for (i = 0; i < 2; i++) { + struct icap_channel *ic = &icap_channel[i]; + ktime_t timestamp; - ced->event_handler(ced); + if (!imxtm->gpt->gpt_is_ic_irq(ic, tstat)) + continue; + + imxtm->gpt->gpt_ic_irq_acknowledge(ic); + + timestamp = ns_to_ktime(timecounter_read(&ic->tc)); + + if (ic->handler) + ic->handler(ic->chan, ic->dev_id, timestamp); + } + + if (imxtm->gpt->gpt_is_oc_irq(imxtm, tstat)) { + imxtm->gpt->gpt_oc_irq_acknowledge(imxtm); + ced->event_handler(ced); + } return IRQ_HANDLED; } @@ -289,16 +458,16 @@ static int __init mxc_clockevent_init(struct imx_timer *imxtm) IRQF_TIMER | IRQF_IRQPOLL, "i.MX Timer Tick", ced); } -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; @@ -311,7 +480,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; @@ -332,10 +501,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, }; @@ -343,10 +514,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, }; @@ -354,27 +527,160 @@ 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, }; +static u64 gpt_ic_read(const struct cyclecounter *cc) +{ + struct icap_channel *ic = container_of(cc, struct icap_channel, cc); + struct imx_timer *imxtm = ic->imxtm; + + return readl_relaxed(imxtm->base + ic->cnt_reg); +} + +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; + u64 start_cycles; + 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 the cyclecounter. The input capture is capturing + * from the mxc clocksource, so it has the same mask/shift/mult. + */ + memset(&ic->cc, 0, sizeof(ic->cc)); + ic->cc.read = gpt_ic_read; + ic->cc.mask = clocksource_mxc.mask; + ic->cc.shift = clocksource_mxc.shift; + ic->cc.mult = clocksource_mxc.mult; + + /* initialize a timecounter for the input capture */ + start_cycles = mxc_read_sched_clock(); + timecounter_init(&ic->tc, &ic->cc, ktime_get_ns()); + /* + * timecounter_init() read the last captured timer count, but + * that's not the start cycle counter, so update it with the + * real start cycles. + */ + ic->tc.cycle_last = start_cycles; + + 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 int __init _mxc_timer_init(struct imx_timer *imxtm) { - int ret; + struct icap_channel *ic; + int i, ret; switch (imxtm->type) { case GPT_TYPE_IMX1: @@ -410,14 +716,23 @@ static int __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 */ ret = mxc_clocksource_init(imxtm); if (ret) return ret; - return mxc_clockevent_init(imxtm); + ret = mxc_clockevent_init(imxtm); + if (ret) + return ret; + + for (i = 0; i < 2; i++) { + ic = &icap_channel[i]; + ic->imxtm = imxtm; + } + + return 0; } void __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type) @@ -439,6 +754,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 int __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 000000000000..fa5ffdf3b589 --- /dev/null +++ b/include/linux/mxc_icap.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * i.MX GPT Input Capture support. + * + * Copyright (C) 2015 Mentor Graphics, Inc. All Rights Reserved. + */ +#ifndef __MXC_ICAP_H__ +#define __MXC_ICAP_H__ + +typedef void (*mxc_icap_handler_t)(int, void *, ktime_t); + +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 -- 2.25.1