All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 0/4] arm: mmp: timer driver
@ 2014-04-17  7:30 ` Chao Xie
  0 siblings, 0 replies; 10+ messages in thread
From: Chao Xie @ 2014-04-17  7:30 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	haojian.zhuang-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	chao.xie-eYqpPyKDWXRBDgjK7y7TUQ

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.

V1->V2
1. remove device tree properties
   "marvell,timer-id"
   "marvell,timer-counter-rating"

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         |  73 +-
 arch/arm/boot/dts/mmp2.dtsi                        |  25 +-
 arch/arm/boot/dts/pxa168.dtsi                      |  32 +-
 arch/arm/boot/dts/pxa910.dtsi                      |  32 +-
 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                         |  11 +-
 arch/arm/mach-mmp/mmp2-dt.c                        |  11 +-
 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                    | 866 +++++++++++++++++++++
 15 files changed, 1089 insertions(+), 285 deletions(-)
 delete mode 100644 arch/arm/mach-mmp/time.c
 create mode 100644 drivers/clocksource/timer-mmp.c

-- 
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] 10+ messages in thread

* [PATCH V2 0/4] arm: mmp: timer driver
@ 2014-04-17  7:30 ` Chao Xie
  0 siblings, 0 replies; 10+ messages in thread
From: Chao Xie @ 2014-04-17  7:30 UTC (permalink / raw)
  To: linux-arm-kernel

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.

V1->V2
1. remove device tree properties
   "marvell,timer-id"
   "marvell,timer-counter-rating"

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         |  73 +-
 arch/arm/boot/dts/mmp2.dtsi                        |  25 +-
 arch/arm/boot/dts/pxa168.dtsi                      |  32 +-
 arch/arm/boot/dts/pxa910.dtsi                      |  32 +-
 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                         |  11 +-
 arch/arm/mach-mmp/mmp2-dt.c                        |  11 +-
 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                    | 866 +++++++++++++++++++++
 15 files changed, 1089 insertions(+), 285 deletions(-)
 delete mode 100644 arch/arm/mach-mmp/time.c
 create mode 100644 drivers/clocksource/timer-mmp.c

-- 
1.8.3.2

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

* [PATCH V2 1/4] clocksource: mmp: add mmp timer driver
  2014-04-17  7:30 ` Chao Xie
@ 2014-04-17  7:30     ` Chao Xie
  -1 siblings, 0 replies; 10+ messages in thread
From: Chao Xie @ 2014-04-17  7:30 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	haojian.zhuang-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	chao.xie-eYqpPyKDWXRBDgjK7y7TUQ

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         |  65 +-
 drivers/clocksource/Makefile                       |   1 +
 drivers/clocksource/timer-mmp.c                    | 852 +++++++++++++++++++++
 include/linux/mmp_timer.h                          |  40 +
 4 files changed, 952 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..b10bc56 100644
--- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
+++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
@@ -1,13 +1,66 @@
 * 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-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 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>;
+	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>;
+	};
+
+	counter1 {
+		compatible = "marvell,timer-counter-clksrc";
+		marvell,timer-counter-id = <1>;
+		marvell,timer-counter-frequency = <32768>;
 	};
