From patchwork Fri Nov 8 21:21:16 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soren Brinkmann X-Patchwork-Id: 3159941 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 4FE01C045B for ; Fri, 8 Nov 2013 21:25:40 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 3DFE120460 for ; Fri, 8 Nov 2013 21:25:39 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 55D612047B for ; Fri, 8 Nov 2013 21:25:37 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VetWo-0000EK-Ie; Fri, 08 Nov 2013 21:23:44 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VetWK-0006Dd-Mv; Fri, 08 Nov 2013 21:23:12 +0000 Received: from mail-pa0-x22a.google.com ([2607:f8b0:400e:c03::22a]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VetV9-0005xv-Kn for linux-arm-kernel@lists.infradead.org; Fri, 08 Nov 2013 21:22:02 +0000 Received: by mail-pa0-f42.google.com with SMTP id kp14so2778999pab.1 for ; Fri, 08 Nov 2013 13:21:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=WWucV9xn7k2s2qCpOk+MklaJieD3dwt5g4EfkltPynI=; b=aNkPQjwhQIgjk/Ro0aV0AYeHWBGPYSe16OM28QTeDhtvCQ5WhOBoBh7SMywx/Lpvy2 WtbDG2FlpyIbxil0nQlpHrft17mKCdDYTPc+JCF6WAsGgyXD/l9SsZlMp/ckdAnrnm4P Elj4kWABfFglmFL61hUOec+kLe6x51hgXe6V8OkEvQg6VzIQ/FA/8JIrlWKoLCvgC5Nu bFFTUnsyMq/0/aNQ+8tYrGmk7hUIsNlS4HRtE5EV32hDQf4Hakfb0DkukGYKrxaJv0FS RP9gQStiC390bUYqS6TqyBLNSAuk1Gi3HuTP5EztHxoNuQ8DS8cqxYWFFGSDx22/Ys10 cVXQ== X-Received: by 10.66.149.231 with SMTP id ud7mr18190665pab.8.1383945697778; Fri, 08 Nov 2013 13:21:37 -0800 (PST) Received: from localhost ([149.199.62.254]) by mx.google.com with ESMTPSA id 7sm16647431paf.22.2013.11.08.13.21.36 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Fri, 08 Nov 2013 13:21:36 -0800 (PST) From: Soren Brinkmann To: Rob Herring , Pawel Moll , Mark Rutland , Stephen Warren , Ian Campbell , Russell King , Michal Simek , Daniel Lezcano , Thomas Gleixner Subject: [PATCH 6/7] clocksource/cadence_ttc: Use only one counter Date: Fri, 8 Nov 2013 13:21:16 -0800 Message-Id: <1383945677-29674-7-git-send-email-soren.brinkmann@xilinx.com> X-Mailer: git-send-email 1.8.4.2 In-Reply-To: <1383945677-29674-1-git-send-email-soren.brinkmann@xilinx.com> References: <1383945677-29674-1-git-send-email-soren.brinkmann@xilinx.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20131108_162159_953673_48A7D996 X-CRM114-Status: GOOD ( 19.94 ) X-Spam-Score: -1.9 (-) Cc: Soren Brinkmann , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Currently the driver uses two of the three counters the TTC provides to implement a clocksource and a clockevent device. By using the TTC's match feature we can implement both use cases using a single counter only. The old approach is to use timer over-/underflow to generate an interrupt. Using the match register allows to generate an interrupt on arbitrary counter values. This way a dedicated clockevent counter can be avoided. Signed-off-by: Soren Brinkmann --- drivers/clocksource/cadence_ttc_timer.c | 92 +++++++++++---------------------- 1 file changed, 31 insertions(+), 61 deletions(-) diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c index 421b942dd707..01dba97cee84 100644 --- a/drivers/clocksource/cadence_ttc_timer.c +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -48,6 +48,7 @@ #define TTC_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */ #define TTC_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */ #define TTC_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */ +#define TTC_MATCH1_OFFSET 0x30 /* Match reg, RW */ #define TTC_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */ #define TTC_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */ @@ -65,7 +66,10 @@ #define PRESCALE 2048 /* The exponent must match this */ #define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1) #define CLK_CNTRL_PRESCALE_EN 1 -#define CNT_CNTRL_RESET (1 << 4) +#define CNT_CNTRL_RESET BIT(4) +#define CNT_CNTRL_MATCH BIT(3) + +#define TTC_INTERRUPT_MATCH1 BIT(1) #define MAX_F_ERR 50 @@ -103,6 +107,7 @@ struct ttc_timer_clocksource { struct ttc_timer_clockevent { struct ttc_timer ttc; struct clock_event_device ce; + unsigned long interval; }; #define to_ttc_timer_clkevent(x) \ @@ -116,25 +121,20 @@ static void __iomem *ttc_sched_clock_val_reg; * @timer: Pointer to the timer instance * @cycles: Timer interval ticks **/ -static void ttc_set_interval(struct ttc_timer *timer, - unsigned long cycles) +static void ttc_set_interval(struct ttc_timer *timer, unsigned long cycles) { - u32 ctrl_reg; + struct ttc_timer_clockevent *ttcce = container_of(timer, + struct ttc_timer_clockevent, ttc); - /* Disable the counter, set the counter value and re-enable counter */ - ctrl_reg = __raw_readl(timer->base_addr + TTC_CNT_CNTRL_OFFSET); - ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET); + /* set interval */ + u32 reg = __raw_readl(timer->base_addr + TTC_COUNT_VAL_OFFSET); + reg += cycles; + __raw_writel(reg, timer->base_addr + TTC_MATCH1_OFFSET); - __raw_writel(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET); + /* enable match interrupt */ + __raw_writel(TTC_INTERRUPT_MATCH1, timer->base_addr + TTC_IER_OFFSET); - /* - * Reset the counter (0x10) so that it starts from 0, one-shot - * mode makes this needed for timing to be right. - */ - ctrl_reg |= CNT_CNTRL_RESET; - ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET); + ttcce->interval = cycles; } /** @@ -152,6 +152,8 @@ static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id) /* Acknowledge the interrupt and call event handler */ __raw_readl(timer->base_addr + TTC_ISR_OFFSET); + if (ttce->ce.mode == CLOCK_EVT_MODE_PERIODIC) + ttc_set_interval(timer, ttce->interval); ttce->ce.event_handler(&ttce->ce); @@ -205,7 +207,6 @@ static void ttc_set_mode(enum clock_event_mode mode, { struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt); struct ttc_timer *timer = &ttce->ttc; - u32 ctrl_reg; switch (mode) { case CLOCK_EVT_MODE_PERIODIC: @@ -215,18 +216,9 @@ static void ttc_set_mode(enum clock_event_mode mode, case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: - ctrl_reg = __raw_readl(timer->base_addr + - TTC_CNT_CNTRL_OFFSET); - ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, - timer->base_addr + TTC_CNT_CNTRL_OFFSET); + __raw_writel(0, timer->base_addr + TTC_IER_OFFSET); break; case CLOCK_EVT_MODE_RESUME: - ctrl_reg = __raw_readl(timer->base_addr + - TTC_CNT_CNTRL_OFFSET); - ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, - timer->base_addr + TTC_CNT_CNTRL_OFFSET); break; } } @@ -392,17 +384,6 @@ static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base) ttccs->cs.mask = CLOCKSOURCE_MASK(16); ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; - /* - * Setup the clock source counter to be an incrementing counter - * with no interrupt and it rolls over at 0xFFFF. Pre-scale - * it by 32 also. Let it start running now. - */ - __raw_writel(0x0, ttccs->ttc.base_addr + TTC_IER_OFFSET); - __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, - ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET); - __raw_writel(CNT_CNTRL_RESET, - ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET); - err = clocksource_register_hz(&ttccs->cs, ttccs->ttc.freq / PRESCALE); if (WARN_ON(err)) { kfree(ttccs); @@ -488,16 +469,6 @@ static void __init ttc_setup_clockevent(struct clk *clk, ttcce->ce.irq = irq; ttcce->ce.cpumask = cpu_possible_mask; - /* - * Setup the clock event timer to be an interval timer which - * is prescaled by 32 using the interval interrupt. Leave it - * disabled for now. - */ - __raw_writel(0x23, ttcce->ttc.base_addr + TTC_CNT_CNTRL_OFFSET); - __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, - ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET); - __raw_writel(0x1, ttcce->ttc.base_addr + TTC_IER_OFFSET); - err = request_irq(irq, ttc_clock_event_interrupt, IRQF_DISABLED | IRQF_TIMER, ttcce->ce.name, ttcce); @@ -520,7 +491,7 @@ static void __init ttc_timer_init(struct device_node *timer) { unsigned int irq; void __iomem *timer_baseaddr; - struct clk *clk_cs, *clk_ce; + struct clk *clk; static int initialized; int clksel; @@ -540,7 +511,7 @@ static void __init ttc_timer_init(struct device_node *timer) BUG(); } - irq = irq_of_parse_and_map(timer, 1); + irq = irq_of_parse_and_map(timer, 0); if (irq <= 0) { pr_err("ERROR: invalid interrupt number\n"); BUG(); @@ -548,22 +519,21 @@ static void __init ttc_timer_init(struct device_node *timer) clksel = __raw_readl(timer_baseaddr + TTC_CLK_CNTRL_OFFSET); clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK); - clk_cs = of_clk_get(timer, clksel); - if (IS_ERR(clk_cs)) { + clk = of_clk_get(timer, clksel); + if (IS_ERR(clk)) { pr_err("ERROR: timer input clock not found\n"); BUG(); } - clksel = __raw_readl(timer_baseaddr + 4 + TTC_CLK_CNTRL_OFFSET); - clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK); - clk_ce = of_clk_get(timer, clksel); - if (IS_ERR(clk_ce)) { - pr_err("ERROR: timer input clock not found\n"); - BUG(); - } + __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, + timer_baseaddr + TTC_CLK_CNTRL_OFFSET); + + /* start timer in overflow and match mode */ + __raw_writel(CNT_CNTRL_RESET | CNT_CNTRL_MATCH, + timer_baseaddr + TTC_CNT_CNTRL_OFFSET); - ttc_setup_clocksource(clk_cs, timer_baseaddr); - ttc_setup_clockevent(clk_ce, timer_baseaddr + 4, irq); + ttc_setup_clocksource(clk, timer_baseaddr); + ttc_setup_clockevent(clk, timer_baseaddr, irq); pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq); }