All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] drivers: clocksource: add mmp timer driver
@ 2015-02-02  8:31 ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-02  8:31 UTC (permalink / raw)
  To: mark.rutland, daniel.lezcano, tglx, chao.xie, haojian.zhuang,
	linux-kernel, devicetree

From: Chao Xie <chao.xie@marvell.com>

These patch will create a new timer driver in drivers/clocksource/
The timer driver will support all SOCes in mach-mmp

There are two patches #2 and #3 changing the arch/mach-mmp to make
DT and no-DT supported SOCes to make use of new timer driver

The final patch will remove the old timer driver.

The patches are tested at pxa910 and mmp2(DT and no-DT support).
Because pxa168 is too old, and it is hard to find the board.
So only pass the compiling.

Chao Xie (4):
  clocksource: mmp: add mmp timer driver
  arm: mmp: make SOCes without DT use new timer driver
  arm: mmp: make SOCes with DT use new timer driver
  arm: mmp: remove the old timer driver

 .../devicetree/bindings/arm/mrvl/timer.txt         |  61 +-
 arch/arm/boot/dts/mmp2.dtsi                        |  19 +-
 arch/arm/boot/dts/pxa168.dtsi                      |  20 +-
 arch/arm/boot/dts/pxa910.dtsi                      |  26 +-
 arch/arm/mach-mmp/Kconfig                          |   2 +
 arch/arm/mach-mmp/Makefile                         |   2 +-
 arch/arm/mach-mmp/common.h                         |   2 -
 arch/arm/mach-mmp/mmp-dt.c                         |   4 +-
 arch/arm/mach-mmp/mmp2-dt.c                        |   4 +-
 arch/arm/mach-mmp/mmp2.c                           |  23 +-
 arch/arm/mach-mmp/pxa168.c                         |  23 +-
 arch/arm/mach-mmp/pxa910.c                         |  24 +-
 arch/arm/mach-mmp/time.c                           | 247 ------
 drivers/clocksource/Makefile                       |   1 +
 drivers/clocksource/timer-mmp.c                    | 837 +++++++++++++++++++++
 include/linux/mmp_timer.h                          |  40 +
 16 files changed, 1058 insertions(+), 277 deletions(-)
 delete mode 100644 arch/arm/mach-mmp/time.c
 create mode 100644 drivers/clocksource/timer-mmp.c
 create mode 100644 include/linux/mmp_timer.h

-- 
1.8.3.2


^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 0/4] drivers: clocksource: add mmp timer driver
@ 2015-02-02  8:31 ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-02  8:31 UTC (permalink / raw)
  To: mark.rutland-5wv7dgnIgG8, daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A,
	tglx-hfZtesqFncYOwBW4kG4KsQ, chao.xie-eYqpPyKDWXRBDgjK7y7TUQ,
	haojian.zhuang-QSEj5FYQhm4dnm+yROfE0A,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Chao Xie <chao.xie-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>

These patch will create a new timer driver in drivers/clocksource/
The timer driver will support all SOCes in mach-mmp

There are two patches #2 and #3 changing the arch/mach-mmp to make
DT and no-DT supported SOCes to make use of new timer driver

The final patch will remove the old timer driver.

The patches are tested at pxa910 and mmp2(DT and no-DT support).
Because pxa168 is too old, and it is hard to find the board.
So only pass the compiling.

Chao Xie (4):
  clocksource: mmp: add mmp timer driver
  arm: mmp: make SOCes without DT use new timer driver
  arm: mmp: make SOCes with DT use new timer driver
  arm: mmp: remove the old timer driver

 .../devicetree/bindings/arm/mrvl/timer.txt         |  61 +-
 arch/arm/boot/dts/mmp2.dtsi                        |  19 +-
 arch/arm/boot/dts/pxa168.dtsi                      |  20 +-
 arch/arm/boot/dts/pxa910.dtsi                      |  26 +-
 arch/arm/mach-mmp/Kconfig                          |   2 +
 arch/arm/mach-mmp/Makefile                         |   2 +-
 arch/arm/mach-mmp/common.h                         |   2 -
 arch/arm/mach-mmp/mmp-dt.c                         |   4 +-
 arch/arm/mach-mmp/mmp2-dt.c                        |   4 +-
 arch/arm/mach-mmp/mmp2.c                           |  23 +-
 arch/arm/mach-mmp/pxa168.c                         |  23 +-
 arch/arm/mach-mmp/pxa910.c                         |  24 +-
 arch/arm/mach-mmp/time.c                           | 247 ------
 drivers/clocksource/Makefile                       |   1 +
 drivers/clocksource/timer-mmp.c                    | 837 +++++++++++++++++++++
 include/linux/mmp_timer.h                          |  40 +
 16 files changed, 1058 insertions(+), 277 deletions(-)
 delete mode 100644 arch/arm/mach-mmp/time.c
 create mode 100644 drivers/clocksource/timer-mmp.c
 create mode 100644 include/linux/mmp_timer.h

-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 1/4] clocksource: mmp: add mmp timer driver
  2015-02-02  8:31 ` Chao Xie
@ 2015-02-02  8:31   ` Chao Xie
  -1 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-02  8:31 UTC (permalink / raw)
  To: mark.rutland, daniel.lezcano, tglx, chao.xie, haojian.zhuang,
	linux-kernel, devicetree

From: Chao Xie <chao.xie@marvell.com>

MMP timer is attached to APB bus, It has the following
limitation.
1. When get count of timer counter, it need some delay
   to get a stable count.
2. When set match register, it need disable the counter
   first, and enable it after set the match register.
   The disabling need wait for 2 clock cycle to take
   effect.

To improve above #1, shadow register for count is added.
To improve above #2, CRSR register is added for quick updating.

So there are three types of timer.
timer1: old timer.
timer2: old timer with shadow register.
timer3: old timer with shadow and CRSR register.

This timer driver will be used for many SOCes.
1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
2. pxa1L88, pxa1U88 have timer1 and timer3.
3. pxa1928 has timer 2.

The driver supports DT and non-DT initialization.
The driver supports UP/SMP SOCes and 64 bit core.

Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
 .../devicetree/bindings/arm/mrvl/timer.txt         |  61 +-
 drivers/clocksource/Makefile                       |   1 +
 drivers/clocksource/timer-mmp.c                    | 837 +++++++++++++++++++++
 include/linux/mmp_timer.h                          |  40 +
 4 files changed, 933 insertions(+), 6 deletions(-)
 create mode 100644 drivers/clocksource/timer-mmp.c
 create mode 100644 include/linux/mmp_timer.h

diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
index 9a6e251..b49cb3e 100644
--- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
+++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
@@ -1,13 +1,62 @@
 * Marvell MMP Timer controller
 
+Each timer have multiple counters, so the timer DT need include counter's
+description.
+
+1. Timer
+
 Required properties:
-- compatible : Should be "mrvl,mmp-timer".
+- compatible : Should be "marvell,mmp-timer".
 - reg : Address and length of the register set of timer controller.
-- interrupts : Should be the interrupt number.
+- clocks : The first clock is the fequency of the apb bus that the timer
+  attached to. The second clock is the fast clock frequency of the timer.
+  This frequency and fast clock are used to calculated delay
+  loops for clock operations.
+
+Optional properties:
+- marvell,timer-has-crsr : This timer has CRSR register.
+- marvell,timer-has-shadow : This timer has shadow register.
+
+2. Counter
+
+Required properties:
+- compatible : It can be
+      "marvell,timer-counter-clkevt" : The counter is used for clock event
+                                       device.
+      "marvell,timer-counter-clksrc" : The counter is used for clock source.
+      "marvell,timer-counter-delay" : The counter is used for delay timer.
+- marvell,timer-counter-id : The counter index in this timer.
 
-Example:
-	timer0: timer@d4014000 {
-		compatible = "mrvl,mmp-timer";
-		reg = <0xd4014000 0x100>;
+Optional properties:
+- marvell,fast-clock : whether the counter use the fast-clock for counting.
+- interrupts : The interrupt for clock event device.
+  Only valid for "marvell,timer-counter-clkevt".
+- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
+  "marvell,timer-counter-clkevt".
+- marvell,timer-counter-broadcast : When this counter acts as clock event
+  device. It is broadcast clock event device.
+  Only valid for "marvell,timer-counter-clkevt".
+- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
+  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
+  Only valid for "marvell,timer-counter-clkevt".
+
+3. Examples
+
+timer0: timer@d4014000 {
+	compatible = "marvell,mmp-timer";
+	reg = <0xd4014000 0xc8>;
+	clocks = <&soc_clocks MMP2_PLL1_24>, <&soc_closk MMP2_CLK_TIMER>;
+	status = "okay";
+
+	counter0 {
+		compatible = "marvell,timer-counter-clkevt";
 		interrupts = <13>;
+		marvell,timer-counter-id = <0>;
+		marvell,timer-counter-cpu = <0>;
+	};
+
+	counter1 {
+		compatible = "marvell,timer-counter-clksrc";
+		marvell,timer-counter-id = <1>;
 	};
+};
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 94d90b2..6a66d4d 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ORION_TIMER)	+= time-orion.o
 obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o
 obj-$(CONFIG_ARCH_CLPS711X)	+= clps711x-timer.o
 obj-$(CONFIG_ARCH_MARCO)	+= timer-marco.o
+obj-$(CONFIG_ARCH_MMP)		+= timer-mmp.o
 obj-$(CONFIG_ARCH_MOXART)	+= moxart_timer.o
 obj-$(CONFIG_ARCH_MXS)		+= mxs_timer.o
 obj-$(CONFIG_ARCH_PXA)		+= pxa_timer.o