+};
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 98cb6c5..f609402 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..0da98c7
--- /dev/null
+++ b/drivers/clocksource/timer-mmp.c
@@ -0,0 +1,852 @@
+/*
+ * 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-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/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, 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;
+
+	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) {
+		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;
+	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;
+
+	/* 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;
+
+	/* 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
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

--
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] 10+ messages in thread

* [PATCH V2 1/4] clocksource: mmp: add mmp timer driver
@ 2014-04-17  7:30     ` Chao Xie
  0 siblings, 0 replies; 10+ messages in thread
From: Chao Xie @ 2014-04-17  7:30 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         |  65 +-
 drivers/clocksource/Makefile                       |   1 +
 drivers/clocksource/timer-mmp.c                    | 852 +++++++++++++++++++++
 include/linux/mmp_timer.h                          |  40 +
 4 files changed, 952 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..b10bc56 100644
--- a/Documentation/devicetree/bindings/arm/mrvl/timer.txt
+++ b/Documentation/devicetree/bindings/arm/mrvl/timer.txt
@@ -1,13 +1,66 @@
 * 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-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 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 at d4014000 {
+	compatible = "marvell,mmp-timer";
+	reg = <0xd4014000 0xc8>;
+	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>;
+	};
+
+	counter1 {
+		compatible = "marvell,timer-counter-clksrc";
+		marvell,timer-counter-id = <1>;
+		marvell,timer-counter-frequency = <32768>;
 	};
+};
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 98cb6c5..f609402 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..0da98c7
--- /dev/null
+++ b/drivers/clocksource/timer-mmp.c
@@ -0,0 +1,852 @@
+/*
+ * 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, 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;
+
+	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) {
+		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;
+	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;
+
+	/* 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;
+
+	/* 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
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] 10+ messages in thread

* [PATCH V2 2/4] arm: mmp: make SOCes without DT use new timer driver
  2014-04-17  7:30 ` Chao Xie
@ 2014-04-17  7:30     ` Chao Xie
  -1 siblings, 0 replies; 10+ messages in thread
From: Chao Xie @ 2014-04-17  7:30 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	haojian.zhuang-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	chao.xie-eYqpPyKDWXRBDgjK7y7TUQ

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] 10+ messages in thread

* [PATCH V2 2/4] arm: mmp: make SOCes without DT use new timer driver
@ 2014-04-17  7:30     ` Chao Xie
  0 siblings, 0 replies; 10+ messages in thread
From: Chao Xie @ 2014-04-17  7:30 UTC (permalink / raw)
  To: linux-arm-kernel

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] 10+ messages in thread

* [PATCH V2 3/4] arm: mmp: make SOCes with DT use new timer driver
  2014-04-17  7:30 ` Chao Xie
@ 2014-04-17  7:30     ` Chao Xie
  -1 siblings, 0 replies; 10+ messages in thread
From: Chao Xie @ 2014-04-17  7:30 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	haojian.zhuang-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	chao.xie-eYqpPyKDWXRBDgjK7y7TUQ

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

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

Signed-off-by: Chao Xie <chao.xie-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
---
 arch/arm/boot/dts/mmp2.dtsi   | 22 +++++++++++++++++++---
 arch/arm/boot/dts/pxa168.dtsi | 29 +++++++++++++++++++----------
 arch/arm/boot/dts/pxa910.dtsi | 29 +++++++++++++++++++----------
 arch/arm/mach-mmp/Kconfig     |  2 ++
 arch/arm/mach-mmp/mmp-dt.c    | 11 +++++++++--
 arch/arm/mach-mmp/mmp2-dt.c   | 11 ++++++++++-
 6 files changed, 78 insertions(+), 26 deletions(-)

diff --git a/arch/arm/boot/dts/mmp2.dtsi b/arch/arm/boot/dts/mmp2.dtsi
index 4e8b08c..bd0872d 100644
--- a/arch/arm/boot/dts/mmp2.dtsi
+++ b/arch/arm/boot/dts/mmp2.dtsi
@@ -126,9 +126,25 @@
 			ranges;
 
 			timer0: timer@d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				marvell,timer-fastclk-frequency = <6500000>;
+				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>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+					marvell,timer-counter-frequency = <32768>;
+                                };
 			};
 
 			uart1: uart@d4030000 {
diff --git a/arch/arm/boot/dts/pxa168.dtsi b/arch/arm/boot/dts/pxa168.dtsi
index 975dad2..422505d 100644
--- a/arch/arm/boot/dts/pxa168.dtsi
+++ b/arch/arm/boot/dts/pxa168.dtsi
@@ -50,16 +50,25 @@
 			ranges;
 
 			timer0: timer@d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
-			};
-
-			uart1: uart@d4017000 {
-				compatible = "mrvl,mmp-uart";
-				reg = <0xd4017000 0x1000>;
-				interrupts = <27>;
-				status = "disabled";
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				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>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+					marvell,timer-counter-frequency = <32768>;
+                                };
 			};
 
 			uart2: uart@d4018000 {
diff --git a/arch/arm/boot/dts/pxa910.dtsi b/arch/arm/boot/dts/pxa910.dtsi
index 0247c62..173ec16 100644
--- a/arch/arm/boot/dts/pxa910.dtsi
+++ b/arch/arm/boot/dts/pxa910.dtsi
@@ -55,16 +55,25 @@
 			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>;
+				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>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+					marvell,timer-counter-frequency = <32768>;
+                                };
 			};
 
 			uart1: uart@d4017000 {
diff --git a/arch/arm/mach-mmp/Kconfig b/arch/arm/mach-mmp/Kconfig
index ebdba87..8828f61 100644
--- a/arch/arm/mach-mmp/Kconfig
+++ b/arch/arm/mach-mmp/Kconfig
@@ -89,6 +89,7 @@ config MACH_MMP_DT
 	select CPU_PXA168
 	select CPU_PXA910
 	select USE_OF
+	select CLKSRC_OF
 	select PINCTRL
 	select PINCTRL_SINGLE
 	help
@@ -101,6 +102,7 @@ config MACH_MMP2_DT
 	depends on !CPU_MOHAWK
 	select CPU_MMP2
 	select USE_OF
+	select CLKSRC_OF
 	select PINCTRL
 	select PINCTRL_SINGLE
 	help
diff --git a/arch/arm/mach-mmp/mmp-dt.c b/arch/arm/mach-mmp/mmp-dt.c
index cca529c..c729e46 100644
--- a/arch/arm/mach-mmp/mmp-dt.c
+++ b/arch/arm/mach-mmp/mmp-dt.c
@@ -11,13 +11,12 @@
 
 #include <linux/irqchip.h>
 #include <linux/of_platform.h>
+#include <linux/clocksource.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 
 #include "common.h"
 
-extern void __init mmp_dt_init_timer(void);
-
 static const struct of_dev_auxdata pxa168_auxdata_lookup[] __initconst = {
 	OF_DEV_AUXDATA("mrvl,mmp-uart", 0xd4017000, "pxa2xx-uart.0", NULL),
 	OF_DEV_AUXDATA("mrvl,mmp-uart", 0xd4018000, "pxa2xx-uart.1", NULL),
@@ -52,6 +51,14 @@ static void __init pxa910_dt_init(void)
 			     pxa910_auxdata_lookup, NULL);
 }
 
+static void __init mmp_dt_init_timer(void)
+{
+	/* TODO: clock framework initialization. */
+
+	/* Initialize timers. */
+	clocksource_of_init();
+}
+
 static const char *mmp_dt_board_compat[] __initdata = {
 	"mrvl,pxa168-aspenite",
 	"mrvl,pxa910-dkb",
diff --git a/arch/arm/mach-mmp/mmp2-dt.c b/arch/arm/mach-mmp/mmp2-dt.c
index 023cb45..fa57341 100644
--- a/arch/arm/mach-mmp/mmp2-dt.c
+++ b/arch/arm/mach-mmp/mmp2-dt.c
@@ -12,6 +12,7 @@
 #include <linux/io.h>
 #include <linux/irqchip.h>
 #include <linux/of_platform.h>
+#include <linux/clocksource.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 
@@ -37,6 +38,14 @@ static void __init mmp2_dt_init(void)
 			     mmp2_auxdata_lookup, NULL);
 }
 
