From patchwork Sat May 25 11:08:05 2013
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Daniel Tang
X-Patchwork-Id: 2613211
Return-Path:
X-Original-To: patchwork-linux-arm@patchwork.kernel.org
Delivered-To: patchwork-process-083081@patchwork1.kernel.org
Received: from casper.infradead.org (casper.infradead.org [85.118.1.10])
by patchwork1.kernel.org (Postfix) with ESMTP id 49FA03FE81
for ;
Sat, 25 May 2013 11:12:28 +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 1UgCMx-0003d0-NS; Sat, 25 May 2013 11:10:42 +0000
Received: from localhost ([::1] helo=merlin.infradead.org)
by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux))
id 1UgCMJ-0007YF-Aq; Sat, 25 May 2013 11:09:59 +0000
Received: from mail-pb0-x235.google.com ([2607:f8b0:400e:c01::235])
by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux))
id 1UgCMB-0007W2-OL for linux-arm-kernel@lists.infradead.org;
Sat, 25 May 2013 11:09:54 +0000
Received: by mail-pb0-f53.google.com with SMTP id un4so5127011pbc.26
for ;
Sat, 25 May 2013 04:09:30 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113;
h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references;
bh=RyZ5rRMFkmCIzqjW1PU3CSpkcq0CdpYhcjsNW4PhDhs=;
b=QOcsNKwUcn49Id1dWyMfGyqGe3WPSFRA7PtuaIlvlbmYOXsMAUFf5MZ92IVlC4u5dB
tkBecyi9PLanZ5LBTLAfICVNQJh4Tm8LRWr+W6BS5uFZFE+pxPsNEqWAE1dELTRFETNT
edpoIoMRH35oUcYuYVMrd704g/rQaQLsGVcXT5t3yAI8pAA1CScfVhRt2T4DDSaWqfxT
qt26sVhxIh+Jwt8ecMBNDqKdr1eqwCdNfqD8sX3RJeC5qdBp3aRIxP9v5WOL4QkN8dtm
sWDnLs1KlqqmYS3ra9PB0OfC9KYYnvCg1Jzl/O5ZI1oSEMoH/2rzqIVIZHCXmOKeiTbR
GE7Q==
X-Received: by 10.66.100.231 with SMTP id fb7mr21560132pab.162.1369480170270;
Sat, 25 May 2013 04:09:30 -0700 (PDT)
Received: from tangrs.lan (110-175-69-66.static.tpgi.com.au. [110.175.69.66])
by mx.google.com with ESMTPSA id
v5sm20151586pbz.4.2013.05.25.04.09.23 for
(version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128);
Sat, 25 May 2013 04:09:29 -0700 (PDT)
From: Daniel Tang
To: linux-arm-kernel@lists.infradead.org,
"linux@arm.linux.org.uk ARM Linux"
Subject: [RFC PATCHv4 4/6] clocksource: TI-Nspire timer support
Date: Sat, 25 May 2013 21:08:05 +1000
Message-Id: <1369480087-24786-5-git-send-email-dt.tangr@gmail.com>
X-Mailer: git-send-email 1.8.1.3
In-Reply-To: <1369480087-24786-1-git-send-email-dt.tangr@gmail.com>
References: <1369480087-24786-1-git-send-email-dt.tangr@gmail.com>
X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3
X-CRM114-CacheID: sfid-20130525_070952_028639_3D42CEA1
X-CRM114-Status: GOOD ( 21.08 )
X-Spam-Score: -2.0 (--)
X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary:
Content analysis details: (-2.0 points)
pts rule name description
---- ----------------------
--------------------------------------------------
0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail
provider (dt.tangr[at]gmail.com)
-0.0 SPF_PASS SPF: sender matches SPF record
-1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1%
[score: 0.0000]
-0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from
author's domain
0.1 DKIM_SIGNED Message has a DKIM or DK signature,
not necessarily valid
-0.1 DKIM_VALID Message has at least one valid DKIM or DK signature
Cc: Arnd Bergmann , Linus Walleij ,
linux-kernel@vger.kernel.org,
"fabian@ritter-vogt.de Vogt" ,
Daniel Tang , John Stultz ,
Thomas Gleixner ,
Lionel Debroux
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
Add clocksource and clockevent driver for the TI-Nspire.
Signed-off-by: Daniel Tang
Reviewed-by: Linus Walleij
---
.../devicetree/bindings/timer/lsi,zevio-timer.txt | 33 +++
drivers/clocksource/Makefile | 1 +
drivers/clocksource/zevio-timer.c | 231 +++++++++++++++++++++
3 files changed, 265 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/lsi,zevio-timer.txt
create mode 100644 drivers/clocksource/zevio-timer.c
--
1.8.1.3
diff --git a/Documentation/devicetree/bindings/timer/lsi,zevio-timer.txt b/Documentation/devicetree/bindings/timer/lsi,zevio-timer.txt
new file mode 100644
index 0000000..b2d07ad
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/lsi,zevio-timer.txt
@@ -0,0 +1,33 @@
+TI-NSPIRE timer
+
+Required properties:
+
+- compatible : should be "lsi,zevio-timer".
+- reg : The physical base address and size of the timer (always first).
+- clocks: phandle to the source clock.
+
+Optional properties:
+
+- interrupts : The interrupt number of the first timer.
+- reg : The interrupt acknowledgement registers
+ (always after timer base address)
+
+If any of the optional properties are not given, the timer is added as a
+clock-source only.
+
+Example:
+
+timer {
+ compatible = "lsi,zevio-timer";
+ reg = <0x900D0000 0x1000>, <0x900A0020 0x8>;
+ interrupts = <19>;
+ clocks = <&timer_clk>;
+};
+
+Example (no clock-events):
+
+timer {
+ compatible = "lsi,zevio-timer";
+ reg = <0x900D0000 0x1000>;
+ clocks = <&timer_clk>;
+};
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 8d979c7..293f86e 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o
obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o
obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o
obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
+obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o
obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o
obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
diff --git a/drivers/clocksource/zevio-timer.c b/drivers/clocksource/zevio-timer.c
new file mode 100644
index 0000000..c6d7f03
--- /dev/null
+++ b/drivers/clocksource/zevio-timer.c
@@ -0,0 +1,231 @@
+/*
+ * linux/drivers/clocksource/zevio-timer.c
+ *
+ * Copyright (C) 2013 Daniel Tang
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define IO_CURRENT_VAL 0x00
+#define IO_DIVIDER 0x04
+#define IO_CONTROL 0x08
+
+#define IO_TIMER1 0x00
+#define IO_TIMER2 0x0C
+
+#define IO_MATCH_BEGIN 0x18
+#define IO_MATCH(x) (IO_MATCH_BEGIN + ((x)<<2))
+
+#define IO_INTR_STS 0x00
+#define IO_INTR_ACK 0x00
+#define IO_INTR_MSK 0x04
+
+#define CNTL_STOP_TIMER (1<<4)
+#define CNTL_RUN_TIMER (0<<4)
+
+#define CNTL_INC (1<<3)
+#define CNTL_DEC (0<<3)
+
+#define CNTL_TOZERO 0
+#define CNTL_MATCH(x) ((x) + 1)
+#define CNTL_FOREVER 7
+
+/* There are 6 match registers but we only use one. */
+#define TIMER_MATCH 0
+
+#define TIMER_INTR_MSK (1<<(TIMER_MATCH))
+#define TIMER_INTR_ALL 0x3F
+
+struct zevio_timer {
+ void __iomem *base;
+ void __iomem *timer1, *timer2;
+ void __iomem *interrupt_regs;
+
+ int irqnr;
+
+ struct clk *clk;
+ struct clock_event_device clkevt;
+ struct irqaction clkevt_irq;
+
+ char clocksource_name[64];
+ char clockevent_name[64];
+};
+
+static int zevio_timer_set_event(unsigned long delta,
+ struct clock_event_device *dev)
+{
+ unsigned long flags;
+ struct zevio_timer *timer = container_of(dev,
+ struct zevio_timer,
+ clkevt);
+
+ local_irq_save(flags);
+
+ writel(delta, timer->timer1 + IO_CURRENT_VAL);
+ writel(CNTL_RUN_TIMER | CNTL_DEC | CNTL_MATCH(TIMER_MATCH),
+ timer->timer1 + IO_CONTROL);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static void zevio_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *dev)
+{
+ unsigned long flags;
+ struct zevio_timer *timer = container_of(dev,
+ struct zevio_timer,
+ clkevt);
+
+ local_irq_save(flags);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ /* Unsupported */
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ case CLOCK_EVT_MODE_ONESHOT:
+ /* Enable timer interrupts */
+ writel(TIMER_INTR_MSK, timer->interrupt_regs + IO_INTR_MSK);
+ writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK);
+ dev->mode = mode;
+ break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ /* Disable timer interrupts */
+ writel(0, timer->interrupt_regs + IO_INTR_MSK);
+ writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK);
+ /* Stop timer */
+ writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL);
+ dev->mode = mode;
+ break;
+ }
+
+ local_irq_restore(flags);
+}
+
+static irqreturn_t zevio_timer_interrupt(int irq, void *dev_id)
+{
+ struct zevio_timer *timer = dev_id;
+ u32 intr;
+
+ intr = readl(timer->interrupt_regs + IO_INTR_ACK);
+ if (!(intr & TIMER_INTR_MSK))
+ return IRQ_NONE;
+
+ writel(TIMER_INTR_MSK, timer->interrupt_regs + IO_INTR_ACK);
+ writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL);
+
+ if (timer->clkevt.event_handler)
+ timer->clkevt.event_handler(&timer->clkevt);
+
+ return IRQ_HANDLED;
+}
+
+static int __init zevio_timer_add(struct device_node *node)
+{
+ struct zevio_timer *timer;
+ struct resource res;
+ int ret;
+
+ timer = kzalloc(sizeof(*timer), GFP_ATOMIC);
+ if (!timer)
+ return -ENOMEM;
+
+ timer->base = of_iomap(node, 0);
+ if (!timer->base) {
+ ret = -EINVAL;
+ goto error_free;
+ }
+ timer->timer1 = timer->base + IO_TIMER1;
+ timer->timer2 = timer->base + IO_TIMER2;
+
+ timer->clk = of_clk_get(node, 0);
+ if (IS_ERR(timer->clk)) {
+ ret = PTR_ERR(timer->clk);
+ pr_err("Timer clock not found! (error %d)\n", ret);
+ goto error_unmap;
+ }
+
+ timer->interrupt_regs = of_iomap(node, 1);
+ timer->irqnr = irq_of_parse_and_map(node, 0);
+
+ of_address_to_resource(node, 0, &res);
+ scnprintf(timer->clocksource_name, sizeof(timer->clocksource_name),
+ "%llx.%s_clocksource",
+ (unsigned long long)res.start, node->name);
+
+ scnprintf(timer->clockevent_name, sizeof(timer->clockevent_name),
+ "%llx.%s_clockevent",
+ (unsigned long long)res.start, node->name);
+
+ if (timer->interrupt_regs && timer->irqnr) {
+ timer->clkevt.name = timer->clockevent_name;
+ timer->clkevt.set_next_event = zevio_timer_set_event;
+ timer->clkevt.set_mode = zevio_timer_set_mode;
+ timer->clkevt.rating = 200;
+ timer->clkevt.cpumask = cpu_all_mask;
+ timer->clkevt.features =
+ CLOCK_EVT_FEAT_ONESHOT;
+
+ writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL);
+ writel(0, timer->timer1 + IO_DIVIDER);
+
+ /* Start with timer interrupts disabled */
+ writel(0, timer->interrupt_regs + IO_INTR_MSK);
+ writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK);
+
+ /* Interrupt to occur when timer value matches 0 */
+ writel(0, timer->base + IO_MATCH(TIMER_MATCH));
+
+ timer->clkevt_irq.name = timer->clockevent_name;
+ timer->clkevt_irq.handler = zevio_timer_interrupt;
+ timer->clkevt_irq.dev_id = timer;
+ timer->clkevt_irq.flags =
+ IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
+
+ setup_irq(timer->irqnr, &timer->clkevt_irq);
+
+ clockevents_config_and_register(&timer->clkevt,
+ clk_get_rate(timer->clk), 0x0001, 0xffff);
+ pr_info("Added %s as clockevent\n", timer->clockevent_name);
+ }
+
+ writel(CNTL_STOP_TIMER, timer->timer2 + IO_CONTROL);
+ writel(0, timer->timer2 + IO_CURRENT_VAL);
+ writel(0, timer->timer2 + IO_DIVIDER);
+ writel(CNTL_RUN_TIMER | CNTL_FOREVER | CNTL_INC,
+ timer->timer2 + IO_CONTROL);
+
+ clocksource_mmio_init(timer->timer2 + IO_CURRENT_VAL,
+ timer->clocksource_name,
+ clk_get_rate(timer->clk),
+ 200, 16,
+ clocksource_mmio_readw_up);
+
+ pr_info("Added %s as clocksource\n", timer->clocksource_name);
+
+ return 0;
+error_unmap:
+ iounmap(timer->base);
+error_free:
+ kfree(timer);
+ return ret;
+}
+
+CLOCKSOURCE_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_add);