diff --git a/drivers/clocksource/timer-mmp.c b/drivers/clocksource/timer-mmp.c
new file mode 100644
index 0000000..b6a9ce1
--- /dev/null
+++ b/drivers/clocksource/timer-mmp.c
@@ -0,0 +1,837 @@
+/*
+ * driver/clocksource/timer-mmp.c
+ *
+ *   Support for clocksource and clockevents
+ *
+ * Copyright (C) 2008 Marvell International Ltd.
+ * All rights reserved.
+ *
+ *   2008-04-11: Jason Chagas <Jason.chagas@marvell.com>
+ *   2008-10-08: Bin Yang <bin.yang@marvell.com>
+ *
+ * The timers module actually includes three timers, each timer with up to
+ * three match comparators. Timer #0 is used here in free-running mode as
+ * the clock source, and match comparator #1 used as clock event device.
+ *
+ * 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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/mmp_timer.h>
+#include <linux/clockchips.h>
+#include <linux/sched_clock.h>
+#include <linux/clk.h>
+
+#define TMR_CCR		(0x0000)
+#define TMR_TN_MM(n, m)	(0x0004 + ((n) << 3) + (((n) + (m)) << 2))
+#define TMR_CR(n)	(0x0028 + ((n) << 2))
+#define TMR_SR(n)	(0x0034 + ((n) << 2))
+#define TMR_IER(n)	(0x0040 + ((n) << 2))
+#define TMR_PLVR(n)	(0x004c + ((n) << 2))
+#define TMR_PLCR(n)	(0x0058 + ((n) << 2))
+#define TMR_WMER	(0x0064)
+#define TMR_WMR		(0x0068)
+#define TMR_WVR		(0x006c)
+#define TMR_WSR		(0x0070)
+#define TMR_ICR(n)	(0x0074 + ((n) << 2))
+#define TMR_WICR	(0x0080)
+#define TMR_CER		(0x0084)
+#define TMR_CMR		(0x0088)
+#define TMR_ILR(n)	(0x008c + ((n) << 2))
+#define TMR_WCR		(0x0098)
+#define TMR_WFAR	(0x009c)
+#define TMR_WSAR	(0x00A0)
+#define TMR_CVWR(n)	(0x00A4 + ((n) << 2))
+#define TMR_CRSR        (0x00B0)
+
+#define TMR_CCR_CS_0(x)	(((x) & 0x3) << 0)
+#define TMR_CCR_CS_1(x)	(((x) & 0x3) << 2)
+#define TMR_CCR_CS_2(x)	(((x) & 0x3) << 5)
+
+#define MAX_EVT_NUM		5
+
+#define MAX_DELTA		(0xfffffffe)
+#define MIN_DELTA		(5)
+
+#define MMP_MAX_COUNTER		3
+#define MMP_MAX_TIMER		4
+
+#define TMR_CER_COUNTER(cid)	(1 << (cid))
+#define MMP_ALL_COUNTERS	((1 << MMP_MAX_COUNTER) - 1)
+
+#define MMP_TIMER_CLOCK_32KHZ	32768
+#define MMP_TIMER_CLOCK_1KHZ	1000
+
+struct mmp_timer;
+struct mmp_timer_counter {
+	unsigned int id;
+	unsigned int usage;
+	unsigned int cnt_freq;
+	int cpu;
+	int loop_delay;
+	struct mmp_timer *timer;
+};
+
+struct mmp_timer {
+	unsigned int id;
+	void __iomem *base;
+	struct mmp_timer_counter counters[MMP_MAX_COUNTER];
+	unsigned int flag;
+	int loop_delay_fastclk;
+	unsigned int fc_freq;
+	/* lock to protect hw operation. */
+	spinlock_t tm_lock;
+};
+
+struct mmp_timer_clkevt {
+	struct mmp_timer_counter *counter;
+	struct clock_event_device ced;
+	struct irqaction irqa;
+	struct notifier_block nb;
+};
+
+struct mmp_timer_clksrc {
+	struct mmp_timer_counter *counter;
+	struct clocksource cs;
+};
+
+#ifdef CONFIG_ARM
+struct mmp_timer_dclk {
+	struct mmp_timer_counter *counter;
+	struct delay_timer *dt;
+};
+static struct mmp_timer_dclk *dclk;
+#endif
+
+static struct mmp_timer *mmp_timers[MMP_MAX_TIMER];
+static struct mmp_timer_clksrc *clksrc;
+
+static int timer_counter_switch_clock(int tid, int cid, unsigned int freq)
+{
+	struct mmp_timer *tm = mmp_timers[tid];
+	u32 ccr, mask;
+
+	ccr = readl_relaxed(tm->base + TMR_CCR);
+
+	if (cid == 0)
+		mask = TMR_CCR_CS_0(3);
+	else if (cid == 1)
+		mask = TMR_CCR_CS_1(3);
+	else
+		mask = TMR_CCR_CS_2(3);
+
+	ccr &= ~mask;
+
+	if (freq == MMP_TIMER_CLOCK_32KHZ) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(2);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(1);
+		else if (cid == 0)
+			ccr |= TMR_CCR_CS_0(1);
+	} else if (freq == MMP_TIMER_CLOCK_1KHZ) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(1);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(2);
+	} else if (freq == tm->fc_freq) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(0);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(0);
+		else if (cid == 0)
+			ccr |= TMR_CCR_CS_0(0);
+	} else {
+		pr_err("Timer %d:%d: invalid clock rate %d\n", tid, cid, freq);
+		return -EINVAL;
+	}
+
+	writel_relaxed(ccr, tm->base + TMR_CCR);
+
+	return 0;
+}
+
+static void timer_counter_disable(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	int delay = tm->loop_delay_fastclk;
+	u32 cer;
+
+	/*
+	 * Stop the counter will need multiple timer clock to take effect.
+	 * Some operations can only be done when counter is disabled. So
+	 * add delay here.
+	 */
+	/* Step1: disable counter */
+	cer = readl_relaxed(tm->base + TMR_CER);
+	writel_relaxed(cer & ~(1 << cnt->id), tm->base + TMR_CER);
+
+	/*
+	 * Step2: switch to fast clock, so the delay can be completed
+	 * quickly.
+	 */
+	if (cnt->cnt_freq != tm->fc_freq)
+		timer_counter_switch_clock(tm->id, cnt->id, tm->fc_freq);
+
+	/*
+	 * Step3: Loop for multiple timer cycles. We do it by clearing
+	 * pending interrupt status.
+	 */
+	while (delay--)
+		writel_relaxed(0x1, tm->base + TMR_ICR(cnt->id));
+}
+
+static void timer_counter_enable(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	u32 cer;
+
+	/* Switch to original clock */
+	if (cnt->cnt_freq != tm->fc_freq)
+		timer_counter_switch_clock(tm->id, cnt->id, cnt->cnt_freq);
+
+	/* Enable timer */
+	cer = readl_relaxed(tm->base + TMR_CER);
+	writel_relaxed(cer | (1 << cnt->id), tm->base + TMR_CER);
+}
+
+static inline uint32_t timer_read(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	int has_shadow = tm->flag & MMP_TIMER_FLAG_SHADOW;
+	int delay = 3;
+	u32 val1, val2;
+
+	if (has_shadow)
+		return readl_relaxed(tm->base + TMR_CR(cnt->id));
+
+	if (cnt->cnt_freq != tm->fc_freq) {
+		/* slow clock */
+		do {
+			val1 = readl_relaxed(tm->base + TMR_CR(cnt->id));
+			val2 = readl_relaxed(tm->base + TMR_CR(cnt->id));
+		} while (val2 != val1);
+	} else {
+		/* fast clock */
+		writel_relaxed(1, tm->base + TMR_CVWR(cnt->id));
+		while (delay--)
+			val1 = readl_relaxed(tm->base +
+					TMR_CVWR(cnt->id));
+	}
+
+	return val1;
+}
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *c = dev_id;
+	struct mmp_timer_clkevt *evt;
+	struct mmp_timer_counter *counter;
+	unsigned int cnt;
+	unsigned long flags;
+	void __iomem *base;
+	int has_crsr;
+
+	evt = container_of(c, struct mmp_timer_clkevt, ced);
+	counter = evt->counter;
+	cnt = counter->id;
+	base = counter->timer->base;
+	has_crsr = counter->timer->flag & MMP_TIMER_FLAG_CRSR;
+
+	spin_lock_irqsave(&(counter->timer->tm_lock), flags);
+	/* We only use match #0 for the counter. */
+	if (readl_relaxed(base + TMR_SR(cnt)) & 0x1) {
+		if (!has_crsr)
+			timer_counter_disable(counter);
+
+		/* Disable the interrupt. */
+		writel_relaxed(0x00, base + TMR_IER(cnt));
+		/* Clear interrupt status */
+		writel_relaxed(0x01, base + TMR_ICR(cnt));
+
+		spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+
+		c->event_handler(c);
+
+		return IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+
+	return IRQ_NONE;
+}
+
+static int timer_set_next_event(unsigned long delta,
+				struct clock_event_device *dev)
+{
+	struct mmp_timer_counter *cnt;
+	struct mmp_timer_clkevt *evt;
+	unsigned long flags;
+	unsigned int cid;
+	u32 cer, crsr;
+	void __iomem *base;
+	int delay, has_crsr;
+
+	evt = container_of(dev, struct mmp_timer_clkevt, ced);
+	cnt = evt->counter;
+	cid = cnt->id;
+	base = cnt->timer->base;
+	has_crsr = cnt->timer->flag & MMP_TIMER_FLAG_CRSR;
+
+	spin_lock_irqsave(&(cnt->timer->tm_lock), flags);
+	if (has_crsr) {
+		/*
+		 * Use TMR_CRSR to restart the counter and make match
+		 * register take effect. This bit should be 0 before
+		 * set it again.
+		 * The polling loop is defined by loop_delay.
+		 */
+		delay = cnt->loop_delay;
+		do {
+			crsr = readl_relaxed(base + TMR_CRSR);
+			delay--;
+		} while ((crsr & (1 << cid)) && delay > 0);
+
+		BUG_ON(delay <= 0);
+
+		writel_relaxed(delta - 1, base + TMR_TN_MM(cid, 0));
+		/*
+		 * After counter is restart, clear the interrupt status for
+		 * safe, and re-enable the interrupt for match #0.
+		 */
+		writel_relaxed(0x01, base + TMR_ICR(cid));
+		writel_relaxed(0x01, base + TMR_IER(cid));
+		writel_relaxed((1 << cid), base + TMR_CRSR);
+	} else {
+		cer = readl_relaxed(base + TMR_CER);
+
+		/* If the timer counter is enabled, first disable it. */
+		if (cer & (1 << cid))
+			timer_counter_disable(cnt);
+
+		/* Setup new counter value */
+		writel_relaxed(delta - 1, base + TMR_TN_MM(cid, 0));
+
+		/* enable the matching interrupt */
+		writel_relaxed(0x1, base + TMR_IER(cid));
+
+		timer_counter_enable(cnt);
+	}
+	spin_unlock_irqrestore(&(cnt->timer->tm_lock), flags);
+
+	return 0;
+}
+
+static void timer_set_mode(enum clock_event_mode mode,
+			   struct clock_event_device *dev)
+{
+	unsigned long flags;
+	unsigned int cnt;
+	struct mmp_timer_counter *counter;
+	struct mmp_timer_clkevt *evt;
+	void __iomem *base;
+
+	evt = container_of(dev, struct mmp_timer_clkevt, ced);
+	counter = evt->counter;
+	cnt = counter->id;
+	base = counter->timer->base;
+
+	spin_lock_irqsave(&(counter->timer->tm_lock), flags);
+	switch (mode) {
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		timer_counter_disable(counter);
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+	case CLOCK_EVT_MODE_ONESHOT:
+		timer_counter_enable(counter);
+		break;
+	}
+	spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+}
+
+static cycle_t clksrc_read(struct clocksource *cs)
+{
+	return timer_read(clksrc->counter);
+}
+
+static u64 notrace mmp_read_sched_clock(void)
+{
+	return timer_read(clksrc->counter);
+}
+
+#ifdef CONFIG_ARM
+static unsigned long d_read_current_timer(void)
+{
+	return timer_read(dclk->counter);
+}
+
+static struct delay_timer d_timer = {
+	.read_current_timer	= d_read_current_timer,
+};
+#endif
+
+static int mmp_timer_cpu_notify(struct notifier_block *self,
+				unsigned long action, void *hcpu)
+{
+	struct mmp_timer_clkevt *evt;
+	struct mmp_timer_counter *cnt;
+
+	evt = container_of(self, struct mmp_timer_clkevt, nb);
+	cnt = evt->counter;
+
+	if (cnt->cpu != (unsigned long)hcpu)
+		return NOTIFY_OK;
+
+	switch (action & ~CPU_TASKS_FROZEN) {
+	case CPU_STARTING:
+		clockevents_config_and_register(&evt->ced,
+						cnt->cnt_freq,
+						MIN_DELTA, MAX_DELTA);
+		break;
+	case CPU_ONLINE:
+		irq_set_affinity(evt->ced.irq, evt->ced.cpumask);
+		enable_irq(evt->ced.irq);
+		break;
+	case CPU_DYING:
+		clockevents_set_mode(&evt->ced,
+				     CLOCK_EVT_MODE_SHUTDOWN);
+		disable_irq(evt->ced.irq);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+int __init mmp_timer_init(int tid, void __iomem *base,
+			  unsigned int flag, unsigned int fc_freq,
+			  unsigned int apb_freq)
+{
+	struct mmp_timer *tm = mmp_timers[tid];
+	u32 tmp, delay;
+	int cid;
+
+	if (tm)
+		return -EINVAL;
+
+	tm = kzalloc(sizeof(*tm), GFP_KERNEL);
+	if (!tm)
+		return -ENOMEM;
+
+	/*
+	 * The calculation formula for the loop cycle is:
+	 *
+	 * (1) need wait for 2 timer's clock cycle:
+	 *        1             2
+	 *     ------- x 2 = -------
+	 *     fc_freq       fc_freq
+	 *
+	 * (2) convert to apb clock cycle:
+	 *        2          1        apb_freq * 2
+	 *     ------- / -------- = ----------------
+	 *     fc_freq   apb_freq       fc_freq
+	 *
+	 * (3) every apb register's accessing will take 8 apb clock cycle,
+	 *     also consider add extral one more time for safe way;
+	 *     so finally need loop times for the apb register accessing:
+	 *
+	 *       (apb_freq * 2)
+	 *     ------------------ / 8 + 1
+	 *          fc_freq
+	 */
+	delay = ((apb_freq * 2) / fc_freq / 8) + 1;
+	pr_debug("Timer %d: loop_delay_fastclk is %d\n", tid, delay);
+
+	tm->id = tid;
+	tm->base = base;
+	tm->flag = flag;
+	tm->loop_delay_fastclk = delay;
+	tm->fc_freq = fc_freq;
+	spin_lock_init(&(tm->tm_lock));
+
+	mmp_timers[tid] = tm;
+
+	for (cid = 0; cid < MMP_MAX_COUNTER; cid++) {
+		tm->counters[cid].id = cid;
+		tm->counters[cid].timer = tm;
+
+		/* We will disable all counters. Switch to fastclk first. */
+		timer_counter_switch_clock(tid, cid, fc_freq);
+	}
+
+	/* disalbe all counters */
+	tmp = readl_relaxed(base + TMR_CER) & ~MMP_ALL_COUNTERS;
+	writel_relaxed(tmp, base + TMR_CER);
+
+	/* disable matching interrupt */
+	writel_relaxed(0x00, base + TMR_IER(0));
+	writel_relaxed(0x00, base + TMR_IER(1));
+	writel_relaxed(0x00, base + TMR_IER(2));
+
+	while (delay--) {
+		/* Clear pending interrupt status */
+		writel_relaxed(0x1, base + TMR_ICR(0));
+		writel_relaxed(0x1, base + TMR_ICR(1));
+		writel_relaxed(0x1, base + TMR_ICR(2));
+		writel_relaxed(tmp, base + TMR_CER);
+	}
+
+	return 0;
+}
+
+static int __init mmp_timer_counter_hw_init(struct mmp_timer *tm, int cid,
+					    unsigned int freq)
+{
+	u32 tmp, delay;
+	unsigned int ratio;
+	int ret;
+
+	ret = timer_counter_switch_clock(tm->id, cid, freq);
+	if (ret)
+		return ret;
+
+	ratio = tm->fc_freq / tm->counters[cid].cnt_freq;
+	tm->counters[cid].cnt_freq = freq;
+	tm->counters[cid].loop_delay = tm->loop_delay_fastclk * ratio;
+
+	/* set timer to free-running mode */
+	tmp = readl_relaxed(tm->base + TMR_CMR) | TMR_CER_COUNTER(cid);
+	writel_relaxed(tmp, tm->base + TMR_CMR);
+
+	/* free-running */
+	writel_relaxed(0x0, tm->base + TMR_PLCR(cid));
+	/* clear status */
+	writel_relaxed(0x7, tm->base + TMR_ICR(cid));
+
+	/* enable counter */
+	tmp = readl_relaxed(tm->base + TMR_CER) | TMR_CER_COUNTER(cid);
+	writel_relaxed(tmp, tm->base + TMR_CER);
+
+	delay = tm->counters[cid].loop_delay;
+	while (delay--)
+		writel_relaxed(tmp, tm->base + TMR_CER);
+
+	return 0;
+}
+
+int __init mmp_counter_clocksource_init(int tid, int cid, unsigned int freq)
+{
+	struct mmp_timer *mt = mmp_timers[tid];
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (clksrc) {
+		pr_err("One clksrc has already been registered!\n");
+		return -EINVAL;
+	}
+	clksrc = kzalloc(sizeof(*clksrc), GFP_KERNEL);
+	if (!clksrc)
+		return -ENOMEM;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_CLKSRC;
+	mt->counters[cid].cnt_freq = freq;
+
+	clksrc->counter = &mt->counters[cid];
+	clksrc->cs.name = "clocksource-mmp";
+	clksrc->cs.rating = 200;
+	clksrc->cs.read = clksrc_read;
+	clksrc->cs.mask = CLOCKSOURCE_MASK(32);
+	clksrc->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+
+	sched_clock_register(mmp_read_sched_clock, 32, freq);
+
+	clocksource_register_hz(&(clksrc->cs), freq);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int __init mmp_counter_timer_delay_init(int tid, int cid, unsigned int freq)
+{
+#ifdef CONFIG_ARM
+	struct mmp_timer *mt = mmp_timers[tid];
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (dclk) {
+		pr_err("Delay clock has already been registered!\n");
+		return -EINVAL;
+	}
+	dclk = kzalloc(sizeof(*dclk), GFP_KERNEL);
+	if (!dclk)
+		return -ENOMEM;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_DELAY;
+	mt->counters[cid].cnt_freq = freq;
+
+	dclk->counter = &mt->counters[cid];
+	dclk->dt = &d_timer;
+	d_timer.freq = freq;
+	register_current_timer_delay(&d_timer);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+#endif
+	return 0;
+}
+
+int __init mmp_counter_clockevent_init(int tid, int cid, int irq,
+				       int freq, int dynirq, unsigned int cpu)
+{
+	struct mmp_timer *mt = mmp_timers[tid];
+	struct mmp_timer_clkevt *clkevt;
+	int broadcast = 0;
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (cpu == MMP_TIMER_ALL_CPU)
+		broadcast = 1;
+	else if (cpu >= num_possible_cpus())
+		return -EINVAL;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_CLKEVT;
+	mt->counters[cid].cnt_freq = freq;
+	mt->counters[cid].cpu = cpu;
+
+	clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
+	if (!clkevt)
+		return -ENOMEM;
+	clkevt->counter = &mt->counters[cid];
+	clkevt->ced.name = "clockevent-mmp";
+	clkevt->ced.features = CLOCK_EVT_FEAT_ONESHOT;
+	clkevt->ced.rating = 200;
+	clkevt->ced.set_next_event = timer_set_next_event;
+	clkevt->ced.set_mode = timer_set_mode;
+	clkevt->ced.irq = irq;
+
+	clkevt->irqa.flags = IRQF_TIMER | IRQF_IRQPOLL;
+	clkevt->irqa.handler = timer_interrupt;
+	clkevt->irqa.dev_id = &(clkevt->ced);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+
+	if (broadcast) {
+		clkevt->irqa.name = "broadcast-timer";
+		if (dynirq)
+			clkevt->ced.features |= CLOCK_EVT_FEAT_DYNIRQ;
+		clkevt->ced.cpumask = cpu_possible_mask;
+		setup_irq(clkevt->ced.irq, &(clkevt->irqa));
+		clockevents_config_and_register(&clkevt->ced,
+						freq, MIN_DELTA, MAX_DELTA);
+	} else {
+		clkevt->irqa.name = "local-timer";
+		clkevt->ced.cpumask = cpumask_of(cpu);
+		clkevt->nb.notifier_call = mmp_timer_cpu_notify;
+		register_cpu_notifier(&clkevt->nb);
+		setup_irq(clkevt->ced.irq, &(clkevt->irqa));
+		/* Enable clock event device for boot CPU. */
+		if (cpu == smp_processor_id()) {
+			clockevents_config_and_register(&clkevt->ced,
+							freq, MIN_DELTA,
+							MAX_DELTA);
+			/* Only online CPU can be set affinity. */
+			irq_set_affinity(clkevt->ced.irq, cpumask_of(cpu));
+		} else {
+			/* disable none boot CPU's irq at first */
+			disable_irq(clkevt->ced.irq);
+		}
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+
+struct of_device_id mmp_counter_of_id[] = {
+	{
+		.compatible = "marvell,timer-counter-clksrc",
+		.data = (void *)MMP_TIMER_COUNTER_CLKSRC,
+	},
+	{
+		.compatible = "marvell,timer-counter-clkevt",
+		.data = (void *)MMP_TIMER_COUNTER_CLKEVT,
+	},
+	{
+		.compatible = "marvell,timer-counter-delay",
+		.data = (void *)MMP_TIMER_COUNTER_DELAY,
+	},
+	{ },
+};
+
+static int __init mmp_of_counter_init(struct device_node *np, int tid,
+				      unsigned int usage)
+{
+	int irq, ret, dynirq;
+	unsigned int cid, freq, cpu;
+	struct mmp_timer *mt = mmp_timers[tid];
+
+	if (!np)
+		return -EINVAL;
+
+	if (of_property_read_bool(np, "marvell,fast-clock"))
+		freq = mt->fc_freq;
+	else
+		freq = MMP_TIMER_CLOCK_32KHZ;
+
+	ret = of_property_read_u32(np, "marvell,timer-counter-id", &cid);
+	if (ret) {
+		pr_err("Timer %d: fail to get counter id\n", tid);
+		return ret;
+	}
+
+	if (usage & MMP_TIMER_COUNTER_DELAY) {
+		ret = mmp_counter_timer_delay_init(tid, cid, freq);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create delay timer\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+
+	if (usage & MMP_TIMER_COUNTER_CLKSRC) {
+		ret = mmp_counter_clocksource_init(tid, cid, freq);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create clksrc\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+	if (usage & MMP_TIMER_COUNTER_CLKEVT) {
+		if (of_property_read_bool(np,
+					  "marvell,timer-counter-broadcast")) {
+			cpu = MMP_TIMER_ALL_CPU;
+		} else {
+			ret = of_property_read_u32(np,
+						   "marvell,timer-counter-cpu",
+						   &cpu);
+			if (ret) {
+				pr_err("Timer %d:%d: fail to get cpu\n",
+				       tid, cid);
+				return ret;
+			}
+		}
+		dynirq = !of_property_read_bool(np,
+				"marvell,timer-counter-nodynirq");
+		irq = irq_of_parse_and_map(np, 0);
+		ret = mmp_counter_clockevent_init(tid, cid,
+						  irq, freq, dynirq, cpu);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create clkevt\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void __init mmp_of_timer_init(struct device_node *np)
+{
+	int tid;
+	unsigned int flag, fc_freq, apb_freq;
+	struct clk *apb_clk, *fast_clk;
+	void __iomem *base;
+	struct device_node *child_np;
+	const struct of_device_id *match;
+	int ret = 0;
+
+	for (tid = 0; tid < MMP_MAX_TIMER; tid++)
+		if (mmp_timers[tid] == NULL)
+			break;
+
+	if (tid >= MMP_MAX_TIMER)
+		return;
+
+	apb_clk = of_clk_get(np, 0);
+	if (!apb_clk)
+		return;
+	apb_freq = clk_get_rate(apb_clk);
+
+	fast_clk = of_clk_get(np, 1);
+	if (!fast_clk)
+		return;
+	fc_freq = clk_get_rate(fast_clk);
+
+	clk_prepare_enable(fast_clk);
+
+	/* timer initialization */
+	base = of_iomap(np, 0);
+	if (!base) {
+		pr_err("Timer: fail to map register space\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	flag = 0;
+	if (of_property_read_bool(np, "marvell,timer-has-crsr"))
+		flag |= MMP_TIMER_FLAG_CRSR;
+
+	if (of_property_read_bool(np, "marvell,timer-has-shadow"))
+		flag |= MMP_TIMER_FLAG_SHADOW;
+
+	ret = mmp_timer_init(tid, base, flag, fc_freq, apb_freq);
+	if (ret)
+		goto out;
+
+	/*
+	 * If device node is marked as not available,
+	 * we then don't try to enable the counter again
+	 */
+	if (!of_device_is_available(np)) {
+		pr_warn("Timer %d: is not used\n", tid);
+		return;
+	}
+
+	/* counter initialization */
+	for_each_child_of_node(np, child_np) {
+		match = of_match_node(mmp_counter_of_id, child_np);
+		ret = mmp_of_counter_init(child_np, tid,
+					  (unsigned int)match->data);
+		if (ret)
+			goto out;
+	}
+
+	return;
+out:
+	if (ret)
+		pr_err("Failed to get timer from dtb with error:%d\n", ret);
+}
+
+CLOCKSOURCE_OF_DECLARE(mmp_timer, "marvell,mmp-timer", mmp_of_timer_init);
+#endif
diff --git a/include/linux/mmp_timer.h b/include/linux/mmp_timer.h
new file mode 100644
index 0000000..6ca7f80
--- /dev/null
+++ b/include/linux/mmp_timer.h
@@ -0,0 +1,40 @@
+/*
+ * mmp_timer: Soc timer driver for mmp architecture.
+ *
+ * Copyright (C) 2008 Marvell International Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MMP_TIMER_H__
+#define __LINUX_MMP_TIMER_H__
+
+/* timer flag bit definition */
+/* bit[0]: MMP_TIMER_FLAG_SHADOW
+ *         Indicate if the timer has shadow registers. If it has,
+ *         counter could be read out directly.
+ * bit[1]: MMP_TIMER_FLAG_CRSR
+ *         Indicate if timer has CRSR register. If it has,
+ *         counter could be restarted by directly writing CRSR.
+ */
+#define MMP_TIMER_FLAG_SHADOW	(1 << 0)
+#define MMP_TIMER_FLAG_CRSR	(1 << 1)
+
+#define MMP_TIMER_COUNTER_CLKSRC	(1 << 0)
+#define MMP_TIMER_COUNTER_CLKEVT	(1 << 1)
+#define MMP_TIMER_COUNTER_DELAY		(1 << 2)
+
+#define MMP_TIMER_ALL_CPU	(0xFFFFFFFF)
+
+int __init mmp_timer_init(int id, void __iomem *base,
+			  unsigned int flag, unsigned int fc_freq,
+			  unsigned int apb_freq);
+
+int __init mmp_counter_clocksource_init(int tid, int cid, unsigned int freq);
+int __init mmp_counter_timer_delay_init(int tid, int cid, unsigned int freq);
+int __init mmp_counter_clockevent_init(int tid, int cid, int irq,
+				       int freq, int dynirq, unsigned int cpu);
+
+#endif /* __LINUX_MMP_TIMER_H__ */
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 1/4] clocksource: mmp: add mmp timer driver
@ 2015-02-02  8:31   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-02  8:31 UTC (permalink / raw)
  To: mark.rutland, daniel.lezcano, tglx, chao.xie, haojian.zhuang,
	linux-kernel, devicetree

From: Chao Xie <chao.xie@marvell.com>

MMP timer is attached to APB bus, It has the following
limitation.
1. When get count of timer counter, it need some delay
   to get a stable count.
2. When set match register, it need disable the counter
   first, and enable it after set the match register.
   The disabling need wait for 2 clock cycle to take
   effect.

To improve above #1, shadow register for count is added.
To improve above #2, CRSR register is added for quick updating.

So there are three types of timer.
timer1: old timer.
timer2: old timer with shadow register.
timer3: old timer with shadow and CRSR register.

This timer driver will be used for many SOCes.
1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
2. pxa1L88, pxa1U88 have timer1 and timer3.
3. pxa1928 has timer 2.

The driver supports DT and non-DT initialization.
The driver supports UP/SMP SOCes and 64 bit core.

Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
 .../devicetree/bindings/arm/mrvl/timer.txt         |  61 +-
 drivers/clocksource/Makefile                       |   1 +
 drivers/clocksource/timer-mmp.c                    | 837 +++++++++++++++++++++
 include/linux/mmp_timer.h                          |  40 +
 4 files changed, 933 insertions(+), 6 deletions(-)
 create mode 100644 drivers/clocksource/timer-mmp.c
 create mode 100644 include/linux/mmp_timer.h

diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
index 9a6e251..b49cb3e 100644
--- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
+++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
@@ -1,13 +1,62 @@
 * Marvell MMP Timer controller
 
+Each timer have multiple counters, so the timer DT need include counter's
+description.
+
+1. Timer
+
 Required properties:
-- compatible : Should be "mrvl,mmp-timer".
+- compatible : Should be "marvell,mmp-timer".
 - reg : Address and length of the register set of timer controller.
-- interrupts : Should be the interrupt number.
+- clocks : The first clock is the fequency of the apb bus that the timer
+  attached to. The second clock is the fast clock frequency of the timer.
+  This frequency and fast clock are used to calculated delay
+  loops for clock operations.
+
+Optional properties:
+- marvell,timer-has-crsr : This timer has CRSR register.
+- marvell,timer-has-shadow : This timer has shadow register.
+
+2. Counter
+
+Required properties:
+- compatible : It can be
+      "marvell,timer-counter-clkevt" : The counter is used for clock event
+                                       device.
+      "marvell,timer-counter-clksrc" : The counter is used for clock source.
+      "marvell,timer-counter-delay" : The counter is used for delay timer.
+- marvell,timer-counter-id : The counter index in this timer.
 
-Example:
-	timer0: timer@d4014000 {
-		compatible = "mrvl,mmp-timer";
-		reg = <0xd4014000 0x100>;
+Optional properties:
+- marvell,fast-clock : whether the counter use the fast-clock for counting.
+- interrupts : The interrupt for clock event device.
+  Only valid for "marvell,timer-counter-clkevt".
+- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
+  "marvell,timer-counter-clkevt".
+- marvell,timer-counter-broadcast : When this counter acts as clock event
+  device. It is broadcast clock event device.
+  Only valid for "marvell,timer-counter-clkevt".
+- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
+  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
+  Only valid for "marvell,timer-counter-clkevt".
+
+3. Examples
+
+timer0: timer@d4014000 {
+	compatible = "marvell,mmp-timer";
+	reg = <0xd4014000 0xc8>;
+	clocks = <&soc_clocks MMP2_PLL1_24>, <&soc_closk MMP2_CLK_TIMER>;
+	status = "okay";
+
+	counter0 {
+		compatible = "marvell,timer-counter-clkevt";
 		interrupts = <13>;
+		marvell,timer-counter-id = <0>;
+		marvell,timer-counter-cpu = <0>;
+	};
+
+	counter1 {
+		compatible = "marvell,timer-counter-clksrc";
+		marvell,timer-counter-id = <1>;
 	};
+};
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 94d90b2..6a66d4d 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ORION_TIMER)	+= time-orion.o
 obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o
 obj-$(CONFIG_ARCH_CLPS711X)	+= clps711x-timer.o
 obj-$(CONFIG_ARCH_MARCO)	+= timer-marco.o
+obj-$(CONFIG_ARCH_MMP)		+= timer-mmp.o
 obj-$(CONFIG_ARCH_MOXART)	+= moxart_timer.o
 obj-$(CONFIG_ARCH_MXS)		+= mxs_timer.o
 obj-$(CONFIG_ARCH_PXA)		+= pxa_timer.o
diff --git a/drivers/clocksource/timer-mmp.c b/drivers/clocksource/timer-mmp.c
new file mode 100644
index 0000000..b6a9ce1
--- /dev/null
+++ b/drivers/clocksource/timer-mmp.c
@@ -0,0 +1,837 @@
+/*
+ * driver/clocksource/timer-mmp.c
+ *
+ *   Support for clocksource and clockevents
+ *
+ * Copyright (C) 2008 Marvell International Ltd.
+ * All rights reserved.
+ *
+ *   2008-04-11: Jason Chagas <Jason.chagas@marvell.com>
+ *   2008-10-08: Bin Yang <bin.yang@marvell.com>
+ *
+ * The timers module actually includes three timers, each timer with up to
+ * three match comparators. Timer #0 is used here in free-running mode as
+ * the clock source, and match comparator #1 used as clock event device.
+ *
+ * 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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/mmp_timer.h>
+#include <linux/clockchips.h>
+#include <linux/sched_clock.h>
+#include <linux/clk.h>
+
+#define TMR_CCR		(0x0000)
+#define TMR_TN_MM(n, m)	(0x0004 + ((n) << 3) + (((n) + (m)) << 2))
+#define TMR_CR(n)	(0x0028 + ((n) << 2))
+#define TMR_SR(n)	(0x0034 + ((n) << 2))
+#define TMR_IER(n)	(0x0040 + ((n) << 2))
+#define TMR_PLVR(n)	(0x004c + ((n) << 2))
+#define TMR_PLCR(n)	(0x0058 + ((n) << 2))
+#define TMR_WMER	(0x0064)
+#define TMR_WMR		(0x0068)
+#define TMR_WVR		(0x006c)
+#define TMR_WSR		(0x0070)
+#define TMR_ICR(n)	(0x0074 + ((n) << 2))
+#define TMR_WICR	(0x0080)
+#define TMR_CER		(0x0084)
+#define TMR_CMR		(0x0088)
+#define TMR_ILR(n)	(0x008c + ((n) << 2))
+#define TMR_WCR		(0x0098)
+#define TMR_WFAR	(0x009c)
+#define TMR_WSAR	(0x00A0)
+#define TMR_CVWR(n)	(0x00A4 + ((n) << 2))
+#define TMR_CRSR        (0x00B0)
+
+#define TMR_CCR_CS_0(x)	(((x) & 0x3) << 0)
+#define TMR_CCR_CS_1(x)	(((x) & 0x3) << 2)
+#define TMR_CCR_CS_2(x)	(((x) & 0x3) << 5)
+
+#define MAX_EVT_NUM		5
+
+#define MAX_DELTA		(0xfffffffe)
+#define MIN_DELTA		(5)
+
+#define MMP_MAX_COUNTER		3
+#define MMP_MAX_TIMER		4
+
+#define TMR_CER_COUNTER(cid)	(1 << (cid))
+#define MMP_ALL_COUNTERS	((1 << MMP_MAX_COUNTER) - 1)
+
+#define MMP_TIMER_CLOCK_32KHZ	32768
+#define MMP_TIMER_CLOCK_1KHZ	1000
+
+struct mmp_timer;
+struct mmp_timer_counter {
+	unsigned int id;
+	unsigned int usage;
+	unsigned int cnt_freq;
+	int cpu;
+	int loop_delay;
+	struct mmp_timer *timer;
+};
+
+struct mmp_timer {
+	unsigned int id;
+	void __iomem *base;
+	struct mmp_timer_counter counters[MMP_MAX_COUNTER];
+	unsigned int flag;
+	int loop_delay_fastclk;
+	unsigned int fc_freq;
+	/* lock to protect hw operation. */
+	spinlock_t tm_lock;
+};
+
+struct mmp_timer_clkevt {
+	struct mmp_timer_counter *counter;
+	struct clock_event_device ced;
+	struct irqaction irqa;
+	struct notifier_block nb;
+};
+
+struct mmp_timer_clksrc {
+	struct mmp_timer_counter *counter;
+	struct clocksource cs;
+};
+
+#ifdef CONFIG_ARM
+struct mmp_timer_dclk {
+	struct mmp_timer_counter *counter;
+	struct delay_timer *dt;
+};
+static struct mmp_timer_dclk *dclk;
+#endif
+
+static struct mmp_timer *mmp_timers[MMP_MAX_TIMER];
+static struct mmp_timer_clksrc *clksrc;
+
+static int timer_counter_switch_clock(int tid, int cid, unsigned int freq)
+{
+	struct mmp_timer *tm = mmp_timers[tid];
+	u32 ccr, mask;
+
+	ccr = readl_relaxed(tm->base + TMR_CCR);
+
+	if (cid == 0)
+		mask = TMR_CCR_CS_0(3);
+	else if (cid == 1)
+		mask = TMR_CCR_CS_1(3);
+	else
+		mask = TMR_CCR_CS_2(3);
+
+	ccr &= ~mask;
+
+	if (freq == MMP_TIMER_CLOCK_32KHZ) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(2);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(1);
+		else if (cid == 0)
+			ccr |= TMR_CCR_CS_0(1);
+	} else if (freq == MMP_TIMER_CLOCK_1KHZ) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(1);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(2);
+	} else if (freq == tm->fc_freq) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(0);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(0);
+		else if (cid == 0)
+			ccr |= TMR_CCR_CS_0(0);
+	} else {
+		pr_err("Timer %d:%d: invalid clock rate %d\n", tid, cid, freq);
+		return -EINVAL;
+	}
+
+	writel_relaxed(ccr, tm->base + TMR_CCR);
+
+	return 0;
+}
+
+static void timer_counter_disable(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	int delay = tm->loop_delay_fastclk;
+	u32 cer;
+
+	/*
+	 * Stop the counter will need multiple timer clock to take effect.
+	 * Some operations can only be done when counter is disabled. So
+	 * add delay here.
+	 */
+	/* Step1: disable counter */
+	cer = readl_relaxed(tm->base + TMR_CER);
+	writel_relaxed(cer & ~(1 << cnt->id), tm->base + TMR_CER);
+
+	/*
+	 * Step2: switch to fast clock, so the delay can be completed
+	 * quickly.
+	 */
+	if (cnt->cnt_freq != tm->fc_freq)
+		timer_counter_switch_clock(tm->id, cnt->id, tm->fc_freq);
+
+	/*
+	 * Step3: Loop for multiple timer cycles. We do it by clearing
+	 * pending interrupt status.
+	 */
+	while (delay--)
+		writel_relaxed(0x1, tm->base + TMR_ICR(cnt->id));
+}
+
+static void timer_counter_enable(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	u32 cer;
+
+	/* Switch to original clock */
+	if (cnt->cnt_freq != tm->fc_freq)
+		timer_counter_switch_clock(tm->id, cnt->id, cnt->cnt_freq);
+
+	/* Enable timer */
+	cer = readl_relaxed(tm->base + TMR_CER);
+	writel_relaxed(cer | (1 << cnt->id), tm->base + TMR_CER);
+}
+
+static inline uint32_t timer_read(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	int has_shadow = tm->flag & MMP_TIMER_FLAG_SHADOW;
+	int delay = 3;
+	u32 val1, val2;
+
+	if (has_shadow)
+		return readl_relaxed(tm->base + TMR_CR(cnt->id));
+
+	if (cnt->cnt_freq != tm->fc_freq) {
+		/* slow clock */
+		do {
+			val1 = readl_relaxed(tm->base + TMR_CR(cnt->id));
+			val2 = readl_relaxed(tm->base + TMR_CR(cnt->id));
+		} while (val2 != val1);
+	} else {
+		/* fast clock */
+		writel_relaxed(1, tm->base + TMR_CVWR(cnt->id));
+		while (delay--)
+			val1 = readl_relaxed(tm->base +
+					TMR_CVWR(cnt->id));
+	}
+
+	return val1;
+}
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *c = dev_id;
+	struct mmp_timer_clkevt *evt;
+	struct mmp_timer_counter *counter;
+	unsigned int cnt;
+	unsigned long flags;
+	void __iomem *base;
+	int has_crsr;
+
+	evt = container_of(c, struct mmp_timer_clkevt, ced);
+	counter = evt->counter;
+	cnt = counter->id;
+	base = counter->timer->base;
+	has_crsr = counter->timer->flag & MMP_TIMER_FLAG_CRSR;
+
+	spin_lock_irqsave(&(counter->timer->tm_lock), flags);
+	/* We only use match #0 for the counter. */
+	if (readl_relaxed(base + TMR_SR(cnt)) & 0x1) {
+		if (!has_crsr)
+			timer_counter_disable(counter);
+
+		/* Disable the interrupt. */
+		writel_relaxed(0x00, base + TMR_IER(cnt));
+		/* Clear interrupt status */
+		writel_relaxed(0x01, base + TMR_ICR(cnt));
+
+		spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+
+		c->event_handler(c);
+
+		return IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+
+	return IRQ_NONE;
+}
+
+static int timer_set_next_event(unsigned long delta,
+				struct clock_event_device *dev)
+{
+	struct mmp_timer_counter *cnt;
+	struct mmp_timer_clkevt *evt;
+	unsigned long flags;
+	unsigned int cid;
+	u32 cer, crsr;
+	void __iomem *base;
+	int delay, has_crsr;
+
+	evt = container_of(dev, struct mmp_timer_clkevt, ced);
+	cnt = evt->counter;
+	cid = cnt->id;
+	base = cnt->timer->base;
+	has_crsr = cnt->timer->flag & MMP_TIMER_FLAG_CRSR;
+
+	spin_lock_irqsave(&(cnt->timer->tm_lock), flags);
+	if (has_crsr) {
+		/*
+		 * Use TMR_CRSR to restart the counter and make match
+		 * register take effect. This bit should be 0 before
+		 * set it again.
+		 * The polling loop is defined by loop_delay.
+		 */
+		delay = cnt->loop_delay;
+		do {
+			crsr = readl_relaxed(base + TMR_CRSR);
+			delay--;
+		} while ((crsr & (1 << cid)) && delay > 0);
+
+		BUG_ON(delay <= 0);
+
+		writel_relaxed(delta - 1, base + TMR_TN_MM(cid, 0));
+		/*
+		 * After counter is restart, clear the interrupt status for
+		 * safe, and re-enable the interrupt for match #0.
+		 */
+		writel_relaxed(0x01, base + TMR_ICR(cid));
+		writel_relaxed(0x01, base + TMR_IER(cid));
+		writel_relaxed((1 << cid), base + TMR_CRSR);
+	} else {
+		cer = readl_relaxed(base + TMR_CER);
+
+		/* If the timer counter is enabled, first disable it. */
+		if (cer & (1 << cid))
+			timer_counter_disable(cnt);
+
+		/* Setup new counter value */
+		writel_relaxed(delta - 1, base + TMR_TN_MM(cid, 0));
+
+		/* enable the matching interrupt */
+		writel_relaxed(0x1, base + TMR_IER(cid));
+
+		timer_counter_enable(cnt);
+	}
+	spin_unlock_irqrestore(&(cnt->timer->tm_lock), flags);
+
+	return 0;
+}
+
+static void timer_set_mode(enum clock_event_mode mode,
+			   struct clock_event_device *dev)
+{
+	unsigned long flags;
+	unsigned int cnt;
+	struct mmp_timer_counter *counter;
+	struct mmp_timer_clkevt *evt;
+	void __iomem *base;
+
+	evt = container_of(dev, struct mmp_timer_clkevt, ced);
+	counter = evt->counter;
+	cnt = counter->id;
+	base = counter->timer->base;
+
+	spin_lock_irqsave(&(counter->timer->tm_lock), flags);
+	switch (mode) {
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		timer_counter_disable(counter);
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+	case CLOCK_EVT_MODE_ONESHOT:
+		timer_counter_enable(counter);
+		break;
+	}
+	spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+}
+
+static cycle_t clksrc_read(struct clocksource *cs)
+{
+	return timer_read(clksrc->counter);
+}
+
+static u64 notrace mmp_read_sched_clock(void)
+{
+	return timer_read(clksrc->counter);
+}
+
+#ifdef CONFIG_ARM
+static unsigned long d_read_current_timer(void)
+{
+	return timer_read(dclk->counter);
+}
+
+static struct delay_timer d_timer = {
+	.read_current_timer	= d_read_current_timer,
+};
+#endif
+
+static int mmp_timer_cpu_notify(struct notifier_block *self,
+				unsigned long action, void *hcpu)
+{
+	struct mmp_timer_clkevt *evt;
+	struct mmp_timer_counter *cnt;
+
+	evt = container_of(self, struct mmp_timer_clkevt, nb);
+	cnt = evt->counter;
+
+	if (cnt->cpu != (unsigned long)hcpu)
+		return NOTIFY_OK;
+
+	switch (action & ~CPU_TASKS_FROZEN) {
+	case CPU_STARTING:
+		clockevents_config_and_register(&evt->ced,
+						cnt->cnt_freq,
+						MIN_DELTA, MAX_DELTA);
+		break;
+	case CPU_ONLINE:
+		irq_set_affinity(evt->ced.irq, evt->ced.cpumask);
+		enable_irq(evt->ced.irq);
+		break;
+	case CPU_DYING:
+		clockevents_set_mode(&evt->ced,
+				     CLOCK_EVT_MODE_SHUTDOWN);
+		disable_irq(evt->ced.irq);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+int __init mmp_timer_init(int tid, void __iomem *base,
+			  unsigned int flag, unsigned int fc_freq,
+			  unsigned int apb_freq)
+{
+	struct mmp_timer *tm = mmp_timers[tid];
+	u32 tmp, delay;
+	int cid;
+
+	if (tm)
+		return -EINVAL;
+
+	tm = kzalloc(sizeof(*tm), GFP_KERNEL);
+	if (!tm)
+		return -ENOMEM;
+
+	/*
+	 * The calculation formula for the loop cycle is:
+	 *
+	 * (1) need wait for 2 timer's clock cycle:
+	 *        1             2
+	 *     ------- x 2 = -------
+	 *     fc_freq       fc_freq
+	 *
+	 * (2) convert to apb clock cycle:
+	 *        2          1        apb_freq * 2
+	 *     ------- / -------- = ----------------
+	 *     fc_freq   apb_freq       fc_freq
+	 *
+	 * (3) every apb register's accessing will take 8 apb clock cycle,
+	 *     also consider add extral one more time for safe way;
+	 *     so finally need loop times for the apb register accessing:
+	 *
+	 *       (apb_freq * 2)
+	 *     ------------------ / 8 + 1
+	 *          fc_freq
+	 */
+	delay = ((apb_freq * 2) / fc_freq / 8) + 1;
+	pr_debug("Timer %d: loop_delay_fastclk is %d\n", tid, delay);
+
+	tm->id = tid;
+	tm->base = base;
+	tm->flag = flag;
+	tm->loop_delay_fastclk = delay;
+	tm->fc_freq = fc_freq;
+	spin_lock_init(&(tm->tm_lock));
+
+	mmp_timers[tid] = tm;
+
+	for (cid = 0; cid < MMP_MAX_COUNTER; cid++) {
+		tm->counters[cid].id = cid;
+		tm->counters[cid].timer = tm;
+
+		/* We will disable all counters. Switch to fastclk first. */
+		timer_counter_switch_clock(tid, cid, fc_freq);
+	}
+
+	/* disalbe all counters */
+	tmp = readl_relaxed(base + TMR_CER) & ~MMP_ALL_COUNTERS;
+	writel_relaxed(tmp, base + TMR_CER);
+
+	/* disable matching interrupt */
+	writel_relaxed(0x00, base + TMR_IER(0));
+	writel_relaxed(0x00, base + TMR_IER(1));
+	writel_relaxed(0x00, base + TMR_IER(2));
+
+	while (delay--) {
+		/* Clear pending interrupt status */
+		writel_relaxed(0x1, base + TMR_ICR(0));
+		writel_relaxed(0x1, base + TMR_ICR(1));
+		writel_relaxed(0x1, base + TMR_ICR(2));
+		writel_relaxed(tmp, base + TMR_CER);
+	}
+
+	return 0;
+}
+
+static int __init mmp_timer_counter_hw_init(struct mmp_timer *tm, int cid,
+					    unsigned int freq)
+{
+	u32 tmp, delay;
+	unsigned int ratio;
+	int ret;
+
+	ret = timer_counter_switch_clock(tm->id, cid, freq);
+	if (ret)
+		return ret;
+
+	ratio = tm->fc_freq / tm->counters[cid].cnt_freq;
+	tm->counters[cid].cnt_freq = freq;
+	tm->counters[cid].loop_delay = tm->loop_delay_fastclk * ratio;
+
+	/* set timer to free-running mode */
+	tmp = readl_relaxed(tm->base + TMR_CMR) | TMR_CER_COUNTER(cid);
+	writel_relaxed(tmp, tm->base + TMR_CMR);
+
+	/* free-running */
+	writel_relaxed(0x0, tm->base + TMR_PLCR(cid));
+	/* clear status */
+	writel_relaxed(0x7, tm->base + TMR_ICR(cid));
+
+	/* enable counter */
+	tmp = readl_relaxed(tm->base + TMR_CER) | TMR_CER_COUNTER(cid);
+	writel_relaxed(tmp, tm->base + TMR_CER);
+
+	delay = tm->counters[cid].loop_delay;
+	while (delay--)
+		writel_relaxed(tmp, tm->base + TMR_CER);
+
+	return 0;
+}
+
+int __init mmp_counter_clocksource_init(int tid, int cid, unsigned int freq)
+{
+	struct mmp_timer *mt = mmp_timers[tid];
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (clksrc) {
+		pr_err("One clksrc has already been registered!\n");
+		return -EINVAL;
+	}
+	clksrc = kzalloc(sizeof(*clksrc), GFP_KERNEL);
+	if (!clksrc)
+		return -ENOMEM;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_CLKSRC;
+	mt->counters[cid].cnt_freq = freq;
+
+	clksrc->counter = &mt->counters[cid];
+	clksrc->cs.name = "clocksource-mmp";
+	clksrc->cs.rating = 200;
+	clksrc->cs.read = clksrc_read;
+	clksrc->cs.mask = CLOCKSOURCE_MASK(32);
+	clksrc->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+
+	sched_clock_register(mmp_read_sched_clock, 32, freq);
+
+	clocksource_register_hz(&(clksrc->cs), freq);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int __init mmp_counter_timer_delay_init(int tid, int cid, unsigned int freq)
+{
+#ifdef CONFIG_ARM
+	struct mmp_timer *mt = mmp_timers[tid];
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (dclk) {
+		pr_err("Delay clock has already been registered!\n");
+		return -EINVAL;
+	}
+	dclk = kzalloc(sizeof(*dclk), GFP_KERNEL);
+	if (!dclk)
+		return -ENOMEM;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_DELAY;
+	mt->counters[cid].cnt_freq = freq;
+
+	dclk->counter = &mt->counters[cid];
+	dclk->dt = &d_timer;
+	d_timer.freq = freq;
+	register_current_timer_delay(&d_timer);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+#endif
+	return 0;
+}
+
+int __init mmp_counter_clockevent_init(int tid, int cid, int irq,
+				       int freq, int dynirq, unsigned int cpu)
+{
+	struct mmp_timer *mt = mmp_timers[tid];
+	struct mmp_timer_clkevt *clkevt;
+	int broadcast = 0;
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (cpu == MMP_TIMER_ALL_CPU)
+		broadcast = 1;
+	else if (cpu >= num_possible_cpus())
+		return -EINVAL;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_CLKEVT;
+	mt->counters[cid].cnt_freq = freq;
+	mt->counters[cid].cpu = cpu;
+
+	clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
+	if (!clkevt)
+		return -ENOMEM;
+	clkevt->counter = &mt->counters[cid];
+	clkevt->ced.name = "clockevent-mmp";
+	clkevt->ced.features = CLOCK_EVT_FEAT_ONESHOT;
+	clkevt->ced.rating = 200;
+	clkevt->ced.set_next_event = timer_set_next_event;
+	clkevt->ced.set_mode = timer_set_mode;
+	clkevt->ced.irq = irq;
+
+	clkevt->irqa.flags = IRQF_TIMER | IRQF_IRQPOLL;
+	clkevt->irqa.handler = timer_interrupt;
+	clkevt->irqa.dev_id = &(clkevt->ced);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+
+	if (broadcast) {
+		clkevt->irqa.name = "broadcast-timer";
+		if (dynirq)
+			clkevt->ced.features |= CLOCK_EVT_FEAT_DYNIRQ;
+		clkevt->ced.cpumask = cpu_possible_mask;
+		setup_irq(clkevt->ced.irq, &(clkevt->irqa));
+		clockevents_config_and_register(&clkevt->ced,
+						freq, MIN_DELTA, MAX_DELTA);
+	} else {
+		clkevt->irqa.name = "local-timer";
+		clkevt->ced.cpumask = cpumask_of(cpu);
+		clkevt->nb.notifier_call = mmp_timer_cpu_notify;
+		register_cpu_notifier(&clkevt->nb);
+		setup_irq(clkevt->ced.irq, &(clkevt->irqa));
+		/* Enable clock event device for boot CPU. */
+		if (cpu == smp_processor_id()) {
+			clockevents_config_and_register(&clkevt->ced,
+							freq, MIN_DELTA,
+							MAX_DELTA);
+			/* Only online CPU can be set affinity. */
+			irq_set_affinity(clkevt->ced.irq, cpumask_of(cpu));
+		} else {
+			/* disable none boot CPU's irq at first */
+			disable_irq(clkevt->ced.irq);
+		}
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+
+struct of_device_id mmp_counter_of_id[] = {
+	{
+		.compatible = "marvell,timer-counter-clksrc",
+		.data = (void *)MMP_TIMER_COUNTER_CLKSRC,
+	},
+	{
+		.compatible = "marvell,timer-counter-clkevt",
+		.data = (void *)MMP_TIMER_COUNTER_CLKEVT,
+	},
+	{
+		.compatible = "marvell,timer-counter-delay",
+		.data = (void *)MMP_TIMER_COUNTER_DELAY,
+	},
+	{ },
+};
+
+static int __init mmp_of_counter_init(struct device_node *np, int tid,
+				      unsigned int usage)
+{
+	int irq, ret, dynirq;
+	unsigned int cid, freq, cpu;
+	struct mmp_timer *mt = mmp_timers[tid];
+
+	if (!np)
+		return -EINVAL;
+
+	if (of_property_read_bool(np, "marvell,fast-clock"))
+		freq = mt->fc_freq;
+	else
+		freq = MMP_TIMER_CLOCK_32KHZ;
+
+	ret = of_property_read_u32(np, "marvell,timer-counter-id", &cid);
+	if (ret) {
+		pr_err("Timer %d: fail to get counter id\n", tid);
+		return ret;
+	}
+
+	if (usage & MMP_TIMER_COUNTER_DELAY) {
+		ret = mmp_counter_timer_delay_init(tid, cid, freq);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create delay timer\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+
+	if (usage & MMP_TIMER_COUNTER_CLKSRC) {
+		ret = mmp_counter_clocksource_init(tid, cid, freq);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create clksrc\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+	if (usage & MMP_TIMER_COUNTER_CLKEVT) {
+		if (of_property_read_bool(np,
+					  "marvell,timer-counter-broadcast")) {
+			cpu = MMP_TIMER_ALL_CPU;
+		} else {
+			ret = of_property_read_u32(np,
+						   "marvell,timer-counter-cpu",
+						   &cpu);
+			if (ret) {
+				pr_err("Timer %d:%d: fail to get cpu\n",
+				       tid, cid);
+				return ret;
+			}
+		}
+		dynirq = !of_property_read_bool(np,
+				"marvell,timer-counter-nodynirq");
+		irq = irq_of_parse_and_map(np, 0);
+		ret = mmp_counter_clockevent_init(tid, cid,
+						  irq, freq, dynirq, cpu);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create clkevt\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void __init mmp_of_timer_init(struct device_node *np)
+{
+	int tid;
+	unsigned int flag, fc_freq, apb_freq;
+	struct clk *apb_clk, *fast_clk;
+	void __iomem *base;
+	struct device_node *child_np;
+	const struct of_device_id *match;
+	int ret = 0;
+
+	for (tid = 0; tid < MMP_MAX_TIMER; tid++)
+		if (mmp_timers[tid] == NULL)
+			break;
+
+	if (tid >= MMP_MAX_TIMER)
+		return;
+
+	apb_clk = of_clk_get(np, 0);
+	if (!apb_clk)
+		return;
+	apb_freq = clk_get_rate(apb_clk);
+
+	fast_clk = of_clk_get(np, 1);
+	if (!fast_clk)
+		return;
+	fc_freq = clk_get_rate(fast_clk);
+
+	clk_prepare_enable(fast_clk);
+
+	/* timer initialization */
+	base = of_iomap(np, 0);
+	if (!base) {
+		pr_err("Timer: fail to map register space\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	flag = 0;
+	if (of_property_read_bool(np, "marvell,timer-has-crsr"))
+		flag |= MMP_TIMER_FLAG_CRSR;
+
+	if (of_property_read_bool(np, "marvell,timer-has-shadow"))
+		flag |= MMP_TIMER_FLAG_SHADOW;
+
+	ret = mmp_timer_init(tid, base, flag, fc_freq, apb_freq);
+	if (ret)
+		goto out;
+
+	/*
+	 * If device node is marked as not available,
+	 * we then don't try to enable the counter again
+	 */
+	if (!of_device_is_available(np)) {
+		pr_warn("Timer %d: is not used\n", tid);
+		return;
+	}
+
+	/* counter initialization */
+	for_each_child_of_node(np, child_np) {
+		match = of_match_node(mmp_counter_of_id, child_np);
+		ret = mmp_of_counter_init(child_np, tid,
+					  (unsigned int)match->data);
+		if (ret)
+			goto out;
+	}
+
+	return;
+out:
+	if (ret)
+		pr_err("Failed to get timer from dtb with error:%d\n", ret);
+}
+
+CLOCKSOURCE_OF_DECLARE(mmp_timer, "marvell,mmp-timer", mmp_of_timer_init);
+#endif
diff --git a/include/linux/mmp_timer.h b/include/linux/mmp_timer.h
new file mode 100644
index 0000000..6ca7f80
--- /dev/null
+++ b/include/linux/mmp_timer.h
@@ -0,0 +1,40 @@
+/*
+ * mmp_timer: Soc timer driver for mmp architecture.
+ *
+ * Copyright (C) 2008 Marvell International Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MMP_TIMER_H__
+#define __LINUX_MMP_TIMER_H__
+
+/* timer flag bit definition */
+/* bit[0]: MMP_TIMER_FLAG_SHADOW
+ *         Indicate if the timer has shadow registers. If it has,
+ *         counter could be read out directly.
+ * bit[1]: MMP_TIMER_FLAG_CRSR
+ *         Indicate if timer has CRSR register. If it has,
+ *         counter could be restarted by directly writing CRSR.
+ */
+#define MMP_TIMER_FLAG_SHADOW	(1 << 0)
+#define MMP_TIMER_FLAG_CRSR	(1 << 1)
+
+#define MMP_TIMER_COUNTER_CLKSRC	(1 << 0)
+#define MMP_TIMER_COUNTER_CLKEVT	(1 << 1)
+#define MMP_TIMER_COUNTER_DELAY		(1 << 2)
+
+#define MMP_TIMER_ALL_CPU	(0xFFFFFFFF)
+
+int __init mmp_timer_init(int id, void __iomem *base,
+			  unsigned int flag, unsigned int fc_freq,
+			  unsigned int apb_freq);
+
+int __init mmp_counter_clocksource_init(int tid, int cid, unsigned int freq);
+int __init mmp_counter_timer_delay_init(int tid, int cid, unsigned int freq);
+int __init mmp_counter_clockevent_init(int tid, int cid, int irq,
+				       int freq, int dynirq, unsigned int cpu);
+
+#endif /* __LINUX_MMP_TIMER_H__ */
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 2/4] arm: mmp: make SOCes without DT use new timer driver
@ 2015-02-02  8:31   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-02  8:31 UTC (permalink / raw)
  To: mark.rutland, daniel.lezcano, tglx, chao.xie, haojian.zhuang,
	linux-kernel, devicetree

From: Chao Xie <chao.xie@marvell.com>

For no DT support, directly call new timer's APIs to initialize
timer.
It need to initialize the timer first, then initialize the clock
event device or clock source which are based on counter.

Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
 arch/arm/mach-mmp/common.h |  2 --
 arch/arm/mach-mmp/mmp2.c   | 23 ++++++++++++++++++++++-
 arch/arm/mach-mmp/pxa168.c | 23 ++++++++++++++++++++++-
 arch/arm/mach-mmp/pxa910.c | 24 +++++++++++++++++++++++-
 4 files changed, 67 insertions(+), 5 deletions(-)

diff --git a/arch/arm/mach-mmp/common.h b/arch/arm/mach-mmp/common.h
index cf445ba..015b209 100644
--- a/arch/arm/mach-mmp/common.h
+++ b/arch/arm/mach-mmp/common.h
@@ -1,8 +1,6 @@
 #include <linux/reboot.h>
 #define ARRAY_AND_SIZE(x)	(x), ARRAY_SIZE(x)
 
-extern void timer_init(int irq);
-
 extern void __init mmp_map_io(void);
 extern void mmp_restart(enum reboot_mode, const char *);
 extern void __init pxa168_clk_init(void);
diff --git a/arch/arm/mach-mmp/mmp2.c b/arch/arm/mach-mmp/mmp2.c
index a70b553..12ba28a 100644
--- a/arch/arm/mach-mmp/mmp2.c
+++ b/arch/arm/mach-mmp/mmp2.c
@@ -16,12 +16,14 @@
 #include <linux/irq.h>
 #include <linux/irqchip/mmp.h>
 #include <linux/platform_device.h>
+#include <linux/mmp_timer.h>
 
 #include <asm/hardware/cache-tauros2.h>
 
 #include <asm/mach/time.h>
 #include <mach/addr-map.h>
 #include <mach/regs-apbc.h>
+#include <mach/regs-timers.h>
 #include <mach/cputype.h>
 #include <mach/irqs.h>
 #include <mach/dma.h>
@@ -133,7 +135,26 @@ void __init mmp2_timer_init(void)
 	clk_rst = APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(1);
 	__raw_writel(clk_rst, APBC_TIMERS);
 
-	timer_init(IRQ_MMP2_TIMER1);
+	/*
+	 * Make use of timer 1 which id is 0.
+	 * It has no shadow and crsr regsiters.
+	 * The fast lock is 6.5M.
+	 * apb bus is 26M.
+	 */
+	mmp_timer_init(0, TIMERS1_VIRT_BASE, 0, 6500000, 26000000);
+
+	/*
+	 * Enable counter 1 to be clock source.
+	 * The frequency is 32K.
+	 */
+	mmp_counter_clocksource_init(0, 1, 32768);
+
+	/*
+	 * Enable counter 0 to be clock event device.
+	 * The frequency is 32K.
+	 * Only one cpu and there is no broadcast timer.
+	 */
+	mmp_counter_clockevent_init(0, 0, IRQ_MMP2_TIMER1, 32768, 0, 0);
 }
 
 /* on-chip devices */
diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
index 144e997..bb65d8f 100644
--- a/arch/arm/mach-mmp/pxa168.c
+++ b/arch/arm/mach-mmp/pxa168.c
@@ -15,6 +15,7 @@
 #include <linux/clk.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/mv_usb.h>
+#include <linux/mmp_timer.h>
 
 #include <asm/mach/time.h>
 #include <asm/system_misc.h>
@@ -22,6 +23,7 @@
 #include <mach/addr-map.h>
 #include <mach/regs-apbc.h>
 #include <mach/regs-apmu.h>
+#include <mach/regs-timers.h>
 #include <mach/irqs.h>
 #include <mach/dma.h>
 #include <mach/devices.h>
@@ -78,7 +80,26 @@ void __init pxa168_timer_init(void)
 	/* 3.25MHz, bus/functional clock enabled, release reset */
 	__raw_writel(TIMER_CLK_RST, APBC_TIMERS);
 
-	timer_init(IRQ_PXA168_TIMER1);
+	/*
+	 * Make use of timer 1 which id is 0.
+	 * It has no shadow and crsr regsiters.
+	 * The fast lock is 3.25M.
+	 * apb bus is 26M.
+	 */
+	mmp_timer_init(0, TIMERS1_VIRT_BASE, 0, 3250000, 26000000);
+
+	/*
+	 * Enable counter 1 to be clock source.
+	 * The frequency is 32K.
+	 */
+	mmp_counter_clocksource_init(0, 1, 32768);
+
+	/*
+	 * Enable counter 0 to be clock event device.
+	 * The frequency is 32K.
+	 * Only one cpu and there is no broadcast timer.
+	 */
+	mmp_counter_clockevent_init(0, 0, IRQ_PXA168_TIMER1, 32768, 0, 0);
 }
 
 void pxa168_clear_keypad_wakeup(void)
diff --git a/arch/arm/mach-mmp/pxa910.c b/arch/arm/mach-mmp/pxa910.c
index eb57ee1..e8ae5b1 100644
--- a/arch/arm/mach-mmp/pxa910.c
+++ b/arch/arm/mach-mmp/pxa910.c
@@ -15,11 +15,13 @@
 #include <linux/irq.h>
 #include <linux/irqchip/mmp.h>
 #include <linux/platform_device.h>
+#include <linux/mmp_timer.h>
 
 #include <asm/hardware/cache-tauros2.h>
 #include <asm/mach/time.h>
 #include <mach/addr-map.h>
 #include <mach/regs-apbc.h>
+#include <mach/regs-timers.h>
 #include <mach/cputype.h>
 #include <mach/irqs.h>
 #include <mach/dma.h>
@@ -114,7 +116,27 @@ void __init pxa910_timer_init(void)
 	__raw_writel(APBC_APBCLK | APBC_RST, APBC_TIMERS);
 	__raw_writel(TIMER_CLK_RST, APBC_TIMERS);
 
-	timer_init(IRQ_PXA910_AP1_TIMER1);
+	/*
+	 * Make use of timer 1 which id is 0.
+	 * It has no shadow and crsr regsiters.
+	 * The fast lock is 3.25M.
+	 * apb bus is 26M.
+	 */
+	mmp_timer_init(0, TIMERS1_VIRT_BASE, 0, 3250000, 26000000);
+
+	/*
+	 * Enable counter 1 to be clock source.
+	 * The frequency is 32K.
+	 */
+	mmp_counter_clocksource_init(0, 1, 32768);
+
+	/*
+	 * Enable counter 0 to be clock event device.
+	 * The frequency is 32K.
+	 * Only one cpu and there is no broadcast timer.
+	 */
+	mmp_counter_clockevent_init(0, 0, IRQ_PXA910_AP1_TIMER1,
+				    32768, 0, 0);
 }
 
 /* on-chip devices */
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 2/4] arm: mmp: make SOCes without DT use new timer driver
@ 2015-02-02  8:31   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-02  8:31 UTC (permalink / raw)
  To: mark.rutland-5wv7dgnIgG8, daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A,
	tglx-hfZtesqFncYOwBW4kG4KsQ, chao.xie-eYqpPyKDWXRBDgjK7y7TUQ,
	haojian.zhuang-QSEj5FYQhm4dnm+yROfE0A,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Chao Xie <chao.xie-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>

For no DT support, directly call new timer's APIs to initialize
timer.
It need to initialize the timer first, then initialize the clock
event device or clock source which are based on counter.

Signed-off-by: Chao Xie <chao.xie-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
---
 arch/arm/mach-mmp/common.h |  2 --
 arch/arm/mach-mmp/mmp2.c   | 23 ++++++++++++++++++++++-
 arch/arm/mach-mmp/pxa168.c | 23 ++++++++++++++++++++++-
 arch/arm/mach-mmp/pxa910.c | 24 +++++++++++++++++++++++-
 4 files changed, 67 insertions(+), 5 deletions(-)

diff --git a/arch/arm/mach-mmp/common.h b/arch/arm/mach-mmp/common.h
index cf445ba..015b209 100644
--- a/arch/arm/mach-mmp/common.h
+++ b/arch/arm/mach-mmp/common.h
@@ -1,8 +1,6 @@
 #include <linux/reboot.h>
 #define ARRAY_AND_SIZE(x)	(x), ARRAY_SIZE(x)
 
-extern void timer_init(int irq);
-
 extern void __init mmp_map_io(void);
 extern void mmp_restart(enum reboot_mode, const char *);
 extern void __init pxa168_clk_init(void);
diff --git a/arch/arm/mach-mmp/mmp2.c b/arch/arm/mach-mmp/mmp2.c
index a70b553..12ba28a 100644
--- a/arch/arm/mach-mmp/mmp2.c
+++ b/arch/arm/mach-mmp/mmp2.c
@@ -16,12 +16,14 @@
 #include <linux/irq.h>
 #include <linux/irqchip/mmp.h>
 #include <linux/platform_device.h>
+#include <linux/mmp_timer.h>
 
 #include <asm/hardware/cache-tauros2.h>
 
 #include <asm/mach/time.h>
 #include <mach/addr-map.h>
 #include <mach/regs-apbc.h>
+#include <mach/regs-timers.h>
 #include <mach/cputype.h>
 #include <mach/irqs.h>
 #include <mach/dma.h>
@@ -133,7 +135,26 @@ void __init mmp2_timer_init(void)
 	clk_rst = APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(1);
 	__raw_writel(clk_rst, APBC_TIMERS);
 
-	timer_init(IRQ_MMP2_TIMER1);
+	/*
+	 * Make use of timer 1 which id is 0.
+	 * It has no shadow and crsr regsiters.
+	 * The fast lock is 6.5M.
+	 * apb bus is 26M.
+	 */
+	mmp_timer_init(0, TIMERS1_VIRT_BASE, 0, 6500000, 26000000);
+
+	/*
+	 * Enable counter 1 to be clock source.
+	 * The frequency is 32K.
+	 */
+	mmp_counter_clocksource_init(0, 1, 32768);
+
+	/*
+	 * Enable counter 0 to be clock event device.
+	 * The frequency is 32K.
+	 * Only one cpu and there is no broadcast timer.
+	 */
+	mmp_counter_clockevent_init(0, 0, IRQ_MMP2_TIMER1, 32768, 0, 0);
 }
 
 /* on-chip devices */
diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
index 144e997..bb65d8f 100644
--- a/arch/arm/mach-mmp/pxa168.c
+++ b/arch/arm/mach-mmp/pxa168.c
@@ -15,6 +15,7 @@
 #include <linux/clk.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/mv_usb.h>
+#include <linux/mmp_timer.h>
 
 #include <asm/mach/time.h>
 #include <asm/system_misc.h>
@@ -22,6 +23,7 @@
 #include <mach/addr-map.h>
 #include <mach/regs-apbc.h>
 #include <mach/regs-apmu.h>
+#include <mach/regs-timers.h>
 #include <mach/irqs.h>
 #include <mach/dma.h>
 #include <mach/devices.h>
@@ -78,7 +80,26 @@ void __init pxa168_timer_init(void)
 	/* 3.25MHz, bus/functional clock enabled, release reset */
 	__raw_writel(TIMER_CLK_RST, APBC_TIMERS);
 
-	timer_init(IRQ_PXA168_TIMER1);
+	/*
+	 * Make use of timer 1 which id is 0.
+	 * It has no shadow and crsr regsiters.
+	 * The fast lock is 3.25M.
+	 * apb bus is 26M.
+	 */
+	mmp_timer_init(0, TIMERS1_VIRT_BASE, 0, 3250000, 26000000);
+
+	/*
+	 * Enable counter 1 to be clock source.
+	 * The frequency is 32K.
+	 */
+	mmp_counter_clocksource_init(0, 1, 32768);
+
+	/*
+	 * Enable counter 0 to be clock event device.
+	 * The frequency is 32K.
+	 * Only one cpu and there is no broadcast timer.
+	 */
+	mmp_counter_clockevent_init(0, 0, IRQ_PXA168_TIMER1, 32768, 0, 0);
 }
 
 void pxa168_clear_keypad_wakeup(void)
diff --git a/arch/arm/mach-mmp/pxa910.c b/arch/arm/mach-mmp/pxa910.c
index eb57ee1..e8ae5b1 100644
--- a/arch/arm/mach-mmp/pxa910.c
+++ b/arch/arm/mach-mmp/pxa910.c
@@ -15,11 +15,13 @@
 #include <linux/irq.h>
 #include <linux/irqchip/mmp.h>
 #include <linux/platform_device.h>
+#include <linux/mmp_timer.h>
 
 #include <asm/hardware/cache-tauros2.h>
 #include <asm/mach/time.h>
 #include <mach/addr-map.h>
 #include <mach/regs-apbc.h>
+#include <mach/regs-timers.h>
 #include <mach/cputype.h>
 #include <mach/irqs.h>
 #include <mach/dma.h>
@@ -114,7 +116,27 @@ void __init pxa910_timer_init(void)
 	__raw_writel(APBC_APBCLK | APBC_RST, APBC_TIMERS);
 	__raw_writel(TIMER_CLK_RST, APBC_TIMERS);
 
-	timer_init(IRQ_PXA910_AP1_TIMER1);
+	/*
+	 * Make use of timer 1 which id is 0.
+	 * It has no shadow and crsr regsiters.
+	 * The fast lock is 3.25M.
+	 * apb bus is 26M.
+	 */
+	mmp_timer_init(0, TIMERS1_VIRT_BASE, 0, 3250000, 26000000);
+
+	/*
+	 * Enable counter 1 to be clock source.
+	 * The frequency is 32K.
+	 */
+	mmp_counter_clocksource_init(0, 1, 32768);
+
+	/*
+	 * Enable counter 0 to be clock event device.
+	 * The frequency is 32K.
+	 * Only one cpu and there is no broadcast timer.
+	 */
+	mmp_counter_clockevent_init(0, 0, IRQ_PXA910_AP1_TIMER1,
+				    32768, 0, 0);
 }
 
 /* on-chip devices */
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 3/4] arm: mmp: make SOCes with DT use new timer driver
  2015-02-02  8:31 ` Chao Xie
@ 2015-02-02  8:31   ` Chao Xie
  -1 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-02  8:31 UTC (permalink / raw)
  To: mark.rutland, daniel.lezcano, tglx, chao.xie, haojian.zhuang,
	linux-kernel, devicetree

From: Chao Xie <chao.xie@marvell.com>

Change the dtsi and DT support for mmp SOCes to make them
use the new timer driver.

Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
 arch/arm/boot/dts/mmp2.dtsi   | 19 ++++++++++++++++---
 arch/arm/boot/dts/pxa168.dtsi | 20 +++++++++++++++++---
 arch/arm/boot/dts/pxa910.dtsi | 26 ++++++++++++++++----------
 arch/arm/mach-mmp/Kconfig     |  2 ++
 arch/arm/mach-mmp/mmp-dt.c    |  4 +++-
 arch/arm/mach-mmp/mmp2-dt.c   |  4 +++-
 6 files changed, 57 insertions(+), 18 deletions(-)

diff --git a/arch/arm/boot/dts/mmp2.dtsi b/arch/arm/boot/dts/mmp2.dtsi
index 766bbb8..e63e3a3 100644
--- a/arch/arm/boot/dts/mmp2.dtsi
+++ b/arch/arm/boot/dts/mmp2.dtsi
@@ -127,9 +127,22 @@
 			ranges;
 
 			timer0: timer@d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				clocks = <&soc_clocks MMP2_CLK_VCTCXO>, <&soc_clocks MMP2_CLK_TIMER>;
+				status = "okay";
+
+				counter0 {
+					compatible = "marvell,timer-counter-clkevt";
+					interrupts = <13>;
+					marvell,timer-counter-id = <0>;
+					marvell,timer-counter-cpu = <0>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+                                };
 			};
 
 			uart1: uart@d4030000 {
diff --git a/arch/arm/boot/dts/pxa168.dtsi b/arch/arm/boot/dts/pxa168.dtsi
index b899e25..5a2f385 100644
--- a/arch/arm/boot/dts/pxa168.dtsi
+++ b/arch/arm/boot/dts/pxa168.dtsi
@@ -51,9 +51,23 @@
 			ranges;
 
 			timer0: timer@d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				clocks = <&soc_clocks PXA168_CLK_PLL1_24>, <&soc_clocks PXA168_CLK_TIMER>;
+				status = "okay";
+
+				counter0 {
+					compatible = "marvell,timer-counter-clkevt";
+					interrupts = <13>;
+					marvell,timer-counter-id = <0>;
+					marvell,timer-counter-cpu = <0>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+                                };
+
 			};
 
 			uart1: uart@d4017000 {
diff --git a/arch/arm/boot/dts/pxa910.dtsi b/arch/arm/boot/dts/pxa910.dtsi
index 0868f67..beb25fc 100644
--- a/arch/arm/boot/dts/pxa910.dtsi
+++ b/arch/arm/boot/dts/pxa910.dtsi
@@ -56,16 +56,22 @@
 			ranges;
 
 			timer0: timer@d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
-			};
-
-			timer1: timer@d4016000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4016000 0x100>;
-				interrupts = <29>;
-				status = "disabled";
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				clocks = <&soc_clocks PXA910_CLK_PLL1_24>, <&soc_clocks PXA910_CLK_TIMER0>;
+				status = "okay";
+
+				counter0 {
+					compatible = "marvell,timer-counter-clkevt";
+					interrupts = <13>;
+					marvell,timer-counter-id = <0>;
+					marvell,timer-counter-cpu = <0>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+                                };
 			};
 
 			uart1: uart@d4017000 {
diff --git a/arch/arm/mach-mmp/Kconfig b/arch/arm/mach-mmp/Kconfig
index fdbfadf..4bbe202 100644
--- a/arch/arm/mach-mmp/Kconfig
+++ b/arch/arm/mach-mmp/Kconfig
@@ -87,6 +87,7 @@ config MACH_GPLUGD
 config MACH_MMP_DT
 	bool "Support MMP (ARMv5) platforms from device tree"
 	select USE_OF
+	select CLKSRC_OF
 	select PINCTRL
 	select PINCTRL_SINGLE
 	select COMMON_CLK
@@ -101,6 +102,7 @@ config MACH_MMP2_DT
 	bool "Support MMP2 (ARMv7) platforms from device tree"
 	depends on !CPU_MOHAWK
 	select USE_OF
+	select CLKSRC_OF
 	select PINCTRL
 	select PINCTRL_SINGLE
 	select COMMON_CLK
diff --git a/arch/arm/mach-mmp/mmp-dt.c b/arch/arm/mach-mmp/mmp-dt.c
index b2296c9..2e91871 100644
--- a/arch/arm/mach-mmp/mmp-dt.c
+++ b/arch/arm/mach-mmp/mmp-dt.c
@@ -12,6 +12,7 @@
 #include <linux/irqchip.h>
 #include <linux/of_platform.h>
 #include <linux/clk-provider.h>
+#include <linux/clocksource.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 #include <asm/hardware/cache-tauros2.h>
@@ -35,8 +36,9 @@ static void __init mmp_init_time(void)
 #ifdef CONFIG_CACHE_TAUROS2
 	tauros2_init(0);
 #endif
-	mmp_dt_init_timer();
 	of_clk_init(NULL);
+	/* Initialize timers. */
+	clocksource_of_init();
 }
 
 DT_MACHINE_START(PXA168_DT, "Marvell PXA168 (Device Tree Support)")
diff --git a/arch/arm/mach-mmp/mmp2-dt.c b/arch/arm/mach-mmp/mmp2-dt.c
index 998c0f5..a42d32e 100644
--- a/arch/arm/mach-mmp/mmp2-dt.c
+++ b/arch/arm/mach-mmp/mmp2-dt.c
@@ -13,6 +13,7 @@
 #include <linux/irqchip.h>
 #include <linux/of_platform.h>
 #include <linux/clk-provider.h>
+#include <linux/clocksource.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 #include <asm/hardware/cache-tauros2.h>
@@ -26,8 +27,9 @@ static void __init mmp_init_time(void)
 #ifdef CONFIG_CACHE_TAUROS2
 	tauros2_init(0);
 #endif
-	mmp_dt_init_timer();
 	of_clk_init(NULL);
+	/* Initialize timers. */
+	clocksource_of_init();
 }
 
 static const char *mmp2_dt_board_compat[] __initdata = {
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 3/4] arm: mmp: make SOCes with DT use new timer driver
@ 2015-02-02  8:31   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-02  8:31 UTC (permalink / raw)
  To: mark.rutland, daniel.lezcano, tglx, chao.xie, haojian.zhuang,
	linux-kernel, devicetree

From: Chao Xie <chao.xie@marvell.com>

Change the dtsi and DT support for mmp SOCes to make them
use the new timer driver.

Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
 arch/arm/boot/dts/mmp2.dtsi   | 19 ++++++++++++++++---
 arch/arm/boot/dts/pxa168.dtsi | 20 +++++++++++++++++---
 arch/arm/boot/dts/pxa910.dtsi | 26 ++++++++++++++++----------
 arch/arm/mach-mmp/Kconfig     |  2 ++
 arch/arm/mach-mmp/mmp-dt.c    |  4 +++-
 arch/arm/mach-mmp/mmp2-dt.c   |  4 +++-
 6 files changed, 57 insertions(+), 18 deletions(-)

diff --git a/arch/arm/boot/dts/mmp2.dtsi b/arch/arm/boot/dts/mmp2.dtsi
index 766bbb8..e63e3a3 100644
--- a/arch/arm/boot/dts/mmp2.dtsi
+++ b/arch/arm/boot/dts/mmp2.dtsi
@@ -127,9 +127,22 @@
 			ranges;
 
 			timer0: timer@d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				clocks = <&soc_clocks MMP2_CLK_VCTCXO>, <&soc_clocks MMP2_CLK_TIMER>;
+				status = "okay";
+
+				counter0 {
+					compatible = "marvell,timer-counter-clkevt";
+					interrupts = <13>;
+					marvell,timer-counter-id = <0>;
+					marvell,timer-counter-cpu = <0>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+                                };
 			};
 
 			uart1: uart@d4030000 {
diff --git a/arch/arm/boot/dts/pxa168.dtsi b/arch/arm/boot/dts/pxa168.dtsi
index b899e25..5a2f385 100644
--- a/arch/arm/boot/dts/pxa168.dtsi
+++ b/arch/arm/boot/dts/pxa168.dtsi
@@ -51,9 +51,23 @@
 			ranges;
 
 			timer0: timer@d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				clocks = <&soc_clocks PXA168_CLK_PLL1_24>, <&soc_clocks PXA168_CLK_TIMER>;
+				status = "okay";
+
+				counter0 {
+					compatible = "marvell,timer-counter-clkevt";
+					interrupts = <13>;
+					marvell,timer-counter-id = <0>;
+					marvell,timer-counter-cpu = <0>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+                                };
+
 			};
 
 			uart1: uart@d4017000 {
diff --git a/arch/arm/boot/dts/pxa910.dtsi b/arch/arm/boot/dts/pxa910.dtsi
index 0868f67..beb25fc 100644
--- a/arch/arm/boot/dts/pxa910.dtsi
+++ b/arch/arm/boot/dts/pxa910.dtsi
@@ -56,16 +56,22 @@
 			ranges;
 
 			timer0: timer@d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
-			};
-
-			timer1: timer@d4016000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4016000 0x100>;
-				interrupts = <29>;
-				status = "disabled";
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				clocks = <&soc_clocks PXA910_CLK_PLL1_24>, <&soc_clocks PXA910_CLK_TIMER0>;
+				status = "okay";
+
+				counter0 {
+					compatible = "marvell,timer-counter-clkevt";
+					interrupts = <13>;
+					marvell,timer-counter-id = <0>;
+					marvell,timer-counter-cpu = <0>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+                                };
 			};
 
 			uart1: uart@d4017000 {
diff --git a/arch/arm/mach-mmp/Kconfig b/arch/arm/mach-mmp/Kconfig
index fdbfadf..4bbe202 100644
--- a/arch/arm/mach-mmp/Kconfig
+++ b/arch/arm/mach-mmp/Kconfig
@@ -87,6 +87,7 @@ config MACH_GPLUGD
 config MACH_MMP_DT
 	bool "Support MMP (ARMv5) platforms from device tree"
 	select USE_OF
+	select CLKSRC_OF
 	select PINCTRL
 	select PINCTRL_SINGLE
 	select COMMON_CLK
@@ -101,6 +102,7 @@ config MACH_MMP2_DT
 	bool "Support MMP2 (ARMv7) platforms from device tree"
 	depends on !CPU_MOHAWK
 	select USE_OF
+	select CLKSRC_OF
 	select PINCTRL
 	select PINCTRL_SINGLE
 	select COMMON_CLK
diff --git a/arch/arm/mach-mmp/mmp-dt.c b/arch/arm/mach-mmp/mmp-dt.c
index b2296c9..2e91871 100644
--- a/arch/arm/mach-mmp/mmp-dt.c
+++ b/arch/arm/mach-mmp/mmp-dt.c
@@ -12,6 +12,7 @@
 #include <linux/irqchip.h>
 #include <linux/of_platform.h>
 #include <linux/clk-provider.h>
+#include <linux/clocksource.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 #include <asm/hardware/cache-tauros2.h>
@@ -35,8 +36,9 @@ static void __init mmp_init_time(void)
 #ifdef CONFIG_CACHE_TAUROS2
 	tauros2_init(0);
 #endif
-	mmp_dt_init_timer();
 	of_clk_init(NULL);
+	/* Initialize timers. */
+	clocksource_of_init();
 }
 
 DT_MACHINE_START(PXA168_DT, "Marvell PXA168 (Device Tree Support)")
diff --git a/arch/arm/mach-mmp/mmp2-dt.c b/arch/arm/mach-mmp/mmp2-dt.c
index 998c0f5..a42d32e 100644
--- a/arch/arm/mach-mmp/mmp2-dt.c
+++ b/arch/arm/mach-mmp/mmp2-dt.c
@@ -13,6 +13,7 @@
 #include <linux/irqchip.h>
 #include <linux/of_platform.h>
 #include <linux/clk-provider.h>
+#include <linux/clocksource.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 #include <asm/hardware/cache-tauros2.h>
@@ -26,8 +27,9 @@ static void __init mmp_init_time(void)
 #ifdef CONFIG_CACHE_TAUROS2
 	tauros2_init(0);
 #endif
-	mmp_dt_init_timer();
 	of_clk_init(NULL);
+	/* Initialize timers. */
+	clocksource_of_init();
 }
 
 static const char *mmp2_dt_board_compat[] __initdata = {
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 4/4] arm: mmp: remove the old timer driver
@ 2015-02-02  8:31   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-02  8:31 UTC (permalink / raw)
  To: mark.rutland, daniel.lezcano, tglx, chao.xie, haojian.zhuang,
	linux-kernel, devicetree

From: Chao Xie <chao.xie@marvell.com>

The new timer driver has been created under
drivers/clocksource/.

After change all SOCes to use the new driver,
remove the old one.

Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
 arch/arm/mach-mmp/Makefile |   2 +-
 arch/arm/mach-mmp/time.c   | 247 ---------------------------------------------
 2 files changed, 1 insertion(+), 248 deletions(-)
 delete mode 100644 arch/arm/mach-mmp/time.c

diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile
index 98f0f63..18ba814 100644
--- a/arch/arm/mach-mmp/Makefile
+++ b/arch/arm/mach-mmp/Makefile
@@ -2,7 +2,7 @@
 # Makefile for Marvell's PXA168 processors line
 #
 
-obj-y				+= common.o devices.o time.o
+obj-y				+= common.o devices.o
 
 # SoC support
 obj-$(CONFIG_CPU_PXA168)	+= pxa168.o
diff --git a/arch/arm/mach-mmp/time.c b/arch/arm/mach-mmp/time.c
deleted file mode 100644
index 2756351..0000000
--- a/arch/arm/mach-mmp/time.c
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * linux/arch/arm/mach-mmp/time.c
- *
- *   Support for clocksource and clockevents
- *
- * Copyright (C) 2008 Marvell International Ltd.
- * All rights reserved.
- *
- *   2008-04-11: Jason Chagas <Jason.chagas@marvell.com>
- *   2008-10-08: Bin Yang <bin.yang@marvell.com>
- *
- * The timers module actually includes three timers, each timer with up to
- * three match comparators. Timer #0 is used here in free-running mode as
- * the clock source, and match comparator #1 used as clock event device.
- *
- * 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 <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/clockchips.h>
-
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/sched_clock.h>
-
-#include <mach/addr-map.h>
-#include <mach/regs-timers.h>
-#include <mach/regs-apbc.h>
-#include <mach/irqs.h>
-#include <mach/cputype.h>
-#include <asm/mach/time.h>
-
-#include "clock.h"
-
-#ifdef CONFIG_CPU_MMP2
-#define MMP_CLOCK_FREQ		6500000
-#else
-#define MMP_CLOCK_FREQ		3250000
-#endif
-
-#define TIMERS_VIRT_BASE	TIMERS1_VIRT_BASE
-
-#define MAX_DELTA		(0xfffffffe)
-#define MIN_DELTA		(16)
-
-static void __iomem *mmp_timer_base = TIMERS_VIRT_BASE;
-
-/*
- * FIXME: the timer needs some delay to stablize the counter capture
- */
-static inline uint32_t timer_read(void)
-{
-	int delay = 100;
-
-	__raw_writel(1, mmp_timer_base + TMR_CVWR(1));
-
-	while (delay--)
-		cpu_relax();
-
-	return __raw_readl(mmp_timer_base + TMR_CVWR(1));
-}
-
-static u64 notrace mmp_read_sched_clock(void)
-{
-	return timer_read();
-}
-
-static irqreturn_t timer_interrupt(int irq, void *dev_id)
-{
-	struct clock_event_device *c = dev_id;
-
-	/*
-	 * Clear pending interrupt status.
-	 */
-	__raw_writel(0x01, mmp_timer_base + TMR_ICR(0));
-
-	/*
-	 * Disable timer 0.
-	 */
-	__raw_writel(0x02, mmp_timer_base + TMR_CER);
-
-	c->event_handler(c);
-
-	return IRQ_HANDLED;
-}
-
-static int timer_set_next_event(unsigned long delta,
-				struct clock_event_device *dev)
-{
-	unsigned long flags;
-
-	local_irq_save(flags);
-
-	/*
-	 * Disable timer 0.
-	 */
-	__raw_writel(0x02, mmp_timer_base + TMR_CER);
-
-	/*
-	 * Clear and enable timer match 0 interrupt.
-	 */
-	__raw_writel(0x01, mmp_timer_base + TMR_ICR(0));
-	__raw_writel(0x01, mmp_timer_base + TMR_IER(0));
-
-	/*
-	 * Setup new clockevent timer value.
-	 */
-	__raw_writel(delta - 1, mmp_timer_base + TMR_TN_MM(0, 0));
-
-	/*
-	 * Enable timer 0.
-	 */
-	__raw_writel(0x03, mmp_timer_base + TMR_CER);
-
-	local_irq_restore(flags);
-
-	return 0;
-}
-
-static void timer_set_mode(enum clock_event_mode mode,
-			   struct clock_event_device *dev)
-{
-	unsigned long flags;
-
-	local_irq_save(flags);
-	switch (mode) {
-	case CLOCK_EVT_MODE_ONESHOT:
-	case CLOCK_EVT_MODE_UNUSED:
-	case CLOCK_EVT_MODE_SHUTDOWN:
-		/* disable the matching interrupt */
-		__raw_writel(0x00, mmp_timer_base + TMR_IER(0));
-		break;
-	case CLOCK_EVT_MODE_RESUME:
-	case CLOCK_EVT_MODE_PERIODIC:
-		break;
-	}
-	local_irq_restore(flags);
-}
-
-static struct clock_event_device ckevt = {
-	.name		= "clockevent",
-	.features	= CLOCK_EVT_FEAT_ONESHOT,
-	.rating		= 200,
-	.set_next_event	= timer_set_next_event,
-	.set_mode	= timer_set_mode,
-};
-
-static cycle_t clksrc_read(struct clocksource *cs)
-{
-	return timer_read();
-}
-
-static struct clocksource cksrc = {
-	.name		= "clocksource",
-	.rating		= 200,
-	.read		= clksrc_read,
-	.mask		= CLOCKSOURCE_MASK(32),
-	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
-};
-
-static void __init timer_config(void)
-{
-	uint32_t ccr = __raw_readl(mmp_timer_base + TMR_CCR);
-
-	__raw_writel(0x0, mmp_timer_base + TMR_CER); /* disable */
-
-	ccr &= (cpu_is_mmp2()) ? (TMR_CCR_CS_0(0) | TMR_CCR_CS_1(0)) :
-		(TMR_CCR_CS_0(3) | TMR_CCR_CS_1(3));
-	__raw_writel(ccr, mmp_timer_base + TMR_CCR);
-
-	/* set timer 0 to periodic mode, and timer 1 to free-running mode */
-	__raw_writel(0x2, mmp_timer_base + TMR_CMR);
-
-	__raw_writel(0x1, mmp_timer_base + TMR_PLCR(0)); /* periodic */
-	__raw_writel(0x7, mmp_timer_base + TMR_ICR(0));  /* clear status */
-	__raw_writel(0x0, mmp_timer_base + TMR_IER(0));
-
-	__raw_writel(0x0, mmp_timer_base + TMR_PLCR(1)); /* free-running */
-	__raw_writel(0x7, mmp_timer_base + TMR_ICR(1));  /* clear status */
-	__raw_writel(0x0, mmp_timer_base + TMR_IER(1));
-
-	/* enable timer 1 counter */
-	__raw_writel(0x2, mmp_timer_base + TMR_CER);
-}
-
-static struct irqaction timer_irq = {
-	.name		= "timer",
-	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
-	.handler	= timer_interrupt,
-	.dev_id		= &ckevt,
-};
-
-void __init timer_init(int irq)
-{
-	timer_config();
-
-	sched_clock_register(mmp_read_sched_clock, 32, MMP_CLOCK_FREQ);
-
-	ckevt.cpumask = cpumask_of(0);
-
-	setup_irq(irq, &timer_irq);
-
-	clocksource_register_hz(&cksrc, MMP_CLOCK_FREQ);
-	clockevents_config_and_register(&ckevt, MMP_CLOCK_FREQ,
-					MIN_DELTA, MAX_DELTA);
-}
-
-#ifdef CONFIG_OF
-static struct of_device_id mmp_timer_dt_ids[] = {
-	{ .compatible = "mrvl,mmp-timer", },
-	{}
-};
-
-void __init mmp_dt_init_timer(void)
-{
-	struct device_node *np;
-	int irq, ret;
-
-	np = of_find_matching_node(NULL, mmp_timer_dt_ids);
-	if (!np) {
-		ret = -ENODEV;
-		goto out;
-	}
-
-	irq = irq_of_parse_and_map(np, 0);
-	if (!irq) {
-		ret = -EINVAL;
-		goto out;
-	}
-	mmp_timer_base = of_iomap(np, 0);
-	if (!mmp_timer_base) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	timer_init(irq);
-	return;
-out:
-	pr_err("Failed to get timer from device tree with error:%d\n", ret);
-}
-#endif
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 4/4] arm: mmp: remove the old timer driver
@ 2015-02-02  8:31   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-02  8:31 UTC (permalink / raw)
  To: mark.rutland-5wv7dgnIgG8, daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A,
	tglx-hfZtesqFncYOwBW4kG4KsQ, chao.xie-eYqpPyKDWXRBDgjK7y7TUQ,
	haojian.zhuang-QSEj5FYQhm4dnm+yROfE0A,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Chao Xie <chao.xie-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>

The new timer driver has been created under
drivers/clocksource/.

After change all SOCes to use the new driver,
remove the old one.

Signed-off-by: Chao Xie <chao.xie-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
---
 arch/arm/mach-mmp/Makefile |   2 +-
 arch/arm/mach-mmp/time.c   | 247 ---------------------------------------------
 2 files changed, 1 insertion(+), 248 deletions(-)
 delete mode 100644 arch/arm/mach-mmp/time.c

diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile
index 98f0f63..18ba814 100644
--- a/arch/arm/mach-mmp/Makefile
+++ b/arch/arm/mach-mmp/Makefile
@@ -2,7 +2,7 @@
 # Makefile for Marvell's PXA168 processors line
 #
 
-obj-y				+= common.o devices.o time.o
+obj-y				+= common.o devices.o
 
 # SoC support
 obj-$(CONFIG_CPU_PXA168)	+= pxa168.o
diff --git a/arch/arm/mach-mmp/time.c b/arch/arm/mach-mmp/time.c
deleted file mode 100644
index 2756351..0000000
--- a/arch/arm/mach-mmp/time.c
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * linux/arch/arm/mach-mmp/time.c
- *
- *   Support for clocksource and clockevents
- *
- * Copyright (C) 2008 Marvell International Ltd.
- * All rights reserved.
- *
- *   2008-04-11: Jason Chagas <Jason.chagas-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
- *   2008-10-08: Bin Yang <bin.yang-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
- *
- * The timers module actually includes three timers, each timer with up to
- * three match comparators. Timer #0 is used here in free-running mode as
- * the clock source, and match comparator #1 used as clock event device.
- *
- * 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 <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/clockchips.h>
-
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/sched_clock.h>
-
-#include <mach/addr-map.h>
-#include <mach/regs-timers.h>
-#include <mach/regs-apbc.h>
-#include <mach/irqs.h>
-#include <mach/cputype.h>
-#include <asm/mach/time.h>
-
-#include "clock.h"
-
-#ifdef CONFIG_CPU_MMP2
-#define MMP_CLOCK_FREQ		6500000
-#else
-#define MMP_CLOCK_FREQ		3250000
-#endif
-
-#define TIMERS_VIRT_BASE	TIMERS1_VIRT_BASE
-
-#define MAX_DELTA		(0xfffffffe)
-#define MIN_DELTA		(16)
-
-static void __iomem *mmp_timer_base = TIMERS_VIRT_BASE;
-
-/*
- * FIXME: the timer needs some delay to stablize the counter capture
- */
-static inline uint32_t timer_read(void)
-{
-	int delay = 100;
-
-	__raw_writel(1, mmp_timer_base + TMR_CVWR(1));
-
-	while (delay--)
-		cpu_relax();
-
-	return __raw_readl(mmp_timer_base + TMR_CVWR(1));
-}
-
-static u64 notrace mmp_read_sched_clock(void)
-{
-	return timer_read();
-}
-
-static irqreturn_t timer_interrupt(int irq, void *dev_id)
-{
-	struct clock_event_device *c = dev_id;
-
-	/*
-	 * Clear pending interrupt status.
-	 */
-	__raw_writel(0x01, mmp_timer_base + TMR_ICR(0));
-
-	/*
-	 * Disable timer 0.
-	 */
-	__raw_writel(0x02, mmp_timer_base + TMR_CER);
-
-	c->event_handler(c);
-
-	return IRQ_HANDLED;
-}
-
-static int timer_set_next_event(unsigned long delta,
-				struct clock_event_device *dev)
-{
-	unsigned long flags;
-
-	local_irq_save(flags);
-
-	/*
-	 * Disable timer 0.
-	 */
-	__raw_writel(0x02, mmp_timer_base + TMR_CER);
-
-	/*
-	 * Clear and enable timer match 0 interrupt.
-	 */
-	__raw_writel(0x01, mmp_timer_base + TMR_ICR(0));
-	__raw_writel(0x01, mmp_timer_base + TMR_IER(0));
-
-	/*
-	 * Setup new clockevent timer value.
-	 */
-	__raw_writel(delta - 1, mmp_timer_base + TMR_TN_MM(0, 0));
-
-	/*
-	 * Enable timer 0.
-	 */
-	__raw_writel(0x03, mmp_timer_base + TMR_CER);
-
-	local_irq_restore(flags);
-
-	return 0;
-}
-
-static void timer_set_mode(enum clock_event_mode mode,
-			   struct clock_event_device *dev)
-{
-	unsigned long flags;
-
-	local_irq_save(flags);
-	switch (mode) {
-	case CLOCK_EVT_MODE_ONESHOT:
-	case CLOCK_EVT_MODE_UNUSED:
-	case CLOCK_EVT_MODE_SHUTDOWN:
-		/* disable the matching interrupt */
-		__raw_writel(0x00, mmp_timer_base + TMR_IER(0));
-		break;
-	case CLOCK_EVT_MODE_RESUME:
-	case CLOCK_EVT_MODE_PERIODIC:
-		break;
-	}
-	local_irq_restore(flags);
-}
-
-static struct clock_event_device ckevt = {
-	.name		= "clockevent",
-	.features	= CLOCK_EVT_FEAT_ONESHOT,
-	.rating		= 200,
-	.set_next_event	= timer_set_next_event,
-	.set_mode	= timer_set_mode,
-};
-
-static cycle_t clksrc_read(struct clocksource *cs)
-{
-	return timer_read();
-}
-
-static struct clocksource cksrc = {
-	.name		= "clocksource",
-	.rating		= 200,
-	.read		= clksrc_read,
-	.mask		= CLOCKSOURCE_MASK(32),
-	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
-};
-
-static void __init timer_config(void)
-{
-	uint32_t ccr = __raw_readl(mmp_timer_base + TMR_CCR);
-
-	__raw_writel(0x0, mmp_timer_base + TMR_CER); /* disable */
-
-	ccr &= (cpu_is_mmp2()) ? (TMR_CCR_CS_0(0) | TMR_CCR_CS_1(0)) :
-		(TMR_CCR_CS_0(3) | TMR_CCR_CS_1(3));
-	__raw_writel(ccr, mmp_timer_base + TMR_CCR);
-
-	/* set timer 0 to periodic mode, and timer 1 to free-running mode */
-	__raw_writel(0x2, mmp_timer_base + TMR_CMR);
-
-	__raw_writel(0x1, mmp_timer_base + TMR_PLCR(0)); /* periodic */
-	__raw_writel(0x7, mmp_timer_base + TMR_ICR(0));  /* clear status */
-	__raw_writel(0x0, mmp_timer_base + TMR_IER(0));
-
-	__raw_writel(0x0, mmp_timer_base + TMR_PLCR(1)); /* free-running */
-	__raw_writel(0x7, mmp_timer_base + TMR_ICR(1));  /* clear status */
-	__raw_writel(0x0, mmp_timer_base + TMR_IER(1));
-
-	/* enable timer 1 counter */
-	__raw_writel(0x2, mmp_timer_base + TMR_CER);
-}
-
-static struct irqaction timer_irq = {
-	.name		= "timer",
-	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
-	.handler	= timer_interrupt,
-	.dev_id		= &ckevt,
-};
-
-void __init timer_init(int irq)
-{
-	timer_config();
-
-	sched_clock_register(mmp_read_sched_clock, 32, MMP_CLOCK_FREQ);
-
-	ckevt.cpumask = cpumask_of(0);
-
-	setup_irq(irq, &timer_irq);
-
-	clocksource_register_hz(&cksrc, MMP_CLOCK_FREQ);
-	clockevents_config_and_register(&ckevt, MMP_CLOCK_FREQ,
-					MIN_DELTA, MAX_DELTA);
-}
-
-#ifdef CONFIG_OF
-static struct of_device_id mmp_timer_dt_ids[] = {
-	{ .compatible = "mrvl,mmp-timer", },
-	{}
-};
-
-void __init mmp_dt_init_timer(void)
-{
-	struct device_node *np;
-	int irq, ret;
-
-	np = of_find_matching_node(NULL, mmp_timer_dt_ids);
-	if (!np) {
-		ret = -ENODEV;
-		goto out;
-	}
-
-	irq = irq_of_parse_and_map(np, 0);
-	if (!irq) {
-		ret = -EINVAL;
-		goto out;
-	}
-	mmp_timer_base = of_iomap(np, 0);
-	if (!mmp_timer_base) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	timer_init(irq);
-	return;
-out:
-	pr_err("Failed to get timer from device tree with error:%d\n", ret);
-}
-#endif
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/4] clocksource: mmp: add mmp timer driver
  2015-02-02  8:31   ` Chao Xie
  (?)
@ 2015-02-02 10:34   ` Mark Rutland
  -1 siblings, 0 replies; 25+ messages in thread
