From patchwork Wed Mar 22 15:48:32 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Kochetkov X-Patchwork-Id: 9639261 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 E820460328 for ; Wed, 22 Mar 2017 15:49:33 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D784E28451 for ; Wed, 22 Mar 2017 15:49:33 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CC65B28460; Wed, 22 Mar 2017 15:49:33 +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.9 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,DKIM_VALID,FREEMAIL_FROM autolearn=ham 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 15EBD2845C for ; Wed, 22 Mar 2017 15:49:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=VW/NtTOHq+4ICp0VP2xOkXOEc97hrBe9J6HKNdY3Gz8=; b=pB4iuKmjAAN0EK5mPESG+AgfNL 9si8GAofAfr/tpwxY/rG30hCSexLSwjmBUO4kJ5mUE8+PDIDV0f1tzSgGledoUpfiKR9lPThonpXS HRtl9tZ8S2jqUZQfCPH/SU6TCS9XCv+aUatYZRY6kG680AJTWSbIcap2RYzBin6i9tQLou9loELb0 3wUh88YRgAD/6tStP+I3lkDtDmsgr4w+1h2HhD21ANo71wGqoyiknJW4CkdNgkR26AQzN32eUq4a5 4f1qr6EZnrHcMqb48V0RJqA+/OUwBE7DnRFB8Bw2/mKgsj+1drZThpiW2ftnNcfGSDX2McA+4Pnef zVkUWZsQ==; 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 1cqiVw-00018P-In; Wed, 22 Mar 2017 15:49:32 +0000 Received: from mail-wm0-x244.google.com ([2a00:1450:400c:c09::244]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1cqiVT-0000ed-52; Wed, 22 Mar 2017 15:49:10 +0000 Received: by mail-wm0-x244.google.com with SMTP id x124so10796662wmf.3; Wed, 22 Mar 2017 08:48:46 -0700 (PDT) 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; bh=TP5fOvZgFsGNt2/F8tpdhS0ReIDPjoXOwfV1mweNo+U=; b=tLTDwssG1XsckrEYffBKFOlqfQynSTwko/98lOszyMRZqqMN5LTs+uQXLFSmJMr6Fl sttNfo9arM7pnlasWe/kV9o+qIGolP6+5rCKhecFDwEsVsAiZmKkUgWV+vh8um8KSiuJ XMIglDknfCqhtXthg1Zs1wnfhS8dB7UU0ED1JSiq2QY5WvAKV7wYK829IUvO9IcXk6Zv 5c+BYQHKkbZGG1y4qzHqcTWE/jYrFRK7xlsXUdTN8tG/dlGQ818RHcy7fHubqQXIRgKD nskaU1X+qbplPRSFe1bj8SRqNknxmzq8Dx5tmPKcL80zh4rkQUfEmYe3K7yhTDwrmxgz t2kg== 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; bh=TP5fOvZgFsGNt2/F8tpdhS0ReIDPjoXOwfV1mweNo+U=; b=t3GEnNE80lanD6AlyaIAwVgRX2xYbv5HJsEDjMh3fBsiL39YC4SwqetFD1dThgXuSS TZTZmieCVZX25JbGFVS7XfgTD7hDzcboSokGR1Jdk+XyU5mIY9Iges/aH4uv4/Jm/ffM PZhU6x07aL84qim42W4HS8ALiv5lO8e4nLoUSI5NUziwaoL3fODp+uVGak3dIPR7eS/v tJwe/Op2arVgvR2XP3V19sDyGMiPa09Ta5BuuMbtSxeY7/hYI45jNZWTLXWLafH/eUBO wqygO73EBmEUcuKsrO1jM9R7dHf+W1TzabzK1ePt2nwiIWf4OBkVEkStPIVTWaCwNwDp dxyQ== X-Gm-Message-State: AFeK/H35ztqNV01rQTY9MKoVdqoNMgBSHVVRTPVwKZD+BhpEyVdle6InAw2/UKPhhi54BA== X-Received: by 10.25.145.77 with SMTP id y13mr10635481lfj.49.1490197724956; Wed, 22 Mar 2017 08:48:44 -0700 (PDT) Received: from ubuntu.lintech.local ([185.35.119.87]) by smtp.gmail.com with ESMTPSA id p27sm591898lfg.5.2017.03.22.08.48.43 (version=TLS1_1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 22 Mar 2017 08:48:44 -0700 (PDT) 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 v7 5/7] clocksource/drivers/rockchip_timer: implement clocksource timer Date: Wed, 22 Mar 2017 18:48:32 +0300 Message-Id: <1490197714-25415-6-git-send-email-al.kochet@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1490197714-25415-1-git-send-email-al.kochet@gmail.com> References: <1490197714-25415-1-git-send-email-al.kochet@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170322_084903_609603_4985AC9E X-CRM114-Status: GOOD ( 22.88 ) 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 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. But new warnings like 'Failed to initialize /timer@....' will appear with old device tree files. To resolve them 'clockevent' attribute should be added to the timer. rk3288 (and probably anything newer) is irrelevant to this patch, as it has the arch timer interface. This patch may be useful for Cortex-A9/A5 based parts. Signed-off-by: Alexander Kochetkov --- arch/arm/mach-rockchip/rockchip.c | 2 + drivers/clocksource/Kconfig | 2 + drivers/clocksource/rockchip_timer.c | 215 ++++++++++++++++++++++++---------- 3 files changed, 156 insertions(+), 63 deletions(-) diff --git a/arch/arm/mach-rockchip/rockchip.c b/arch/arm/mach-rockchip/rockchip.c index a7ab9ec..5184f7d 100644 --- a/arch/arm/mach-rockchip/rockchip.c +++ b/arch/arm/mach-rockchip/rockchip.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,7 @@ static void __init rockchip_timer_init(void) } of_clk_init(NULL); + clockevent_probe(); clocksource_probe(); } diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 21f84ea..5e0df76 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -71,6 +71,8 @@ config ROCKCHIP_TIMER bool "Rockchip timer driver" if COMPILE_TEST depends on ARM || ARM64 select CLKSRC_OF + select CLKEVT_OF + select CLKSRC_MMIO help Enables the support for the rockchip timer driver. diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c index 23e267a..0ac71bb 100644 --- a/drivers/clocksource/rockchip_timer.c +++ b/drivers/clocksource/rockchip_timer.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -19,6 +21,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 @@ -29,103 +33,118 @@ #define TIMER_MODE_USER_DEFINED_COUNT (1 << 1) #define TIMER_INT_UNMASK (1 << 2) -struct bc_timer { - struct clock_event_device ce; +struct rk_timer { void __iomem *base; void __iomem *ctrl; + struct clk *clk; + struct clk *pclk; u32 freq; + int irq; }; -static struct bc_timer bc_timer; +struct rk_clkevt { + struct clock_event_device ce; + struct rk_timer timer; +}; -static inline struct bc_timer *rk_timer(struct clock_event_device *ce) -{ - return container_of(ce, struct bc_timer, ce); -} +/* global instance for rk_timer_sched_read() */ +static struct rk_timer *rk_clksrc; -static inline void __iomem *rk_base(struct clock_event_device *ce) +static inline struct rk_timer *rk_timer(struct clock_event_device *ce) { - return rk_timer(ce)->base; + return &container_of(ce, struct rk_clkevt, ce)->timer; } -static inline void __iomem *rk_ctrl(struct clock_event_device *ce) +static inline void rk_timer_disable(struct rk_timer *timer) { - return rk_timer(ce)->ctrl; + writel_relaxed(TIMER_DISABLE, timer->ctrl); } -static inline void rk_timer_disable(struct clock_event_device *ce) +static inline void rk_timer_enable(struct rk_timer *timer, u32 flags) { - writel_relaxed(TIMER_DISABLE, rk_ctrl(ce)); -} - -static inline void rk_timer_enable(struct clock_event_device *ce, u32 flags) -{ - writel_relaxed(TIMER_ENABLE | TIMER_INT_UNMASK | flags, - rk_ctrl(ce)); + writel_relaxed(TIMER_ENABLE | flags, timer->ctrl); } static void rk_timer_update_counter(unsigned long cycles, - struct clock_event_device *ce) + struct rk_timer *timer) { - writel_relaxed(cycles, rk_base(ce) + TIMER_LOAD_COUNT0); - writel_relaxed(0, rk_base(ce) + TIMER_LOAD_COUNT1); + writel_relaxed(cycles, timer->base + TIMER_LOAD_COUNT0); + writel_relaxed(0, timer->base + TIMER_LOAD_COUNT1); } -static void rk_timer_interrupt_clear(struct clock_event_device *ce) +static void rk_timer_interrupt_clear(struct rk_timer *timer) { - writel_relaxed(1, rk_base(ce) + TIMER_INT_STATUS); + writel_relaxed(1, timer->base + TIMER_INT_STATUS); } static inline int rk_timer_set_next_event(unsigned long cycles, struct clock_event_device *ce) { - rk_timer_disable(ce); - rk_timer_update_counter(cycles, ce); - rk_timer_enable(ce, TIMER_MODE_USER_DEFINED_COUNT); + struct rk_timer *timer = rk_timer(ce); + + rk_timer_disable(timer); + rk_timer_update_counter(cycles, timer); + rk_timer_enable(timer, TIMER_MODE_USER_DEFINED_COUNT | + TIMER_INT_UNMASK); return 0; } static int rk_timer_shutdown(struct clock_event_device *ce) { - rk_timer_disable(ce); + struct rk_timer *timer = rk_timer(ce); + + rk_timer_disable(timer); return 0; } static int rk_timer_set_periodic(struct clock_event_device *ce) { - rk_timer_disable(ce); - rk_timer_update_counter(rk_timer(ce)->freq / HZ - 1, ce); - rk_timer_enable(ce, TIMER_MODE_FREE_RUNNING); + struct rk_timer *timer = rk_timer(ce); + + rk_timer_disable(timer); + rk_timer_update_counter(timer->freq / HZ - 1, timer); + rk_timer_enable(timer, TIMER_MODE_FREE_RUNNING | TIMER_INT_UNMASK); return 0; } static irqreturn_t rk_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *ce = dev_id; + struct rk_timer *timer = rk_timer(ce); - rk_timer_interrupt_clear(ce); + rk_timer_interrupt_clear(timer); if (clockevent_state_oneshot(ce)) - rk_timer_disable(ce); + rk_timer_disable(timer); ce->event_handler(ce); return IRQ_HANDLED; } -static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg) +static u64 notrace rk_timer_sched_read(void) +{ + return ~readl_relaxed(rk_clksrc->base + TIMER_CURRENT_VALUE0); +} + +static int __init +rk_timer_probe(struct rk_timer *timer, struct device_node *np) { - struct clock_event_device *ce = &bc_timer.ce; struct clk *timer_clk; struct clk *pclk; int ret = -EINVAL, irq; + u32 ctrl_reg = TIMER_CONTROL_REG3288; - bc_timer.base = of_iomap(np, 0); - if (!bc_timer.base) { + timer->base = of_iomap(np, 0); + if (!timer->base) { pr_err("Failed to get base address for '%s'\n", TIMER_NAME); return -ENXIO; } - bc_timer.ctrl = bc_timer.base + ctrl_reg; + + if (of_device_is_compatible(np, "rockchip,rk3399-timer")) + ctrl_reg = TIMER_CONTROL_REG3399; + + timer->ctrl = timer->base + ctrl_reg; pclk = of_clk_get_by_name(np, "pclk"); if (IS_ERR(pclk)) { @@ -139,6 +158,7 @@ static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg) pr_err("Failed to enable pclk for '%s'\n", TIMER_NAME); goto out_unmap; } + timer->pclk = pclk; timer_clk = of_clk_get_by_name(np, "timer"); if (IS_ERR(timer_clk)) { @@ -152,8 +172,9 @@ static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg) pr_err("Failed to enable timer clock\n"); goto out_timer_clk; } + timer->clk = timer_clk; - bc_timer.freq = clk_get_rate(timer_clk); + timer->freq = clk_get_rate(timer_clk); irq = irq_of_parse_and_map(np, 0); if (!irq) { @@ -161,51 +182,119 @@ static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg) pr_err("Failed to map interrupts for '%s'\n", TIMER_NAME); goto out_irq; } + timer->irq = irq; + rk_timer_interrupt_clear(timer); + rk_timer_disable(timer); + return 0; + +out_irq: + clk_disable_unprepare(timer_clk); +out_timer_clk: + clk_disable_unprepare(pclk); +out_unmap: + iounmap(timer->base); + + return ret; +} + +static void __init rk_timer_cleanup(struct rk_timer *timer) +{ + clk_disable_unprepare(timer->clk); + clk_disable_unprepare(timer->pclk); + iounmap(timer->base); +} + +static int __init rk_clkevt_init(struct device_node *np) +{ + struct rk_clkevt *clkevt; + struct clock_event_device *ce; + int ret = -EINVAL; + + clkevt = kzalloc(sizeof(struct rk_clkevt), GFP_KERNEL); + if (!clkevt) + return -ENOMEM; + + ret = rk_timer_probe(&clkevt->timer, np); + if (ret) + goto out_probe; + + ce = &clkevt->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->irq = clkevt->timer.irq; ce->cpumask = cpu_possible_mask; ce->rating = 250; - rk_timer_interrupt_clear(ce); - rk_timer_disable(ce); - - ret = request_irq(irq, rk_timer_interrupt, IRQF_TIMER, TIMER_NAME, ce); + ret = request_irq(clkevt->timer.irq, rk_timer_interrupt, IRQF_TIMER, + TIMER_NAME, ce); if (ret) { - pr_err("Failed to initialize '%s': %d\n", TIMER_NAME, ret); + pr_err("Failed to initialize '%s': %d\n", + TIMER_NAME, ret); goto out_irq; } - clockevents_config_and_register(ce, bc_timer.freq, 1, UINT_MAX); - + clockevents_config_and_register(&clkevt->ce, + clkevt->timer.freq, 1, UINT_MAX); return 0; out_irq: - clk_disable_unprepare(timer_clk); -out_timer_clk: - clk_disable_unprepare(pclk); -out_unmap: - iounmap(bc_timer.base); - + rk_timer_cleanup(&clkevt->timer); +out_probe: + kfree(clkevt); return ret; } -static int __init rk3288_timer_init(struct device_node *np) +static int __init rk_clksrc_init(struct device_node *np) { - return rk_timer_init(np, TIMER_CONTROL_REG3288); -} + struct rk_timer *clksrc; + int ret = -EINVAL; + + /* + * Old DT-files has timer entries without 'clocksource' property + * and that timer assumed to be a clockevent, so skip them. + */ + if (!of_property_read_bool(np, "clocksource")) + return -EINVAL; + + clksrc = kzalloc(sizeof(struct rk_timer), GFP_KERNEL); + if (!clksrc) + return -ENOMEM; + + ret = rk_timer_probe(clksrc, np); + if (ret) + goto out_probe; + + rk_timer_update_counter(UINT_MAX, clksrc); + rk_timer_enable(clksrc, 0); + + ret = clocksource_mmio_init(clksrc->base + TIMER_CURRENT_VALUE0, + TIMER_NAME, clksrc->freq, 250, 32, + clocksource_mmio_readl_down); + if (ret) { + pr_err("Failed to register clocksource"); + goto out_clocksource; + } -static int __init rk3399_timer_init(struct device_node *np) -{ - return rk_timer_init(np, TIMER_CONTROL_REG3399); + if (!rk_clksrc) { + rk_clksrc = clksrc; + sched_clock_register(rk_timer_sched_read, 32, rk_clksrc->freq); + } + return 0; + +out_clocksource: + rk_timer_cleanup(clksrc); +out_probe: + kfree(clksrc); + return ret; } -CLOCKSOURCE_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer", - rk3288_timer_init); -CLOCKSOURCE_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer", - rk3399_timer_init); +CLOCKEVENT_OF_DECLARE(rk3288_clkevt, "rockchip,rk3288-timer", rk_clkevt_init); +CLOCKEVENT_OF_DECLARE(rk3399_clkevt, "rockchip,rk3399-timer", rk_clkevt_init); + +CLOCKSOURCE_OF_DECLARE(rk3288_clksrc, "rockchip,rk3288-timer", rk_clksrc_init); +CLOCKSOURCE_OF_DECLARE(rk3399_clksrc, "rockchip,rk3399-timer", rk_clksrc_init);