From patchwork Tue Nov 29 16:14:52 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Kochetkov X-Patchwork-Id: 9452643 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 ED2DE6071E for ; Tue, 29 Nov 2016 16:19:09 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E022F283EC for ; Tue, 29 Nov 2016 16:19:09 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D4B7A283FE; Tue, 29 Nov 2016 16:19:09 +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=-4.1 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,FREEMAIL_FROM,RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 70D6D283EC for ; Tue, 29 Nov 2016 16:19:07 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1cBl63-0002LO-ID; Tue, 29 Nov 2016 16:17:31 +0000 Received: from mail-lf0-x241.google.com ([2a00:1450:4010:c07::241]) by bombadil.infradead.org with esmtps (Exim 4.85_2 #1 (Red Hat Linux)) id 1cBl4L-0001Sy-9y; Tue, 29 Nov 2016 16:15:49 +0000 Received: by mail-lf0-x241.google.com with SMTP id p100so13145707lfg.2; Tue, 29 Nov 2016 08:15:28 -0800 (PST) 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=MqIrS61HbgI88Sf+RSbh/V9RI+RhmpGqQqKVUIlq/uo=; b=N6OF8kJcS9u0w0wL17LSd4QhysMoM4bRZM4WpHNYySCIpgtNLQmhAS2VvSEpnPO8bT rI4qphiPS8mQkx8zPBv0PG/o03CI5bcftj4DfoZ3nd+wB45oiIiPPxRwo6D3wDIMiODt hSXnXe0QCbW8ABGI2i9h+LqUTYA2pBPDDK/YN3uV/7JrIkFezvRZAoLEe0lurKtGis+n pJiwxminaODALN1tbM+asAhWVqCnZQsl/uOJmIR2LaqKrX7BWv63RTpIYieDC/p7+KNv GgE661aAIE6CX7ev3OZ2nDVhJryj3E2hdajUXuKtOtjTepkaY7FbiNywvwihiMhR9unE IXTg== 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=MqIrS61HbgI88Sf+RSbh/V9RI+RhmpGqQqKVUIlq/uo=; b=dmc2YYCV62n/dt8aovOzgFhkONu0PnAdcU4p8SjSw91Tuoi65v59xBZMe3p03octP+ uWwjwkg2Mcfl66/Mqw2uIILErsf3TNZryvl2xCT/W300cnp6A5jiond+aDH64c1G8JlT chWq+z5nUqSWiWkN+Fs4ss0FsNLqoDcqxdRTWjdkjJlwNpGK2Nl3QTUspK4ACUe97jHQ 4101ACdUKTSguBaleVGXVfe8pqioBrBCPFSHHDS1iG+aTMug+9uurBr2HlkiZb+t109w U8WI+iktdJr2VRXm1+mwqcm9rQmTFdcJdVHg4ggQpsLPSkiOfmq/If28a3ACZ5nM/Kg0 BfAw== X-Gm-Message-State: AKaTC03iecJL1DK9wji6H/qXaxmW1ARRwDsdyhY9uX8CVulOU6yr0wzhfgpp78BgyDkjsw== X-Received: by 10.46.69.11 with SMTP id s11mr13872099lja.44.1480436127267; Tue, 29 Nov 2016 08:15:27 -0800 (PST) Received: from ubuntu.lintech.local ([185.35.119.87]) by smtp.gmail.com with ESMTPSA id e78sm13771566lfg.23.2016.11.29.08.15.26 (version=TLS1_1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 29 Nov 2016 08:15:26 -0800 (PST) From: Alexander Kochetkov To: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org Subject: [PATCH v4 9/9] clocksource/drivers/rockchip_timer: implement clocksource timer Date: Tue, 29 Nov 2016 19:14:52 +0300 Message-Id: <1480436092-10728-10-git-send-email-al.kochet@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1480436092-10728-1-git-send-email-al.kochet@gmail.com> References: <1480343486-25539-1-git-send-email-al.kochet@gmail.com> <1480436092-10728-1-git-send-email-al.kochet@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20161129_081545_941580_AA83F610 X-CRM114-Status: GOOD ( 20.01 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , Huang Tao , Heiko Stuebner , Alexander Kochetkov , Daniel Lezcano , Russell King , Rob Herring , Thomas Gleixner , Caesar Wang MIME-Version: 1.0 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 --- 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..a127822 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 cycle_t 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;