From: Mark Rutland @ 2015-02-02 10:34 UTC (permalink / raw)
  To: Chao Xie; +Cc: daniel.lezcano, tglx, haojian.zhuang, linux-kernel, devicetree

On Mon, Feb 02, 2015 at 08:31:36AM +0000, Chao Xie wrote:
> From: Chao Xie <chao.xie@marvell.com>
> 
> MMP timer is attached to APB bus, It has the following
> limitation.
> 1. When get count of timer counter, it need some delay
>    to get a stable count.
> 2. When set match register, it need disable the counter
>    first, and enable it after set the match register.
>    The disabling need wait for 2 clock cycle to take
>    effect.
> 
> To improve above #1, shadow register for count is added.
> To improve above #2, CRSR register is added for quick updating.
> 
> So there are three types of timer.
> timer1: old timer.
> timer2: old timer with shadow register.
> timer3: old timer with shadow and CRSR register.
> 
> This timer driver will be used for many SOCes.
> 1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
> 2. pxa1L88, pxa1U88 have timer1 and timer3.
> 3. pxa1928 has timer 2.
> 
> The driver supports DT and non-DT initialization.
> The driver supports UP/SMP SOCes and 64 bit core.
> 
> Signed-off-by: Chao Xie <chao.xie@marvell.com>
> ---
>  .../devicetree/bindings/arm/mrvl/timer.txt         |  61 +-
>  drivers/clocksource/Makefile                       |   1 +
>  drivers/clocksource/timer-mmp.c                    | 837 +++++++++++++++++++++
>  include/linux/mmp_timer.h                          |  40 +
>  4 files changed, 933 insertions(+), 6 deletions(-)
>  create mode 100644 drivers/clocksource/timer-mmp.c
>  create mode 100644 include/linux/mmp_timer.h
> 
> diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> index 9a6e251..b49cb3e 100644
> --- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> +++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> @@ -1,13 +1,62 @@
>  * Marvell MMP Timer controller
> 
> +Each timer have multiple counters, so the timer DT need include counter's
> +description.
> +
> +1. Timer
> +
>  Required properties:
> -- compatible : Should be "mrvl,mmp-timer".
> +- compatible : Should be "marvell,mmp-timer".
>  - reg : Address and length of the register set of timer controller.
> -- interrupts : Should be the interrupt number.
> +- clocks : The first clock is the fequency of the apb bus that the timer
> +  attached to. The second clock is the fast clock frequency of the timer.
> +  This frequency and fast clock are used to calculated delay
> +  loops for clock operations.

