From patchwork Tue Jan 24 12:16:43 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Alexander Kochetkov X-Patchwork-Id: 9535119 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 979C06042C for ; Tue, 24 Jan 2017 12:22:32 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8771827D4A for ; Tue, 24 Jan 2017 12:22:32 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7BB30279E0; Tue, 24 Jan 2017 12:22:32 +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=-1.3 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=no version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 0F581279E0 for ; Tue, 24 Jan 2017 12:22:32 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1cW07K-0003jC-0d; Tue, 24 Jan 2017 12:22:30 +0000 Received: from mail-lf0-f68.google.com ([209.85.215.68]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1cW03J-0007dA-J7; Tue, 24 Jan 2017 12:18:23 +0000 Received: by mail-lf0-f68.google.com with SMTP id h65so16956688lfi.3; Tue, 24 Jan 2017 04:18:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=hZZxqDv+j3/fCIDy+hPB+WIJv3CLji6RHY/V8NHaIPU=; b=Kt7Vl2gtz1e7sg/je3CRxYGqshe5IumZC54Frp15HBLBaHVF5wnBIUmgh6mmvmyKVr xIcH5TjGcNBjteiQkaOUWXgXiRTePxn0NBXq9OgSi7GITtsnKEk7xBoDPQXhHzD4EB4Q V4fY+v1w8lyaMJYoIMi07dZ6clPr+12Ntlnx03BjvGBYMpfSnauOptZs1MbP7IZ1pYmg MYEZYXHkz0X4haU7y2vdVJVS+za7OQWlZ4+ein+ViGB3+/D7I/jJMCnMhZ0mER8gD4Wa bu7o5GL30/tEKn7DsgkgDa4DgR+GHhmqTCPAEvz46ZTVzmr4Zu1kIGMDQdzjitGNWjsK 4h1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=hZZxqDv+j3/fCIDy+hPB+WIJv3CLji6RHY/V8NHaIPU=; b=jWwdu3N+zTObgHvLVoK5ms5BRGhosWfeliB112fydezvvPI+7bqm5WIz+5qzwG92tc QxQysRi5UBctIELvNsNkqoi1XD9RW5fIMYI+y4UwBaNyCK5ZUsuLCMK7xqdfDaQ87R/+ ITIe/uSzgCDEbQ1j/M8DY8054IVAH0/1IBG4Ye5IvSEIqYPamo8GvwCLGx4D0sUhYv2I 2KFdYLNETZyT/Wphlwh0oRUw3j+DHH6RBLQ0Zii/38hPGy868+CM25ONODSIgb8sJzZX x4HVmZGx4+NBU8+RmTvwQ8Ekmdg9bwnYQ9EF+/mNTDJ/ZxkAP99ix74KebDdquLv5F+W bTXw== X-Gm-Message-State: AIkVDXJDJKc0t9kAVrA4mYk3iy/voq87cKqOtUJaYxlfcaYQLmnjJJSgNEJpSyzEwNdeuQ== X-Received: by 10.25.33.21 with SMTP id h21mr9765893lfh.114.1485260219929; Tue, 24 Jan 2017 04:16:59 -0800 (PST) Received: from ubuntu.lintech.local ([185.35.119.87]) by smtp.gmail.com with ESMTPSA id e86sm7348934lji.32.2017.01.24.04.16.58 (version=TLS1_1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 24 Jan 2017 04:16:59 -0800 (PST) From: Alexander Kochetkov To: Daniel Lezcano , Heiko Stuebner , linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org Subject: [PATCH v5 8/8] clocksource/drivers/rockchip_timer: implement clocksource timer Date: Tue, 24 Jan 2017 15:16:43 +0300 Message-Id: <1485260203-14216-9-git-send-email-al.kochet@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1485260203-14216-1-git-send-email-al.kochet@gmail.com> References: <1485260203-14216-1-git-send-email-al.kochet@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170124_041821_837348_8858AF55 X-CRM114-Status: GOOD ( 20.06 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , Huang Tao , Alexander Kochetkov , Russell King , Rob Herring , Thomas Gleixner , Caesar Wang Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP The clock supplying the arm-global-timer on the rk3188 is coming from the the cpu clock itself and thus changes its rate everytime cpufreq adjusts the cpu frequency making this timer unsuitable as a stable clocksource and sched clock. The rk3188, rk3288 and following socs share a separate timer block already handled by the rockchip-timer driver. Therefore adapt this driver to also be able to act as clocksource and sched clock on rk3188. In order to test clocksource you can run following commands and check how much time it take in real. On rk3188 it take about ~45 seconds. cpufreq-set -f 1.6GHZ date; sleep 60; date In order to use the patch you need to declare two timers in the dts file. The first timer will be initialized as clockevent provider and the second one as clocksource. The clockevent must be from alive subsystem as it used as backup for the local timers at sleep time. The patch does not break compatibility with older device tree files. The older device tree files contain only one timer. The timer will be initialized as clockevent, as expected. rk3288 (and probably anything newer) is irrelevant to this patch, as it has the arch timer interface. This patch may be usefull for Cortex-A9/A5 based parts. Signed-off-by: Alexander Kochetkov Reviwed-by: Heiko Stübner --- drivers/clocksource/rockchip_timer.c | 137 +++++++++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 20 deletions(-) diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c index 61c3bb1..3ff533c 100644 --- a/drivers/clocksource/rockchip_timer.c +++ b/drivers/clocksource/rockchip_timer.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,8 @@ #define TIMER_LOAD_COUNT0 0x00 #define TIMER_LOAD_COUNT1 0x04 +#define TIMER_CURRENT_VALUE0 0x08 +#define TIMER_CURRENT_VALUE1 0x0C #define TIMER_CONTROL_REG3288 0x10 #define TIMER_CONTROL_REG3399 0x1c #define TIMER_INT_STATUS 0x18 @@ -40,7 +43,19 @@ struct rk_clock_event_device { struct rk_timer timer; }; +struct rk_clocksource { + struct clocksource cs; + struct rk_timer timer; +}; + +enum { + ROCKCHIP_CLKSRC_CLOCKEVENT = 0, + ROCKCHIP_CLKSRC_CLOCKSOURCE = 1, +}; + static struct rk_clock_event_device bc_timer; +static struct rk_clocksource cs_timer; +static int rk_next_clksrc = ROCKCHIP_CLKSRC_CLOCKEVENT; static inline struct rk_clock_event_device* rk_clock_event_device(struct clock_event_device *ce) @@ -63,11 +78,37 @@ static inline void rk_timer_enable(struct rk_timer *timer, u32 flags) writel_relaxed(TIMER_ENABLE | flags, timer->ctrl); } -static void rk_timer_update_counter(unsigned long cycles, - struct rk_timer *timer) +static void rk_timer_update_counter(u64 cycles, struct rk_timer *timer) +{ + u32 lower = cycles & 0xFFFFFFFF; + u32 upper = (cycles >> 32) & 0xFFFFFFFF; + + writel_relaxed(lower, timer->base + TIMER_LOAD_COUNT0); + writel_relaxed(upper, timer->base + TIMER_LOAD_COUNT1); +} + +static u64 notrace _rk_timer_counter_read(struct rk_timer *timer) { - writel_relaxed(cycles, timer->base + TIMER_LOAD_COUNT0); - writel_relaxed(0, timer->base + TIMER_LOAD_COUNT1); + u64 counter; + u32 lower; + u32 upper, old_upper; + + upper = readl_relaxed(timer->base + TIMER_CURRENT_VALUE1); + do { + old_upper = upper; + lower = readl_relaxed(timer->base + TIMER_CURRENT_VALUE0); + upper = readl_relaxed(timer->base + TIMER_CURRENT_VALUE1); + } while (upper != old_upper); + + counter = upper; + counter <<= 32; + counter |= lower; + return counter; +} + +static u64 rk_timer_counter_read(struct rk_timer *timer) +{ + return _rk_timer_counter_read(timer); } static void rk_timer_interrupt_clear(struct rk_timer *timer) @@ -120,13 +161,46 @@ static irqreturn_t rk_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static u64 rk_timer_clocksource_read(struct clocksource *cs) +{ + struct rk_clocksource *_cs = + container_of(cs, struct rk_clocksource, cs); + + return ~rk_timer_counter_read(&_cs->timer); +} + +static u64 notrace rk_timer_sched_clock_read(void) +{ + struct rk_clocksource *_cs = &cs_timer; + + return ~_rk_timer_counter_read(&_cs->timer); +} + static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg) { - struct clock_event_device *ce = &bc_timer.ce; - struct rk_timer *timer = &bc_timer.timer; + struct clock_event_device *ce = NULL; + struct clocksource *cs = NULL; + struct rk_timer *timer = NULL; struct clk *timer_clk; struct clk *pclk; int ret = -EINVAL, irq; + int clksrc; + + clksrc = rk_next_clksrc; + rk_next_clksrc++; + + switch (clksrc) { + case ROCKCHIP_CLKSRC_CLOCKEVENT: + ce = &bc_timer.ce; + timer = &bc_timer.timer; + break; + case ROCKCHIP_CLKSRC_CLOCKSOURCE: + cs = &cs_timer.cs; + timer = &cs_timer.timer; + break; + default: + return -ENODEV; + } timer->base = of_iomap(np, 0); if (!timer->base) { @@ -170,26 +244,49 @@ static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg) goto out_irq; } - ce->name = TIMER_NAME; - ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | - CLOCK_EVT_FEAT_DYNIRQ; - ce->set_next_event = rk_timer_set_next_event; - ce->set_state_shutdown = rk_timer_shutdown; - ce->set_state_periodic = rk_timer_set_periodic; - ce->irq = irq; - ce->cpumask = cpu_possible_mask; - ce->rating = 250; + if (ce) { + ce->name = TIMER_NAME; + ce->features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_DYNIRQ; + ce->set_next_event = rk_timer_set_next_event; + ce->set_state_shutdown = rk_timer_shutdown; + ce->set_state_periodic = rk_timer_set_periodic; + ce->irq = irq; + ce->cpumask = cpu_possible_mask; + ce->rating = 250; + } + + if (cs) { + cs->name = TIMER_NAME; + cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; + cs->mask = CLOCKSOURCE_MASK(64); + cs->read = rk_timer_clocksource_read; + cs->rating = 250; + } rk_timer_interrupt_clear(timer); rk_timer_disable(timer); - ret = request_irq(irq, rk_timer_interrupt, IRQF_TIMER, TIMER_NAME, ce); - if (ret) { - pr_err("Failed to initialize '%s': %d\n", TIMER_NAME, ret); - goto out_irq; + if (ce) { + ret = request_irq(irq, rk_timer_interrupt, IRQF_TIMER, + TIMER_NAME, ce); + if (ret) { + pr_err("Failed to initialize '%s': %d\n", + TIMER_NAME, ret); + goto out_irq; + } + + clockevents_config_and_register(ce, timer->freq, 1, UINT_MAX); } - clockevents_config_and_register(ce, timer->freq, 1, UINT_MAX); + if (cs) { + rk_timer_update_counter(U64_MAX, timer); + rk_timer_enable(timer, 0); + clocksource_register_hz(cs, timer->freq); + sched_clock_register(rk_timer_sched_clock_read, 64, + timer->freq); + } return 0;