All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/10] arm: zynq: Use standard timer binding
@ 2013-03-25 13:53 Michal Simek
  2013-03-25 13:53 ` [PATCH 02/10] arm: zynq: Move timer to clocksource interface Michal Simek
                   ` (8 more replies)
  0 siblings, 9 replies; 32+ messages in thread
From: Michal Simek @ 2013-03-25 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

Use standard dt description for zynq timer
and also prepare for moving to clocksource
initialization.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
Josh: We have talked about this change at ELC.
Using standard dt binding as other timers.

I have also discussed this with Rob some time ago.
https://patchwork.kernel.org/patch/2112791/
---
 arch/arm/boot/dts/zynq-7000.dtsi |   41 +-----
 arch/arm/boot/dts/zynq-zc702.dts |   10 --
 arch/arm/mach-zynq/common.c      |    1 +
 arch/arm/mach-zynq/timer.c       |  261 +++++++++++++++++++++++++++-----------
 4 files changed, 194 insertions(+), 119 deletions(-)

diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
index 5914b56..2a72339 100644
--- a/arch/arm/boot/dts/zynq-7000.dtsi
+++ b/arch/arm/boot/dts/zynq-7000.dtsi
@@ -111,56 +111,23 @@
 		};
 
 		ttc0: ttc0 at f8001000 {
-			#address-cells = <1>;
-			#size-cells = <0>;
+			interrupt-parent = <&intc>;
+			interrupts = < 0 10 4 0 11 4 0 12 4 >;
 			compatible = "xlnx,ttc";
 			reg = <0xF8001000 0x1000>;
 			clocks = <&cpu_clk 3>;
 			clock-names = "cpu_1x";
 			clock-ranges;
-
-			ttc0_0: ttc0.0 {
-				status = "disabled";
-				reg = <0>;
-				interrupts = <0 10 4>;
-			};
-			ttc0_1: ttc0.1 {
-				status = "disabled";
-				reg = <1>;
-				interrupts = <0 11 4>;
-			};
-			ttc0_2: ttc0.2 {
-				status = "disabled";
-				reg = <2>;
-				interrupts = <0 12 4>;
-			};
 		};
 
 		ttc1: ttc1 at f8002000 {
-			#interrupt-parent = <&intc>;
-			#address-cells = <1>;
-			#size-cells = <0>;
+			interrupt-parent = <&intc>;
+			interrupts = < 0 37 4 0 38 4 0 39 4 >;
 			compatible = "xlnx,ttc";
 			reg = <0xF8002000 0x1000>;
 			clocks = <&cpu_clk 3>;
 			clock-names = "cpu_1x";
 			clock-ranges;
-
-			ttc1_0: ttc1.0 {
-				status = "disabled";
-				reg = <0>;
-				interrupts = <0 37 4>;
-			};
-			ttc1_1: ttc1.1 {
-				status = "disabled";
-				reg = <1>;
-				interrupts = <0 38 4>;
-			};
-			ttc1_2: ttc1.2 {
-				status = "disabled";
-				reg = <2>;
-				interrupts = <0 39 4>;
-			};
 		};
 	};
 };
diff --git a/arch/arm/boot/dts/zynq-zc702.dts b/arch/arm/boot/dts/zynq-zc702.dts
index c772942..86f44d5 100644
--- a/arch/arm/boot/dts/zynq-zc702.dts
+++ b/arch/arm/boot/dts/zynq-zc702.dts
@@ -32,13 +32,3 @@
 &ps_clk {
 	clock-frequency = <33333330>;
 };
-
-&ttc0_0 {
-	status = "ok";
-	compatible = "xlnx,ttc-counter-clocksource";
-};
-
-&ttc0_1 {
-	status = "ok";
-	compatible = "xlnx,ttc-counter-clockevent";
-};
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 5c89832..76493b0 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -20,6 +20,7 @@
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/clk/zynq.h>
+#include <linux/clocksource.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c
index f9fbc9c..896ba3e 100644
--- a/arch/arm/mach-zynq/timer.c
+++ b/arch/arm/mach-zynq/timer.c
@@ -15,6 +15,7 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/clk.h>
 #include <linux/interrupt.h>
 #include <linux/clockchips.h>
 #include <linux/of_address.h>
@@ -24,6 +25,21 @@
 #include "common.h"
 
 /*
+ * This driver configures the 2 16-bit count-up timers as follows:
+ *
+ * T1: Timer 1, clocksource for generic timekeeping
+ * T2: Timer 2, clockevent source for hrtimers
+ * T3: Timer 3, <unused>
+ *
+ * The input frequency to the timer module for emulation is 2.5MHz which is
+ * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
+ * the timers are clocked at 78.125KHz (12.8 us resolution).
+
+ * The input frequency to the timer module in silicon is configurable and
+ * obtained from device tree. The pre-scaler of 32 is used.
+ */
+
+/*
  * Timer Register Offset Definitions of Timer 1, Increment base address by 4
  * and use same offsets for Timer 2
  */
@@ -44,17 +60,24 @@
 #define PRESCALE		2048	/* The exponent must match this */
 #define CLK_CNTRL_PRESCALE	((PRESCALE_EXPONENT - 1) << 1)
 #define CLK_CNTRL_PRESCALE_EN	1
-#define CNT_CNTRL_RESET		(1<<4)
+#define CNT_CNTRL_RESET		(1 << 4)
 
 /**
  * struct xttcps_timer - This definition defines local timer structure
  *
  * @base_addr:	Base address of timer
- **/
+ * @clk:	Associated clock source
+ * @clk_rate_change_nb	Notifier block for clock rate changes
+ */
 struct xttcps_timer {
-	void __iomem	*base_addr;
+	void __iomem *base_addr;
+	struct clk *clk;
+	struct notifier_block clk_rate_change_nb;
 };
 
+#define to_xttcps_timer(x) \
+		container_of(x, struct xttcps_timer, clk_rate_change_nb)
+
 struct xttcps_timer_clocksource {
 	struct xttcps_timer	xttc;
 	struct clocksource	cs;
@@ -66,7 +89,6 @@ struct xttcps_timer_clocksource {
 struct xttcps_timer_clockevent {
 	struct xttcps_timer		xttc;
 	struct clock_event_device	ce;
-	struct clk			*clk;
 };
 
 #define to_xttcps_timer_clkevent(x) \
@@ -167,8 +189,8 @@ static void xttcps_set_mode(enum clock_event_mode mode,
 	switch (mode) {
 	case CLOCK_EVT_MODE_PERIODIC:
 		xttcps_set_interval(timer,
-				     DIV_ROUND_CLOSEST(clk_get_rate(xttce->clk),
-						       PRESCALE * HZ));
+				DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk),
+					PRESCALE * HZ));
 		break;
 	case CLOCK_EVT_MODE_ONESHOT:
 	case CLOCK_EVT_MODE_UNUSED:
@@ -189,79 +211,149 @@ static void xttcps_set_mode(enum clock_event_mode mode,
 	}
 }
 
-static void __init zynq_ttc_setup_clocksource(struct device_node *np,
-					     void __iomem *base)
+static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb,
+		unsigned long event, void *data)
+{
+	struct clk_notifier_data *ndata = data;
+	struct xttcps_timer *xttcps = to_xttcps_timer(nb);
+	struct xttcps_timer_clocksource *xttccs = container_of(xttcps,
+			struct xttcps_timer_clocksource, xttc);
+
+	switch (event) {
+	case POST_RATE_CHANGE:
+		/*
+		 * Do whatever is necessare to maintain a proper time base
+		 *
+		 * I cannot find a way to adjust the currently used clocksource
+		 * to the new frequency. __clocksource_updatefreq_hz() sounds
+		 * good, but does not work. Not sure what's that missing.
+		 *
+		 * This approach works, but triggers two clocksource switches.
+		 * The first after unregister to clocksource jiffies. And
+		 * another one after the register to the newly registered timer.
+		 *
+		 * Alternatively we could 'waste' another HW timer to ping pong
+		 * between clock sources. That would also use one register and
+		 * one unregister call, but only trigger one clocksource switch
+		 * for the cost of another HW timer used by the OS.
+		 */
+		clocksource_unregister(&xttccs->cs);
+		clocksource_register_hz(&xttccs->cs,
+				ndata->new_rate / PRESCALE);
+		/* fall through */
+	case PRE_RATE_CHANGE:
+	case ABORT_RATE_CHANGE:
+	default:
+		return NOTIFY_DONE;
+	}
+}
+
+static void __init zynq_ttc_setup_clocksource(struct clk *clk,
+							void __iomem *base)
 {
 	struct xttcps_timer_clocksource *ttccs;
-	struct clk *clk;
 	int err;
-	u32 reg;
 
 	ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
 	if (WARN_ON(!ttccs))
 		return;
 
-	err = of_property_read_u32(np, "reg", &reg);
-	if (WARN_ON(err))
-		return;
+	ttccs->xttc.clk = clk;
 
-	clk = of_clk_get_by_name(np, "cpu_1x");
-	if (WARN_ON(IS_ERR(clk)))
-		return;
-
-	err = clk_prepare_enable(clk);
+	err = clk_prepare_enable(ttccs->xttc.clk);
 	if (WARN_ON(err))
 		return;
 
-	ttccs->xttc.base_addr = base + reg * 4;
+	ttccs->xttc.clk_rate_change_nb.notifier_call =
+		xttcps_rate_change_clocksource_cb;
+	ttccs->xttc.clk_rate_change_nb.next = NULL;
+	if (clk_notifier_register(ttccs->xttc.clk,
+				&ttccs->xttc.clk_rate_change_nb))
+		pr_warn("Unable to register clock notifier.\n");
 
-	ttccs->cs.name = np->name;
+	ttccs->xttc.base_addr = base;
+	ttccs->cs.name = "xttcps_clocksource";
 	ttccs->cs.rating = 200;
 	ttccs->cs.read = __xttc_clocksource_read;
 	ttccs->cs.mask = CLOCKSOURCE_MASK(16);
 	ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
 
+	/*
+	 * Setup the clock source counter to be an incrementing counter
+	 * with no interrupt and it rolls over at 0xFFFF. Pre-scale
+	 * it by 32 also. Let it start running now.
+	 */
 	__raw_writel(0x0,  ttccs->xttc.base_addr + XTTCPS_IER_OFFSET);
 	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
 		     ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
 	__raw_writel(CNT_CNTRL_RESET,
 		     ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
 
-	err = clocksource_register_hz(&ttccs->cs, clk_get_rate(clk) / PRESCALE);
+	err = clocksource_register_hz(&ttccs->cs,
+			clk_get_rate(ttccs->xttc.clk) / PRESCALE);
 	if (WARN_ON(err))
 		return;
+
 }
 
-static void __init zynq_ttc_setup_clockevent(struct device_node *np,
-					    void __iomem *base)
+static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
+		unsigned long event, void *data)
+{
+	struct clk_notifier_data *ndata = data;
+	struct xttcps_timer *xttcps = to_xttcps_timer(nb);
+	struct xttcps_timer_clockevent *xttcce = container_of(xttcps,
+			struct xttcps_timer_clockevent, xttc);
+
+	switch (event) {
+	case POST_RATE_CHANGE:
+	{
+		unsigned long flags;
+
+		/*
+		 * clockevents_update_freq should be called with IRQ disabled on
+		 * the CPU the timer provides events for. The timer we use is
+		 * common to both CPUs, not sure if we need to run on both
+		 * cores.
+		 */
+		local_irq_save(flags);
+		clockevents_update_freq(&xttcce->ce,
+				ndata->new_rate / PRESCALE);
+		local_irq_restore(flags);
+
+		/* fall through */
+	}
+	case PRE_RATE_CHANGE:
+	case ABORT_RATE_CHANGE:
+	default:
+		return NOTIFY_DONE;
+	}
+}
+
+static void __init zynq_ttc_setup_clockevent(struct clk *clk,
+						void __iomem *base, u32 irq)
 {
 	struct xttcps_timer_clockevent *ttcce;
-	int err, irq;
-	u32 reg;
+	int err;
 
 	ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
 	if (WARN_ON(!ttcce))
 		return;
 
-	err = of_property_read_u32(np, "reg", &reg);
-	if (WARN_ON(err))
-		return;
+	ttcce->xttc.clk = clk;
 
-	ttcce->xttc.base_addr = base + reg * 4;
-
-	ttcce->clk = of_clk_get_by_name(np, "cpu_1x");
-	if (WARN_ON(IS_ERR(ttcce->clk)))
-		return;
-
-	err = clk_prepare_enable(ttcce->clk);
+	err = clk_prepare_enable(ttcce->xttc.clk);
 	if (WARN_ON(err))
 		return;
 
-	irq = irq_of_parse_and_map(np, 0);
-	if (WARN_ON(!irq))
-		return;
+	ttcce->xttc.clk_rate_change_nb.notifier_call =
+		xttcps_rate_change_clockevent_cb;
+	ttcce->xttc.clk_rate_change_nb.next = NULL;
+	if (clk_notifier_register(ttcce->xttc.clk,
+				&ttcce->xttc.clk_rate_change_nb))
+		pr_warn("Unable to register clock notifier.\n");
 
-	ttcce->ce.name = np->name;
+	ttcce->xttc.base_addr = base;
+	ttcce->ce.name = "xttcps_clockevent";
 	ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
 	ttcce->ce.set_next_event = xttcps_set_next_event;
 	ttcce->ce.set_mode = xttcps_set_mode;
@@ -269,56 +361,81 @@ static void __init zynq_ttc_setup_clockevent(struct device_node *np,
 	ttcce->ce.irq = irq;
 	ttcce->ce.cpumask = cpu_possible_mask;
 
+	/*
+	 * Setup the clock event timer to be an interval timer which
+	 * is prescaled by 32 using the interval interrupt. Leave it
+	 * disabled for now.
+	 */
 	__raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
 	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
 		     ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
 	__raw_writel(0x1,  ttcce->xttc.base_addr + XTTCPS_IER_OFFSET);
 
-	err = request_irq(irq, xttcps_clock_event_interrupt, IRQF_TIMER,
-			  np->name, ttcce);
+	err = request_irq(irq, xttcps_clock_event_interrupt,
+			  IRQF_DISABLED | IRQF_TIMER,
+			  ttcce->ce.name, ttcce);
 	if (WARN_ON(err))
 		return;
 
 	clockevents_config_and_register(&ttcce->ce,
-					clk_get_rate(ttcce->clk) / PRESCALE,
-					1, 0xfffe);
+			clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe);
 }
 
-static const __initconst struct of_device_id zynq_ttc_match[] = {
-	{ .compatible = "xlnx,ttc-counter-clocksource",
-		.data = zynq_ttc_setup_clocksource, },
-	{ .compatible = "xlnx,ttc-counter-clockevent",
-		.data = zynq_ttc_setup_clockevent, },
-	{}
-};
-
 /**
  * xttcps_timer_init - Initialize the timer
  *
  * Initializes the timer hardware and register the clock source and clock event
  * timers with Linux kernal timer framework
- **/
+ */
+static void __init xttcps_timer_init_of(struct device_node *timer)
+{
+	unsigned int irq;
+	void __iomem *timer_baseaddr;
+	struct clk *clk;
+
+	/*
+	 * Get the 1st Triple Timer Counter (TTC) block from the device tree
+	 * and use it. Note that the event timer uses the interrupt and it's the
+	 * 2nd TTC hence the irq_of_parse_and_map(,1)
+	 */
+	timer_baseaddr = of_iomap(timer, 0);
+	if (!timer_baseaddr) {
+		pr_err("ERROR: invalid timer base address\n");
+		BUG();
+	}
+
+	irq = irq_of_parse_and_map(timer, 1);
+	if (irq <= 0) {
+		pr_err("ERROR: invalid interrupt number\n");
+		BUG();
+	}
+
+	clk = of_clk_get_by_name(timer, "cpu_1x");
+	if (IS_ERR(clk)) {
+		pr_err("ERROR: timer input clock not found\n");
+		BUG();
+	}
+
+	zynq_ttc_setup_clocksource(clk, timer_baseaddr);
+	zynq_ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
+
+	pr_info("%s #0@%p, irq=%d\n", timer->name, timer_baseaddr, irq);
+}
+
+
 void __init xttcps_timer_init(void)
 {
-	struct device_node *np;
-
-	for_each_compatible_node(np, NULL, "xlnx,ttc") {
-		struct device_node *np_chld;
-		void __iomem *base;
-
-		base = of_iomap(np, 0);
-		if (WARN_ON(!base))
-			return;
-
-		for_each_available_child_of_node(np, np_chld) {
-			int (*cb)(struct device_node *np, void __iomem *base);
-			const struct of_device_id *match;
-
-			match = of_match_node(zynq_ttc_match, np_chld);
-			if (match) {
-				cb = match->data;
-				cb(np_chld, base);
-			}
-		}
+	const char * const timer_list[] = {
+		"xlnx,ttc",
+		NULL
+	};
+	struct device_node *timer;
+
+	timer = of_find_compatible_node(NULL, NULL, timer_list[0]);
+	if (!timer) {
+		pr_err("ERROR: no compatible timer found\n");
+		BUG();
 	}
+
+	xttcps_timer_init_of(timer);
 }
-- 
1.7.9.7

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

* [PATCH 02/10] arm: zynq: Move timer to clocksource interface
  2013-03-25 13:53 [PATCH 01/10] arm: zynq: Use standard timer binding Michal Simek
@ 2013-03-25 13:53 ` Michal Simek
  2013-03-25 13:53 ` [PATCH 03/10] arm: zynq: Move timer to generic location Michal Simek
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 32+ messages in thread
From: Michal Simek @ 2013-03-25 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

Use clocksource timer initialization.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
 arch/arm/mach-zynq/common.c |    2 +-
 arch/arm/mach-zynq/common.h |    2 --
 arch/arm/mach-zynq/timer.c  |   27 ++++++++-------------------
 3 files changed, 9 insertions(+), 22 deletions(-)

diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 76493b0..68e0907 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -78,7 +78,7 @@ static void __init xilinx_zynq_timer_init(void)
 
 	xilinx_zynq_clocks_init(slcr);
 
-	xttcps_timer_init();
+	clocksource_of_init();
 }
 
 /**
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index 8b4dbba..5050bb1 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -17,6 +17,4 @@
 #ifndef __MACH_ZYNQ_COMMON_H__
 #define __MACH_ZYNQ_COMMON_H__
 
-void __init xttcps_timer_init(void);
-
 #endif
diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c
index 896ba3e..3b1faed 100644
--- a/arch/arm/mach-zynq/timer.c
+++ b/arch/arm/mach-zynq/timer.c
@@ -22,7 +22,6 @@
 #include <linux/of_irq.h>
 #include <linux/slab.h>
 #include <linux/clk-provider.h>
-#include "common.h"
 
 /*
  * This driver configures the 2 16-bit count-up timers as follows:
@@ -387,11 +386,17 @@ static void __init zynq_ttc_setup_clockevent(struct clk *clk,
  * Initializes the timer hardware and register the clock source and clock event
  * timers with Linux kernal timer framework
  */