It might be a good idea to use clock-names for "apb" and "fast" and use
them in the driver.

> +
> +Optional properties:
> +- marvell,timer-has-crsr : This timer has CRSR register.
> +- marvell,timer-has-shadow : This timer has shadow register.
> +
> +2. Counter

The coutner nodes appear to be sub-nodes of the timer. That should be
stated explicitly.

> +
> +Required properties:
> +- compatible : It can be
> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
> +                                       device.
> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
> +      "marvell,timer-counter-delay" : The counter is used for delay timer.

These are all Linux-internal details. I don't see why we should need
separate strings; Linux should be able to allocate these as appropriate
(and a single timer should be able to be both a clocksource and a delay
timer).

Is there any reason you envisage for having separate strings here? It
douesn't make sense to me to do so.

> +- marvell,timer-counter-id : The counter index in this timer.

It sounds like you could use reg for this, unless you have other
sub-nodes you'll need to instantiate?

> 
> -Example:
> -       timer0: timer@d4014000 {
> -               compatible = "mrvl,mmp-timer";
> -               reg = <0xd4014000 0x100>;
> +Optional properties:
> +- marvell,fast-clock : whether the counter use the fast-clock for counting.

It looks below like this is a policy decision, rather than a description
of the HW. When would you select the fast clock and when would you not?
Why can't the driver figure this out?

> +- interrupts : The interrupt for clock event device.
> +  Only valid for "marvell,timer-counter-clkevt".
> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
> +  "marvell,timer-counter-clkevt".

This sounds like a policy decision, rather than a description of the HW.

Additionally, the number usage seems to be a Linux logical ID, rather
than a physical CPU ID. This is broken.

> +- marvell,timer-counter-broadcast : When this counter acts as clock event
> +  device. It is broadcast clock event device.
> +  Only valid for "marvell,timer-counter-clkevt".

This is a Linux-internal detail. There shouldn't be a binding for this.

> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
> +  Only valid for "marvell,timer-counter-clkevt".

Likewise. Why can't you change the affinity?

For all of the above properties, it sounds like what you actually
need/want to do is to check if the number of available timers >=
num_possible_cpus(), and if so, (dynamically) allocate a counter to each
CPU. If not, set up a timer as a broadcast source (with dynirq).

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/4] clocksource: mmp: add mmp timer driver
@ 2015-02-03  1:25   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-03  1:25 UTC (permalink / raw)
  To: Chao Xie, daniel.lezcano, tglx, haojian.zhuang, linux-kernel,
	devicetree, mark.rutland

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 6999 bytes --]




>From: Mark Rutland [mailto:mark.rutland@arm.com] 
>Sent: 2015年2月2日 18:35
>To: Chao Xie
>Cc: daniel.lezcano@linaro.org; tglx@linutronix.de; haojian.zhuang@linaro.org; linux-kernel@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH 1/4] clocksource: mmp: add mmp timer driver
>
>On Mon, Feb 02, 2015 at 08:31:36AM +0000, Chao Xie wrote:
>> From: Chao Xie <chao.xie@marvell.com>
>> 
>> MMP timer is attached to APB bus, It has the following limitation.
>> 1. When get count of timer counter, it need some delay
>>    to get a stable count.
>> 2. When set match register, it need disable the counter
>>    first, and enable it after set the match register.
>>    The disabling need wait for 2 clock cycle to take
>>    effect.
>> 
>> To improve above #1, shadow register for count is added.
>> To improve above #2, CRSR register is added for quick updating.
>> 
>> So there are three types of timer.
>> timer1: old timer.
>> timer2: old timer with shadow register.
>> timer3: old timer with shadow and CRSR register.
>> 
>> This timer driver will be used for many SOCes.
>> 1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
>> 2. pxa1L88, pxa1U88 have timer1 and timer3.
>> 3. pxa1928 has timer 2.
>> 
>> The driver supports DT and non-DT initialization.
>> The driver supports UP/SMP SOCes and 64 bit core.
>> 
>> Signed-off-by: Chao Xie <chao.xie@marvell.com>
>> ---
>>  .../devicetree/bindings/arm/mrvl/timer.txt         |  61 +-
>>  drivers/clocksource/Makefile                       |   1 +
>>  drivers/clocksource/timer-mmp.c                    | 837 +++++++++++++++++++++
>>  include/linux/mmp_timer.h                          |  40 +
>>  4 files changed, 933 insertions(+), 6 deletions(-)  create mode 
>> 100644 drivers/clocksource/timer-mmp.c  create mode 100644 
>> include/linux/mmp_timer.h
>> 
>> diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt 
>> b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> index 9a6e251..b49cb3e 100644
>> --- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> +++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> @@ -1,13 +1,62 @@
>>  * Marvell MMP Timer controller
>> 
>> +Each timer have multiple counters, so the timer DT need include 
>> +counter's description.
>> +
>> +1. Timer
>> +
>>  Required properties:
>> -- compatible : Should be "mrvl,mmp-timer".
>> +- compatible : Should be "marvell,mmp-timer".
>>  - reg : Address and length of the register set of timer controller.
>> -- interrupts : Should be the interrupt number.
>> +- clocks : The first clock is the fequency of the apb bus that the 
>> +timer
>> +  attached to. The second clock is the fast clock frequency of the timer.
>> +  This frequency and fast clock are used to calculated delay
>> +  loops for clock operations.
>
>It might be a good idea to use clock-names for "apb" and "fast" and use them in the driver.

>


Yes. I will fix it.

>> +
>> +Optional properties:
>> +- marvell,timer-has-crsr : This timer has CRSR register.
>> +- marvell,timer-has-shadow : This timer has shadow register.
>> +
>> +2. Counter
>
>The coutner nodes appear to be sub-nodes of the timer. That should be stated explicitly.

>


I will fix it.


>> +>> +Required properties:
>> +- compatible : It can be
>> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
>> +                                       device.
>> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
>> +      "marvell,timer-counter-delay" : The counter is used for delay timer.
>
>These are all Linux-internal details. I don't see why we should need separate strings; Linux should be able to allocate these as appropriate (and a single timer should be able to be both a clocksource and a delay timer).
>
>Is there any reason you envisage for having separate strings here? It douesn't make sense to me to do so.
>
>> +- marvell,timer-counter-id : The counter index in this timer.
>
>It sounds like you could use reg for this, unless you have other sub-nodes you'll need to instantiate?
>
>> 
>> -Example:
>> -       timer0: timer@d4014000 {
>> -               compatible = "mrvl,mmp-timer";
>> -               reg = <0xd4014000 0x100>;
>> +Optional properties:
>> +- marvell,fast-clock : whether the counter use the fast-clock for counting.
>
>It looks below like this is a policy decision, rather than a description of the HW. When would you select the fast clock and when would you not?
>Why can't the driver figure this out?

>


The name is a little confused. There are two types of fast clock inputs in all the SOCes. From vctcxo or PLL1.
In some low power mode, PLL1 will be shutdown, and timer can not use this clock because it need wake up the cores when time out.
Timer driver can not get the information that whether this fast clock is coming from a clock source that will be shutdown in the low
power mode or not.
So i need tell it in the device tree that the fast clock is still active in the low power mode.


>> +- interrupts : The interrupt for clock event device.>> +  Only valid for "marvell,timer-counter-clkevt".
>> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only 
>> +valid for
>> +  "marvell,timer-counter-clkevt".
>
>This sounds like a policy decision, rather than a description of the HW.
>
>Additionally, the number usage seems to be a Linux logical ID, rather than a physical CPU ID. This is broken.
>
>> +- marvell,timer-counter-broadcast : When this counter acts as clock 
>> +event
>> +  device. It is broadcast clock event device.
>> +  Only valid for "marvell,timer-counter-clkevt".
>
>This is a Linux-internal detail. There shouldn't be a binding for this.
>
>> +- marvell,timer-counter-nodynirq : When this counter acts as 
>> +broadcast clock
>> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
>> +  Only valid for "marvell,timer-counter-clkevt".
>
>Likewise. Why can't you change the affinity?
>
>For all of the above properties, it sounds like what you actually need/want to do is to check if the number of available timers >= num_possible_cpus(), and if so, (dynamically) allocate a counter to each CPU. If not, set up a timer as a broadcast source (with dynirq).

>
i understand what you suggested
I want to make a driver for a timer, not for timers.
So if the device tree has
timer0 {
}
timer1 {
}
The driver will be probed twice, and it does not need care the relationship between these timers.


To do what your suggested, the device tree will be
timers {
    timer0 {
    };
    timer1 {
    };
}
The driver is for timers, and only be probed once, and it will consider the relationship between the timers. For example, how much counter can be allocated for per-cpu local timer.
If above device tree is accepted, i can change the driver.

>Thanks,
>Mark.
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/4] clocksource: mmp: add mmp timer driver
@ 2015-02-03  1:25   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2015-02-03  1:25 UTC (permalink / raw)
  To: Chao Xie, daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A,
	tglx-hfZtesqFncYOwBW4kG4KsQ,
	haojian.zhuang-QSEj5FYQhm4dnm+yROfE0A,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8




>From: Mark Rutland [mailto:mark.rutland@arm.com] 
>Sent: 2015年2月2日 18:35
>To: Chao Xie
>Cc: daniel.lezcano@linaro.org; tglx@linutronix.de; haojian.zhuang@linaro.org; linux-kernel@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH 1/4] clocksource: mmp: add mmp timer driver
>
>On Mon, Feb 02, 2015 at 08:31:36AM +0000, Chao Xie wrote:
>> From: Chao Xie <chao.xie@marvell.com>
>> 
>> MMP timer is attached to APB bus, It has the following limitation.
>> 1. When get count of timer counter, it need some delay
>>    to get a stable count.
>> 2. When set match register, it need disable the counter
>>    first, and enable it after set the match register.
>>    The disabling need wait for 2 clock cycle to take
>>    effect.
>> 
>> To improve above #1, shadow register for count is added.
>> To improve above #2, CRSR register is added for quick updating.
>> 
>> So there are three types of timer.
>> timer1: old timer.
>> timer2: old timer with shadow register.
>> timer3: old timer with shadow and CRSR register.
>> 
>> This timer driver will be used for many SOCes.
>> 1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
>> 2. pxa1L88, pxa1U88 have timer1 and timer3.
>> 3. pxa1928 has timer 2.
>> 
>> The driver supports DT and non-DT initialization.
>> The driver supports UP/SMP SOCes and 64 bit core.
>> 
>> Signed-off-by: Chao Xie <chao.xie@marvell.com>
>> ---
>>  .../devicetree/bindings/arm/mrvl/timer.txt         |  61 +-
>>  drivers/clocksource/Makefile                       |   1 +
>>  drivers/clocksource/timer-mmp.c                    | 837 +++++++++++++++++++++
>>  include/linux/mmp_timer.h                          |  40 +
>>  4 files changed, 933 insertions(+), 6 deletions(-)  create mode 
>> 100644 drivers/clocksource/timer-mmp.c  create mode 100644 
>> include/linux/mmp_timer.h
>> 
>> diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt 
>> b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> index 9a6e251..b49cb3e 100644
>> --- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> +++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> @@ -1,13 +1,62 @@
>>  * Marvell MMP Timer controller
>> 
>> +Each timer have multiple counters, so the timer DT need include 
>> +counter's description.
>> +
>> +1. Timer
>> +
>>  Required properties:
>> -- compatible : Should be "mrvl,mmp-timer".
>> +- compatible : Should be "marvell,mmp-timer".
>>  - reg : Address and length of the register set of timer controller.
>> -- interrupts : Should be the interrupt number.
>> +- clocks : The first clock is the fequency of the apb bus that the 
>> +timer
>> +  attached to. The second clock is the fast clock frequency of the timer.
>> +  This frequency and fast clock are used to calculated delay
>> +  loops for clock operations.
>
>It might be a good idea to use clock-names for "apb" and "fast" and use them in the driver.

>


Yes. I will fix it.

>> +
>> +Optional properties:
>> +- marvell,timer-has-crsr : This timer has CRSR register.
>> +- marvell,timer-has-shadow : This timer has shadow register.
>> +
>> +2. Counter
>
>The coutner nodes appear to be sub-nodes of the timer. That should be stated explicitly.

>


I will fix it.


>> +>> +Required properties:
>> +- compatible : It can be
>> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
>> +                                       device.
>> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
>> +      "marvell,timer-counter-delay" : The counter is used for delay timer.
>
>These are all Linux-internal details. I don't see why we should need separate strings; Linux should be able to allocate these as appropriate (and a single timer should be able to be both a clocksource and a delay timer).
>
>Is there any reason you envisage for having separate strings here? It douesn't make sense to me to do so.
>
>> +- marvell,timer-counter-id : The counter index in this timer.
>
>It sounds like you could use reg for this, unless you have other sub-nodes you'll need to instantiate?
>
>> 
>> -Example:
>> -       timer0: timer@d4014000 {
>> -               compatible = "mrvl,mmp-timer";
>> -               reg = <0xd4014000 0x100>;
>> +Optional properties:
>> +- marvell,fast-clock : whether the counter use the fast-clock for counting.
>
>It looks below like this is a policy decision, rather than a description of the HW. When would you select the fast clock and when would you not?
>Why can't the driver figure this out?

>


The name is a little confused. There are two types of fast clock inputs in all the SOCes. From vctcxo or PLL1.
In some low power mode, PLL1 will be shutdown, and timer can not use this clock because it need wake up the cores when time out.
Timer driver can not get the information that whether this fast clock is coming from a clock source that will be shutdown in the low
power mode or not.
So i need tell it in the device tree that the fast clock is still active in the low power mode.


>> +- interrupts : The interrupt for clock event device.>> +  Only valid for "marvell,timer-counter-clkevt".
>> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only 
>> +valid for
>> +  "marvell,timer-counter-clkevt".
>
>This sounds like a policy decision, rather than a description of the HW.
>
>Additionally, the number usage seems to be a Linux logical ID, rather than a physical CPU ID. This is broken.
>
>> +- marvell,timer-counter-broadcast : When this counter acts as clock 
>> +event
>> +  device. It is broadcast clock event device.
>> +  Only valid for "marvell,timer-counter-clkevt".
>
>This is a Linux-internal detail. There shouldn't be a binding for this.
>
>> +- marvell,timer-counter-nodynirq : When this counter acts as 
>> +broadcast clock
>> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
>> +  Only valid for "marvell,timer-counter-clkevt".
>
>Likewise. Why can't you change the affinity?
>
>For all of the above properties, it sounds like what you actually need/want to do is to check if the number of available timers >= num_possible_cpus(), and if so, (dynamically) allocate a counter to each CPU. If not, set up a timer as a broadcast source (with dynirq).

>
i understand what you suggested
I want to make a driver for a timer, not for timers.
So if the device tree has
timer0 {
}
timer1 {
}
The driver will be probed twice, and it does not need care the relationship between these timers.


To do what your suggested, the device tree will be
timers {
    timer0 {
    };
    timer1 {
    };
}
The driver is for timers, and only be probed once, and it will consider the relationship between the timers. For example, how much counter can be allocated for per-cpu local timer.
If above device tree is accepted, i can change the driver.

>Thanks,
>Mark.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/4] clocksource: mmp: add mmp timer driver
  2014-03-27 12:13               ` Chao Xie
@ 2014-03-28  1:55                   ` Chao Xie
  -1 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2014-03-28  1:55 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Chao Xie, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	haojian.zhuang-Re5JQEeQqe8AvxtiuMwx3w

hi, Mark

Thanks very much for your review. I appreiciate your professional
suggestions and comments.

In order to make it clear, i need describe the HW fist.

There are several timer controllers, and we call each one as "timer". Each timer
has its own register space.
A timer will have 3 counters, and each counter can be seperately
disabled/enabled.
Each counter has 3 match registers. Any match of the three matches
will generate the same
interrupt.
>From the timer controller, each counter will export its own interrupt
siginal out of the controller,
but before it is connected to interrupt chip, SOC designer may combine them.

There is a silicon requirment for the timer reading.
Every time read the count, there will be a delay, or you may read a
part of the counter.
So we repeatly read the counter until two reads are same.

There is a slicon requirment for the timer setting
When we set match registers, we need disable the counter first, then
set the match register,
after that enable the counter. Disabling and re-enabling the counter
will make the counter
restart from 0.

There are two clock inputs: fastclk and 32K. fastclk will be 3.25M,
6.5M or somehing else.
Fastclk can not work if the system goes to some deep idle states.
These deep idle states: core subsystem shutdown and some clock unit will
shutdown.
We make use of these deep idle states as part of CPU idle states to
save more power.
For these power states, only 32K clock can work.

So you have questions about the DT settings
1. "marvell,timer-fastclk-frequency" marvell,timer-apb-frequency
You suggest to use  the clock bindings:

       clocks = <&apb_plck>, <&clock_controller 0 3>;
       clock-names = "apb_pclk", "fastclk";
I agree with you. The problem is now mach-mmp CLOCK DT support just starts.
I am afraid in this patch set, i can not do like this, but i am sure i
can change it
after clock DT support is done.

2. "marvell,timer-id" "marvlll,timer-counter-id"
The "marvell,timer-id" i think is not needed, because i do not need to know
the index of the timer. ALL the timer are same to me.
I need "marvell,timer-counter-id" because i need distinuish it when
set the registers

3. "marvell,timer-counter-rating"
I agree with you. It need to be fix in the code not exported in DT file.

4. "marvell,timer-counter-clkevt", "marvell,timer-counter-clksrc",
marvell,timer-counter-delay
For UP SOC, it need at least 1 clksrc and 1 clockevent device

For SMP SOC with arch timer, it need at least 1 clksrc, 1 clockevent
device for broadcast, 1 delay timer
The reason for needing delay timer: It will be running at fastclk to
achieve more accurate delay. clksrc need to be
 set 32K or i will lose the count if system goes to deep idle.
The reason for needing broadcast clkevt: arch timer will not work if
core subsystem is down, i will make them switch to
broadcast timer, so timer can still wake up cores out of idle.

For SMP SOC without arch timer, it only happens for some early version
of SOCes. except the clock devices
needed buy SMP SOC, we create the per cpu clock event device to
replace the arch timer.

So i still do not understand your suggestions "Why can you not just
describe the set of timers available to Linux and
allow it to allocate them as required?"
Can you describe it more clear?

5. marvell,timer-counter-nodynirq
The reason is couple idle.
The couple idle implementation implies that CPU0 will be first core wake up.
So, we can not have CLOCK_EVT_FEAT_DYNIRQ because it may make CPU1 to
be first core wake up.


On Thu, Mar 27, 2014 at 8:13 PM, Chao Xie <xiechao.mail-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>>> >>
>>> >> +Each timer have multiple counters, so the timer DT need include counter's
>>> >> +description.
>>> >> +
>>> >> +1. Timer
>>> >> +
>>> >>  Required properties:
>>> >> -- compatible : Should be "mrvl,mmp-timer".
>>> >> +- compatible : Should be "marvell,mmp-timer".
>>> >>  - reg : Address and length of the register set of timer controller.
>>> >> -- interrupts : Should be the interrupt number.
>>> >> +- marvell,timer-id : The index of the timer. The timer controller may
>>> >> +  include several timers.
>>> >
>>> > Is this a single entry or a list?
>>>
>>> Generally, the may be 2/3/4 timers, and each timers will have at most
>>> 3 coounters.
>>> Each counters will have 3 matches. Each counter may have its own
>>> interrupts, or share same
>>> interrupts(counter1 has one interrupts, counter2 and 3 share one). It depends on
>>> how SOC connets the signals.
>>>
>>> The DT will be
>>> timer 0 {
>>>      ...
>>>      ...
>>>      counter0 {
>>>      };
>>>      counter2 {
>>>      };
>>> }
>>
>> I'm somewhat confused here.
>>
>> The top level node appears to be a timer controller (per the reg
>> property description), but the marvell,timer-id property describes an
>> index within the timer controller. This does not seem to make sense.
>>
>> How do you describe multiple timers within a given timer contoller? Is
>> the timer-id a list? Or do you describe the controller repeatedly?
>>
> Thanks for your reply.
>
> the driver only take care of one timer node, if we have 3 timer node defined.
> It means that the driver will be probed 3 times.
>
> Each timer is a controller, and one timer has 3 counters. each counter can be
> set to be free running or period mode.
>
> marvell,timer-id may not be needed, but the marvell,counter-id need to
> be passed.
> The timer driver depends on the id to distinguish the register or bit
> in the register.
>
>>>
>>> >
>>> >> +- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
>>> >> +  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
>>> >> +  SOCes use this timer controller, fast clock may not be same.
>>> >> +- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
>>> >> +  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
>>> >> +  to calculated delay loops for clock operations.
>>> >
>>> > Wouldn't these be better described as clock inputs?
>>>
>>> "apb" frequency is only used to calculated the delay loop.
>>> I do not want to fix the delay loop in the driver.
>>> So pass it in DT directly will make the things easier to handle.
>>>
>>> "fastclk" is a clock input. So waht do you mean "described as clock inputs"?
>>
>> Using the clock bindings:
>>
>>         clocks = <&apb_plck>, <&clock_controller 0 3>;
>>         clock-names = "apb_pclk", "fastclk";
>>
>> The driver can then query the frequencies, enable/disable the clocks as
>> required, etc.
>>
> It is the final choice, but it depends on the clock DT support.
> I just begin the clock DT support for mach-mmp. They are still in design phase.
> Clock framework is more complicated than the timer controller.
>
>>>
>>> >
>>> >> +
>>> >> +Optional properties:
>>> >> +- marvell,timer-has-crsr : This timer has CRSR register.
>>> >> +- marvell,timer-has-shadow : This timer has shadow register.
>>> >> +
>>> >> +2. Counter
>>> >> +
>>> >> +Required properties:
>>> >> +- compatible : It can be
>>> >> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
>>> >> +                                       device.
>>> >> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
>>> >> +      "marvell,timer-counter-delay" : The counter is used for delay timer.
>>> >
>>> > These are _not_ hardware properties. Why can the driver not choose how
>>> > to use each of the counter sub-blocks?
>>> >
>>> > How would blocks with each of these strings actually differ?
>>> >
>>>
>>> SOCes may have 2 or 3 or 4 timers, some timers may be used by CP not AP but
>>> AP can still access it.
>>> There may be UP SOC, or SMP SOC. So there are may be some general usage
>>> 1. UP SOC: need one clocksource, need one clock event device
>>> 2. SMP SOC: need one clocksource, need one broadcast clock event device because
>>>    arch timer will be shutdown when core is down
>>> 3. SMP SOC: it has bug with arch timer. So need one clock source,
>>> NR_CPUs clock event
>>>     device to replace the arch timer. There are may be 2 cores or 4 cores.
>>
>> Ok, but in all of these cases the blocks that you are giving different
>> compatible strings are identical. The only difference is how you expect
>> Linux to use them.
>>
>> Why can you not just describe the set of timers available to Linux and
>> allow it to allocate them as required?
>>
>
>
> So how can I make Linux to allocate them as required?
>
>
>>> Above requeimetns are also hardware requriments from SOCes.
>>>
>>> The driver will be used for many SOCes, we can not to fix the counter
>>> usage in driver.
>>
>> I'm arguing for dynamically deciding how to use the timers, not assuming
>> one of the three cases you've laid out above, and not describing a
>> possible software use of the counters in the DT.
>>
>
> I am a little confused.
>
> So what is your suggestions?
> we have the below usage
> 1. UP SOC, we need clock source, clock event device
> 2. SMP SOC with arch timer, we need clock source, broadcast clock event device
> 3. SMP SOC without arch timer, we need clock source, NR_CPU clock
> event device and delay timer.
>
>
>>> >> +- marvell,timer-counter-id : The counter index in this timer.
>>> >
>>> > Perhaps use a reg for this?
>>> >
>>>
>>> The counter does not have its own register space. They are belongs to a timer.
>>> Counter does not need to map the regsiter spance, it is done by timer.
>>> For example, for counter enable, all coutners share same register. CER
>>> For interrupt enable, each counter has its own register. IERx
>>>
>>> So if we use like "reg = <IERx offset>", i do not see any benefit. while
>>> direct setting "marvell,timer-counter-id" seems make the DT more clear.
>>
>> The reg doesn't have to be a register space -- it's often used simply as
>> a form of address/identifier without a size (consider SPI's use of the
>> reg property). Here the index is essentially an address/identifier for
>> the counter.
>>
>> Given that the each timer also seems to have a unique ID perhaps using
>> the reg is not the best strategy here. I was simply raising the
>> possiblity.
>>
>>>
>>> >>
>>> >> -Example:
>>> >> -       timer0: timer@d4014000 {
>>> >> -               compatible = "mrvl,mmp-timer";
>>> >> -               reg = <0xd4014000 0x100>;
>>> >> +Optional properties:
>>> >> +- interrupts : The counters may have different IRQs or share same IRQs.
>>> >> +  Only valid for "marvell,timer-counter-clkevt".
>>> >
>>> > Describe what the interrupt is logically.
>>> >
>>> > What is the interrupt logically?
>>> >
>>>
>>> The interrupt topolog is designed by SOCes.
>>> Each counter has one interrupt, but SOCes may connects some coutners' interrupt
>>> into one.
>>> In timer driver, we do not care about it. We depeneds on IRQ chip to handle it.
>>
>> How the interrupt is wired and whether it is shared is not important
>> from the view of the binding. What is important is which interrupt from
>> the timer is being described. If it has a given name, that should be
>> mentioned. Otherwise a simple description will suffice.
>>
>> The presence of external interrupt muxing doesn't seem to be important
>> here to the binding -- in that case an OS could request shared
>> interrupts or decide to only make use of a subset of interrupts. That
>> seems to be outside of the scope of the binding.
>>
>
> I see. I think a simple description is enough. i do not need mention the shared
> interrupts here because the timer driver does not care about it.
>
>>>
>>> >> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
>>> >> +  "marvell,timer-counter-clkevt".
>>> >
>>> > How is the counter bound to a particular CPU? Is there really a
>>> > relationship in hardware between a given CPU and counter?
>>> >
>>>
>>> For some SMP SOCes, for example, marvell's pxa988/pxa1088/pxa1L88
>>> They have bug in arch timer. So we make each CPU to have one dedicated
>>> counter to replace the arch timer.
>>
>> Is there any real topological relationship between CPUs and counters, or
>> is this arbitrary? If the latter, why can the kernel not decide how to
>> allocate timers to CPUs?
>>
>
> All the timers can be seen by every CPUes.
> For arch timer, each CPU see its own instance, while for the mmp
> timers, each CPU
> can see all of them.
> So there are some problems
> 1. The number of the timer is not fixed. It can be 2, 3, 4
> 2. Some timers can be seen by CP, and CP will use these timers. Which
> timer can be seen
>     by CP is defined by SOC design
> 3. Some counter share one interrupt, these counters can not be
> allocated for per cpu timer to replace
>     arch timer.
> So which counter allocated is limited by SOC design.
>
> So if do not have above limitation, can you tell me that how can i let
> kernel to decide allocation?
> Thanks.
>
>>>
>>> >> +- "marvell,timer-counter-rating" : The rating when register clock event device
>>> >> +  or clock source. Only valid for "marvell,timer-counter-clkevt" and
>>> >> +  "marvell,timer-counter-clksrc".
>>> >
>>> > What does this mean? This seems like another leak of Linux internals.
>>>
>>> It is the rating when register clock source and clock event.
>>> I think i can fix it in the driver.
>>
>> Ok. This is a Linux interenal detail that I do not think should be in
>> the DT.
>>
>>>
>>> >
>>> >> +- marvell,timer-counter-broadcast : When this counter acts as clock event
>>> >> +  device. It is broadcast clock event device.
>>> >> +  Only valid for "marvell,timer-counter-clkevt".
>>> >
>>> > This does not seem like a hardware property. Why can Linux not decide
>>> > which counter (if any) to use as the broadcast source?
>>> >
>>>
>>> For some SMP soces, for example, marvell's pxa988/pxa1088/pxa1L88
>>> They have bug in arch timer. So we make each CPU to have one dedicated
>>> counter to replace the arch timer. So for these SOCes, broadcast timer is not
>>> needed. For these counters, we have to set IRQ affinity to the specific core.
>>
>> That's fine, we can do that in the absence of both this property and
>> another timer (the arch timer).
>>
>>> For some SMP soces, for example, they can use arch timer, but we have
>>> to register
>>> a clock event device for broadcast because we lose arch timer if core is down.
>>> For this coutner, we have to set IRQ affinity to ALL cores.
>>
>> In that case the arch timer will register (which can have a higher
>> rating than this timer), and this timers can be automatically repurposed
>> as the broadcast source.
>>
>> I do not see the necessity of this property.
>>
> It is true that arch timer is first priority.
> There are something need to be considered
> 1. we registered NR_CPU timers to acts as arch timer. We set the
> cpu_mask of be cpu_mask(cpu_id), not
>     cpu_mask(ALL_CPUes). We also bind the interrupt to the dedicated
> CPU. For example, if timer 0 counter 0
>     is used by CPU0 to replace arch timer. the counter's IRQ only
> route to the CPU0.
>     If we have arch timers, it means that there are NR_CPU timers, i
> do not think they can be acts as broadcast timer
> 2. As i said, some coutners share interrupts. We can not allocated
> this kind of counter as per cpu clock event device.
>     For exmaple, timer 0 coutner1 and counter 2 share same interrupt.
> If they are used by CPU0 and CPU1, it means that
>     same interrupt will be bind to two CPUes. each timer interrupt
> happens, a power-down core need wake up to check the
>     interrupt does not targets at it.
>
> So how to use these counter in fact depends on the SOCes design.
> That why i need pass this kind of information to timer driver.
>
>>> >> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
>>> >> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
>>> >> +  Only valid for "marvell,timer-counter-clkevt".
>>> >
>>> > Likewise this does not sound like a hardware property.
>>>
>>> For a broadcast timer, there is feature "CLOCK_EVT_FEAT_DYNIRQ".
>>> This feature is a clock event device's fearture. It means that the
>>> clock event device's irq can
>>> be set to any core.
>>> In fact it does not for all SOCes that the interrupt of the counter
>>> can be set to any core.
>>
>> The naming and use of this property still seems incredibly
>> Linux-specific.
>>
>> Why can some of the counter interrupts not be routed to an arbitrary
>> CPU?
>>
> The reason is couple idle.
> The couple idle implementation implies that CPU0 will be first core wake up.
> So, we can not have CLOCK_EVT_FEAT_DYNIRQ because it may make CPU1 to
> be first core wake up.
>
>> Cheers,
>> Mark.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 1/4] clocksource: mmp: add mmp timer driver
@ 2014-03-28  1:55                   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2014-03-28  1:55 UTC (permalink / raw)
  To: linux-arm-kernel

hi, Mark

Thanks very much for your review. I appreiciate your professional
suggestions and comments.

In order to make it clear, i need describe the HW fist.

There are several timer controllers, and we call each one as "timer". Each timer
has its own register space.
A timer will have 3 counters, and each counter can be seperately
disabled/enabled.
Each counter has 3 match registers. Any match of the three matches
will generate the same
interrupt.
>From the timer controller, each counter will export its own interrupt
siginal out of the controller,
but before it is connected to interrupt chip, SOC designer may combine them.

There is a silicon requirment for the timer reading.
Every time read the count, there will be a delay, or you may read a
part of the counter.
So we repeatly read the counter until two reads are same.

