From effbfdd7baf7babc73154b87a5ff940969cf6559 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 6 Feb 2013 14:40:22 -0600 Subject: 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(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c index 50c68fef944..766611d2994 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 bdabdaa8d00..3ef11fba781 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 0bde03feb09..b3396ab15f6 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 8efc86b5b5d..24225528559 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", -- cgit v1.2.3 From 1d16cfb3aeba71bc6ecf2d19ccbabed0426e5c22 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 7 Feb 2013 11:36:23 -0600 Subject: 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(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index b3396ab15f6..15cc723f699 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; -- cgit v1.2.3 From da4a686a2cfb077a8bfc1697f597e7f86235b822 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 6 Feb 2013 21:17:47 -0600 Subject: 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 --- drivers/clocksource/tegra20_timer.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index 15cc723f699..2e4d8a666c3 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); -- cgit v1.2.3 From 6f88fb8af6c67f281b8e2cd607f08e0089c8ccbe Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Mon, 4 Feb 2013 15:40:30 +0200 Subject: clocksource: tegra: move to of_clk_get The new clockframework introduced DT IDs for each clock. To be able to remove the device registrations, this driver needs to be updated to use the DT IDs. Note that the actual removal of the clk_register_clkdev() calls will be done in a later series. Signed-off-by: Peter De Schrijver Signed-off-by: Stephen Warren --- drivers/clocksource/tegra20_timer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index 0bde03feb09..bc4b8ad78ae 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -189,7 +189,7 @@ static void __init tegra20_init_timer(void) BUG(); } - clk = clk_get_sys("timer", NULL); + clk = of_clk_get(np, 0); if (IS_ERR(clk)) { pr_warn("Unable to get timer clock. Assuming 12Mhz input clock.\n"); rate = 12000000; @@ -216,7 +216,7 @@ static void __init tegra20_init_timer(void) * rtc registers are used by read_persistent_clock, keep the rtc clock * enabled */ - clk = clk_get_sys("rtc-tegra", NULL); + clk = of_clk_get(np, 0); if (IS_ERR(clk)) pr_warn("Unable to get rtc-tegra clock\n"); else -- cgit v1.2.3 From 3d5a96582303e28c48699f3faaf920ef7d43e6f2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 19 Mar 2013 15:38:50 +0100 Subject: 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 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c index 3ef11fba781..37f5325bec9 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 24225528559..64f553f04fa 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); -- cgit v1.2.3 From c74512bf83f3568baf566265c5bd3b8a96ede353 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 25 Mar 2013 20:16:52 +0800 Subject: ARM: mxs: move timer driver into drivers/clocksource Move mxs timer driver into drivers/clocksource as mxs_timer.c. Signed-off-by: Shawn Guo --- drivers/clocksource/Makefile | 1 + drivers/clocksource/mxs_timer.c | 304 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 drivers/clocksource/mxs_timer.c (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 4d8283aec5b..89c5adc498b 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o +obj-$(CONFIG_ARCH_MXS) += mxs_timer.o obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c new file mode 100644 index 00000000000..a4d469b21d9 --- /dev/null +++ b/drivers/clocksource/mxs_timer.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2000-2001 Deep Blue Solutions + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com) + * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) + * Copyright (C) 2010 Freescale Semiconductor, Inc. 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 + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * There are 2 versions of the timrot on Freescale MXS-based SoCs. + * The v1 on MX23 only gets 16 bits counter, while v2 on MX28 + * extends the counter to 32 bits. + * + * The implementation uses two timers, one for clock_event and + * another for clocksource. MX28 uses timrot 0 and 1, while MX23 + * uses 0 and 2. + */ + +#define MX23_TIMROT_VERSION_OFFSET 0x0a0 +#define MX28_TIMROT_VERSION_OFFSET 0x120 +#define BP_TIMROT_MAJOR_VERSION 24 +#define BV_TIMROT_VERSION_1 0x01 +#define BV_TIMROT_VERSION_2 0x02 +#define timrot_is_v1() (timrot_major_version == BV_TIMROT_VERSION_1) + +/* + * There are 4 registers for each timrotv2 instance, and 2 registers + * for each timrotv1. So address step 0x40 in macros below strides + * one instance of timrotv2 while two instances of timrotv1. + * + * As the result, HW_TIMROT_XXXn(1) defines the address of timrot1 + * on MX28 while timrot2 on MX23. + */ +/* common between v1 and v2 */ +#define HW_TIMROT_ROTCTRL 0x00 +#define HW_TIMROT_TIMCTRLn(n) (0x20 + (n) * 0x40) +/* v1 only */ +#define HW_TIMROT_TIMCOUNTn(n) (0x30 + (n) * 0x40) +/* v2 only */ +#define HW_TIMROT_RUNNING_COUNTn(n) (0x30 + (n) * 0x40) +#define HW_TIMROT_FIXED_COUNTn(n) (0x40 + (n) * 0x40) + +#define BM_TIMROT_TIMCTRLn_RELOAD (1 << 6) +#define BM_TIMROT_TIMCTRLn_UPDATE (1 << 7) +#define BM_TIMROT_TIMCTRLn_IRQ_EN (1 << 14) +#define BM_TIMROT_TIMCTRLn_IRQ (1 << 15) +#define BP_TIMROT_TIMCTRLn_SELECT 0 +#define BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL 0x8 +#define BV_TIMROTv2_TIMCTRLn_SELECT__32KHZ_XTAL 0xb +#define BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS 0xf + +static struct clock_event_device mxs_clockevent_device; +static enum clock_event_mode mxs_clockevent_mode = CLOCK_EVT_MODE_UNUSED; + +static void __iomem *mxs_timrot_base; +static u32 timrot_major_version; + +static inline void timrot_irq_disable(void) +{ + __raw_writel(BM_TIMROT_TIMCTRLn_IRQ_EN, mxs_timrot_base + + HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_CLR); +} + +static inline void timrot_irq_enable(void) +{ + __raw_writel(BM_TIMROT_TIMCTRLn_IRQ_EN, mxs_timrot_base + + HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_SET); +} + +static void timrot_irq_acknowledge(void) +{ + __raw_writel(BM_TIMROT_TIMCTRLn_IRQ, mxs_timrot_base + + HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_CLR); +} + +static cycle_t timrotv1_get_cycles(struct clocksource *cs) +{ + return ~((__raw_readl(mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1)) + & 0xffff0000) >> 16); +} + +static int timrotv1_set_next_event(unsigned long evt, + struct clock_event_device *dev) +{ + /* timrot decrements the count */ + __raw_writel(evt, mxs_timrot_base + HW_TIMROT_TIMCOUNTn(0)); + + return 0; +} + +static int timrotv2_set_next_event(unsigned long evt, + struct clock_event_device *dev) +{ + /* timrot decrements the count */ + __raw_writel(evt, mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(0)); + + return 0; +} + +static irqreturn_t mxs_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + timrot_irq_acknowledge(); + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction mxs_timer_irq = { + .name = "MXS Timer Tick", + .dev_id = &mxs_clockevent_device, + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = mxs_timer_interrupt, +}; + +#ifdef DEBUG +static const char *clock_event_mode_label[] const = { + [CLOCK_EVT_MODE_PERIODIC] = "CLOCK_EVT_MODE_PERIODIC", + [CLOCK_EVT_MODE_ONESHOT] = "CLOCK_EVT_MODE_ONESHOT", + [CLOCK_EVT_MODE_SHUTDOWN] = "CLOCK_EVT_MODE_SHUTDOWN", + [CLOCK_EVT_MODE_UNUSED] = "CLOCK_EVT_MODE_UNUSED" +}; +#endif /* DEBUG */ + +static void mxs_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + /* Disable interrupt in timer module */ + timrot_irq_disable(); + + if (mode != mxs_clockevent_mode) { + /* Set event time into the furthest future */ + if (timrot_is_v1()) + __raw_writel(0xffff, + mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1)); + else + __raw_writel(0xffffffff, + mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(1)); + + /* Clear pending interrupt */ + timrot_irq_acknowledge(); + } + +#ifdef DEBUG + pr_info("%s: changing mode from %s to %s\n", __func__, + clock_event_mode_label[mxs_clockevent_mode], + clock_event_mode_label[mode]); +#endif /* DEBUG */ + + /* Remember timer mode */ + mxs_clockevent_mode = mode; + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + pr_err("%s: Periodic mode is not implemented\n", __func__); + break; + case CLOCK_EVT_MODE_ONESHOT: + timrot_irq_enable(); + break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_RESUME: + /* Left event sources disabled, no more interrupts appear */ + break; + } +} + +static struct clock_event_device mxs_clockevent_device = { + .name = "mxs_timrot", + .features = CLOCK_EVT_FEAT_ONESHOT, + .set_mode = mxs_set_mode, + .set_next_event = timrotv2_set_next_event, + .rating = 200, +}; + +static int __init mxs_clockevent_init(struct clk *timer_clk) +{ + if (timrot_is_v1()) + mxs_clockevent_device.set_next_event = timrotv1_set_next_event; + mxs_clockevent_device.cpumask = cpumask_of(0); + clockevents_config_and_register(&mxs_clockevent_device, + clk_get_rate(timer_clk), + timrot_is_v1() ? 0xf : 0x2, + timrot_is_v1() ? 0xfffe : 0xfffffffe); + + return 0; +} + +static struct clocksource clocksource_mxs = { + .name = "mxs_timer", + .rating = 200, + .read = timrotv1_get_cycles, + .mask = CLOCKSOURCE_MASK(16), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static u32 notrace mxs_read_sched_clock_v2(void) +{ + return ~readl_relaxed(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1)); +} + +static int __init mxs_clocksource_init(struct clk *timer_clk) +{ + unsigned int c = clk_get_rate(timer_clk); + + if (timrot_is_v1()) + clocksource_register_hz(&clocksource_mxs, c); + else { + clocksource_mmio_init(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1), + "mxs_timer", c, 200, 32, clocksource_mmio_readl_down); + setup_sched_clock(mxs_read_sched_clock_v2, 32, c); + } + + return 0; +} + +static void __init mxs_timer_init(struct device_node *np) +{ + struct clk *timer_clk; + int irq; + + mxs_timrot_base = of_iomap(np, 0); + WARN_ON(!mxs_timrot_base); + + timer_clk = of_clk_get(np, 0); + if (IS_ERR(timer_clk)) { + pr_err("%s: failed to get clk\n", __func__); + return; + } + + clk_prepare_enable(timer_clk); + + /* + * Initialize timers to a known state + */ + stmp_reset_block(mxs_timrot_base + HW_TIMROT_ROTCTRL); + + /* get timrot version */ + timrot_major_version = __raw_readl(mxs_timrot_base + + (of_device_is_compatible(np, "fsl,imx23-timrot") ? + MX23_TIMROT_VERSION_OFFSET : + MX28_TIMROT_VERSION_OFFSET)); + timrot_major_version >>= BP_TIMROT_MAJOR_VERSION; + + /* one for clock_event */ + __raw_writel((timrot_is_v1() ? + BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL : + BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS) | + BM_TIMROT_TIMCTRLn_UPDATE | + BM_TIMROT_TIMCTRLn_IRQ_EN, + mxs_timrot_base + HW_TIMROT_TIMCTRLn(0)); + + /* another for clocksource */ + __raw_writel((timrot_is_v1() ? + BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL : + BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS) | + BM_TIMROT_TIMCTRLn_RELOAD, + mxs_timrot_base + HW_TIMROT_TIMCTRLn(1)); + + /* set clocksource timer fixed count to the maximum */ + if (timrot_is_v1()) + __raw_writel(0xffff, + mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1)); + else + __raw_writel(0xffffffff, + mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(1)); + + /* init and register the timer to the framework */ + mxs_clocksource_init(timer_clk); + mxs_clockevent_init(timer_clk); + + /* Make irqs happen */ + irq = irq_of_parse_and_map(np, 0); + setup_irq(irq, &mxs_timer_irq); +} +CLOCKSOURCE_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init) -- cgit v1.2.3 From e933a1a12a02f42e0013cda87bba37ccb59efc47 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 2 Apr 2013 13:30:32 -0300 Subject: clocksource: mxs_timer: Add semicolon at end of line Fix the following build error: drivers/clocksource/mxs_timer.c:304:1: error: expected ',' or ';' at end of input Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- drivers/clocksource/mxs_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c index a4d469b21d9..02af4204af8 100644 --- a/drivers/clocksource/mxs_timer.c +++ b/drivers/clocksource/mxs_timer.c @@ -301,4 +301,4 @@ static void __init mxs_timer_init(struct device_node *np) irq = irq_of_parse_and_map(np, 0); setup_irq(irq, &mxs_timer_irq); } -CLOCKSOURCE_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init) +CLOCKSOURCE_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init); -- cgit v1.2.3 From 049817319a5cf2812ada74018ae9f5c5b739607b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Sun, 10 Mar 2013 17:03:46 +0100 Subject: clocksource: sunxi: Cleanup the timer code The timer code was not exact to some aspects, since most of this code was written wihout any datasheet. Make the needed corrections to match the datasheet. Signed-off-by: Maxime Ripard --- drivers/clocksource/sunxi_timer.c | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c index 0ce85e29769..7a3ea236be5 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sunxi_timer.c @@ -25,15 +25,15 @@ #include #include -#define TIMER_CTL_REG 0x00 -#define TIMER_CTL_ENABLE (1 << 0) +#define TIMER_IRQ_EN_REG 0x00 +#define TIMER_IRQ_EN(val) (1 << val) #define TIMER_IRQ_ST_REG 0x04 -#define TIMER0_CTL_REG 0x10 -#define TIMER0_CTL_ENABLE (1 << 0) -#define TIMER0_CTL_AUTORELOAD (1 << 1) -#define TIMER0_CTL_ONESHOT (1 << 7) -#define TIMER0_INTVAL_REG 0x14 -#define TIMER0_CNTVAL_REG 0x18 +#define TIMER_CTL_REG(val) (0x10 * val + 0x10) +#define TIMER_CTL_ENABLE (1 << 0) +#define TIMER_CTL_AUTORELOAD (1 << 1) +#define TIMER_CTL_ONESHOT (1 << 7) +#define TIMER_INTVAL_REG(val) (0x10 * val + 0x14) +#define TIMER_CNTVAL_REG(val) (0x10 * val + 0x18) #define TIMER_SCAL 16 @@ -42,21 +42,21 @@ static void __iomem *timer_base; static void sunxi_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *clk) { - u32 u = readl(timer_base + TIMER0_CTL_REG); + u32 u = readl(timer_base + TIMER_CTL_REG(0)); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - u &= ~(TIMER0_CTL_ONESHOT); - writel(u | TIMER0_CTL_ENABLE, timer_base + TIMER0_CTL_REG); + u &= ~(TIMER_CTL_ONESHOT); + writel(u | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(0)); break; case CLOCK_EVT_MODE_ONESHOT: - writel(u | TIMER0_CTL_ONESHOT, timer_base + TIMER0_CTL_REG); + writel(u | TIMER_CTL_ONESHOT, timer_base + TIMER_CTL_REG(0)); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: default: - writel(u & ~(TIMER0_CTL_ENABLE), timer_base + TIMER0_CTL_REG); + writel(u & ~(TIMER_CTL_ENABLE), timer_base + TIMER_CTL_REG(0)); break; } } @@ -64,10 +64,10 @@ static void sunxi_clkevt_mode(enum clock_event_mode mode, static int sunxi_clkevt_next_event(unsigned long evt, struct clock_event_device *unused) { - u32 u = readl(timer_base + TIMER0_CTL_REG); - writel(evt, timer_base + TIMER0_CNTVAL_REG); - writel(u | TIMER0_CTL_ENABLE | TIMER0_CTL_AUTORELOAD, - timer_base + TIMER0_CTL_REG); + u32 u = readl(timer_base + TIMER_CTL_REG(0)); + writel(evt, timer_base + TIMER_CNTVAL_REG(0)); + writel(u | TIMER_CTL_ENABLE | TIMER_CTL_AUTORELOAD, + timer_base + TIMER_CTL_REG(0)); return 0; } @@ -132,26 +132,26 @@ void __init sunxi_timer_init(void) rate = clk_get_rate(clk); writel(rate / (TIMER_SCAL * HZ), - timer_base + TIMER0_INTVAL_REG); + timer_base + TIMER_INTVAL_REG(0)); /* set clock source to HOSC, 16 pre-division */ - val = readl(timer_base + TIMER0_CTL_REG); + val = readl(timer_base + TIMER_CTL_REG(0)); val &= ~(0x07 << 4); val &= ~(0x03 << 2); val |= (4 << 4) | (1 << 2); - writel(val, timer_base + TIMER0_CTL_REG); + writel(val, timer_base + TIMER_CTL_REG(0)); /* set mode to auto reload */ - val = readl(timer_base + TIMER0_CTL_REG); - writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG); + val = readl(timer_base + TIMER_CTL_REG(0)); + writel(val | TIMER_CTL_AUTORELOAD, timer_base + TIMER_CTL_REG(0)); ret = setup_irq(irq, &sunxi_timer_irq); if (ret) pr_warn("failed to setup irq %d\n", irq); /* Enable timer0 interrupt */ - val = readl(timer_base + TIMER_CTL_REG); - writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG); + val = readl(timer_base + TIMER_IRQ_EN_REG); + writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); sunxi_clockevent.cpumask = cpumask_of(0); -- cgit v1.2.3 From ea71d9a600e769ca669f4ba3e25ffdecdeede240 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Sun, 24 Mar 2013 00:01:48 +0100 Subject: clocksource: sunxi: make use of CLKSRC_OF Using CLKSRC_OF allows to remove the SoC specific sunxi_timer.h header, and instead of using a custom init function in the machine definition use the standard clocksource_of_init function. Signed-off-by: Maxime Ripard --- drivers/clocksource/sunxi_timer.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c index 7a3ea236be5..308bbe32831 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sunxi_timer.c @@ -22,8 +22,6 @@ #include #include #include -#include -#include #define TIMER_IRQ_EN_REG 0x00 #define TIMER_IRQ_EN(val) (1 << val) @@ -98,23 +96,13 @@ static struct irqaction sunxi_timer_irq = { .dev_id = &sunxi_clockevent, }; -static struct of_device_id sunxi_timer_dt_ids[] = { - { .compatible = "allwinner,sunxi-timer" }, - { } -}; - -void __init sunxi_timer_init(void) +static void __init sunxi_timer_init(struct device_node *node) { - struct device_node *node; unsigned long rate = 0; struct clk *clk; int ret, irq; u32 val; - node = of_find_matching_node(NULL, sunxi_timer_dt_ids); - if (!node) - panic("No sunxi timer node"); - timer_base = of_iomap(node, 0); if (!timer_base) panic("Can't map registers"); @@ -123,8 +111,6 @@ void __init sunxi_timer_init(void) if (irq <= 0) panic("Can't parse IRQ"); - sunxi_init_clocks(); - clk = of_clk_get(node, 0); if (IS_ERR(clk)) panic("Can't get timer clock"); @@ -158,3 +144,5 @@ void __init sunxi_timer_init(void) clockevents_config_and_register(&sunxi_clockevent, rate / TIMER_SCAL, 0x1, 0xff); } +CLOCKSOURCE_OF_DECLARE(sunxi, "allwinner,sunxi-timer", + sunxi_timer_init); -- cgit v1.2.3 From 119fd635e383c1a58990e5805acc29f48ed3e360 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Sun, 24 Mar 2013 11:49:25 +0100 Subject: clocksource: sunxi: Rename sunxi to sun4i During the introduction of the Allwinner SoC platforms, sunxi was initially meant as a generic name for all the variants of the Allwinner SoC. It was ok at the time of the support of only the A10 and A13 that looks pretty much the same, but it's beginning to be troublesome with the future addition of the Allwinner A31 (sun6i) that is quite different, and would introduce some weird logic, where sunxi would actually mean in some case sun4i and sun5i but without sun6i... Moreover, it makes the compatible strings naming scheme not consistent with other architectures, where usually for this kind of compability, we just use the oldest SoC name that has this IP, so let's do just this. Signed-off-by: Maxime Ripard --- drivers/clocksource/Kconfig | 2 +- drivers/clocksource/Makefile | 2 +- drivers/clocksource/sun4i_timer.c | 148 ++++++++++++++++++++++++++++++++++++++ drivers/clocksource/sunxi_timer.c | 148 -------------------------------------- 4 files changed, 150 insertions(+), 150 deletions(-) create mode 100644 drivers/clocksource/sun4i_timer.c delete mode 100644 drivers/clocksource/sunxi_timer.c (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index e507ab7df60..9002185a0a1 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -25,7 +25,7 @@ config DW_APB_TIMER_OF config ARMADA_370_XP_TIMER bool -config SUNXI_TIMER +config SUN4I_TIMER bool config VT8500_TIMER diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 4d8283aec5b..7d5d23a591d 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -16,7 +16,7 @@ obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o -obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o +obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c new file mode 100644 index 00000000000..d4674e78ef3 --- /dev/null +++ b/drivers/clocksource/sun4i_timer.c @@ -0,0 +1,148 @@ +/* + * Allwinner A1X SoCs timer handling. + * + * Copyright (C) 2012 Maxime Ripard + * + * Maxime Ripard + * + * Based on code from + * Allwinner Technology Co., Ltd. + * Benn Huang + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMER_IRQ_EN_REG 0x00 +#define TIMER_IRQ_EN(val) (1 << val) +#define TIMER_IRQ_ST_REG 0x04 +#define TIMER_CTL_REG(val) (0x10 * val + 0x10) +#define TIMER_CTL_ENABLE (1 << 0) +#define TIMER_CTL_AUTORELOAD (1 << 1) +#define TIMER_CTL_ONESHOT (1 << 7) +#define TIMER_INTVAL_REG(val) (0x10 * val + 0x14) +#define TIMER_CNTVAL_REG(val) (0x10 * val + 0x18) + +#define TIMER_SCAL 16 + +static void __iomem *timer_base; + +static void sun4i_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + u32 u = readl(timer_base + TIMER_CTL_REG(0)); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + u &= ~(TIMER_CTL_ONESHOT); + writel(u | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(0)); + break; + + case CLOCK_EVT_MODE_ONESHOT: + writel(u | TIMER_CTL_ONESHOT, timer_base + TIMER_CTL_REG(0)); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + writel(u & ~(TIMER_CTL_ENABLE), timer_base + TIMER_CTL_REG(0)); + break; + } +} + +static int sun4i_clkevt_next_event(unsigned long evt, + struct clock_event_device *unused) +{ + u32 u = readl(timer_base + TIMER_CTL_REG(0)); + writel(evt, timer_base + TIMER_CNTVAL_REG(0)); + writel(u | TIMER_CTL_ENABLE | TIMER_CTL_AUTORELOAD, + timer_base + TIMER_CTL_REG(0)); + + return 0; +} + +static struct clock_event_device sun4i_clockevent = { + .name = "sun4i_tick", + .rating = 300, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = sun4i_clkevt_mode, + .set_next_event = sun4i_clkevt_next_event, +}; + + +static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = (struct clock_event_device *)dev_id; + + writel(0x1, timer_base + TIMER_IRQ_ST_REG); + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction sun4i_timer_irq = { + .name = "sun4i_timer0", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = sun4i_timer_interrupt, + .dev_id = &sun4i_clockevent, +}; + +static void __init sun4i_timer_init(struct device_node *node) +{ + unsigned long rate = 0; + struct clk *clk; + int ret, irq; + u32 val; + + timer_base = of_iomap(node, 0); + if (!timer_base) + panic("Can't map registers"); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) + panic("Can't parse IRQ"); + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) + panic("Can't get timer clock"); + + rate = clk_get_rate(clk); + + writel(rate / (TIMER_SCAL * HZ), + timer_base + TIMER_INTVAL_REG(0)); + + /* set clock source to HOSC, 16 pre-division */ + val = readl(timer_base + TIMER_CTL_REG(0)); + val &= ~(0x07 << 4); + val &= ~(0x03 << 2); + val |= (4 << 4) | (1 << 2); + writel(val, timer_base + TIMER_CTL_REG(0)); + + /* set mode to auto reload */ + val = readl(timer_base + TIMER_CTL_REG(0)); + writel(val | TIMER_CTL_AUTORELOAD, timer_base + TIMER_CTL_REG(0)); + + ret = setup_irq(irq, &sun4i_timer_irq); + if (ret) + pr_warn("failed to setup irq %d\n", irq); + + /* Enable timer0 interrupt */ + val = readl(timer_base + TIMER_IRQ_EN_REG); + writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); + + sun4i_clockevent.cpumask = cpumask_of(0); + + clockevents_config_and_register(&sun4i_clockevent, rate / TIMER_SCAL, + 0x1, 0xff); +} +CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer", + sun4i_timer_init); diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c deleted file mode 100644 index 308bbe32831..00000000000 --- a/drivers/clocksource/sunxi_timer.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Allwinner A1X SoCs timer handling. - * - * Copyright (C) 2012 Maxime Ripard - * - * Maxime Ripard - * - * Based on code from - * Allwinner Technology Co., Ltd. - * Benn Huang - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define TIMER_IRQ_EN_REG 0x00 -#define TIMER_IRQ_EN(val) (1 << val) -#define TIMER_IRQ_ST_REG 0x04 -#define TIMER_CTL_REG(val) (0x10 * val + 0x10) -#define TIMER_CTL_ENABLE (1 << 0) -#define TIMER_CTL_AUTORELOAD (1 << 1) -#define TIMER_CTL_ONESHOT (1 << 7) -#define TIMER_INTVAL_REG(val) (0x10 * val + 0x14) -#define TIMER_CNTVAL_REG(val) (0x10 * val + 0x18) - -#define TIMER_SCAL 16 - -static void __iomem *timer_base; - -static void sunxi_clkevt_mode(enum clock_event_mode mode, - struct clock_event_device *clk) -{ - u32 u = readl(timer_base + TIMER_CTL_REG(0)); - - switch (mode) { - case CLOCK_EVT_MODE_PERIODIC: - u &= ~(TIMER_CTL_ONESHOT); - writel(u | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(0)); - break; - - case CLOCK_EVT_MODE_ONESHOT: - writel(u | TIMER_CTL_ONESHOT, timer_base + TIMER_CTL_REG(0)); - break; - case CLOCK_EVT_MODE_UNUSED: - case CLOCK_EVT_MODE_SHUTDOWN: - default: - writel(u & ~(TIMER_CTL_ENABLE), timer_base + TIMER_CTL_REG(0)); - break; - } -} - -static int sunxi_clkevt_next_event(unsigned long evt, - struct clock_event_device *unused) -{ - u32 u = readl(timer_base + TIMER_CTL_REG(0)); - writel(evt, timer_base + TIMER_CNTVAL_REG(0)); - writel(u | TIMER_CTL_ENABLE | TIMER_CTL_AUTORELOAD, - timer_base + TIMER_CTL_REG(0)); - - return 0; -} - -static struct clock_event_device sunxi_clockevent = { - .name = "sunxi_tick", - .rating = 300, - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .set_mode = sunxi_clkevt_mode, - .set_next_event = sunxi_clkevt_next_event, -}; - - -static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id) -{ - struct clock_event_device *evt = (struct clock_event_device *)dev_id; - - writel(0x1, timer_base + TIMER_IRQ_ST_REG); - evt->event_handler(evt); - - return IRQ_HANDLED; -} - -static struct irqaction sunxi_timer_irq = { - .name = "sunxi_timer0", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = sunxi_timer_interrupt, - .dev_id = &sunxi_clockevent, -}; - -static void __init sunxi_timer_init(struct device_node *node) -{ - unsigned long rate = 0; - struct clk *clk; - int ret, irq; - u32 val; - - timer_base = of_iomap(node, 0); - if (!timer_base) - panic("Can't map registers"); - - irq = irq_of_parse_and_map(node, 0); - if (irq <= 0) - panic("Can't parse IRQ"); - - clk = of_clk_get(node, 0); - if (IS_ERR(clk)) - panic("Can't get timer clock"); - - rate = clk_get_rate(clk); - - writel(rate / (TIMER_SCAL * HZ), - timer_base + TIMER_INTVAL_REG(0)); - - /* set clock source to HOSC, 16 pre-division */ - val = readl(timer_base + TIMER_CTL_REG(0)); - val &= ~(0x07 << 4); - val &= ~(0x03 << 2); - val |= (4 << 4) | (1 << 2); - writel(val, timer_base + TIMER_CTL_REG(0)); - - /* set mode to auto reload */ - val = readl(timer_base + TIMER_CTL_REG(0)); - writel(val | TIMER_CTL_AUTORELOAD, timer_base + TIMER_CTL_REG(0)); - - ret = setup_irq(irq, &sunxi_timer_irq); - if (ret) - pr_warn("failed to setup irq %d\n", irq); - - /* Enable timer0 interrupt */ - val = readl(timer_base + TIMER_IRQ_EN_REG); - writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); - - sunxi_clockevent.cpumask = cpumask_of(0); - - clockevents_config_and_register(&sunxi_clockevent, rate / TIMER_SCAL, - 0x1, 0xff); -} -CLOCKSOURCE_OF_DECLARE(sunxi, "allwinner,sunxi-timer", - sunxi_timer_init); -- cgit v1.2.3