-static void __init xttcps_timer_init_of(struct device_node *timer)
+static void __init xttcps_timer_init(struct device_node *timer)
 {
 	unsigned int irq;
 	void __iomem *timer_baseaddr;
 	struct clk *clk;
+	static int initialized;
+
+	if (initialized)
+		return;
+
+	initialized = 1;
 
 	/*
 	 * Get the 1st Triple Timer Counter (TTC) block from the device tree
@@ -422,20 +427,4 @@ static void __init xttcps_timer_init_of(struct device_node *timer)
 	pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
 }
 
-
-void __init xttcps_timer_init(void)
-{
-	const char * const timer_list[] = {
-		"xlnx,ttc",
-		NULL
-	};
-	struct device_node *timer;
-
-	timer = of_find_compatible_node(NULL, NULL, timer_list[0]);
-	if (!timer) {
-		pr_err("ERROR: no compatible timer found\n");
-		BUG();
-	}
-
-	xttcps_timer_init_of(timer);
-}
+CLOCKSOURCE_OF_DECLARE(zynq, "xlnx,ttc", xttcps_timer_init);
-- 
1.7.9.7

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

* [PATCH 03/10] arm: zynq: Move timer to generic location
  2013-03-25 13:53 [PATCH 01/10] arm: zynq: Use standard timer binding Michal Simek
  2013-03-25 13:53 ` [PATCH 02/10] arm: zynq: Move timer to clocksource interface Michal Simek
@ 2013-03-25 13:53 ` Michal Simek
  2013-03-25 16:01   ` Steffen Trumtrar
  2013-03-25 13:53 ` [PATCH 04/10] arm: zynq: Load scu baseaddress at run time Michal Simek
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 32+ messages in thread
From: Michal Simek @ 2013-03-25 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

Move zynq timer out of mach folder to generic location.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
Arnd: This is the last patch as we discuss about moving
this timer to generic location.

It depends on ARCH_ZYNQ as Tegra and others.
It should be ok to use it or we can use different name if you like.
---
 arch/arm/mach-zynq/Makefile      |    2 +-
 arch/arm/mach-zynq/timer.c       |  430 --------------------------------------
 drivers/clocksource/Makefile     |    1 +
 drivers/clocksource/zynq_timer.c |  430 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 432 insertions(+), 431 deletions(-)
 delete mode 100644 arch/arm/mach-zynq/timer.c
 create mode 100644 drivers/clocksource/zynq_timer.c

diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
index 397268c..320faed 100644
--- a/arch/arm/mach-zynq/Makefile
+++ b/arch/arm/mach-zynq/Makefile
@@ -3,4 +3,4 @@
 #
 
 # Common support
-obj-y				:= common.o timer.o
+obj-y				:= common.o
diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c
deleted file mode 100644
index 3b1faed..0000000
--- a/arch/arm/mach-zynq/timer.c
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * This file contains driver for the Xilinx PS Timer Counter IP.
- *
- *  Copyright (C) 2011 Xilinx
- *
- * based on arch/mips/kernel/time.c timer driver
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/clockchips.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/slab.h>
-#include <linux/clk-provider.h>
-
-/*
- * This driver configures the 2 16-bit count-up timers as follows:
- *
- * T1: Timer 1, clocksource for generic timekeeping
- * T2: Timer 2, clockevent source for hrtimers
- * T3: Timer 3, <unused>
- *
- * The input frequency to the timer module for emulation is 2.5MHz which is
- * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
- * the timers are clocked at 78.125KHz (12.8 us resolution).
-
- * The input frequency to the timer module in silicon is configurable and
- * obtained from device tree. The pre-scaler of 32 is used.
- */
-
-/*
- * Timer Register Offset Definitions of Timer 1, Increment base address by 4
- * and use same offsets for Timer 2
- */
-#define XTTCPS_CLK_CNTRL_OFFSET		0x00 /* Clock Control Reg, RW */
-#define XTTCPS_CNT_CNTRL_OFFSET		0x0C /* Counter Control Reg, RW */
-#define XTTCPS_COUNT_VAL_OFFSET		0x18 /* Counter Value Reg, RO */
-#define XTTCPS_INTR_VAL_OFFSET		0x24 /* Interval Count Reg, RW */
-#define XTTCPS_ISR_OFFSET		0x54 /* Interrupt Status Reg, RO */
-#define XTTCPS_IER_OFFSET		0x60 /* Interrupt Enable Reg, RW */
-
-#define XTTCPS_CNT_CNTRL_DISABLE_MASK	0x1
-
-/*
- * Setup the timers to use pre-scaling, using a fixed value for now that will
- * work across most input frequency, but it may need to be more dynamic
- */
-#define PRESCALE_EXPONENT	11	/* 2 ^ PRESCALE_EXPONENT = PRESCALE */
-#define PRESCALE		2048	/* The exponent must match this */
-#define CLK_CNTRL_PRESCALE	((PRESCALE_EXPONENT - 1) << 1)
-#define CLK_CNTRL_PRESCALE_EN	1
-#define CNT_CNTRL_RESET		(1 << 4)
-
-/**
- * struct xttcps_timer - This definition defines local timer structure
- *
- * @base_addr:	Base address of timer
- * @clk:	Associated clock source
- * @clk_rate_change_nb	Notifier block for clock rate changes
- */
-struct xttcps_timer {
-	void __iomem *base_addr;
-	struct clk *clk;
-	struct notifier_block clk_rate_change_nb;
-};
-
-#define to_xttcps_timer(x) \
-		container_of(x, struct xttcps_timer, clk_rate_change_nb)
-
-struct xttcps_timer_clocksource {
-	struct xttcps_timer	xttc;
-	struct clocksource	cs;
-};
-
-#define to_xttcps_timer_clksrc(x) \
-		container_of(x, struct xttcps_timer_clocksource, cs)
-
-struct xttcps_timer_clockevent {
-	struct xttcps_timer		xttc;
-	struct clock_event_device	ce;
-};
-
-#define to_xttcps_timer_clkevent(x) \
-		container_of(x, struct xttcps_timer_clockevent, ce)
-
-/**
- * xttcps_set_interval - Set the timer interval value
- *
- * @timer:	Pointer to the timer instance
- * @cycles:	Timer interval ticks
- **/
-static void xttcps_set_interval(struct xttcps_timer *timer,
-					unsigned long cycles)
-{
-	u32 ctrl_reg;
-
-	/* Disable the counter, set the counter value  and re-enable counter */
-	ctrl_reg = __raw_readl(timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
-	ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
-	__raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
-
-	__raw_writel(cycles, timer->base_addr + XTTCPS_INTR_VAL_OFFSET);
-
-	/*
-	 * Reset the counter (0x10) so that it starts from 0, one-shot
-	 * mode makes this needed for timing to be right.
-	 */
-	ctrl_reg |= CNT_CNTRL_RESET;
-	ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
-	__raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
-}
-
-/**
- * xttcps_clock_event_interrupt - Clock event timer interrupt handler
- *
- * @irq:	IRQ number of the Timer
- * @dev_id:	void pointer to the xttcps_timer instance
- *
- * returns: Always IRQ_HANDLED - success
- **/
-static irqreturn_t xttcps_clock_event_interrupt(int irq, void *dev_id)
-{
-	struct xttcps_timer_clockevent *xttce = dev_id;
-	struct xttcps_timer *timer = &xttce->xttc;
-
-	/* Acknowledge the interrupt and call event handler */
-	__raw_readl(timer->base_addr + XTTCPS_ISR_OFFSET);
-
-	xttce->ce.event_handler(&xttce->ce);
-
-	return IRQ_HANDLED;
-}
-
-/**
- * __xttc_clocksource_read - Reads the timer counter register
- *
- * returns: Current timer counter register value
- **/
-static cycle_t __xttc_clocksource_read(struct clocksource *cs)
-{
-	struct xttcps_timer *timer = &to_xttcps_timer_clksrc(cs)->xttc;
-
-	return (cycle_t)__raw_readl(timer->base_addr +
-				XTTCPS_COUNT_VAL_OFFSET);
-}
-
-/**
- * xttcps_set_next_event - Sets the time interval for next event
- *
- * @cycles:	Timer interval ticks
- * @evt:	Address of clock event instance
- *
- * returns: Always 0 - success
- **/
-static int xttcps_set_next_event(unsigned long cycles,
-					struct clock_event_device *evt)
-{
-	struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
-	struct xttcps_timer *timer = &xttce->xttc;
-
-	xttcps_set_interval(timer, cycles);
-	return 0;
-}
-
-/**
- * xttcps_set_mode - Sets the mode of timer
- *
- * @mode:	Mode to be set
- * @evt:	Address of clock event instance
- **/
-static void xttcps_set_mode(enum clock_event_mode mode,
-					struct clock_event_device *evt)
-{
-	struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
-	struct xttcps_timer *timer = &xttce->xttc;
-	u32 ctrl_reg;
-
-	switch (mode) {
-	case CLOCK_EVT_MODE_PERIODIC:
-		xttcps_set_interval(timer,
-				DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk),
-					PRESCALE * HZ));
-		break;
-	case CLOCK_EVT_MODE_ONESHOT:
-	case CLOCK_EVT_MODE_UNUSED:
-	case CLOCK_EVT_MODE_SHUTDOWN:
-		ctrl_reg = __raw_readl(timer->base_addr +
-					XTTCPS_CNT_CNTRL_OFFSET);
-		ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
-		__raw_writel(ctrl_reg,
-				timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
-		break;
-	case CLOCK_EVT_MODE_RESUME:
-		ctrl_reg = __raw_readl(timer->base_addr +
-					XTTCPS_CNT_CNTRL_OFFSET);
-		ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
-		__raw_writel(ctrl_reg,
-				timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
-		break;
-	}
-}
-
-static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb,
-		unsigned long event, void *data)
-{
-	struct clk_notifier_data *ndata = data;
-	struct xttcps_timer *xttcps = to_xttcps_timer(nb);
-	struct xttcps_timer_clocksource *xttccs = container_of(xttcps,
-			struct xttcps_timer_clocksource, xttc);
-
-	switch (event) {
-	case POST_RATE_CHANGE:
-		/*
-		 * Do whatever is necessare to maintain a proper time base
-		 *
-		 * I cannot find a way to adjust the currently used clocksource
-		 * to the new frequency. __clocksource_updatefreq_hz() sounds
-		 * good, but does not work. Not sure what's that missing.
-		 *
-		 * This approach works, but triggers two clocksource switches.
-		 * The first after unregister to clocksource jiffies. And
-		 * another one after the register to the newly registered timer.
-		 *
-		 * Alternatively we could 'waste' another HW timer to ping pong
-		 * between clock sources. That would also use one register and
-		 * one unregister call, but only trigger one clocksource switch
-		 * for the cost of another HW timer used by the OS.
-		 */
-		clocksource_unregister(&xttccs->cs);
-		clocksource_register_hz(&xttccs->cs,
-				ndata->new_rate / PRESCALE);
-		/* fall through */
-	case PRE_RATE_CHANGE:
-	case ABORT_RATE_CHANGE:
-	default:
-		return NOTIFY_DONE;
-	}
-}
-
-static void __init zynq_ttc_setup_clocksource(struct clk *clk,
-							void __iomem *base)
-{
-	struct xttcps_timer_clocksource *ttccs;
-	int err;
-
-	ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
-	if (WARN_ON(!ttccs))
-		return;
-
-	ttccs->xttc.clk = clk;
-
-	err = clk_prepare_enable(ttccs->xttc.clk);
-	if (WARN_ON(err))
-		return;
-
-	ttccs->xttc.clk_rate_change_nb.notifier_call =
-		xttcps_rate_change_clocksource_cb;
-	ttccs->xttc.clk_rate_change_nb.next = NULL;
-	if (clk_notifier_register(ttccs->xttc.clk,
-				&ttccs->xttc.clk_rate_change_nb))
-		pr_warn("Unable to register clock notifier.\n");
-
-	ttccs->xttc.base_addr = base;
-	ttccs->cs.name = "xttcps_clocksource";
-	ttccs->cs.rating = 200;
-	ttccs->cs.read = __xttc_clocksource_read;
-	ttccs->cs.mask = CLOCKSOURCE_MASK(16);
-	ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
-
-	/*
-	 * Setup the clock source counter to be an incrementing counter
-	 * with no interrupt and it rolls over at 0xFFFF. Pre-scale
-	 * it by 32 also. Let it start running now.
-	 */
-	__raw_writel(0x0,  ttccs->xttc.base_addr + XTTCPS_IER_OFFSET);
-	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
-		     ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
-	__raw_writel(CNT_CNTRL_RESET,
-		     ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
-
-	err = clocksource_register_hz(&ttccs->cs,
-			clk_get_rate(ttccs->xttc.clk) / PRESCALE);
-	if (WARN_ON(err))
-		return;
-
-}
-
-static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
-		unsigned long event, void *data)
-{
-	struct clk_notifier_data *ndata = data;
-	struct xttcps_timer *xttcps = to_xttcps_timer(nb);
-	struct xttcps_timer_clockevent *xttcce = container_of(xttcps,
-			struct xttcps_timer_clockevent, xttc);
-
-	switch (event) {
-	case POST_RATE_CHANGE:
-	{
-		unsigned long flags;
-
-		/*
-		 * clockevents_update_freq should be called with IRQ disabled on
-		 * the CPU the timer provides events for. The timer we use is
-		 * common to both CPUs, not sure if we need to run on both
-		 * cores.
-		 */
-		local_irq_save(flags);
-		clockevents_update_freq(&xttcce->ce,
-				ndata->new_rate / PRESCALE);
-		local_irq_restore(flags);
-
-		/* fall through */
-	}
-	case PRE_RATE_CHANGE:
-	case ABORT_RATE_CHANGE:
-	default:
-		return NOTIFY_DONE;
-	}
-}
-
-static void __init zynq_ttc_setup_clockevent(struct clk *clk,
-						void __iomem *base, u32 irq)
-{
-	struct xttcps_timer_clockevent *ttcce;
-	int err;
-
-	ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
-	if (WARN_ON(!ttcce))
-		return;
-
-	ttcce->xttc.clk = clk;
-
-	err = clk_prepare_enable(ttcce->xttc.clk);
-	if (WARN_ON(err))
-		return;
-
-	ttcce->xttc.clk_rate_change_nb.notifier_call =
-		xttcps_rate_change_clockevent_cb;
-	ttcce->xttc.clk_rate_change_nb.next = NULL;
-	if (clk_notifier_register(ttcce->xttc.clk,
-				&ttcce->xttc.clk_rate_change_nb))
-		pr_warn("Unable to register clock notifier.\n");
-
-	ttcce->xttc.base_addr = base;
-	ttcce->ce.name = "xttcps_clockevent";
-	ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
-	ttcce->ce.set_next_event = xttcps_set_next_event;
-	ttcce->ce.set_mode = xttcps_set_mode;
-	ttcce->ce.rating = 200;
-	ttcce->ce.irq = irq;
-	ttcce->ce.cpumask = cpu_possible_mask;
-
-	/*
-	 * Setup the clock event timer to be an interval timer which
-	 * is prescaled by 32 using the interval interrupt. Leave it
-	 * disabled for now.
-	 */
-	__raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
-	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
-		     ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
-	__raw_writel(0x1,  ttcce->xttc.base_addr + XTTCPS_IER_OFFSET);
-
-	err = request_irq(irq, xttcps_clock_event_interrupt,
-			  IRQF_DISABLED | IRQF_TIMER,
-			  ttcce->ce.name, ttcce);
-	if (WARN_ON(err))
-		return;
-
-	clockevents_config_and_register(&ttcce->ce,
-			clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe);
-}
-
-/**
- * xttcps_timer_init - Initialize the timer
- *
- * Initializes the timer hardware and register the clock source and clock event
- * timers with Linux kernal timer framework
- */
-static void __init xttcps_timer_init(struct device_node *timer)
-{
-	unsigned int irq;
-	void __iomem *timer_baseaddr;
-	struct clk *clk;
-	static int initialized;
-
-	if (initialized)
-		return;
-
-	initialized = 1;
-
-	/*
-	 * Get the 1st Triple Timer Counter (TTC) block from the device tree
-	 * and use it. Note that the event timer uses the interrupt and it's the
-	 * 2nd TTC hence the irq_of_parse_and_map(,1)
-	 */
-	timer_baseaddr = of_iomap(timer, 0);
-	if (!timer_baseaddr) {
-		pr_err("ERROR: invalid timer base address\n");
-		BUG();
-	}
-
-	irq = irq_of_parse_and_map(timer, 1);
-	if (irq <= 0) {
-		pr_err("ERROR: invalid interrupt number\n");
-		BUG();
-	}
-
-	clk = of_clk_get_by_name(timer, "cpu_1x");
-	if (IS_ERR(clk)) {
-		pr_err("ERROR: timer input clock not found\n");
-		BUG();
-	}
-
-	zynq_ttc_setup_clocksource(clk, timer_baseaddr);
-	zynq_ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
-
-	pr_info("%s #0@%p, irq=%d\n", timer->name, timer_baseaddr, irq);
-}
-
-CLOCKSOURCE_OF_DECLARE(zynq, "xlnx,ttc", xttcps_timer_init);
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 4d8283a..665af7f 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o
 obj-$(CONFIG_SUNXI_TIMER)	+= sunxi_timer.o
 obj-$(CONFIG_ARCH_TEGRA)	+= tegra20_timer.o
 obj-$(CONFIG_VT8500_TIMER)	+= vt8500_timer.o
+obj-$(CONFIG_ARCH_ZYNQ)		+= zynq_timer.o
 
 obj-$(CONFIG_ARM_ARCH_TIMER)		+= arm_arch_timer.o
 obj-$(CONFIG_CLKSRC_METAG_GENERIC)	+= metag_generic.o
diff --git a/drivers/clocksource/zynq_timer.c b/drivers/clocksource/zynq_timer.c
new file mode 100644
index 0000000..3b1faed
--- /dev/null
+++ b/drivers/clocksource/zynq_timer.c
@@ -0,0 +1,430 @@
+/*
+ * This file contains driver for the Xilinx PS Timer Counter IP.
+ *
+ *  Copyright (C) 2011 Xilinx
+ *
+ * based on arch/mips/kernel/time.c timer driver
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+
+/*
+ * This driver configures the 2 16-bit count-up timers as follows:
+ *
+ * T1: Timer 1, clocksource for generic timekeeping
+ * T2: Timer 2, clockevent source for hrtimers
+ * T3: Timer 3, <unused>
+ *
+ * The input frequency to the timer module for emulation is 2.5MHz which is
+ * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
+ * the timers are clocked at 78.125KHz (12.8 us resolution).
+
+ * The input frequency to the timer module in silicon is configurable and
+ * obtained from device tree. The pre-scaler of 32 is used.
+ */
+
+/*
+ * Timer Register Offset Definitions of Timer 1, Increment base address by 4
+ * and use same offsets for Timer 2
+ */
+#define XTTCPS_CLK_CNTRL_OFFSET		0x00 /* Clock Control Reg, RW */
+#define XTTCPS_CNT_CNTRL_OFFSET		0x0C /* Counter Control Reg, RW */
+#define XTTCPS_COUNT_VAL_OFFSET		0x18 /* Counter Value Reg, RO */
+#define XTTCPS_INTR_VAL_OFFSET		0x24 /* Interval Count Reg, RW */
+#define XTTCPS_ISR_OFFSET		0x54 /* Interrupt Status Reg, RO */
+#define XTTCPS_IER_OFFSET		0x60 /* Interrupt Enable Reg, RW */
+
+#define XTTCPS_CNT_CNTRL_DISABLE_MASK	0x1
+
+/*
+ * Setup the timers to use pre-scaling, using a fixed value for now that will
+ * work across most input frequency, but it may need to be more dynamic
+ */
+#define PRESCALE_EXPONENT	11	/* 2 ^ PRESCALE_EXPONENT = PRESCALE */
+#define PRESCALE		2048	/* The exponent must match this */
+#define CLK_CNTRL_PRESCALE	((PRESCALE_EXPONENT - 1) << 1)
+#define CLK_CNTRL_PRESCALE_EN	1
+#define CNT_CNTRL_RESET		(1 << 4)
+
+/**
+ * struct xttcps_timer - This definition defines local timer structure
+ *
+ * @base_addr:	Base address of timer
+ * @clk:	Associated clock source
+ * @clk_rate_change_nb	Notifier block for clock rate changes
+ */
+struct xttcps_timer {
+	void __iomem *base_addr;
+	struct clk *clk;
+	struct notifier_block clk_rate_change_nb;
+};
+
+#define to_xttcps_timer(x) \
+		container_of(x, struct xttcps_timer, clk_rate_change_nb)
+
+struct xttcps_timer_clocksource {
+	struct xttcps_timer	xttc;
+	struct clocksource	cs;
+};
+
+#define to_xttcps_timer_clksrc(x) \
+		container_of(x, struct xttcps_timer_clocksource, cs)
+
+struct xttcps_timer_clockevent {
+	struct xttcps_timer		xttc;
+	struct clock_event_device	ce;
+};
+
+#define to_xttcps_timer_clkevent(x) \
+		container_of(x, struct xttcps_timer_clockevent, ce)
+
+/**
+ * xttcps_set_interval - Set the timer interval value
+ *
+ * @timer:	Pointer to the timer instance
+ * @cycles:	Timer interval ticks
+ **/
+static void xttcps_set_interval(struct xttcps_timer *timer,
+					unsigned long cycles)
+{
+	u32 ctrl_reg;
+
+	/* Disable the counter, set the counter value  and re-enable counter */
+	ctrl_reg = __raw_readl(timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
+	ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
+	__raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
+
+	__raw_writel(cycles, timer->base_addr + XTTCPS_INTR_VAL_OFFSET);
+
+	/*
+	 * Reset the counter (0x10) so that it starts from 0, one-shot
+	 * mode makes this needed for timing to be right.
+	 */
+	ctrl_reg |= CNT_CNTRL_RESET;
+	ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
+	__raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
+}
+
+/**
+ * xttcps_clock_event_interrupt - Clock event timer interrupt handler
+ *
+ * @irq:	IRQ number of the Timer
+ * @dev_id:	void pointer to the xttcps_timer instance
+ *
+ * returns: Always IRQ_HANDLED - success
+ **/
+static irqreturn_t xttcps_clock_event_interrupt(int irq, void *dev_id)
+{
+	struct xttcps_timer_clockevent *xttce = dev_id;
+	struct xttcps_timer *timer = &xttce->xttc;
+
+	/* Acknowledge the interrupt and call event handler */
+	__raw_readl(timer->base_addr + XTTCPS_ISR_OFFSET);
+
+	xttce->ce.event_handler(&xttce->ce);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * __xttc_clocksource_read - Reads the timer counter register
+ *
+ * returns: Current timer counter register value
+ **/
+static cycle_t __xttc_clocksource_read(struct clocksource *cs)
+{
+	struct xttcps_timer *timer = &to_xttcps_timer_clksrc(cs)->xttc;
+
+	return (cycle_t)__raw_readl(timer->base_addr +
+				XTTCPS_COUNT_VAL_OFFSET);
+}
+
+/**
+ * xttcps_set_next_event - Sets the time interval for next event
+ *
+ * @cycles:	Timer interval ticks
+ * @evt:	Address of clock event instance
+ *
+ * returns: Always 0 - success
+ **/
+static int xttcps_set_next_event(unsigned long cycles,
+					struct clock_event_device *evt)
+{
+	struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
+	struct xttcps_timer *timer = &xttce->xttc;
+
+	xttcps_set_interval(timer, cycles);
+	return 0;
+}
+
+/**
+ * xttcps_set_mode - Sets the mode of timer
+ *
+ * @mode:	Mode to be set
+ * @evt:	Address of clock event instance
+ **/
+static void xttcps_set_mode(enum clock_event_mode mode,
+					struct clock_event_device *evt)
+{
+	struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
+	struct xttcps_timer *timer = &xttce->xttc;
+	u32 ctrl_reg;
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		xttcps_set_interval(timer,
+				DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk),
+					PRESCALE * HZ));
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		ctrl_reg = __raw_readl(timer->base_addr +
+					XTTCPS_CNT_CNTRL_OFFSET);
+		ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
+		__raw_writel(ctrl_reg,
+				timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+		ctrl_reg = __raw_readl(timer->base_addr +
+					XTTCPS_CNT_CNTRL_OFFSET);
+		ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
+		__raw_writel(ctrl_reg,
+				timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
+		break;
+	}
+}
+
+static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb,
+		unsigned long event, void *data)
+{
+	struct clk_notifier_data *ndata = data;
+	struct xttcps_timer *xttcps = to_xttcps_timer(nb);
+	struct xttcps_timer_clocksource *xttccs = container_of(xttcps,
+			struct xttcps_timer_clocksource, xttc);
+
+	switch (event) {
+	case POST_RATE_CHANGE:
+		/*
+		 * Do whatever is necessare to maintain a proper time base
+		 *
+		 * I cannot find a way to adjust the currently used clocksource
+		 * to the new frequency. __clocksource_updatefreq_hz() sounds
+		 * good, but does not work. Not sure what's that missing.
+		 *
+		 * This approach works, but triggers two clocksource switches.
+		 * The first after unregister to clocksource jiffies. And
+		 * another one after the register to the newly registered timer.
+		 *
+		 * Alternatively we could 'waste' another HW timer to ping pong
+		 * between clock sources. That would also use one register and
+		 * one unregister call, but only trigger one clocksource switch
+		 * for the cost of another HW timer used by the OS.
+		 */
+		clocksource_unregister(&xttccs->cs);
+		clocksource_register_hz(&xttccs->cs,
+				ndata->new_rate / PRESCALE);
+		/* fall through */
+	case PRE_RATE_CHANGE:
+	case ABORT_RATE_CHANGE:
+	default:
+		return NOTIFY_DONE;
+	}
+}
+
+static void __init zynq_ttc_setup_clocksource(struct clk *clk,
+							void __iomem *base)
+{
+	struct xttcps_timer_clocksource *ttccs;
+	int err;
+
+	ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
+	if (WARN_ON(!ttccs))
+		return;
+
+	ttccs->xttc.clk = clk;
+
+	err = clk_prepare_enable(ttccs->xttc.clk);
+	if (WARN_ON(err))
+		return;
+
+	ttccs->xttc.clk_rate_change_nb.notifier_call =
+		xttcps_rate_change_clocksource_cb;
+	ttccs->xttc.clk_rate_change_nb.next = NULL;
+	if (clk_notifier_register(ttccs->xttc.clk,
+				&ttccs->xttc.clk_rate_change_nb))
+		pr_warn("Unable to register clock notifier.\n");
+
+	ttccs->xttc.base_addr = base;
+	ttccs->cs.name = "xttcps_clocksource";
+	ttccs->cs.rating = 200;
+	ttccs->cs.read = __xttc_clocksource_read;
+	ttccs->cs.mask = CLOCKSOURCE_MASK(16);
+	ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+
+	/*
+	 * Setup the clock source counter to be an incrementing counter
+	 * with no interrupt and it rolls over at 0xFFFF. Pre-scale
+	 * it by 32 also. Let it start running now.
+	 */
+	__raw_writel(0x0,  ttccs->xttc.base_addr + XTTCPS_IER_OFFSET);
+	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
+		     ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
+	__raw_writel(CNT_CNTRL_RESET,
+		     ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
+
+	err = clocksource_register_hz(&ttccs->cs,
+			clk_get_rate(ttccs->xttc.clk) / PRESCALE);
+	if (WARN_ON(err))
+		return;
+
+}
+
+static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
+		unsigned long event, void *data)
+{
+	struct clk_notifier_data *ndata = data;
+	struct xttcps_timer *xttcps = to_xttcps_timer(nb);
+	struct xttcps_timer_clockevent *xttcce = container_of(xttcps,
+			struct xttcps_timer_clockevent, xttc);
+
+	switch (event) {
+	case POST_RATE_CHANGE:
+	{
+		unsigned long flags;
+
+		/*
+		 * clockevents_update_freq should be called with IRQ disabled on
+		 * the CPU the timer provides events for. The timer we use is
+		 * common to both CPUs, not sure if we need to run on both
+		 * cores.
+		 */
+		local_irq_save(flags);
+		clockevents_update_freq(&xttcce->ce,
+				ndata->new_rate / PRESCALE);
+		local_irq_restore(flags);
+
+		/* fall through */
+	}
+	case PRE_RATE_CHANGE:
+	case ABORT_RATE_CHANGE:
+	default:
+		return NOTIFY_DONE;
+	}
+}
+
+static void __init zynq_ttc_setup_clockevent(struct clk *clk,
+						void __iomem *base, u32 irq)
+{
+	struct xttcps_timer_clockevent *ttcce;
+	int err;
+
+	ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
+	if (WARN_ON(!ttcce))
+		return;
+
+	ttcce->xttc.clk = clk;
+
+	err = clk_prepare_enable(ttcce->xttc.clk);
+	if (WARN_ON(err))
+		return;
+
+	ttcce->xttc.clk_rate_change_nb.notifier_call =
+		xttcps_rate_change_clockevent_cb;
+	ttcce->xttc.clk_rate_change_nb.next = NULL;
+	if (clk_notifier_register(ttcce->xttc.clk,
+				&ttcce->xttc.clk_rate_change_nb))
+		pr_warn("Unable to register clock notifier.\n");
+
+	ttcce->xttc.base_addr = base;
+	ttcce->ce.name = "xttcps_clockevent";
+	ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	ttcce->ce.set_next_event = xttcps_set_next_event;
+	ttcce->ce.set_mode = xttcps_set_mode;
+	ttcce->ce.rating = 200;
+	ttcce->ce.irq = irq;
+	ttcce->ce.cpumask = cpu_possible_mask;
+
+	/*
+	 * Setup the clock event timer to be an interval timer which
+	 * is prescaled by 32 using the interval interrupt. Leave it
+	 * disabled for now.
+	 */
+	__raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
+	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
+		     ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
+	__raw_writel(0x1,  ttcce->xttc.base_addr + XTTCPS_IER_OFFSET);
+
+	err = request_irq(irq, xttcps_clock_event_interrupt,
+			  IRQF_DISABLED | IRQF_TIMER,
+			  ttcce->ce.name, ttcce);
+	if (WARN_ON(err))
+		return;
+
+	clockevents_config_and_register(&ttcce->ce,
+			clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe);
+}
+
+/**
+ * xttcps_timer_init - Initialize the timer
+ *
+ * Initializes the timer hardware and register the clock source and clock event
+ * timers with Linux kernal timer framework
+ */
+static void __init xttcps_timer_init(struct device_node *timer)
+{
+	unsigned int irq;
+	void __iomem *timer_baseaddr;
+	struct clk *clk;
+	static int initialized;
+
+	if (initialized)
+		return;
+
+	initialized = 1;
+
+	/*
+	 * Get the 1st Triple Timer Counter (TTC) block from the device tree
+	 * and use it. Note that the event timer uses the interrupt and it's the
+	 * 2nd TTC hence the irq_of_parse_and_map(,1)
+	 */
+	timer_baseaddr = of_iomap(timer, 0);
+	if (!timer_baseaddr) {
+		pr_err("ERROR: invalid timer base address\n");
+		BUG();
+	}
+
+	irq = irq_of_parse_and_map(timer, 1);
+	if (irq <= 0) {
+		pr_err("ERROR: invalid interrupt number\n");
+		BUG();
+	}
+
+	clk = of_clk_get_by_name(timer, "cpu_1x");
+	if (IS_ERR(clk)) {
+		pr_err("ERROR: timer input clock not found\n");
+		BUG();
+	}
+
+	zynq_ttc_setup_clocksource(clk, timer_baseaddr);
+	zynq_ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
+
+	pr_info("%s #0@%p, irq=%d\n", timer->name, timer_baseaddr, irq);
+}
+
+CLOCKSOURCE_OF_DECLARE(zynq, "xlnx,ttc", xttcps_timer_init);
-- 
1.7.9.7

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
  2013-03-25 13:53 [PATCH 01/10] arm: zynq: Use standard timer binding Michal Simek
  2013-03-25 13:53 ` [PATCH 02/10] arm: zynq: Move timer to clocksource interface Michal Simek
  2013-03-25 13:53 ` [PATCH 03/10] arm: zynq: Move timer to generic location Michal Simek
@ 2013-03-25 13:53 ` Michal Simek
  2013-03-25 14:06   ` Rob Herring
  2013-03-25 13:53 ` [PATCH 05/10] arm: zynq: Move slcr initialization to separate file Michal Simek
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 32+ messages in thread
From: Michal Simek @ 2013-03-25 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

Use Cortex a9 cp15 to read scu baseaddress.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
 arch/arm/boot/dts/zynq-7000.dtsi |    5 +++++
 arch/arm/mach-zynq/common.c      |   33 +++++++++++++++++++++------------
 arch/arm/mach-zynq/common.h      |    2 ++
 3 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
index 2a72339..4943568 100644
--- a/arch/arm/boot/dts/zynq-7000.dtsi
+++ b/arch/arm/boot/dts/zynq-7000.dtsi
@@ -22,6 +22,11 @@
 		interrupt-parent = <&intc>;
 		ranges;
 
+		scu: scu at f8f000000 {
+			compatible = "arm,cortex-a9-scu";
+			reg = <0xf8f00000 0x58>;
+		};
+
 		intc: interrupt-controller at f8f01000 {
 			compatible = "arm,cortex-a9-gic";
 			#interrupt-cells = <3>;
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 68e0907..13f9d8b 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -33,10 +33,13 @@
 #include <asm/mach-types.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
+#include <asm/smp_scu.h>
 #include <asm/hardware/cache-l2x0.h>
 
 #include "common.h"
 
+void __iomem *scu_base;
+
 static struct of_device_id zynq_of_bus_ids[] __initdata = {
 	{ .compatible = "simple-bus", },
 	{}
@@ -56,17 +59,6 @@ static void __init xilinx_init_machine(void)
 	of_platform_bus_probe(NULL, zynq_of_bus_ids, NULL);
 }
 
-#define SCU_PERIPH_PHYS		0xF8F00000
-#define SCU_PERIPH_SIZE		SZ_8K
-#define SCU_PERIPH_VIRT		(VMALLOC_END - SCU_PERIPH_SIZE)
-
-static struct map_desc scu_desc __initdata = {
-	.virtual	= SCU_PERIPH_VIRT,
-	.pfn		= __phys_to_pfn(SCU_PERIPH_PHYS),
-	.length		= SCU_PERIPH_SIZE,
-	.type		= MT_DEVICE,
-};
-
 static void __init xilinx_zynq_timer_init(void)
 {
 	struct device_node *np;
@@ -81,13 +73,30 @@ static void __init xilinx_zynq_timer_init(void)
 	clocksource_of_init();
 }
 
+static struct map_desc zynq_cortex_a9_scu_map __initdata = {
+	.length	= SZ_256,
+	.type	= MT_DEVICE,
+};
+
+static void __init scu_init(void)
+{
+	unsigned long base;
+
+	base = scu_a9_get_base();
+	zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
+	zynq_cortex_a9_scu_map.virtual = base;
+	iotable_init(&zynq_cortex_a9_scu_map, 1);
+	scu_base = ioremap(base, zynq_cortex_a9_scu_map.length);
+	BUG_ON(!scu_base);
+}
+
 /**
  * xilinx_map_io() - Create memory mappings needed for early I/O.
  */
 static void __init xilinx_map_io(void)
 {
 	debug_ll_io_init();
-	iotable_init(&scu_desc, 1);
+	scu_init();
 }
 
 static const char *xilinx_dt_match[] = {
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index 5050bb1..38727a2 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -17,4 +17,6 @@
 #ifndef __MACH_ZYNQ_COMMON_H__
 #define __MACH_ZYNQ_COMMON_H__
 
+extern void __iomem *scu_base;
+
 #endif
-- 
1.7.9.7

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

* [PATCH 05/10] arm: zynq: Move slcr initialization to separate file
  2013-03-25 13:53 [PATCH 01/10] arm: zynq: Use standard timer binding Michal Simek
                   ` (2 preceding siblings ...)
  2013-03-25 13:53 ` [PATCH 04/10] arm: zynq: Load scu baseaddress at run time Michal Simek
@ 2013-03-25 13:53 ` Michal Simek
  2013-03-25 16:19   ` Steffen Trumtrar
  2013-03-25 13:53 ` [PATCH 06/10] arm: zynq: Add support for system reset Michal Simek
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 32+ messages in thread
From: Michal Simek @ 2013-03-25 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

Create separate slcr driver instead of pollute common code.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
 arch/arm/mach-zynq/Makefile |    2 +-
 arch/arm/mach-zynq/common.c |   10 +------
 arch/arm/mach-zynq/common.h |    3 ++
 arch/arm/mach-zynq/slcr.c   |   69 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 74 insertions(+), 10 deletions(-)
 create mode 100644 arch/arm/mach-zynq/slcr.c

diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
index 320faed..13ee09b 100644
--- a/arch/arm/mach-zynq/Makefile
+++ b/arch/arm/mach-zynq/Makefile
@@ -3,4 +3,4 @@
 #
 
 # Common support
-obj-y				:= common.o
+obj-y				:= common.o slcr.o
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 13f9d8b..2734bd6 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -61,15 +61,7 @@ static void __init xilinx_init_machine(void)
 
 static void __init xilinx_zynq_timer_init(void)
 {
-	struct device_node *np;
-	void __iomem *slcr;
-
-	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
-	slcr = of_iomap(np, 0);
-	WARN_ON(!slcr);
-
-	xilinx_zynq_clocks_init(slcr);
-
+	slcr_init();
 	clocksource_of_init();
 }
 
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index 38727a2..e30898a 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -17,6 +17,9 @@
 #ifndef __MACH_ZYNQ_COMMON_H__
 #define __MACH_ZYNQ_COMMON_H__
 
+extern int slcr_init(void);
+
+extern void __iomem *zynq_slcr_base;
 extern void __iomem *scu_base;
 
 #endif
diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c
new file mode 100644
index 0000000..1883b70
--- /dev/null
+++ b/arch/arm/mach-zynq/slcr.c
@@ -0,0 +1,69 @@
+/*
+ * Xilinx SLCR driver
+ *
+ * Copyright (c) 2011-2013 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
+ * 02139, USA.
+ */
+
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/clk/zynq.h>
+#include "common.h"
+
+#define SLCR_UNLOCK_MAGIC		0xDF0D
+#define SLCR_UNLOCK			0x8   /* SCLR unlock register */
+
+void __iomem *zynq_slcr_base;
+
+/**
+ * xslcr_init()
+ * Returns 0 on success, negative errno otherwise.
+ *
+ * Called early during boot from platform code to remap SLCR area.
+ */
+int __init slcr_init(void)
+{
+	struct device_node *np;
+
+	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
+	if (!np) {
+		pr_err("%s: no slcr node found\n", __func__);
+		BUG();
+	}
+
+	zynq_slcr_base = of_iomap(np, 0);
+	if (!zynq_slcr_base) {
+		pr_err("%s: Unable to map I/O memory\n", __func__);
+		BUG();
+	}
+
+	/* unlock the SLCR so that registers can be changed */
+	writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK);
+
+	pr_info("%s mapped to %p\n", np->name, zynq_slcr_base);
+
+	xilinx_zynq_clocks_init(zynq_slcr_base);
+
+	of_node_put(np);
+
+	return 0;
+}
-- 
1.7.9.7

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