There is a slicon requirment for the timer setting
When we set match registers, we need disable the counter first, then
set the match register,
after that enable the counter. Disabling and re-enabling the counter
will make the counter
restart from 0.

There are two clock inputs: fastclk and 32K. fastclk will be 3.25M,
6.5M or somehing else.
Fastclk can not work if the system goes to some deep idle states.
These deep idle states: core subsystem shutdown and some clock unit will
shutdown.
We make use of these deep idle states as part of CPU idle states to
save more power.
For these power states, only 32K clock can work.

So you have questions about the DT settings
1. "marvell,timer-fastclk-frequency" marvell,timer-apb-frequency
You suggest to use  the clock bindings:

       clocks = <&apb_plck>, <&clock_controller 0 3>;
       clock-names = "apb_pclk", "fastclk";
I agree with you. The problem is now mach-mmp CLOCK DT support just starts.
I am afraid in this patch set, i can not do like this, but i am sure i
can change it
after clock DT support is done.

2. "marvell,timer-id" "marvlll,timer-counter-id"
The "marvell,timer-id" i think is not needed, because i do not need to know
the index of the timer. ALL the timer are same to me.
I need "marvell,timer-counter-id" because i need distinuish it when
set the registers

3. "marvell,timer-counter-rating"
I agree with you. It need to be fix in the code not exported in DT file.

4. "marvell,timer-counter-clkevt", "marvell,timer-counter-clksrc",
marvell,timer-counter-delay
For UP SOC, it need at least 1 clksrc and 1 clockevent device

For SMP SOC with arch timer, it need at least 1 clksrc, 1 clockevent
device for broadcast, 1 delay timer
The reason for needing delay timer: It will be running at fastclk to
achieve more accurate delay. clksrc need to be
 set 32K or i will lose the count if system goes to deep idle.
The reason for needing broadcast clkevt: arch timer will not work if
core subsystem is down, i will make them switch to
broadcast timer, so timer can still wake up cores out of idle.

For SMP SOC without arch timer, it only happens for some early version
of SOCes. except the clock devices
needed buy SMP SOC, we create the per cpu clock event device to
replace the arch timer.

So i still do not understand your suggestions "Why can you not just
describe the set of timers available to Linux and
allow it to allocate them as required?"
Can you describe it more clear?

5. marvell,timer-counter-nodynirq
The reason is couple idle.
The couple idle implementation implies that CPU0 will be first core wake up.
So, we can not have CLOCK_EVT_FEAT_DYNIRQ because it may make CPU1 to
be first core wake up.


On Thu, Mar 27, 2014 at 8:13 PM, Chao Xie <xiechao.mail@gmail.com> wrote:
>>> >>
>>> >> +Each timer have multiple counters, so the timer DT need include counter's
>>> >> +description.
>>> >> +
>>> >> +1. Timer
>>> >> +
>>> >>  Required properties:
>>> >> -- compatible : Should be "mrvl,mmp-timer".
>>> >> +- compatible : Should be "marvell,mmp-timer".
>>> >>  - reg : Address and length of the register set of timer controller.
>>> >> -- interrupts : Should be the interrupt number.
>>> >> +- marvell,timer-id : The index of the timer. The timer controller may
>>> >> +  include several timers.
>>> >
>>> > Is this a single entry or a list?
>>>
>>> Generally, the may be 2/3/4 timers, and each timers will have at most
>>> 3 coounters.
>>> Each counters will have 3 matches. Each counter may have its own
>>> interrupts, or share same
>>> interrupts(counter1 has one interrupts, counter2 and 3 share one). It depends on
>>> how SOC connets the signals.
>>>
>>> The DT will be
>>> timer 0 {
>>>      ...
>>>      ...
>>>      counter0 {
>>>      };
>>>      counter2 {
>>>      };
>>> }
>>
>> I'm somewhat confused here.
>>
>> The top level node appears to be a timer controller (per the reg
>> property description), but the marvell,timer-id property describes an
>> index within the timer controller. This does not seem to make sense.
>>
>> How do you describe multiple timers within a given timer contoller? Is
>> the timer-id a list? Or do you describe the controller repeatedly?
>>
> Thanks for your reply.
>
> the driver only take care of one timer node, if we have 3 timer node defined.
> It means that the driver will be probed 3 times.
>
> Each timer is a controller, and one timer has 3 counters. each counter can be
> set to be free running or period mode.
>
> marvell,timer-id may not be needed, but the marvell,counter-id need to
> be passed.
> The timer driver depends on the id to distinguish the register or bit
> in the register.
>
>>>
>>> >
>>> >> +- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
>>> >> +  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
>>> >> +  SOCes use this timer controller, fast clock may not be same.
>>> >> +- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
>>> >> +  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
>>> >> +  to calculated delay loops for clock operations.
>>> >
>>> > Wouldn't these be better described as clock inputs?
>>>
>>> "apb" frequency is only used to calculated the delay loop.
>>> I do not want to fix the delay loop in the driver.
>>> So pass it in DT directly will make the things easier to handle.
>>>
>>> "fastclk" is a clock input. So waht do you mean "described as clock inputs"?
>>
>> Using the clock bindings:
>>
>>         clocks = <&apb_plck>, <&clock_controller 0 3>;
>>         clock-names = "apb_pclk", "fastclk";
>>
>> The driver can then query the frequencies, enable/disable the clocks as
>> required, etc.
>>
> It is the final choice, but it depends on the clock DT support.
> I just begin the clock DT support for mach-mmp. They are still in design phase.
> Clock framework is more complicated than the timer controller.
>
>>>
>>> >
>>> >> +
>>> >> +Optional properties:
>>> >> +- marvell,timer-has-crsr : This timer has CRSR register.
>>> >> +- marvell,timer-has-shadow : This timer has shadow register.
>>> >> +
>>> >> +2. Counter
>>> >> +
>>> >> +Required properties:
>>> >> +- compatible : It can be
>>> >> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
>>> >> +                                       device.
>>> >> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
>>> >> +      "marvell,timer-counter-delay" : The counter is used for delay timer.
>>> >
>>> > These are _not_ hardware properties. Why can the driver not choose how
>>> > to use each of the counter sub-blocks?
>>> >
>>> > How would blocks with each of these strings actually differ?
>>> >
>>>
>>> SOCes may have 2 or 3 or 4 timers, some timers may be used by CP not AP but
>>> AP can still access it.
>>> There may be UP SOC, or SMP SOC. So there are may be some general usage
>>> 1. UP SOC: need one clocksource, need one clock event device
>>> 2. SMP SOC: need one clocksource, need one broadcast clock event device because
>>>    arch timer will be shutdown when core is down
>>> 3. SMP SOC: it has bug with arch timer. So need one clock source,
>>> NR_CPUs clock event
>>>     device to replace the arch timer. There are may be 2 cores or 4 cores.
>>
>> Ok, but in all of these cases the blocks that you are giving different
>> compatible strings are identical. The only difference is how you expect
>> Linux to use them.
>>
>> Why can you not just describe the set of timers available to Linux and
>> allow it to allocate them as required?
>>
>
>
> So how can I make Linux to allocate them as required?
>
>
>>> Above requeimetns are also hardware requriments from SOCes.
>>>
>>> The driver will be used for many SOCes, we can not to fix the counter
>>> usage in driver.
>>
>> I'm arguing for dynamically deciding how to use the timers, not assuming
>> one of the three cases you've laid out above, and not describing a
>> possible software use of the counters in the DT.
>>
>
> I am a little confused.
>
> So what is your suggestions?
> we have the below usage
> 1. UP SOC, we need clock source, clock event device
> 2. SMP SOC with arch timer, we need clock source, broadcast clock event device
> 3. SMP SOC without arch timer, we need clock source, NR_CPU clock
> event device and delay timer.
>
>
>>> >> +- marvell,timer-counter-id : The counter index in this timer.
>>> >
>>> > Perhaps use a reg for this?
>>> >
>>>
>>> The counter does not have its own register space. They are belongs to a timer.
>>> Counter does not need to map the regsiter spance, it is done by timer.
>>> For example, for counter enable, all coutners share same register. CER
>>> For interrupt enable, each counter has its own register. IERx
>>>
>>> So if we use like "reg = <IERx offset>", i do not see any benefit. while
>>> direct setting "marvell,timer-counter-id" seems make the DT more clear.
>>
>> The reg doesn't have to be a register space -- it's often used simply as
>> a form of address/identifier without a size (consider SPI's use of the
>> reg property). Here the index is essentially an address/identifier for
>> the counter.
>>
>> Given that the each timer also seems to have a unique ID perhaps using
>> the reg is not the best strategy here. I was simply raising the
>> possiblity.
>>
>>>
>>> >>
>>> >> -Example:
>>> >> -       timer0: timer at d4014000 {
>>> >> -               compatible = "mrvl,mmp-timer";
>>> >> -               reg = <0xd4014000 0x100>;
>>> >> +Optional properties:
>>> >> +- interrupts : The counters may have different IRQs or share same IRQs.
>>> >> +  Only valid for "marvell,timer-counter-clkevt".
>>> >
>>> > Describe what the interrupt is logically.
>>> >
>>> > What is the interrupt logically?
>>> >
>>>
>>> The interrupt topolog is designed by SOCes.
>>> Each counter has one interrupt, but SOCes may connects some coutners' interrupt
>>> into one.
>>> In timer driver, we do not care about it. We depeneds on IRQ chip to handle it.
>>
>> How the interrupt is wired and whether it is shared is not important
>> from the view of the binding. What is important is which interrupt from
>> the timer is being described. If it has a given name, that should be
>> mentioned. Otherwise a simple description will suffice.
>>
>> The presence of external interrupt muxing doesn't seem to be important
>> here to the binding -- in that case an OS could request shared
>> interrupts or decide to only make use of a subset of interrupts. That
>> seems to be outside of the scope of the binding.
>>
>
> I see. I think a simple description is enough. i do not need mention the shared
> interrupts here because the timer driver does not care about it.
>
>>>
>>> >> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
>>> >> +  "marvell,timer-counter-clkevt".
>>> >
>>> > How is the counter bound to a particular CPU? Is there really a
>>> > relationship in hardware between a given CPU and counter?
>>> >
>>>
>>> For some SMP SOCes, for example, marvell's pxa988/pxa1088/pxa1L88
>>> They have bug in arch timer. So we make each CPU to have one dedicated
>>> counter to replace the arch timer.
>>
>> Is there any real topological relationship between CPUs and counters, or
>> is this arbitrary? If the latter, why can the kernel not decide how to
>> allocate timers to CPUs?
>>
>
> All the timers can be seen by every CPUes.
> For arch timer, each CPU see its own instance, while for the mmp
> timers, each CPU
> can see all of them.
> So there are some problems
> 1. The number of the timer is not fixed. It can be 2, 3, 4
> 2. Some timers can be seen by CP, and CP will use these timers. Which
> timer can be seen
>     by CP is defined by SOC design
> 3. Some counter share one interrupt, these counters can not be
> allocated for per cpu timer to replace
>     arch timer.
> So which counter allocated is limited by SOC design.
>
> So if do not have above limitation, can you tell me that how can i let
> kernel to decide allocation?
> Thanks.
>
>>>
>>> >> +- "marvell,timer-counter-rating" : The rating when register clock event device
>>> >> +  or clock source. Only valid for "marvell,timer-counter-clkevt" and
>>> >> +  "marvell,timer-counter-clksrc".
>>> >
>>> > What does this mean? This seems like another leak of Linux internals.
>>>
>>> It is the rating when register clock source and clock event.
>>> I think i can fix it in the driver.
>>
>> Ok. This is a Linux interenal detail that I do not think should be in
>> the DT.
>>
>>>
>>> >
>>> >> +- marvell,timer-counter-broadcast : When this counter acts as clock event
>>> >> +  device. It is broadcast clock event device.
>>> >> +  Only valid for "marvell,timer-counter-clkevt".
>>> >
>>> > This does not seem like a hardware property. Why can Linux not decide
>>> > which counter (if any) to use as the broadcast source?
>>> >
>>>
>>> For some SMP soces, for example, marvell's pxa988/pxa1088/pxa1L88
>>> They have bug in arch timer. So we make each CPU to have one dedicated
>>> counter to replace the arch timer. So for these SOCes, broadcast timer is not
>>> needed. For these counters, we have to set IRQ affinity to the specific core.
>>
>> That's fine, we can do that in the absence of both this property and
>> another timer (the arch timer).
>>
>>> For some SMP soces, for example, they can use arch timer, but we have
>>> to register
>>> a clock event device for broadcast because we lose arch timer if core is down.
>>> For this coutner, we have to set IRQ affinity to ALL cores.
>>
>> In that case the arch timer will register (which can have a higher
>> rating than this timer), and this timers can be automatically repurposed
>> as the broadcast source.
>>
>> I do not see the necessity of this property.
>>
> It is true that arch timer is first priority.
> There are something need to be considered
> 1. we registered NR_CPU timers to acts as arch timer. We set the
> cpu_mask of be cpu_mask(cpu_id), not
>     cpu_mask(ALL_CPUes). We also bind the interrupt to the dedicated
> CPU. For example, if timer 0 counter 0
>     is used by CPU0 to replace arch timer. the counter's IRQ only
> route to the CPU0.
>     If we have arch timers, it means that there are NR_CPU timers, i
> do not think they can be acts as broadcast timer
> 2. As i said, some coutners share interrupts. We can not allocated
> this kind of counter as per cpu clock event device.
>     For exmaple, timer 0 coutner1 and counter 2 share same interrupt.
> If they are used by CPU0 and CPU1, it means that
>     same interrupt will be bind to two CPUes. each timer interrupt
> happens, a power-down core need wake up to check the
>     interrupt does not targets at it.
>
> So how to use these counter in fact depends on the SOCes design.
> That why i need pass this kind of information to timer driver.
>
>>> >> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
>>> >> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
>>> >> +  Only valid for "marvell,timer-counter-clkevt".
>>> >
>>> > Likewise this does not sound like a hardware property.
>>>
>>> For a broadcast timer, there is feature "CLOCK_EVT_FEAT_DYNIRQ".
>>> This feature is a clock event device's fearture. It means that the
>>> clock event device's irq can
>>> be set to any core.
>>> In fact it does not for all SOCes that the interrupt of the counter
>>> can be set to any core.
>>
>> The naming and use of this property still seems incredibly
>> Linux-specific.
>>
>> Why can some of the counter interrupts not be routed to an arbitrary
>> CPU?
>>
> The reason is couple idle.
> The couple idle implementation implies that CPU0 will be first core wake up.
> So, we can not have CLOCK_EVT_FEAT_DYNIRQ because it may make CPU1 to
> be first core wake up.
>
>> Cheers,
>> Mark.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/4] clocksource: mmp: add mmp timer driver
  2014-03-27  9:59           ` Mark Rutland
@ 2014-03-27 12:13               ` Chao Xie
  -1 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2014-03-27 12:13 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Chao Xie, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	haojian.zhuang-Re5JQEeQqe8AvxtiuMwx3w

>> >>
>> >> +Each timer have multiple counters, so the timer DT need include counter's
>> >> +description.
>> >> +
>> >> +1. Timer
>> >> +
>> >>  Required properties:
>> >> -- compatible : Should be "mrvl,mmp-timer".
>> >> +- compatible : Should be "marvell,mmp-timer".
>> >>  - reg : Address and length of the register set of timer controller.
>> >> -- interrupts : Should be the interrupt number.
>> >> +- marvell,timer-id : The index of the timer. The timer controller may
>> >> +  include several timers.
>> >
>> > Is this a single entry or a list?
>>
>> Generally, the may be 2/3/4 timers, and each timers will have at most
>> 3 coounters.
>> Each counters will have 3 matches. Each counter may have its own
>> interrupts, or share same
>> interrupts(counter1 has one interrupts, counter2 and 3 share one). It depends on
>> how SOC connets the signals.
>>
>> The DT will be
>> timer 0 {
>>      ...
>>      ...
>>      counter0 {
>>      };
>>      counter2 {
>>      };
>> }
>
> I'm somewhat confused here.
>
> The top level node appears to be a timer controller (per the reg
> property description), but the marvell,timer-id property describes an
> index within the timer controller. This does not seem to make sense.
>
> How do you describe multiple timers within a given timer contoller? Is
> the timer-id a list? Or do you describe the controller repeatedly?
>
Thanks for your reply.

the driver only take care of one timer node, if we have 3 timer node defined.
It means that the driver will be probed 3 times.

Each timer is a controller, and one timer has 3 counters. each counter can be
set to be free running or period mode.

marvell,timer-id may not be needed, but the marvell,counter-id need to
be passed.
The timer driver depends on the id to distinguish the register or bit
in the register.

>>
>> >
>> >> +- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
>> >> +  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
>> >> +  SOCes use this timer controller, fast clock may not be same.
>> >> +- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
>> >> +  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
>> >> +  to calculated delay loops for clock operations.
>> >
>> > Wouldn't these be better described as clock inputs?
>>
>> "apb" frequency is only used to calculated the delay loop.
>> I do not want to fix the delay loop in the driver.
>> So pass it in DT directly will make the things easier to handle.
>>
>> "fastclk" is a clock input. So waht do you mean "described as clock inputs"?
>
> Using the clock bindings:
>
>         clocks = <&apb_plck>, <&clock_controller 0 3>;
>         clock-names = "apb_pclk", "fastclk";
>
> The driver can then query the frequencies, enable/disable the clocks as
> required, etc.
>
It is the final choice, but it depends on the clock DT support.
I just begin the clock DT support for mach-mmp. They are still in design phase.
Clock framework is more complicated than the timer controller.

>>
>> >
>> >> +
>> >> +Optional properties:
>> >> +- marvell,timer-has-crsr : This timer has CRSR register.
>> >> +- marvell,timer-has-shadow : This timer has shadow register.
>> >> +
>> >> +2. Counter
>> >> +
>> >> +Required properties:
>> >> +- compatible : It can be
>> >> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
>> >> +                                       device.
>> >> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
>> >> +      "marvell,timer-counter-delay" : The counter is used for delay timer.
>> >
>> > These are _not_ hardware properties. Why can the driver not choose how
>> > to use each of the counter sub-blocks?
>> >
>> > How would blocks with each of these strings actually differ?
>> >
>>
>> SOCes may have 2 or 3 or 4 timers, some timers may be used by CP not AP but
>> AP can still access it.
>> There may be UP SOC, or SMP SOC. So there are may be some general usage
>> 1. UP SOC: need one clocksource, need one clock event device
>> 2. SMP SOC: need one clocksource, need one broadcast clock event device because
>>    arch timer will be shutdown when core is down
>> 3. SMP SOC: it has bug with arch timer. So need one clock source,
>> NR_CPUs clock event
>>     device to replace the arch timer. There are may be 2 cores or 4 cores.
>
> Ok, but in all of these cases the blocks that you are giving different
> compatible strings are identical. The only difference is how you expect
> Linux to use them.
>
> Why can you not just describe the set of timers available to Linux and
> allow it to allocate them as required?
>


So how can I make Linux to allocate them as required?


>> Above requeimetns are also hardware requriments from SOCes.
>>
>> The driver will be used for many SOCes, we can not to fix the counter
>> usage in driver.
>
> I'm arguing for dynamically deciding how to use the timers, not assuming
> one of the three cases you've laid out above, and not describing a
> possible software use of the counters in the DT.
>

I am a little confused.

So what is your suggestions?
we have the below usage
1. UP SOC, we need clock source, clock event device
2. SMP SOC with arch timer, we need clock source, broadcast clock event device
3. SMP SOC without arch timer, we need clock source, NR_CPU clock
event device and delay timer.


>> >> +- marvell,timer-counter-id : The counter index in this timer.
>> >
>> > Perhaps use a reg for this?
>> >
>>
>> The counter does not have its own register space. They are belongs to a timer.
>> Counter does not need to map the regsiter spance, it is done by timer.
>> For example, for counter enable, all coutners share same register. CER
>> For interrupt enable, each counter has its own register. IERx
>>
>> So if we use like "reg = <IERx offset>", i do not see any benefit. while
>> direct setting "marvell,timer-counter-id" seems make the DT more clear.
>
> The reg doesn't have to be a register space -- it's often used simply as
> a form of address/identifier without a size (consider SPI's use of the
> reg property). Here the index is essentially an address/identifier for
> the counter.
>
> Given that the each timer also seems to have a unique ID perhaps using
> the reg is not the best strategy here. I was simply raising the
> possiblity.
>
>>
>> >>
>> >> -Example:
>> >> -       timer0: timer@d4014000 {
>> >> -               compatible = "mrvl,mmp-timer";
>> >> -               reg = <0xd4014000 0x100>;
>> >> +Optional properties:
>> >> +- interrupts : The counters may have different IRQs or share same IRQs.
>> >> +  Only valid for "marvell,timer-counter-clkevt".
>> >
>> > Describe what the interrupt is logically.
>> >
>> > What is the interrupt logically?
>> >
>>
>> The interrupt topolog is designed by SOCes.
>> Each counter has one interrupt, but SOCes may connects some coutners' interrupt
>> into one.
>> In timer driver, we do not care about it. We depeneds on IRQ chip to handle it.
>
> How the interrupt is wired and whether it is shared is not important
> from the view of the binding. What is important is which interrupt from
> the timer is being described. If it has a given name, that should be
> mentioned. Otherwise a simple description will suffice.
>
> The presence of external interrupt muxing doesn't seem to be important
> here to the binding -- in that case an OS could request shared
> interrupts or decide to only make use of a subset of interrupts. That
> seems to be outside of the scope of the binding.
>

I see. I think a simple description is enough. i do not need mention the shared
interrupts here because the timer driver does not care about it.

>>
>> >> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
>> >> +  "marvell,timer-counter-clkevt".
>> >
>> > How is the counter bound to a particular CPU? Is there really a
>> > relationship in hardware between a given CPU and counter?
>> >
>>
>> For some SMP SOCes, for example, marvell's pxa988/pxa1088/pxa1L88
>> They have bug in arch timer. So we make each CPU to have one dedicated
>> counter to replace the arch timer.
>
> Is there any real topological relationship between CPUs and counters, or
> is this arbitrary? If the latter, why can the kernel not decide how to
> allocate timers to CPUs?
>

All the timers can be seen by every CPUes.
For arch timer, each CPU see its own instance, while for the mmp
timers, each CPU
can see all of them.
So there are some problems
1. The number of the timer is not fixed. It can be 2, 3, 4
2. Some timers can be seen by CP, and CP will use these timers. Which
timer can be seen
    by CP is defined by SOC design
3. Some counter share one interrupt, these counters can not be
allocated for per cpu timer to replace
    arch timer.
So which counter allocated is limited by SOC design.

So if do not have above limitation, can you tell me that how can i let
kernel to decide allocation?
Thanks.

>>
>> >> +- "marvell,timer-counter-rating" : The rating when register clock event device
>> >> +  or clock source. Only valid for "marvell,timer-counter-clkevt" and
>> >> +  "marvell,timer-counter-clksrc".
>> >
>> > What does this mean? This seems like another leak of Linux internals.
>>
>> It is the rating when register clock source and clock event.
>> I think i can fix it in the driver.
>
> Ok. This is a Linux interenal detail that I do not think should be in
> the DT.
>
>>
>> >
>> >> +- marvell,timer-counter-broadcast : When this counter acts as clock event
>> >> +  device. It is broadcast clock event device.
>> >> +  Only valid for "marvell,timer-counter-clkevt".
>> >
>> > This does not seem like a hardware property. Why can Linux not decide
>> > which counter (if any) to use as the broadcast source?
>> >
>>
>> For some SMP soces, for example, marvell's pxa988/pxa1088/pxa1L88
>> They have bug in arch timer. So we make each CPU to have one dedicated
>> counter to replace the arch timer. So for these SOCes, broadcast timer is not
>> needed. For these counters, we have to set IRQ affinity to the specific core.
>
> That's fine, we can do that in the absence of both this property and
> another timer (the arch timer).
>
>> For some SMP soces, for example, they can use arch timer, but we have
>> to register
>> a clock event device for broadcast because we lose arch timer if core is down.
>> For this coutner, we have to set IRQ affinity to ALL cores.
>
> In that case the arch timer will register (which can have a higher
> rating than this timer), and this timers can be automatically repurposed
> as the broadcast source.
>
> I do not see the necessity of this property.
>
It is true that arch timer is first priority.
There are something need to be considered
1. we registered NR_CPU timers to acts as arch timer. We set the
cpu_mask of be cpu_mask(cpu_id), not
    cpu_mask(ALL_CPUes). We also bind the interrupt to the dedicated
CPU. For example, if timer 0 counter 0
    is used by CPU0 to replace arch timer. the counter's IRQ only
route to the CPU0.
    If we have arch timers, it means that there are NR_CPU timers, i
do not think they can be acts as broadcast timer
2. As i said, some coutners share interrupts. We can not allocated
this kind of counter as per cpu clock event device.
    For exmaple, timer 0 coutner1 and counter 2 share same interrupt.
If they are used by CPU0 and CPU1, it means that
    same interrupt will be bind to two CPUes. each timer interrupt
happens, a power-down core need wake up to check the
    interrupt does not targets at it.

So how to use these counter in fact depends on the SOCes design.
That why i need pass this kind of information to timer driver.

>> >> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
>> >> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
>> >> +  Only valid for "marvell,timer-counter-clkevt".
>> >
>> > Likewise this does not sound like a hardware property.
>>
>> For a broadcast timer, there is feature "CLOCK_EVT_FEAT_DYNIRQ".
>> This feature is a clock event device's fearture. It means that the
>> clock event device's irq can
>> be set to any core.
>> In fact it does not for all SOCes that the interrupt of the counter
>> can be set to any core.
>
> The naming and use of this property still seems incredibly
> Linux-specific.
>
> Why can some of the counter interrupts not be routed to an arbitrary
> CPU?
>
The reason is couple idle.
The couple idle implementation implies that CPU0 will be first core wake up.
So, we can not have CLOCK_EVT_FEAT_DYNIRQ because it may make CPU1 to
be first core wake up.

> Cheers,
> Mark.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 1/4] clocksource: mmp: add mmp timer driver
@ 2014-03-27 12:13               ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2014-03-27 12:13 UTC (permalink / raw)
  To: linux-arm-kernel

>> >>
>> >> +Each timer have multiple counters, so the timer DT need include counter's
>> >> +description.
>> >> +
>> >> +1. Timer
>> >> +
>> >>  Required properties:
>> >> -- compatible : Should be "mrvl,mmp-timer".
>> >> +- compatible : Should be "marvell,mmp-timer".
>> >>  - reg : Address and length of the register set of timer controller.
>> >> -- interrupts : Should be the interrupt number.
>> >> +- marvell,timer-id : The index of the timer. The timer controller may
>> >> +  include several timers.
>> >
>> > Is this a single entry or a list?
>>
>> Generally, the may be 2/3/4 timers, and each timers will have at most
>> 3 coounters.
>> Each counters will have 3 matches. Each counter may have its own
>> interrupts, or share same
>> interrupts(counter1 has one interrupts, counter2 and 3 share one). It depends on
>> how SOC connets the signals.
>>
>> The DT will be
>> timer 0 {
>>      ...
>>      ...
>>      counter0 {
>>      };
>>      counter2 {
>>      };
>> }
>
> I'm somewhat confused here.
>
> The top level node appears to be a timer controller (per the reg
> property description), but the marvell,timer-id property describes an
> index within the timer controller. This does not seem to make sense.
>
> How do you describe multiple timers within a given timer contoller? Is
> the timer-id a list? Or do you describe the controller repeatedly?
>
Thanks for your reply.

the driver only take care of one timer node, if we have 3 timer node defined.
It means that the driver will be probed 3 times.

Each timer is a controller, and one timer has 3 counters. each counter can be
set to be free running or period mode.

marvell,timer-id may not be needed, but the marvell,counter-id need to
be passed.
The timer driver depends on the id to distinguish the register or bit
in the register.

>>
>> >
>> >> +- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
>> >> +  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
>> >> +  SOCes use this timer controller, fast clock may not be same.
>> >> +- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
>> >> +  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
>> >> +  to calculated delay loops for clock operations.
>> >
>> > Wouldn't these be better described as clock inputs?
>>
>> "apb" frequency is only used to calculated the delay loop.
>> I do not want to fix the delay loop in the driver.
>> So pass it in DT directly will make the things easier to handle.
>>
>> "fastclk" is a clock input. So waht do you mean "described as clock inputs"?
>
> Using the clock bindings:
>
>         clocks = <&apb_plck>, <&clock_controller 0 3>;
>         clock-names = "apb_pclk", "fastclk";
>
> The driver can then query the frequencies, enable/disable the clocks as
> required, etc.
>
It is the final choice, but it depends on the clock DT support.
I just begin the clock DT support for mach-mmp. They are still in design phase.
Clock framework is more complicated than the timer controller.

>>
>> >
>> >> +
>> >> +Optional properties:
>> >> +- marvell,timer-has-crsr : This timer has CRSR register.
>> >> +- marvell,timer-has-shadow : This timer has shadow register.
>> >> +
>> >> +2. Counter
>> >> +
>> >> +Required properties:
>> >> +- compatible : It can be
>> >> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
>> >> +                                       device.
>> >> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
>> >> +      "marvell,timer-counter-delay" : The counter is used for delay timer.
>> >
>> > These are _not_ hardware properties. Why can the driver not choose how
>> > to use each of the counter sub-blocks?
>> >
>> > How would blocks with each of these strings actually differ?
>> >
>>
>> SOCes may have 2 or 3 or 4 timers, some timers may be used by CP not AP but
>> AP can still access it.
>> There may be UP SOC, or SMP SOC. So there are may be some general usage
>> 1. UP SOC: need one clocksource, need one clock event device
>> 2. SMP SOC: need one clocksource, need one broadcast clock event device because
>>    arch timer will be shutdown when core is down
>> 3. SMP SOC: it has bug with arch timer. So need one clock source,
>> NR_CPUs clock event
>>     device to replace the arch timer. There are may be 2 cores or 4 cores.
>
> Ok, but in all of these cases the blocks that you are giving different
> compatible strings are identical. The only difference is how you expect
> Linux to use them.
>
> Why can you not just describe the set of timers available to Linux and
> allow it to allocate them as required?
>


So how can I make Linux to allocate them as required?


>> Above requeimetns are also hardware requriments from SOCes.
>>
>> The driver will be used for many SOCes, we can not to fix the counter
>> usage in driver.
>
> I'm arguing for dynamically deciding how to use the timers, not assuming
> one of the three cases you've laid out above, and not describing a
> possible software use of the counters in the DT.
>

I am a little confused.

So what is your suggestions?
we have the below usage
1. UP SOC, we need clock source, clock event device
2. SMP SOC with arch timer, we need clock source, broadcast clock event device
3. SMP SOC without arch timer, we need clock source, NR_CPU clock
event device and delay timer.


>> >> +- marvell,timer-counter-id : The counter index in this timer.
>> >
>> > Perhaps use a reg for this?
>> >
>>
>> The counter does not have its own register space. They are belongs to a timer.
>> Counter does not need to map the regsiter spance, it is done by timer.
>> For example, for counter enable, all coutners share same register. CER
>> For interrupt enable, each counter has its own register. IERx
>>
>> So if we use like "reg = <IERx offset>", i do not see any benefit. while
>> direct setting "marvell,timer-counter-id" seems make the DT more clear.
>
> The reg doesn't have to be a register space -- it's often used simply as
> a form of address/identifier without a size (consider SPI's use of the
> reg property). Here the index is essentially an address/identifier for
> the counter.
>
> Given that the each timer also seems to have a unique ID perhaps using
> the reg is not the best strategy here. I was simply raising the
> possiblity.
>
>>
>> >>
>> >> -Example:
>> >> -       timer0: timer at d4014000 {
>> >> -               compatible = "mrvl,mmp-timer";
>> >> -               reg = <0xd4014000 0x100>;
>> >> +Optional properties:
>> >> +- interrupts : The counters may have different IRQs or share same IRQs.
>> >> +  Only valid for "marvell,timer-counter-clkevt".
>> >
>> > Describe what the interrupt is logically.
>> >
>> > What is the interrupt logically?
>> >
>>
>> The interrupt topolog is designed by SOCes.
>> Each counter has one interrupt, but SOCes may connects some coutners' interrupt
>> into one.
>> In timer driver, we do not care about it. We depeneds on IRQ chip to handle it.
>
> How the interrupt is wired and whether it is shared is not important
> from the view of the binding. What is important is which interrupt from
> the timer is being described. If it has a given name, that should be
> mentioned. Otherwise a simple description will suffice.
>
> The presence of external interrupt muxing doesn't seem to be important
> here to the binding -- in that case an OS could request shared
> interrupts or decide to only make use of a subset of interrupts. That
> seems to be outside of the scope of the binding.
>

I see. I think a simple description is enough. i do not need mention the shared
interrupts here because the timer driver does not care about it.

>>
>> >> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
>> >> +  "marvell,timer-counter-clkevt".
>> >
>> > How is the counter bound to a particular CPU? Is there really a
>> > relationship in hardware between a given CPU and counter?
>> >
>>
>> For some SMP SOCes, for example, marvell's pxa988/pxa1088/pxa1L88
>> They have bug in arch timer. So we make each CPU to have one dedicated
>> counter to replace the arch timer.
>
> Is there any real topological relationship between CPUs and counters, or
> is this arbitrary? If the latter, why can the kernel not decide how to
> allocate timers to CPUs?
>

All the timers can be seen by every CPUes.
For arch timer, each CPU see its own instance, while for the mmp
timers, each CPU
can see all of them.
So there are some problems
1. The number of the timer is not fixed. It can be 2, 3, 4
2. Some timers can be seen by CP, and CP will use these timers. Which
timer can be seen
    by CP is defined by SOC design
3. Some counter share one interrupt, these counters can not be
allocated for per cpu timer to replace
    arch timer.
So which counter allocated is limited by SOC design.

So if do not have above limitation, can you tell me that how can i let
kernel to decide allocation?
Thanks.

>>
>> >> +- "marvell,timer-counter-rating" : The rating when register clock event device
>> >> +  or clock source. Only valid for "marvell,timer-counter-clkevt" and
>> >> +  "marvell,timer-counter-clksrc".
>> >
>> > What does this mean? This seems like another leak of Linux internals.
>>
>> It is the rating when register clock source and clock event.
>> I think i can fix it in the driver.
>
> Ok. This is a Linux interenal detail that I do not think should be in
> the DT.
>
>>
>> >
>> >> +- marvell,timer-counter-broadcast : When this counter acts as clock event
>> >> +  device. It is broadcast clock event device.
>> >> +  Only valid for "marvell,timer-counter-clkevt".
>> >
>> > This does not seem like a hardware property. Why can Linux not decide
>> > which counter (if any) to use as the broadcast source?
>> >
>>
>> For some SMP soces, for example, marvell's pxa988/pxa1088/pxa1L88
>> They have bug in arch timer. So we make each CPU to have one dedicated
>> counter to replace the arch timer. So for these SOCes, broadcast timer is not
>> needed. For these counters, we have to set IRQ affinity to the specific core.
>
> That's fine, we can do that in the absence of both this property and
> another timer (the arch timer).
>
>> For some SMP soces, for example, they can use arch timer, but we have
>> to register
>> a clock event device for broadcast because we lose arch timer if core is down.
>> For this coutner, we have to set IRQ affinity to ALL cores.
>
> In that case the arch timer will register (which can have a higher
> rating than this timer), and this timers can be automatically repurposed
> as the broadcast source.
>
> I do not see the necessity of this property.
>
It is true that arch timer is first priority.
There are something need to be considered
1. we registered NR_CPU timers to acts as arch timer. We set the
cpu_mask of be cpu_mask(cpu_id), not
    cpu_mask(ALL_CPUes). We also bind the interrupt to the dedicated
CPU. For example, if timer 0 counter 0
    is used by CPU0 to replace arch timer. the counter's IRQ only
route to the CPU0.
    If we have arch timers, it means that there are NR_CPU timers, i
do not think they can be acts as broadcast timer
2. As i said, some coutners share interrupts. We can not allocated
this kind of counter as per cpu clock event device.
    For exmaple, timer 0 coutner1 and counter 2 share same interrupt.
If they are used by CPU0 and CPU1, it means that
    same interrupt will be bind to two CPUes. each timer interrupt
happens, a power-down core need wake up to check the
    interrupt does not targets at it.

So how to use these counter in fact depends on the SOCes design.
That why i need pass this kind of information to timer driver.

>> >> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
>> >> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
>> >> +  Only valid for "marvell,timer-counter-clkevt".
>> >
>> > Likewise this does not sound like a hardware property.
>>
>> For a broadcast timer, there is feature "CLOCK_EVT_FEAT_DYNIRQ".
>> This feature is a clock event device's fearture. It means that the
>> clock event device's irq can
>> be set to any core.
>> In fact it does not for all SOCes that the interrupt of the counter
>> can be set to any core.
>
> The naming and use of this property still seems incredibly
> Linux-specific.
>
> Why can some of the counter interrupts not be routed to an arbitrary
> CPU?
>
The reason is couple idle.
The couple idle implementation implies that CPU0 will be first core wake up.
So, we can not have CLOCK_EVT_FEAT_DYNIRQ because it may make CPU1 to
be first core wake up.

> Cheers,
> Mark.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/4] clocksource: mmp: add mmp timer driver
  2014-03-27  1:27         ` Chao Xie
@ 2014-03-27  9:59           ` Mark Rutland
  -1 siblings, 0 replies; 25+ messages in thread
