From patchwork Sun Aug 17 10:49:50 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Carlo Caione X-Patchwork-Id: 4730961 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 8C1569F377 for ; Sun, 17 Aug 2014 10:54:48 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 7B8B820115 for ; Sun, 17 Aug 2014 10:54:47 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (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 7A559200E5 for ; Sun, 17 Aug 2014 10:54:46 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1XIy3I-0002xb-EE; Sun, 17 Aug 2014 10:51:08 +0000 Received: from mail-wg0-x22b.google.com ([2a00:1450:400c:c00::22b]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XIy33-0002qg-Gt for linux-arm-kernel@lists.infradead.org; Sun, 17 Aug 2014 10:50:54 +0000 Received: by mail-wg0-f43.google.com with SMTP id l18so3825537wgh.2 for ; Sun, 17 Aug 2014 03:50:31 -0700 (PDT) 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=p5CJAoRCnh+NWyqXvrL7Nlg5lCaS/VIWUj++fW9xkw0=; b=jmz8FBoCVNi44iG/UV4T1WZlkY3MApw1xP84KKT0aoKzS0/zYp8d33UZLbtrY5hFnD oeWDW2xAo9Kb/8OEdJaOyP1lQt2QPIoqBrGwlOj/DJt/ds//OjfBlkF1dEzykoW1zvwD NGYwMDjvNV99tyYqR4990lqCqhh/TrXG/D7QD+z5bzAMkUNGVKhYVAu3WRp8soHHkqKe 52h6r6v9rW/4fHg9LhEs9VBN7CwgY2lTiWbluxC+ovvnCvFxPuMTFASVVuUKvYL8OiwB c37Ux9TL4nTLjBVXr8diAI3DuCzfwkFoZICkVGb3mpSe1ML1u4fEcKY+nEHUgM/3NRUa kH9A== X-Received: by 10.194.187.4 with SMTP id fo4mr34015565wjc.35.1408272631333; Sun, 17 Aug 2014 03:50:31 -0700 (PDT) Received: from localhost.localdomain (host20-19-dynamic.47-79-r.retail.telecomitalia.it. [79.47.19.20]) by mx.google.com with ESMTPSA id jo10sm18523539wjc.41.2014.08.17.03.50.28 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 17 Aug 2014 03:50:30 -0700 (PDT) From: Carlo Caione To: linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, linux-serial@vger.kernel.org, linux@arm.linux.org.uk, robh+dt@kernel.org, mark.rutland@arm.com, ijc+devicetree@hellion.org.uk, daniel.lezcano@linaro.org, tglx@linutronix.de, gregkh@linuxfoundation.org, jslaby@suse.cz, grant.likely@linaro.org, b.galvani@gmail.com Subject: [PATCH 3/7] ARM: meson6: clocksource: add Meson6 timer support Date: Sun, 17 Aug 2014 12:49:50 +0200 Message-Id: <1408272594-10814-4-git-send-email-carlo@caione.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1408272594-10814-1-git-send-email-carlo@caione.org> References: <1408272594-10814-1-git-send-email-carlo@caione.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140817_035053_861504_3D1D142D X-CRM114-Status: GOOD ( 17.18 ) X-Spam-Score: -0.7 (/) Cc: Carlo Caione X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 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=-2.5 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_NONE,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 Meson6 SoCs are equipped with 5 32-bit timers, called TIMER_A, TIMER_B, TIMER_C, TIMER_D and TIMER_E. The driver is providing clocksource support for the 32-bit counter using TIMER_E. Clockevents are also supported using TIMER_A. Signed-off-by: Carlo Caione --- drivers/clocksource/Kconfig | 3 + drivers/clocksource/Makefile | 1 + drivers/clocksource/meson6_timer.c | 187 +++++++++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 drivers/clocksource/meson6_timer.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index cfd6519..38029ca 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -30,6 +30,9 @@ config ARMADA_370_XP_TIMER bool select CLKSRC_OF +config MESON6_TIMER + bool + config ORION_TIMER select CLKSRC_OF select CLKSRC_MMIO diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 7fd9fd1..e4ae987 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o obj-$(CONFIG_ARCH_U300) += timer-u300.o obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o +obj-$(CONFIG_MESON6_TIMER) += meson6_timer.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o diff --git a/drivers/clocksource/meson6_timer.c b/drivers/clocksource/meson6_timer.c new file mode 100644 index 0000000..1ef1095 --- /dev/null +++ b/drivers/clocksource/meson6_timer.c @@ -0,0 +1,187 @@ +/* + * Amlogic Meson6 SoCs timer handling. + * + * Copyright (C) 2014 Carlo Caione + * + * Carlo Caione + * + * Based on code from Amlogic, Inc + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + A = 0, + B, + C, + D, +}; + +#define TIMER_ISA_MUX 0 +#define TIMER_ISA_E_VAL 0x14 +#define TIMER_ISA_t_VAL(t) ((t + 1) << 2) + +#define TIMER_t_INPUT_BIT(t) (2 * t) +#define TIMER_E_INPUT_BIT 8 +#define TIMER_t_INPUT_MASK(t) (3UL << TIMER_t_INPUT_BIT(t)) +#define TIMER_E_INPUT_MASK (7UL << TIMER_E_INPUT_BIT) +#define TIMER_t_ENABLE_BIT(t) (16 + t) +#define TIMER_E_ENABLE_BIT 20 +#define TIMER_t_PERIODIC_BIT(t) (12 + t) + +#define TIMER_UNIT_1us 0 +#define TIMER_E_UNIT_1us 1 + +static void __iomem *timer_base; + +static cycle_t cycle_read_timer_e(struct clocksource *cs) +{ + return (cycle_t)readl(timer_base + TIMER_ISA_E_VAL); +} + +static struct clocksource clocksource_timer_e = { + .name = "meson6_timerE", + .rating = 300, + .read = cycle_read_timer_e, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static u64 notrace meson6_timer_sched_read(void) +{ + return (u64)readl(timer_base + TIMER_ISA_E_VAL); +} + +static void meson6_clkevt_time_stop(unsigned char timer) +{ + u32 val = readl(timer_base + TIMER_ISA_MUX); + + writel(val & ~TIMER_t_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX); +} + +static void meson6_clkevt_time_setup(unsigned char timer, unsigned long delay) +{ + writel(delay, timer_base + TIMER_ISA_t_VAL(timer)); +} + +static void meson6_clkevt_time_start(unsigned char timer, bool periodic) +{ + u32 val = readl(timer_base + TIMER_ISA_MUX); + + if (periodic) + val |= TIMER_t_PERIODIC_BIT(timer); + else + val &= ~TIMER_t_PERIODIC_BIT(timer); + + writel(val | TIMER_t_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX); +} + +static void meson6_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + meson6_clkevt_time_stop(A); + meson6_clkevt_time_setup(A, USEC_PER_SEC/HZ - 1); + meson6_clkevt_time_start(A, true); + break; + case CLOCK_EVT_MODE_ONESHOT: + meson6_clkevt_time_stop(A); + meson6_clkevt_time_start(A, false); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + meson6_clkevt_time_stop(A); + break; + } +} + +static int meson6_clkevt_next_event(unsigned long evt, + struct clock_event_device *unused) +{ + meson6_clkevt_time_stop(A); + meson6_clkevt_time_setup(A, evt); + meson6_clkevt_time_start(A, false); + + return 0; +} + +static struct clock_event_device meson6_clockevent = { + .name = "meson6_tick", + .rating = 400, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = meson6_clkevt_mode, + .set_next_event = meson6_clkevt_next_event, +}; + +static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = (struct clock_event_device *)dev_id; + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction meson6_timer_irq = { + .name = "meson6_timerA", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = meson6_timer_interrupt, + .dev_id = &meson6_clockevent, +}; + +static void __init meson6_timer_init(struct device_node *node) +{ + u32 val; + int ret, irq; + + timer_base = of_iomap(node, 0); + if (!timer_base) + panic("Can't map registers"); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) + panic("Can't parse IRQ"); + + /* Set 1us for timer E */ + val = readl(timer_base + TIMER_ISA_MUX); + val &= ~TIMER_E_INPUT_MASK; + val |= TIMER_E_UNIT_1us << TIMER_E_INPUT_BIT; + writel(val, timer_base + TIMER_ISA_MUX); + + sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC); + clocksource_register_khz(&clocksource_timer_e, 1000); + + /* Timer A base 1us */ + val &= ~TIMER_t_INPUT_MASK(A); + val |= TIMER_UNIT_1us << TIMER_t_INPUT_BIT(A); + writel(val, timer_base + TIMER_ISA_MUX); + + /* Stop the timer A */ + meson6_clkevt_time_stop(A); + + ret = setup_irq(irq, &meson6_timer_irq); + if (ret) + pr_warn("failed to setup irq %d\n", irq); + + meson6_clockevent.cpumask = cpu_possible_mask; + meson6_clockevent.irq = irq; + + clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC, + 1, 0xfffe); +} +CLOCKSOURCE_OF_DECLARE(meson6, "amlogic,meson6-timer", + meson6_timer_init);