diff options
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/bcm2835_timer.c | 12 | ||||
-rw-r--r-- | drivers/clocksource/clksrc-of.c | 4 | ||||
-rw-r--r-- | drivers/clocksource/mxs_timer.c | 304 | ||||
-rw-r--r-- | drivers/clocksource/sunxi_timer.c | 4 | ||||
-rw-r--r-- | drivers/clocksource/tegra20_timer.c | 75 | ||||
-rw-r--r-- | drivers/clocksource/vt8500_timer.c | 14 |
7 files changed, 339 insertions, 75 deletions
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 1c1b15db7c4..7c88173295a 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/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/mxs_timer.c b/drivers/clocksource/mxs_timer.c new file mode 100644 index 00000000000..02af4204af8 --- /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 <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/clockchips.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/stmp_device.h> + +#include <asm/mach/time.h> +#include <asm/sched_clock.h> + +/* + * 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); diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c index 4086b916715..0ce85e29769 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sunxi_timer.c @@ -23,7 +23,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/sunxi_timer.h> -#include <linux/clk-provider.h> +#include <linux/clk/sunxi.h> #define TIMER_CTL_REG 0x00 #define TIMER_CTL_ENABLE (1 << 0) @@ -123,7 +123,7 @@ void __init sunxi_timer_init(void) if (irq <= 0) panic("Can't parse IRQ"); - of_clk_init(NULL); + sunxi_init_clocks(); clk = of_clk_get(node, 0); if (IS_ERR(clk)) diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index 0bde03feb09..ae877b021b5 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -154,29 +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(void) +static void __init tegra20_init_timer(struct device_node *np) { - 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"); @@ -189,7 +172,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; @@ -200,30 +183,6 @@ static void __init tegra20_init_timer(void) 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); @@ -259,12 +218,34 @@ static void __init tegra20_init_timer(void) 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); + +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 = of_clk_get(np, 0); + 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; 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", |