* Zynq core changes v3
@ 2013-03-28 16:38 Michal Simek
2013-03-28 16:38 ` [PATCH v3 01/12] arm: zynq: Use standard timer binding Michal Simek
` (11 more replies)
0 siblings, 12 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
I am sending v3 patchset.
Changes against v2:
Get rid of xilinx name and use zynq instead of it.
Clean names in timer code
Add scutimer to this series from Steffen.
Thanks,
Michal
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 01/12] arm: zynq: Use standard timer binding
2013-03-28 16:38 Zynq core changes v3 Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 02/12] arm: zynq: Move timer to clocksource interface Michal Simek
` (10 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
Use cdns,ttc because this driver is Cadence Rev06
Triple Timer Counter and everybody can use it
without xilinx specific function name or probing.
Also use standard dt description for timer
and also prepare for moving to clocksource
initialization.
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
v2: Update year in copyright
Fix typo fault
Remove all zynq specific names
Steffen: I have done this change based on our discussion.
Moving to generic location will be done in the next patch.
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 | 45 +------
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, 195 insertions(+), 122 deletions(-)
diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
index 5914b56..51243db 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>;
- compatible = "xlnx,ttc";
+ interrupt-parent = <&intc>;
+ interrupts = < 0 10 4 0 11 4 0 12 4 >;
+ compatible = "cdns,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>;
- compatible = "xlnx,ttc";
+ interrupt-parent = <&intc>;
+ interrupts = < 0 37 4 0 38 4 0 39 4 >;
+ compatible = "cdns,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..82357d9 100644
--- a/arch/arm/mach-zynq/timer.c
+++ b/arch/arm/mach-zynq/timer.c
@@ -1,7 +1,7 @@
/*
* This file contains driver for the Xilinx PS Timer Counter IP.
*
- * Copyright (C) 2011 Xilinx
+ * Copyright (C) 2011-2013 Xilinx
*
* based on arch/mips/kernel/time.c timer driver
*
@@ -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,148 @@ 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 necessary 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 xttc_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", ®);
- 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 xttc_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", ®);
- 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 +360,80 @@ 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();
+ }
+
+ xttc_setup_clocksource(clk, timer_baseaddr);
+ xttc_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[] = {
+ "cdns,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] 13+ messages in thread
* [PATCH v3 02/12] arm: zynq: Move timer to clocksource interface
2013-03-28 16:38 Zynq core changes v3 Michal Simek
2013-03-28 16:38 ` [PATCH v3 01/12] arm: zynq: Use standard timer binding Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 03/12] arm: zynq: Do not use xilinx specific function names Michal Simek
` (9 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
Use clocksource timer initialization.
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
v2: Free allocated space for structures
Till this time this was only one timer driver which
could be possible to use on zynq.
---
arch/arm/mach-zynq/common.c | 2 +-
arch/arm/mach-zynq/common.h | 2 --
arch/arm/mach-zynq/timer.c | 43 ++++++++++++++++++++-----------------------
3 files changed, 21 insertions(+), 26 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 82357d9..ab5b839 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:
@@ -260,8 +259,10 @@ static void __init xttc_setup_clocksource(struct clk *clk, void __iomem *base)
ttccs->xttc.clk = clk;
err = clk_prepare_enable(ttccs->xttc.clk);
- if (WARN_ON(err))
+ if (WARN_ON(err)) {
+ kfree(ttccs);
return;
+ }
ttccs->xttc.clk_rate_change_nb.notifier_call =
xttcps_rate_change_clocksource_cb;
@@ -290,9 +291,10 @@ static void __init xttc_setup_clocksource(struct clk *clk, void __iomem *base)
err = clocksource_register_hz(&ttccs->cs,
clk_get_rate(ttccs->xttc.clk) / PRESCALE);
- if (WARN_ON(err))
+ if (WARN_ON(err)) {
+ kfree(ttccs);
return;
-
+ }
}
static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
@@ -341,8 +343,10 @@ static void __init xttc_setup_clockevent(struct clk *clk,
ttcce->xttc.clk = clk;
err = clk_prepare_enable(ttcce->xttc.clk);
- if (WARN_ON(err))
+ if (WARN_ON(err)) {
+ kfree(ttcce);
return;
+ }
ttcce->xttc.clk_rate_change_nb.notifier_call =
xttcps_rate_change_clockevent_cb;
@@ -373,8 +377,10 @@ static void __init xttc_setup_clockevent(struct clk *clk,
err = request_irq(irq, xttcps_clock_event_interrupt,
IRQF_DISABLED | IRQF_TIMER,
ttcce->ce.name, ttcce);
- if (WARN_ON(err))
+ if (WARN_ON(err)) {
+ kfree(ttcce);
return;
+ }
clockevents_config_and_register(&ttcce->ce,
clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe);
@@ -386,11 +392,17 @@ static void __init xttc_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
@@ -421,19 +433,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[] = {
- "cdns,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(ttc, "cdns,ttc", xttcps_timer_init);
--
1.7.9.7
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v3 03/12] arm: zynq: Do not use xilinx specific function names
2013-03-28 16:38 Zynq core changes v3 Michal Simek
2013-03-28 16:38 ` [PATCH v3 01/12] arm: zynq: Use standard timer binding Michal Simek
2013-03-28 16:38 ` [PATCH v3 02/12] arm: zynq: Move timer to clocksource interface Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 04/12] arm: zynq: Move timer to generic location Michal Simek
` (8 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
Remove all xilinx specific names from the driver
because this is generic driver for cadence ttc.
xttc->ttc
ttcps->ttc
...
No functional changes in this driver.
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
----
v3: The first version of this patch.
Done based on discussion with Steffen to use proper naming
---
arch/arm/mach-zynq/timer.c | 212 ++++++++++++++++++++++----------------------
1 file changed, 106 insertions(+), 106 deletions(-)
diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c
index ab5b839..685bc60 100644
--- a/arch/arm/mach-zynq/timer.c
+++ b/arch/arm/mach-zynq/timer.c
@@ -1,5 +1,5 @@
/*
- * This file contains driver for the Xilinx PS Timer Counter IP.
+ * This file contains driver for the Cadence Triple Timer Counter Rev 06
*
* Copyright (C) 2011-2013 Xilinx
*
@@ -42,14 +42,14 @@
* 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 TTC_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */
+#define TTC_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */
+#define TTC_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */
+#define TTC_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */
+#define TTC_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */
+#define TTC_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */
-#define XTTCPS_CNT_CNTRL_DISABLE_MASK 0x1
+#define TTC_CNT_CNTRL_DISABLE_MASK 0x1
/*
* Setup the timers to use pre-scaling, using a fixed value for now that will
@@ -62,161 +62,161 @@
#define CNT_CNTRL_RESET (1 << 4)
/**
- * struct xttcps_timer - This definition defines local timer structure
+ * struct ttc_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 {
+struct ttc_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)
+#define to_ttc_timer(x) \
+ container_of(x, struct ttc_timer, clk_rate_change_nb)
-struct xttcps_timer_clocksource {
- struct xttcps_timer xttc;
+struct ttc_timer_clocksource {
+ struct ttc_timer ttc;
struct clocksource cs;
};
-#define to_xttcps_timer_clksrc(x) \
- container_of(x, struct xttcps_timer_clocksource, cs)
+#define to_ttc_timer_clksrc(x) \
+ container_of(x, struct ttc_timer_clocksource, cs)
-struct xttcps_timer_clockevent {
- struct xttcps_timer xttc;
+struct ttc_timer_clockevent {
+ struct ttc_timer ttc;
struct clock_event_device ce;
};
-#define to_xttcps_timer_clkevent(x) \
- container_of(x, struct xttcps_timer_clockevent, ce)
+#define to_ttc_timer_clkevent(x) \
+ container_of(x, struct ttc_timer_clockevent, ce)
/**
- * xttcps_set_interval - Set the timer interval value
+ * ttc_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,
+static void ttc_set_interval(struct ttc_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);
+ ctrl_reg = __raw_readl(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
+ ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
+ __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
- __raw_writel(cycles, timer->base_addr + XTTCPS_INTR_VAL_OFFSET);
+ __raw_writel(cycles, timer->base_addr + TTC_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);
+ ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
+ __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
}
/**
- * xttcps_clock_event_interrupt - Clock event timer interrupt handler
+ * ttc_clock_event_interrupt - Clock event timer interrupt handler
*
* @irq: IRQ number of the Timer
- * @dev_id: void pointer to the xttcps_timer instance
+ * @dev_id: void pointer to the ttc_timer instance
*
* returns: Always IRQ_HANDLED - success
**/
-static irqreturn_t xttcps_clock_event_interrupt(int irq, void *dev_id)
+static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id)
{
- struct xttcps_timer_clockevent *xttce = dev_id;
- struct xttcps_timer *timer = &xttce->xttc;
+ struct ttc_timer_clockevent *ttce = dev_id;
+ struct ttc_timer *timer = &ttce->ttc;
/* Acknowledge the interrupt and call event handler */
- __raw_readl(timer->base_addr + XTTCPS_ISR_OFFSET);
+ __raw_readl(timer->base_addr + TTC_ISR_OFFSET);
- xttce->ce.event_handler(&xttce->ce);
+ ttce->ce.event_handler(&ttce->ce);
return IRQ_HANDLED;
}
/**
- * __xttc_clocksource_read - Reads the timer counter register
+ * __ttc_clocksource_read - Reads the timer counter register
*
* returns: Current timer counter register value
**/
-static cycle_t __xttc_clocksource_read(struct clocksource *cs)
+static cycle_t __ttc_clocksource_read(struct clocksource *cs)
{
- struct xttcps_timer *timer = &to_xttcps_timer_clksrc(cs)->xttc;
+ struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc;
return (cycle_t)__raw_readl(timer->base_addr +
- XTTCPS_COUNT_VAL_OFFSET);
+ TTC_COUNT_VAL_OFFSET);
}
/**
- * xttcps_set_next_event - Sets the time interval for next event
+ * ttc_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,
+static int ttc_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;
+ struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
+ struct ttc_timer *timer = &ttce->ttc;
- xttcps_set_interval(timer, cycles);
+ ttc_set_interval(timer, cycles);
return 0;
}
/**
- * xttcps_set_mode - Sets the mode of timer
+ * ttc_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,
+static void ttc_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;
+ struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
+ struct ttc_timer *timer = &ttce->ttc;
u32 ctrl_reg;
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- xttcps_set_interval(timer,
- DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk),
+ ttc_set_interval(timer,
+ DIV_ROUND_CLOSEST(clk_get_rate(ttce->ttc.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;
+ TTC_CNT_CNTRL_OFFSET);
+ ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg,
- timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
+ timer->base_addr + TTC_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;
+ TTC_CNT_CNTRL_OFFSET);
+ ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg,
- timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
+ timer->base_addr + TTC_CNT_CNTRL_OFFSET);
break;
}
}
-static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb,
+static int ttc_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);
+ struct ttc_timer *ttc = to_ttc_timer(nb);
+ struct ttc_timer_clocksource *ttccs = container_of(ttc,
+ struct ttc_timer_clocksource, ttc);
switch (event) {
case POST_RATE_CHANGE:
@@ -236,8 +236,8 @@ static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb,
* 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,
+ clocksource_unregister(&ttccs->cs);
+ clocksource_register_hz(&ttccs->cs,
ndata->new_rate / PRESCALE);
/* fall through */
case PRE_RATE_CHANGE:
@@ -247,34 +247,34 @@ static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb,
}
}
-static void __init xttc_setup_clocksource(struct clk *clk, void __iomem *base)
+static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
{
- struct xttcps_timer_clocksource *ttccs;
+ struct ttc_timer_clocksource *ttccs;
int err;
ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
if (WARN_ON(!ttccs))
return;
- ttccs->xttc.clk = clk;
+ ttccs->ttc.clk = clk;
- err = clk_prepare_enable(ttccs->xttc.clk);
+ err = clk_prepare_enable(ttccs->ttc.clk);
if (WARN_ON(err)) {
kfree(ttccs);
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))
+ ttccs->ttc.clk_rate_change_nb.notifier_call =
+ ttc_rate_change_clocksource_cb;
+ ttccs->ttc.clk_rate_change_nb.next = NULL;
+ if (clk_notifier_register(ttccs->ttc.clk,
+ &ttccs->ttc.clk_rate_change_nb))
pr_warn("Unable to register clock notifier.\n");
- ttccs->xttc.base_addr = base;
- ttccs->cs.name = "xttcps_clocksource";
+ ttccs->ttc.base_addr = base;
+ ttccs->cs.name = "ttc_clocksource";
ttccs->cs.rating = 200;
- ttccs->cs.read = __xttc_clocksource_read;
+ ttccs->cs.read = __ttc_clocksource_read;
ttccs->cs.mask = CLOCKSOURCE_MASK(16);
ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
@@ -283,27 +283,27 @@ static void __init xttc_setup_clocksource(struct clk *clk, void __iomem *base)
* 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(0x0, ttccs->ttc.base_addr + TTC_IER_OFFSET);
__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
- ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
+ ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
__raw_writel(CNT_CNTRL_RESET,
- ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
+ ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
err = clocksource_register_hz(&ttccs->cs,
- clk_get_rate(ttccs->xttc.clk) / PRESCALE);
+ clk_get_rate(ttccs->ttc.clk) / PRESCALE);
if (WARN_ON(err)) {
kfree(ttccs);
return;
}
}
-static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
+static int ttc_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);
+ struct ttc_timer *ttc = to_ttc_timer(nb);
+ struct ttc_timer_clockevent *ttcce = container_of(ttc,
+ struct ttc_timer_clockevent, ttc);
switch (event) {
case POST_RATE_CHANGE:
@@ -317,7 +317,7 @@ static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
* cores.
*/
local_irq_save(flags);
- clockevents_update_freq(&xttcce->ce,
+ clockevents_update_freq(&ttcce->ce,
ndata->new_rate / PRESCALE);
local_irq_restore(flags);
@@ -330,36 +330,36 @@ static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb,
}
}
-static void __init xttc_setup_clockevent(struct clk *clk,
+static void __init ttc_setup_clockevent(struct clk *clk,
void __iomem *base, u32 irq)
{
- struct xttcps_timer_clockevent *ttcce;
+ struct ttc_timer_clockevent *ttcce;
int err;
ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
if (WARN_ON(!ttcce))
return;
- ttcce->xttc.clk = clk;
+ ttcce->ttc.clk = clk;
- err = clk_prepare_enable(ttcce->xttc.clk);
+ err = clk_prepare_enable(ttcce->ttc.clk);
if (WARN_ON(err)) {
kfree(ttcce);
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))
+ ttcce->ttc.clk_rate_change_nb.notifier_call =
+ ttc_rate_change_clockevent_cb;
+ ttcce->ttc.clk_rate_change_nb.next = NULL;
+ if (clk_notifier_register(ttcce->ttc.clk,
+ &ttcce->ttc.clk_rate_change_nb))
pr_warn("Unable to register clock notifier.\n");
- ttcce->xttc.base_addr = base;
- ttcce->ce.name = "xttcps_clockevent";
+ ttcce->ttc.base_addr = base;
+ ttcce->ce.name = "ttc_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.set_next_event = ttc_set_next_event;
+ ttcce->ce.set_mode = ttc_set_mode;
ttcce->ce.rating = 200;
ttcce->ce.irq = irq;
ttcce->ce.cpumask = cpu_possible_mask;
@@ -369,12 +369,12 @@ static void __init xttc_setup_clockevent(struct clk *clk,
* 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(0x23, ttcce->ttc.base_addr + TTC_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);
+ ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
+ __raw_writel(0x1, ttcce->ttc.base_addr + TTC_IER_OFFSET);
- err = request_irq(irq, xttcps_clock_event_interrupt,
+ err = request_irq(irq, ttc_clock_event_interrupt,
IRQF_DISABLED | IRQF_TIMER,
ttcce->ce.name, ttcce);
if (WARN_ON(err)) {
@@ -383,16 +383,16 @@ static void __init xttc_setup_clockevent(struct clk *clk,
}
clockevents_config_and_register(&ttcce->ce,
- clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe);
+ clk_get_rate(ttcce->ttc.clk) / PRESCALE, 1, 0xfffe);
}
/**
- * xttcps_timer_init - Initialize the timer
+ * ttc_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)
+static void __init ttc_timer_init(struct device_node *timer)
{
unsigned int irq;
void __iomem *timer_baseaddr;
@@ -427,10 +427,10 @@ static void __init xttcps_timer_init(struct device_node *timer)
BUG();
}
- xttc_setup_clocksource(clk, timer_baseaddr);
- xttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
+ ttc_setup_clocksource(clk, timer_baseaddr);
+ 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(ttc, "cdns,ttc", xttcps_timer_init);
+CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init);
--
1.7.9.7
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v3 04/12] arm: zynq: Move timer to generic location
2013-03-28 16:38 Zynq core changes v3 Michal Simek
` (2 preceding siblings ...)
2013-03-28 16:38 ` [PATCH v3 03/12] arm: zynq: Do not use xilinx specific function names Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 05/12] arm: zynq: Load scu baseaddress at run time Michal Simek
` (7 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
Move zynq timer out of mach folder to generic location
and enable it.
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
v2: Add binding documentation
Rename zynq_timer.c to cadence_ttc_timer.c
Use CADENCE_TTC_TIMER to be able to enable this driver for others
Arnd: This is the last patch as we discuss about moving
this timer to generic location.
---
.../bindings/timer/cadence,ttc-timer.txt | 17 +
arch/arm/mach-zynq/Kconfig | 1 +
arch/arm/mach-zynq/Makefile | 2 +-
arch/arm/mach-zynq/timer.c | 436 --------------------
drivers/clocksource/Kconfig | 3 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/cadence_ttc_timer.c | 436 ++++++++++++++++++++
7 files changed, 459 insertions(+), 437 deletions(-)
create mode 100644 Documentation/devicetree/bindings/timer/cadence,ttc-timer.txt
delete mode 100644 arch/arm/mach-zynq/timer.c
create mode 100644 drivers/clocksource/cadence_ttc_timer.c
diff --git a/Documentation/devicetree/bindings/timer/cadence,ttc-timer.txt b/Documentation/devicetree/bindings/timer/cadence,ttc-timer.txt
new file mode 100644
index 0000000..993695c
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/cadence,ttc-timer.txt
@@ -0,0 +1,17 @@
+Cadence TTC - Triple Timer Counter
+
+Required properties:
+- compatible : Should be "cdns,ttc".
+- reg : Specifies base physical address and size of the registers.
+- interrupts : A list of 3 interrupts; one per timer channel.
+- clocks: phandle to the source clock
+
+Example:
+
+ttc0: ttc0 at f8001000 {
+ interrupt-parent = <&intc>;
+ interrupts = < 0 10 4 0 11 4 0 12 4 >;
+ compatible = "cdns,ttc";
+ reg = <0xF8001000 0x1000>;
+ clocks = <&cpu_clk 3>;
+};
diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig
index adb6c0e..d70651e 100644
--- a/arch/arm/mach-zynq/Kconfig
+++ b/arch/arm/mach-zynq/Kconfig
@@ -9,5 +9,6 @@ config ARCH_ZYNQ
select MIGHT_HAVE_CACHE_L2X0
select USE_OF
select SPARSE_IRQ
+ select CADENCE_TTC_TIMER
help
Support for Xilinx Zynq ARM Cortex A9 Platform
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 685bc60..0000000
--- a/arch/arm/mach-zynq/timer.c
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- * This file contains driver for the Cadence Triple Timer Counter Rev 06
- *
- * Copyright (C) 2011-2013 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 TTC_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */
-#define TTC_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */
-#define TTC_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */
-#define TTC_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */
-#define TTC_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */
-#define TTC_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */
-
-#define TTC_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 ttc_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 ttc_timer {
- void __iomem *base_addr;
- struct clk *clk;
- struct notifier_block clk_rate_change_nb;
-};
-
-#define to_ttc_timer(x) \
- container_of(x, struct ttc_timer, clk_rate_change_nb)
-
-struct ttc_timer_clocksource {
- struct ttc_timer ttc;
- struct clocksource cs;
-};
-
-#define to_ttc_timer_clksrc(x) \
- container_of(x, struct ttc_timer_clocksource, cs)
-
-struct ttc_timer_clockevent {
- struct ttc_timer ttc;
- struct clock_event_device ce;
-};
-
-#define to_ttc_timer_clkevent(x) \
- container_of(x, struct ttc_timer_clockevent, ce)
-
-/**
- * ttc_set_interval - Set the timer interval value
- *
- * @timer: Pointer to the timer instance
- * @cycles: Timer interval ticks
- **/
-static void ttc_set_interval(struct ttc_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 + TTC_CNT_CNTRL_OFFSET);
- ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
- __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
-
- __raw_writel(cycles, timer->base_addr + TTC_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 &= ~TTC_CNT_CNTRL_DISABLE_MASK;
- __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
-}
-
-/**
- * ttc_clock_event_interrupt - Clock event timer interrupt handler
- *
- * @irq: IRQ number of the Timer
- * @dev_id: void pointer to the ttc_timer instance
- *
- * returns: Always IRQ_HANDLED - success
- **/
-static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id)
-{
- struct ttc_timer_clockevent *ttce = dev_id;
- struct ttc_timer *timer = &ttce->ttc;
-
- /* Acknowledge the interrupt and call event handler */
- __raw_readl(timer->base_addr + TTC_ISR_OFFSET);
-
- ttce->ce.event_handler(&ttce->ce);
-
- return IRQ_HANDLED;
-}
-
-/**
- * __ttc_clocksource_read - Reads the timer counter register
- *
- * returns: Current timer counter register value
- **/
-static cycle_t __ttc_clocksource_read(struct clocksource *cs)
-{
- struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc;
-
- return (cycle_t)__raw_readl(timer->base_addr +
- TTC_COUNT_VAL_OFFSET);
-}
-
-/**
- * ttc_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 ttc_set_next_event(unsigned long cycles,
- struct clock_event_device *evt)
-{
- struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
- struct ttc_timer *timer = &ttce->ttc;
-
- ttc_set_interval(timer, cycles);
- return 0;
-}
-
-/**
- * ttc_set_mode - Sets the mode of timer
- *
- * @mode: Mode to be set
- * @evt: Address of clock event instance
- **/
-static void ttc_set_mode(enum clock_event_mode mode,
- struct clock_event_device *evt)
-{
- struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
- struct ttc_timer *timer = &ttce->ttc;
- u32 ctrl_reg;
-
- switch (mode) {
- case CLOCK_EVT_MODE_PERIODIC:
- ttc_set_interval(timer,
- DIV_ROUND_CLOSEST(clk_get_rate(ttce->ttc.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 +
- TTC_CNT_CNTRL_OFFSET);
- ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
- __raw_writel(ctrl_reg,
- timer->base_addr + TTC_CNT_CNTRL_OFFSET);
- break;
- case CLOCK_EVT_MODE_RESUME:
- ctrl_reg = __raw_readl(timer->base_addr +
- TTC_CNT_CNTRL_OFFSET);
- ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
- __raw_writel(ctrl_reg,
- timer->base_addr + TTC_CNT_CNTRL_OFFSET);
- break;
- }
-}
-
-static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
- unsigned long event, void *data)
-{
- struct clk_notifier_data *ndata = data;
- struct ttc_timer *ttc = to_ttc_timer(nb);
- struct ttc_timer_clocksource *ttccs = container_of(ttc,
- struct ttc_timer_clocksource, ttc);
-
- switch (event) {
- case POST_RATE_CHANGE:
- /*
- * Do whatever is necessary 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(&ttccs->cs);
- clocksource_register_hz(&ttccs->cs,
- ndata->new_rate / PRESCALE);
- /* fall through */
- case PRE_RATE_CHANGE:
- case ABORT_RATE_CHANGE:
- default:
- return NOTIFY_DONE;
- }
-}
-
-static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
-{
- struct ttc_timer_clocksource *ttccs;
- int err;
-
- ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
- if (WARN_ON(!ttccs))
- return;
-
- ttccs->ttc.clk = clk;
-
- err = clk_prepare_enable(ttccs->ttc.clk);
- if (WARN_ON(err)) {
- kfree(ttccs);
- return;
- }
-
- ttccs->ttc.clk_rate_change_nb.notifier_call =
- ttc_rate_change_clocksource_cb;
- ttccs->ttc.clk_rate_change_nb.next = NULL;
- if (clk_notifier_register(ttccs->ttc.clk,
- &ttccs->ttc.clk_rate_change_nb))
- pr_warn("Unable to register clock notifier.\n");
-
- ttccs->ttc.base_addr = base;
- ttccs->cs.name = "ttc_clocksource";
- ttccs->cs.rating = 200;
- ttccs->cs.read = __ttc_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->ttc.base_addr + TTC_IER_OFFSET);
- __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
- ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
- __raw_writel(CNT_CNTRL_RESET,
- ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
-
- err = clocksource_register_hz(&ttccs->cs,
- clk_get_rate(ttccs->ttc.clk) / PRESCALE);
- if (WARN_ON(err)) {
- kfree(ttccs);
- return;
- }
-}
-
-static int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
- unsigned long event, void *data)
-{
- struct clk_notifier_data *ndata = data;
- struct ttc_timer *ttc = to_ttc_timer(nb);
- struct ttc_timer_clockevent *ttcce = container_of(ttc,
- struct ttc_timer_clockevent, ttc);
-
- 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(&ttcce->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 ttc_setup_clockevent(struct clk *clk,
- void __iomem *base, u32 irq)
-{
- struct ttc_timer_clockevent *ttcce;
- int err;
-
- ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
- if (WARN_ON(!ttcce))
- return;
-
- ttcce->ttc.clk = clk;
-
- err = clk_prepare_enable(ttcce->ttc.clk);
- if (WARN_ON(err)) {
- kfree(ttcce);
- return;
- }
-
- ttcce->ttc.clk_rate_change_nb.notifier_call =
- ttc_rate_change_clockevent_cb;
- ttcce->ttc.clk_rate_change_nb.next = NULL;
- if (clk_notifier_register(ttcce->ttc.clk,
- &ttcce->ttc.clk_rate_change_nb))
- pr_warn("Unable to register clock notifier.\n");
-
- ttcce->ttc.base_addr = base;
- ttcce->ce.name = "ttc_clockevent";
- ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
- ttcce->ce.set_next_event = ttc_set_next_event;
- ttcce->ce.set_mode = ttc_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->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
- __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
- ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
- __raw_writel(0x1, ttcce->ttc.base_addr + TTC_IER_OFFSET);
-
- err = request_irq(irq, ttc_clock_event_interrupt,
- IRQF_DISABLED | IRQF_TIMER,
- ttcce->ce.name, ttcce);
- if (WARN_ON(err)) {
- kfree(ttcce);
- return;
- }
-
- clockevents_config_and_register(&ttcce->ce,
- clk_get_rate(ttcce->ttc.clk) / PRESCALE, 1, 0xfffe);
-}
-
-/**
- * ttc_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 ttc_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();
- }
-
- ttc_setup_clocksource(clk, timer_baseaddr);
- ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
-
- pr_info("%s #0@%p, irq=%d\n", timer->name, timer_baseaddr, irq);
-}
-
-CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index e507ab7..3167fda 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -31,6 +31,9 @@ config SUNXI_TIMER
config VT8500_TIMER
bool
+config CADENCE_TTC_TIMER
+ bool
+
config CLKSRC_NOMADIK_MTU
bool
depends on (ARCH_NOMADIK || ARCH_U8500)
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 4d8283a..e74c8ce 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_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
new file mode 100644
index 0000000..685bc60
--- /dev/null
+++ b/drivers/clocksource/cadence_ttc_timer.c
@@ -0,0 +1,436 @@
+/*
+ * This file contains driver for the Cadence Triple Timer Counter Rev 06
+ *
+ * Copyright (C) 2011-2013 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 TTC_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */
+#define TTC_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */
+#define TTC_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */
+#define TTC_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */
+#define TTC_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */
+#define TTC_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */
+
+#define TTC_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 ttc_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 ttc_timer {
+ void __iomem *base_addr;
+ struct clk *clk;
+ struct notifier_block clk_rate_change_nb;
+};
+
+#define to_ttc_timer(x) \
+ container_of(x, struct ttc_timer, clk_rate_change_nb)
+
+struct ttc_timer_clocksource {
+ struct ttc_timer ttc;
+ struct clocksource cs;
+};
+
+#define to_ttc_timer_clksrc(x) \
+ container_of(x, struct ttc_timer_clocksource, cs)
+
+struct ttc_timer_clockevent {
+ struct ttc_timer ttc;
+ struct clock_event_device ce;
+};
+
+#define to_ttc_timer_clkevent(x) \
+ container_of(x, struct ttc_timer_clockevent, ce)
+
+/**
+ * ttc_set_interval - Set the timer interval value
+ *
+ * @timer: Pointer to the timer instance
+ * @cycles: Timer interval ticks
+ **/
+static void ttc_set_interval(struct ttc_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 + TTC_CNT_CNTRL_OFFSET);
+ ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
+ __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
+
+ __raw_writel(cycles, timer->base_addr + TTC_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 &= ~TTC_CNT_CNTRL_DISABLE_MASK;
+ __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
+}
+
+/**
+ * ttc_clock_event_interrupt - Clock event timer interrupt handler
+ *
+ * @irq: IRQ number of the Timer
+ * @dev_id: void pointer to the ttc_timer instance
+ *
+ * returns: Always IRQ_HANDLED - success
+ **/
+static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id)
+{
+ struct ttc_timer_clockevent *ttce = dev_id;
+ struct ttc_timer *timer = &ttce->ttc;
+
+ /* Acknowledge the interrupt and call event handler */
+ __raw_readl(timer->base_addr + TTC_ISR_OFFSET);
+
+ ttce->ce.event_handler(&ttce->ce);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * __ttc_clocksource_read - Reads the timer counter register
+ *
+ * returns: Current timer counter register value
+ **/
+static cycle_t __ttc_clocksource_read(struct clocksource *cs)
+{
+ struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc;
+
+ return (cycle_t)__raw_readl(timer->base_addr +
+ TTC_COUNT_VAL_OFFSET);
+}
+
+/**
+ * ttc_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 ttc_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
+ struct ttc_timer *timer = &ttce->ttc;
+
+ ttc_set_interval(timer, cycles);
+ return 0;
+}
+
+/**
+ * ttc_set_mode - Sets the mode of timer
+ *
+ * @mode: Mode to be set
+ * @evt: Address of clock event instance
+ **/
+static void ttc_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
+ struct ttc_timer *timer = &ttce->ttc;
+ u32 ctrl_reg;
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ ttc_set_interval(timer,
+ DIV_ROUND_CLOSEST(clk_get_rate(ttce->ttc.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 +
+ TTC_CNT_CNTRL_OFFSET);
+ ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
+ __raw_writel(ctrl_reg,
+ timer->base_addr + TTC_CNT_CNTRL_OFFSET);
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ ctrl_reg = __raw_readl(timer->base_addr +
+ TTC_CNT_CNTRL_OFFSET);
+ ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
+ __raw_writel(ctrl_reg,
+ timer->base_addr + TTC_CNT_CNTRL_OFFSET);
+ break;
+ }
+}
+
+static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct clk_notifier_data *ndata = data;
+ struct ttc_timer *ttc = to_ttc_timer(nb);
+ struct ttc_timer_clocksource *ttccs = container_of(ttc,
+ struct ttc_timer_clocksource, ttc);
+
+ switch (event) {
+ case POST_RATE_CHANGE:
+ /*
+ * Do whatever is necessary 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(&ttccs->cs);
+ clocksource_register_hz(&ttccs->cs,
+ ndata->new_rate / PRESCALE);
+ /* fall through */
+ case PRE_RATE_CHANGE:
+ case ABORT_RATE_CHANGE:
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
+{
+ struct ttc_timer_clocksource *ttccs;
+ int err;
+
+ ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
+ if (WARN_ON(!ttccs))
+ return;
+
+ ttccs->ttc.clk = clk;
+
+ err = clk_prepare_enable(ttccs->ttc.clk);
+ if (WARN_ON(err)) {
+ kfree(ttccs);
+ return;
+ }
+
+ ttccs->ttc.clk_rate_change_nb.notifier_call =
+ ttc_rate_change_clocksource_cb;
+ ttccs->ttc.clk_rate_change_nb.next = NULL;
+ if (clk_notifier_register(ttccs->ttc.clk,
+ &ttccs->ttc.clk_rate_change_nb))
+ pr_warn("Unable to register clock notifier.\n");
+
+ ttccs->ttc.base_addr = base;
+ ttccs->cs.name = "ttc_clocksource";
+ ttccs->cs.rating = 200;
+ ttccs->cs.read = __ttc_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->ttc.base_addr + TTC_IER_OFFSET);
+ __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
+ ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
+ __raw_writel(CNT_CNTRL_RESET,
+ ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
+
+ err = clocksource_register_hz(&ttccs->cs,
+ clk_get_rate(ttccs->ttc.clk) / PRESCALE);
+ if (WARN_ON(err)) {
+ kfree(ttccs);
+ return;
+ }
+}
+
+static int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct clk_notifier_data *ndata = data;
+ struct ttc_timer *ttc = to_ttc_timer(nb);
+ struct ttc_timer_clockevent *ttcce = container_of(ttc,
+ struct ttc_timer_clockevent, ttc);
+
+ 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(&ttcce->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 ttc_setup_clockevent(struct clk *clk,
+ void __iomem *base, u32 irq)
+{
+ struct ttc_timer_clockevent *ttcce;
+ int err;
+
+ ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
+ if (WARN_ON(!ttcce))
+ return;
+
+ ttcce->ttc.clk = clk;
+
+ err = clk_prepare_enable(ttcce->ttc.clk);
+ if (WARN_ON(err)) {
+ kfree(ttcce);
+ return;
+ }
+
+ ttcce->ttc.clk_rate_change_nb.notifier_call =
+ ttc_rate_change_clockevent_cb;
+ ttcce->ttc.clk_rate_change_nb.next = NULL;
+ if (clk_notifier_register(ttcce->ttc.clk,
+ &ttcce->ttc.clk_rate_change_nb))
+ pr_warn("Unable to register clock notifier.\n");
+
+ ttcce->ttc.base_addr = base;
+ ttcce->ce.name = "ttc_clockevent";
+ ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+ ttcce->ce.set_next_event = ttc_set_next_event;
+ ttcce->ce.set_mode = ttc_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->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
+ __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
+ ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
+ __raw_writel(0x1, ttcce->ttc.base_addr + TTC_IER_OFFSET);
+
+ err = request_irq(irq, ttc_clock_event_interrupt,
+ IRQF_DISABLED | IRQF_TIMER,
+ ttcce->ce.name, ttcce);
+ if (WARN_ON(err)) {
+ kfree(ttcce);
+ return;
+ }
+
+ clockevents_config_and_register(&ttcce->ce,
+ clk_get_rate(ttcce->ttc.clk) / PRESCALE, 1, 0xfffe);
+}
+
+/**
+ * ttc_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 ttc_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();
+ }
+
+ ttc_setup_clocksource(clk, timer_baseaddr);
+ ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
+
+ pr_info("%s #0@%p, irq=%d\n", timer->name, timer_baseaddr, irq);
+}
+
+CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init);
--
1.7.9.7
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v3 05/12] arm: zynq: Load scu baseaddress at run time
2013-03-28 16:38 Zynq core changes v3 Michal Simek
` (3 preceding siblings ...)
2013-03-28 16:38 ` [PATCH v3 04/12] arm: zynq: Move timer to generic location Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 06/12] arm: zynq: Move slcr initialization to separate file Michal Simek
` (6 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
Use Cortex a9 cp15 to read scu baseaddress.
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
v3: Rename scu_base to zynq_scu_base
v2: Remove dynamic mapping because it is probably not needed
(code taken from vexpress platsmp code)
Remove scu node from DTS
Use zynq_scu_map_io() instead of scu_init() as Steffen suggested
Add comment to scu_init to be sure that we know what we are doing here
---
arch/arm/mach-zynq/common.c | 34 ++++++++++++++++++++++------------
arch/arm/mach-zynq/common.h | 2 ++
2 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 68e0907..f9e5f3a 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 *zynq_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,31 @@ 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 zynq_scu_map_io(void)
+{
+ unsigned long base;
+
+ base = scu_a9_get_base();
+ zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
+ /* Expected address is in vmalloc area that's why simple assign here */
+ zynq_cortex_a9_scu_map.virtual = base;
+ iotable_init(&zynq_cortex_a9_scu_map, 1);
+ zynq_scu_base = (void __iomem *)base;
+ BUG_ON(!zynq_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);
+ zynq_scu_map_io();
}
static const char *xilinx_dt_match[] = {
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index 5050bb1..d7164d5 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 *zynq_scu_base;
+
#endif
--
1.7.9.7
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v3 06/12] arm: zynq: Move slcr initialization to separate file
2013-03-28 16:38 Zynq core changes v3 Michal Simek
` (4 preceding siblings ...)
2013-03-28 16:38 ` [PATCH v3 05/12] arm: zynq: Load scu baseaddress at run time Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 07/12] arm: zynq: Add support for system reset Michal Simek
` (5 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
Create separate slcr driver instead of polluting common code.
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
v3: Rename slcr_init to zynq_slcr_init
---
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 f9e5f3a..cd3968c 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);
-
+ zynq_slcr_init();
clocksource_of_init();
}
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index d7164d5..dd594e6 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 zynq_slcr_init(void);
+
+extern void __iomem *zynq_slcr_base;
extern void __iomem *zynq_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..f9f3349
--- /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;
+
+/**
+ * zynq_slcr_init
+ * Returns 0 on success, negative errno otherwise.
+ *
+ * Called early during boot from platform code to remap SLCR area.
+ */
+int __init zynq_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] 13+ messages in thread
* [PATCH v3 07/12] arm: zynq: Add support for system reset
2013-03-28 16:38 Zynq core changes v3 Michal Simek
` (5 preceding siblings ...)
2013-03-28 16:38 ` [PATCH v3 06/12] arm: zynq: Move slcr initialization to separate file Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 08/12] arm: zynq: Add support for pmu Michal Simek
` (4 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
Do system reset via slcr registers.
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
v3: Use zynq_ prefix for all zynq specific functions
v2: Fix comment
use writel instead of __raw_writel
Do not use PSS - use PS instead
---
arch/arm/mach-zynq/common.c | 6 ++++++
arch/arm/mach-zynq/common.h | 1 +
arch/arm/mach-zynq/slcr.c | 27 +++++++++++++++++++++++++++
3 files changed, 34 insertions(+)
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index cd3968c..f0a8533 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -92,6 +92,11 @@ static void __init xilinx_map_io(void)
zynq_scu_map_io();
}
+static void zynq_system_reset(char mode, const char *cmd)
+{
+ zynq_slcr_system_reset();
+}
+
static const char *xilinx_dt_match[] = {
"xlnx,zynq-zc702",
"xlnx,zynq-7000",
@@ -104,4 +109,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 = zynq_system_reset,
MACHINE_END
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index dd594e6..d7ec3ca 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 zynq_slcr_init(void);
+extern void zynq_slcr_system_reset(void);
extern void __iomem *zynq_slcr_base;
extern void __iomem *zynq_scu_base;
diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c
index f9f3349..d58c996 100644
--- a/arch/arm/mach-zynq/slcr.c
+++ b/arch/arm/mach-zynq/slcr.c
@@ -32,9 +32,36 @@
#define SLCR_UNLOCK_MAGIC 0xDF0D
#define SLCR_UNLOCK 0x8 /* SCLR unlock register */
+#define SLCR_PS_RST_CTRL_OFFSET 0x200 /* PS Software Reset Control */
+#define SLCR_REBOOT_STATUS 0x258 /* PS Reboot Status */
+
void __iomem *zynq_slcr_base;
/**
+ * zynq_slcr_system_reset - Reset the entire system.
+ */
+void zynq_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?
+ */
+ 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_PS_RST_CTRL_OFFSET);
+}
+
+/**
* zynq_slcr_init
* Returns 0 on success, negative errno otherwise.
*
--
1.7.9.7
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v3 08/12] arm: zynq: Add support for pmu
2013-03-28 16:38 Zynq core changes v3 Michal Simek
` (6 preceding siblings ...)
2013-03-28 16:38 ` [PATCH v3 07/12] arm: zynq: Add support for system reset Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 09/12] arm: zynq: Add smp_twd timer Michal Simek
` (3 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 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 51243db..748fc34 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] 13+ messages in thread
* [PATCH v3 09/12] arm: zynq: Add smp_twd timer
2013-03-28 16:38 Zynq core changes v3 Michal Simek
` (7 preceding siblings ...)
2013-03-28 16:38 ` [PATCH v3 08/12] arm: zynq: Add support for pmu Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 10/12] arm: zynq: Add smp support Michal Simek
` (2 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
The zynq has a Cortex-A9 with the corresponding smp_twd timers. Use them.
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
v3: New patch for this series to avoid bug in hotplug
Warning log:
zynq> echo 1 > /sys/devices/system/cpu/cpu1/online
CPU1: Booted secondary processor
Clockevents: could not switch to one-shot mode: dummy_timer is not functional.
Could not switch to high resolution mode on CPU 1
---
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 748fc34..14fb2e6 100644
--- a/arch/arm/boot/dts/zynq-7000.dtsi
+++ b/arch/arm/boot/dts/zynq-7000.dtsi
@@ -136,5 +136,12 @@
clock-names = "cpu_1x";
clock-ranges;
};
+ scutimer: scutimer at f8f00600 {
+ interrupt-parent = <&intc>;
+ interrupts = < 1 13 0x301 >;
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = < 0xf8f00600 0x20 >;
+ clocks = <&cpu_clk 1>;
+ } ;
};
};
--
1.7.9.7
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v3 10/12] arm: zynq: Add smp support
2013-03-28 16:38 Zynq core changes v3 Michal Simek
` (8 preceding siblings ...)
2013-03-28 16:38 ` [PATCH v3 09/12] arm: zynq: Add smp_twd timer Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 11/12] arm: zynq: Add hotplug support Michal Simek
2013-03-28 16:38 ` [PATCH v3 12/12] arm: zynq: Get rid of xilinx function prefix Michal Simek
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 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>
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
v3: Fix jump vector placing - forget to remove
Use zynq_ prefix in function names
v2: Remove boot_lock from secondary_init
Incorporate Steffen changes in smp code
Use flush range instead of simple flush
Add HAVE_SMP
Add support for the booting kernels with
non zero starting address
---
arch/arm/mach-zynq/Kconfig | 1 +
arch/arm/mach-zynq/Makefile | 1 +
arch/arm/mach-zynq/common.c | 1 +
arch/arm/mach-zynq/common.h | 11 ++++
arch/arm/mach-zynq/headsmp.S | 24 +++++++
arch/arm/mach-zynq/platsmp.c | 149 ++++++++++++++++++++++++++++++++++++++++++
arch/arm/mach-zynq/slcr.c | 29 ++++++++
7 files changed, 216 insertions(+)
create mode 100644 arch/arm/mach-zynq/headsmp.S
create mode 100644 arch/arm/mach-zynq/platsmp.c
diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig
index d70651e..f4a7e63 100644
--- a/arch/arm/mach-zynq/Kconfig
+++ b/arch/arm/mach-zynq/Kconfig
@@ -8,6 +8,7 @@ config ARCH_ZYNQ
select ICST
select MIGHT_HAVE_CACHE_L2X0
select USE_OF
+ select HAVE_SMP
select SPARSE_IRQ
select CADENCE_TTC_TIMER
help
diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
index 13ee09b..b595d22 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) += headsmp.o platsmp.o
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index f0a8533..8981345 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -104,6 +104,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 d7ec3ca..fd308f8 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -19,6 +19,17 @@
extern int zynq_slcr_init(void);
extern void zynq_slcr_system_reset(void);
+extern void zynq_slcr_cpu_stop(int cpu);
+extern void zynq_slcr_cpu_start(int cpu);
+
+#ifdef CONFIG_SMP
+extern void secondary_startup(void);
+extern char zynq_secondary_trampoline;
+extern char zynq_secondary_trampoline_jump;
+extern char zynq_secondary_trampoline_end;
+extern int __cpuinit zynq_cpun_start(u32 address, int cpu);
+extern struct smp_operations zynq_smp_ops __initdata;
+#endif
extern void __iomem *zynq_slcr_base;
extern void __iomem *zynq_scu_base;
diff --git a/arch/arm/mach-zynq/headsmp.S b/arch/arm/mach-zynq/headsmp.S
new file mode 100644
index 0000000..d183cd2
--- /dev/null
+++ b/arch/arm/mach-zynq/headsmp.S
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ * Copyright (c) 2012-2013 Xilinx
+ *
+ * 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/linkage.h>
+#include <linux/init.h>
+
+ __CPUINIT
+
+ENTRY(zynq_secondary_trampoline)
+ ldr r0, [pc]
+ bx r0
+.globl zynq_secondary_trampoline_jump
+zynq_secondary_trampoline_jump:
+ /* Space for jumping address */
+ .word /* cpu 1 */
+.globl zynq_secondary_trampoline_end
+zynq_secondary_trampoline_end:
+
+ENDPROC(zynq_secondary_trampoline)
diff --git a/arch/arm/mach-zynq/platsmp.c b/arch/arm/mach-zynq/platsmp.c
new file mode 100644
index 0000000..cdfd888
--- /dev/null
+++ b/arch/arm/mach-zynq/platsmp.c
@@ -0,0 +1,149 @@
+/*
+ * 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"
+
+/*
+ * 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);
+}
+
+int __cpuinit zynq_cpun_start(u32 address, int cpu)
+{
+ u32 trampoline_code_size = &zynq_secondary_trampoline_end -
+ &zynq_secondary_trampoline;
+
+ 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 >= trampoline_code_size))) {
+ /* Store pointer to ioremap area which points to address 0x0 */
+ static u8 __iomem *zero;
+ u32 trampoline_size = &zynq_secondary_trampoline_jump -
+ &zynq_secondary_trampoline;
+
+ zynq_slcr_cpu_stop(cpu);
+
+ if (__pa(PAGE_OFFSET)) {
+ zero = ioremap(0, trampoline_code_size);
+ if (!zero) {
+ pr_warn("BOOTUP jump vectors not accessible\n");
+ return -1;
+ }
+ } else {
+ zero = (__force u8 __iomem *)PAGE_OFFSET;
+ }
+
+ /*
+ * 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
+ */
+ memcpy((__force void *)zero, &zynq_secondary_trampoline,
+ trampoline_size);
+ writel(address, zero + trampoline_size);
+
+ flush_cache_all();
+ outer_flush_range(0, trampoline_code_size);
+ smp_wmb();
+
+ if (__pa(PAGE_OFFSET))
+ iounmap(zero);
+
+ zynq_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)
+{
+ return zynq_cpun_start(virt_to_phys(secondary_startup), cpu);
+}
+
+/*
+ * 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(zynq_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(zynq_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 d58c996..c70969b 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_PS_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;
@@ -62,6 +67,30 @@ void zynq_slcr_system_reset(void)
}
/**
+ * zynq_slcr_cpu_start - Start cpu
+ * @cpu: cpu number
+ */
+void zynq_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);
+}
+
+/**
+ * zynq_slcr_cpu_stop - Stop cpu
+ * @cpu: cpu number
+ */
+void zynq_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);
+}
+
+/**
* zynq_slcr_init
* Returns 0 on success, negative errno otherwise.
*
--
1.7.9.7
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v3 11/12] arm: zynq: Add hotplug support
2013-03-28 16:38 Zynq core changes v3 Michal Simek
` (9 preceding siblings ...)
2013-03-28 16:38 ` [PATCH v3 10/12] arm: zynq: Add smp support Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
2013-03-28 16:38 ` [PATCH v3 12/12] arm: zynq: Get rid of xilinx function prefix Michal Simek
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
v3: Use zynq_ prefix for all functions
v2: Change platform_cpu_die name function to zynq_platform_cpu_dio
Fix header
Add cpu compilation flags to be able to build on V6 too
---
arch/arm/mach-zynq/Makefile | 3 ++
arch/arm/mach-zynq/common.h | 3 ++
arch/arm/mach-zynq/hotplug.c | 104 ++++++++++++++++++++++++++++++++++++++++++
arch/arm/mach-zynq/platsmp.c | 3 ++
4 files changed, 113 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 b595d22..1b25d92 100644
--- a/arch/arm/mach-zynq/Makefile
+++ b/arch/arm/mach-zynq/Makefile
@@ -4,4 +4,7 @@
# Common support
obj-y := common.o slcr.o
+CFLAGS_REMOVE_hotplug.o =-march=armv6k
+CFLAGS_hotplug.o =-Wa,-march=armv7-a -mcpu=cortex-a9
+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index fd308f8..fbbd0e2 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -34,4 +34,7 @@ extern struct smp_operations zynq_smp_ops __initdata;
extern void __iomem *zynq_slcr_base;
extern void __iomem *zynq_scu_base;
+/* Hotplug */
+extern void zynq_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..c89672b
--- /dev/null
+++ b/arch/arm/mach-zynq/hotplug.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012-2013 Xilinx
+ *
+ * based on 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 zynq_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 zynq_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 zynq_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 zynq_platform_cpu_die(unsigned int cpu)
+{
+ int spurious = 0;
+
+ /*
+ * we're ready for shutdown now, so do it
+ */
+ zynq_cpu_enter_lowpower();
+ zynq_platform_do_lowpower(cpu, &spurious);
+
+ /*
+ * bring this CPU back into the world of cache
+ * coherency, and then restore interrupts
+ */
+ zynq_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 cdfd888..3072cbd 100644
--- a/arch/arm/mach-zynq/platsmp.c
+++ b/arch/arm/mach-zynq/platsmp.c
@@ -146,4 +146,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 = zynq_platform_cpu_die,
+#endif
};
--
1.7.9.7
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v3 12/12] arm: zynq: Get rid of xilinx function prefix
2013-03-28 16:38 Zynq core changes v3 Michal Simek
` (10 preceding siblings ...)
2013-03-28 16:38 ` [PATCH v3 11/12] arm: zynq: Add hotplug support Michal Simek
@ 2013-03-28 16:38 ` Michal Simek
11 siblings, 0 replies; 13+ messages in thread
From: Michal Simek @ 2013-03-28 16:38 UTC (permalink / raw)
To: linux-arm-kernel
Xilinx is vendor name not SoC name. Use zynq instead.
Also remove one checkpatch warning:
WARNING: static const char * array should probably be
static const char * const
+static const char *xilinx_dt_match[] = {
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
v3: First version - based on discussion on mailing list
---
arch/arm/mach-zynq/common.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 8981345..5bfe703 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -46,10 +46,10 @@ static struct of_device_id zynq_of_bus_ids[] __initdata = {
};
/**
- * xilinx_init_machine() - System specific initialization, intended to be
- * called from board specific initialization.
+ * zynq_init_machine - System specific initialization, intended to be
+ * called from board specific initialization.
*/
-static void __init xilinx_init_machine(void)
+static void __init zynq_init_machine(void)
{
/*
* 64KB way size, 8-way associativity, parity disabled
@@ -59,7 +59,7 @@ static void __init xilinx_init_machine(void)
of_platform_bus_probe(NULL, zynq_of_bus_ids, NULL);
}
-static void __init xilinx_zynq_timer_init(void)
+static void __init zynq_timer_init(void)
{
zynq_slcr_init();
clocksource_of_init();
@@ -84,9 +84,9 @@ static void __init zynq_scu_map_io(void)
}
/**
- * xilinx_map_io() - Create memory mappings needed for early I/O.
+ * zynq_map_io - Create memory mappings needed for early I/O.
*/
-static void __init xilinx_map_io(void)
+static void __init zynq_map_io(void)
{
debug_ll_io_init();
zynq_scu_map_io();
@@ -97,7 +97,7 @@ static void zynq_system_reset(char mode, const char *cmd)
zynq_slcr_system_reset();
}
-static const char *xilinx_dt_match[] = {
+static const char * const zynq_dt_match[] = {
"xlnx,zynq-zc702",
"xlnx,zynq-7000",
NULL
@@ -105,10 +105,10 @@ static const char *xilinx_dt_match[] = {
MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
.smp = smp_ops(zynq_smp_ops),
- .map_io = xilinx_map_io,
+ .map_io = zynq_map_io,
.init_irq = irqchip_init,
- .init_machine = xilinx_init_machine,
- .init_time = xilinx_zynq_timer_init,
- .dt_compat = xilinx_dt_match,
+ .init_machine = zynq_init_machine,
+ .init_time = zynq_timer_init,
+ .dt_compat = zynq_dt_match,
.restart = zynq_system_reset,
MACHINE_END
--
1.7.9.7
^ permalink raw reply related [flat|nested] 13+ messages in thread
end of thread, other threads:[~2013-03-28 16:38 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-03-28 16:38 Zynq core changes v3 Michal Simek
2013-03-28 16:38 ` [PATCH v3 01/12] arm: zynq: Use standard timer binding Michal Simek
2013-03-28 16:38 ` [PATCH v3 02/12] arm: zynq: Move timer to clocksource interface Michal Simek
2013-03-28 16:38 ` [PATCH v3 03/12] arm: zynq: Do not use xilinx specific function names Michal Simek
2013-03-28 16:38 ` [PATCH v3 04/12] arm: zynq: Move timer to generic location Michal Simek
2013-03-28 16:38 ` [PATCH v3 05/12] arm: zynq: Load scu baseaddress at run time Michal Simek
2013-03-28 16:38 ` [PATCH v3 06/12] arm: zynq: Move slcr initialization to separate file Michal Simek
2013-03-28 16:38 ` [PATCH v3 07/12] arm: zynq: Add support for system reset Michal Simek
2013-03-28 16:38 ` [PATCH v3 08/12] arm: zynq: Add support for pmu Michal Simek
2013-03-28 16:38 ` [PATCH v3 09/12] arm: zynq: Add smp_twd timer Michal Simek
2013-03-28 16:38 ` [PATCH v3 10/12] arm: zynq: Add smp support Michal Simek
2013-03-28 16:38 ` [PATCH v3 11/12] arm: zynq: Add hotplug support Michal Simek
2013-03-28 16:38 ` [PATCH v3 12/12] arm: zynq: Get rid of xilinx function prefix Michal Simek
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).