From: Mark Rutland @ 2014-03-27  9:59 UTC (permalink / raw)
  To: Chao Xie; +Cc: devicetree, haojian.zhuang, linux-arm-kernel, Chao Xie

On Thu, Mar 27, 2014 at 01:27:33AM +0000, Chao Xie wrote:
> On Wed, Mar 26, 2014 at 6:41 PM, Mark Rutland <mark.rutland@arm.com> wrote:
> > On Wed, Mar 26, 2014 at 04:52:40AM +0000, Chao Xie wrote:
> >> From: Chao Xie <chao.xie@marvell.com>
> >>
> >> MMP timer is attached to APB bus, It has the following
> >> limitation.
> >> 1. When get count of timer counter, it need some delay
> >>    to get a stable count.
> >> 2. When set match register, it need disable the counter
> >>    first, and enable it after set the match register.
> >>    The disabling need wait for 2 clock cycle to take
> >>    effect.
> >>
> >> To improve above #1, shadow register for count is added.
> >> To improve above #2, CRSR register is added for quick updating.
> >>
> >> So there are three types of timer.
> >> timer1: old timer.
> >> timer2: old timer with shadow register.
> >> timer3: old timer with shadow and CRSR register.
> >>
> >> This timer driver will be used for many SOCes.
> >> 1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
> >> 2. pxa1L88, pxa1U88 have timer1 and timer3.
> >> 3. pxa1928 has timer 2.
> >>
> >> The driver supports DT and non-DT initialization.
> >> The driver supports UP/SMP SOCes and 64 bit core.
> >>
> >> Signed-off-by: Chao Xie <chao.xie@marvell.com>
> >> ---
> >>  .../devicetree/bindings/arm/mrvl/timer.txt         |  73 +-
> >>  drivers/clocksource/Makefile                       |   1 +
> >>  drivers/clocksource/timer-mmp.c                    | 866 +++++++++++++++++++++
> >>  3 files changed, 934 insertions(+), 6 deletions(-)
> >>  create mode 100644 drivers/clocksource/timer-mmp.c
> >>
> >> diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> >> index 9a6e251..b9af4bf 100644
> >> --- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> >> +++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> >> @@ -1,13 +1,74 @@
> >>  * Marvell MMP Timer controller
> >>
> >> +Each timer have multiple counters, so the timer DT need include counter's
> >> +description.
> >> +
> >> +1. Timer
> >> +
> >>  Required properties:
> >> -- compatible : Should be "mrvl,mmp-timer".
> >> +- compatible : Should be "marvell,mmp-timer".
> >>  - reg : Address and length of the register set of timer controller.
> >> -- interrupts : Should be the interrupt number.
> >> +- marvell,timer-id : The index of the timer. The timer controller may
> >> +  include several timers.
> >
> > Is this a single entry or a list?
> 
> Generally, the may be 2/3/4 timers, and each timers will have at most
> 3 coounters.
> Each counters will have 3 matches. Each counter may have its own
> interrupts, or share same
> interrupts(counter1 has one interrupts, counter2 and 3 share one). It depends on
> how SOC connets the signals.
> 
> The DT will be
> timer 0 {
>      ...
>      ...
>      counter0 {
>      };
>      counter2 {
>      };
> }

I'm somewhat confused here.

The top level node appears to be a timer controller (per the reg
property description), but the marvell,timer-id property describes an
index within the timer controller. This does not seem to make sense.

How do you describe multiple timers within a given timer contoller? Is
the timer-id a list? Or do you describe the controller repeatedly?

> 
> >
> >> +- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
> >> +  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
> >> +  SOCes use this timer controller, fast clock may not be same.
> >> +- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
> >> +  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
> >> +  to calculated delay loops for clock operations.
> >
> > Wouldn't these be better described as clock inputs?
> 
> "apb" frequency is only used to calculated the delay loop.
> I do not want to fix the delay loop in the driver.
> So pass it in DT directly will make the things easier to handle.
> 
> "fastclk" is a clock input. So waht do you mean "described as clock inputs"?

Using the clock bindings:

	clocks = <&apb_plck>, <&clock_controller 0 3>;
	clock-names = "apb_pclk", "fastclk";

The driver can then query the frequencies, enable/disable the clocks as
required, etc.

> 
> >
> >> +
> >> +Optional properties:
> >> +- marvell,timer-has-crsr : This timer has CRSR register.
> >> +- marvell,timer-has-shadow : This timer has shadow register.
> >> +
> >> +2. Counter
> >> +
> >> +Required properties:
> >> +- compatible : It can be
> >> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
> >> +                                       device.
> >> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
> >> +      "marvell,timer-counter-delay" : The counter is used for delay timer.
> >
> > These are _not_ hardware properties. Why can the driver not choose how
> > to use each of the counter sub-blocks?
> >
> > How would blocks with each of these strings actually differ?
> >
> 
> SOCes may have 2 or 3 or 4 timers, some timers may be used by CP not AP but
> AP can still access it.
> There may be UP SOC, or SMP SOC. So there are may be some general usage
> 1. UP SOC: need one clocksource, need one clock event device
> 2. SMP SOC: need one clocksource, need one broadcast clock event device because
>    arch timer will be shutdown when core is down
> 3. SMP SOC: it has bug with arch timer. So need one clock source,
> NR_CPUs clock event
>     device to replace the arch timer. There are may be 2 cores or 4 cores.

Ok, but in all of these cases the blocks that you are giving different
compatible strings are identical. The only difference is how you expect
Linux to use them.

Why can you not just describe the set of timers available to Linux and
allow it to allocate them as required?

> Above requeimetns are also hardware requriments from SOCes.
> 
> The driver will be used for many SOCes, we can not to fix the counter
> usage in driver.

I'm arguing for dynamically deciding how to use the timers, not assuming
one of the three cases you've laid out above, and not describing a
possible software use of the counters in the DT.

> >> +- marvell,timer-counter-id : The counter index in this timer.
> >
> > Perhaps use a reg for this?
> >
> 
> The counter does not have its own register space. They are belongs to a timer.
> Counter does not need to map the regsiter spance, it is done by timer.
> For example, for counter enable, all coutners share same register. CER
> For interrupt enable, each counter has its own register. IERx
> 
> So if we use like "reg = <IERx offset>", i do not see any benefit. while
> direct setting "marvell,timer-counter-id" seems make the DT more clear.

The reg doesn't have to be a register space -- it's often used simply as
a form of address/identifier without a size (consider SPI's use of the
reg property). Here the index is essentially an address/identifier for
the counter.

Given that the each timer also seems to have a unique ID perhaps using
the reg is not the best strategy here. I was simply raising the
possiblity.

> 
> >>
> >> -Example:
> >> -       timer0: timer@d4014000 {
> >> -               compatible = "mrvl,mmp-timer";
> >> -               reg = <0xd4014000 0x100>;
> >> +Optional properties:
> >> +- interrupts : The counters may have different IRQs or share same IRQs.
> >> +  Only valid for "marvell,timer-counter-clkevt".
> >
> > Describe what the interrupt is logically.
> >
> > What is the interrupt logically?
> >
> 
> The interrupt topolog is designed by SOCes.
> Each counter has one interrupt, but SOCes may connects some coutners' interrupt
> into one.
> In timer driver, we do not care about it. We depeneds on IRQ chip to handle it.

How the interrupt is wired and whether it is shared is not important
from the view of the binding. What is important is which interrupt from
the timer is being described. If it has a given name, that should be
mentioned. Otherwise a simple description will suffice.

The presence of external interrupt muxing doesn't seem to be important
here to the binding -- in that case an OS could request shared
interrupts or decide to only make use of a subset of interrupts. That
seems to be outside of the scope of the binding.

> 
> >> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
> >> +  "marvell,timer-counter-clkevt".
> >
> > How is the counter bound to a particular CPU? Is there really a
> > relationship in hardware between a given CPU and counter?
> >
> 
> For some SMP SOCes, for example, marvell's pxa988/pxa1088/pxa1L88
> They have bug in arch timer. So we make each CPU to have one dedicated
> counter to replace the arch timer.

Is there any real topological relationship between CPUs and counters, or
is this arbitrary? If the latter, why can the kernel not decide how to
allocate timers to CPUs?

> 
> >> +- "marvell,timer-counter-rating" : The rating when register clock event device
> >> +  or clock source. Only valid for "marvell,timer-counter-clkevt" and
> >> +  "marvell,timer-counter-clksrc".
> >
> > What does this mean? This seems like another leak of Linux internals.
> 
> It is the rating when register clock source and clock event.
> I think i can fix it in the driver.

Ok. This is a Linux interenal detail that I do not think should be in
the DT.

> 
> >
> >> +- marvell,timer-counter-broadcast : When this counter acts as clock event
> >> +  device. It is broadcast clock event device.
> >> +  Only valid for "marvell,timer-counter-clkevt".
> >
> > This does not seem like a hardware property. Why can Linux not decide
> > which counter (if any) to use as the broadcast source?
> >
> 
> For some SMP soces, for example, marvell's pxa988/pxa1088/pxa1L88
> They have bug in arch timer. So we make each CPU to have one dedicated
> counter to replace the arch timer. So for these SOCes, broadcast timer is not
> needed. For these counters, we have to set IRQ affinity to the specific core.

That's fine, we can do that in the absence of both this property and
another timer (the arch timer).

> For some SMP soces, for example, they can use arch timer, but we have
> to register
> a clock event device for broadcast because we lose arch timer if core is down.
> For this coutner, we have to set IRQ affinity to ALL cores.

In that case the arch timer will register (which can have a higher
rating than this timer), and this timers can be automatically repurposed
as the broadcast source.

I do not see the necessity of this property.

> >> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
> >> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
> >> +  Only valid for "marvell,timer-counter-clkevt".
> >
> > Likewise this does not sound like a hardware property.
> 
> For a broadcast timer, there is feature "CLOCK_EVT_FEAT_DYNIRQ".
> This feature is a clock event device's fearture. It means that the
> clock event device's irq can
> be set to any core.
> In fact it does not for all SOCes that the interrupt of the counter
> can be set to any core.

The naming and use of this property still seems incredibly
Linux-specific.

Why can some of the counter interrupts not be routed to an arbitrary
CPU?

Cheers,
Mark.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 1/4] clocksource: mmp: add mmp timer driver
@ 2014-03-27  9:59           ` Mark Rutland
  0 siblings, 0 replies; 25+ messages in thread
From: Mark Rutland @ 2014-03-27  9:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Mar 27, 2014 at 01:27:33AM +0000, Chao Xie wrote:
> On Wed, Mar 26, 2014 at 6:41 PM, Mark Rutland <mark.rutland@arm.com> wrote:
> > On Wed, Mar 26, 2014 at 04:52:40AM +0000, Chao Xie wrote:
> >> From: Chao Xie <chao.xie@marvell.com>
> >>
> >> MMP timer is attached to APB bus, It has the following
> >> limitation.
> >> 1. When get count of timer counter, it need some delay
> >>    to get a stable count.
> >> 2. When set match register, it need disable the counter
> >>    first, and enable it after set the match register.
> >>    The disabling need wait for 2 clock cycle to take
> >>    effect.
> >>
> >> To improve above #1, shadow register for count is added.
> >> To improve above #2, CRSR register is added for quick updating.
> >>
> >> So there are three types of timer.
> >> timer1: old timer.
> >> timer2: old timer with shadow register.
> >> timer3: old timer with shadow and CRSR register.
> >>
> >> This timer driver will be used for many SOCes.
> >> 1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
> >> 2. pxa1L88, pxa1U88 have timer1 and timer3.
> >> 3. pxa1928 has timer 2.
> >>
> >> The driver supports DT and non-DT initialization.
> >> The driver supports UP/SMP SOCes and 64 bit core.
> >>
> >> Signed-off-by: Chao Xie <chao.xie@marvell.com>
> >> ---
> >>  .../devicetree/bindings/arm/mrvl/timer.txt         |  73 +-
> >>  drivers/clocksource/Makefile                       |   1 +
> >>  drivers/clocksource/timer-mmp.c                    | 866 +++++++++++++++++++++
> >>  3 files changed, 934 insertions(+), 6 deletions(-)
> >>  create mode 100644 drivers/clocksource/timer-mmp.c
> >>
> >> diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> >> index 9a6e251..b9af4bf 100644
> >> --- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> >> +++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> >> @@ -1,13 +1,74 @@
> >>  * Marvell MMP Timer controller
> >>
> >> +Each timer have multiple counters, so the timer DT need include counter's
> >> +description.
> >> +
> >> +1. Timer
> >> +
> >>  Required properties:
> >> -- compatible : Should be "mrvl,mmp-timer".
> >> +- compatible : Should be "marvell,mmp-timer".
> >>  - reg : Address and length of the register set of timer controller.
> >> -- interrupts : Should be the interrupt number.
> >> +- marvell,timer-id : The index of the timer. The timer controller may
> >> +  include several timers.
> >
> > Is this a single entry or a list?
> 
> Generally, the may be 2/3/4 timers, and each timers will have at most
> 3 coounters.
> Each counters will have 3 matches. Each counter may have its own
> interrupts, or share same
> interrupts(counter1 has one interrupts, counter2 and 3 share one). It depends on
> how SOC connets the signals.
> 
> The DT will be
> timer 0 {
>      ...
>      ...
>      counter0 {
>      };
>      counter2 {
>      };
> }

I'm somewhat confused here.

The top level node appears to be a timer controller (per the reg
property description), but the marvell,timer-id property describes an
index within the timer controller. This does not seem to make sense.

How do you describe multiple timers within a given timer contoller? Is
the timer-id a list? Or do you describe the controller repeatedly?

> 
> >
> >> +- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
> >> +  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
> >> +  SOCes use this timer controller, fast clock may not be same.
> >> +- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
> >> +  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
> >> +  to calculated delay loops for clock operations.
> >
> > Wouldn't these be better described as clock inputs?
> 
> "apb" frequency is only used to calculated the delay loop.
> I do not want to fix the delay loop in the driver.
> So pass it in DT directly will make the things easier to handle.
> 
> "fastclk" is a clock input. So waht do you mean "described as clock inputs"?

Using the clock bindings:

	clocks = <&apb_plck>, <&clock_controller 0 3>;
	clock-names = "apb_pclk", "fastclk";

The driver can then query the frequencies, enable/disable the clocks as
required, etc.

> 
> >
> >> +
> >> +Optional properties:
> >> +- marvell,timer-has-crsr : This timer has CRSR register.
> >> +- marvell,timer-has-shadow : This timer has shadow register.
> >> +
> >> +2. Counter
> >> +
> >> +Required properties:
> >> +- compatible : It can be
> >> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
> >> +                                       device.
> >> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
> >> +      "marvell,timer-counter-delay" : The counter is used for delay timer.
> >
> > These are _not_ hardware properties. Why can the driver not choose how
> > to use each of the counter sub-blocks?
> >
> > How would blocks with each of these strings actually differ?
> >
> 
> SOCes may have 2 or 3 or 4 timers, some timers may be used by CP not AP but
> AP can still access it.
> There may be UP SOC, or SMP SOC. So there are may be some general usage
> 1. UP SOC: need one clocksource, need one clock event device
> 2. SMP SOC: need one clocksource, need one broadcast clock event device because
>    arch timer will be shutdown when core is down
> 3. SMP SOC: it has bug with arch timer. So need one clock source,
> NR_CPUs clock event
>     device to replace the arch timer. There are may be 2 cores or 4 cores.

Ok, but in all of these cases the blocks that you are giving different
compatible strings are identical. The only difference is how you expect
Linux to use them.

Why can you not just describe the set of timers available to Linux and
allow it to allocate them as required?

> Above requeimetns are also hardware requriments from SOCes.
> 
> The driver will be used for many SOCes, we can not to fix the counter
> usage in driver.

I'm arguing for dynamically deciding how to use the timers, not assuming
one of the three cases you've laid out above, and not describing a
possible software use of the counters in the DT.

> >> +- marvell,timer-counter-id : The counter index in this timer.
> >
> > Perhaps use a reg for this?
> >
> 
> The counter does not have its own register space. They are belongs to a timer.
> Counter does not need to map the regsiter spance, it is done by timer.
> For example, for counter enable, all coutners share same register. CER
> For interrupt enable, each counter has its own register. IERx
> 
> So if we use like "reg = <IERx offset>", i do not see any benefit. while
> direct setting "marvell,timer-counter-id" seems make the DT more clear.

The reg doesn't have to be a register space -- it's often used simply as
a form of address/identifier without a size (consider SPI's use of the
reg property). Here the index is essentially an address/identifier for
the counter.

Given that the each timer also seems to have a unique ID perhaps using
the reg is not the best strategy here. I was simply raising the
possiblity.

> 
> >>
> >> -Example:
> >> -       timer0: timer at d4014000 {
> >> -               compatible = "mrvl,mmp-timer";
> >> -               reg = <0xd4014000 0x100>;
> >> +Optional properties:
> >> +- interrupts : The counters may have different IRQs or share same IRQs.
> >> +  Only valid for "marvell,timer-counter-clkevt".
> >
> > Describe what the interrupt is logically.
> >
> > What is the interrupt logically?
> >
> 
> The interrupt topolog is designed by SOCes.
> Each counter has one interrupt, but SOCes may connects some coutners' interrupt
> into one.
> In timer driver, we do not care about it. We depeneds on IRQ chip to handle it.

How the interrupt is wired and whether it is shared is not important
from the view of the binding. What is important is which interrupt from
the timer is being described. If it has a given name, that should be
mentioned. Otherwise a simple description will suffice.

The presence of external interrupt muxing doesn't seem to be important
here to the binding -- in that case an OS could request shared
interrupts or decide to only make use of a subset of interrupts. That
seems to be outside of the scope of the binding.

> 
> >> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
> >> +  "marvell,timer-counter-clkevt".
> >
> > How is the counter bound to a particular CPU? Is there really a
> > relationship in hardware between a given CPU and counter?
> >
> 
> For some SMP SOCes, for example, marvell's pxa988/pxa1088/pxa1L88
> They have bug in arch timer. So we make each CPU to have one dedicated
> counter to replace the arch timer.

Is there any real topological relationship between CPUs and counters, or
is this arbitrary? If the latter, why can the kernel not decide how to
allocate timers to CPUs?

> 
> >> +- "marvell,timer-counter-rating" : The rating when register clock event device
> >> +  or clock source. Only valid for "marvell,timer-counter-clkevt" and
> >> +  "marvell,timer-counter-clksrc".
> >
> > What does this mean? This seems like another leak of Linux internals.
> 
> It is the rating when register clock source and clock event.
> I think i can fix it in the driver.

Ok. This is a Linux interenal detail that I do not think should be in
the DT.

> 
> >
> >> +- marvell,timer-counter-broadcast : When this counter acts as clock event
> >> +  device. It is broadcast clock event device.
> >> +  Only valid for "marvell,timer-counter-clkevt".
> >
> > This does not seem like a hardware property. Why can Linux not decide
> > which counter (if any) to use as the broadcast source?
> >
> 
> For some SMP soces, for example, marvell's pxa988/pxa1088/pxa1L88
> They have bug in arch timer. So we make each CPU to have one dedicated
> counter to replace the arch timer. So for these SOCes, broadcast timer is not
> needed. For these counters, we have to set IRQ affinity to the specific core.

That's fine, we can do that in the absence of both this property and
another timer (the arch timer).

> For some SMP soces, for example, they can use arch timer, but we have
> to register
> a clock event device for broadcast because we lose arch timer if core is down.
> For this coutner, we have to set IRQ affinity to ALL cores.

In that case the arch timer will register (which can have a higher
rating than this timer), and this timers can be automatically repurposed
as the broadcast source.

I do not see the necessity of this property.

> >> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
> >> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
> >> +  Only valid for "marvell,timer-counter-clkevt".
> >
> > Likewise this does not sound like a hardware property.
> 
> For a broadcast timer, there is feature "CLOCK_EVT_FEAT_DYNIRQ".
> This feature is a clock event device's fearture. It means that the
> clock event device's irq can
> be set to any core.
> In fact it does not for all SOCes that the interrupt of the counter
> can be set to any core.

The naming and use of this property still seems incredibly
Linux-specific.

Why can some of the counter interrupts not be routed to an arbitrary
CPU?

Cheers,
Mark.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/4] clocksource: mmp: add mmp timer driver
  2014-03-26 10:41     ` Mark Rutland
@ 2014-03-27  1:27         ` Chao Xie
  -1 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2014-03-27  1:27 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Chao Xie, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	haojian.zhuang-Re5JQEeQqe8AvxtiuMwx3w

On Wed, Mar 26, 2014 at 6:41 PM, Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org> wrote:
> On Wed, Mar 26, 2014 at 04:52:40AM +0000, Chao Xie wrote:
>> From: Chao Xie <chao.xie-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
>>
>> MMP timer is attached to APB bus, It has the following
>> limitation.
>> 1. When get count of timer counter, it need some delay
>>    to get a stable count.
>> 2. When set match register, it need disable the counter
>>    first, and enable it after set the match register.
>>    The disabling need wait for 2 clock cycle to take
>>    effect.
>>
>> To improve above #1, shadow register for count is added.
>> To improve above #2, CRSR register is added for quick updating.
>>
>> So there are three types of timer.
>> timer1: old timer.
>> timer2: old timer with shadow register.
>> timer3: old timer with shadow and CRSR register.
>>
>> This timer driver will be used for many SOCes.
>> 1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
>> 2. pxa1L88, pxa1U88 have timer1 and timer3.
>> 3. pxa1928 has timer 2.
>>
>> The driver supports DT and non-DT initialization.
>> The driver supports UP/SMP SOCes and 64 bit core.
>>
>> Signed-off-by: Chao Xie <chao.xie-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
>> ---
>>  .../devicetree/bindings/arm/mrvl/timer.txt         |  73 +-
>>  drivers/clocksource/Makefile                       |   1 +
>>  drivers/clocksource/timer-mmp.c                    | 866 +++++++++++++++++++++
>>  3 files changed, 934 insertions(+), 6 deletions(-)
>>  create mode 100644 drivers/clocksource/timer-mmp.c
>>
>> diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> index 9a6e251..b9af4bf 100644
>> --- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> +++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> @@ -1,13 +1,74 @@
>>  * Marvell MMP Timer controller
>>
>> +Each timer have multiple counters, so the timer DT need include counter's
>> +description.
>> +
>> +1. Timer
>> +
>>  Required properties:
>> -- compatible : Should be "mrvl,mmp-timer".
>> +- compatible : Should be "marvell,mmp-timer".
>>  - reg : Address and length of the register set of timer controller.
>> -- interrupts : Should be the interrupt number.
>> +- marvell,timer-id : The index of the timer. The timer controller may
>> +  include several timers.
>
> Is this a single entry or a list?

Generally, the may be 2/3/4 timers, and each timers will have at most
3 coounters.
Each counters will have 3 matches. Each counter may have its own
interrupts, or share same
interrupts(counter1 has one interrupts, counter2 and 3 share one). It depends on
how SOC connets the signals.

The DT will be
timer 0 {
     ...
     ...
     counter0 {
     };
     counter2 {
     };
}

>
>> +- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
>> +  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
>> +  SOCes use this timer controller, fast clock may not be same.
>> +- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
>> +  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
>> +  to calculated delay loops for clock operations.
>
> Wouldn't these be better described as clock inputs?

"apb" frequency is only used to calculated the delay loop.
I do not want to fix the delay loop in the driver.
So pass it in DT directly will make the things easier to handle.

"fastclk" is a clock input. So waht do you mean "described as clock inputs"?

>
>> +
>> +Optional properties:
>> +- marvell,timer-has-crsr : This timer has CRSR register.
>> +- marvell,timer-has-shadow : This timer has shadow register.
>> +
>> +2. Counter
>> +
>> +Required properties:
>> +- compatible : It can be
>> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
>> +                                       device.
>> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
>> +      "marvell,timer-counter-delay" : The counter is used for delay timer.
>
> These are _not_ hardware properties. Why can the driver not choose how
> to use each of the counter sub-blocks?
>
> How would blocks with each of these strings actually differ?
>

SOCes may have 2 or 3 or 4 timers, some timers may be used by CP not AP but
AP can still access it.
There may be UP SOC, or SMP SOC. So there are may be some general usage
1. UP SOC: need one clocksource, need one clock event device
2. SMP SOC: need one clocksource, need one broadcast clock event device because
   arch timer will be shutdown when core is down
3. SMP SOC: it has bug with arch timer. So need one clock source,
NR_CPUs clock event
    device to replace the arch timer. There are may be 2 cores or 4 cores.


Above requeimetns are also hardware requriments from SOCes.

The driver will be used for many SOCes, we can not to fix the counter
usage in driver.


>> +- marvell,timer-counter-id : The counter index in this timer.
>
> Perhaps use a reg for this?
>

The counter does not have its own register space. They are belongs to a timer.
Counter does not need to map the regsiter spance, it is done by timer.
For example, for counter enable, all coutners share same register. CER
For interrupt enable, each counter has its own register. IERx

So if we use like "reg = <IERx offset>", i do not see any benefit. while
direct setting "marvell,timer-counter-id" seems make the DT more clear.

>>
>> -Example:
>> -       timer0: timer@d4014000 {
>> -               compatible = "mrvl,mmp-timer";
>> -               reg = <0xd4014000 0x100>;
>> +Optional properties:
>> +- interrupts : The counters may have different IRQs or share same IRQs.
>> +  Only valid for "marvell,timer-counter-clkevt".
>
> Describe what the interrupt is logically.
>
> What is the interrupt logically?
>

The interrupt topolog is designed by SOCes.
Each counter has one interrupt, but SOCes may connects some coutners' interrupt
into one.
In timer driver, we do not care about it. We depeneds on IRQ chip to handle it.

>> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
>> +  "marvell,timer-counter-clkevt".
>
> How is the counter bound to a particular CPU? Is there really a
> relationship in hardware between a given CPU and counter?
>

For some SMP SOCes, for example, marvell's pxa988/pxa1088/pxa1L88
They have bug in arch timer. So we make each CPU to have one dedicated
counter to replace the arch timer.

>> +- "marvell,timer-counter-rating" : The rating when register clock event device
>> +  or clock source. Only valid for "marvell,timer-counter-clkevt" and
>> +  "marvell,timer-counter-clksrc".
>
> What does this mean? This seems like another leak of Linux internals.

It is the rating when register clock source and clock event.
I think i can fix it in the driver.

>
>> +- marvell,timer-counter-broadcast : When this counter acts as clock event
>> +  device. It is broadcast clock event device.
>> +  Only valid for "marvell,timer-counter-clkevt".
>
> This does not seem like a hardware property. Why can Linux not decide
> which counter (if any) to use as the broadcast source?
>

For some SMP soces, for example, marvell's pxa988/pxa1088/pxa1L88
They have bug in arch timer. So we make each CPU to have one dedicated
counter to replace the arch timer. So for these SOCes, broadcast timer is not
needed. For these counters, we have to set IRQ affinity to the specific core.

For some SMP soces, for example, they can use arch timer, but we have
to register
a clock event device for broadcast because we lose arch timer if core is down.
For this coutner, we have to set IRQ affinity to ALL cores.

>> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
>> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
>> +  Only valid for "marvell,timer-counter-clkevt".
>
> Likewise this does not sound like a hardware property.

For a broadcast timer, there is feature "CLOCK_EVT_FEAT_DYNIRQ".
This feature is a clock event device's fearture. It means that the
clock event device's irq can
be set to any core.
In fact it does not for all SOCes that the interrupt of the counter
can be set to any core.

>
> Mark.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 1/4] clocksource: mmp: add mmp timer driver
@ 2014-03-27  1:27         ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2014-03-27  1:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 26, 2014 at 6:41 PM, Mark Rutland <mark.rutland@arm.com> wrote:
> On Wed, Mar 26, 2014 at 04:52:40AM +0000, Chao Xie wrote:
>> From: Chao Xie <chao.xie@marvell.com>
>>
>> MMP timer is attached to APB bus, It has the following
>> limitation.
>> 1. When get count of timer counter, it need some delay
>>    to get a stable count.
>> 2. When set match register, it need disable the counter
>>    first, and enable it after set the match register.
>>    The disabling need wait for 2 clock cycle to take
>>    effect.
>>
>> To improve above #1, shadow register for count is added.
>> To improve above #2, CRSR register is added for quick updating.
>>
>> So there are three types of timer.
>> timer1: old timer.
>> timer2: old timer with shadow register.
>> timer3: old timer with shadow and CRSR register.
>>
>> This timer driver will be used for many SOCes.
>> 1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
>> 2. pxa1L88, pxa1U88 have timer1 and timer3.
>> 3. pxa1928 has timer 2.
>>
>> The driver supports DT and non-DT initialization.
>> The driver supports UP/SMP SOCes and 64 bit core.
>>
>> Signed-off-by: Chao Xie <chao.xie@marvell.com>
>> ---
>>  .../devicetree/bindings/arm/mrvl/timer.txt         |  73 +-
>>  drivers/clocksource/Makefile                       |   1 +
>>  drivers/clocksource/timer-mmp.c                    | 866 +++++++++++++++++++++
>>  3 files changed, 934 insertions(+), 6 deletions(-)
>>  create mode 100644 drivers/clocksource/timer-mmp.c
>>
>> diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> index 9a6e251..b9af4bf 100644
>> --- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> +++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
>> @@ -1,13 +1,74 @@
>>  * Marvell MMP Timer controller
>>
>> +Each timer have multiple counters, so the timer DT need include counter's
>> +description.
>> +
>> +1. Timer
>> +
>>  Required properties:
>> -- compatible : Should be "mrvl,mmp-timer".
>> +- compatible : Should be "marvell,mmp-timer".
>>  - reg : Address and length of the register set of timer controller.
>> -- interrupts : Should be the interrupt number.
>> +- marvell,timer-id : The index of the timer. The timer controller may
>> +  include several timers.
>
> Is this a single entry or a list?

Generally, the may be 2/3/4 timers, and each timers will have at most
3 coounters.
Each counters will have 3 matches. Each counter may have its own
interrupts, or share same
interrupts(counter1 has one interrupts, counter2 and 3 share one). It depends on
how SOC connets the signals.

The DT will be
timer 0 {
     ...
     ...
     counter0 {
     };
     counter2 {
     };
}

>
>> +- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
>> +  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
>> +  SOCes use this timer controller, fast clock may not be same.
>> +- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
>> +  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
>> +  to calculated delay loops for clock operations.
>
> Wouldn't these be better described as clock inputs?

"apb" frequency is only used to calculated the delay loop.
I do not want to fix the delay loop in the driver.
So pass it in DT directly will make the things easier to handle.

"fastclk" is a clock input. So waht do you mean "described as clock inputs"?

>
>> +
>> +Optional properties:
>> +- marvell,timer-has-crsr : This timer has CRSR register.
>> +- marvell,timer-has-shadow : This timer has shadow register.
>> +
>> +2. Counter
>> +
>> +Required properties:
>> +- compatible : It can be
>> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
>> +                                       device.
>> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
>> +      "marvell,timer-counter-delay" : The counter is used for delay timer.
>
> These are _not_ hardware properties. Why can the driver not choose how
> to use each of the counter sub-blocks?
>
> How would blocks with each of these strings actually differ?
>

SOCes may have 2 or 3 or 4 timers, some timers may be used by CP not AP but
AP can still access it.
There may be UP SOC, or SMP SOC. So there are may be some general usage
1. UP SOC: need one clocksource, need one clock event device
2. SMP SOC: need one clocksource, need one broadcast clock event device because
   arch timer will be shutdown when core is down
3. SMP SOC: it has bug with arch timer. So need one clock source,
NR_CPUs clock event
    device to replace the arch timer. There are may be 2 cores or 4 cores.


Above requeimetns are also hardware requriments from SOCes.

The driver will be used for many SOCes, we can not to fix the counter
usage in driver.


>> +- marvell,timer-counter-id : The counter index in this timer.
>
> Perhaps use a reg for this?
>

The counter does not have its own register space. They are belongs to a timer.
Counter does not need to map the regsiter spance, it is done by timer.
For example, for counter enable, all coutners share same register. CER
For interrupt enable, each counter has its own register. IERx

So if we use like "reg = <IERx offset>", i do not see any benefit. while
direct setting "marvell,timer-counter-id" seems make the DT more clear.

>>
>> -Example:
>> -       timer0: timer at d4014000 {
>> -               compatible = "mrvl,mmp-timer";
>> -               reg = <0xd4014000 0x100>;
>> +Optional properties:
>> +- interrupts : The counters may have different IRQs or share same IRQs.
>> +  Only valid for "marvell,timer-counter-clkevt".
>
> Describe what the interrupt is logically.
>
> What is the interrupt logically?
>

The interrupt topolog is designed by SOCes.
Each counter has one interrupt, but SOCes may connects some coutners' interrupt
into one.
In timer driver, we do not care about it. We depeneds on IRQ chip to handle it.

>> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
>> +  "marvell,timer-counter-clkevt".
>
> How is the counter bound to a particular CPU? Is there really a
> relationship in hardware between a given CPU and counter?
>

For some SMP SOCes, for example, marvell's pxa988/pxa1088/pxa1L88
They have bug in arch timer. So we make each CPU to have one dedicated
counter to replace the arch timer.

>> +- "marvell,timer-counter-rating" : The rating when register clock event device
>> +  or clock source. Only valid for "marvell,timer-counter-clkevt" and
>> +  "marvell,timer-counter-clksrc".
>
> What does this mean? This seems like another leak of Linux internals.

It is the rating when register clock source and clock event.
I think i can fix it in the driver.

>
>> +- marvell,timer-counter-broadcast : When this counter acts as clock event
>> +  device. It is broadcast clock event device.
>> +  Only valid for "marvell,timer-counter-clkevt".
>
> This does not seem like a hardware property. Why can Linux not decide
> which counter (if any) to use as the broadcast source?
>

For some SMP soces, for example, marvell's pxa988/pxa1088/pxa1L88
They have bug in arch timer. So we make each CPU to have one dedicated
counter to replace the arch timer. So for these SOCes, broadcast timer is not
needed. For these counters, we have to set IRQ affinity to the specific core.

For some SMP soces, for example, they can use arch timer, but we have
to register
a clock event device for broadcast because we lose arch timer if core is down.
For this coutner, we have to set IRQ affinity to ALL cores.

>> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
>> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
>> +  Only valid for "marvell,timer-counter-clkevt".
>
> Likewise this does not sound like a hardware property.

For a broadcast timer, there is feature "CLOCK_EVT_FEAT_DYNIRQ".
This feature is a clock event device's fearture. It means that the
clock event device's irq can
be set to any core.
In fact it does not for all SOCes that the interrupt of the counter
can be set to any core.

>
> Mark.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/4] clocksource: mmp: add mmp timer driver
  2014-03-26  4:52   ` Chao Xie