* [PATCH 06/10] arm: zynq: Add support for system reset
  2013-03-25 13:53 [PATCH 01/10] arm: zynq: Use standard timer binding Michal Simek
                   ` (3 preceding siblings ...)
  2013-03-25 13:53 ` [PATCH 05/10] arm: zynq: Move slcr initialization to separate file Michal Simek
@ 2013-03-25 13:53 ` Michal Simek
  2013-03-25 13:53 ` [PATCH 07/10] arm: zynq: Add support for pmu Michal Simek
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 32+ messages in thread
From: Michal Simek @ 2013-03-25 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

Do system reset via slcr registers.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
 arch/arm/mach-zynq/common.c |    6 ++++++
 arch/arm/mach-zynq/common.h |    1 +
 arch/arm/mach-zynq/slcr.c   |   26 ++++++++++++++++++++++++++
 3 files changed, 33 insertions(+)

diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 2734bd6..292f775 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -91,6 +91,11 @@ static void __init xilinx_map_io(void)
 	scu_init();
 }
 
+static void xilinx_system_reset(char mode, const char *cmd)
+{
+	slcr_system_reset();
+}
+
 static const char *xilinx_dt_match[] = {
 	"xlnx,zynq-zc702",
 	"xlnx,zynq-7000",
@@ -103,4 +108,5 @@ MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
 	.init_machine	= xilinx_init_machine,
 	.init_time	= xilinx_zynq_timer_init,
 	.dt_compat	= xilinx_dt_match,
+	.restart	= xilinx_system_reset,
 MACHINE_END
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index e30898a..e5628f7 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -18,6 +18,7 @@
 #define __MACH_ZYNQ_COMMON_H__
 
 extern int slcr_init(void);
+extern void slcr_system_reset(void);
 
 extern void __iomem *zynq_slcr_base;
 extern void __iomem *scu_base;
diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c
index 1883b70..36b79d8 100644
--- a/arch/arm/mach-zynq/slcr.c
+++ b/arch/arm/mach-zynq/slcr.c
@@ -32,9 +32,35 @@
 #define SLCR_UNLOCK_MAGIC		0xDF0D
 #define SLCR_UNLOCK			0x8   /* SCLR unlock register */
 
+#define SLCR_PSS_RST_CTRL_OFFSET	0x200 /* PS Software Reset Control */
+#define SLCR_REBOOT_STATUS		0x258 /* PS Reboot Status */
+
 void __iomem *zynq_slcr_base;
 
 /**
+ * xslcr_system_reset - Reset the entire system.
+ *
+ */
+void slcr_system_reset(void)
+{
+	u32 reboot;
+
+	/* Unlock the SLCR then reset the system.
+	 * Note that this seems to require raw i/o
+	 * functions or there's a lockup?
+	 */
+	__raw_writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK);
+
+	/* Clear 0x0F000000 bits of reboot status register to workaround
+	 * the FSBL not loading the bitstream after soft-reboot
+	 * This is a temporary solution until we know more.
+	 */
+	reboot = readl(zynq_slcr_base + SLCR_REBOOT_STATUS);
+	writel(reboot & 0xF0FFFFFF, zynq_slcr_base + SLCR_REBOOT_STATUS);
+	writel(1, zynq_slcr_base + SLCR_PSS_RST_CTRL_OFFSET);
+}
+
+/**
  * xslcr_init()
  * Returns 0 on success, negative errno otherwise.
  *
-- 
1.7.9.7

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

* [PATCH 07/10] arm: zynq: Add support for pmu
  2013-03-25 13:53 [PATCH 01/10] arm: zynq: Use standard timer binding Michal Simek
                   ` (4 preceding siblings ...)
  2013-03-25 13:53 ` [PATCH 06/10] arm: zynq: Add support for system reset Michal Simek
@ 2013-03-25 13:53 ` Michal Simek
  2013-03-25 13:53 ` [PATCH 08/10] arm: zynq: Add smp support Michal Simek
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 32+ messages in thread
From: Michal Simek @ 2013-03-25 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

Zynq is standard PMU with 2 interrupt per core.
There is also access via register which is not used right now.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
 arch/arm/boot/dts/zynq-7000.dtsi |    7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
index 4943568..2144f07 100644
--- a/arch/arm/boot/dts/zynq-7000.dtsi
+++ b/arch/arm/boot/dts/zynq-7000.dtsi
@@ -15,6 +15,13 @@
 / {
 	compatible = "xlnx,zynq-7000";
 
+	pmu {
+		compatible = "arm,cortex-a9-pmu";
+		interrupts = <0 5 4>, <0 6 4>;
+		interrupt-parent = <&intc>;
+		reg = < 0xf8891000 0x1000 0xf8893000 0x1000 >;
+	};
+
 	amba {
 		compatible = "simple-bus";
 		#address-cells = <1>;
-- 
1.7.9.7

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

* [PATCH 08/10] arm: zynq: Add smp support
  2013-03-25 13:53 [PATCH 01/10] arm: zynq: Use standard timer binding Michal Simek
                   ` (5 preceding siblings ...)
  2013-03-25 13:53 ` [PATCH 07/10] arm: zynq: Add support for pmu Michal Simek
@ 2013-03-25 13:53 ` Michal Simek
  2013-03-25 14:16   ` Rob Herring
  2013-03-25 13:53 ` [PATCH 09/10] arm: zynq: Add hotplug support Michal Simek
  2013-03-25 13:53 ` [PATCH 10/10] arm: zynq: Add cpuidle support Michal Simek
  8 siblings, 1 reply; 32+ messages in thread
From: Michal Simek @ 2013-03-25 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

Zynq is dual core Cortex A9 which starts always
at zero. Using simple trampoline ensure long jump
to secondary_startup code.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
 arch/arm/mach-zynq/Makefile  |    1 +
 arch/arm/mach-zynq/common.c  |    1 +
 arch/arm/mach-zynq/common.h  |    7 ++
 arch/arm/mach-zynq/platsmp.c |  160 ++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-zynq/slcr.c    |   29 ++++++++
 5 files changed, 198 insertions(+)
 create mode 100644 arch/arm/mach-zynq/platsmp.c

diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
index 13ee09b..8493a89 100644
--- a/arch/arm/mach-zynq/Makefile
+++ b/arch/arm/mach-zynq/Makefile
@@ -4,3 +4,4 @@
 
 # Common support
 obj-y				:= common.o slcr.o
+obj-$(CONFIG_SMP)		+= platsmp.o
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 292f775..a58f9d6 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -103,6 +103,7 @@ static const char *xilinx_dt_match[] = {
 };
 
 MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
+	.smp		= smp_ops(zynq_smp_ops),
 	.map_io		= xilinx_map_io,
 	.init_irq	= irqchip_init,
 	.init_machine	= xilinx_init_machine,
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index e5628f7..84145fb 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -19,6 +19,13 @@
 
 extern int slcr_init(void);
 extern void slcr_system_reset(void);
+extern void slcr_cpu_stop(int cpu);
+extern void slcr_cpu_start(int cpu);
+
+#ifdef CONFIG_SMP
+extern void secondary_startup(void);
+#endif
+extern struct smp_operations zynq_smp_ops __initdata;
 
 extern void __iomem *zynq_slcr_base;
 extern void __iomem *scu_base;
diff --git a/arch/arm/mach-zynq/platsmp.c b/arch/arm/mach-zynq/platsmp.c
new file mode 100644
index 0000000..d31ef72
--- /dev/null
+++ b/arch/arm/mach-zynq/platsmp.c
@@ -0,0 +1,160 @@
+/*
+ * This file contains Xilinx specific SMP code, used to start up
+ * the second processor.
+ *
+ * Copyright (C) 2011 - 2013 Xilinx
+ *
+ * based on linux/arch/arm/mach-realview/platsmp.c
+ *
+ * Copyright (C) 2002 ARM Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/jiffies.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <asm/smp_scu.h>
+#include <linux/irqchip/arm-gic.h>
+#include "common.h"
+
+static DEFINE_SPINLOCK(boot_lock);
+
+/*
+ * Store number of cores in the system
+ * Because of scu_get_core_count() must be in __init section and can't
+ * be called from zynq_cpun_start() because it is in __cpuinit section.
+ */
+static int ncores;
+
+/* Secondary CPU kernel startup is a 2 step process. The primary CPU
+ * starts the secondary CPU by giving it the address of the kernel and
+ * then sending it an event to wake it up. The secondary CPU then
+ * starts the kernel and tells the primary CPU it's up and running.
+ */
+static void __cpuinit zynq_secondary_init(unsigned int cpu)
+{
+	/*
+	 * if any interrupts are already enabled for the primary
+	 * core (e.g. timer irq), then they will not have been enabled
+	 * for us: do so
+	 */
+	gic_secondary_init(0);
+
+	/*
+	 * Synchronise with the boot thread.
+	 */
+	spin_lock(&boot_lock);
+	spin_unlock(&boot_lock);
+}
+
+int __cpuinit zynq_cpun_start(u32 address, int cpu)
+{
+	if (cpu > ncores) {
+		pr_warn("CPU No. is not available in the system\n");
+		return -1;
+	}
+
+	/* MS: Expectation that SLCR are directly map and accessible */
+	/* Not possible to jump to non aligned address */
+	if (!(address & 3) && (!address || (address >= 0xC))) {
+		slcr_cpu_stop(cpu);
+
+		/*
+		 * This is elegant way how to jump to any address
+		 * 0x0: Load address at 0x8 to r0
+		 * 0x4: Jump by mov instruction
+		 * 0x8: Jumping address
+		 */
+		if (address) {
+			/* 0: ldr r0, [8] */
+			__raw_writel(0xe59f0000, phys_to_virt(0x0));
+			/* 4: mov pc, r0 */
+			__raw_writel(0xe1a0f000, phys_to_virt(0x4));
+			__raw_writel(address, phys_to_virt(0x8));
+		}
+
+		flush_cache_all();
+		outer_flush_all();
+		wmb();
+
+		slcr_cpu_start(cpu);
+
+		return 0;
+	}
+
+	pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address);
+
+	return -1;
+}
+EXPORT_SYMBOL(zynq_cpun_start);
+
+static int __cpuinit zynq_boot_secondary(unsigned int cpu,
+						struct task_struct *idle)
+{
+	int ret;
+
+	/*
+	 * set synchronisation state between this boot processor
+	 * and the secondary one
+	 */
+	spin_lock(&boot_lock);
+
+	ret = zynq_cpun_start(virt_to_phys(secondary_startup), cpu);
+	if (ret) {
+		spin_unlock(&boot_lock);
+		return -1;
+	}
+
+	/*
+	 * now the secondary core is starting up let it run its
+	 * calibrations, then wait for it to finish
+	 */
+	spin_unlock(&boot_lock);
+
+	return 0;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+static void __init zynq_smp_init_cpus(void)
+{
+	int i;
+
+	ncores = scu_get_core_count(scu_base);
+
+	for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++)
+		set_cpu_possible(i, true);
+}
+
+static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
+{
+	int i;
+
+	/*
+	 * Initialise the present map, which describes the set of CPUs
+	 * actually populated@the present time.
+	 */
+	for (i = 0; i < max_cpus; i++)
+		set_cpu_present(i, true);
+
+	scu_enable(scu_base);
+}
+
+struct smp_operations zynq_smp_ops __initdata = {
+	.smp_init_cpus		= zynq_smp_init_cpus,
+	.smp_prepare_cpus	= zynq_smp_prepare_cpus,
+	.smp_secondary_init	= zynq_secondary_init,
+	.smp_boot_secondary	= zynq_boot_secondary,
+};
diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c
index 36b79d8..7f2a919 100644
--- a/arch/arm/mach-zynq/slcr.c
+++ b/arch/arm/mach-zynq/slcr.c
@@ -33,6 +33,11 @@
 #define SLCR_UNLOCK			0x8   /* SCLR unlock register */
 
 #define SLCR_PSS_RST_CTRL_OFFSET	0x200 /* PS Software Reset Control */
+
+#define SLCR_A9_CPU_CLKSTOP		0x10
+#define SLCR_A9_CPU_RST			0x1
+
+#define SLCR_A9_CPU_RST_CTRL		0x244 /* CPU Software Reset Control */
 #define SLCR_REBOOT_STATUS		0x258 /* PS Reboot Status */
 
 void __iomem *zynq_slcr_base;
