From patchwork Mon Feb 3 22:34:35 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soren Brinkmann X-Patchwork-Id: 3572231 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 C47E3C02DC for ; Mon, 3 Feb 2014 22:38:36 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C3F842015E for ; Mon, 3 Feb 2014 22:38:35 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 9A05120158 for ; Mon, 3 Feb 2014 22:38:34 +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 1WAS7v-0008JD-NM; Mon, 03 Feb 2014 22:36:30 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WAS7P-0004ut-6K; Mon, 03 Feb 2014 22:35:55 +0000 Received: from mail-qa0-x229.google.com ([2607:f8b0:400d:c00::229]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WAS6r-0004nb-GU for linux-arm-kernel@lists.infradead.org; Mon, 03 Feb 2014 22:35:24 +0000 Received: by mail-qa0-f41.google.com with SMTP id w8so11274909qac.0 for ; Mon, 03 Feb 2014 14:35:00 -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=7vlTmFExN3QCUFxA++ozf3ibjoF7tMfoZnJFTFVYQq4=; b=AbLsNSd73ksnk0rXMp8YacUK6JRuw/YJlPqfmex6kQokIymCivQwkpVKps3H4yfVrh wid/1tnDSHwR5BTuzi8h08vnzD1kx5Y7XXcLd3D2wXd30oqazGt25qWVlW+DLhOa7qeG J+xlGL3NEuRDck3OA560ueHoVX+YtjnV+P/RNkQles9XWsGWAUwCquVBHjG5WpswAjNI cZSQzy8TS5ef0NmLuDXMhyq1mloN2rqemrC9GW33ie/KghAkk1DFzjqVohwoANzHYryr +PZsIA54hsiqunoIAJ5wDXRKhGQhQ5jEIbjUM155/bVsJaA662cIocYvtvitSjjeG8W0 iDOw== X-Received: by 10.229.35.194 with SMTP id q2mr60320778qcd.7.1391466900136; Mon, 03 Feb 2014 14:35:00 -0800 (PST) Received: from localhost ([149.199.62.254]) by mx.google.com with ESMTPSA id 30sm29291084qgt.4.2014.02.03.14.34.58 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Mon, 03 Feb 2014 14:34:59 -0800 (PST) From: Soren Brinkmann To: Thomas Gleixner , Daniel Lezcano , Michal Simek Subject: [PATCH v3 5/7] clocksource/cadence_ttc: Use only one counter Date: Mon, 3 Feb 2014 14:34:35 -0800 Message-Id: <1391466877-28908-6-git-send-email-soren.brinkmann@xilinx.com> X-Mailer: git-send-email 1.8.5.3 In-Reply-To: <1391466877-28908-1-git-send-email-soren.brinkmann@xilinx.com> References: <1391466877-28908-1-git-send-email-soren.brinkmann@xilinx.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140203_173521_755646_2026EEE9 X-CRM114-Status: GOOD ( 18.96 ) X-Spam-Score: -1.9 (-) Cc: =?UTF-8?q?S=C3=B6ren=20Brinkmann?= , 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.6 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 49fbe2847c84..d922e982af95 100644 --- a/drivers/clocksource/cadence_ttc_timer.c +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -47,6 +47,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 */ @@ -64,7 +65,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 @@ -99,6 +103,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) \ @@ -112,25 +117,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; } /** @@ -148,6 +148,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); @@ -201,7 +203,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: @@ -211,18 +212,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; } } @@ -354,17 +346,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); @@ -433,16 +414,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_TIMER, ttcce->ce.name, ttcce); if (WARN_ON(err)) { @@ -464,7 +435,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; @@ -484,7 +455,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(); @@ -492,22 +463,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); }