From e0c25362384f4be9c755c98560cd4b1cdb2ec79c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 10 Mar 2013 21:52:53 -0500 Subject: [PATCH 1/9] clocksource: add empty version of clocksource_of_init Add an empty clocksource_of_init when !CLKSRC_OF. This is needed for builds where no timer has selected CLKSRC_OF. Signed-off-by: Rob Herring Cc: John Stultz Cc: Thomas Gleixner --- include/linux/clocksource.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 27cfda427dd9..08ed5e19d8c6 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -340,6 +340,7 @@ extern void clocksource_of_init(void); __used __section(__clksrc_of_table) \ = { .compatible = compat, .data = fn }; #else +static inline void clocksource_of_init(void) {} #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) #endif From effbfdd7baf7babc73154b87a5ff940969cf6559 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 6 Feb 2013 14:40:22 -0600 Subject: [PATCH 2/9] clocksource: pass DT node pointer to init functions In cases where we have multiple nodes of the same type, we may need the node pointer to know which node was matched. Passing the node pointer also keeps the init function from having to match the node a 2nd time. Update bcm2835, vt8500, and tegra20 init functions for the new function prototype. Further tegra20 clean-ups are in follow-up commit. Signed-off-by: Rob Herring Cc: John Stultz Cc: Thomas Gleixner Reviewed-by: Stephen Warren Tested-by: Stephen Warren Acked-by: Arnd Bergmann Acked-by: Tony Prisk Tested-by: Michal Simek --- drivers/clocksource/bcm2835_timer.c | 12 +----------- drivers/clocksource/clksrc-of.c | 4 ++-- drivers/clocksource/tegra20_timer.c | 3 +-- drivers/clocksource/vt8500_timer.c | 14 +------------- 4 files changed, 5 insertions(+), 28 deletions(-) diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c index 50c68fef944b..766611d29945 100644 --- a/drivers/clocksource/bcm2835_timer.c +++ b/drivers/clocksource/bcm2835_timer.c @@ -95,23 +95,13 @@ static irqreturn_t bcm2835_time_interrupt(int irq, void *dev_id) } } -static struct of_device_id bcm2835_time_match[] __initconst = { - { .compatible = "brcm,bcm2835-system-timer" }, - {} -}; - -static void __init bcm2835_timer_init(void) +static void __init bcm2835_timer_init(struct device_node *node) { - struct device_node *node; void __iomem *base; u32 freq; int irq; struct bcm2835_timer *timer; - node = of_find_matching_node(NULL, bcm2835_time_match); - if (!node) - panic("No bcm2835 timer node"); - base = of_iomap(node, 0); if (!base) panic("Can't remap registers"); diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c index bdabdaa8d00f..3ef11fba781c 100644 --- a/drivers/clocksource/clksrc-of.c +++ b/drivers/clocksource/clksrc-of.c @@ -26,10 +26,10 @@ void __init clocksource_of_init(void) { struct device_node *np; const struct of_device_id *match; - void (*init_func)(void); + void (*init_func)(struct device_node *); for_each_matching_node_and_match(np, __clksrc_of_table, &match) { init_func = match->data; - init_func(); + init_func(np); } } diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index 0bde03feb095..b3396ab15f63 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -164,9 +164,8 @@ static const struct of_device_id rtc_match[] __initconst = { {} }; -static void __init tegra20_init_timer(void) +static void __init tegra20_init_timer(struct device_node *np) { - struct device_node *np; struct clk *clk; unsigned long rate; int ret; diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c index 8efc86b5b5dd..242255285597 100644 --- a/drivers/clocksource/vt8500_timer.c +++ b/drivers/clocksource/vt8500_timer.c @@ -129,22 +129,10 @@ static struct irqaction irq = { .dev_id = &clockevent, }; -static struct of_device_id vt8500_timer_ids[] = { - { .compatible = "via,vt8500-timer" }, - { } -}; - -static void __init vt8500_timer_init(void) +static void __init vt8500_timer_init(struct device_node *np) { - struct device_node *np; int timer_irq; - np = of_find_matching_node(NULL, vt8500_timer_ids); - if (!np) { - pr_err("%s: Timer description missing from Device Tree\n", - __func__); - return; - } regbase = of_iomap(np, 0); if (!regbase) { pr_err("%s: Missing iobase description in Device Tree\n", From 1d16cfb3aeba71bc6ecf2d19ccbabed0426e5c22 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 7 Feb 2013 11:36:23 -0600 Subject: [PATCH 3/9] clocksource: tegra20: use the device_node pointer passed to init We've already matched the node, so use the node pointer passed in. The rtc init was intermingled with the timer init, so split this out to a separate init function. Signed-off-by: Rob Herring Cc: John Stultz Cc: Thomas Gleixner Reviewed-by: Stephen Warren Tested-by: Stephen Warren --- drivers/clocksource/tegra20_timer.c | 67 +++++++++++------------------ 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index b3396ab15f63..15cc723f699f 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -154,28 +154,12 @@ static struct irqaction tegra_timer_irq = { .dev_id = &tegra_clockevent, }; -static const struct of_device_id timer_match[] __initconst = { - { .compatible = "nvidia,tegra20-timer" }, - {} -}; - -static const struct of_device_id rtc_match[] __initconst = { - { .compatible = "nvidia,tegra20-rtc" }, - {} -}; - static void __init tegra20_init_timer(struct device_node *np) { struct clk *clk; unsigned long rate; int ret; - np = of_find_matching_node(NULL, timer_match); - if (!np) { - pr_err("Failed to find timer DT node\n"); - BUG(); - } - timer_reg_base = of_iomap(np, 0); if (!timer_reg_base) { pr_err("Can't map timer registers\n"); @@ -199,30 +183,6 @@ static void __init tegra20_init_timer(struct device_node *np) of_node_put(np); - np = of_find_matching_node(NULL, rtc_match); - if (!np) { - pr_err("Failed to find RTC DT node\n"); - BUG(); - } - - rtc_base = of_iomap(np, 0); - if (!rtc_base) { - pr_err("Can't map RTC registers"); - BUG(); - } - - /* - * rtc registers are used by read_persistent_clock, keep the rtc clock - * enabled - */ - clk = clk_get_sys("rtc-tegra", NULL); - if (IS_ERR(clk)) - pr_warn("Unable to get rtc-tegra clock\n"); - else - clk_prepare_enable(clk); - - of_node_put(np); - switch (rate) { case 12000000: timer_writel(0x000b, TIMERUS_USEC_CFG); @@ -261,9 +221,34 @@ static void __init tegra20_init_timer(struct device_node *np) #ifdef CONFIG_HAVE_ARM_TWD twd_local_timer_of_register(); #endif +} +CLOCKSOURCE_OF_DECLARE(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer); + +static void __init tegra20_init_rtc(struct device_node *np) +{ + struct clk *clk; + + rtc_base = of_iomap(np, 0); + if (!rtc_base) { + pr_err("Can't map RTC registers"); + BUG(); + } + + /* + * rtc registers are used by read_persistent_clock, keep the rtc clock + * enabled + */ + clk = clk_get_sys("rtc-tegra", NULL); + if (IS_ERR(clk)) + pr_warn("Unable to get rtc-tegra clock\n"); + else + clk_prepare_enable(clk); + + of_node_put(np); + register_persistent_clock(NULL, tegra_read_persistent_clock); } -CLOCKSOURCE_OF_DECLARE(tegra20, "nvidia,tegra20-timer", tegra20_init_timer); +CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc); #ifdef CONFIG_PM static u32 usec_config; From da4a686a2cfb077a8bfc1697f597e7f86235b822 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 6 Feb 2013 21:17:47 -0600 Subject: [PATCH 4/9] ARM: smp_twd: convert to use CLKSRC_OF init Now that we have OF based init with CLKSRC_OF, convert smp_twd init function to use it and covert all callers of twd_local_timer_of_register. Signed-off-by: Rob Herring Cc: Shawn Guo Cc: Sascha Hauer Cc: Russell King Cc: Viresh Kumar Cc: Shiraz Hashim Cc: Srinidhi Kasagar Cc: John Stultz Cc: Thomas Gleixner Cc: linux-omap@vger.kernel.org Cc: spear-devel@list.st.com Reviewed-by: Stephen Warren Acked-by: Santosh Shilimkar Acked-by: Tony Lindgren Acked-by: Linus Walleij --- arch/arm/Kconfig | 1 + arch/arm/include/asm/smp_twd.h | 8 -------- arch/arm/kernel/smp_twd.c | 17 ++++------------- arch/arm/mach-highbank/highbank.c | 5 ++--- arch/arm/mach-imx/mach-imx6q.c | 5 ++--- arch/arm/mach-omap2/timer.c | 2 +- arch/arm/mach-spear13xx/spear13xx.c | 4 ++-- arch/arm/mach-ux500/timer.c | 3 ++- arch/arm/mach-vexpress/v2m.c | 6 +++--- drivers/clocksource/tegra20_timer.c | 3 --- 10 files changed, 17 insertions(+), 37 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5b714695b01b..5bfd584929c8 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1597,6 +1597,7 @@ config HAVE_ARM_ARCH_TIMER config HAVE_ARM_TWD bool depends on SMP + select CLKSRC_OF if OF help This options enables support for the ARM timer and watchdog unit diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h index 0f01f4677bd2..7b2899c2f7fc 100644 --- a/arch/arm/include/asm/smp_twd.h +++ b/arch/arm/include/asm/smp_twd.h @@ -34,12 +34,4 @@ struct twd_local_timer name __initdata = { \ int twd_local_timer_register(struct twd_local_timer *); -#ifdef CONFIG_HAVE_ARM_TWD -void twd_local_timer_of_register(void); -#else -static inline void twd_local_timer_of_register(void) -{ -} -#endif - #endif diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 3f2565037480..90525d9d290b 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -362,25 +362,13 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) } #ifdef CONFIG_OF -const static struct of_device_id twd_of_match[] __initconst = { - { .compatible = "arm,cortex-a9-twd-timer", }, - { .compatible = "arm,cortex-a5-twd-timer", }, - { .compatible = "arm,arm11mp-twd-timer", }, - { }, -}; - -void __init twd_local_timer_of_register(void) +static void __init twd_local_timer_of_register(struct device_node *np) { - struct device_node *np; int err; if (!is_smp() || !setup_max_cpus) return; - np = of_find_matching_node(NULL, twd_of_match); - if (!np) - return; - twd_ppi = irq_of_parse_and_map(np, 0); if (!twd_ppi) { err = -EINVAL; @@ -398,4 +386,7 @@ void __init twd_local_timer_of_register(void) out: WARN(err, "twd_local_timer_of_register failed (%d)\n", err); } +CLOCKSOURCE_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register); +CLOCKSOURCE_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register); +CLOCKSOURCE_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register); #endif diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c index a4f9f50247d4..76c1170b3528 100644 --- a/arch/arm/mach-highbank/highbank.c +++ b/arch/arm/mach-highbank/highbank.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -119,10 +118,10 @@ static void __init highbank_timer_init(void) sp804_clocksource_and_sched_clock_init(timer_base + 0x20, "timer1"); sp804_clockevents_init(timer_base, irq, "timer0"); - twd_local_timer_of_register(); - arch_timer_of_register(); arch_timer_sched_clock_init(); + + clocksource_of_init(); } static void highbank_power_off(void) diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 9ffd103b27e4..b59ddcb57c78 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -28,11 +29,9 @@ #include #include #include -#include #include #include #include -#include #include #include "common.h" @@ -292,7 +291,7 @@ static void __init imx6q_init_irq(void) static void __init imx6q_timer_init(void) { mx6q_clocks_init(); - twd_local_timer_of_register(); + clocksource_of_init(); imx_print_silicon_rev("i.MX6Q", imx6q_revision()); } diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c index 2bdd4cf17a8f..4fd80257c73e 100644 --- a/arch/arm/mach-omap2/timer.c +++ b/arch/arm/mach-omap2/timer.c @@ -597,7 +597,7 @@ void __init omap4_local_timer_init(void) int err; if (of_have_populated_dt()) { - twd_local_timer_of_register(); + clocksource_of_init(); return; } diff --git a/arch/arm/mach-spear13xx/spear13xx.c b/arch/arm/mach-spear13xx/spear13xx.c index c7d2b4a8d8cc..25a10191b021 100644 --- a/arch/arm/mach-spear13xx/spear13xx.c +++ b/arch/arm/mach-spear13xx/spear13xx.c @@ -15,12 +15,12 @@ #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -179,5 +179,5 @@ void __init spear13xx_timer_init(void) clk_put(pclk); spear_setup_of_timer(); - twd_local_timer_of_register(); + clocksource_of_init(); } diff --git a/arch/arm/mach-ux500/timer.c b/arch/arm/mach-ux500/timer.c index a6af0b8732ba..d07bbe7f04a6 100644 --- a/arch/arm/mach-ux500/timer.c +++ b/arch/arm/mach-ux500/timer.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,7 @@ static void __init ux500_twd_init(void) twd_local_timer = &u8500_twd_local_timer; if (of_have_populated_dt()) - twd_local_timer_of_register(); + clocksource_of_init(); else { err = twd_local_timer_register(twd_local_timer); if (err) diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c index 915683cb67d6..d0ad78998cb6 100644 --- a/arch/arm/mach-vexpress/v2m.c +++ b/arch/arm/mach-vexpress/v2m.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -435,6 +435,7 @@ static void __init v2m_dt_timer_init(void) vexpress_clk_of_init(); + clocksource_of_init(); do { node = of_find_compatible_node(node, NULL, "arm,sp804"); } while (node && vexpress_get_site_by_node(node) != VEXPRESS_SITE_MB); @@ -445,8 +446,7 @@ static void __init v2m_dt_timer_init(void) irq_of_parse_and_map(node, 0)); } - if (arch_timer_of_register() != 0) - twd_local_timer_of_register(); + arch_timer_of_register(); if (arch_timer_sched_clock_init() != 0) versatile_sched_clock_init(vexpress_get_24mhz_clock_base(), diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index 15cc723f699f..2e4d8a666c36 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -218,9 +218,6 @@ static void __init tegra20_init_timer(struct device_node *np) tegra_clockevent.irq = tegra_timer_irq.irq; clockevents_config_and_register(&tegra_clockevent, 1000000, 0x1, 0x1fffffff); -#ifdef CONFIG_HAVE_ARM_TWD - twd_local_timer_of_register(); -#endif } CLOCKSOURCE_OF_DECLARE(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer); From 3d5a96582303e28c48699f3faaf920ef7d43e6f2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 19 Mar 2013 15:38:50 +0100 Subject: [PATCH 5/9] clocksource: make CLOCKSOURCE_OF_DECLARE type safe This ensures that a function pointer passed into CLOCKSOURCE_OF_DECLARE takes the same arguments that we use for calling that function later. Also fix the extraneous semicolon at end of the CLOCKSOURCE_OF_DECLARE definition. Signed-off-by: Arnd Bergmann Acked-by: Rob Herring --- drivers/clocksource/clksrc-of.c | 3 ++- drivers/clocksource/vt8500_timer.c | 2 +- include/linux/clocksource.h | 11 +++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c index 3ef11fba781c..37f5325bec95 100644 --- a/drivers/clocksource/clksrc-of.c +++ b/drivers/clocksource/clksrc-of.c @@ -16,6 +16,7 @@ #include #include +#include extern struct of_device_id __clksrc_of_table[]; @@ -26,7 +27,7 @@ void __init clocksource_of_init(void) { struct device_node *np; const struct of_device_id *match; - void (*init_func)(struct device_node *); + clocksource_of_init_fn init_func; for_each_matching_node_and_match(np, __clksrc_of_table, &match) { init_func = match->data; diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c index 242255285597..64f553f04fa4 100644 --- a/drivers/clocksource/vt8500_timer.c +++ b/drivers/clocksource/vt8500_timer.c @@ -165,4 +165,4 @@ static void __init vt8500_timer_init(struct device_node *np) 4, 0xf0000000); } -CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init) +CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init); diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 08ed5e19d8c6..192d6d1771ee 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -332,16 +332,23 @@ extern int clocksource_mmio_init(void __iomem *, const char *, extern int clocksource_i8253_init(void); +struct device_node; +typedef void(*clocksource_of_init_fn)(struct device_node *); #ifdef CONFIG_CLKSRC_OF extern void clocksource_of_init(void); #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ static const struct of_device_id __clksrc_of_table_##name \ __used __section(__clksrc_of_table) \ - = { .compatible = compat, .data = fn }; + = { .compatible = compat, \ + .data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn } #else static inline void clocksource_of_init(void) {} -#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) +#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ + static const struct of_device_id __clksrc_of_table_##name \ + __attribute__((unused)) \ + = { .compatible = compat, \ + .data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn } #endif #endif /* _LINUX_CLOCKSOURCE_H */ From e932900a3279b5dbb6d8f43c7b369003620e137c Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 20 Mar 2013 10:15:28 +0100 Subject: [PATCH 6/9] arm: zynq: Use standard timer binding 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 --- 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 5914b5654591..51243db2e9e4 100644 --- a/arch/arm/boot/dts/zynq-7000.dtsi +++ b/arch/arm/boot/dts/zynq-7000.dtsi @@ -111,56 +111,23 @@ }; ttc0: ttc0@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@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 c772942a399a..86f44d5b0265 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 5c8983218183..76493b050beb 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c index f9fbc9c1e7a6..82357d94a0e0 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 #include #include #include @@ -23,6 +24,21 @@ #include #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, + * + * 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 at %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); } From c5263bb8b7944f1e34b36b5ea8a9119fc48a31ae Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 20 Mar 2013 10:24:59 +0100 Subject: [PATCH 7/9] arm: zynq: Move timer to clocksource interface Use clocksource timer initialization. Signed-off-by: Michal Simek --- 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 76493b050beb..68e0907de5d0 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 8b4dbbaa01cf..5050bb10bb12 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 82357d94a0e0..ab5b839e22f0 100644 --- a/arch/arm/mach-zynq/timer.c +++ b/arch/arm/mach-zynq/timer.c @@ -22,7 +22,6 @@ #include #include #include -#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); From 9e09dc5f7fdc1e914c3b7bc186fa4b54d05a88d6 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 27 Mar 2013 12:05:28 +0100 Subject: [PATCH 8/9] arm: zynq: Do not use xilinx specific function names 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 --- 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 ab5b839e22f0..685bc60e210a 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); From 4f0f234fce1d263cc9881456352e8fd56ead0514 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 20 Mar 2013 10:46:01 +0100 Subject: [PATCH 9/9] arm: zynq: Move timer to generic location Move zynq timer out of mach folder to generic location and enable it. Signed-off-by: Michal Simek --- .../bindings/timer/cadence,ttc-timer.txt | 17 +++++++++++++++++ arch/arm/mach-zynq/Kconfig | 1 + arch/arm/mach-zynq/Makefile | 2 +- drivers/clocksource/Kconfig | 3 +++ drivers/clocksource/Makefile | 1 + .../clocksource/cadence_ttc_timer.c | 0 6 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/timer/cadence,ttc-timer.txt rename arch/arm/mach-zynq/timer.c => drivers/clocksource/cadence_ttc_timer.c (100%) 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 000000000000..993695c659e1 --- /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@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 adb6c0ea0e53..d70651e8b705 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 397268c1b250..320faedeb484 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/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index e507ab7df60b..3167fda9bbb3 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 4d8283aec5b5..e74c8ce26bf0 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/arch/arm/mach-zynq/timer.c b/drivers/clocksource/cadence_ttc_timer.c similarity index 100% rename from arch/arm/mach-zynq/timer.c rename to drivers/clocksource/cadence_ttc_timer.c