@@ -61,6 +66,30 @@ void slcr_system_reset(void)
 }
 
 /**
+ * slcr_cpu_start - Start cpu
+ * @cpu:	cpu number
+ */
+void slcr_cpu_start(int cpu)
+{
+	/* enable CPUn */
+	writel(SLCR_A9_CPU_CLKSTOP << cpu,
+	       zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
+	/* enable CLK for CPUn */
+	writel(0x0 << cpu, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
+}
+
+/**
+ * slcr_cpu_stop - Stop cpu
+ * @cpu:	cpu number
+ */
+void slcr_cpu_stop(int cpu)
+{
+	/* stop CLK and reset CPUn */
+	writel((SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu,
+	       zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
+}
+
+/**
  * xslcr_init()
  * Returns 0 on success, negative errno otherwise.
  *
-- 
1.7.9.7

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

* [PATCH 09/10] arm: zynq: Add hotplug support
  2013-03-25 13:53 [PATCH 01/10] arm: zynq: Use standard timer binding Michal Simek
                   ` (6 preceding siblings ...)
  2013-03-25 13:53 ` [PATCH 08/10] arm: zynq: Add smp support Michal Simek
@ 2013-03-25 13:53 ` Michal Simek
  2013-03-25 13:53 ` [PATCH 10/10] arm: zynq: Add cpuidle support Michal Simek
  8 siblings, 0 replies; 32+ messages in thread
From: Michal Simek @ 2013-03-25 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
 arch/arm/mach-zynq/Makefile  |    1 +
 arch/arm/mach-zynq/common.h  |    3 ++
 arch/arm/mach-zynq/hotplug.c |  102 ++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-zynq/platsmp.c |    3 ++
 4 files changed, 109 insertions(+)
 create mode 100644 arch/arm/mach-zynq/hotplug.c

diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
index 8493a89..8e75490 100644
--- a/arch/arm/mach-zynq/Makefile
+++ b/arch/arm/mach-zynq/Makefile
@@ -4,4 +4,5 @@
 
 # Common support
 obj-y				:= common.o slcr.o
+obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 obj-$(CONFIG_SMP)		+= platsmp.o
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index 84145fb..b4614fa 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -30,4 +30,7 @@ extern struct smp_operations zynq_smp_ops __initdata;
 extern void __iomem *zynq_slcr_base;
 extern void __iomem *scu_base;
 
+/* Hotplug */
+extern void platform_cpu_die(unsigned int cpu);
+
 #endif
diff --git a/arch/arm/mach-zynq/hotplug.c b/arch/arm/mach-zynq/hotplug.c
new file mode 100644
index 0000000..012bddf
--- /dev/null
+++ b/arch/arm/mach-zynq/hotplug.c
@@ -0,0 +1,102 @@
+/*
+ *  linux/arch/arm/mach-realview/hotplug.c
+ *
+ *  Copyright (C) 2002 ARM Ltd.
+ *  All Rights Reserved
+ *
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cp15.h>
+#include "common.h"
+
+static inline void cpu_enter_lowpower(void)
+{
+	unsigned int v;
+
+	flush_cache_all();
+	asm volatile(
+	"	mcr	p15, 0, %1, c7, c5, 0\n"
+	"	dsb\n"
+	/*
+	 * Turn off coherency
+	 */
+	"	mrc	p15, 0, %0, c1, c0, 1\n"
+	"	bic	%0, %0, #0x40\n"
+	"	mcr	p15, 0, %0, c1, c0, 1\n"
+	"	mrc	p15, 0, %0, c1, c0, 0\n"
+	"	bic	%0, %0, %2\n"
+	"	mcr	p15, 0, %0, c1, c0, 0\n"
+	  : "=&r" (v)
+	  : "r" (0), "Ir" (CR_C)
+	  : "cc");
+}
+
+static inline void cpu_leave_lowpower(void)
+{
+	unsigned int v;
+
+	asm volatile(
+	"	mrc	p15, 0, %0, c1, c0, 0\n"
+	"	orr	%0, %0, %1\n"
+	"	mcr	p15, 0, %0, c1, c0, 0\n"
+	"	mrc	p15, 0, %0, c1, c0, 1\n"
+	"	orr	%0, %0, #0x40\n"
+	"	mcr	p15, 0, %0, c1, c0, 1\n"
+	  : "=&r" (v)
+	  : "Ir" (CR_C)
+	  : "cc");
+}
+
+static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
+{
+	/*
+	 * there is no power-control hardware on this platform, so all
+	 * we can do is put the core into WFI; this is safe as the calling
+	 * code will have already disabled interrupts
+	 */
+	for (;;) {
+		dsb();
+		wfi();
+
+		/*
+		 * Getting here, means that we have come out of WFI without
+		 * having been woken up - this shouldn't happen
+		 *
+		 * Just note it happening - when we're woken, we can report
+		 * its occurrence.
+		 */
+		(*spurious)++;
+	}
+}
+
+/*
+ * platform-specific code to shutdown a CPU
+ *
+ * Called with IRQs disabled
+ */
+void platform_cpu_die(unsigned int cpu)
+{
+	int spurious = 0;
+
+	/*
+	 * we're ready for shutdown now, so do it
+	 */
+	cpu_enter_lowpower();
+	platform_do_lowpower(cpu, &spurious);
+
+	/*
+	 * bring this CPU back into the world of cache
+	 * coherency, and then restore interrupts
+	 */
+	cpu_leave_lowpower();
+
+	if (spurious)
+		pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
+}
diff --git a/arch/arm/mach-zynq/platsmp.c b/arch/arm/mach-zynq/platsmp.c
index d31ef72..9757a2d 100644
--- a/arch/arm/mach-zynq/platsmp.c
+++ b/arch/arm/mach-zynq/platsmp.c
@@ -157,4 +157,7 @@ struct smp_operations zynq_smp_ops __initdata = {
 	.smp_prepare_cpus	= zynq_smp_prepare_cpus,
 	.smp_secondary_init	= zynq_secondary_init,
 	.smp_boot_secondary	= zynq_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+	.cpu_die		= platform_cpu_die,
+#endif
 };
-- 
1.7.9.7

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

* [PATCH 10/10] arm: zynq: Add cpuidle support
  2013-03-25 13:53 [PATCH 01/10] arm: zynq: Use standard timer binding Michal Simek
                   ` (7 preceding siblings ...)
  2013-03-25 13:53 ` [PATCH 09/10] arm: zynq: Add hotplug support Michal Simek
@ 2013-03-25 13:53 ` Michal Simek
  8 siblings, 0 replies; 32+ messages in thread
From: Michal Simek @ 2013-03-25 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

Add support for cpuidle.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
 arch/arm/mach-zynq/Makefile  |    1 +
 arch/arm/mach-zynq/cpuidle.c |  131 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 132 insertions(+)
 create mode 100644 arch/arm/mach-zynq/cpuidle.c

diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
index 8e75490..a085d86 100644
--- a/arch/arm/mach-zynq/Makefile
+++ b/arch/arm/mach-zynq/Makefile
@@ -4,5 +4,6 @@
 
 # Common support
 obj-y				:= common.o slcr.o
+obj-$(CONFIG_CPU_IDLE) 		+= cpuidle.o
 obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 obj-$(CONFIG_SMP)		+= platsmp.o
diff --git a/arch/arm/mach-zynq/cpuidle.c b/arch/arm/mach-zynq/cpuidle.c
new file mode 100644
index 0000000..24f0333
--- /dev/null
+++ b/arch/arm/mach-zynq/cpuidle.c
@@ -0,0 +1,131 @@
+/*
+ * based on arch/arm/mach-at91/cpuidle.c
+ *
+ * CPU idle support for Xilinx
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * The cpu idle uses wait-for-interrupt and RAM self refresh in order
+ * to implement two idle states -
+ * #1 wait-for-interrupt
+ * #2 wait-for-interrupt and RAM self refresh
+ *
+ * Note that this code is only intended as a prototype and is not tested
+ * well yet, or tuned for the Xilinx Cortex A9. Also note that for a
+ * tickless kernel, high res timers must not be turned on. The cpuidle
+ * framework must also be turned on in the kernel.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/cpuidle.h>
+#include <linux/io.h>
+#include <linux/export.h>
+#include <linux/clockchips.h>
+#include <linux/cpu_pm.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <asm/proc-fns.h>
+
+#define XILINX_MAX_STATES	1
+
+static DEFINE_PER_CPU(struct cpuidle_device, xilinx_cpuidle_device);
+
+/* Actual code that puts the SoC in different idle states */
+static int xilinx_enter_idle(struct cpuidle_device *dev,
+		struct cpuidle_driver *drv, int index)
+{
+	struct timeval before, after;
+	int idle_time;
+
+	local_irq_disable();
+	do_gettimeofday(&before);
+
+	if (index == 0)
+		/* Wait for interrupt state */
+		cpu_do_idle();
+
+	else if (index == 1) {
+		unsigned int cpu_id = smp_processor_id();
+
+		clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
+
+		/* Devices must be stopped here */
+		cpu_pm_enter();
+
+		/* Add code for DDR self refresh start */
+
+		cpu_do_idle();
+		/*cpu_suspend(foo, bar);*/
+
+		/* Add code for DDR self refresh stop */
+
+		cpu_pm_exit();
+
+		clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
+	}
+
+	do_gettimeofday(&after);
+	local_irq_enable();
+	idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
+			(after.tv_usec - before.tv_usec);
+
+	dev->last_residency = idle_time;
+	return index;
+}
+
+static struct cpuidle_driver xilinx_idle_driver = {
+	.name = "xilinx_idle",
+	.owner = THIS_MODULE,
+	.state_count = XILINX_MAX_STATES,
+	/* Wait for interrupt state */
+	.states[0] = {
+		.enter = xilinx_enter_idle,
+		.exit_latency = 1,
+		.target_residency = 10000,
+		.flags = CPUIDLE_FLAG_TIME_VALID,
+		.name = "WFI",
+		.desc = "Wait for interrupt",
+	},
+	/* Wait for interrupt and RAM self refresh state */
+	.states[1] = {
+		.enter = xilinx_enter_idle,
+		.exit_latency = 10,
+		.target_residency = 10000,
+		.flags = CPUIDLE_FLAG_TIME_VALID,
+		.name = "RAM_SR",
+		.desc = "WFI and RAM Self Refresh",
+	},
+};
+
+/* Initialize CPU idle by registering the idle states */
+static int xilinx_init_cpuidle(void)
+{
+	unsigned int cpu;
+	struct cpuidle_device *device;
+	int ret;
+
+	ret = cpuidle_register_driver(&xilinx_idle_driver);
+	if (ret) {
+		pr_err("Registering Xilinx CpuIdle Driver failed.\n");
+		return ret;
+	}
+
+	for_each_possible_cpu(cpu) {
+		device = &per_cpu(xilinx_cpuidle_device, cpu);
+		device->state_count = XILINX_MAX_STATES;
+		device->cpu = cpu;
+		ret = cpuidle_register_device(device);
+		if (ret) {
+			pr_err("xilinx_init_cpuidle: Failed registering\n");
+			return ret;
+		}
+	}
+
+	pr_info("Xilinx CpuIdle Driver started\n");
+	return 0;
+}
+device_initcall(xilinx_init_cpuidle);
-- 
1.7.9.7

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
  2013-03-25 13:53 ` [PATCH 04/10] arm: zynq: Load scu baseaddress at run time Michal Simek
@ 2013-03-25 14:06   ` Rob Herring
  2013-03-25 14:51     ` Michal Simek
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Herring @ 2013-03-25 14:06 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/25/2013 08:53 AM, Michal Simek wrote:
> Use Cortex a9 cp15 to read scu baseaddress.
> 
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> ---
>  arch/arm/boot/dts/zynq-7000.dtsi |    5 +++++
>  arch/arm/mach-zynq/common.c      |   33 +++++++++++++++++++++------------
>  arch/arm/mach-zynq/common.h      |    2 ++
>  3 files changed, 28 insertions(+), 12 deletions(-)
> 
> diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
> index 2a72339..4943568 100644
> --- a/arch/arm/boot/dts/zynq-7000.dtsi
> +++ b/arch/arm/boot/dts/zynq-7000.dtsi
> @@ -22,6 +22,11 @@
>  		interrupt-parent = <&intc>;
>  		ranges;
>  
> +		scu: scu at f8f000000 {
> +			compatible = "arm,cortex-a9-scu";
> +			reg = <0xf8f00000 0x58>;
> +		};
> +

It's fine to add this, but you don't really need it for this patch.

>  		intc: interrupt-controller at f8f01000 {
>  			compatible = "arm,cortex-a9-gic";
>  			#interrupt-cells = <3>;
> diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
> index 68e0907..13f9d8b 100644
> --- a/arch/arm/mach-zynq/common.c
> +++ b/arch/arm/mach-zynq/common.c
> @@ -33,10 +33,13 @@
>  #include <asm/mach-types.h>
>  #include <asm/page.h>
>  #include <asm/pgtable.h>
> +#include <asm/smp_scu.h>
>  #include <asm/hardware/cache-l2x0.h>
>  
>  #include "common.h"
>  
> +void __iomem *scu_base;
> +
>  static struct of_device_id zynq_of_bus_ids[] __initdata = {
>  	{ .compatible = "simple-bus", },
>  	{}
> @@ -56,17 +59,6 @@ static void __init xilinx_init_machine(void)
>  	of_platform_bus_probe(NULL, zynq_of_bus_ids, NULL);
>  }
>  
> -#define SCU_PERIPH_PHYS		0xF8F00000
> -#define SCU_PERIPH_SIZE		SZ_8K
> -#define SCU_PERIPH_VIRT		(VMALLOC_END - SCU_PERIPH_SIZE)
> -
> -static struct map_desc scu_desc __initdata = {
> -	.virtual	= SCU_PERIPH_VIRT,
> -	.pfn		= __phys_to_pfn(SCU_PERIPH_PHYS),
> -	.length		= SCU_PERIPH_SIZE,
> -	.type		= MT_DEVICE,
> -};
> -
>  static void __init xilinx_zynq_timer_init(void)
>  {
>  	struct device_node *np;
> @@ -81,13 +73,30 @@ static void __init xilinx_zynq_timer_init(void)
>  	clocksource_of_init();
>  }
>  
> +static struct map_desc zynq_cortex_a9_scu_map __initdata = {
> +	.length	= SZ_256,
> +	.type	= MT_DEVICE,
> +};
> +
> +static void __init scu_init(void)
> +{
> +	unsigned long base;
> +
> +	base = scu_a9_get_base();
> +	zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
> +	zynq_cortex_a9_scu_map.virtual = base;

You are setting the virtual address to the physical base?

> +	iotable_init(&zynq_cortex_a9_scu_map, 1);

Then creating a static mapping...

> +	scu_base = ioremap(base, zynq_cortex_a9_scu_map.length);

And also a dynamic mapping?

Rob

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

* [PATCH 08/10] arm: zynq: Add smp support
  2013-03-25 13:53 ` [PATCH 08/10] arm: zynq: Add smp support Michal Simek
@ 2013-03-25 14:16   ` Rob Herring
  2013-03-25 16:31     ` Michal Simek
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Herring @ 2013-03-25 14:16 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/25/2013 08:53 AM, Michal Simek wrote:
> Zynq is dual core Cortex A9 which starts always
> at zero. Using simple trampoline ensure long jump
> to secondary_startup code.
> 
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> ---
>  arch/arm/mach-zynq/Makefile  |    1 +
>  arch/arm/mach-zynq/common.c  |    1 +
>  arch/arm/mach-zynq/common.h  |    7 ++
>  arch/arm/mach-zynq/platsmp.c |  160 ++++++++++++++++++++++++++++++++++++++++++
>  arch/arm/mach-zynq/slcr.c    |   29 ++++++++
>  5 files changed, 198 insertions(+)
>  create mode 100644 arch/arm/mach-zynq/platsmp.c

[...]

> +/* Secondary CPU kernel startup is a 2 step process. The primary CPU
> + * starts the secondary CPU by giving it the address of the kernel and
> + * then sending it an event to wake it up. The secondary CPU then
> + * starts the kernel and tells the primary CPU it's up and running.
> + */
> +static void __cpuinit zynq_secondary_init(unsigned int cpu)
> +{
> +	/*
> +	 * if any interrupts are already enabled for the primary
> +	 * core (e.g. timer irq), then they will not have been enabled
> +	 * for us: do so
> +	 */
> +	gic_secondary_init(0);
> +
> +	/*
> +	 * Synchronise with the boot thread.
> +	 */
> +	spin_lock(&boot_lock);
> +	spin_unlock(&boot_lock);

Why do you think this is needed? Platforms that need this
synchronization are only the ones that just do wfi for hotplug rather
than properly reseting the core. You appear to do the latter and should
not need this.

> +}
> +
> +int __cpuinit zynq_cpun_start(u32 address, int cpu)
> +{
> +	if (cpu > ncores) {
> +		pr_warn("CPU No. is not available in the system\n");
> +		return -1;
> +	}
> +
> +	/* MS: Expectation that SLCR are directly map and accessible */
> +	/* Not possible to jump to non aligned address */
> +	if (!(address & 3) && (!address || (address >= 0xC))) {

What about Thumb2 kernel entry?

> +		slcr_cpu_stop(cpu);

Isn't a secondary cpu already stopped?

> +
> +		/*
> +		 * This is elegant way how to jump to any address
> +		 * 0x0: Load address at 0x8 to r0
> +		 * 0x4: Jump by mov instruction
> +		 * 0x8: Jumping address
> +		 */
> +		if (address) {
> +			/* 0: ldr r0, [8] */
> +			__raw_writel(0xe59f0000, phys_to_virt(0x0));
> +			/* 4: mov pc, r0 */
> +			__raw_writel(0xe1a0f000, phys_to_virt(0x4));
> +			__raw_writel(address, phys_to_virt(0x8));
> +		}
> +
> +		flush_cache_all();
> +		outer_flush_all();

You should only need to flush range on address 0-4 here.

> +		wmb();
> +
> +		slcr_cpu_start(cpu);
> +
> +		return 0;
> +	}
> +
> +	pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address);
> +
> +	return -1;
> +}
> +EXPORT_SYMBOL(zynq_cpun_start);
> +
> +static int __cpuinit zynq_boot_secondary(unsigned int cpu,
> +						struct task_struct *idle)
> +{
> +	int ret;
> +
> +	/*
> +	 * set synchronisation state between this boot processor
> +	 * and the secondary one
> +	 */
> +	spin_lock(&boot_lock);
> +
> +	ret = zynq_cpun_start(virt_to_phys(secondary_startup), cpu);
> +	if (ret) {
> +		spin_unlock(&boot_lock);
> +		return -1;
> +	}
> +
> +	/*
> +	 * now the secondary core is starting up let it run its
> +	 * calibrations, then wait for it to finish
> +	 */
> +	spin_unlock(&boot_lock);
> +
> +	return 0;
> +}
> +
> +/*
> + * Initialise the CPU possible map early - this describes the CPUs
> + * which may be present or become present in the system.
> + */
> +static void __init zynq_smp_init_cpus(void)
> +{
> +	int i;
> +
> +	ncores = scu_get_core_count(scu_base);
> +
> +	for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++)
> +		set_cpu_possible(i, true);
> +}
> +
> +static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
> +{
> +	int i;
> +
> +	/*
> +	 * Initialise the present map, which describes the set of CPUs
> +	 * actually populated at the present time.
> +	 */
> +	for (i = 0; i < max_cpus; i++)
> +		set_cpu_present(i, true);
> +
> +	scu_enable(scu_base);
> +}
> +
> +struct smp_operations zynq_smp_ops __initdata = {
> +	.smp_init_cpus		= zynq_smp_init_cpus,
> +	.smp_prepare_cpus	= zynq_smp_prepare_cpus,
> +	.smp_secondary_init	= zynq_secondary_init,
> +	.smp_boot_secondary	= zynq_boot_secondary,
> +};
> diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c
> index 36b79d8..7f2a919 100644
> --- a/arch/arm/mach-zynq/slcr.c
> +++ b/arch/arm/mach-zynq/slcr.c
> @@ -33,6 +33,11 @@
>  #define SLCR_UNLOCK			0x8   /* SCLR unlock register */
>  
>  #define SLCR_PSS_RST_CTRL_OFFSET	0x200 /* PS Software Reset Control */
> +
> +#define SLCR_A9_CPU_CLKSTOP		0x10
> +#define SLCR_A9_CPU_RST			0x1
> +
> +#define SLCR_A9_CPU_RST_CTRL		0x244 /* CPU Software Reset Control */
>  #define SLCR_REBOOT_STATUS		0x258 /* PS Reboot Status */
>  
>  void __iomem *zynq_slcr_base;
> @@ -61,6 +66,30 @@ void slcr_system_reset(void)
>  }
>  
>  /**
> + * slcr_cpu_start - Start cpu
> + * @cpu:	cpu number
> + */
> +void slcr_cpu_start(int cpu)
> +{
> +	/* enable CPUn */
> +	writel(SLCR_A9_CPU_CLKSTOP << cpu,
> +	       zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
> +	/* enable CLK for CPUn */
> +	writel(0x0 << cpu, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
> +}
> +
> +/**
> + * slcr_cpu_stop - Stop cpu
> + * @cpu:	cpu number
> + */
> +void slcr_cpu_stop(int cpu)
> +{
> +	/* stop CLK and reset CPUn */
> +	writel((SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu,
> +	       zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
> +}
> +
> +/**
>   * xslcr_init()
>   * Returns 0 on success, negative errno otherwise.
>   *
> 

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
  2013-03-25 14:06   ` Rob Herring
@ 2013-03-25 14:51     ` Michal Simek
  2013-03-25 15:37       ` Rob Herring
  0 siblings, 1 reply; 32+ messages in thread
From: Michal Simek @ 2013-03-25 14:51 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Rob,

2013/3/25 Rob Herring <robherring2@gmail.com>:
> On 03/25/2013 08:53 AM, Michal Simek wrote:
>> Use Cortex a9 cp15 to read scu baseaddress.
>>
>> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
>> ---
>>  arch/arm/boot/dts/zynq-7000.dtsi |    5 +++++
>>  arch/arm/mach-zynq/common.c      |   33 +++++++++++++++++++++------------
>>  arch/arm/mach-zynq/common.h      |    2 ++
>>  3 files changed, 28 insertions(+), 12 deletions(-)
>>
>> diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
>> index 2a72339..4943568 100644
>> --- a/arch/arm/boot/dts/zynq-7000.dtsi
>> +++ b/arch/arm/boot/dts/zynq-7000.dtsi
>> @@ -22,6 +22,11 @@
>>               interrupt-parent = <&intc>;
>>               ranges;
>>
>> +             scu: scu at f8f000000 {
>> +                     compatible = "arm,cortex-a9-scu";
>> +                     reg = <0xf8f00000 0x58>;
>> +             };
>> +
>
> It's fine to add this, but you don't really need it for this patch.

yes truth.

>
>>               intc: interrupt-controller at f8f01000 {
>>                       compatible = "arm,cortex-a9-gic";
>>                       #interrupt-cells = <3>;
>> diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
>> index 68e0907..13f9d8b 100644
>> --- a/arch/arm/mach-zynq/common.c
>> +++ b/arch/arm/mach-zynq/common.c
>> @@ -33,10 +33,13 @@
>>  #include <asm/mach-types.h>
>>  #include <asm/page.h>
>>  #include <asm/pgtable.h>
>> +#include <asm/smp_scu.h>
>>  #include <asm/hardware/cache-l2x0.h>
>>
>>  #include "common.h"
>>
>> +void __iomem *scu_base;
>> +
>>  static struct of_device_id zynq_of_bus_ids[] __initdata = {
>>       { .compatible = "simple-bus", },
>>       {}
>> @@ -56,17 +59,6 @@ static void __init xilinx_init_machine(void)
>>       of_platform_bus_probe(NULL, zynq_of_bus_ids, NULL);
>>  }
>>
>> -#define SCU_PERIPH_PHYS              0xF8F00000
>> -#define SCU_PERIPH_SIZE              SZ_8K
>> -#define SCU_PERIPH_VIRT              (VMALLOC_END - SCU_PERIPH_SIZE)
>> -
>> -static struct map_desc scu_desc __initdata = {
>> -     .virtual        = SCU_PERIPH_VIRT,
>> -     .pfn            = __phys_to_pfn(SCU_PERIPH_PHYS),
>> -     .length         = SCU_PERIPH_SIZE,
>> -     .type           = MT_DEVICE,
>> -};
>> -
>>  static void __init xilinx_zynq_timer_init(void)
>>  {
>>       struct device_node *np;
>> @@ -81,13 +73,30 @@ static void __init xilinx_zynq_timer_init(void)
>>       clocksource_of_init();
>>  }
>>
>> +static struct map_desc zynq_cortex_a9_scu_map __initdata = {
>> +     .length = SZ_256,
>> +     .type   = MT_DEVICE,
>> +};
>> +
>> +static void __init scu_init(void)
>> +{
>> +     unsigned long base;
>> +
>> +     base = scu_a9_get_base();
>> +     zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
>> +     zynq_cortex_a9_scu_map.virtual = base;
>
> You are setting the virtual address to the physical base?
>
>> +     iotable_init(&zynq_cortex_a9_scu_map, 1);
>
> Then creating a static mapping...
>
>> +     scu_base = ioremap(base, zynq_cortex_a9_scu_map.length);
>
> And also a dynamic mapping?

Yes - exactly.
I was talking to Olof about this code at ELC and he mentioned that someone
else might know better way how to do it.

It is quite a long time I played with this code.
I found this solution in vexpress platform (mach-vexpress/platsmp.c)

IRC: Static mapping is necessary to be able to access device so early.
On the other hand you can't use this static mapping when kernel runs
for that you need to use dynamic allocation.
Calling ioremap so early caused that the return address is the same
and you can just use it later. and it is also in the correct vmalloc area.

We are using scu_base for power management code.
Let me check if dynamic mapping is also required.

Thanks,
Michal


-- 
Michal Simek, Ing. (M.Eng)
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/
Maintainer of Linux kernel - Xilinx Zynq ARM architecture
Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
  2013-03-25 14:51     ` Michal Simek
@ 2013-03-25 15:37       ` Rob Herring
  2013-03-25 16:07         ` Michal Simek
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Herring @ 2013-03-25 15:37 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/25/2013 09:51 AM, Michal Simek wrote:
> Hi Rob,
> 
> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>> On 03/25/2013 08:53 AM, Michal Simek wrote:
>>> Use Cortex a9 cp15 to read scu baseaddress.

[...]

>>> +static void __init scu_init(void)
>>> +{
>>> +     unsigned long base;
>>> +
>>> +     base = scu_a9_get_base();
>>> +     zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
>>> +     zynq_cortex_a9_scu_map.virtual = base;
>>
>> You are setting the virtual address to the physical base?
>>
>>> +     iotable_init(&zynq_cortex_a9_scu_map, 1);
>>
>> Then creating a static mapping...
>>
>>> +     scu_base = ioremap(base, zynq_cortex_a9_scu_map.length);
>>
>> And also a dynamic mapping?
> 
> Yes - exactly.

You are simply getting lucky that it works. If the physical address did
not happen to be in the vmalloc address region, it would not work. You
should not do this because you have an implicit requirement and the code
will look broken to anyone that reads it.

> I was talking to Olof about this code at ELC and he mentioned that someone
> else might know better way how to do it.
> 
> It is quite a long time I played with this code.
> I found this solution in vexpress platform (mach-vexpress/platsmp.c)
> 
> IRC: Static mapping is necessary to be able to access device so early.

Only to read the number of cores. That's not really worth an early
mapping. I would move to using DT to count the number of cores. Support
for that is already in place.

> On the other hand you can't use this static mapping when kernel runs
> for that you need to use dynamic allocation.
> Calling ioremap so early caused that the return address is the same
> and you can just use it later. and it is also in the correct vmalloc area.
> 
> We are using scu_base for power management code.
> Let me check if dynamic mapping is also required.

The should be no reason you need both.

Rob

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

* [PATCH 03/10] arm: zynq: Move timer to generic location
  2013-03-25 13:53 ` [PATCH 03/10] arm: zynq: Move timer to generic location Michal Simek
@ 2013-03-25 16:01   ` Steffen Trumtrar
  2013-03-25 16:24     ` Michal Simek
  0 siblings, 1 reply; 32+ messages in thread
From: Steffen Trumtrar @ 2013-03-25 16:01 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Michal!

On Mon, Mar 25, 2013 at 02:53:09PM +0100, Michal Simek wrote:
> Move zynq timer out of mach folder to generic location.
> 
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> ---
> Arnd: This is the last patch as we discuss about moving
> this timer to generic location.
> 
> It depends on ARCH_ZYNQ as Tegra and others.
> It should be ok to use it or we can use different name if you like.
> ---

Moving out of mach-zynq is good, because this is a
	Cadence Rev06 Triple Timer Counter (see Zynq-7000 TRM page 25)
Which leads me to a proposed rename to: cadence_timer.c or something like that.
And the fn-names should be changed accordingly while at it.

>  arch/arm/mach-zynq/Makefile      |    2 +-
>  arch/arm/mach-zynq/timer.c       |  430 --------------------------------------
>  drivers/clocksource/Makefile     |    1 +
>  drivers/clocksource/zynq_timer.c |  430 ++++++++++++++++++++++++++++++++++++++
>  4 files changed, 432 insertions(+), 431 deletions(-)
>  delete mode 100644 arch/arm/mach-zynq/timer.c
>  create mode 100644 drivers/clocksource/zynq_timer.c
> 
> diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
> index 397268c..320faed 100644
> --- a/arch/arm/mach-zynq/Makefile
> +++ b/arch/arm/mach-zynq/Makefile
> @@ -3,4 +3,4 @@
>  #
>  
>  # Common support
> -obj-y				:= common.o timer.o
> +obj-y				:= common.o
> diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c
> deleted file mode 100644
> index 3b1faed..0000000
> --- a/arch/arm/mach-zynq/timer.c
> +++ /dev/null
> @@ -1,430 +0,0 @@
> -/*
> - * This file contains driver for the Xilinx PS Timer Counter IP.
> - *
> - *  Copyright (C) 2011 Xilinx
> - *
> - * based on arch/mips/kernel/time.c timer driver
> - *
> - * This software is licensed under the terms of the GNU General Public
> - * License version 2, as published by the Free Software Foundation, and
> - * may be copied, distributed, and modified under those terms.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - */
> -
> -#include <linux/clk.h>
> -#include <linux/interrupt.h>
> -#include <linux/clockchips.h>
> -#include <linux/of_address.h>
> -#include <linux/of_irq.h>
> -#include <linux/slab.h>
> -#include <linux/clk-provider.h>
> -
> -/*
> - * This driver configures the 2 16-bit count-up timers as follows:
> - *
> - * T1: Timer 1, clocksource for generic timekeeping
> - * T2: Timer 2, clockevent source for hrtimers
> - * T3: Timer 3, <unused>
> - *
> - * The input frequency to the timer module for emulation is 2.5MHz which is
> - * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
> - * the timers are clocked at 78.125KHz (12.8 us resolution).
> -
> - * The input frequency to the timer module in silicon is configurable and
> - * obtained from device tree. The pre-scaler of 32 is used.
> - */
> -
> -/*
> - * Timer Register Offset Definitions of Timer 1, Increment base address by 4
> - * and use same offsets for Timer 2
> - */
> -#define XTTCPS_CLK_CNTRL_OFFSET		0x00 /* Clock Control Reg, RW */
> -#define XTTCPS_CNT_CNTRL_OFFSET		0x0C /* Counter Control Reg, RW */
> -#define XTTCPS_COUNT_VAL_OFFSET		0x18 /* Counter Value Reg, RO */
> -#define XTTCPS_INTR_VAL_OFFSET		0x24 /* Interval Count Reg, RW */
> -#define XTTCPS_ISR_OFFSET		0x54 /* Interrupt Status Reg, RO */
> -#define XTTCPS_IER_OFFSET		0x60 /* Interrupt Enable Reg, RW */
> -
> -#define XTTCPS_CNT_CNTRL_DISABLE_MASK	0x1
> -
> -/*
> - * Setup the timers to use pre-scaling, using a fixed value for now that will
> - * work across most input frequency, but it may need to be more dynamic
> - */
> -#define PRESCALE_EXPONENT	11	/* 2 ^ PRESCALE_EXPONENT = PRESCALE */
> -#define PRESCALE		2048	/* The exponent must match this */
> -#define CLK_CNTRL_PRESCALE	((PRESCALE_EXPONENT - 1) << 1)
> -#define CLK_CNTRL_PRESCALE_EN	1
> -#define CNT_CNTRL_RESET		(1 << 4)
> -
> -/**
> - * struct xttcps_timer - This definition defines local timer structure
> - *
> - * @base_addr:	Base address of timer
> - * @clk:	Associated clock source
> - * @clk_rate_change_nb	Notifier block for clock rate changes
> - */
> -struct xttcps_timer {
> -	void __iomem *base_addr;
> -	struct clk *clk;
> -	struct notifier_block clk_rate_change_nb;
> -};
> -
> -#define to_xttcps_timer(x) \
> -		container_of(x, struct xttcps_timer, clk_rate_change_nb)
> -
> -struct xttcps_timer_clocksource {
> -	struct xttcps_timer	xttc;
> -	struct clocksource	cs;
> -};
> -
> -#define to_xttcps_timer_clksrc(x) \
> -		container_of(x, struct xttcps_timer_clocksource, cs)
> -
> -struct xttcps_timer_clockevent {
> -	struct xttcps_timer		xttc;
> -	struct clock_event_device	ce;
> -};
> -
> -#define to_xttcps_timer_clkevent(x) \
> -		container_of(x, struct xttcps_timer_clockevent, ce)
> -
> -/**
> - * xttcps_set_interval - Set the timer interval value
> - *
> - * @timer:	Pointer to the timer instance
> - * @cycles:	Timer interval ticks
> - **/
> -static void xttcps_set_interval(struct xttcps_timer *timer,
> -					unsigned long cycles)
> -{
> -	u32 ctrl_reg;
> -
> -	/* Disable the counter, set the counter value  and re-enable counter */
> -	ctrl_reg = __raw_readl(timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> -	ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
> -	__raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> -
> -	__raw_writel(cycles, timer->base_addr + XTTCPS_INTR_VAL_OFFSET);
> -
> -	/*
> -	 * Reset the counter (0x10) so that it starts from 0, one-shot
> -	 * mode makes this needed for timing to be right.
> -	 */
> -	ctrl_reg |= CNT_CNTRL_RESET;
> -	ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
> -	__raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> -}
> -
> -/**
> - * xttcps_clock_event_interrupt - Clock event timer interrupt handler
> - *
> - * @irq:	IRQ number of the Timer
> - * @dev_id:	void pointer to the xttcps_timer instance
> - *
> - * returns: Always IRQ_HANDLED - success
> - **/
> -static irqreturn_t xttcps_clock_event_interrupt(int irq, void *dev_id)
> -{
> -	struct xttcps_timer_clockevent *xttce = dev_id;
> -	struct xttcps_timer *timer = &xttce->xttc;
> -
> -	/* Acknowledge the interrupt and call event handler */
> -	__raw_readl(timer->base_addr + XTTCPS_ISR_OFFSET);
> -
> -	xttce->ce.event_handler(&xttce->ce);
> -
> -	return IRQ_HANDLED;
> -}
> -
> -/**
> - * __xttc_clocksource_read - Reads the timer counter register
> - *
> - * returns: Current timer counter register value
> - **/
> -static cycle_t __xttc_clocksource_read(struct clocksource *cs)
> -{
> -	struct xttcps_timer *timer = &to_xttcps_timer_clksrc(cs)->xttc;
> -
> -	return (cycle_t)__raw_readl(timer->base_addr +
> -				XTTCPS_COUNT_VAL_OFFSET);
> -}
> -
> -/**
> - * xttcps_set_next_event - Sets the time interval for next event
> - *
> - * @cycles:	Timer interval ticks
> - * @evt:	Address of clock event instance
> - *
> - * returns: Always 0 - success
> - **/
> -static int xttcps_set_next_event(unsigned long cycles,
> -					struct clock_event_device *evt)
> -{
> -	struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
> -	struct xttcps_timer *timer = &xttce->xttc;
> -
> -	xttcps_set_interval(timer, cycles);
> -	return 0;
> -}
> -
> -/**
> - * xttcps_set_mode - Sets the mode of timer
> - *
> - * @mode:	Mode to be set
> - * @evt:	Address of clock event instance
> - **/
> -static void xttcps_set_mode(enum clock_event_mode mode,
> -					struct clock_event_device *evt)
> -{
> -	struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
> -	struct xttcps_timer *timer = &xttce->xttc;
> -	u32 ctrl_reg;
> -
> -	switch (mode) {
> -	case CLOCK_EVT_MODE_PERIODIC:
> -		xttcps_set_interval(timer,
> -				DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk),
> -					PRESCALE * HZ));
> -		break;
> -	case CLOCK_EVT_MODE_ONESHOT:
> -	case CLOCK_EVT_MODE_UNUSED:
> -	case CLOCK_EVT_MODE_SHUTDOWN:
> -		ctrl_reg = __raw_readl(timer->base_addr +
> -					XTTCPS_CNT_CNTRL_OFFSET);
> -		ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
> -		__raw_writel(ctrl_reg,
> -				timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> -		break;
> -	case CLOCK_EVT_MODE_RESUME:
> -		ctrl_reg = __raw_readl(timer->base_addr +
> -					XTTCPS_CNT_CNTRL_OFFSET);
> -		ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
> -		__raw_writel(ctrl_reg,
> -				timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> -		break;
> -	}
> -}
> -
> -static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb,
> -		unsigned long event, void *data)
> -{
> -	struct clk_notifier_data *ndata = data;
> -	struct xttcps_timer *xttcps = to_xttcps_timer(nb);
> -	struct xttcps_timer_clocksource *xttccs = container_of(xttcps,
> -			struct xttcps_timer_clocksource, xttc);
> -
> -	switch (event) {
> -	case POST_RATE_CHANGE:
> -		/*
> -		 * Do whatever is necessare to maintain a proper time base
> -		 *
> -		 * I cannot find a way to adjust the currently used clocksource
> -		 * to the new frequency. __clocksource_updatefreq_hz() sounds
> -		 * good, but does not work. Not sure what's that missing.
> -		 *
> -		 * This approach works, but triggers two clocksource switches.
> -		 * The first after unregister to clocksource jiffies. And
> -		 * another one after the register to the newly registered timer.
> -		 *
> -		 * Alternatively we could 'waste' another HW timer to ping pong
> -		 * between clock sources. That would also use one register and
> -		 * one unregister call, but only trigger one clocksource switch
> -		 * for the cost of another HW timer used by the OS.
> -		 */
> -		clocksource_unregister(&xttccs->cs);
> -		clocksource_register_hz(&xttccs->cs,
> -				ndata->new_rate / PRESCALE);
> -		/* fall through */
> -	case PRE_RATE_CHANGE:
> -	case ABORT_RATE_CHANGE:
> -	default:
> -		return NOTIFY_DONE;
> -	}
> -}
> -
> -static void __init zynq_ttc_setup_clocksource(struct clk *clk,
> -							void __iomem *base)
> -{
> -	struct xttcps_timer_clocksource *ttccs;
> -	int err;
> -
> -	ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
> -	if (WARN_ON(!ttccs))
> -		return;
> -
> -	ttccs->xttc.clk = clk;
> -
> -	err = clk_prepare_enable(ttccs->xttc.clk);
> -	if (WARN_ON(err))
> -		return;
> -
> -	ttccs->xttc.clk_rate_change_nb.notifier_call =
> -		xttcps_rate_change_clocksource_cb;
> -	ttccs->xttc.clk_rate_change_nb.next = NULL;
> -	if (clk_notifier_register(ttccs->xttc.clk,
> -				&ttccs->xttc.clk_rate_change_nb))
> -		pr_warn("Unable to register clock notifier.\n");
> -
> -	ttccs->xttc.base_addr = base;
> -	ttccs->cs.name = "xttcps_clocksource";
> -	ttccs->cs.rating = 200;
> -	ttccs->cs.read = __xttc_clocksource_read;
> -	ttccs->cs.mask = CLOCKSOURCE_MASK(16);
> -	ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
> -
> -	/*
> -	 * Setup the clock source counter to be an incrementing counter
> -	 * with no interrupt and it rolls over at 0xFFFF. Pre-scale
> -	 * it by 32 also. Let it start running now.
> -	 */
> -	__raw_writel(0x0,  ttccs->xttc.base_addr + XTTCPS_IER_OFFSET);
> -	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
> -		     ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
> -	__raw_writel(CNT_CNTRL_RESET,
> -		     ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> -
> -	err = clocksource_register_hz(&ttccs->cs,
> -			clk_get_rate(ttccs->xttc.clk) / PRESCALE);
> -	if (WARN_ON(err))
> -		return;
> -
> -}
> -
> -static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
> -		unsigned long event, void *data)
> -{
> -	struct clk_notifier_data *ndata = data;
> -	struct xttcps_timer *xttcps = to_xttcps_timer(nb);
> -	struct xttcps_timer_clockevent *xttcce = container_of(xttcps,
> -			struct xttcps_timer_clockevent, xttc);
> -
> -	switch (event) {
> -	case POST_RATE_CHANGE:
> -	{
> -		unsigned long flags;
> -
> -		/*
> -		 * clockevents_update_freq should be called with IRQ disabled on
> -		 * the CPU the timer provides events for. The timer we use is
> -		 * common to both CPUs, not sure if we need to run on both
> -		 * cores.
> -		 */
> -		local_irq_save(flags);
> -		clockevents_update_freq(&xttcce->ce,
> -				ndata->new_rate / PRESCALE);
> -		local_irq_restore(flags);
> -
> -		/* fall through */
> -	}
> -	case PRE_RATE_CHANGE:
> -	case ABORT_RATE_CHANGE:
> -	default:
> -		return NOTIFY_DONE;
> -	}
> -}
> -
> -static void __init zynq_ttc_setup_clockevent(struct clk *clk,
> -						void __iomem *base, u32 irq)
> -{
> -	struct xttcps_timer_clockevent *ttcce;
> -	int err;
> -
> -	ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
> -	if (WARN_ON(!ttcce))
> -		return;
> -
> -	ttcce->xttc.clk = clk;
> -
> -	err = clk_prepare_enable(ttcce->xttc.clk);
> -	if (WARN_ON(err))
> -		return;
> -
> -	ttcce->xttc.clk_rate_change_nb.notifier_call =
> -		xttcps_rate_change_clockevent_cb;
> -	ttcce->xttc.clk_rate_change_nb.next = NULL;
> -	if (clk_notifier_register(ttcce->xttc.clk,
> -				&ttcce->xttc.clk_rate_change_nb))
> -		pr_warn("Unable to register clock notifier.\n");
> -
> -	ttcce->xttc.base_addr = base;
> -	ttcce->ce.name = "xttcps_clockevent";
> -	ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
> -	ttcce->ce.set_next_event = xttcps_set_next_event;
> -	ttcce->ce.set_mode = xttcps_set_mode;
> -	ttcce->ce.rating = 200;
> -	ttcce->ce.irq = irq;
> -	ttcce->ce.cpumask = cpu_possible_mask;
> -
> -	/*
> -	 * Setup the clock event timer to be an interval timer which
> -	 * is prescaled by 32 using the interval interrupt. Leave it
> -	 * disabled for now.
> -	 */
> -	__raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> -	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
> -		     ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
> -	__raw_writel(0x1,  ttcce->xttc.base_addr + XTTCPS_IER_OFFSET);
> -
> -	err = request_irq(irq, xttcps_clock_event_interrupt,
> -			  IRQF_DISABLED | IRQF_TIMER,
> -			  ttcce->ce.name, ttcce);
> -	if (WARN_ON(err))
> -		return;
> -
> -	clockevents_config_and_register(&ttcce->ce,
> -			clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe);
> -}
> -
> -/**
> - * xttcps_timer_init - Initialize the timer
> - *
> - * Initializes the timer hardware and register the clock source and clock event
> - * timers with Linux kernal timer framework
> - */
> -static void __init xttcps_timer_init(struct device_node *timer)
> -{
> -	unsigned int irq;
> -	void __iomem *timer_baseaddr;
> -	struct clk *clk;
> -	static int initialized;
> -
> -	if (initialized)
> -		return;
> -
> -	initialized = 1;
> -
> -	/*
> -	 * Get the 1st Triple Timer Counter (TTC) block from the device tree
> -	 * and use it. Note that the event timer uses the interrupt and it's the
> -	 * 2nd TTC hence the irq_of_parse_and_map(,1)
> -	 */
> -	timer_baseaddr = of_iomap(timer, 0);
> -	if (!timer_baseaddr) {
> -		pr_err("ERROR: invalid timer base address\n");
> -		BUG();
> -	}
> -
> -	irq = irq_of_parse_and_map(timer, 1);
> -	if (irq <= 0) {
> -		pr_err("ERROR: invalid interrupt number\n");
> -		BUG();
> -	}
> -
> -	clk = of_clk_get_by_name(timer, "cpu_1x");
> -	if (IS_ERR(clk)) {
> -		pr_err("ERROR: timer input clock not found\n");
> -		BUG();
> -	}
> -
> -	zynq_ttc_setup_clocksource(clk, timer_baseaddr);
> -	zynq_ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
> -
> -	pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
> -}
> -
> -CLOCKSOURCE_OF_DECLARE(zynq, "xlnx,ttc", xttcps_timer_init);
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 4d8283a..665af7f 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o
>  obj-$(CONFIG_SUNXI_TIMER)	+= sunxi_timer.o
>  obj-$(CONFIG_ARCH_TEGRA)	+= tegra20_timer.o
>  obj-$(CONFIG_VT8500_TIMER)	+= vt8500_timer.o
> +obj-$(CONFIG_ARCH_ZYNQ)		+= zynq_timer.o
>  
>  obj-$(CONFIG_ARM_ARCH_TIMER)		+= arm_arch_timer.o
>  obj-$(CONFIG_CLKSRC_METAG_GENERIC)	+= metag_generic.o
> diff --git a/drivers/clocksource/zynq_timer.c b/drivers/clocksource/zynq_timer.c
> new file mode 100644
> index 0000000..3b1faed
> --- /dev/null
> +++ b/drivers/clocksource/zynq_timer.c
> @@ -0,0 +1,430 @@
> +/*
> + * This file contains driver for the Xilinx PS Timer Counter IP.
> + *
> + *  Copyright (C) 2011 Xilinx
> + *
> + * based on arch/mips/kernel/time.c timer driver
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/clockchips.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/slab.h>
> +#include <linux/clk-provider.h>
> +
> +/*
> + * This driver configures the 2 16-bit count-up timers as follows:
> + *
> + * T1: Timer 1, clocksource for generic timekeeping
> + * T2: Timer 2, clockevent source for hrtimers
> + * T3: Timer 3, <unused>
> + *
> + * The input frequency to the timer module for emulation is 2.5MHz which is
> + * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
> + * the timers are clocked at 78.125KHz (12.8 us resolution).
> +
> + * The input frequency to the timer module in silicon is configurable and
> + * obtained from device tree. The pre-scaler of 32 is used.
> + */
> +
> +/*
> + * Timer Register Offset Definitions of Timer 1, Increment base address by 4
> + * and use same offsets for Timer 2
> + */
> +#define XTTCPS_CLK_CNTRL_OFFSET		0x00 /* Clock Control Reg, RW */
> +#define XTTCPS_CNT_CNTRL_OFFSET		0x0C /* Counter Control Reg, RW */
> +#define XTTCPS_COUNT_VAL_OFFSET		0x18 /* Counter Value Reg, RO */
> +#define XTTCPS_INTR_VAL_OFFSET		0x24 /* Interval Count Reg, RW */
> +#define XTTCPS_ISR_OFFSET		0x54 /* Interrupt Status Reg, RO */
> +#define XTTCPS_IER_OFFSET		0x60 /* Interrupt Enable Reg, RW */
> +
> +#define XTTCPS_CNT_CNTRL_DISABLE_MASK	0x1
> +
> +/*
> + * Setup the timers to use pre-scaling, using a fixed value for now that will
> + * work across most input frequency, but it may need to be more dynamic
> + */
> +#define PRESCALE_EXPONENT	11	/* 2 ^ PRESCALE_EXPONENT = PRESCALE */
> +#define PRESCALE		2048	/* The exponent must match this */
> +#define CLK_CNTRL_PRESCALE	((PRESCALE_EXPONENT - 1) << 1)
> +#define CLK_CNTRL_PRESCALE_EN	1
> +#define CNT_CNTRL_RESET		(1 << 4)
> +
> +/**
> + * struct xttcps_timer - This definition defines local timer structure
> + *
> + * @base_addr:	Base address of timer
> + * @clk:	Associated clock source
> + * @clk_rate_change_nb	Notifier block for clock rate changes
> + */
> +struct xttcps_timer {
> +	void __iomem *base_addr;
> +	struct clk *clk;
> +	struct notifier_block clk_rate_change_nb;
> +};
> +
> +#define to_xttcps_timer(x) \
> +		container_of(x, struct xttcps_timer, clk_rate_change_nb)
> +
> +struct xttcps_timer_clocksource {
> +	struct xttcps_timer	xttc;
> +	struct clocksource	cs;
> +};
> +
> +#define to_xttcps_timer_clksrc(x) \
> +		container_of(x, struct xttcps_timer_clocksource, cs)
> +
> +struct xttcps_timer_clockevent {
> +	struct xttcps_timer		xttc;
> +	struct clock_event_device	ce;
> +};
> +
> +#define to_xttcps_timer_clkevent(x) \
> +		container_of(x, struct xttcps_timer_clockevent, ce)
> +
> +/**
> + * xttcps_set_interval - Set the timer interval value
> + *
> + * @timer:	Pointer to the timer instance
> + * @cycles:	Timer interval ticks
> + **/
> +static void xttcps_set_interval(struct xttcps_timer *timer,
> +					unsigned long cycles)
> +{
> +	u32 ctrl_reg;
> +
> +	/* Disable the counter, set the counter value  and re-enable counter */
> +	ctrl_reg = __raw_readl(timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> +	ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
> +	__raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> +
> +	__raw_writel(cycles, timer->base_addr + XTTCPS_INTR_VAL_OFFSET);
> +
> +	/*
> +	 * Reset the counter (0x10) so that it starts from 0, one-shot
> +	 * mode makes this needed for timing to be right.
> +	 */
> +	ctrl_reg |= CNT_CNTRL_RESET;
> +	ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
> +	__raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> +}
> +
> +/**
> + * xttcps_clock_event_interrupt - Clock event timer interrupt handler
> + *
> + * @irq:	IRQ number of the Timer
> + * @dev_id:	void pointer to the xttcps_timer instance
> + *
> + * returns: Always IRQ_HANDLED - success
> + **/
> +static irqreturn_t xttcps_clock_event_interrupt(int irq, void *dev_id)
> +{
> +	struct xttcps_timer_clockevent *xttce = dev_id;
> +	struct xttcps_timer *timer = &xttce->xttc;
> +
> +	/* Acknowledge the interrupt and call event handler */
> +	__raw_readl(timer->base_addr + XTTCPS_ISR_OFFSET);
> +
> +	xttce->ce.event_handler(&xttce->ce);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * __xttc_clocksource_read - Reads the timer counter register
> + *
> + * returns: Current timer counter register value
> + **/
> +static cycle_t __xttc_clocksource_read(struct clocksource *cs)
> +{
> +	struct xttcps_timer *timer = &to_xttcps_timer_clksrc(cs)->xttc;
> +
> +	return (cycle_t)__raw_readl(timer->base_addr +
> +				XTTCPS_COUNT_VAL_OFFSET);
> +}
> +
> +/**
> + * xttcps_set_next_event - Sets the time interval for next event
> + *
> + * @cycles:	Timer interval ticks
> + * @evt:	Address of clock event instance
> + *
> + * returns: Always 0 - success
> + **/
> +static int xttcps_set_next_event(unsigned long cycles,
> +					struct clock_event_device *evt)
> +{
> +	struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
> +	struct xttcps_timer *timer = &xttce->xttc;
> +
> +	xttcps_set_interval(timer, cycles);
> +	return 0;
> +}
> +
> +/**
> + * xttcps_set_mode - Sets the mode of timer
> + *
> + * @mode:	Mode to be set
> + * @evt:	Address of clock event instance
> + **/
> +static void xttcps_set_mode(enum clock_event_mode mode,
> +					struct clock_event_device *evt)
> +{
> +	struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
> +	struct xttcps_timer *timer = &xttce->xttc;
> +	u32 ctrl_reg;
> +
> +	switch (mode) {
> +	case CLOCK_EVT_MODE_PERIODIC:
> +		xttcps_set_interval(timer,
> +				DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk),
> +					PRESCALE * HZ));
> +		break;
> +	case CLOCK_EVT_MODE_ONESHOT:
> +	case CLOCK_EVT_MODE_UNUSED:
> +	case CLOCK_EVT_MODE_SHUTDOWN:
> +		ctrl_reg = __raw_readl(timer->base_addr +
> +					XTTCPS_CNT_CNTRL_OFFSET);
> +		ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
> +		__raw_writel(ctrl_reg,
> +				timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> +		break;
> +	case CLOCK_EVT_MODE_RESUME:
> +		ctrl_reg = __raw_readl(timer->base_addr +
> +					XTTCPS_CNT_CNTRL_OFFSET);
> +		ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
> +		__raw_writel(ctrl_reg,
> +				timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> +		break;
> +	}
> +}
> +
> +static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb,
> +		unsigned long event, void *data)
> +{
> +	struct clk_notifier_data *ndata = data;
> +	struct xttcps_timer *xttcps = to_xttcps_timer(nb);
> +	struct xttcps_timer_clocksource *xttccs = container_of(xttcps,
> +			struct xttcps_timer_clocksource, xttc);
> +
> +	switch (event) {
> +	case POST_RATE_CHANGE:
> +		/*
> +		 * Do whatever is necessare to maintain a proper time base
------------------------------------------^ little typo

> +		 *
> +		 * I cannot find a way to adjust the currently used clocksource
> +		 * to the new frequency. __clocksource_updatefreq_hz() sounds
> +		 * good, but does not work. Not sure what's that missing.
> +		 *
> +		 * This approach works, but triggers two clocksource switches.
> +		 * The first after unregister to clocksource jiffies. And
> +		 * another one after the register to the newly registered timer.
> +		 *
> +		 * Alternatively we could 'waste' another HW timer to ping pong
> +		 * between clock sources. That would also use one register and
> +		 * one unregister call, but only trigger one clocksource switch
> +		 * for the cost of another HW timer used by the OS.
> +		 */
> +		clocksource_unregister(&xttccs->cs);
> +		clocksource_register_hz(&xttccs->cs,
> +				ndata->new_rate / PRESCALE);
> +		/* fall through */
> +	case PRE_RATE_CHANGE:
> +	case ABORT_RATE_CHANGE:
> +	default:
> +		return NOTIFY_DONE;
> +	}
> +}
> +
> +static void __init zynq_ttc_setup_clocksource(struct clk *clk,
> +							void __iomem *base)
> +{
> +	struct xttcps_timer_clocksource *ttccs;
> +	int err;
> +
> +	ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
> +	if (WARN_ON(!ttccs))
> +		return;
> +
> +	ttccs->xttc.clk = clk;
> +
> +	err = clk_prepare_enable(ttccs->xttc.clk);
> +	if (WARN_ON(err))
> +		return;

I think you are losing memory here...

> +
> +	ttccs->xttc.clk_rate_change_nb.notifier_call =
> +		xttcps_rate_change_clocksource_cb;
> +	ttccs->xttc.clk_rate_change_nb.next = NULL;
> +	if (clk_notifier_register(ttccs->xttc.clk,
> +				&ttccs->xttc.clk_rate_change_nb))
> +		pr_warn("Unable to register clock notifier.\n");
> +
> +	ttccs->xttc.base_addr = base;
> +	ttccs->cs.name = "xttcps_clocksource";
> +	ttccs->cs.rating = 200;
> +	ttccs->cs.read = __xttc_clocksource_read;
> +	ttccs->cs.mask = CLOCKSOURCE_MASK(16);
> +	ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
> +
> +	/*
> +	 * Setup the clock source counter to be an incrementing counter
> +	 * with no interrupt and it rolls over at 0xFFFF. Pre-scale
> +	 * it by 32 also. Let it start running now.
> +	 */
> +	__raw_writel(0x0,  ttccs->xttc.base_addr + XTTCPS_IER_OFFSET);
> +	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
> +		     ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
> +	__raw_writel(CNT_CNTRL_RESET,
> +		     ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> +
> +	err = clocksource_register_hz(&ttccs->cs,
> +			clk_get_rate(ttccs->xttc.clk) / PRESCALE);
> +	if (WARN_ON(err))
> +		return;
... and here and below in zynq_ttc_setup_clockevent the same.
> +
> +}
> +
> +static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
> +		unsigned long event, void *data)
> +{
> +	struct clk_notifier_data *ndata = data;
> +	struct xttcps_timer *xttcps = to_xttcps_timer(nb);
> +	struct xttcps_timer_clockevent *xttcce = container_of(xttcps,
> +			struct xttcps_timer_clockevent, xttc);
> +
> +	switch (event) {
> +	case POST_RATE_CHANGE:
> +	{
> +		unsigned long flags;
> +
> +		/*
> +		 * clockevents_update_freq should be called with IRQ disabled on
> +		 * the CPU the timer provides events for. The timer we use is
> +		 * common to both CPUs, not sure if we need to run on both
> +		 * cores.
> +		 */
> +		local_irq_save(flags);
> +		clockevents_update_freq(&xttcce->ce,
> +				ndata->new_rate / PRESCALE);
> +		local_irq_restore(flags);
> +
> +		/* fall through */
> +	}
> +	case PRE_RATE_CHANGE:
> +	case ABORT_RATE_CHANGE:
> +	default:
> +		return NOTIFY_DONE;
> +	}
> +}
> +
> +static void __init zynq_ttc_setup_clockevent(struct clk *clk,
> +						void __iomem *base, u32 irq)
> +{
> +	struct xttcps_timer_clockevent *ttcce;
> +	int err;
> +
> +	ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
> +	if (WARN_ON(!ttcce))
> +		return;
> +
> +	ttcce->xttc.clk = clk;
> +
> +	err = clk_prepare_enable(ttcce->xttc.clk);
> +	if (WARN_ON(err))
> +		return;
> +
> +	ttcce->xttc.clk_rate_change_nb.notifier_call =
> +		xttcps_rate_change_clockevent_cb;
> +	ttcce->xttc.clk_rate_change_nb.next = NULL;
> +	if (clk_notifier_register(ttcce->xttc.clk,
> +				&ttcce->xttc.clk_rate_change_nb))
> +		pr_warn("Unable to register clock notifier.\n");
> +
> +	ttcce->xttc.base_addr = base;
> +	ttcce->ce.name = "xttcps_clockevent";
> +	ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
> +	ttcce->ce.set_next_event = xttcps_set_next_event;
> +	ttcce->ce.set_mode = xttcps_set_mode;
> +	ttcce->ce.rating = 200;
> +	ttcce->ce.irq = irq;
> +	ttcce->ce.cpumask = cpu_possible_mask;
> +
> +	/*
> +	 * Setup the clock event timer to be an interval timer which
> +	 * is prescaled by 32 using the interval interrupt. Leave it
> +	 * disabled for now.
> +	 */
> +	__raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
> +	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
> +		     ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
> +	__raw_writel(0x1,  ttcce->xttc.base_addr + XTTCPS_IER_OFFSET);
> +
> +	err = request_irq(irq, xttcps_clock_event_interrupt,
> +			  IRQF_DISABLED | IRQF_TIMER,
> +			  ttcce->ce.name, ttcce);
> +	if (WARN_ON(err))
> +		return;
> +
> +	clockevents_config_and_register(&ttcce->ce,
> +			clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe);
> +}
> +
> +/**
> + * xttcps_timer_init - Initialize the timer
> + *
> + * Initializes the timer hardware and register the clock source and clock event
> + * timers with Linux kernal timer framework
> + */
> +static void __init xttcps_timer_init(struct device_node *timer)
> +{
> +	unsigned int irq;
> +	void __iomem *timer_baseaddr;
> +	struct clk *clk;
> +	static int initialized;
> +
> +	if (initialized)
> +		return;
> +
> +	initialized = 1;
> +
??? This looks very strange ???
I think this should be handled in a different manner.
> +	/*
> +	 * Get the 1st Triple Timer Counter (TTC) block from the device tree
> +	 * and use it. Note that the event timer uses the interrupt and it's the
> +	 * 2nd TTC hence the irq_of_parse_and_map(,1)
> +	 */
> +	timer_baseaddr = of_iomap(timer, 0);
> +	if (!timer_baseaddr) {
> +		pr_err("ERROR: invalid timer base address\n");
> +		BUG();
> +	}
> +
> +	irq = irq_of_parse_and_map(timer, 1);
> +	if (irq <= 0) {
> +		pr_err("ERROR: invalid interrupt number\n");
> +		BUG();
> +	}
> +
> +	clk = of_clk_get_by_name(timer, "cpu_1x");
> +	if (IS_ERR(clk)) {
> +		pr_err("ERROR: timer input clock not found\n");
> +		BUG();
> +	}
> +
> +	zynq_ttc_setup_clocksource(clk, timer_baseaddr);
> +	zynq_ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
> +
> +	pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
> +}
> +
> +CLOCKSOURCE_OF_DECLARE(zynq, "xlnx,ttc", xttcps_timer_init);

What if do not want to use the ttc as clocksource and therefore don't want it
enabled? I still have the smp_twd which I might prefere.

Regards,
Steffen

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
  2013-03-25 15:37       ` Rob Herring
@ 2013-03-25 16:07         ` Michal Simek
  2013-03-25 22:34           ` Rob Herring
  2013-04-02 16:40           ` Pawel Moll
  0 siblings, 2 replies; 32+ messages in thread
From: Michal Simek @ 2013-03-25 16:07 UTC (permalink / raw)
  To: linux-arm-kernel

2013/3/25 Rob Herring <robherring2@gmail.com>:
> On 03/25/2013 09:51 AM, Michal Simek wrote:
>> Hi Rob,
>>
>> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>>> On 03/25/2013 08:53 AM, Michal Simek wrote:
>>>> Use Cortex a9 cp15 to read scu baseaddress.
>
> [...]
>
>>>> +static void __init scu_init(void)
>>>> +{
>>>> +     unsigned long base;
>>>> +
>>>> +     base = scu_a9_get_base();
>>>> +     zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
>>>> +     zynq_cortex_a9_scu_map.virtual = base;
>>>
>>> You are setting the virtual address to the physical base?
>>>
>>>> +     iotable_init(&zynq_cortex_a9_scu_map, 1);
>>>
>>> Then creating a static mapping...
>>>
>>>> +     scu_base = ioremap(base, zynq_cortex_a9_scu_map.length);
>>>
>>> And also a dynamic mapping?
>>
>> Yes - exactly.
>
> You are simply getting lucky that it works. If the physical address did
> not happen to be in the vmalloc address region, it would not work. You
> should not do this because you have an implicit requirement and the code
> will look broken to anyone that reads it.

yeah correct. I will add there a comment to mentioned that.

>> I was talking to Olof about this code at ELC and he mentioned that someone
>> else might know better way how to do it.
>>
>> It is quite a long time I played with this code.
>> I found this solution in vexpress platform (mach-vexpress/platsmp.c)
>>
>> IRC: Static mapping is necessary to be able to access device so early.
>
> Only to read the number of cores. That's not really worth an early
> mapping. I would move to using DT to count the number of cores. Support
> for that is already in place.

What's the functions for that?
>From my point of view is better to read it directly on SoC and not to read
dts. It should be also faster.

>> On the other hand you can't use this static mapping when kernel runs
>> for that you need to use dynamic allocation.
>> Calling ioremap so early caused that the return address is the same
>> and you can just use it later. and it is also in the correct vmalloc area.
>>
>> We are using scu_base for power management code.
>> Let me check if dynamic mapping is also required.
>
> The should be no reason you need both.

I have done some tests and that ioremap could be probably removed.
Pawel: You are the author of this code in vexpress. Is there any
particular reason
to have there that ioremap?

Thanks,
Michal


-- 
Michal Simek, Ing. (M.Eng)
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/
Maintainer of Linux kernel - Xilinx Zynq ARM architecture
Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform

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

* [PATCH 05/10] arm: zynq: Move slcr initialization to separate file
  2013-03-25 13:53 ` [PATCH 05/10] arm: zynq: Move slcr initialization to separate file Michal Simek
@ 2013-03-25 16:19   ` Steffen Trumtrar
  2013-03-25 16:37     ` Michal Simek
  0 siblings, 1 reply; 32+ messages in thread
From: Steffen Trumtrar @ 2013-03-25 16:19 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Mar 25, 2013 at 02:53:11PM +0100, Michal Simek wrote:
> Create separate slcr driver instead of pollute common code.
> 
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> ---
>  arch/arm/mach-zynq/Makefile |    2 +-
>  arch/arm/mach-zynq/common.c |   10 +------
>  arch/arm/mach-zynq/common.h |    3 ++
>  arch/arm/mach-zynq/slcr.c   |   69 +++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 74 insertions(+), 10 deletions(-)
>  create mode 100644 arch/arm/mach-zynq/slcr.c
> 
> diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
> index 320faed..13ee09b 100644
> --- a/arch/arm/mach-zynq/Makefile
> +++ b/arch/arm/mach-zynq/Makefile
> @@ -3,4 +3,4 @@
>  #
>  
>  # Common support
> -obj-y				:= common.o
> +obj-y				:= common.o slcr.o
> diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
> index 13f9d8b..2734bd6 100644
> --- a/arch/arm/mach-zynq/common.c
> +++ b/arch/arm/mach-zynq/common.c
> @@ -61,15 +61,7 @@ static void __init xilinx_init_machine(void)
>  
>  static void __init xilinx_zynq_timer_init(void)
>  {
> -	struct device_node *np;
> -	void __iomem *slcr;
> -
> -	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
> -	slcr = of_iomap(np, 0);
> -	WARN_ON(!slcr);
> -
> -	xilinx_zynq_clocks_init(slcr);
> -
> +	slcr_init();
>  	clocksource_of_init();
>  }
>  
> diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
> index 38727a2..e30898a 100644
> --- a/arch/arm/mach-zynq/common.h
> +++ b/arch/arm/mach-zynq/common.h
> @@ -17,6 +17,9 @@
>  #ifndef __MACH_ZYNQ_COMMON_H__
>  #define __MACH_ZYNQ_COMMON_H__
>  
> +extern int slcr_init(void);
> +
> +extern void __iomem *zynq_slcr_base;
>  extern void __iomem *scu_base;
>  
>  #endif
> diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c
> new file mode 100644
> index 0000000..1883b70
> --- /dev/null
> +++ b/arch/arm/mach-zynq/slcr.c
> @@ -0,0 +1,69 @@
> +/*
> + * Xilinx SLCR driver
> + *
> + * Copyright (c) 2011-2013 Xilinx Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; if not, write to the Free
> + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
> + * 02139, USA.
> + */
> +
> +#include <linux/export.h>
> +#include <linux/io.h>
> +#include <linux/fs.h>
> +#include <linux/interrupt.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/uaccess.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/clk/zynq.h>
> +#include "common.h"
> +
> +#define SLCR_UNLOCK_MAGIC		0xDF0D
> +#define SLCR_UNLOCK			0x8   /* SCLR unlock register */
--------------------------------------------------^ small typo
> +
> +void __iomem *zynq_slcr_base;
> +
> +/**
> + * xslcr_init()
> + * Returns 0 on success, negative errno otherwise.
> + *
> + * Called early during boot from platform code to remap SLCR area.
> + */
> +int __init slcr_init(void)
> +{
> +	struct device_node *np;
> +
> +	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
> +	if (!np) {
> +		pr_err("%s: no slcr node found\n", __func__);
> +		BUG();
> +	}
> +
> +	zynq_slcr_base = of_iomap(np, 0);
> +	if (!zynq_slcr_base) {
> +		pr_err("%s: Unable to map I/O memory\n", __func__);
> +		BUG();
> +	}
> +
> +	/* unlock the SLCR so that registers can be changed */
> +	writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK);
> +
> +	pr_info("%s mapped to %p\n", np->name, zynq_slcr_base);
> +
> +	xilinx_zynq_clocks_init(zynq_slcr_base);
> +
> +	of_node_put(np);
> +
> +	return 0;
> +}

The slcr should definitely get it's own driver. I like that.
But wouldn't it be better to call this in the map_io call and then, for the time
being, assume that the slcr is unlocked and put the clocks_init-call in
zynq_timer_init?

Regards,
Steffen

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* [PATCH 03/10] arm: zynq: Move timer to generic location
  2013-03-25 16:01   ` Steffen Trumtrar
@ 2013-03-25 16:24     ` Michal Simek
  0 siblings, 0 replies; 32+ messages in thread
From: Michal Simek @ 2013-03-25 16:24 UTC (permalink / raw)
  To: linux-arm-kernel

2013/3/25 Steffen Trumtrar <s.trumtrar@pengutronix.de>:
> Hi Michal!
>
> On Mon, Mar 25, 2013 at 02:53:09PM +0100, Michal Simek wrote:
>> Move zynq timer out of mach folder to generic location.
>>
>> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
>> ---
>> Arnd: This is the last patch as we discuss about moving
>> this timer to generic location.
>>
>> It depends on ARCH_ZYNQ as Tegra and others.
>> It should be ok to use it or we can use different name if you like.
>> ---
>
> Moving out of mach-zynq is good, because this is a
>         Cadence Rev06 Triple Timer Counter (see Zynq-7000 TRM page 25)
> Which leads me to a proposed rename to: cadence_timer.c or something like that.
> And the fn-names should be changed accordingly while at it.

Agree. Let me do one middle patch which will just remove all zynq reference
before we can move it to generic location.


>>  arch/arm/mach-zynq/Makefile      |    2 +-
>>  arch/arm/mach-zynq/timer.c       |  430 --------------------------------------
>>  drivers/clocksource/Makefile     |    1 +
>>  drivers/clocksource/zynq_timer.c |  430 ++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 432 insertions(+), 431 deletions(-)
>>  delete mode 100644 arch/arm/mach-zynq/timer.c
>>  create mode 100644 drivers/clocksource/zynq_timer.c
>>
>> diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
>> index 397268c..320faed 100644
>> --- a/arch/arm/mach-zynq/Makefile
>> +++ b/arch/arm/mach-zynq/Makefile
>> @@ -3,4 +3,4 @@
>>  #
>>
>>  # Common support
>> -obj-y                                := common.o timer.o
>> +obj-y                                := common.o
>> diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c
>> deleted file mode 100644
>> index 3b1faed..0000000
>> --- a/arch/arm/mach-zynq/timer.c
>> +++ /dev/null
>> @@ -1,430 +0,0 @@
>> -/*
>> - * This file contains driver for the Xilinx PS Timer Counter IP.
>> - *
>> - *  Copyright (C) 2011 Xilinx
>> - *
>> - * based on arch/mips/kernel/time.c timer driver
>> - *
>> - * This software is licensed under the terms of the GNU General Public
>> - * License version 2, as published by the Free Software Foundation, and
>> - * may be copied, distributed, and modified under those terms.
>> - *
>> - * This program is distributed in the hope that it will be useful,
>> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> - * GNU General Public License for more details.
>> - */
>> -
>> -#include <linux/clk.h>
>> -#include <linux/interrupt.h>
>> -#include <linux/clockchips.h>
>> -#include <linux/of_address.h>
>> -#include <linux/of_irq.h>
>> -#include <linux/slab.h>
>> -#include <linux/clk-provider.h>
>> -
>> -/*
>> - * This driver configures the 2 16-bit count-up timers as follows:
>> - *
>> - * T1: Timer 1, clocksource for generic timekeeping
>> - * T2: Timer 2, clockevent source for hrtimers
>> - * T3: Timer 3, <unused>
>> - *
>> - * The input frequency to the timer module for emulation is 2.5MHz which is
>> - * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
>> - * the timers are clocked at 78.125KHz (12.8 us resolution).
>> -
>> - * The input frequency to the timer module in silicon is configurable and
>> - * obtained from device tree. The pre-scaler of 32 is used.
>> - */
>> -
>> -/*
>> - * Timer Register Offset Definitions of Timer 1, Increment base address by 4
>> - * and use same offsets for Timer 2
>> - */
>> -#define XTTCPS_CLK_CNTRL_OFFSET              0x00 /* Clock Control Reg, RW */
>> -#define XTTCPS_CNT_CNTRL_OFFSET              0x0C /* Counter Control Reg, RW */
>> -#define XTTCPS_COUNT_VAL_OFFSET              0x18 /* Counter Value Reg, RO */
>> -#define XTTCPS_INTR_VAL_OFFSET               0x24 /* Interval Count Reg, RW */
>> -#define XTTCPS_ISR_OFFSET            0x54 /* Interrupt Status Reg, RO */
>> -#define XTTCPS_IER_OFFSET            0x60 /* Interrupt Enable Reg, RW */
>> -
>> -#define XTTCPS_CNT_CNTRL_DISABLE_MASK        0x1
>> -
>> -/*
>> - * Setup the timers to use pre-scaling, using a fixed value for now that will
>> - * work across most input frequency, but it may need to be more dynamic
>> - */
>> -#define PRESCALE_EXPONENT    11      /* 2 ^ PRESCALE_EXPONENT = PRESCALE */
>> -#define PRESCALE             2048    /* The exponent must match this */
>> -#define CLK_CNTRL_PRESCALE   ((PRESCALE_EXPONENT - 1) << 1)
>> -#define CLK_CNTRL_PRESCALE_EN        1
>> -#define CNT_CNTRL_RESET              (1 << 4)
>> -
>> -/**
>> - * struct xttcps_timer - This definition defines local timer structure
>> - *
>> - * @base_addr:       Base address of timer
>> - * @clk:     Associated clock source
>> - * @clk_rate_change_nb       Notifier block for clock rate changes
>> - */
>> -struct xttcps_timer {
>> -     void __iomem *base_addr;
>> -     struct clk *clk;
>> -     struct notifier_block clk_rate_change_nb;
>> -};
>> -
>> -#define to_xttcps_timer(x) \
>> -             container_of(x, struct xttcps_timer, clk_rate_change_nb)
>> -
>> -struct xttcps_timer_clocksource {
>> -     struct xttcps_timer     xttc;
>> -     struct clocksource      cs;
>> -};
>> -
>> -#define to_xttcps_timer_clksrc(x) \
>> -             container_of(x, struct xttcps_timer_clocksource, cs)
>> -
>> -struct xttcps_timer_clockevent {
>> -     struct xttcps_timer             xttc;
>> -     struct clock_event_device       ce;
>> -};
>> -
>> -#define to_xttcps_timer_clkevent(x) \
>> -             container_of(x, struct xttcps_timer_clockevent, ce)
>> -
>> -/**
>> - * xttcps_set_interval - Set the timer interval value
>> - *
>> - * @timer:   Pointer to the timer instance
>> - * @cycles:  Timer interval ticks
>> - **/
>> -static void xttcps_set_interval(struct xttcps_timer *timer,
>> -                                     unsigned long cycles)
>> -{
>> -     u32 ctrl_reg;
>> -
>> -     /* Disable the counter, set the counter value  and re-enable counter */
>> -     ctrl_reg = __raw_readl(timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> -     ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
>> -     __raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> -
>> -     __raw_writel(cycles, timer->base_addr + XTTCPS_INTR_VAL_OFFSET);
>> -
>> -     /*
>> -      * Reset the counter (0x10) so that it starts from 0, one-shot
>> -      * mode makes this needed for timing to be right.
>> -      */
>> -     ctrl_reg |= CNT_CNTRL_RESET;
>> -     ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
>> -     __raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> -}
>> -
>> -/**
>> - * xttcps_clock_event_interrupt - Clock event timer interrupt handler
>> - *
>> - * @irq:     IRQ number of the Timer
>> - * @dev_id:  void pointer to the xttcps_timer instance
>> - *
>> - * returns: Always IRQ_HANDLED - success
>> - **/
>> -static irqreturn_t xttcps_clock_event_interrupt(int irq, void *dev_id)
>> -{
>> -     struct xttcps_timer_clockevent *xttce = dev_id;
>> -     struct xttcps_timer *timer = &xttce->xttc;
>> -
>> -     /* Acknowledge the interrupt and call event handler */
>> -     __raw_readl(timer->base_addr + XTTCPS_ISR_OFFSET);
>> -
>> -     xttce->ce.event_handler(&xttce->ce);
>> -
>> -     return IRQ_HANDLED;
>> -}
>> -
>> -/**
>> - * __xttc_clocksource_read - Reads the timer counter register
>> - *
>> - * returns: Current timer counter register value
>> - **/
>> -static cycle_t __xttc_clocksource_read(struct clocksource *cs)
>> -{
>> -     struct xttcps_timer *timer = &to_xttcps_timer_clksrc(cs)->xttc;
>> -
>> -     return (cycle_t)__raw_readl(timer->base_addr +
>> -                             XTTCPS_COUNT_VAL_OFFSET);
>> -}
>> -
>> -/**
>> - * xttcps_set_next_event - Sets the time interval for next event
>> - *
>> - * @cycles:  Timer interval ticks
>> - * @evt:     Address of clock event instance
>> - *
>> - * returns: Always 0 - success
>> - **/
>> -static int xttcps_set_next_event(unsigned long cycles,
>> -                                     struct clock_event_device *evt)
>> -{
>> -     struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
>> -     struct xttcps_timer *timer = &xttce->xttc;
>> -
>> -     xttcps_set_interval(timer, cycles);
>> -     return 0;
>> -}
>> -
>> -/**
>> - * xttcps_set_mode - Sets the mode of timer
>> - *
>> - * @mode:    Mode to be set
>> - * @evt:     Address of clock event instance
>> - **/
>> -static void xttcps_set_mode(enum clock_event_mode mode,
>> -                                     struct clock_event_device *evt)
>> -{
>> -     struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
>> -     struct xttcps_timer *timer = &xttce->xttc;
>> -     u32 ctrl_reg;
>> -
>> -     switch (mode) {
>> -     case CLOCK_EVT_MODE_PERIODIC:
>> -             xttcps_set_interval(timer,
>> -                             DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk),
>> -                                     PRESCALE * HZ));
>> -             break;
>> -     case CLOCK_EVT_MODE_ONESHOT:
>> -     case CLOCK_EVT_MODE_UNUSED:
>> -     case CLOCK_EVT_MODE_SHUTDOWN:
>> -             ctrl_reg = __raw_readl(timer->base_addr +
>> -                                     XTTCPS_CNT_CNTRL_OFFSET);
>> -             ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
>> -             __raw_writel(ctrl_reg,
>> -                             timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> -             break;
>> -     case CLOCK_EVT_MODE_RESUME:
>> -             ctrl_reg = __raw_readl(timer->base_addr +
>> -                                     XTTCPS_CNT_CNTRL_OFFSET);
>> -             ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
>> -             __raw_writel(ctrl_reg,
>> -                             timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> -             break;
>> -     }
>> -}
>> -
>> -static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb,
>> -             unsigned long event, void *data)
>> -{
>> -     struct clk_notifier_data *ndata = data;
>> -     struct xttcps_timer *xttcps = to_xttcps_timer(nb);
>> -     struct xttcps_timer_clocksource *xttccs = container_of(xttcps,
>> -                     struct xttcps_timer_clocksource, xttc);
>> -
>> -     switch (event) {
>> -     case POST_RATE_CHANGE:
>> -             /*
>> -              * Do whatever is necessare to maintain a proper time base
>> -              *
>> -              * I cannot find a way to adjust the currently used clocksource
>> -              * to the new frequency. __clocksource_updatefreq_hz() sounds
>> -              * good, but does not work. Not sure what's that missing.
>> -              *
>> -              * This approach works, but triggers two clocksource switches.
>> -              * The first after unregister to clocksource jiffies. And
>> -              * another one after the register to the newly registered timer.
>> -              *
>> -              * Alternatively we could 'waste' another HW timer to ping pong
>> -              * between clock sources. That would also use one register and
>> -              * one unregister call, but only trigger one clocksource switch
>> -              * for the cost of another HW timer used by the OS.
>> -              */
>> -             clocksource_unregister(&xttccs->cs);
>> -             clocksource_register_hz(&xttccs->cs,
>> -                             ndata->new_rate / PRESCALE);
>> -             /* fall through */
>> -     case PRE_RATE_CHANGE:
>> -     case ABORT_RATE_CHANGE:
>> -     default:
>> -             return NOTIFY_DONE;
>> -     }
>> -}
>> -
>> -static void __init zynq_ttc_setup_clocksource(struct clk *clk,
>> -                                                     void __iomem *base)
>> -{
>> -     struct xttcps_timer_clocksource *ttccs;
>> -     int err;
>> -
>> -     ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
>> -     if (WARN_ON(!ttccs))
>> -             return;
>> -
>> -     ttccs->xttc.clk = clk;
>> -
>> -     err = clk_prepare_enable(ttccs->xttc.clk);
>> -     if (WARN_ON(err))
>> -             return;
>> -
>> -     ttccs->xttc.clk_rate_change_nb.notifier_call =
>> -             xttcps_rate_change_clocksource_cb;
>> -     ttccs->xttc.clk_rate_change_nb.next = NULL;
>> -     if (clk_notifier_register(ttccs->xttc.clk,
>> -                             &ttccs->xttc.clk_rate_change_nb))
>> -             pr_warn("Unable to register clock notifier.\n");
>> -
>> -     ttccs->xttc.base_addr = base;
>> -     ttccs->cs.name = "xttcps_clocksource";
>> -     ttccs->cs.rating = 200;
>> -     ttccs->cs.read = __xttc_clocksource_read;
>> -     ttccs->cs.mask = CLOCKSOURCE_MASK(16);
>> -     ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
>> -
>> -     /*
>> -      * Setup the clock source counter to be an incrementing counter
>> -      * with no interrupt and it rolls over at 0xFFFF. Pre-scale
>> -      * it by 32 also. Let it start running now.
>> -      */
>> -     __raw_writel(0x0,  ttccs->xttc.base_addr + XTTCPS_IER_OFFSET);
>> -     __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
>> -                  ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
>> -     __raw_writel(CNT_CNTRL_RESET,
>> -                  ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> -
>> -     err = clocksource_register_hz(&ttccs->cs,
>> -                     clk_get_rate(ttccs->xttc.clk) / PRESCALE);
>> -     if (WARN_ON(err))
>> -             return;
>> -
>> -}
>> -
>> -static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
>> -             unsigned long event, void *data)
>> -{
>> -     struct clk_notifier_data *ndata = data;
>> -     struct xttcps_timer *xttcps = to_xttcps_timer(nb);
>> -     struct xttcps_timer_clockevent *xttcce = container_of(xttcps,
>> -                     struct xttcps_timer_clockevent, xttc);
>> -
>> -     switch (event) {
>> -     case POST_RATE_CHANGE:
>> -     {
>> -             unsigned long flags;
>> -
>> -             /*
>> -              * clockevents_update_freq should be called with IRQ disabled on
>> -              * the CPU the timer provides events for. The timer we use is
>> -              * common to both CPUs, not sure if we need to run on both
>> -              * cores.
>> -              */
>> -             local_irq_save(flags);
>> -             clockevents_update_freq(&xttcce->ce,
>> -                             ndata->new_rate / PRESCALE);
>> -             local_irq_restore(flags);
>> -
>> -             /* fall through */
>> -     }
>> -     case PRE_RATE_CHANGE:
>> -     case ABORT_RATE_CHANGE:
>> -     default:
>> -             return NOTIFY_DONE;
>> -     }
>> -}
>> -
>> -static void __init zynq_ttc_setup_clockevent(struct clk *clk,
>> -                                             void __iomem *base, u32 irq)
>> -{
>> -     struct xttcps_timer_clockevent *ttcce;
>> -     int err;
>> -
>> -     ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
>> -     if (WARN_ON(!ttcce))
>> -             return;
>> -
>> -     ttcce->xttc.clk = clk;
>> -
>> -     err = clk_prepare_enable(ttcce->xttc.clk);
>> -     if (WARN_ON(err))
>> -             return;
>> -
>> -     ttcce->xttc.clk_rate_change_nb.notifier_call =
>> -             xttcps_rate_change_clockevent_cb;
>> -     ttcce->xttc.clk_rate_change_nb.next = NULL;
>> -     if (clk_notifier_register(ttcce->xttc.clk,
>> -                             &ttcce->xttc.clk_rate_change_nb))
>> -             pr_warn("Unable to register clock notifier.\n");
>> -
>> -     ttcce->xttc.base_addr = base;
>> -     ttcce->ce.name = "xttcps_clockevent";
>> -     ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
>> -     ttcce->ce.set_next_event = xttcps_set_next_event;
>> -     ttcce->ce.set_mode = xttcps_set_mode;
>> -     ttcce->ce.rating = 200;
>> -     ttcce->ce.irq = irq;
>> -     ttcce->ce.cpumask = cpu_possible_mask;
>> -
>> -     /*
>> -      * Setup the clock event timer to be an interval timer which
>> -      * is prescaled by 32 using the interval interrupt. Leave it
>> -      * disabled for now.
>> -      */
>> -     __raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> -     __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
>> -                  ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
>> -     __raw_writel(0x1,  ttcce->xttc.base_addr + XTTCPS_IER_OFFSET);
>> -
>> -     err = request_irq(irq, xttcps_clock_event_interrupt,
>> -                       IRQF_DISABLED | IRQF_TIMER,
>> -                       ttcce->ce.name, ttcce);
>> -     if (WARN_ON(err))
>> -             return;
>> -
>> -     clockevents_config_and_register(&ttcce->ce,
>> -                     clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe);
>> -}
>> -
>> -/**
>> - * xttcps_timer_init - Initialize the timer
>> - *
>> - * Initializes the timer hardware and register the clock source and clock event
>> - * timers with Linux kernal timer framework
>> - */
>> -static void __init xttcps_timer_init(struct device_node *timer)
>> -{
>> -     unsigned int irq;
>> -     void __iomem *timer_baseaddr;
>> -     struct clk *clk;
>> -     static int initialized;
>> -
>> -     if (initialized)
>> -             return;
>> -
>> -     initialized = 1;
>> -
>> -     /*
>> -      * Get the 1st Triple Timer Counter (TTC) block from the device tree
>> -      * and use it. Note that the event timer uses the interrupt and it's the
>> -      * 2nd TTC hence the irq_of_parse_and_map(,1)
>> -      */
>> -     timer_baseaddr = of_iomap(timer, 0);
>> -     if (!timer_baseaddr) {
>> -             pr_err("ERROR: invalid timer base address\n");
>> -             BUG();
>> -     }
>> -
>> -     irq = irq_of_parse_and_map(timer, 1);
>> -     if (irq <= 0) {
>> -             pr_err("ERROR: invalid interrupt number\n");
>> -             BUG();
>> -     }
>> -
>> -     clk = of_clk_get_by_name(timer, "cpu_1x");
>> -     if (IS_ERR(clk)) {
>> -             pr_err("ERROR: timer input clock not found\n");
>> -             BUG();
>> -     }
>> -
>> -     zynq_ttc_setup_clocksource(clk, timer_baseaddr);
>> -     zynq_ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
>> -
>> -     pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
>> -}
>> -
>> -CLOCKSOURCE_OF_DECLARE(zynq, "xlnx,ttc", xttcps_timer_init);
>> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
>> index 4d8283a..665af7f 100644
>> --- a/drivers/clocksource/Makefile
>> +++ b/drivers/clocksource/Makefile
>> @@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_BCM2835)  += bcm2835_timer.o
>>  obj-$(CONFIG_SUNXI_TIMER)    += sunxi_timer.o
>>  obj-$(CONFIG_ARCH_TEGRA)     += tegra20_timer.o
>>  obj-$(CONFIG_VT8500_TIMER)   += vt8500_timer.o
>> +obj-$(CONFIG_ARCH_ZYNQ)              += zynq_timer.o
>>
>>  obj-$(CONFIG_ARM_ARCH_TIMER)         += arm_arch_timer.o
>>  obj-$(CONFIG_CLKSRC_METAG_GENERIC)   += metag_generic.o
>> diff --git a/drivers/clocksource/zynq_timer.c b/drivers/clocksource/zynq_timer.c
>> new file mode 100644
>> index 0000000..3b1faed
>> --- /dev/null
>> +++ b/drivers/clocksource/zynq_timer.c
>> @@ -0,0 +1,430 @@
>> +/*
>> + * This file contains driver for the Xilinx PS Timer Counter IP.
>> + *
>> + *  Copyright (C) 2011 Xilinx
>> + *
>> + * based on arch/mips/kernel/time.c timer driver
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/clockchips.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/slab.h>
>> +#include <linux/clk-provider.h>
>> +
>> +/*
>> + * This driver configures the 2 16-bit count-up timers as follows:
>> + *
>> + * T1: Timer 1, clocksource for generic timekeeping
>> + * T2: Timer 2, clockevent source for hrtimers
>> + * T3: Timer 3, <unused>
>> + *
>> + * The input frequency to the timer module for emulation is 2.5MHz which is
>> + * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
>> + * the timers are clocked at 78.125KHz (12.8 us resolution).
>> +
>> + * The input frequency to the timer module in silicon is configurable and
>> + * obtained from device tree. The pre-scaler of 32 is used.
>> + */
>> +
>> +/*
>> + * Timer Register Offset Definitions of Timer 1, Increment base address by 4
>> + * and use same offsets for Timer 2
>> + */
>> +#define XTTCPS_CLK_CNTRL_OFFSET              0x00 /* Clock Control Reg, RW */
>> +#define XTTCPS_CNT_CNTRL_OFFSET              0x0C /* Counter Control Reg, RW */
>> +#define XTTCPS_COUNT_VAL_OFFSET              0x18 /* Counter Value Reg, RO */
>> +#define XTTCPS_INTR_VAL_OFFSET               0x24 /* Interval Count Reg, RW */
>> +#define XTTCPS_ISR_OFFSET            0x54 /* Interrupt Status Reg, RO */
>> +#define XTTCPS_IER_OFFSET            0x60 /* Interrupt Enable Reg, RW */
>> +
>> +#define XTTCPS_CNT_CNTRL_DISABLE_MASK        0x1
>> +
>> +/*
>> + * Setup the timers to use pre-scaling, using a fixed value for now that will
>> + * work across most input frequency, but it may need to be more dynamic
>> + */
>> +#define PRESCALE_EXPONENT    11      /* 2 ^ PRESCALE_EXPONENT = PRESCALE */
>> +#define PRESCALE             2048    /* The exponent must match this */
>> +#define CLK_CNTRL_PRESCALE   ((PRESCALE_EXPONENT - 1) << 1)
>> +#define CLK_CNTRL_PRESCALE_EN        1
>> +#define CNT_CNTRL_RESET              (1 << 4)
>> +
>> +/**
>> + * struct xttcps_timer - This definition defines local timer structure
>> + *
>> + * @base_addr:       Base address of timer
>> + * @clk:     Associated clock source
>> + * @clk_rate_change_nb       Notifier block for clock rate changes
>> + */
>> +struct xttcps_timer {
>> +     void __iomem *base_addr;
>> +     struct clk *clk;
>> +     struct notifier_block clk_rate_change_nb;
>> +};
>> +
>> +#define to_xttcps_timer(x) \
>> +             container_of(x, struct xttcps_timer, clk_rate_change_nb)
>> +
>> +struct xttcps_timer_clocksource {
>> +     struct xttcps_timer     xttc;
>> +     struct clocksource      cs;
>> +};
>> +
>> +#define to_xttcps_timer_clksrc(x) \
>> +             container_of(x, struct xttcps_timer_clocksource, cs)
>> +
>> +struct xttcps_timer_clockevent {
>> +     struct xttcps_timer             xttc;
>> +     struct clock_event_device       ce;
>> +};
>> +
>> +#define to_xttcps_timer_clkevent(x) \
>> +             container_of(x, struct xttcps_timer_clockevent, ce)
>> +
>> +/**
>> + * xttcps_set_interval - Set the timer interval value
>> + *
>> + * @timer:   Pointer to the timer instance
>> + * @cycles:  Timer interval ticks
>> + **/
>> +static void xttcps_set_interval(struct xttcps_timer *timer,
>> +                                     unsigned long cycles)
>> +{
>> +     u32 ctrl_reg;
>> +
>> +     /* Disable the counter, set the counter value  and re-enable counter */
>> +     ctrl_reg = __raw_readl(timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> +     ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
>> +     __raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> +
>> +     __raw_writel(cycles, timer->base_addr + XTTCPS_INTR_VAL_OFFSET);
>> +
>> +     /*
>> +      * Reset the counter (0x10) so that it starts from 0, one-shot
>> +      * mode makes this needed for timing to be right.
>> +      */
>> +     ctrl_reg |= CNT_CNTRL_RESET;
>> +     ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
>> +     __raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> +}
>> +
>> +/**
>> + * xttcps_clock_event_interrupt - Clock event timer interrupt handler
>> + *
>> + * @irq:     IRQ number of the Timer
>> + * @dev_id:  void pointer to the xttcps_timer instance
>> + *
>> + * returns: Always IRQ_HANDLED - success
>> + **/
>> +static irqreturn_t xttcps_clock_event_interrupt(int irq, void *dev_id)
>> +{
>> +     struct xttcps_timer_clockevent *xttce = dev_id;
>> +     struct xttcps_timer *timer = &xttce->xttc;
>> +
>> +     /* Acknowledge the interrupt and call event handler */
>> +     __raw_readl(timer->base_addr + XTTCPS_ISR_OFFSET);
>> +
>> +     xttce->ce.event_handler(&xttce->ce);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +/**
>> + * __xttc_clocksource_read - Reads the timer counter register
>> + *
>> + * returns: Current timer counter register value
>> + **/
>> +static cycle_t __xttc_clocksource_read(struct clocksource *cs)
>> +{
>> +     struct xttcps_timer *timer = &to_xttcps_timer_clksrc(cs)->xttc;
>> +
>> +     return (cycle_t)__raw_readl(timer->base_addr +
>> +                             XTTCPS_COUNT_VAL_OFFSET);
>> +}
>> +
>> +/**
>> + * xttcps_set_next_event - Sets the time interval for next event
>> + *
>> + * @cycles:  Timer interval ticks
>> + * @evt:     Address of clock event instance
>> + *
>> + * returns: Always 0 - success
>> + **/
>> +static int xttcps_set_next_event(unsigned long cycles,
>> +                                     struct clock_event_device *evt)
>> +{
>> +     struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
>> +     struct xttcps_timer *timer = &xttce->xttc;
>> +
>> +     xttcps_set_interval(timer, cycles);
>> +     return 0;
>> +}
>> +
>> +/**
>> + * xttcps_set_mode - Sets the mode of timer
>> + *
>> + * @mode:    Mode to be set
>> + * @evt:     Address of clock event instance
>> + **/
>> +static void xttcps_set_mode(enum clock_event_mode mode,
>> +                                     struct clock_event_device *evt)
>> +{
>> +     struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
>> +     struct xttcps_timer *timer = &xttce->xttc;
>> +     u32 ctrl_reg;
>> +
>> +     switch (mode) {
>> +     case CLOCK_EVT_MODE_PERIODIC:
>> +             xttcps_set_interval(timer,
>> +                             DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk),
>> +                                     PRESCALE * HZ));
>> +             break;
>> +     case CLOCK_EVT_MODE_ONESHOT:
>> +     case CLOCK_EVT_MODE_UNUSED:
>> +     case CLOCK_EVT_MODE_SHUTDOWN:
>> +             ctrl_reg = __raw_readl(timer->base_addr +
>> +                                     XTTCPS_CNT_CNTRL_OFFSET);
>> +             ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
>> +             __raw_writel(ctrl_reg,
>> +                             timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> +             break;
>> +     case CLOCK_EVT_MODE_RESUME:
>> +             ctrl_reg = __raw_readl(timer->base_addr +
>> +                                     XTTCPS_CNT_CNTRL_OFFSET);
>> +             ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
>> +             __raw_writel(ctrl_reg,
>> +                             timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> +             break;
>> +     }
>> +}
>> +
>> +static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb,
>> +             unsigned long event, void *data)
>> +{
>> +     struct clk_notifier_data *ndata = data;
>> +     struct xttcps_timer *xttcps = to_xttcps_timer(nb);
>> +     struct xttcps_timer_clocksource *xttccs = container_of(xttcps,
>> +                     struct xttcps_timer_clocksource, xttc);
>> +
>> +     switch (event) {
>> +     case POST_RATE_CHANGE:
>> +             /*
>> +              * Do whatever is necessare to maintain a proper time base
> ------------------------------------------^ little typo
>
>> +              *
>> +              * I cannot find a way to adjust the currently used clocksource
>> +              * to the new frequency. __clocksource_updatefreq_hz() sounds
>> +              * good, but does not work. Not sure what's that missing.
>> +              *
>> +              * This approach works, but triggers two clocksource switches.
>> +              * The first after unregister to clocksource jiffies. And
>> +              * another one after the register to the newly registered timer.
>> +              *
>> +              * Alternatively we could 'waste' another HW timer to ping pong
>> +              * between clock sources. That would also use one register and
>> +              * one unregister call, but only trigger one clocksource switch
>> +              * for the cost of another HW timer used by the OS.
>> +              */
>> +             clocksource_unregister(&xttccs->cs);
>> +             clocksource_register_hz(&xttccs->cs,
>> +                             ndata->new_rate / PRESCALE);
>> +             /* fall through */
>> +     case PRE_RATE_CHANGE:
>> +     case ABORT_RATE_CHANGE:
>> +     default:
>> +             return NOTIFY_DONE;
>> +     }
>> +}
>> +
>> +static void __init zynq_ttc_setup_clocksource(struct clk *clk,
>> +                                                     void __iomem *base)
>> +{
>> +     struct xttcps_timer_clocksource *ttccs;
>> +     int err;
>> +
>> +     ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
>> +     if (WARN_ON(!ttccs))
>> +             return;
>> +
>> +     ttccs->xttc.clk = clk;
>> +
>> +     err = clk_prepare_enable(ttccs->xttc.clk);
>> +     if (WARN_ON(err))
>> +             return;
>
> I think you are losing memory here...

right. Let's free that memory. I have pinged Soren to look if we can handle
clock better.
Till now this is only one timer in the system we have used.
Look below.

>
>> +
>> +     ttccs->xttc.clk_rate_change_nb.notifier_call =
>> +             xttcps_rate_change_clocksource_cb;
>> +     ttccs->xttc.clk_rate_change_nb.next = NULL;
>> +     if (clk_notifier_register(ttccs->xttc.clk,
>> +                             &ttccs->xttc.clk_rate_change_nb))
>> +             pr_warn("Unable to register clock notifier.\n");
>> +
>> +     ttccs->xttc.base_addr = base;
>> +     ttccs->cs.name = "xttcps_clocksource";
>> +     ttccs->cs.rating = 200;
>> +     ttccs->cs.read = __xttc_clocksource_read;
>> +     ttccs->cs.mask = CLOCKSOURCE_MASK(16);
>> +     ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
>> +
>> +     /*
>> +      * Setup the clock source counter to be an incrementing counter
>> +      * with no interrupt and it rolls over at 0xFFFF. Pre-scale
>> +      * it by 32 also. Let it start running now.
>> +      */
>> +     __raw_writel(0x0,  ttccs->xttc.base_addr + XTTCPS_IER_OFFSET);
>> +     __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
>> +                  ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
>> +     __raw_writel(CNT_CNTRL_RESET,
>> +                  ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> +
>> +     err = clocksource_register_hz(&ttccs->cs,
>> +                     clk_get_rate(ttccs->xttc.clk) / PRESCALE);
>> +     if (WARN_ON(err))
>> +             return;
> ... and here and below in zynq_ttc_setup_clockevent the same.

yep

>> +
>> +}
>> +
>> +static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
>> +             unsigned long event, void *data)
>> +{
>> +     struct clk_notifier_data *ndata = data;
>> +     struct xttcps_timer *xttcps = to_xttcps_timer(nb);
>> +     struct xttcps_timer_clockevent *xttcce = container_of(xttcps,
>> +                     struct xttcps_timer_clockevent, xttc);
>> +
>> +     switch (event) {
>> +     case POST_RATE_CHANGE:
>> +     {
>> +             unsigned long flags;
>> +
>> +             /*
>> +              * clockevents_update_freq should be called with IRQ disabled on
>> +              * the CPU the timer provides events for. The timer we use is
>> +              * common to both CPUs, not sure if we need to run on both
>> +              * cores.
>> +              */
>> +             local_irq_save(flags);
>> +             clockevents_update_freq(&xttcce->ce,
>> +                             ndata->new_rate / PRESCALE);
>> +             local_irq_restore(flags);
>> +
>> +             /* fall through */
>> +     }
>> +     case PRE_RATE_CHANGE:
>> +     case ABORT_RATE_CHANGE:
>> +     default:
>> +             return NOTIFY_DONE;
>> +     }
>> +}
>> +
>> +static void __init zynq_ttc_setup_clockevent(struct clk *clk,
>> +                                             void __iomem *base, u32 irq)
>> +{
>> +     struct xttcps_timer_clockevent *ttcce;
>> +     int err;
>> +
>> +     ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
>> +     if (WARN_ON(!ttcce))
>> +             return;
>> +
>> +     ttcce->xttc.clk = clk;
>> +
>> +     err = clk_prepare_enable(ttcce->xttc.clk);
>> +     if (WARN_ON(err))
>> +             return;
>> +
>> +     ttcce->xttc.clk_rate_change_nb.notifier_call =
>> +             xttcps_rate_change_clockevent_cb;
>> +     ttcce->xttc.clk_rate_change_nb.next = NULL;
>> +     if (clk_notifier_register(ttcce->xttc.clk,
>> +                             &ttcce->xttc.clk_rate_change_nb))
>> +             pr_warn("Unable to register clock notifier.\n");
>> +
>> +     ttcce->xttc.base_addr = base;
>> +     ttcce->ce.name = "xttcps_clockevent";
>> +     ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
>> +     ttcce->ce.set_next_event = xttcps_set_next_event;
>> +     ttcce->ce.set_mode = xttcps_set_mode;
>> +     ttcce->ce.rating = 200;
>> +     ttcce->ce.irq = irq;
>> +     ttcce->ce.cpumask = cpu_possible_mask;
>> +
>> +     /*
>> +      * Setup the clock event timer to be an interval timer which
>> +      * is prescaled by 32 using the interval interrupt. Leave it
>> +      * disabled for now.
>> +      */
>> +     __raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
>> +     __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
>> +                  ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
>> +     __raw_writel(0x1,  ttcce->xttc.base_addr + XTTCPS_IER_OFFSET);
>> +
>> +     err = request_irq(irq, xttcps_clock_event_interrupt,
>> +                       IRQF_DISABLED | IRQF_TIMER,
>> +                       ttcce->ce.name, ttcce);
>> +     if (WARN_ON(err))
>> +             return;
>> +
>> +     clockevents_config_and_register(&ttcce->ce,
>> +                     clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe);
>> +}
>> +
>> +/**
>> + * xttcps_timer_init - Initialize the timer
>> + *
>> + * Initializes the timer hardware and register the clock source and clock event
>> + * timers with Linux kernal timer framework
>> + */
>> +static void __init xttcps_timer_init(struct device_node *timer)
>> +{
>> +     unsigned int irq;
>> +     void __iomem *timer_baseaddr;
>> +     struct clk *clk;
>> +     static int initialized;
>> +
>> +     if (initialized)
>> +             return;
>> +
>> +     initialized = 1;
>> +
> ??? This looks very strange ???
> I think this should be handled in a different manner.

Feel free to propose better solution. Because there are two ttc in the
system and the first
is taken, second shouldn't be initialized as clocksource or clockevent.

>> +     /*
>> +      * Get the 1st Triple Timer Counter (TTC) block from the device tree
>> +      * and use it. Note that the event timer uses the interrupt and it's the
>> +      * 2nd TTC hence the irq_of_parse_and_map(,1)
>> +      */
>> +     timer_baseaddr = of_iomap(timer, 0);
>> +     if (!timer_baseaddr) {
>> +             pr_err("ERROR: invalid timer base address\n");
>> +             BUG();
>> +     }
>> +
>> +     irq = irq_of_parse_and_map(timer, 1);
>> +     if (irq <= 0) {
>> +             pr_err("ERROR: invalid interrupt number\n");
>> +             BUG();
>> +     }
>> +
>> +     clk = of_clk_get_by_name(timer, "cpu_1x");
>> +     if (IS_ERR(clk)) {
>> +             pr_err("ERROR: timer input clock not found\n");
>> +             BUG();
>> +     }
>> +
>> +     zynq_ttc_setup_clocksource(clk, timer_baseaddr);
>> +     zynq_ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
>> +
>> +     pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
>> +}
>> +
>> +CLOCKSOURCE_OF_DECLARE(zynq, "xlnx,ttc", xttcps_timer_init);
>
> What if do not want to use the ttc as clocksource and therefore don't want it
> enabled? I still have the smp_twd which I might prefere.

Renaming this to cadence name we will have to change dt name to more generic
one cdns,ttc? Also we should probably add it to Kconfig.

IRC: If there is not compatible string kernel will try to find out
different one.
If you find some of them the more precise one is used. It is quite long time
I was playing with it.

Thanks,
Michal


-- 
Michal Simek, Ing. (M.Eng)
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/
Maintainer of Linux kernel - Xilinx Zynq ARM architecture
Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform

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

* [PATCH 08/10] arm: zynq: Add smp support
  2013-03-25 14:16   ` Rob Herring
@ 2013-03-25 16:31     ` Michal Simek
  2013-03-25 22:10       ` Rob Herring
  0 siblings, 1 reply; 32+ messages in thread
From: Michal Simek @ 2013-03-25 16:31 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/25/2013 03:16 PM, Rob Herring wrote:
> On 03/25/2013 08:53 AM, Michal Simek wrote:
>> Zynq is dual core Cortex A9 which starts always
>> at zero. Using simple trampoline ensure long jump
>> to secondary_startup code.
>>
>> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
>> ---
>>   arch/arm/mach-zynq/Makefile  |    1 +
>>   arch/arm/mach-zynq/common.c  |    1 +
>>   arch/arm/mach-zynq/common.h  |    7 ++
>>   arch/arm/mach-zynq/platsmp.c |  160 ++++++++++++++++++++++++++++++++++++++++++
>>   arch/arm/mach-zynq/slcr.c    |   29 ++++++++
>>   5 files changed, 198 insertions(+)
>>   create mode 100644 arch/arm/mach-zynq/platsmp.c
>
> [...]
>
>> +/* Secondary CPU kernel startup is a 2 step process. The primary CPU
>> + * starts the secondary CPU by giving it the address of the kernel and
>> + * then sending it an event to wake it up. The secondary CPU then
>> + * starts the kernel and tells the primary CPU it's up and running.
>> + */
>> +static void __cpuinit zynq_secondary_init(unsigned int cpu)
>> +{
>> +	/*
>> +	 * if any interrupts are already enabled for the primary
>> +	 * core (e.g. timer irq), then they will not have been enabled
>> +	 * for us: do so
>> +	 */
>> +	gic_secondary_init(0);
>> +
>> +	/*
>> +	 * Synchronise with the boot thread.
>> +	 */
>> +	spin_lock(&boot_lock);
>> +	spin_unlock(&boot_lock);
>
> Why do you think this is needed? Platforms that need this
> synchronization are only the ones that just do wfi for hotplug rather
> than properly reseting the core. You appear to do the latter and should
> not need this.

ok.


>> +}
>> +
>> +int __cpuinit zynq_cpun_start(u32 address, int cpu)
>> +{
>> +	if (cpu > ncores) {
>> +		pr_warn("CPU No. is not available in the system\n");
>> +		return -1;
>> +	}
>> +
>> +	/* MS: Expectation that SLCR are directly map and accessible */
>> +	/* Not possible to jump to non aligned address */
>> +	if (!(address & 3) && (!address || (address >= 0xC))) {
>
> What about Thumb2 kernel entry?

I have no idea what's that.
Still more microblaze guy than Arm one.

>
>> +		slcr_cpu_stop(cpu);
>
> Isn't a secondary cpu already stopped?

On the normal boot this is really necessary because first stage bootloader
doesn't stop cpu just keep it in loop and without stopping cpu
and starting it again it doesn't work.

>> +
>> +		/*
>> +		 * This is elegant way how to jump to any address
>> +		 * 0x0: Load address at 0x8 to r0
>> +		 * 0x4: Jump by mov instruction
>> +		 * 0x8: Jumping address
>> +		 */
>> +		if (address) {
>> +			/* 0: ldr r0, [8] */
>> +			__raw_writel(0xe59f0000, phys_to_virt(0x0));
>> +			/* 4: mov pc, r0 */
>> +			__raw_writel(0xe1a0f000, phys_to_virt(0x4));
>> +			__raw_writel(address, phys_to_virt(0x8));
>> +		}
>> +
>> +		flush_cache_all();
>> +		outer_flush_all();
>
> You should only need to flush range on address 0-4 here.

ok.

Thanks,
Michal



-- 
Michal Simek, Ing. (M.Eng)
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/
Microblaze U-BOOT custodian

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

* [PATCH 05/10] arm: zynq: Move slcr initialization to separate file
  2013-03-25 16:19   ` Steffen Trumtrar
@ 2013-03-25 16:37     ` Michal Simek
  0 siblings, 0 replies; 32+ messages in thread
From: Michal Simek @ 2013-03-25 16:37 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/25/2013 05:19 PM, Steffen Trumtrar wrote:
> On Mon, Mar 25, 2013 at 02:53:11PM +0100, Michal Simek wrote:
>> Create separate slcr driver instead of pollute common code.
>>
>> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
>> ---
>>   arch/arm/mach-zynq/Makefile |    2 +-
>>   arch/arm/mach-zynq/common.c |   10 +------
>>   arch/arm/mach-zynq/common.h |    3 ++
>>   arch/arm/mach-zynq/slcr.c   |   69 +++++++++++++++++++++++++++++++++++++++++++
>>   4 files changed, 74 insertions(+), 10 deletions(-)
>>   create mode 100644 arch/arm/mach-zynq/slcr.c
>>
>> diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
>> index 320faed..13ee09b 100644
>> --- a/arch/arm/mach-zynq/Makefile
>> +++ b/arch/arm/mach-zynq/Makefile
>> @@ -3,4 +3,4 @@
>>   #
>>
>>   # Common support
>> -obj-y				:= common.o
>> +obj-y				:= common.o slcr.o
>> diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
>> index 13f9d8b..2734bd6 100644
>> --- a/arch/arm/mach-zynq/common.c
>> +++ b/arch/arm/mach-zynq/common.c
>> @@ -61,15 +61,7 @@ static void __init xilinx_init_machine(void)
>>
>>   static void __init xilinx_zynq_timer_init(void)
>>   {
>> -	struct device_node *np;
>> -	void __iomem *slcr;
>> -
>> -	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
>> -	slcr = of_iomap(np, 0);
>> -	WARN_ON(!slcr);
>> -
>> -	xilinx_zynq_clocks_init(slcr);
>> -
>> +	slcr_init();
>>   	clocksource_of_init();
>>   }
>>
>> diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
>> index 38727a2..e30898a 100644
>> --- a/arch/arm/mach-zynq/common.h
>> +++ b/arch/arm/mach-zynq/common.h
>> @@ -17,6 +17,9 @@
>>   #ifndef __MACH_ZYNQ_COMMON_H__
>>   #define __MACH_ZYNQ_COMMON_H__
>>
>> +extern int slcr_init(void);
>> +
>> +extern void __iomem *zynq_slcr_base;
>>   extern void __iomem *scu_base;
>>
>>   #endif
>> diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c
>> new file mode 100644
>> index 0000000..1883b70
>> --- /dev/null
>> +++ b/arch/arm/mach-zynq/slcr.c
>> @@ -0,0 +1,69 @@
>> +/*
>> + * Xilinx SLCR driver
>> + *
>> + * Copyright (c) 2011-2013 Xilinx Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version
>> + * 2 of the License, or (at your option) any later version.
>> + *
>> + * You should have received a copy of the GNU General Public
>> + * License along with this program; if not, write to the Free
>> + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
>> + * 02139, USA.
>> + */
>> +
>> +#include <linux/export.h>
>> +#include <linux/io.h>
>> +#include <linux/fs.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/init.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of_address.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/string.h>
>> +#include <linux/clk/zynq.h>
>> +#include "common.h"
>> +
>> +#define SLCR_UNLOCK_MAGIC		0xDF0D
>> +#define SLCR_UNLOCK			0x8   /* SCLR unlock register */
> --------------------------------------------------^ small typo
>> +
>> +void __iomem *zynq_slcr_base;
>> +
>> +/**
>> + * xslcr_init()
>> + * Returns 0 on success, negative errno otherwise.
>> + *
>> + * Called early during boot from platform code to remap SLCR area.
>> + */
>> +int __init slcr_init(void)
>> +{
>> +	struct device_node *np;
>> +
>> +	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
>> +	if (!np) {
>> +		pr_err("%s: no slcr node found\n", __func__);
>> +		BUG();
>> +	}
>> +
>> +	zynq_slcr_base = of_iomap(np, 0);
>> +	if (!zynq_slcr_base) {
>> +		pr_err("%s: Unable to map I/O memory\n", __func__);
>> +		BUG();
>> +	}
>> +
>> +	/* unlock the SLCR so that registers can be changed */
>> +	writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK);
>> +
>> +	pr_info("%s mapped to %p\n", np->name, zynq_slcr_base);
>> +
>> +	xilinx_zynq_clocks_init(zynq_slcr_base);
>> +
>> +	of_node_put(np);
>> +
>> +	return 0;
>> +}
>
> The slcr should definitely get it's own driver. I like that.
> But wouldn't it be better to call this in the map_io call and then, for the time
> being, assume that the slcr is unlocked and put the clocks_init-call in
> zynq_timer_init?

map_io can work with static mapping. Dynamic mapping only doesn't work in that time.
That's why it must be done later.
The reason why I call xilinx_zynq_clocks_init() from this code is
that slcr IP contains almost everything and clocks are logical subnode of this device.

Thanks,
Michal
-- 
Michal Simek, Ing. (M.Eng)
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/
Microblaze U-BOOT custodian

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

* [PATCH 08/10] arm: zynq: Add smp support
  2013-03-25 16:31     ` Michal Simek
@ 2013-03-25 22:10       ` Rob Herring
  2013-03-26  7:42         ` Michal Simek
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Herring @ 2013-03-25 22:10 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/25/2013 11:31 AM, Michal Simek wrote:
> On 03/25/2013 03:16 PM, Rob Herring wrote:
>> On 03/25/2013 08:53 AM, Michal Simek wrote:
>>> Zynq is dual core Cortex A9 which starts always
>>> at zero. Using simple trampoline ensure long jump
>>> to secondary_startup code.
>>>
>>> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
>>> ---
>>>   arch/arm/mach-zynq/Makefile  |    1 +
>>>   arch/arm/mach-zynq/common.c  |    1 +
>>>   arch/arm/mach-zynq/common.h  |    7 ++
>>>   arch/arm/mach-zynq/platsmp.c |  160
>>> ++++++++++++++++++++++++++++++++++++++++++
>>>   arch/arm/mach-zynq/slcr.c    |   29 ++++++++
>>>   5 files changed, 198 insertions(+)
>>>   create mode 100644 arch/arm/mach-zynq/platsmp.c

[...]

>>> +}
>>> +
>>> +int __cpuinit zynq_cpun_start(u32 address, int cpu)
>>> +{
>>> +    if (cpu > ncores) {
>>> +        pr_warn("CPU No. is not available in the system\n");
>>> +        return -1;
>>> +    }
>>> +
>>> +    /* MS: Expectation that SLCR are directly map and accessible */
>>> +    /* Not possible to jump to non aligned address */
>>> +    if (!(address & 3) && (!address || (address >= 0xC))) {
>>
>> What about Thumb2 kernel entry?
> 
> I have no idea what's that.
> Still more microblaze guy than Arm one.

It's the 16-bit (mostly) instruction mode. Why it matters here is bit 0
being set in an address will trigger a switch to Thumb mode in a bx/blx
instruction. So you can't really check for alignment as only 0x2 would
not be allowed. More below...

> 
>>
>>> +        slcr_cpu_stop(cpu);
>>
>> Isn't a secondary cpu already stopped?
> 
> On the normal boot this is really necessary because first stage bootloader
> doesn't stop cpu just keep it in loop and without stopping cpu
> and starting it again it doesn't work.

And there is no way to exit the loop other than a reset?

So for for hotplug this would not be needed. Perhaps .smp_prepare_cpus
is a better spot for this.

If you can change the bootloader, then you should look at doing PSCI
support. Here's some information:

http://lca-13.zerista.com/files_user/attachments/9311/psci_update.pdf

I've also submitted highbank patches which add support for PSCI.

>>> +
>>> +        /*
>>> +         * This is elegant way how to jump to any address
>>> +         * 0x0: Load address at 0x8 to r0
>>> +         * 0x4: Jump by mov instruction
>>> +         * 0x8: Jumping address
>>> +         */
>>> +        if (address) {
>>> +            /* 0: ldr r0, [8] */
>>> +            __raw_writel(0xe59f0000, phys_to_virt(0x0));
>>> +            /* 4: mov pc, r0 */
>>> +            __raw_writel(0xe1a0f000, phys_to_virt(0x4));

This should be a "bx r0" to work with Thumb2 entry address.

Also, this part of the setup could be one time rather than every hotplug.

>>> +            __raw_writel(address, phys_to_virt(0x8));

This should be a per core address including core 0 if you ever want to
do something like cpuidle powergating on one core and hotplug on another.

Rob

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
  2013-03-25 16:07         ` Michal Simek
@ 2013-03-25 22:34           ` Rob Herring
  2013-03-26 10:45             ` Michal Simek
  2013-04-02 16:40           ` Pawel Moll
  1 sibling, 1 reply; 32+ messages in thread
From: Rob Herring @ 2013-03-25 22:34 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/25/2013 11:07 AM, Michal Simek wrote:
> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>> On 03/25/2013 09:51 AM, Michal Simek wrote:
>>> Hi Rob,
>>>
>>> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>>>> On 03/25/2013 08:53 AM, Michal Simek wrote:
>>>>> Use Cortex a9 cp15 to read scu baseaddress.
>>
>> [...]
>>
>>>>> +static void __init scu_init(void)
>>>>> +{
>>>>> +     unsigned long base;
>>>>> +
>>>>> +     base = scu_a9_get_base();
>>>>> +     zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
>>>>> +     zynq_cortex_a9_scu_map.virtual = base;
>>>>
>>>> You are setting the virtual address to the physical base?
>>>>
>>>>> +     iotable_init(&zynq_cortex_a9_scu_map, 1);
>>>>
>>>> Then creating a static mapping...
>>>>
>>>>> +     scu_base = ioremap(base, zynq_cortex_a9_scu_map.length);
>>>>
>>>> And also a dynamic mapping?
>>>
>>> Yes - exactly.
>>
>> You are simply getting lucky that it works. If the physical address did
>> not happen to be in the vmalloc address region, it would not work. You
>> should not do this because you have an implicit requirement and the code
>> will look broken to anyone that reads it.
> 
> yeah correct. I will add there a comment to mentioned that.

I think leaving the define would be better, but either way is fine.

>>> I was talking to Olof about this code at ELC and he mentioned that someone
>>> else might know better way how to do it.
>>>
>>> It is quite a long time I played with this code.
>>> I found this solution in vexpress platform (mach-vexpress/platsmp.c)
>>>
>>> IRC: Static mapping is necessary to be able to access device so early.
>>
>> Only to read the number of cores. That's not really worth an early
>> mapping. I would move to using DT to count the number of cores. Support
>> for that is already in place.
> 
> What's the functions for that?
> From my point of view is better to read it directly on SoC and not to read
> dts. It should be also faster.

Actually, I think you just remove all the code related to
set_cpu_possible().

The relevant function is arm_dt_init_cpu_maps.

Rob

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

* [PATCH 08/10] arm: zynq: Add smp support
  2013-03-25 22:10       ` Rob Herring
@ 2013-03-26  7:42         ` Michal Simek
  2013-04-01 22:40           ` Rob Herring
  0 siblings, 1 reply; 32+ messages in thread
From: Michal Simek @ 2013-03-26  7:42 UTC (permalink / raw)
  To: linux-arm-kernel

2013/3/25 Rob Herring <robherring2@gmail.com>:
> On 03/25/2013 11:31 AM, Michal Simek wrote:
>> On 03/25/2013 03:16 PM, Rob Herring wrote:
>>> On 03/25/2013 08:53 AM, Michal Simek wrote:
>>>> Zynq is dual core Cortex A9 which starts always
>>>> at zero. Using simple trampoline ensure long jump
>>>> to secondary_startup code.
>>>>
>>>> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
>>>> ---
>>>>   arch/arm/mach-zynq/Makefile  |    1 +
>>>>   arch/arm/mach-zynq/common.c  |    1 +
>>>>   arch/arm/mach-zynq/common.h  |    7 ++
>>>>   arch/arm/mach-zynq/platsmp.c |  160
>>>> ++++++++++++++++++++++++++++++++++++++++++
>>>>   arch/arm/mach-zynq/slcr.c    |   29 ++++++++
>>>>   5 files changed, 198 insertions(+)
>>>>   create mode 100644 arch/arm/mach-zynq/platsmp.c
>
> [...]
>
>>>> +}
>>>> +
>>>> +int __cpuinit zynq_cpun_start(u32 address, int cpu)
>>>> +{
>>>> +    if (cpu > ncores) {
>>>> +        pr_warn("CPU No. is not available in the system\n");
>>>> +        return -1;
>>>> +    }
>>>> +
>>>> +    /* MS: Expectation that SLCR are directly map and accessible */
>>>> +    /* Not possible to jump to non aligned address */
>>>> +    if (!(address & 3) && (!address || (address >= 0xC))) {
>>>
>>> What about Thumb2 kernel entry?
>>
>> I have no idea what's that.
>> Still more microblaze guy than Arm one.
>
> It's the 16-bit (mostly) instruction mode. Why it matters here is bit 0
> being set in an address will trigger a switch to Thumb mode in a bx/blx
> instruction. So you can't really check for alignment as only 0x2 would
> not be allowed. More below...

ok.


>
>>
>>>
>>>> +        slcr_cpu_stop(cpu);
>>>
>>> Isn't a secondary cpu already stopped?
>>
>> On the normal boot this is really necessary because first stage bootloader
>> doesn't stop cpu just keep it in loop and without stopping cpu
>> and starting it again it doesn't work.
>
> And there is no way to exit the loop other than a reset?

You can exit the loop by writing jump address to one location where bootloader
expect it. Then it jumps to proper function and it was done like that before.
But this is not suitable for cpu hotplug because that loop is placed
in OCM (on chip memory)
and it can be used for different purpose.
Also there is no way how to return cpu to this mode.

> So for for hotplug this would not be needed. Perhaps .smp_prepare_cpus
> is a better spot for this.

I don't think so because smp_prepare_cpus is called by the kernel just once
in bootup time.
For cpu hotplug I see that only smp_boot_secondary is called.


> If you can change the bootloader, then you should look at doing PSCI
> support. Here's some information:

Unfortunately no.

>
> http://lca-13.zerista.com/files_user/attachments/9311/psci_update.pdf
>
> I've also submitted highbank patches which add support for PSCI.

Interesting. I will look at it. Thanks.


>>>> +
>>>> +        /*
>>>> +         * This is elegant way how to jump to any address
>>>> +         * 0x0: Load address at 0x8 to r0
>>>> +         * 0x4: Jump by mov instruction
>>>> +         * 0x8: Jumping address
>>>> +         */
>>>> +        if (address) {
>>>> +            /* 0: ldr r0, [8] */
>>>> +            __raw_writel(0xe59f0000, phys_to_virt(0x0));
>>>> +            /* 4: mov pc, r0 */
>>>> +            __raw_writel(0xe1a0f000, phys_to_virt(0x4));
>
> This should be a "bx r0" to work with Thumb2 entry address.
>
> Also, this part of the setup could be one time rather than every hotplug.

It is better to copy it there all the time because we have one AMP demo
where rtos runs from 0x0 because there are reset vectors for it.
That's why it is necessary to copy this trampoline code all the time.


>>>> +            __raw_writel(address, phys_to_virt(0x8));
>
> This should be a per core address including core 0 if you ever want to
> do something like cpuidle powergating on one core and hotplug on another.

That's interesting idea.
Please correct me if I am wrong, I didn't play with cpuidle.

Zynq is dual core and hotplug can be done only on cpu1. (not sure if
in general cpu0
can be unplugged too. If yes, are you doing that?).
I didn't play with cpuidle code but I am not quite sure if you can use hotplug
if cpu0 is in idle because this code is for >cpu0.

I can imagine to be more flexible on quad core where your comment make
definitely sense.

Thanks,
Michal

-- 
Michal Simek, Ing. (M.Eng)
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/
Maintainer of Linux kernel - Xilinx Zynq ARM architecture
Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
  2013-03-25 22:34           ` Rob Herring
@ 2013-03-26 10:45             ` Michal Simek
  2013-03-26 12:28               ` Rob Herring
  0 siblings, 1 reply; 32+ messages in thread
From: Michal Simek @ 2013-03-26 10:45 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/25/2013 11:34 PM, Rob Herring wrote:
> On 03/25/2013 11:07 AM, Michal Simek wrote:
>> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>>> On 03/25/2013 09:51 AM, Michal Simek wrote:
>>>> Hi Rob,
>>>>
>>>> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>>>>> On 03/25/2013 08:53 AM, Michal Simek wrote:
>>>>>> Use Cortex a9 cp15 to read scu baseaddress.
>>>
>>> [...]
>>>
>>>>>> +static void __init scu_init(void)
>>>>>> +{
>>>>>> +     unsigned long base;
>>>>>> +
>>>>>> +     base = scu_a9_get_base();
>>>>>> +     zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
>>>>>> +     zynq_cortex_a9_scu_map.virtual = base;
>>>>>
>>>>> You are setting the virtual address to the physical base?
>>>>>
>>>>>> +     iotable_init(&zynq_cortex_a9_scu_map, 1);
>>>>>
>>>>> Then creating a static mapping...
>>>>>
>>>>>> +     scu_base = ioremap(base, zynq_cortex_a9_scu_map.length);
>>>>>
>>>>> And also a dynamic mapping?
>>>>
>>>> Yes - exactly.
>>>
>>> You are simply getting lucky that it works. If the physical address did
>>> not happen to be in the vmalloc address region, it would not work. You
>>> should not do this because you have an implicit requirement and the code
>>> will look broken to anyone that reads it.
>>
>> yeah correct. I will add there a comment to mentioned that.
>
> I think leaving the define would be better, but either way is fine.

ok. From my point of view is better to keep scu where it is and any early vmalloc
allocator will be really helpful. Because there is also early map
for early console where virtual addresses are hardcoded.
It will be just easier to ask generic code to return base if if you request
for size.

static unsigned long iomapstart = VMALLOC_END;

unsigned long get_iomap_virtbase(unsigned long size)
{
	iomapstart -= size;
	return iomapstart;
}

Something like this will be helpful and then instead of setup virtual
addresses for every static io mapping just to call this function
and you will get mapping correct all the time.


>>>> I was talking to Olof about this code at ELC and he mentioned that someone
>>>> else might know better way how to do it.
>>>>
>>>> It is quite a long time I played with this code.
>>>> I found this solution in vexpress platform (mach-vexpress/platsmp.c)
>>>>
>>>> IRC: Static mapping is necessary to be able to access device so early.
>>>
>>> Only to read the number of cores. That's not really worth an early
>>> mapping. I would move to using DT to count the number of cores. Support
>>> for that is already in place.
>>
>> What's the functions for that?
>>  From my point of view is better to read it directly on SoC and not to read
>> dts. It should be also faster.
>
> Actually, I think you just remove all the code related to
> set_cpu_possible().
>
> The relevant function is arm_dt_init_cpu_maps.

Ok. Thanks.

I will look at it but currently I prefer to cound number of cores
via scu because we are using too many dtses.

Thanks,
Michal



-- 
Michal Simek, Ing. (M.Eng)
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/
Microblaze U-BOOT custodian

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
  2013-03-26 10:45             ` Michal Simek
@ 2013-03-26 12:28               ` Rob Herring
  2013-03-26 12:33                 ` Michal Simek
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Herring @ 2013-03-26 12:28 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/26/2013 05:45 AM, Michal Simek wrote:
> On 03/25/2013 11:34 PM, Rob Herring wrote:
>> On 03/25/2013 11:07 AM, Michal Simek wrote:
>>> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>>>> On 03/25/2013 09:51 AM, Michal Simek wrote:
>>>>> Hi Rob,
>>>>>
>>>>> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>>>>>> On 03/25/2013 08:53 AM, Michal Simek wrote:
>>>>>>> Use Cortex a9 cp15 to read scu baseaddress.
>>>>
>>>> [...]
>>>>
>>>>>>> +static void __init scu_init(void)
>>>>>>> +{
>>>>>>> +     unsigned long base;
>>>>>>> +
>>>>>>> +     base = scu_a9_get_base();
>>>>>>> +     zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
>>>>>>> +     zynq_cortex_a9_scu_map.virtual = base;
>>>>>>
>>>>>> You are setting the virtual address to the physical base?
>>>>>>
>>>>>>> +     iotable_init(&zynq_cortex_a9_scu_map, 1);
>>>>>>
>>>>>> Then creating a static mapping...
>>>>>>
>>>>>>> +     scu_base = ioremap(base, zynq_cortex_a9_scu_map.length);
>>>>>>
>>>>>> And also a dynamic mapping?
>>>>>
>>>>> Yes - exactly.
>>>>
>>>> You are simply getting lucky that it works. If the physical address did
>>>> not happen to be in the vmalloc address region, it would not work. You
>>>> should not do this because you have an implicit requirement and the
>>>> code
>>>> will look broken to anyone that reads it.
>>>
>>> yeah correct. I will add there a comment to mentioned that.
>>
>> I think leaving the define would be better, but either way is fine.
> 
> ok. From my point of view is better to keep scu where it is and any
> early vmalloc
> allocator will be really helpful. Because there is also early map
> for early console where virtual addresses are hardcoded.
> It will be just easier to ask generic code to return base if if you request
> for size.
> 
> static unsigned long iomapstart = VMALLOC_END;
> 
> unsigned long get_iomap_virtbase(unsigned long size)
> {
>     iomapstart -= size;
>     return iomapstart;
> }

It wouldn't be so simple with page/section alignment requirements and such.

> Something like this will be helpful and then instead of setup virtual
> addresses for every static io mapping just to call this function
> and you will get mapping correct all the time.

If you use DT for core count, then you don't need the static mapping and
can use ioremap (or of_iomap).

Rob

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
  2013-03-26 12:28               ` Rob Herring
@ 2013-03-26 12:33                 ` Michal Simek
  0 siblings, 0 replies; 32+ messages in thread
From: Michal Simek @ 2013-03-26 12:33 UTC (permalink / raw)
  To: linux-arm-kernel

2013/3/26 Rob Herring <robherring2@gmail.com>:
> On 03/26/2013 05:45 AM, Michal Simek wrote:
>> On 03/25/2013 11:34 PM, Rob Herring wrote:
>>> On 03/25/2013 11:07 AM, Michal Simek wrote:
>>>> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>>>>> On 03/25/2013 09:51 AM, Michal Simek wrote:
>>>>>> Hi Rob,
>>>>>>
>>>>>> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>>>>>>> On 03/25/2013 08:53 AM, Michal Simek wrote:
>>>>>>>> Use Cortex a9 cp15 to read scu baseaddress.
>>>>>
>>>>> [...]
>>>>>
>>>>>>>> +static void __init scu_init(void)
>>>>>>>> +{
>>>>>>>> +     unsigned long base;
>>>>>>>> +
>>>>>>>> +     base = scu_a9_get_base();
>>>>>>>> +     zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
>>>>>>>> +     zynq_cortex_a9_scu_map.virtual = base;
>>>>>>>
>>>>>>> You are setting the virtual address to the physical base?
>>>>>>>
>>>>>>>> +     iotable_init(&zynq_cortex_a9_scu_map, 1);
>>>>>>>
>>>>>>> Then creating a static mapping...
>>>>>>>
>>>>>>>> +     scu_base = ioremap(base, zynq_cortex_a9_scu_map.length);
>>>>>>>
>>>>>>> And also a dynamic mapping?
>>>>>>
>>>>>> Yes - exactly.
>>>>>
>>>>> You are simply getting lucky that it works. If the physical address did
>>>>> not happen to be in the vmalloc address region, it would not work. You
>>>>> should not do this because you have an implicit requirement and the
>>>>> code
>>>>> will look broken to anyone that reads it.
>>>>
>>>> yeah correct. I will add there a comment to mentioned that.
>>>
>>> I think leaving the define would be better, but either way is fine.
>>
>> ok. From my point of view is better to keep scu where it is and any
>> early vmalloc
>> allocator will be really helpful. Because there is also early map
>> for early console where virtual addresses are hardcoded.
>> It will be just easier to ask generic code to return base if if you request
>> for size.
>>
>> static unsigned long iomapstart = VMALLOC_END;
>>
>> unsigned long get_iomap_virtbase(unsigned long size)
>> {
>>     iomapstart -= size;
>>     return iomapstart;
>> }
>
> It wouldn't be so simple with page/section alignment requirements and such.

Adding alignment to it is not so hard.


>> Something like this will be helpful and then instead of setup virtual
>> addresses for every static io mapping just to call this function
>> and you will get mapping correct all the time.
>
> If you use DT for core count, then you don't need the static mapping and
> can use ioremap (or of_iomap).

yep but on the other hand I have to be sure that all users
are using proper DTS files. And based on my experience with users
I would rather relate on scu count instead of DT count.

Thanks,
Michal

-- 
Michal Simek, Ing. (M.Eng)
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/
Maintainer of Linux kernel - Xilinx Zynq ARM architecture
Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform

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

* [PATCH 08/10] arm: zynq: Add smp support
  2013-03-26  7:42         ` Michal Simek
@ 2013-04-01 22:40           ` Rob Herring
  2013-04-03  6:44             ` Michal Simek
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Herring @ 2013-04-01 22:40 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/26/2013 02:42 AM, Michal Simek wrote:
> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>> On 03/25/2013 11:31 AM, Michal Simek wrote:
>>> On 03/25/2013 03:16 PM, Rob Herring wrote:
>>>> On 03/25/2013 08:53 AM, Michal Simek wrote:
>>>>> Zynq is dual core Cortex A9 which starts always
>>>>> at zero. Using simple trampoline ensure long jump
>>>>> to secondary_startup code.

[...]

>>>>> +        slcr_cpu_stop(cpu);
>>>>
>>>> Isn't a secondary cpu already stopped?
>>>
>>> On the normal boot this is really necessary because first stage bootloader
>>> doesn't stop cpu just keep it in loop and without stopping cpu
>>> and starting it again it doesn't work.
>>
>> And there is no way to exit the loop other than a reset?
> 
> You can exit the loop by writing jump address to one location where bootloader
> expect it. Then it jumps to proper function and it was done like that before.
> But this is not suitable for cpu hotplug because that loop is placed
> in OCM (on chip memory)
> and it can be used for different purpose.
> Also there is no way how to return cpu to this mode.

I was asking about the cold boot case, not hotplug. If you are spinning
in the bootloader waiting for a jump address, then why do you need the
slcr_cpu_stop for cold boot? In the hotplug case, you have already
called slcr_cpu_stop in the unplug path, so this shouldn't be needed
there either.

[...]

>>>>> +            __raw_writel(address, phys_to_virt(0x8));
>>
>> This should be a per core address including core 0 if you ever want to
>> do something like cpuidle powergating on one core and hotplug on another.
> 
> That's interesting idea.
> Please correct me if I am wrong, I didn't play with cpuidle.
> 
> Zynq is dual core and hotplug can be done only on cpu1. (not sure if
> in general cpu0
> can be unplugged too. If yes, are you doing that?).

You cannot do hotplug on cpu 0, but that is a current Linux limitation.
I believe support to hotplug cpu 0 was recently added for x86. So this
may change at some point.

What the h/w can support is another issue. Some chips have independent
power domains per core and some have combined domains. I can and do
powergate individual cores including core 0 in cpuidle for highbank. The
main difference to hotplug is whether I set the wake-up address when I
go down (cpuidle) or when I wake the core (hotplug). For hotplug, you
don't want the core to come back before the kernel is ready for it.

> I didn't play with cpuidle code but I am not quite sure if you can use hotplug
> if cpu0 is in idle because this code is for >cpu0.
> 
> I can imagine to be more flexible on quad core where your comment make
> definitely sense.

The next part could be quad core...

Rob

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
  2013-03-25 16:07         ` Michal Simek
  2013-03-25 22:34           ` Rob Herring
@ 2013-04-02 16:40           ` Pawel Moll
       [not found]             ` <CAHTX3dKD4G0E8qoxTR2HnJVdagoeOerM+TiZzkJUPjcGwYdX_Q@mail.gmail.com>
  1 sibling, 1 reply; 32+ messages in thread
From: Pawel Moll @ 2013-04-02 16:40 UTC (permalink / raw)
  To: linux-arm-kernel

Greetings,

Apologies about "slightly" delayed response.

On Mon, 2013-03-25 at 16:07 +0000, Michal Simek wrote:
> Pawel: You are the author of this code in vexpress. Is there any
> particular reason
> to have there that ioremap?

At the time there was no standard DT->platsmp solution and the
traditional way was using the SCU to work out the CPUs.

If I was doing this today (and I'll re-do this code at some point) I'd
rely on the DT in the first place and maybe then have a SCU
"micro-driver" double-checking the DT data against the SCU information.
That way I could reduce the dependency on static mappings. And this is
what I want to get - if possible I don't want any static mapping at all.

Pawe?

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

* [PATCH 08/10] arm: zynq: Add smp support
  2013-04-01 22:40           ` Rob Herring
@ 2013-04-03  6:44             ` Michal Simek
  0 siblings, 0 replies; 32+ messages in thread
From: Michal Simek @ 2013-04-03  6:44 UTC (permalink / raw)
  To: linux-arm-kernel

2013/4/2 Rob Herring <robherring2@gmail.com>:
> On 03/26/2013 02:42 AM, Michal Simek wrote:
>> 2013/3/25 Rob Herring <robherring2@gmail.com>:
>>> On 03/25/2013 11:31 AM, Michal Simek wrote:
>>>> On 03/25/2013 03:16 PM, Rob Herring wrote:
>>>>> On 03/25/2013 08:53 AM, Michal Simek wrote:
>>>>>> Zynq is dual core Cortex A9 which starts always
>>>>>> at zero. Using simple trampoline ensure long jump
>>>>>> to secondary_startup code.
>
> [...]
>
>>>>>> +        slcr_cpu_stop(cpu);
>>>>>
>>>>> Isn't a secondary cpu already stopped?
>>>>
>>>> On the normal boot this is really necessary because first stage bootloader
>>>> doesn't stop cpu just keep it in loop and without stopping cpu
>>>> and starting it again it doesn't work.
>>>
>>> And there is no way to exit the loop other than a reset?
>>
>> You can exit the loop by writing jump address to one location where bootloader
>> expect it. Then it jumps to proper function and it was done like that before.
>> But this is not suitable for cpu hotplug because that loop is placed
>> in OCM (on chip memory)
>> and it can be used for different purpose.
>> Also there is no way how to return cpu to this mode.
>
> I was asking about the cold boot case, not hotplug. If you are spinning
> in the bootloader waiting for a jump address, then why do you need the
> slcr_cpu_stop for cold boot? In the hotplug case, you have already
> called slcr_cpu_stop in the unplug path, so this shouldn't be needed
> there either.

For cold boot case there is an option to write jump addr
to specific address which fsbl (xilinx proprietary bootloader) expects
or just do cpu reset.
Waiting loop is placed in OCM. Then u-boot runs and user can change OCM mapping
for whatever reason.

My point is that reset is cleaner way because I am exactly sure
that cpu will be in the correct state and previous bootloader/code just
hasn't done any weird setting/changes.
It means that my requirement is that any bootloader will just ensure
that cpu1 is
in any waiting loop/sleep/whatever and I will do proper reset when I
need to start it.

I understand your concern about moving slcr_cpu_stop to different location
or even just stop cpu in bootloader but that's even more problematic.

>>>>>> +            __raw_writel(address, phys_to_virt(0x8));
>>>
>>> This should be a per core address including core 0 if you ever want to
>>> do something like cpuidle powergating on one core and hotplug on another.
>>
>> That's interesting idea.
>> Please correct me if I am wrong, I didn't play with cpuidle.
>>
>> Zynq is dual core and hotplug can be done only on cpu1. (not sure if
>> in general cpu0
>> can be unplugged too. If yes, are you doing that?).
>
> You cannot do hotplug on cpu 0, but that is a current Linux limitation.
> I believe support to hotplug cpu 0 was recently added for x86. So this
> may change at some point.
>
> What the h/w can support is another issue. Some chips have independent
> power domains per core and some have combined domains. I can and do
> powergate individual cores including core 0 in cpuidle for highbank. The
> main difference to hotplug is whether I set the wake-up address when I
> go down (cpuidle) or when I wake the core (hotplug). For hotplug, you
> don't want the core to come back before the kernel is ready for it.

ok

>> I didn't play with cpuidle code but I am not quite sure if you can use hotplug
>> if cpu0 is in idle because this code is for >cpu0.
>>
>> I can imagine to be more flexible on quad core where your comment make
>> definitely sense.
>
> The next part could be quad core...

I will look at changing this boot up code for future.

Thanks,
Michal



-- 
Michal Simek, Ing. (M.Eng)
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/
Maintainer of Linux kernel - Xilinx Zynq ARM architecture
Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
       [not found]             ` <CAHTX3dKD4G0E8qoxTR2HnJVdagoeOerM+TiZzkJUPjcGwYdX_Q@mail.gmail.com>
@ 2013-04-03  7:25               ` Steffen Trumtrar
  2013-04-03 16:06               ` Pawel Moll
  1 sibling, 0 replies; 32+ messages in thread
From: Steffen Trumtrar @ 2013-04-03  7:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 03, 2013 at 08:33:19AM +0200, Michal Simek wrote:
> Hi
> 
> 2013/4/2 Pawel Moll <pawel.moll@arm.com>
> 
> > Greetings,
> >
> > Apologies about "slightly" delayed response.
> >
> > On Mon, 2013-03-25 at 16:07 +0000, Michal Simek wrote:
> > > Pawel: You are the author of this code in vexpress. Is there any
> > > particular reason
> > > to have there that ioremap?
> >
> > At the time there was no standard DT->platsmp solution and the
> > traditional way was using the SCU to work out the CPUs.
> >
> > If I was doing this today (and I'll re-do this code at some point) I'd
> > rely on the DT in the first place and maybe then have a SCU
> > "micro-driver" double-checking the DT data against the SCU information.
> > That way I could reduce the dependency on static mappings. And this is
> > what I want to get - if possible I don't want any static mapping at all.
> >
> 
> Sure. Currently I want to use that static mapping because I know that user
> DTSes are just wrong and relating on HW is better for me right now.
> 

Wouldn't we risk getting more faulty DTSes instead of stopping them?
If you stop the support starting with kernel 3.xx everyone has to fix
their *.dts and be done with it.

Regards,
Steffen

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
       [not found]             ` <CAHTX3dKD4G0E8qoxTR2HnJVdagoeOerM+TiZzkJUPjcGwYdX_Q@mail.gmail.com>
  2013-04-03  7:25               ` Steffen Trumtrar
@ 2013-04-03 16:06               ` Pawel Moll
       [not found]                 ` <CAHTX3dJMpp+E2u-cAeYbqtxC1WAYWpCeRx6W7G=dWDcgzUz5DA@mail.gmail.com>
  1 sibling, 1 reply; 32+ messages in thread
From: Pawel Moll @ 2013-04-03 16:06 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 2013-04-03 at 07:33 +0100, Michal Simek wrote:
> The point is different. Was there any reason why you used this static
> mapping
> and then dynamic?
> This code:
>         iotable_init(&vexpress_dt_cortex_a9_scu_map, 1);
>         vexpress_dt_cortex_a9_scu_base = ioremap(phys_addr, SZ_256);

The SCU code is being used very early - too early for "normal" ioremap()
(one problem I'm aware of is lack of kmalloc, but there may be more). So
the static mapping is required. Now, traditionally there was some (more
or less cryptic) pointer arithmetic was used (eg. "base = map_virt_addr
+ 0x100"). I'm sure some would argue it's fine, but I personally dislike
it. Then Nico Pitre's patches made the ioremap() aware of the static
mappings so it will reuse them when possible. Of course it would be
better to be able to do normal ioremap() without any tricks, but it's
another story and I don't feel like discussing it.

So, to summarize, the static mapping is there to make the ioremap()
possible in the first place. If your ioremap() works without it (which
means that you're doing it late enough) you can ignore the
iotable_init() completely.

Does this answer the question?

Pawe?

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

* [PATCH 04/10] arm: zynq: Load scu baseaddress at run time
       [not found]                 ` <CAHTX3dJMpp+E2u-cAeYbqtxC1WAYWpCeRx6W7G=dWDcgzUz5DA@mail.gmail.com>
@ 2013-04-03 17:11                   ` Pawel Moll
  0 siblings, 0 replies; 32+ messages in thread
From: Pawel Moll @ 2013-04-03 17:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 2013-04-03 at 18:04 +0100, Michal Simek wrote:
> Then my question is if you can just remove that ioremap, as I have
> done for zynq,
> without any related problem.

Well, the vexpress platsmp functions are going away soon, replaced by
the generic DT code. When this happen I won't even try to access the
SCU, so both its static mapping and the ioremap will go as well :-)

Pawe?

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

end of thread, other threads:[~2013-04-03 17:11 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-03-25 13:53 [PATCH 01/10] arm: zynq: Use standard timer binding Michal Simek
2013-03-25 13:53 ` [PATCH 02/10] arm: zynq: Move timer to clocksource interface Michal Simek
2013-03-25 13:53 ` [PATCH 03/10] arm: zynq: Move timer to generic location Michal Simek
2013-03-25 16:01   ` Steffen Trumtrar
2013-03-25 16:24     ` Michal Simek
2013-03-25 13:53 ` [PATCH 04/10] arm: zynq: Load scu baseaddress at run time Michal Simek
2013-03-25 14:06   ` Rob Herring
2013-03-25 14:51     ` Michal Simek
2013-03-25 15:37       ` Rob Herring
2013-03-25 16:07         ` Michal Simek
2013-03-25 22:34           ` Rob Herring
2013-03-26 10:45             ` Michal Simek
2013-03-26 12:28               ` Rob Herring
2013-03-26 12:33                 ` Michal Simek
2013-04-02 16:40           ` Pawel Moll
     [not found]             ` <CAHTX3dKD4G0E8qoxTR2HnJVdagoeOerM+TiZzkJUPjcGwYdX_Q@mail.gmail.com>
2013-04-03  7:25               ` Steffen Trumtrar
2013-04-03 16:06               ` Pawel Moll
     [not found]                 ` <CAHTX3dJMpp+E2u-cAeYbqtxC1WAYWpCeRx6W7G=dWDcgzUz5DA@mail.gmail.com>
2013-04-03 17:11                   ` Pawel Moll
2013-03-25 13:53 ` [PATCH 05/10] arm: zynq: Move slcr initialization to separate file Michal Simek
2013-03-25 16:19   ` Steffen Trumtrar
2013-03-25 16:37     ` Michal Simek
2013-03-25 13:53 ` [PATCH 06/10] arm: zynq: Add support for system reset Michal Simek
2013-03-25 13:53 ` [PATCH 07/10] arm: zynq: Add support for pmu Michal Simek
2013-03-25 13:53 ` [PATCH 08/10] arm: zynq: Add smp support Michal Simek
2013-03-25 14:16   ` Rob Herring
2013-03-25 16:31     ` Michal Simek
2013-03-25 22:10       ` Rob Herring
2013-03-26  7:42         ` Michal Simek
2013-04-01 22:40           ` Rob Herring
2013-04-03  6:44             ` Michal Simek
2013-03-25 13:53 ` [PATCH 09/10] arm: zynq: Add hotplug support Michal Simek
2013-03-25 13:53 ` [PATCH 10/10] arm: zynq: Add cpuidle support Michal Simek

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.