linux-clk.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Wan Zongshun <vw@iommu.org>
To: linux-arm-kernel@lists.infradead.org,
	Russell King <linux@armlinux.org.uk>,
	devicetree@vger.kernel.org, linux-clk@vger.kernel.org
Cc: Arnd Bergmann <arnd@arndb.de>,
	Daniel Lezcano <daniel.lezcano@linaro.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	linux-kernel@vger.kernel.org, robh@kernel.org,
	jason@lakedaemon.net, p.zabel@pengutronix.de,
	Wan Zongshun <vw@iommu.org>, Wan Zongshun <mcuos.com@gmail.com>
Subject: [PATCH v2 03/10] Clocksource: add nuc970 clocksource driver
Date: Sun, 10 Jul 2016 15:27:23 +0800	[thread overview]
Message-ID: <1468135649-19980-4-git-send-email-vw@iommu.org> (raw)
In-Reply-To: <1468135649-19980-1-git-send-email-vw@iommu.org>

This patch is to add nuc970 clocksource driver support.

NUC970 general timer controller includes five channels, TIMER0, TIMER1,
TIMER2, TIMER3, and TIMER4, which allow user to easily implement a
counting scheme or timing control for applications.The timer possesses
features such as adjustable resolution, and programmable counting period.
The timer can generate an interrupt signal upon timeout, or provide the
current value of count during operation.

Currently, we are using TIMER0 and TIMER1 for clocksource and clockevent
device driver support.

Signed-off-by: Wan Zongshun <mcuos.com@gmail.com>
---
 drivers/clocksource/Kconfig        |   8 +
 drivers/clocksource/Makefile       |   1 +
 drivers/clocksource/timer-nuc900.c | 305 +++++++++++++++++++++++++++++++++++++
 3 files changed, 314 insertions(+)
 create mode 100644 drivers/clocksource/timer-nuc900.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 47352d2..441c5ee 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -427,4 +427,12 @@ config CLKSRC_ST_LPC
 	  Enable this option to use the Low Power controller timer
 	  as clocksource.
 
+config NUC900_TIMER
+        bool "Clocksource timer for nuc900 platform" if COMPILE_TEST
+        depends on ARM
+        select CLKSRC_OF if OF
+        select CLKSRC_MMIO
+        help
+          Enables the clocksource for the NUC900 platform.
+
 endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 473974f..c74e252 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -67,3 +67,4 @@ obj-$(CONFIG_H8300_TMR16)		+= h8300_timer16.o
 obj-$(CONFIG_H8300_TPU)			+= h8300_tpu.o
 obj-$(CONFIG_CLKSRC_ST_LPC)		+= clksrc_st_lpc.o
 obj-$(CONFIG_X86_NUMACHIP)		+= numachip.o
