From patchwork Wed Jul 6 23:06:48 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steve Longerbeam X-Patchwork-Id: 9217379 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 3BD5B607D9 for ; Wed, 6 Jul 2016 23:09:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2A31528480 for ; Wed, 6 Jul 2016 23:09:41 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1E9A828520; Wed, 6 Jul 2016 23:09:41 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9F2CB28480 for ; Wed, 6 Jul 2016 23:09:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932930AbcGFXJg (ORCPT ); Wed, 6 Jul 2016 19:09:36 -0400 Received: from mail-pf0-f194.google.com ([209.85.192.194]:34284 "EHLO mail-pf0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932606AbcGFXHm (ORCPT ); Wed, 6 Jul 2016 19:07:42 -0400 Received: by mail-pf0-f194.google.com with SMTP id 66so101938pfy.1 for ; Wed, 06 Jul 2016 16:07:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=ZTJ5WS6NVNPONFCwF3TnA3BtE3iR5ohQUxEq5FPjFkk=; b=AKrk/06CsPe9rFvXEm5Rra9dTaodWwyDWdaBOg2L8bul/Cq8M19lHwv+wR5idsZmxh KcwN60Pux6wYYY4XdTCDeNCMUy2v+Wzzw/TCCKX9bufb2b062C9D67C0XxATxyBfRzC2 crgeB08pSC1AwwKRG1U2Ua7HGb5RppmRmveySLin4D8e6C55vq1MXgQwjjsLx+shmLDY tPIRFsvmSVsm/blgkibQy650vCmUyOJmTXl4uEeky49LCCakc1NyLT5USlXJxOek7d/9 ptmpixHo0Jc9EFu5XoD7W0jKmvfEB/Lckyx7l4Q7aNRPJMyFfQA7keD2hwkwClnPQ98D Kkmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=ZTJ5WS6NVNPONFCwF3TnA3BtE3iR5ohQUxEq5FPjFkk=; b=cxmDTP1ecUN4yfXcV2y88sYGsknQcEWITe2cylC47uoTF+Y0MuWgp3xbZA5zhpT/lI +AnDFok6ZieffXcxsT0Mi88aZ9qV4W94patVWMqwPC+OE5hHa/EoWK9f/qPJGLtaFwCB BNYyG7MeODHImgwlgHZrPoYdR8/34OJbwNZQJDCPYywz6WX73TwVj6dFILZCaIlxJxi4 lZej9396fSUsmF4WA3nCpGILtZS/i/bKLe/AZR5Ei0YxdwxD4dqSJKOCT/sY9rqB5Xf0 5HbXxrDJPTRUta+NlzjyuTORKLmJetJeYROb+aDemggpmDPd62URfoFdcoOUpan9X09N lsyQ== X-Gm-Message-State: ALyK8tKZkwZqz9jmHv9G0mSqEvmXM//zIIlZdjW4kx+fBwqQXjEwyAvY1zAxIY6bRv9Eug== X-Received: by 10.98.192.135 with SMTP id g7mr46634739pfk.64.1467846460664; Wed, 06 Jul 2016 16:07:40 -0700 (PDT) Received: from mothership.sklembedded.com (c-73-241-2-21.hsd1.ca.comcast.net. [73.241.2.21]) by smtp.googlemail.com with ESMTPSA id c189sm6724125pfg.19.2016.07.06.16.07.39 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 06 Jul 2016 16:07:40 -0700 (PDT) From: Steve Longerbeam X-Google-Original-From: Steve Longerbeam To: linux-media@vger.kernel.org Cc: Steve Longerbeam Subject: [PATCH 18/28] clocksource/drivers/imx: add input capture support Date: Wed, 6 Jul 2016 16:06:48 -0700 Message-Id: <1467846418-12913-19-git-send-email-steve_longerbeam@mentor.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1467846418-12913-1-git-send-email-steve_longerbeam@mentor.com> References: <1465944574-15745-1-git-send-email-steve_longerbeam@mentor.com> <1467846418-12913-1-git-send-email-steve_longerbeam@mentor.com> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 #include #include #include @@ -32,6 +33,8 @@ #include #include #include +#include +#include #include /* @@ -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