+static void __init mmp2_dt_init_timer(void)
+{
+	/* TODO: clock framework initialization. */
+
+	/* Initialize timers. */
+	clocksource_of_init();
+}
+
 static const char *mmp2_dt_board_compat[] __initdata = {
 	"mrvl,mmp2-brownstone",
 	NULL,
@@ -44,7 +53,7 @@ static const char *mmp2_dt_board_compat[] __initdata = {
 
 DT_MACHINE_START(MMP2_DT, "Marvell MMP2 (Device Tree Support)")
 	.map_io		= mmp_map_io,
-	.init_time	= mmp_dt_init_timer,
+	.init_time	= mmp2_dt_init_timer,
 	.init_machine	= mmp2_dt_init,
 	.dt_compat	= mmp2_dt_board_compat,
 MACHINE_END
-- 
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] 10+ messages in thread

* [PATCH V2 3/4] arm: mmp: make SOCes with DT use new timer driver
@ 2014-04-17  7:30     ` Chao Xie
  0 siblings, 0 replies; 10+ messages in thread
From: Chao Xie @ 2014-04-17  7:30 UTC (permalink / raw)
  To: linux-arm-kernel

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   | 22 +++++++++++++++++++---
 arch/arm/boot/dts/pxa168.dtsi | 29 +++++++++++++++++++----------
 arch/arm/boot/dts/pxa910.dtsi | 29 +++++++++++++++++++----------
 arch/arm/mach-mmp/Kconfig     |  2 ++
 arch/arm/mach-mmp/mmp-dt.c    | 11 +++++++++--
 arch/arm/mach-mmp/mmp2-dt.c   | 11 ++++++++++-
 6 files changed, 78 insertions(+), 26 deletions(-)

diff --git a/arch/arm/boot/dts/mmp2.dtsi b/arch/arm/boot/dts/mmp2.dtsi
index 4e8b08c..bd0872d 100644
--- a/arch/arm/boot/dts/mmp2.dtsi
+++ b/arch/arm/boot/dts/mmp2.dtsi
@@ -126,9 +126,25 @@
 			ranges;
 
 			timer0: timer at d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				marvell,timer-fastclk-frequency = <6500000>;
+				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>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+					marvell,timer-counter-frequency = <32768>;
+                                };
 			};
 
 			uart1: uart at d4030000 {
diff --git a/arch/arm/boot/dts/pxa168.dtsi b/arch/arm/boot/dts/pxa168.dtsi
index 975dad2..422505d 100644
--- a/arch/arm/boot/dts/pxa168.dtsi
+++ b/arch/arm/boot/dts/pxa168.dtsi
@@ -50,16 +50,25 @@
 			ranges;
 
 			timer0: timer at d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
-			};
-
-			uart1: uart at d4017000 {
-				compatible = "mrvl,mmp-uart";
-				reg = <0xd4017000 0x1000>;
-				interrupts = <27>;
-				status = "disabled";
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				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>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+					marvell,timer-counter-frequency = <32768>;
+                                };
 			};
 
 			uart2: uart at d4018000 {
diff --git a/arch/arm/boot/dts/pxa910.dtsi b/arch/arm/boot/dts/pxa910.dtsi
index 0247c62..173ec16 100644
--- a/arch/arm/boot/dts/pxa910.dtsi
+++ b/arch/arm/boot/dts/pxa910.dtsi
@@ -55,16 +55,25 @@
 			ranges;
 
 			timer0: timer at d4014000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4014000 0x100>;
-				interrupts = <13>;
-			};
-
-			timer1: timer at d4016000 {
-				compatible = "mrvl,mmp-timer";
-				reg = <0xd4016000 0x100>;
-				interrupts = <29>;
-				status = "disabled";
+				compatible = "marvell,mmp-timer";
+				reg = <0xd4014000 0xc8>;
+				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>;
+                                };
+
+                                counter1 {
+					compatible = "marvell,timer-counter-clksrc";
+					marvell,timer-counter-id = <1>;
+					marvell,timer-counter-frequency = <32768>;
+                                };
 			};
 
 			uart1: uart at d4017000 {
diff --git a/arch/arm/mach-mmp/Kconfig b/arch/arm/mach-mmp/Kconfig
index ebdba87..8828f61 100644
--- a/arch/arm/mach-mmp/Kconfig
+++ b/arch/arm/mach-mmp/Kconfig
@@ -89,6 +89,7 @@ config MACH_MMP_DT
 	select CPU_PXA168
 	select CPU_PXA910
 	select USE_OF
+	select CLKSRC_OF
 	select PINCTRL
 	select PINCTRL_SINGLE
 	help
@@ -101,6 +102,7 @@ config MACH_MMP2_DT
 	depends on !CPU_MOHAWK
 	select CPU_MMP2
 	select USE_OF
+	select CLKSRC_OF
 	select PINCTRL
 	select PINCTRL_SINGLE
 	help
diff --git a/arch/arm/mach-mmp/mmp-dt.c b/arch/arm/mach-mmp/mmp-dt.c
index cca529c..c729e46 100644
--- a/arch/arm/mach-mmp/mmp-dt.c
+++ b/arch/arm/mach-mmp/mmp-dt.c
@@ -11,13 +11,12 @@
 
 #include <linux/irqchip.h>
 #include <linux/of_platform.h>
+#include <linux/clocksource.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 
 #include "common.h"
 
-extern void __init mmp_dt_init_timer(void);
-
 static const struct of_dev_auxdata pxa168_auxdata_lookup[] __initconst = {
 	OF_DEV_AUXDATA("mrvl,mmp-uart", 0xd4017000, "pxa2xx-uart.0", NULL),
 	OF_DEV_AUXDATA("mrvl,mmp-uart", 0xd4018000, "pxa2xx-uart.1", NULL),
@@ -52,6 +51,14 @@ static void __init pxa910_dt_init(void)
 			     pxa910_auxdata_lookup, NULL);
 }
 
+static void __init mmp_dt_init_timer(void)
+{
+	/* TODO: clock framework initialization. */
+
+	/* Initialize timers. */
+	clocksource_of_init();
+}
+
 static const char *mmp_dt_board_compat[] __initdata = {
 	"mrvl,pxa168-aspenite",
 	"mrvl,pxa910-dkb",
diff --git a/arch/arm/mach-mmp/mmp2-dt.c b/arch/arm/mach-mmp/mmp2-dt.c
index 023cb45..fa57341 100644
--- a/arch/arm/mach-mmp/mmp2-dt.c
+++ b/arch/arm/mach-mmp/mmp2-dt.c
@@ -12,6 +12,7 @@
 #include <linux/io.h>
 #include <linux/irqchip.h>
 #include <linux/of_platform.h>
+#include <linux/clocksource.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 
@@ -37,6 +38,14 @@ static void __init mmp2_dt_init(void)
 			     mmp2_auxdata_lookup, NULL);
 }
 
+static void __init mmp2_dt_init_timer(void)
+{
+	/* TODO: clock framework initialization. */
+
+	/* Initialize timers. */
+	clocksource_of_init();
+}
+
 static const char *mmp2_dt_board_compat[] __initdata = {
 	"mrvl,mmp2-brownstone",
 	NULL,
@@ -44,7 +53,7 @@ static const char *mmp2_dt_board_compat[] __initdata = {
 
 DT_MACHINE_START(MMP2_DT, "Marvell MMP2 (Device Tree Support)")
 	.map_io		= mmp_map_io,
-	.init_time	= mmp_dt_init_timer,
+	.init_time	= mmp2_dt_init_timer,
 	.init_machine	= mmp2_dt_init,
 	.dt_compat	= mmp2_dt_board_compat,
 MACHINE_END
-- 
1.8.3.2

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

* [PATCH V2 4/4] arm: mmp: remove the old timer driver
  2014-04-17  7:30 ` Chao Xie
@ 2014-04-17  7:30     ` Chao Xie
  -1 siblings, 0 replies; 10+ messages in thread
From: Chao Xie @ 2014-04-17  7:30 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	haojian.zhuang-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	chao.xie-eYqpPyKDWXRBDgjK7y7TUQ

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] 10+ messages in thread

* [PATCH V2 4/4] arm: mmp: remove the old timer driver
@ 2014-04-17  7:30     ` Chao Xie
  0 siblings, 0 replies; 10+ messages in thread
From: Chao Xie @ 2014-04-17  7:30 UTC (permalink / raw)
  To: linux-arm-kernel

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] 10+ messages in thread

end of thread, other threads:[~2014-04-17  7:30 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-17  7:30 [PATCH V2 0/4] arm: mmp: timer driver Chao Xie
2014-04-17  7:30 ` Chao Xie
     [not found] ` <1397719831-23677-1-git-send-email-chao.xie-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
2014-04-17  7:30   ` [PATCH V2 1/4] clocksource: mmp: add mmp " Chao Xie
2014-04-17  7:30     ` Chao Xie
2014-04-17  7:30   ` [PATCH V2 2/4] arm: mmp: make SOCes without DT use new " Chao Xie
2014-04-17  7:30     ` Chao Xie
2014-04-17  7:30   ` [PATCH V2 3/4] arm: mmp: make SOCes with " Chao Xie
2014-04-17  7:30     ` Chao Xie
2014-04-17  7:30   ` [PATCH V2 4/4] arm: mmp: remove the old " Chao Xie
2014-04-17  7:30     ` 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.