+obj-$(CONFIG_NUC900_TIMER)		+= timer-nuc900.o
diff --git a/drivers/clocksource/timer-nuc900.c b/drivers/clocksource/timer-nuc900.c
new file mode 100644
index 0000000..28ccbcf
--- /dev/null
+++ b/drivers/clocksource/timer-nuc900.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuos.com@gmail.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/clockchips.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include <linux/io.h>
+#include <asm/mach/time.h>
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#define TMR_TCSR0	0x00
+#define TMR_TICR0	0x04
+#define TMR_TDR0	0x08
+#define TMR_TCSR1	0x10
+#define TMR_TICR1	0x14
+#define TMR_TDR1	0x18
+#define TMR_TISR	0x60
+
+#define RESETINT	0x1f
+#define PERIODIC	(0x01 << 27)
+#define ONESHOT		(0x00 << 27)
+#define COUNTEN		(0x01 << 30)
+#define INTEN		(0x01 << 29)
+
+#define TICKS_PER_SEC	100
+/* Divider = prescale + 1 */
+#define PRESCALE	0x63
+
+#define	TDR_SHIFT	24
+#define	TDR_MASK	((1 << TDR_SHIFT) - 1)
+#define OPMODE_MASK	~(0x03 << 27)
+
+struct nuc970_clockevents {
+	struct clock_event_device clkevt;
+	unsigned int timer0_load;
+	void __iomem *base;
+};
+
+struct nuc970_clockevents *clkevt_to_nuc970(struct clock_event_device *clk)
+{
+	return container_of(clk, struct nuc970_clockevents, clkevt);
+}
+
+static int nuc970_clockevent_set(bool periodic, struct clock_event_device *clk)
+{
+	struct nuc970_clockevents *evt = clkevt_to_nuc970(clk);
+	unsigned int val;
+
+	val = readl(evt->base + TMR_TCSR0);
+	val &= OPMODE_MASK;
+
+	writel(evt->timer0_load, evt->base + TMR_TICR0);
+
+	val |= periodic ? PERIODIC:ONESHOT;
+	val |= (COUNTEN | INTEN | PRESCALE);
+
+	writel(val, evt->base + TMR_TCSR0);
+
+	return 0;
+}
+
+
+static int nuc970_clockevent_set_oneshot(struct clock_event_device *clk)
+{
+	nuc970_clockevent_set(0, clk);
+	return 0;
+}
+
+static int nuc970_clockevent_set_periodic(struct clock_event_device *clk)
+{
+	nuc970_clockevent_set(1, clk);
+	return 0;
+}
+
+static int nuc970_clockevent_setnextevent(unsigned long evtval,
+					  struct clock_event_device *clk)
+{
+	struct nuc970_clockevents *evt = clkevt_to_nuc970(clk);
+	unsigned int tcsr, tdelta;
+
+	tcsr = readl(evt->base + TMR_TCSR0);
+	tdelta = readl(evt->base + TMR_TICR0) - readl(evt->base + TMR_TDR0);
+
+	writel(evtval, evt->base + TMR_TICR0);
+
+	if (!(tcsr & COUNTEN) && ((tdelta > 2) || (tdelta == 0)))
+		writel(readl(evt->base + TMR_TCSR0) | COUNTEN,
+			     evt->base + TMR_TCSR0);
+
+	return 0;
+}
+
+static int nuc970_clockevent_shutdown(struct clock_event_device *clk)
+{
+	struct nuc970_clockevents *evt = clkevt_to_nuc970(clk);
+	unsigned int val = readl(evt->base + TMR_TCSR0) & ~(0x03 << 27);
+
+	writel(val, evt->base + TMR_TCSR0);
+
+	return 0;
+}
+
+/*IRQ handler for the timer*/
+static irqreturn_t nuc970_timer0_interrupt(int irq, void *dev_id)
+{
+	struct nuc970_clockevents *evt = dev_id;
+	struct clock_event_device *clk = &(evt->clkevt);
+
+	writel(0x01, evt->base + TMR_TISR);
+
+	clk->event_handler(clk);
+
+	return IRQ_HANDLED;
+}
+
+static int clock_event_device_init(struct clock_event_device *clk,
+				   unsigned int rate)
+{
+	clk->name = "nuc970-timer0";
+	clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	clk->set_state_shutdown = nuc970_clockevent_shutdown;
+	clk->set_state_periodic = nuc970_clockevent_set_periodic;
+	clk->set_state_oneshot = nuc970_clockevent_set_oneshot;
+	clk->set_state_oneshot_stopped = nuc970_clockevent_shutdown;
+	clk->set_next_event = nuc970_clockevent_setnextevent;
+	clk->cpumask = cpumask_of(0);
+	clk->rating = 300;
+
+	clockevents_config_and_register(clk, rate, 0xf, 0xffffffff);
+	return 0;
+}
+
+static int __init nuc970_clkevt_init(struct nuc970_clockevents *evt,
+				     int irq, struct clk *clk_timer0)
+{
+	unsigned int rate = 0;
+	int ret;
+
+	/*Clockevents init*/
+	ret = clk_prepare_enable(clk_timer0);
+	if (ret) {
+		pr_err("Failed to enable clk_timer0 clock :%d.\n", ret);
+		goto err_clk_prepare_enable;
+	}
+
+	writel(0x00, evt->base + TMR_TCSR0);
+
+	rate = clk_get_rate(clk_timer0);
+	if (!rate) {
+		pr_err("Failed to get clk_timer0 rate :%d.\n", rate);
+		ret = -EINVAL;
+		goto err_clk_get_rate;
+	}
+
+	rate /= (PRESCALE + 1);
+
+	evt->timer0_load = (rate / TICKS_PER_SEC);
+
+	writel(RESETINT, evt->base + TMR_TISR);
+
+	clock_event_device_init(&(evt->clkevt), rate);
+
+	ret = request_irq(irq, nuc970_timer0_interrupt,
+			  IRQF_TIMER | IRQF_IRQPOLL, "nuc970-timer0", evt);
+	if (ret) {
+		pr_err("Failed to request irq :%d, return: %d.\n", irq, ret);
+		goto err_clk_get_rate;
+	}
+
+	return 0;
+
+err_clk_get_rate:
+	clk_disable_unprepare(clk_timer0);
+err_clk_prepare_enable:
+	return ret;
+}
+
+static int __init nuc970_clksrc_init(struct nuc970_clockevents *evt,
+				     struct clk *clk_timer1)
+{
+	unsigned int val, rate = 0;
+	int ret;
+
+	/*Clocksource init*/
+	ret = clk_prepare_enable(clk_timer1);
+	if (ret) {
+		pr_err("Failed to enable clk_timer1 clock :%d.\n", ret);
+		goto err_clk_prepare_enable;
+	}
+
+	writel(0x00, evt->base + TMR_TCSR1);
+
+	rate = clk_get_rate(clk_timer1);
+	if (!rate) {
+		pr_err("Failed to get clk_timer1 rate :%d.\n", rate);
+		ret = -EINVAL;
+		goto err_clk_get_rate;
+	}
+
+	rate /= (PRESCALE + 1);
+
+	writel(0xffffffff, evt->base + TMR_TICR1);
+
+	val = readl(evt->base + TMR_TCSR1);
+	val |= (COUNTEN | PERIODIC  | PRESCALE);
+	writel(val, evt->base + TMR_TCSR1);
+
+	ret = clocksource_mmio_init(evt->base + TMR_TDR1, "nuc970-timer1",
+			      rate, 200, TDR_SHIFT,
+			      clocksource_mmio_readl_down);
+	if (ret) {
+		pr_err("nuc970-timer1: can't register clocksource\n");
+		goto err_clk_get_rate;
+	}
+
+	return 0;
+
+err_clk_get_rate:
+	clk_disable_unprepare(clk_timer1);
+err_clk_prepare_enable:
+	return ret;
+
+}
+
+static void __init nuc970_timer_of_init(struct device_node *node)
+{
+	struct nuc970_clockevents *nuc970_evt;
+	struct clk *clk_timer0, *clk_timer1;
+	int irq, ret;
+
+	nuc970_evt = kzalloc(sizeof(struct nuc970_clockevents), GFP_KERNEL);
+	if (!nuc970_evt) {
+		ret = -ENOMEM;
+		pr_err("%s: Failed to kzalloc memory\n", node->full_name);
+		goto err_alloc_mem;
+	}
+
+	nuc970_evt->base = of_iomap(node, 0);
+	if (!nuc970_evt->base) {
+		ret = -ENOMEM;
+		pr_err("%s: Unable to map timer base\n", node->full_name);
+		goto err_iomap;
+	}
+
+	clk_timer0 = of_clk_get_by_name(node, "timer0");
+	if (IS_ERR(clk_timer0)) {
+		ret = PTR_ERR(clk_timer0);
+		pr_err("%s: Unable to get clk_timer0\n", node->full_name);
+		goto err_get_clk;
+	}
+
+	clk_timer1 = of_clk_get_by_name(node, "timer1");
+	if (IS_ERR(clk_timer1)) {
+		ret = PTR_ERR(clk_timer1);
+		pr_err("%s: Unable to get clk_timer1\n", node->full_name);
+		goto err_get_clk;
+	}
+
+	irq = irq_of_parse_and_map(node, 0);
+	if (irq <= 0) {
+		ret = irq;
+		pr_err("%s: Unable to get irq\n", node->full_name);
+		goto err_get_clk;
+	}
+
+	ret = nuc970_clksrc_init(nuc970_evt, clk_timer1);
+	if (ret) {
+		pr_err("Failed to init clock source :%d.\n", ret);
+		goto err_get_clk;
+	}
+
+	nuc970_clkevt_init(nuc970_evt, irq, clk_timer0);
+	if (ret) {
+		pr_err("Failed to init clock event device :%d.\n", ret);
+		goto err_get_clk;
+	}
+
+	return;
+
+err_get_clk:
+	iounmap(nuc970_evt->base);
+err_iomap:
+	kfree(nuc970_evt);
+err_alloc_mem:
+	return;
+}
+
+CLOCKSOURCE_OF_DECLARE(nuc970, "nuvoton,nuc970-tmr", nuc970_timer_of_init);
-- 
2.7.4


  parent reply	other threads:[~2016-07-10  7:27 UTC|newest]