@ 2014-03-26 10:41     ` Mark Rutland
  -1 siblings, 0 replies; 25+ messages in thread
From: Mark Rutland @ 2014-03-26 10:41 UTC (permalink / raw)
  To: Chao Xie; +Cc: devicetree, xiechao.mail, haojian.zhuang, linux-arm-kernel

On Wed, Mar 26, 2014 at 04:52:40AM +0000, Chao Xie wrote:
> From: Chao Xie <chao.xie@marvell.com>
>
> MMP timer is attached to APB bus, It has the following
> limitation.
> 1. When get count of timer counter, it need some delay
>    to get a stable count.
> 2. When set match register, it need disable the counter
>    first, and enable it after set the match register.
>    The disabling need wait for 2 clock cycle to take
>    effect.
>
> To improve above #1, shadow register for count is added.
> To improve above #2, CRSR register is added for quick updating.
>
> So there are three types of timer.
> timer1: old timer.
> timer2: old timer with shadow register.
> timer3: old timer with shadow and CRSR register.
>
> This timer driver will be used for many SOCes.
> 1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
> 2. pxa1L88, pxa1U88 have timer1 and timer3.
> 3. pxa1928 has timer 2.
>
> The driver supports DT and non-DT initialization.
> The driver supports UP/SMP SOCes and 64 bit core.
>
> Signed-off-by: Chao Xie <chao.xie@marvell.com>
> ---
>  .../devicetree/bindings/arm/mrvl/timer.txt         |  73 +-
>  drivers/clocksource/Makefile                       |   1 +
>  drivers/clocksource/timer-mmp.c                    | 866 +++++++++++++++++++++
>  3 files changed, 934 insertions(+), 6 deletions(-)
>  create mode 100644 drivers/clocksource/timer-mmp.c
>
> diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> index 9a6e251..b9af4bf 100644
> --- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> +++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> @@ -1,13 +1,74 @@
>  * Marvell MMP Timer controller
>
> +Each timer have multiple counters, so the timer DT need include counter's
> +description.
> +
> +1. Timer
> +
>  Required properties:
> -- compatible : Should be "mrvl,mmp-timer".
> +- compatible : Should be "marvell,mmp-timer".
>  - reg : Address and length of the register set of timer controller.
> -- interrupts : Should be the interrupt number.
> +- marvell,timer-id : The index of the timer. The timer controller may
> +  include several timers.

Is this a single entry or a list?

> +- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
> +  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
> +  SOCes use this timer controller, fast clock may not be same.
> +- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
> +  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
> +  to calculated delay loops for clock operations.

Wouldn't these be better described as clock inputs?

> +
> +Optional properties:
> +- marvell,timer-has-crsr : This timer has CRSR register.
> +- marvell,timer-has-shadow : This timer has shadow register.
> +
> +2. Counter
> +
> +Required properties:
> +- compatible : It can be
> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
> +                                       device.
> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
> +      "marvell,timer-counter-delay" : The counter is used for delay timer.

These are _not_ hardware properties. Why can the driver not choose how
to use each of the counter sub-blocks?

How would blocks with each of these strings actually differ?

> +- marvell,timer-counter-id : The counter index in this timer.

Perhaps use a reg for this?

>
> -Example:
> -       timer0: timer@d4014000 {
> -               compatible = "mrvl,mmp-timer";
> -               reg = <0xd4014000 0x100>;
> +Optional properties:
> +- interrupts : The counters may have different IRQs or share same IRQs.
> +  Only valid for "marvell,timer-counter-clkevt".

Describe what the interrupt is logically.

What is the interrupt logically?

> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
> +  "marvell,timer-counter-clkevt".

How is the counter bound to a particular CPU? Is there really a
relationship in hardware between a given CPU and counter?

> +- "marvell,timer-counter-rating" : The rating when register clock event device
> +  or clock source. Only valid for "marvell,timer-counter-clkevt" and
> +  "marvell,timer-counter-clksrc".

What does this mean? This seems like another leak of Linux internals.

> +- marvell,timer-counter-broadcast : When this counter acts as clock event
> +  device. It is broadcast clock event device.
> +  Only valid for "marvell,timer-counter-clkevt".

This does not seem like a hardware property. Why can Linux not decide
which counter (if any) to use as the broadcast source?

> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
> +  Only valid for "marvell,timer-counter-clkevt".

Likewise this does not sound like a hardware property.

Mark.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 1/4] clocksource: mmp: add mmp timer driver
@ 2014-03-26 10:41     ` Mark Rutland
  0 siblings, 0 replies; 25+ messages in thread
From: Mark Rutland @ 2014-03-26 10:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 26, 2014 at 04:52:40AM +0000, Chao Xie wrote:
> From: Chao Xie <chao.xie@marvell.com>
>
> MMP timer is attached to APB bus, It has the following
> limitation.
> 1. When get count of timer counter, it need some delay
>    to get a stable count.
> 2. When set match register, it need disable the counter
>    first, and enable it after set the match register.
>    The disabling need wait for 2 clock cycle to take
>    effect.
>
> To improve above #1, shadow register for count is added.
> To improve above #2, CRSR register is added for quick updating.
>
> So there are three types of timer.
> timer1: old timer.
> timer2: old timer with shadow register.
> timer3: old timer with shadow and CRSR register.
>
> This timer driver will be used for many SOCes.
> 1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
> 2. pxa1L88, pxa1U88 have timer1 and timer3.
> 3. pxa1928 has timer 2.
>
> The driver supports DT and non-DT initialization.
> The driver supports UP/SMP SOCes and 64 bit core.
>
> Signed-off-by: Chao Xie <chao.xie@marvell.com>
> ---
>  .../devicetree/bindings/arm/mrvl/timer.txt         |  73 +-
>  drivers/clocksource/Makefile                       |   1 +
>  drivers/clocksource/timer-mmp.c                    | 866 +++++++++++++++++++++
>  3 files changed, 934 insertions(+), 6 deletions(-)
>  create mode 100644 drivers/clocksource/timer-mmp.c
>
> diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> index 9a6e251..b9af4bf 100644
> --- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> +++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
> @@ -1,13 +1,74 @@
>  * Marvell MMP Timer controller
>
> +Each timer have multiple counters, so the timer DT need include counter's
> +description.
> +
> +1. Timer
> +
>  Required properties:
> -- compatible : Should be "mrvl,mmp-timer".
> +- compatible : Should be "marvell,mmp-timer".
>  - reg : Address and length of the register set of timer controller.
> -- interrupts : Should be the interrupt number.
> +- marvell,timer-id : The index of the timer. The timer controller may
> +  include several timers.

Is this a single entry or a list?

> +- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
> +  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
> +  SOCes use this timer controller, fast clock may not be same.
> +- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
> +  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
> +  to calculated delay loops for clock operations.

Wouldn't these be better described as clock inputs?

> +
> +Optional properties:
> +- marvell,timer-has-crsr : This timer has CRSR register.
> +- marvell,timer-has-shadow : This timer has shadow register.
> +
> +2. Counter
> +
> +Required properties:
> +- compatible : It can be
> +      "marvell,timer-counter-clkevt" : The counter is used for clock event
> +                                       device.
> +      "marvell,timer-counter-clksrc" : The counter is used for clock source.
> +      "marvell,timer-counter-delay" : The counter is used for delay timer.

These are _not_ hardware properties. Why can the driver not choose how
to use each of the counter sub-blocks?

How would blocks with each of these strings actually differ?

> +- marvell,timer-counter-id : The counter index in this timer.

Perhaps use a reg for this?

>
> -Example:
> -       timer0: timer at d4014000 {
> -               compatible = "mrvl,mmp-timer";
> -               reg = <0xd4014000 0x100>;
> +Optional properties:
> +- interrupts : The counters may have different IRQs or share same IRQs.
> +  Only valid for "marvell,timer-counter-clkevt".

Describe what the interrupt is logically.

What is the interrupt logically?

> +- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
> +  "marvell,timer-counter-clkevt".

How is the counter bound to a particular CPU? Is there really a
relationship in hardware between a given CPU and counter?

> +- "marvell,timer-counter-rating" : The rating when register clock event device
> +  or clock source. Only valid for "marvell,timer-counter-clkevt" and
> +  "marvell,timer-counter-clksrc".

What does this mean? This seems like another leak of Linux internals.

> +- marvell,timer-counter-broadcast : When this counter acts as clock event
> +  device. It is broadcast clock event device.
> +  Only valid for "marvell,timer-counter-clkevt".

This does not seem like a hardware property. Why can Linux not decide
which counter (if any) to use as the broadcast source?

> +- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
> +  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
> +  Only valid for "marvell,timer-counter-clkevt".

Likewise this does not sound like a hardware property.

Mark.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 1/4] clocksource: mmp: add mmp timer driver
  2014-03-26  4:52 [PATCH 0/4] arm: mmp: " Chao Xie
@ 2014-03-26  4:52   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2014-03-26  4:52 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, xiechao.mail, haojian.zhuang; +Cc: Chao Xie

From: Chao Xie <chao.xie@marvell.com>

MMP timer is attached to APB bus, It has the following
limitation.
1. When get count of timer counter, it need some delay
   to get a stable count.
2. When set match register, it need disable the counter
   first, and enable it after set the match register.
   The disabling need wait for 2 clock cycle to take
   effect.

To improve above #1, shadow register for count is added.
To improve above #2, CRSR register is added for quick updating.

So there are three types of timer.
timer1: old timer.
timer2: old timer with shadow register.
timer3: old timer with shadow and CRSR register.

This timer driver will be used for many SOCes.
1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
2. pxa1L88, pxa1U88 have timer1 and timer3.
3. pxa1928 has timer 2.

The driver supports DT and non-DT initialization.
The driver supports UP/SMP SOCes and 64 bit core.

Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
 .../devicetree/bindings/arm/mrvl/timer.txt         |  73 +-
 drivers/clocksource/Makefile                       |   1 +
 drivers/clocksource/timer-mmp.c                    | 866 +++++++++++++++++++++
 3 files changed, 934 insertions(+), 6 deletions(-)
 create mode 100644 drivers/clocksource/timer-mmp.c

diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
index 9a6e251..b9af4bf 100644
--- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
+++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
@@ -1,13 +1,74 @@
 * Marvell MMP Timer controller
 
+Each timer have multiple counters, so the timer DT need include counter's
+description.
+
+1. Timer
+
 Required properties:
-- compatible : Should be "mrvl,mmp-timer".
+- compatible : Should be "marvell,mmp-timer".
 - reg : Address and length of the register set of timer controller.
-- interrupts : Should be the interrupt number.
+- marvell,timer-id : The index of the timer. The timer controller may
+  include several timers.
+- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
+  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
+  SOCes use this timer controller, fast clock may not be same.
+- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
+  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
+  to calculated delay loops for clock operations.
+
+Optional properties:
+- marvell,timer-has-crsr : This timer has CRSR register.
+- marvell,timer-has-shadow : This timer has shadow register.
+
+2. Counter
+
+Required properties:
+- compatible : It can be
+      "marvell,timer-counter-clkevt" : The counter is used for clock event
+                                       device.
+      "marvell,timer-counter-clksrc" : The counter is used for clock source.
+      "marvell,timer-counter-delay" : The counter is used for delay timer.
+- marvell,timer-counter-id : The counter index in this timer.
 
-Example:
-	timer0: timer@d4014000 {
-		compatible = "mrvl,mmp-timer";
-		reg = <0xd4014000 0x100>;
+Optional properties:
+- interrupts : The counters may have different IRQs or share same IRQs.
+  Only valid for "marvell,timer-counter-clkevt".
+- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
+  "marvell,timer-counter-clkevt".
+- "marvell,timer-counter-rating" : The rating when register clock event device
+  or clock source. Only valid for "marvell,timer-counter-clkevt" and
+  "marvell,timer-counter-clksrc".
+- marvell,timer-counter-broadcast : When this counter acts as clock event
+  device. It is broadcast clock event device.
+  Only valid for "marvell,timer-counter-clkevt".
+- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
+  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
+  Only valid for "marvell,timer-counter-clkevt".
+
+3. Examples
+
+timer0: timer@d4014000 {
+	compatible = "marvell,mmp-timer";
+	reg = <0xd4014000 0xc8>;
+	marvell,timer-id = <0>;
+	marvell,timer-fastclk-frequency = <3250000>;
+	marvell,timer-apb-frequency = <26000000>;
+	status = "okay";
+
+	counter0 {
+		compatible = "marvell,timer-counter-clkevt";
 		interrupts = <13>;
+		marvell,timer-counter-id = <0>;
+		marvell,timer-counter-cpu = <0>;
+		marvell,timer-counter-frequency = <32768>;
+		marvell,timer-counter-rating = <200>;
+	};
+
+	counter1 {
+		compatible = "marvell,timer-counter-clksrc";
+		marvell,timer-counter-id = <1>;
+		marvell,timer-counter-frequency = <32768>;
+		marvell,timer-counter-rating = <200>;
 	};
+};
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index fb0d82e0..b877a1e 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_ARMADA_370_XP_TIMER)	+= time-armada-370-xp.o
 obj-$(CONFIG_ORION_TIMER)	+= time-orion.o
 obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o
 obj-$(CONFIG_ARCH_MARCO)	+= timer-marco.o
+obj-$(CONFIG_ARCH_MMP)		+= timer-mmp.o
 obj-$(CONFIG_ARCH_MOXART)	+= moxart_timer.o
 obj-$(CONFIG_ARCH_MXS)		+= mxs_timer.o
 obj-$(CONFIG_ARCH_PRIMA2)	+= timer-prima2.o