Thread overview: 45+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-07-10  7:27 [PATCH v2 00/10] ARM: NUC900: Add NUC970 SoC support Wan Zongshun
2016-07-10  7:27 ` [PATCH v2 01/10] ARM: NUC900: Add nuc970 machine support Wan Zongshun
2016-07-10 22:11   ` Arnd Bergmann
2016-07-11 16:04   ` Arnd Bergmann
2016-07-12  4:30     ` Wan Zongshun
2016-07-12  7:14       ` Wan Zongshun
2016-07-12  8:23         ` Arnd Bergmann
2016-07-10  7:27 ` [PATCH v2 02/10] irqchip: add irqchip driver for nuc900 Wan Zongshun
2016-07-10 21:51   ` Paul Gortmaker
2016-07-11  2:19     ` Wan Zongshun
2016-07-11 15:46   ` Arnd Bergmann
2016-07-12  7:04     ` Wan Zongshun
2016-07-12  8:26       ` Arnd Bergmann
2016-07-14  8:52         ` Wan Zongshun
2016-07-14 11:09           ` Arnd Bergmann
2016-07-13 20:09   ` Jason Cooper
2016-07-14  3:36     ` Wan Zongshun
2016-07-14 13:54       ` Jason Cooper
2016-07-15  5:15         ` Wan Zongshun
2016-07-15  7:00           ` Arnd Bergmann
2016-07-15  9:44             ` Wan ZongShun
2016-07-15 10:02               ` Arnd Bergmann
2016-07-21 18:45                 ` Jason Cooper
2016-07-15 15:45               ` Jason Cooper
2016-07-10  7:27 ` Wan Zongshun [this message]
2016-07-11 15:36   ` [PATCH v2 03/10] Clocksource: add nuc970 clocksource driver Arnd Bergmann
2016-07-12  7:32     ` Wan Zongshun
2016-07-12  8:28       ` Arnd Bergmann
2016-07-21 12:52         ` Daniel Lezcano
2016-07-21 12:54           ` Arnd Bergmann
2016-07-10  7:27 ` [PATCH v2 04/10] clk: add Clock driver for nuc970 Wan Zongshun
2016-07-11 22:14   ` Michael Turquette
2016-07-10  7:27 ` [PATCH v2 05/10] power/reset: Add reset driver support for nuc900 Wan Zongshun
2016-07-10 21:56   ` Paul Gortmaker
2016-07-11  2:30     ` Wan Zongshun
2016-07-11  2:58       ` Paul Gortmaker
2016-07-10  7:27 ` [PATCH v2 06/10] soc: Add SoC specific " Wan Zongshun
2016-07-11  8:03   ` Arnd Bergmann
2016-07-11  9:07     ` Wan Zongshun
2016-07-11 10:24       ` Arnd Bergmann
2016-07-11 10:28         ` Wan ZongShun
2016-07-11 10:36           ` Arnd Bergmann
2016-07-12  9:06     ` Wan Zongshun
2016-07-12  9:50       ` Arnd Bergmann
2016-07-10  7:27 ` [PATCH v2 07/10] ARM: dts: Add clock header file into dt-bindings Wan Zongshun

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1468135649-19980-4-git-send-email-vw@iommu.org \
    --to=vw@iommu.org \
    --cc=arnd@arndb.de \
    --cc=daniel.lezcano@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=jason@lakedaemon.net \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    --cc=mcuos.com@gmail.com \
    --cc=p.zabel@pengutronix.de \
    --cc=robh@kernel.org \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).