diff --git a/drivers/clocksource/timer-mmp.c b/drivers/clocksource/timer-mmp.c
new file mode 100644
index 0000000..c83d722
--- /dev/null
+++ b/drivers/clocksource/timer-mmp.c
@@ -0,0 +1,866 @@
+/*
+ * driver/clocksource/timer-mmp.c
+ *
+ *   Support for clocksource and clockevents
+ *
+ * Copyright (C) 2008 Marvell International Ltd.
+ * All rights reserved.
+ *
+ *   2008-04-11: Jason Chagas <Jason.chagas@marvell.com>
+ *   2008-10-08: Bin Yang <bin.yang@marvell.com>
+ *
+ * The timers module actually includes three timers, each timer with up to
+ * three match comparators. Timer #0 is used here in free-running mode as
+ * the clock source, and match comparator #1 used as clock event device.
+ *
+ * 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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/mmp_timer.h>
+#include <linux/clockchips.h>
+#include <linux/sched_clock.h>
+
+#define TMR_CCR		(0x0000)
+#define TMR_TN_MM(n, m)	(0x0004 + ((n) << 3) + (((n) + (m)) << 2))
+#define TMR_CR(n)	(0x0028 + ((n) << 2))
+#define TMR_SR(n)	(0x0034 + ((n) << 2))
+#define TMR_IER(n)	(0x0040 + ((n) << 2))
+#define TMR_PLVR(n)	(0x004c + ((n) << 2))
+#define TMR_PLCR(n)	(0x0058 + ((n) << 2))
+#define TMR_WMER	(0x0064)
+#define TMR_WMR		(0x0068)
+#define TMR_WVR		(0x006c)
+#define TMR_WSR		(0x0070)
+#define TMR_ICR(n)	(0x0074 + ((n) << 2))
+#define TMR_WICR	(0x0080)
+#define TMR_CER		(0x0084)
+#define TMR_CMR		(0x0088)
+#define TMR_ILR(n)	(0x008c + ((n) << 2))
+#define TMR_WCR		(0x0098)
+#define TMR_WFAR	(0x009c)
+#define TMR_WSAR	(0x00A0)
+#define TMR_CVWR(n)	(0x00A4 + ((n) << 2))
+#define TMR_CRSR        (0x00B0)
+
+#define TMR_CCR_CS_0(x)	(((x) & 0x3) << 0)
+#define TMR_CCR_CS_1(x)	(((x) & 0x3) << 2)
+#define TMR_CCR_CS_2(x)	(((x) & 0x3) << 5)
+
+#define MAX_EVT_NUM		5
+
+#define MAX_DELTA		(0xfffffffe)
+#define MIN_DELTA		(5)
+
+#define MMP_MAX_COUNTER		3
+#define MMP_MAX_TIMER		4
+
+#define TMR_CER_COUNTER(cid)	(1 << (cid))
+#define MMP_ALL_COUNTERS	((1 << MMP_MAX_COUNTER) - 1)
+
+#define MMP_TIMER_CLOCK_32KHZ	32768
+#define MMP_TIMER_CLOCK_1KHZ	1000
+
+struct mmp_timer;
+struct mmp_timer_counter {
+	unsigned int id;
+	unsigned int usage;
+	unsigned int cnt_freq;
+	int cpu;
+	int loop_delay;
+	struct mmp_timer *timer;
+};
+
+struct mmp_timer {
+	unsigned int id;
+	void __iomem *base;
+	struct mmp_timer_counter counters[MMP_MAX_COUNTER];
+	unsigned int flag;
+	int loop_delay_fastclk;
+	unsigned int fc_freq;
+	/* lock to protect hw operation. */
+	spinlock_t tm_lock;
+};
+
+struct mmp_timer_clkevt {
+	struct mmp_timer_counter *counter;
+	struct clock_event_device ced;
+	struct irqaction irqa;
+	struct notifier_block nb;
+};
+
+struct mmp_timer_clksrc {
+	struct mmp_timer_counter *counter;
+	struct clocksource cs;
+};
+
+#ifdef CONFIG_ARM
+struct mmp_timer_dclk {
+	struct mmp_timer_counter *counter;
+	struct delay_timer *dt;
+};
+static struct mmp_timer_dclk *dclk;
+#endif
+
+static struct mmp_timer *mmp_timers[MMP_MAX_TIMER];
+static struct mmp_timer_clksrc *clksrc;
+
+static int timer_counter_switch_clock(int tid, int cid, unsigned int freq)
+{
+	struct mmp_timer *tm = mmp_timers[tid];
+	u32 ccr, mask;
+
+	ccr = __raw_readl(tm->base + TMR_CCR);
+
+	if (cid == 0)
+		mask = TMR_CCR_CS_0(3);
+	else if (cid == 1)
+		mask = TMR_CCR_CS_1(3);
+	else
+		mask = TMR_CCR_CS_2(3);
+
+	ccr &= ~mask;
+
+	if (freq == MMP_TIMER_CLOCK_32KHZ) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(2);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(1);
+		else if (cid == 0)
+			ccr |= TMR_CCR_CS_0(1);
+	} else if (freq == MMP_TIMER_CLOCK_1KHZ) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(1);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(2);
+	} else if (freq == tm->fc_freq) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(0);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(0);
+		else if (cid == 0)
+			ccr |= TMR_CCR_CS_0(0);
+	} else {
+		pr_err("Timer %d:%d: invalid clock rate %d\n", tid, cid, freq);
+		return -EINVAL;
+	}
+
+	__raw_writel(ccr, tm->base + TMR_CCR);
+
+	return 0;
+}
+
+static void timer_counter_disable(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	int delay = tm->loop_delay_fastclk;
+	u32 cer;
+
+	/*
+	 * Stop the counter will need mutiple timer clock to take effect.
+	 * Some operations can only be done when counter is disabled. So
+	 * add delay here.
+	 */
+	/* Step1: disable counter */
+	cer = __raw_readl(tm->base + TMR_CER);
+	__raw_writel(cer & ~(1 << cnt->id), tm->base + TMR_CER);
+
+	/*
+	 * Step2: switch to fast clock, so the delay can be completed
+	 * quickly.
+	 */
+	if (cnt->cnt_freq != tm->fc_freq)
+		timer_counter_switch_clock(tm->id, cnt->id, tm->fc_freq);
+
+	/*
+	 * Step3: Loop for mutiple timer cycles. We do it by clearing
+	 * pending interrupt status.
+	 */
+	while (delay--)
+		__raw_writel(0x1, tm->base + TMR_ICR(cnt->id));
+}
+
+static void timer_counter_enable(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	u32 cer;
+
+	/* Switch to original clock */
+	if (cnt->cnt_freq != tm->fc_freq)
+		timer_counter_switch_clock(tm->id, cnt->id, cnt->cnt_freq);
+
+	/* Enable timer */
+	cer = __raw_readl(tm->base + TMR_CER);
+	__raw_writel(cer | (1 << cnt->id), tm->base + TMR_CER);
+}
+
+static inline uint32_t timer_read(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	int has_shadow = tm->flag & MMP_TIMER_FLAG_SHADOW;
+	int delay = 3;
+	u32 val1, val2;
+
+	if (has_shadow) {
+		return __raw_readl(tm->base + TMR_CR(cnt->id));
+	} else {
+		if (cnt->cnt_freq != tm->fc_freq) {
+			/* slow clock */
+			do {
+				val1 = __raw_readl(tm->base + TMR_CR(cnt->id));
+				val2 = __raw_readl(tm->base + TMR_CR(cnt->id));
+			} while (val2 != val1);
+		} else {
+			/* fast clock */
+			__raw_writel(1, tm->base + TMR_CVWR(cnt->id));
+			while (delay--)
+				val1 = __raw_readl(tm->base +
+						TMR_CVWR(cnt->id));
+		}
+		return val1;
+	}
+}
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *c = dev_id;
+	struct mmp_timer_clkevt *evt;
+	struct mmp_timer_counter *counter;
+	unsigned int cnt;
+	unsigned long flags;
+	void __iomem *base;
+	int has_crsr;
+
+	evt = container_of(c, struct mmp_timer_clkevt, ced);
+	counter = evt->counter;
+	cnt = counter->id;
+	base = counter->timer->base;
+	has_crsr = counter->timer->flag & MMP_TIMER_FLAG_CRSR;
+
+	spin_lock_irqsave(&(counter->timer->tm_lock), flags);
+	/* We only use match #0 for the counter. */
+	if (__raw_readl(base + TMR_SR(cnt)) & 0x1) {
+		if (!has_crsr)
+			timer_counter_disable(counter);
+
+		/* Disable the interrupt. */
+		__raw_writel(0x00, base + TMR_IER(cnt));
+		/* Clear interrupt status */
+		__raw_writel(0x01, base + TMR_ICR(cnt));
+
+		spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+
+		c->event_handler(c);
+
+		return IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+
+	return IRQ_NONE;
+}
+
+static int timer_set_next_event(unsigned long delta,
+				struct clock_event_device *dev)
+{
+	struct mmp_timer_counter *cnt;
+	struct mmp_timer_clkevt *evt;
+	unsigned long flags;
+	unsigned int cid;
+	u32 cer, crsr;
+	void __iomem *base;
+	int delay, has_crsr;
+
+	evt = container_of(dev, struct mmp_timer_clkevt, ced);
+	cnt = evt->counter;
+	cid = cnt->id;
+	base = cnt->timer->base;
+	has_crsr = cnt->timer->flag & MMP_TIMER_FLAG_CRSR;
+
+	spin_lock_irqsave(&(cnt->timer->tm_lock), flags);
+	if (has_crsr) {
+		/*
+		 * Use TMR_CRSR to restart the counter and make match
+		 * register take effect. This bit should be 0 before
+		 * set it again.
+		 * The polling loop is defined by loop_delay.
+		 */
+		delay = cnt->loop_delay;
+		do {
+			crsr = __raw_readl(base + TMR_CRSR);
+			delay--;
+		} while ((crsr & (1 << cid)) && delay > 0);
+
+		BUG_ON(delay <= 0);
+
+		__raw_writel(delta - 1, base + TMR_TN_MM(cid, 0));
+		/*
+		 * After counter is restart, clear the interrupt status for
+		 * safe, and re-enable the interrupt for match #0.
+		 */
+		__raw_writel(0x01, base + TMR_ICR(cid));
+		__raw_writel(0x01, base + TMR_IER(cid));
+		__raw_writel((1 << cid), base + TMR_CRSR);
+	} else {
+		cer = __raw_readl(base + TMR_CER);
+
+		/* If the timer counter is enabled, first disable it. */
+		if (cer & (1 << cid))
+			timer_counter_disable(cnt);
+
+		/* Setup new counter value */
+		__raw_writel(delta - 1, base + TMR_TN_MM(cid, 0));
+
+		/* enable the matching interrupt */
+		__raw_writel(0x1, base + TMR_IER(cid));
+
+		timer_counter_enable(cnt);
+	}
+	spin_unlock_irqrestore(&(cnt->timer->tm_lock), flags);
+
+	return 0;
+}
+
+static void timer_set_mode(enum clock_event_mode mode,
+			   struct clock_event_device *dev)
+{
+	unsigned long flags;
+	unsigned int cnt;
+	struct mmp_timer_counter *counter;
+	struct mmp_timer_clkevt *evt;
+	void __iomem *base;
+
+	evt = container_of(dev, struct mmp_timer_clkevt, ced);
+	counter = evt->counter;
+	cnt = counter->id;
+	base = counter->timer->base;
+
+	spin_lock_irqsave(&(counter->timer->tm_lock), flags);
+	switch (mode) {
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		timer_counter_disable(counter);
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+	case CLOCK_EVT_MODE_ONESHOT:
+		timer_counter_enable(counter);
+		break;
+	}
+	spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+}
+
+static cycle_t clksrc_read(struct clocksource *cs)
+{
+	return timer_read(clksrc->counter);
+}
+
+static u64 notrace mmp_read_sched_clock(void)
+{
+	return timer_read(clksrc->counter);
+}
+
+#ifdef CONFIG_ARM
+static unsigned long d_read_current_timer(void)
+{
+	return timer_read(dclk->counter);
+}
+
+static struct delay_timer d_timer = {
+	.read_current_timer	= d_read_current_timer,
+};
+#endif
+
+static int mmp_timer_cpu_notify(struct notifier_block *self,
+					   unsigned long action, void *hcpu)
+{
+	struct mmp_timer_clkevt *evt;
+	struct mmp_timer_counter *cnt;
+
+	evt = container_of(self, struct mmp_timer_clkevt, nb);
+	cnt = evt->counter;
+
+	if (cnt->cpu != (unsigned long)hcpu)
+		return NOTIFY_OK;
+
+	switch (action & ~CPU_TASKS_FROZEN) {
+	case CPU_STARTING:
+		clockevents_config_and_register(&evt->ced,
+						cnt->cnt_freq,
+						MIN_DELTA, MAX_DELTA);
+		break;
+	case CPU_ONLINE:
+		irq_set_affinity(evt->ced.irq, evt->ced.cpumask);
+		enable_irq(evt->ced.irq);
+		break;
+	case CPU_DYING:
+		clockevents_set_mode(&evt->ced,
+				     CLOCK_EVT_MODE_SHUTDOWN);
+		disable_irq(evt->ced.irq);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+int __init mmp_timer_init(int tid, void __iomem *base,
+			unsigned int flag, unsigned int fc_freq,
+			unsigned int apb_freq)
+{
+	struct mmp_timer *tm = mmp_timers[tid];
+	u32 tmp, delay;
+	int cid;
+
+	if (tm)
+		return -EINVAL;
+
+	tm = kzalloc(sizeof(*tm), GFP_KERNEL);
+	if (!tm)
+		return -ENOMEM;
+
+	/*
+	 * The calculation formula for the loop cycle is:
+	 *
+	 * (1) need wait for 2 timer's clock cycle:
+	 *        1             2
+	 *     ------- x 2 = -------
+	 *     fc_freq       fc_freq
+	 *
+	 * (2) convert to apb clock cycle:
+	 *        2          1        apb_freq * 2
+	 *     ------- / -------- = ----------------
+	 *     fc_freq   apb_freq       fc_freq
+	 *
+	 * (3) every apb register's accessing will take 8 apb clock cycle,
+	 *     also consider add extral one more time for safe way;
+	 *     so finally need loop times for the apb register accessing:
+	 *
+	 *       (apb_freq * 2)
+	 *     ------------------ / 8 + 1
+	 *          fc_freq
+	 */
+	delay = ((apb_freq * 2) / fc_freq / 8) + 1;
+	pr_debug("Timer %d: loop_delay_fastclk is %d\n", tid, delay);
+
+	tm->id = tid;
+	tm->base = base;
+	tm->flag = flag;
+	tm->loop_delay_fastclk = delay;
+	tm->fc_freq = fc_freq;
+	spin_lock_init(&(tm->tm_lock));
+
+	mmp_timers[tid] = tm;
+
+	for (cid = 0; cid < MMP_MAX_COUNTER; cid++) {
+		tm->counters[cid].id = cid;
+		tm->counters[cid].timer = tm;
+
+		/* We will disable all counters. Switch to fastclk first. */
+		timer_counter_switch_clock(tid, cid, fc_freq);
+	}
+
+	/* disalbe all counters */
+	tmp = __raw_readl(base + TMR_CER) & ~MMP_ALL_COUNTERS;
+	__raw_writel(tmp, base + TMR_CER);
+
+	/* disable matching interrupt */
+	__raw_writel(0x00, base + TMR_IER(0));
+	__raw_writel(0x00, base + TMR_IER(1));
+	__raw_writel(0x00, base + TMR_IER(2));
+
+	while (delay--) {
+		/* Clear pending interrupt status */
+		__raw_writel(0x1, base + TMR_ICR(0));
+		__raw_writel(0x1, base + TMR_ICR(1));
+		__raw_writel(0x1, base + TMR_ICR(2));
+		__raw_writel(tmp, base + TMR_CER);
+	}
+
+	return 0;
+}
+
+static int __init mmp_timer_counter_hw_init(struct mmp_timer *tm, int cid,
+					unsigned int freq)
+{
+	u32 tmp, delay;
+	unsigned int ratio;
+	int ret;
+
+	ret = timer_counter_switch_clock(tm->id, cid, freq);
+	if (ret)
+		return ret;
+
+	ratio = tm->fc_freq / tm->counters[cid].cnt_freq;
+	tm->counters[cid].cnt_freq = freq;
+	tm->counters[cid].loop_delay = tm->loop_delay_fastclk * ratio;
+
+	/* set timer to free-running mode */
+	tmp = __raw_readl(tm->base + TMR_CMR) | TMR_CER_COUNTER(cid);
+	__raw_writel(tmp, tm->base + TMR_CMR);
+
+	/* free-running */
+	__raw_writel(0x0, tm->base + TMR_PLCR(cid));
+	/* clear status */
+	__raw_writel(0x7, tm->base + TMR_ICR(cid));
+
+	/* enable counter */
+	tmp = __raw_readl(tm->base + TMR_CER) | TMR_CER_COUNTER(cid);
+	__raw_writel(tmp, tm->base + TMR_CER);
+
+	delay = tm->counters[cid].loop_delay;
+	while (delay--)
+		__raw_writel(tmp, tm->base + TMR_CER);
+
+	return 0;
+}
+
+int __init mmp_counter_clocksource_init(int tid, int cid, int rating,
+		unsigned int freq)
+{
+	struct mmp_timer *mt = mmp_timers[tid];
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (clksrc) {
+		pr_err("One clksrc has already been registered!\n");
+		return -EINVAL;
+	}
+	clksrc = kzalloc(sizeof(*clksrc), GFP_KERNEL);
+	if (!clksrc)
+		return -ENOMEM;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_CLKSRC;
+	mt->counters[cid].cnt_freq = freq;
+
+	clksrc->counter = &mt->counters[cid];
+	clksrc->cs.name = "clocksource-mmp";
+	clksrc->cs.rating = rating;
+	clksrc->cs.read = clksrc_read;
+	clksrc->cs.mask = CLOCKSOURCE_MASK(32);
+	clksrc->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+
+	sched_clock_register(mmp_read_sched_clock, 32, freq);
+
+	clocksource_register_hz(&(clksrc->cs), freq);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int __init mmp_counter_timer_delay_init(int tid, int cid, unsigned int freq)
+{
+#ifdef CONFIG_ARM
+	struct mmp_timer *mt = mmp_timers[tid];
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (dclk) {
+		pr_err("Delay clock has already been registered!\n");
+		return -EINVAL;
+	}
+	dclk = kzalloc(sizeof(*dclk), GFP_KERNEL);
+	if (!dclk)
+		return -ENOMEM;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_DELAY;
+	mt->counters[cid].cnt_freq = freq;
+
+	dclk->counter = &mt->counters[cid];
+	dclk->dt = &d_timer;
+	d_timer.freq = freq;
+	register_current_timer_delay(&d_timer);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+#endif
+	return 0;
+}
+
+int __init mmp_counter_clockevent_init(int tid, int cid, int rating, int irq,
+		int freq, int dynirq, unsigned int cpu)
+{
+	struct mmp_timer *mt = mmp_timers[tid];
+	struct mmp_timer_clkevt *clkevt;
+	int broadcast = 0;
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (cpu == MMP_TIMER_ALL_CPU)
+		broadcast = 1;
+	else if (cpu >= num_possible_cpus())
+		return -EINVAL;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_CLKEVT;
+	mt->counters[cid].cnt_freq = freq;
+	mt->counters[cid].cpu = cpu;
+
+	clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
+	if (!clkevt)
+		return -ENOMEM;
+	clkevt->counter = &mt->counters[cid];
+	clkevt->ced.name = "clockevent-mmp";
+	clkevt->ced.features = CLOCK_EVT_FEAT_ONESHOT;
+	clkevt->ced.rating = rating;
+	clkevt->ced.set_next_event = timer_set_next_event;
+	clkevt->ced.set_mode = timer_set_mode;
+	clkevt->ced.irq = irq;
+
+	clkevt->irqa.flags = IRQF_DISABLED | IRQF_TIMER |
+		IRQF_IRQPOLL;
+	clkevt->irqa.handler = timer_interrupt;
+	clkevt->irqa.dev_id = &(clkevt->ced);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+
+	if (broadcast) {
+		clkevt->irqa.name = "broadcast-timer";
+		if (dynirq)
+			clkevt->ced.features |= CLOCK_EVT_FEAT_DYNIRQ;
+		clkevt->ced.cpumask = cpu_possible_mask;
+		setup_irq(clkevt->ced.irq, &(clkevt->irqa));
+		clockevents_config_and_register(&clkevt->ced,
+						freq, MIN_DELTA, MAX_DELTA);
+	} else {
+		clkevt->irqa.name = "local-timer";
+		clkevt->ced.cpumask = cpumask_of(cpu);
+		clkevt->nb.notifier_call = mmp_timer_cpu_notify;
+		register_cpu_notifier(&clkevt->nb);
+		setup_irq(clkevt->ced.irq, &(clkevt->irqa));
+		/* Enable clock event device for boot CPU. */
+		if (cpu == smp_processor_id()) {
+			clockevents_config_and_register(&clkevt->ced,
+							freq, MIN_DELTA,
+							MAX_DELTA);
+			/* Only online CPU can be set affinity. */
+			irq_set_affinity(clkevt->ced.irq, cpumask_of(cpu));
+		} else {
+			/* disable none boot CPU's irq at first */
+			disable_irq(clkevt->ced.irq);
+		}
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+
+struct of_device_id mmp_counter_of_id[] = {
+	{
+		.compatible = "marvell,timer-counter-clksrc",
+		.data = (void *)MMP_TIMER_COUNTER_CLKSRC,
+	},
+	{
+		.compatible = "marvell,timer-counter-clkevt",
+		.data = (void *)MMP_TIMER_COUNTER_CLKEVT,
+	},
+	{
+		.compatible = "marvell,timer-counter-delay",
+		.data = (void *)MMP_TIMER_COUNTER_DELAY,
+	},
+	{ },
+};
+
+static int __init mmp_of_counter_init(struct device_node *np, int tid,
+					unsigned int usage)
+{
+	int irq, ret, dynirq;
+	unsigned int cid, freq, cpu, rating;
+
+	if (!np)
+		return -EINVAL;
+
+	ret = of_property_read_u32(np, "marvell,timer-counter-id", &cid);
+	if (ret) {
+		pr_err("Timer %d: fail to get counter id\n", tid);
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "marvell,timer-counter-frequency",
+			&freq);
+	if (ret) {
+		pr_err("Timer %d:%d: fail to get counter frequency\n",
+		       tid, cid);
+		return ret;
+	}
+
+	if (usage & MMP_TIMER_COUNTER_DELAY) {
+		ret = mmp_counter_timer_delay_init(tid, cid, freq);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create delay timer\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+
+	if (usage & (MMP_TIMER_COUNTER_CLKSRC | MMP_TIMER_COUNTER_CLKEVT)) {
+		ret = of_property_read_u32(np,
+				"marvell,timer-counter-rating", &rating);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to get counter rating\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+	if (usage & MMP_TIMER_COUNTER_CLKSRC) {
+		ret = mmp_counter_clocksource_init(tid, cid, rating,
+					freq);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create clksrc\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+	if (usage & MMP_TIMER_COUNTER_CLKEVT) {
+		if (of_property_read_bool(np,
+					  "marvell,timer-counter-broadcast")) {
+			cpu = MMP_TIMER_ALL_CPU;
+		} else {
+			ret = of_property_read_u32(np,
+					"marvell,timer-counter-cpu", &cpu);
+			if (ret) {
+				pr_err("Timer %d:%d: fail to get cpu\n",
+				       tid, cid);
+				return ret;
+			}
+		}
+		dynirq = !of_property_read_bool(np,
+				"marvell,timer-counter-nodynirq");
+		irq = irq_of_parse_and_map(np, 0);
+		ret = mmp_counter_clockevent_init(tid, cid, rating,
+					irq, freq, dynirq, cpu);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create clkevt\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void __init mmp_of_timer_init(struct device_node *np)
+{
+	int tid;
+	unsigned int flag, fc_freq, apb_freq;
+	void __iomem *base;
+	struct device_node *child_np;
+	const struct of_device_id *match;
+	int ret = 0;
+
+	/* timer initialization */
+	ret = of_property_read_u32(np, "marvell,timer-id", &tid);
+	if (ret) {
+		pr_err("Timer: fail to get timer id with err %d\n", ret);
+		goto out;
+	}
+	if (tid < 0 || tid >= MMP_MAX_TIMER) {
+		pr_err("Timer: id too large or too small\n");
+		ret = -EINVAL;
+		goto out;
+	}
+	base = of_iomap(np, 0);
+	if (!base) {
+		pr_err("Timer: fail to map register space\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	flag = 0;
+	if (of_property_read_bool(np, "marvell,timer-has-crsr"))
+		flag |= MMP_TIMER_FLAG_CRSR;
+
+	if (of_property_read_bool(np, "marvell,timer-has-shadow"))
+		flag |= MMP_TIMER_FLAG_SHADOW;
+
+	/* timer's fast clock and apb frequency */
+	ret = of_property_read_u32(np, "marvell,timer-fastclk-frequency",
+			&fc_freq);
+	if (ret) {
+		pr_err("Timer %d: fail to get fastclk-frequency with err %d\n",
+		       tid, ret);
+		goto out;
+	}
+	ret = of_property_read_u32(np, "marvell,timer-apb-frequency",
+			&apb_freq);
+	if (ret) {
+		pr_err("Timer %d: fail to get apb-frequency with err %d\n",
+		       tid, ret);
+		goto out;
+	}
+
+	/*
+	 * Need use loop for more safe register's accessing,
+	 * so at here dynamically calculate the loop time.
+	 */
+	if (!fc_freq || !apb_freq) {
+		pr_err("mmp timer's fast clock or apb freq are incorrect!\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = mmp_timer_init(tid, base, flag, fc_freq, apb_freq);
+	if (ret)
+		goto out;
+
+	/*
+	 * If device node is marked as not available,
+	 * we then don't try to enable the counter again
+	 */
+	if (!of_device_is_available(np)) {
+		pr_warn("Timer %d: is not used\n", tid);
+		return;
+	}
+
+	/* counter initialization */
+	for_each_child_of_node(np, child_np) {
+		match = of_match_node(mmp_counter_of_id, child_np);
+		ret = mmp_of_counter_init(child_np, tid,
+					(unsigned int)match->data);
+		if (ret)
+			goto out;
+	}
+
+	return;
+out:
+	if (ret)
+		pr_err("Failed to get timer from dtb with error:%d\n", ret);
+	return;
+}
+
+CLOCKSOURCE_OF_DECLARE(mmp_timer, "marvell,mmp-timer", mmp_of_timer_init);
+#endif
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 1/4] clocksource: mmp: add mmp timer driver
@ 2014-03-26  4:52   ` Chao Xie
  0 siblings, 0 replies; 25+ messages in thread
From: Chao Xie @ 2014-03-26  4:52 UTC (permalink / raw)
  To: linux-arm-kernel

From: Chao Xie <chao.xie@marvell.com>

MMP timer is attached to APB bus, It has the following
limitation.
1. When get count of timer counter, it need some delay
   to get a stable count.
2. When set match register, it need disable the counter
   first, and enable it after set the match register.
   The disabling need wait for 2 clock cycle to take
   effect.

To improve above #1, shadow register for count is added.
To improve above #2, CRSR register is added for quick updating.

So there are three types of timer.
timer1: old timer.
timer2: old timer with shadow register.
timer3: old timer with shadow and CRSR register.

This timer driver will be used for many SOCes.
1. pxa168, pxa910, pxa988 pxa1088 have only timer1.
2. pxa1L88, pxa1U88 have timer1 and timer3.
3. pxa1928 has timer 2.

The driver supports DT and non-DT initialization.
The driver supports UP/SMP SOCes and 64 bit core.

Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
 .../devicetree/bindings/arm/mrvl/timer.txt         |  73 +-
 drivers/clocksource/Makefile                       |   1 +
 drivers/clocksource/timer-mmp.c                    | 866 +++++++++++++++++++++
 3 files changed, 934 insertions(+), 6 deletions(-)
 create mode 100644 drivers/clocksource/timer-mmp.c

diff --git a/Documentation/devicetree/bindings/arm/mrvl/timer.txt b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
index 9a6e251..b9af4bf 100644
--- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
+++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
@@ -1,13 +1,74 @@
 * Marvell MMP Timer controller
 
+Each timer have multiple counters, so the timer DT need include counter's
+description.
+
+1. Timer
+
 Required properties:
-- compatible : Should be "mrvl,mmp-timer".
+- compatible : Should be "marvell,mmp-timer".
 - reg : Address and length of the register set of timer controller.
-- interrupts : Should be the interrupt number.
+- marvell,timer-id : The index of the timer. The timer controller may
+  include several timers.
+- marvell,timer-fastclk-frequency : The fast clock frequency of the timer.
+  Timer will have several clock inputs: fast clock, 32KHZ, 1KHZ. For all
+  SOCes use this timer controller, fast clock may not be same.
+- marvell,timer-apb-frequency : The fequency of the apb bus that the timer
+  attached to. This frequency and "marvell,timer-fastclk-frequency" are used
+  to calculated delay loops for clock operations.
+
+Optional properties:
+- marvell,timer-has-crsr : This timer has CRSR register.
+- marvell,timer-has-shadow : This timer has shadow register.
+
+2. Counter
+
+Required properties:
+- compatible : It can be
+      "marvell,timer-counter-clkevt" : The counter is used for clock event
+                                       device.
+      "marvell,timer-counter-clksrc" : The counter is used for clock source.
+      "marvell,timer-counter-delay" : The counter is used for delay timer.
+- marvell,timer-counter-id : The counter index in this timer.
 
-Example:
-	timer0: timer at d4014000 {
-		compatible = "mrvl,mmp-timer";
-		reg = <0xd4014000 0x100>;
+Optional properties:
+- interrupts : The counters may have different IRQs or share same IRQs.
+  Only valid for "marvell,timer-counter-clkevt".
+- marvell,timer-counter-cpu : which CPU the counter is bound. Only valid for
+  "marvell,timer-counter-clkevt".
+- "marvell,timer-counter-rating" : The rating when register clock event device
+  or clock source. Only valid for "marvell,timer-counter-clkevt" and
+  "marvell,timer-counter-clksrc".
+- marvell,timer-counter-broadcast : When this counter acts as clock event
+  device. It is broadcast clock event device.
+  Only valid for "marvell,timer-counter-clkevt".
+- marvell,timer-counter-nodynirq : When this counter acts as broadcast clock
+  event device, it cannot switch the IRQ of broadcast clock event to any CPU.
+  Only valid for "marvell,timer-counter-clkevt".
+
+3. Examples
+
+timer0: timer at d4014000 {
+	compatible = "marvell,mmp-timer";
+	reg = <0xd4014000 0xc8>;
+	marvell,timer-id = <0>;
+	marvell,timer-fastclk-frequency = <3250000>;
+	marvell,timer-apb-frequency = <26000000>;
+	status = "okay";
+
+	counter0 {
+		compatible = "marvell,timer-counter-clkevt";
 		interrupts = <13>;
+		marvell,timer-counter-id = <0>;
+		marvell,timer-counter-cpu = <0>;
+		marvell,timer-counter-frequency = <32768>;
+		marvell,timer-counter-rating = <200>;
+	};
+
+	counter1 {
+		compatible = "marvell,timer-counter-clksrc";
+		marvell,timer-counter-id = <1>;
+		marvell,timer-counter-frequency = <32768>;
+		marvell,timer-counter-rating = <200>;
 	};
+};
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index fb0d82e0..b877a1e 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_ARMADA_370_XP_TIMER)	+= time-armada-370-xp.o
 obj-$(CONFIG_ORION_TIMER)	+= time-orion.o
 obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o
 obj-$(CONFIG_ARCH_MARCO)	+= timer-marco.o
+obj-$(CONFIG_ARCH_MMP)		+= timer-mmp.o
 obj-$(CONFIG_ARCH_MOXART)	+= moxart_timer.o
 obj-$(CONFIG_ARCH_MXS)		+= mxs_timer.o
 obj-$(CONFIG_ARCH_PRIMA2)	+= timer-prima2.o
diff --git a/drivers/clocksource/timer-mmp.c b/drivers/clocksource/timer-mmp.c
new file mode 100644
index 0000000..c83d722
--- /dev/null
+++ b/drivers/clocksource/timer-mmp.c
@@ -0,0 +1,866 @@
+/*
+ * driver/clocksource/timer-mmp.c
+ *
+ *   Support for clocksource and clockevents
+ *
+ * Copyright (C) 2008 Marvell International Ltd.
+ * All rights reserved.
+ *
+ *   2008-04-11: Jason Chagas <Jason.chagas@marvell.com>
+ *   2008-10-08: Bin Yang <bin.yang@marvell.com>
+ *
+ * The timers module actually includes three timers, each timer with up to
+ * three match comparators. Timer #0 is used here in free-running mode as
+ * the clock source, and match comparator #1 used as clock event device.
+ *
+ * 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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/mmp_timer.h>
+#include <linux/clockchips.h>
+#include <linux/sched_clock.h>
+
+#define TMR_CCR		(0x0000)
+#define TMR_TN_MM(n, m)	(0x0004 + ((n) << 3) + (((n) + (m)) << 2))
+#define TMR_CR(n)	(0x0028 + ((n) << 2))
+#define TMR_SR(n)	(0x0034 + ((n) << 2))
+#define TMR_IER(n)	(0x0040 + ((n) << 2))
+#define TMR_PLVR(n)	(0x004c + ((n) << 2))
+#define TMR_PLCR(n)	(0x0058 + ((n) << 2))
+#define TMR_WMER	(0x0064)
+#define TMR_WMR		(0x0068)
+#define TMR_WVR		(0x006c)
+#define TMR_WSR		(0x0070)
+#define TMR_ICR(n)	(0x0074 + ((n) << 2))
+#define TMR_WICR	(0x0080)
+#define TMR_CER		(0x0084)
+#define TMR_CMR		(0x0088)
+#define TMR_ILR(n)	(0x008c + ((n) << 2))
+#define TMR_WCR		(0x0098)
+#define TMR_WFAR	(0x009c)
+#define TMR_WSAR	(0x00A0)
+#define TMR_CVWR(n)	(0x00A4 + ((n) << 2))
+#define TMR_CRSR        (0x00B0)
+
+#define TMR_CCR_CS_0(x)	(((x) & 0x3) << 0)
+#define TMR_CCR_CS_1(x)	(((x) & 0x3) << 2)
+#define TMR_CCR_CS_2(x)	(((x) & 0x3) << 5)
+
+#define MAX_EVT_NUM		5
+
+#define MAX_DELTA		(0xfffffffe)
+#define MIN_DELTA		(5)
+
+#define MMP_MAX_COUNTER		3
+#define MMP_MAX_TIMER		4
+
+#define TMR_CER_COUNTER(cid)	(1 << (cid))
+#define MMP_ALL_COUNTERS	((1 << MMP_MAX_COUNTER) - 1)
+
+#define MMP_TIMER_CLOCK_32KHZ	32768
+#define MMP_TIMER_CLOCK_1KHZ	1000
+
+struct mmp_timer;
+struct mmp_timer_counter {
+	unsigned int id;
+	unsigned int usage;
+	unsigned int cnt_freq;
+	int cpu;
+	int loop_delay;
+	struct mmp_timer *timer;
+};
+
+struct mmp_timer {
+	unsigned int id;
+	void __iomem *base;
+	struct mmp_timer_counter counters[MMP_MAX_COUNTER];
+	unsigned int flag;
+	int loop_delay_fastclk;
+	unsigned int fc_freq;
+	/* lock to protect hw operation. */
+	spinlock_t tm_lock;
+};
+
+struct mmp_timer_clkevt {
+	struct mmp_timer_counter *counter;
+	struct clock_event_device ced;
+	struct irqaction irqa;
+	struct notifier_block nb;
+};
+
+struct mmp_timer_clksrc {
+	struct mmp_timer_counter *counter;
+	struct clocksource cs;
+};
+
+#ifdef CONFIG_ARM
+struct mmp_timer_dclk {
+	struct mmp_timer_counter *counter;
+	struct delay_timer *dt;
+};
+static struct mmp_timer_dclk *dclk;
+#endif
+
+static struct mmp_timer *mmp_timers[MMP_MAX_TIMER];
+static struct mmp_timer_clksrc *clksrc;
+
+static int timer_counter_switch_clock(int tid, int cid, unsigned int freq)
+{
+	struct mmp_timer *tm = mmp_timers[tid];
+	u32 ccr, mask;
+
+	ccr = __raw_readl(tm->base + TMR_CCR);
+
+	if (cid == 0)
+		mask = TMR_CCR_CS_0(3);
+	else if (cid == 1)
+		mask = TMR_CCR_CS_1(3);
+	else
+		mask = TMR_CCR_CS_2(3);
+
+	ccr &= ~mask;
+
+	if (freq == MMP_TIMER_CLOCK_32KHZ) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(2);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(1);
+		else if (cid == 0)
+			ccr |= TMR_CCR_CS_0(1);
+	} else if (freq == MMP_TIMER_CLOCK_1KHZ) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(1);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(2);
+	} else if (freq == tm->fc_freq) {
+		if (cid == 2)
+			ccr |= TMR_CCR_CS_2(0);
+		else if (cid == 1)
+			ccr |= TMR_CCR_CS_1(0);
+		else if (cid == 0)
+			ccr |= TMR_CCR_CS_0(0);
+	} else {
+		pr_err("Timer %d:%d: invalid clock rate %d\n", tid, cid, freq);
+		return -EINVAL;
+	}
+
+	__raw_writel(ccr, tm->base + TMR_CCR);
+
+	return 0;
+}
+
+static void timer_counter_disable(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	int delay = tm->loop_delay_fastclk;
+	u32 cer;
+
+	/*
+	 * Stop the counter will need mutiple timer clock to take effect.
+	 * Some operations can only be done when counter is disabled. So
+	 * add delay here.
+	 */
+	/* Step1: disable counter */
+	cer = __raw_readl(tm->base + TMR_CER);
+	__raw_writel(cer & ~(1 << cnt->id), tm->base + TMR_CER);
+
+	/*
+	 * Step2: switch to fast clock, so the delay can be completed
+	 * quickly.
+	 */
+	if (cnt->cnt_freq != tm->fc_freq)
+		timer_counter_switch_clock(tm->id, cnt->id, tm->fc_freq);
+
+	/*
+	 * Step3: Loop for mutiple timer cycles. We do it by clearing
+	 * pending interrupt status.
+	 */
+	while (delay--)
+		__raw_writel(0x1, tm->base + TMR_ICR(cnt->id));
+}
+
+static void timer_counter_enable(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	u32 cer;
+
+	/* Switch to original clock */
+	if (cnt->cnt_freq != tm->fc_freq)
+		timer_counter_switch_clock(tm->id, cnt->id, cnt->cnt_freq);
+
+	/* Enable timer */
+	cer = __raw_readl(tm->base + TMR_CER);
+	__raw_writel(cer | (1 << cnt->id), tm->base + TMR_CER);
+}
+
+static inline uint32_t timer_read(struct mmp_timer_counter *cnt)
+{
+	struct mmp_timer *tm = cnt->timer;
+	int has_shadow = tm->flag & MMP_TIMER_FLAG_SHADOW;
+	int delay = 3;
+	u32 val1, val2;
+
+	if (has_shadow) {
+		return __raw_readl(tm->base + TMR_CR(cnt->id));
+	} else {
+		if (cnt->cnt_freq != tm->fc_freq) {
+			/* slow clock */
+			do {
+				val1 = __raw_readl(tm->base + TMR_CR(cnt->id));
+				val2 = __raw_readl(tm->base + TMR_CR(cnt->id));
+			} while (val2 != val1);
+		} else {
+			/* fast clock */
+			__raw_writel(1, tm->base + TMR_CVWR(cnt->id));
+			while (delay--)
+				val1 = __raw_readl(tm->base +
+						TMR_CVWR(cnt->id));
+		}
+		return val1;
+	}
+}
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *c = dev_id;
+	struct mmp_timer_clkevt *evt;
+	struct mmp_timer_counter *counter;
+	unsigned int cnt;
+	unsigned long flags;
+	void __iomem *base;
+	int has_crsr;
+
+	evt = container_of(c, struct mmp_timer_clkevt, ced);
+	counter = evt->counter;
+	cnt = counter->id;
+	base = counter->timer->base;
+	has_crsr = counter->timer->flag & MMP_TIMER_FLAG_CRSR;
+
+	spin_lock_irqsave(&(counter->timer->tm_lock), flags);
+	/* We only use match #0 for the counter. */
+	if (__raw_readl(base + TMR_SR(cnt)) & 0x1) {
+		if (!has_crsr)
+			timer_counter_disable(counter);
+
+		/* Disable the interrupt. */
+		__raw_writel(0x00, base + TMR_IER(cnt));
+		/* Clear interrupt status */
+		__raw_writel(0x01, base + TMR_ICR(cnt));
+
+		spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+
+		c->event_handler(c);
+
+		return IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+
+	return IRQ_NONE;
+}
+
+static int timer_set_next_event(unsigned long delta,
+				struct clock_event_device *dev)
+{
+	struct mmp_timer_counter *cnt;
+	struct mmp_timer_clkevt *evt;
+	unsigned long flags;
+	unsigned int cid;
+	u32 cer, crsr;
+	void __iomem *base;
+	int delay, has_crsr;
+
+	evt = container_of(dev, struct mmp_timer_clkevt, ced);
+	cnt = evt->counter;
+	cid = cnt->id;
+	base = cnt->timer->base;
+	has_crsr = cnt->timer->flag & MMP_TIMER_FLAG_CRSR;
+
+	spin_lock_irqsave(&(cnt->timer->tm_lock), flags);
+	if (has_crsr) {
+		/*
+		 * Use TMR_CRSR to restart the counter and make match
+		 * register take effect. This bit should be 0 before
+		 * set it again.
+		 * The polling loop is defined by loop_delay.
+		 */
+		delay = cnt->loop_delay;
+		do {
+			crsr = __raw_readl(base + TMR_CRSR);
+			delay--;
+		} while ((crsr & (1 << cid)) && delay > 0);
+
+		BUG_ON(delay <= 0);
+
+		__raw_writel(delta - 1, base + TMR_TN_MM(cid, 0));
+		/*
+		 * After counter is restart, clear the interrupt status for
+		 * safe, and re-enable the interrupt for match #0.
+		 */
+		__raw_writel(0x01, base + TMR_ICR(cid));
+		__raw_writel(0x01, base + TMR_IER(cid));
+		__raw_writel((1 << cid), base + TMR_CRSR);
+	} else {
+		cer = __raw_readl(base + TMR_CER);
+
+		/* If the timer counter is enabled, first disable it. */
+		if (cer & (1 << cid))
+			timer_counter_disable(cnt);
+
+		/* Setup new counter value */
+		__raw_writel(delta - 1, base + TMR_TN_MM(cid, 0));
+
+		/* enable the matching interrupt */
+		__raw_writel(0x1, base + TMR_IER(cid));
+
+		timer_counter_enable(cnt);
+	}
+	spin_unlock_irqrestore(&(cnt->timer->tm_lock), flags);
+
+	return 0;
+}
+
+static void timer_set_mode(enum clock_event_mode mode,
+			   struct clock_event_device *dev)
+{
+	unsigned long flags;
+	unsigned int cnt;
+	struct mmp_timer_counter *counter;
+	struct mmp_timer_clkevt *evt;
+	void __iomem *base;
+
+	evt = container_of(dev, struct mmp_timer_clkevt, ced);
+	counter = evt->counter;
+	cnt = counter->id;
+	base = counter->timer->base;
+
+	spin_lock_irqsave(&(counter->timer->tm_lock), flags);
+	switch (mode) {
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		timer_counter_disable(counter);
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+	case CLOCK_EVT_MODE_ONESHOT:
+		timer_counter_enable(counter);
+		break;
+	}
+	spin_unlock_irqrestore(&(counter->timer->tm_lock), flags);
+}
+
+static cycle_t clksrc_read(struct clocksource *cs)
+{
+	return timer_read(clksrc->counter);
+}
+
+static u64 notrace mmp_read_sched_clock(void)
+{
+	return timer_read(clksrc->counter);
+}
+
+#ifdef CONFIG_ARM
+static unsigned long d_read_current_timer(void)
+{
+	return timer_read(dclk->counter);
+}
+
+static struct delay_timer d_timer = {
+	.read_current_timer	= d_read_current_timer,
+};
+#endif
+
+static int mmp_timer_cpu_notify(struct notifier_block *self,
+					   unsigned long action, void *hcpu)
+{
+	struct mmp_timer_clkevt *evt;
+	struct mmp_timer_counter *cnt;
+
+	evt = container_of(self, struct mmp_timer_clkevt, nb);
+	cnt = evt->counter;
+
+	if (cnt->cpu != (unsigned long)hcpu)
+		return NOTIFY_OK;
+
+	switch (action & ~CPU_TASKS_FROZEN) {
+	case CPU_STARTING:
+		clockevents_config_and_register(&evt->ced,
+						cnt->cnt_freq,
+						MIN_DELTA, MAX_DELTA);
+		break;
+	case CPU_ONLINE:
+		irq_set_affinity(evt->ced.irq, evt->ced.cpumask);
+		enable_irq(evt->ced.irq);
+		break;
+	case CPU_DYING:
+		clockevents_set_mode(&evt->ced,
+				     CLOCK_EVT_MODE_SHUTDOWN);
+		disable_irq(evt->ced.irq);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+int __init mmp_timer_init(int tid, void __iomem *base,
+			unsigned int flag, unsigned int fc_freq,
+			unsigned int apb_freq)
+{
+	struct mmp_timer *tm = mmp_timers[tid];
+	u32 tmp, delay;
+	int cid;
+
+	if (tm)
+		return -EINVAL;
+
+	tm = kzalloc(sizeof(*tm), GFP_KERNEL);
+	if (!tm)
+		return -ENOMEM;
+
+	/*
+	 * The calculation formula for the loop cycle is:
+	 *
+	 * (1) need wait for 2 timer's clock cycle:
+	 *        1             2
+	 *     ------- x 2 = -------
+	 *     fc_freq       fc_freq
+	 *
+	 * (2) convert to apb clock cycle:
+	 *        2          1        apb_freq * 2
+	 *     ------- / -------- = ----------------
+	 *     fc_freq   apb_freq       fc_freq
+	 *
+	 * (3) every apb register's accessing will take 8 apb clock cycle,
+	 *     also consider add extral one more time for safe way;
+	 *     so finally need loop times for the apb register accessing:
+	 *
+	 *       (apb_freq * 2)
+	 *     ------------------ / 8 + 1
+	 *          fc_freq
+	 */
+	delay = ((apb_freq * 2) / fc_freq / 8) + 1;
+	pr_debug("Timer %d: loop_delay_fastclk is %d\n", tid, delay);
+
+	tm->id = tid;
+	tm->base = base;
+	tm->flag = flag;
+	tm->loop_delay_fastclk = delay;
+	tm->fc_freq = fc_freq;
+	spin_lock_init(&(tm->tm_lock));
+
+	mmp_timers[tid] = tm;
+
+	for (cid = 0; cid < MMP_MAX_COUNTER; cid++) {
+		tm->counters[cid].id = cid;
+		tm->counters[cid].timer = tm;
+
+		/* We will disable all counters. Switch to fastclk first. */
+		timer_counter_switch_clock(tid, cid, fc_freq);
+	}
+
+	/* disalbe all counters */
+	tmp = __raw_readl(base + TMR_CER) & ~MMP_ALL_COUNTERS;
+	__raw_writel(tmp, base + TMR_CER);
+
+	/* disable matching interrupt */
+	__raw_writel(0x00, base + TMR_IER(0));
+	__raw_writel(0x00, base + TMR_IER(1));
+	__raw_writel(0x00, base + TMR_IER(2));
+
+	while (delay--) {
+		/* Clear pending interrupt status */
+		__raw_writel(0x1, base + TMR_ICR(0));
+		__raw_writel(0x1, base + TMR_ICR(1));
+		__raw_writel(0x1, base + TMR_ICR(2));
+		__raw_writel(tmp, base + TMR_CER);
+	}
+
+	return 0;
+}
+
+static int __init mmp_timer_counter_hw_init(struct mmp_timer *tm, int cid,
+					unsigned int freq)
+{
+	u32 tmp, delay;
+	unsigned int ratio;
+	int ret;
+
+	ret = timer_counter_switch_clock(tm->id, cid, freq);
+	if (ret)
+		return ret;
+
+	ratio = tm->fc_freq / tm->counters[cid].cnt_freq;
+	tm->counters[cid].cnt_freq = freq;
+	tm->counters[cid].loop_delay = tm->loop_delay_fastclk * ratio;
+
+	/* set timer to free-running mode */
+	tmp = __raw_readl(tm->base + TMR_CMR) | TMR_CER_COUNTER(cid);
+	__raw_writel(tmp, tm->base + TMR_CMR);
+
+	/* free-running */
+	__raw_writel(0x0, tm->base + TMR_PLCR(cid));
+	/* clear status */
+	__raw_writel(0x7, tm->base + TMR_ICR(cid));
+
+	/* enable counter */
+	tmp = __raw_readl(tm->base + TMR_CER) | TMR_CER_COUNTER(cid);
+	__raw_writel(tmp, tm->base + TMR_CER);
+
+	delay = tm->counters[cid].loop_delay;
+	while (delay--)
+		__raw_writel(tmp, tm->base + TMR_CER);
+
+	return 0;
+}
+
+int __init mmp_counter_clocksource_init(int tid, int cid, int rating,
+		unsigned int freq)
+{
+	struct mmp_timer *mt = mmp_timers[tid];
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (clksrc) {
+		pr_err("One clksrc has already been registered!\n");
+		return -EINVAL;
+	}
+	clksrc = kzalloc(sizeof(*clksrc), GFP_KERNEL);
+	if (!clksrc)
+		return -ENOMEM;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_CLKSRC;
+	mt->counters[cid].cnt_freq = freq;
+
+	clksrc->counter = &mt->counters[cid];
+	clksrc->cs.name = "clocksource-mmp";
+	clksrc->cs.rating = rating;
+	clksrc->cs.read = clksrc_read;
+	clksrc->cs.mask = CLOCKSOURCE_MASK(32);
+	clksrc->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+
+	sched_clock_register(mmp_read_sched_clock, 32, freq);
+
+	clocksource_register_hz(&(clksrc->cs), freq);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int __init mmp_counter_timer_delay_init(int tid, int cid, unsigned int freq)
+{
+#ifdef CONFIG_ARM
+	struct mmp_timer *mt = mmp_timers[tid];
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (dclk) {
+		pr_err("Delay clock has already been registered!\n");
+		return -EINVAL;
+	}
+	dclk = kzalloc(sizeof(*dclk), GFP_KERNEL);
+	if (!dclk)
+		return -ENOMEM;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_DELAY;
+	mt->counters[cid].cnt_freq = freq;
+
+	dclk->counter = &mt->counters[cid];
+	dclk->dt = &d_timer;
+	d_timer.freq = freq;
+	register_current_timer_delay(&d_timer);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+#endif
+	return 0;
+}
+
+int __init mmp_counter_clockevent_init(int tid, int cid, int rating, int irq,
+		int freq, int dynirq, unsigned int cpu)
+{
+	struct mmp_timer *mt = mmp_timers[tid];
+	struct mmp_timer_clkevt *clkevt;
+	int broadcast = 0;
+	int ret;
+
+	if (!mt)
+		return -EINVAL;
+
+	if (cid < 0 || cid >= MMP_MAX_COUNTER)
+		return -EINVAL;
+
+	if (cpu == MMP_TIMER_ALL_CPU)
+		broadcast = 1;
+	else if (cpu >= num_possible_cpus())
+		return -EINVAL;
+
+	mt->counters[cid].usage |= MMP_TIMER_COUNTER_CLKEVT;
+	mt->counters[cid].cnt_freq = freq;
+	mt->counters[cid].cpu = cpu;
+
+	clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
+	if (!clkevt)
+		return -ENOMEM;
+	clkevt->counter = &mt->counters[cid];
+	clkevt->ced.name = "clockevent-mmp";
+	clkevt->ced.features = CLOCK_EVT_FEAT_ONESHOT;
+	clkevt->ced.rating = rating;
+	clkevt->ced.set_next_event = timer_set_next_event;
+	clkevt->ced.set_mode = timer_set_mode;
+	clkevt->ced.irq = irq;
+
+	clkevt->irqa.flags = IRQF_DISABLED | IRQF_TIMER |
+		IRQF_IRQPOLL;
+	clkevt->irqa.handler = timer_interrupt;
+	clkevt->irqa.dev_id = &(clkevt->ced);
+
+	ret = mmp_timer_counter_hw_init(mt, cid, freq);
+	if (ret)
+		return ret;
+
+	if (broadcast) {
+		clkevt->irqa.name = "broadcast-timer";
+		if (dynirq)
+			clkevt->ced.features |= CLOCK_EVT_FEAT_DYNIRQ;
+		clkevt->ced.cpumask = cpu_possible_mask;
+		setup_irq(clkevt->ced.irq, &(clkevt->irqa));
+		clockevents_config_and_register(&clkevt->ced,
+						freq, MIN_DELTA, MAX_DELTA);
+	} else {
+		clkevt->irqa.name = "local-timer";
+		clkevt->ced.cpumask = cpumask_of(cpu);
+		clkevt->nb.notifier_call = mmp_timer_cpu_notify;
+		register_cpu_notifier(&clkevt->nb);
+		setup_irq(clkevt->ced.irq, &(clkevt->irqa));
+		/* Enable clock event device for boot CPU. */
+		if (cpu == smp_processor_id()) {
+			clockevents_config_and_register(&clkevt->ced,
+							freq, MIN_DELTA,
+							MAX_DELTA);
+			/* Only online CPU can be set affinity. */
+			irq_set_affinity(clkevt->ced.irq, cpumask_of(cpu));
+		} else {
+			/* disable none boot CPU's irq at first */
+			disable_irq(clkevt->ced.irq);
+		}
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+
+struct of_device_id mmp_counter_of_id[] = {
+	{
+		.compatible = "marvell,timer-counter-clksrc",
+		.data = (void *)MMP_TIMER_COUNTER_CLKSRC,
+	},
+	{
+		.compatible = "marvell,timer-counter-clkevt",
+		.data = (void *)MMP_TIMER_COUNTER_CLKEVT,
+	},
+	{
+		.compatible = "marvell,timer-counter-delay",
+		.data = (void *)MMP_TIMER_COUNTER_DELAY,
+	},
+	{ },
+};
+
+static int __init mmp_of_counter_init(struct device_node *np, int tid,
+					unsigned int usage)
+{
+	int irq, ret, dynirq;
+	unsigned int cid, freq, cpu, rating;
+
+	if (!np)
+		return -EINVAL;
+
+	ret = of_property_read_u32(np, "marvell,timer-counter-id", &cid);
+	if (ret) {
+		pr_err("Timer %d: fail to get counter id\n", tid);
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "marvell,timer-counter-frequency",
+			&freq);
+	if (ret) {
+		pr_err("Timer %d:%d: fail to get counter frequency\n",
+		       tid, cid);
+		return ret;
+	}
+
+	if (usage & MMP_TIMER_COUNTER_DELAY) {
+		ret = mmp_counter_timer_delay_init(tid, cid, freq);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create delay timer\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+
+	if (usage & (MMP_TIMER_COUNTER_CLKSRC | MMP_TIMER_COUNTER_CLKEVT)) {
+		ret = of_property_read_u32(np,
+				"marvell,timer-counter-rating", &rating);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to get counter rating\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+	if (usage & MMP_TIMER_COUNTER_CLKSRC) {
+		ret = mmp_counter_clocksource_init(tid, cid, rating,
+					freq);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create clksrc\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+	if (usage & MMP_TIMER_COUNTER_CLKEVT) {
+		if (of_property_read_bool(np,
+					  "marvell,timer-counter-broadcast")) {
+			cpu = MMP_TIMER_ALL_CPU;
+		} else {
+			ret = of_property_read_u32(np,
+					"marvell,timer-counter-cpu", &cpu);
+			if (ret) {
+				pr_err("Timer %d:%d: fail to get cpu\n",
+				       tid, cid);
+				return ret;
+			}
+		}
+		dynirq = !of_property_read_bool(np,
+				"marvell,timer-counter-nodynirq");
+		irq = irq_of_parse_and_map(np, 0);
+		ret = mmp_counter_clockevent_init(tid, cid, rating,
+					irq, freq, dynirq, cpu);
+		if (ret) {
+			pr_err("Timer %d:%d: fail to create clkevt\n",
+			       tid, cid);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void __init mmp_of_timer_init(struct device_node *np)
+{
+	int tid;
+	unsigned int flag, fc_freq, apb_freq;
+	void __iomem *base;
+	struct device_node *child_np;
+	const struct of_device_id *match;
+	int ret = 0;
+
+	/* timer initialization */
+	ret = of_property_read_u32(np, "marvell,timer-id", &tid);
+	if (ret) {
+		pr_err("Timer: fail to get timer id with err %d\n", ret);
+		goto out;
+	}
+	if (tid < 0 || tid >= MMP_MAX_TIMER) {
+		pr_err("Timer: id too large or too small\n");
+		ret = -EINVAL;
+		goto out;
+	}
+	base = of_iomap(np, 0);
+	if (!base) {
+		pr_err("Timer: fail to map register space\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	flag = 0;
+	if (of_property_read_bool(np, "marvell,timer-has-crsr"))
+		flag |= MMP_TIMER_FLAG_CRSR;
+
+	if (of_property_read_bool(np, "marvell,timer-has-shadow"))
+		flag |= MMP_TIMER_FLAG_SHADOW;
+
+	/* timer's fast clock and apb frequency */
+	ret = of_property_read_u32(np, "marvell,timer-fastclk-frequency",
+			&fc_freq);
+	if (ret) {
+		pr_err("Timer %d: fail to get fastclk-frequency with err %d\n",
+		       tid, ret);
+		goto out;
+	}
+	ret = of_property_read_u32(np, "marvell,timer-apb-frequency",
+			&apb_freq);
+	if (ret) {
+		pr_err("Timer %d: fail to get apb-frequency with err %d\n",
+		       tid, ret);
+		goto out;
+	}
+
+	/*
+	 * Need use loop for more safe register's accessing,
+	 * so at here dynamically calculate the loop time.
+	 */
+	if (!fc_freq || !apb_freq) {
+		pr_err("mmp timer's fast clock or apb freq are incorrect!\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = mmp_timer_init(tid, base, flag, fc_freq, apb_freq);
+	if (ret)
+		goto out;
+
+	/*
+	 * If device node is marked as not available,
+	 * we then don't try to enable the counter again
+	 */
+	if (!of_device_is_available(np)) {
+		pr_warn("Timer %d: is not used\n", tid);
+		return;
+	}
+
+	/* counter initialization */
+	for_each_child_of_node(np, child_np) {
+		match = of_match_node(mmp_counter_of_id, child_np);
+		ret = mmp_of_counter_init(child_np, tid,
+					(unsigned int)match->data);
+		if (ret)
+			goto out;
+	}
+
+	return;
+out:
+	if (ret)
+		pr_err("Failed to get timer from dtb with error:%d\n", ret);
+	return;
+}
+
+CLOCKSOURCE_OF_DECLARE(mmp_timer, "marvell,mmp-timer", mmp_of_timer_init);
+#endif
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2015-02-03  1:42 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-02-02  8:31 [PATCH 0/4] drivers: clocksource: add mmp timer driver Chao Xie
2015-02-02  8:31 ` Chao Xie
2015-02-02  8:31 ` [PATCH 1/4] clocksource: mmp: " Chao Xie
2015-02-02  8:31   ` Chao Xie
2015-02-02 10:34   ` Mark Rutland
2015-02-02  8:31 ` [PATCH 2/4] arm: mmp: make SOCes without DT use new " Chao Xie
2015-02-02  8:31   ` Chao Xie
2015-02-02  8:31 ` [PATCH 3/4] arm: mmp: make SOCes with " Chao Xie
2015-02-02  8:31   ` Chao Xie
2015-02-02  8:31 ` [PATCH 4/4] arm: mmp: remove the old " Chao Xie
2015-02-02  8:31   ` Chao Xie
     [not found] <AAD1C6EB06EE3649B35B7E026785068D340D60B6DE@SC-VEXCH2.marvell.com>
2015-02-03  1:25 ` [PATCH 1/4] clocksource: mmp: add mmp " Chao Xie
2015-02-03  1:25   ` Chao Xie
  -- strict thread matches above, loose matches on Subject: below --
2014-03-26  4:52 [PATCH 0/4] arm: mmp: " Chao Xie
2014-03-26  4:52 ` [PATCH 1/4] clocksource: mmp: add mmp " Chao Xie
2014-03-26  4:52   ` Chao Xie
2014-03-26 10:41   ` Mark Rutland
2014-03-26 10:41     ` Mark Rutland
     [not found]     ` <20140326104137.GQ10341-NuALmloUBlrZROr8t4l/smS4ubULX0JqMm0uRHvK7Nw@public.gmane.org>
2014-03-27  1:27       ` Chao Xie
2014-03-27  1:27         ` Chao Xie
2014-03-27  9:59         ` Mark Rutland
2014-03-27  9:59           ` Mark Rutland
     [not found]           ` <20140327095925.GA13071-NuALmloUBlrZROr8t4l/smS4ubULX0JqMm0uRHvK7Nw@public.gmane.org>
2014-03-27 12:13             ` Chao Xie
2014-03-27 12:13               ` Chao Xie
     [not found]               ` <CADApbeidd5Kg9dhewfoCYy0PxGhR7C2C=2icmofEaaZz2ZLbNg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-03-28  1:55                 ` Chao Xie
2014-03-28  1:55                   ` Chao Xie

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.