diff options
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/Kconfig | 20 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 2 | ||||
-rw-r--r-- | drivers/clocksource/arm_arch_timer.c | 31 | ||||
-rw-r--r-- | drivers/clocksource/arm_global_timer.c | 321 | ||||
-rw-r--r-- | drivers/clocksource/bcm_kona_timer.c | 15 | ||||
-rw-r--r-- | drivers/clocksource/cadence_ttc_timer.c | 23 | ||||
-rw-r--r-- | drivers/clocksource/clksrc-dbx500-prcmu.c | 11 | ||||
-rw-r--r-- | drivers/clocksource/dummy_timer.c | 6 | ||||
-rw-r--r-- | drivers/clocksource/dw_apb_timer_of.c | 95 | ||||
-rw-r--r-- | drivers/clocksource/exynos_mct.c | 43 | ||||
-rw-r--r-- | drivers/clocksource/metag_generic.c | 8 | ||||
-rw-r--r-- | drivers/clocksource/nomadik-mtu.c | 60 | ||||
-rw-r--r-- | drivers/clocksource/time-armada-370-xp.c | 8 | ||||
-rw-r--r-- | drivers/clocksource/time-orion.c | 150 | ||||
-rw-r--r-- | drivers/clocksource/timer-marco.c | 10 |
15 files changed, 670 insertions, 133 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 0a04257edf65..b7b9b040a89b 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -21,10 +21,17 @@ config DW_APB_TIMER config DW_APB_TIMER_OF bool + select DW_APB_TIMER + select CLKSRC_OF config ARMADA_370_XP_TIMER bool +config ORION_TIMER + select CLKSRC_OF + select CLKSRC_MMIO + bool + config SUN4I_TIMER bool @@ -67,6 +74,19 @@ config ARM_ARCH_TIMER bool select CLKSRC_OF if OF +config ARM_GLOBAL_TIMER + bool + select CLKSRC_OF if OF + help + This options enables support for the ARM global timer unit + +config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK + bool + depends on ARM_GLOBAL_TIMER + default y + help + Use ARM global timer clock source as sched_clock + config CLKSRC_METAG_GENERIC def_bool y if METAG help diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 9ba8b4d867e3..8b00c5cebfa4 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o 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_ORION_TIMER) += time-orion.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o obj-$(CONFIG_ARCH_MARCO) += timer-marco.o obj-$(CONFIG_ARCH_MXS) += mxs_timer.o @@ -30,5 +31,6 @@ obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o +obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index a2b254189782..ffadd836e0b5 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -123,7 +123,7 @@ static int arch_timer_set_next_event_phys(unsigned long evt, return 0; } -static int __cpuinit arch_timer_setup(struct clock_event_device *clk) +static int arch_timer_setup(struct clock_event_device *clk) { clk->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP; clk->name = "arch_sys_timer"; @@ -186,27 +186,19 @@ u32 arch_timer_get_rate(void) return arch_timer_rate; } -/* - * Some external users of arch_timer_read_counter (e.g. sched_clock) may try to - * call it before it has been initialised. Rather than incur a performance - * penalty checking for initialisation, provide a default implementation that - * won't lead to time appearing to jump backwards. - */ -static u64 arch_timer_read_zero(void) +u64 arch_timer_read_counter(void) { - return 0; + return arch_counter_get_cntvct(); } -u64 (*arch_timer_read_counter)(void) = arch_timer_read_zero; - static cycle_t arch_counter_read(struct clocksource *cs) { - return arch_timer_read_counter(); + return arch_counter_get_cntvct(); } static cycle_t arch_counter_read_cc(const struct cyclecounter *cc) { - return arch_timer_read_counter(); + return arch_counter_get_cntvct(); } static struct clocksource clocksource_counter = { @@ -229,7 +221,7 @@ struct timecounter *arch_timer_get_timecounter(void) return &timecounter; } -static void __cpuinit arch_timer_stop(struct clock_event_device *clk) +static void arch_timer_stop(struct clock_event_device *clk) { pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n", clk->irq, smp_processor_id()); @@ -245,7 +237,7 @@ static void __cpuinit arch_timer_stop(struct clock_event_device *clk) clk->set_mode(CLOCK_EVT_MODE_UNUSED, clk); } -static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self, +static int arch_timer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { /* @@ -264,7 +256,7 @@ static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } -static struct notifier_block arch_timer_cpu_nb __cpuinitdata = { +static struct notifier_block arch_timer_cpu_nb = { .notifier_call = arch_timer_cpu_notify, }; @@ -287,7 +279,7 @@ static int __init arch_timer_register(void) cyclecounter.mult = clocksource_counter.mult; cyclecounter.shift = clocksource_counter.shift; timecounter_init(&timecounter, &cyclecounter, - arch_counter_get_cntpct()); + arch_counter_get_cntvct()); if (arch_timer_use_virtual) { ppi = arch_timer_ppi[VIRT_PPI]; @@ -376,11 +368,6 @@ static void __init arch_timer_init(struct device_node *np) } } - if (arch_timer_use_virtual) - arch_timer_read_counter = arch_counter_get_cntvct; - else - arch_timer_read_counter = arch_counter_get_cntpct; - arch_timer_register(); arch_timer_arch_init(); } diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c new file mode 100644 index 000000000000..b66c1f36066c --- /dev/null +++ b/drivers/clocksource/arm_global_timer.c @@ -0,0 +1,321 @@ +/* + * drivers/clocksource/arm_global_timer.c + * + * Copyright (C) 2013 STMicroelectronics (R&D) Limited. + * Author: Stuart Menefy <stuart.menefy@st.com> + * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <linux/cpu.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/sched_clock.h> + +#include <asm/cputype.h> + +#define GT_COUNTER0 0x00 +#define GT_COUNTER1 0x04 + +#define GT_CONTROL 0x08 +#define GT_CONTROL_TIMER_ENABLE BIT(0) /* this bit is NOT banked */ +#define GT_CONTROL_COMP_ENABLE BIT(1) /* banked */ +#define GT_CONTROL_IRQ_ENABLE BIT(2) /* banked */ +#define GT_CONTROL_AUTO_INC BIT(3) /* banked */ + +#define GT_INT_STATUS 0x0c +#define GT_INT_STATUS_EVENT_FLAG BIT(0) + +#define GT_COMP0 0x10 +#define GT_COMP1 0x14 +#define GT_AUTO_INC 0x18 + +/* + * We are expecting to be clocked by the ARM peripheral clock. + * + * Note: it is assumed we are using a prescaler value of zero, so this is + * the units for all operations. + */ +static void __iomem *gt_base; +static unsigned long gt_clk_rate; +static int gt_ppi; +static struct clock_event_device __percpu *gt_evt; + +/* + * To get the value from the Global Timer Counter register proceed as follows: + * 1. Read the upper 32-bit timer counter register + * 2. Read the lower 32-bit timer counter register + * 3. Read the upper 32-bit timer counter register again. If the value is + * different to the 32-bit upper value read previously, go back to step 2. + * Otherwise the 64-bit timer counter value is correct. + */ +static u64 gt_counter_read(void) +{ + u64 counter; + u32 lower; + u32 upper, old_upper; + + upper = readl_relaxed(gt_base + GT_COUNTER1); + do { + old_upper = upper; + lower = readl_relaxed(gt_base + GT_COUNTER0); + upper = readl_relaxed(gt_base + GT_COUNTER1); + } while (upper != old_upper); + + counter = upper; + counter <<= 32; + counter |= lower; + return counter; +} + +/** + * To ensure that updates to comparator value register do not set the + * Interrupt Status Register proceed as follows: + * 1. Clear the Comp Enable bit in the Timer Control Register. + * 2. Write the lower 32-bit Comparator Value Register. + * 3. Write the upper 32-bit Comparator Value Register. + * 4. Set the Comp Enable bit and, if necessary, the IRQ enable bit. + */ +static void gt_compare_set(unsigned long delta, int periodic) +{ + u64 counter = gt_counter_read(); + unsigned long ctrl; + + counter += delta; + ctrl = GT_CONTROL_TIMER_ENABLE; + writel(ctrl, gt_base + GT_CONTROL); + writel(lower_32_bits(counter), gt_base + GT_COMP0); + writel(upper_32_bits(counter), gt_base + GT_COMP1); + + if (periodic) { + writel(delta, gt_base + GT_AUTO_INC); + ctrl |= GT_CONTROL_AUTO_INC; + } + + ctrl |= GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE; + writel(ctrl, gt_base + GT_CONTROL); +} + +static void gt_clockevent_set_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + unsigned long ctrl; + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + gt_compare_set(DIV_ROUND_CLOSEST(gt_clk_rate, HZ), 1); + break; + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + ctrl = readl(gt_base + GT_CONTROL); + ctrl &= ~(GT_CONTROL_COMP_ENABLE | + GT_CONTROL_IRQ_ENABLE | GT_CONTROL_AUTO_INC); + writel(ctrl, gt_base + GT_CONTROL); + break; + default: + break; + } +} + +static int gt_clockevent_set_next_event(unsigned long evt, + struct clock_event_device *unused) +{ + gt_compare_set(evt, 0); + return 0; +} + +static irqreturn_t gt_clockevent_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + if (!(readl_relaxed(gt_base + GT_INT_STATUS) & + GT_INT_STATUS_EVENT_FLAG)) + return IRQ_NONE; + + /** + * ERRATA 740657( Global Timer can send 2 interrupts for + * the same event in single-shot mode) + * Workaround: + * Either disable single-shot mode. + * Or + * Modify the Interrupt Handler to avoid the + * offending sequence. This is achieved by clearing + * the Global Timer flag _after_ having incremented + * the Comparator register value to a higher value. + */ + if (evt->mode == CLOCK_EVT_MODE_ONESHOT) + gt_compare_set(ULONG_MAX, 0); + + writel_relaxed(GT_INT_STATUS_EVENT_FLAG, gt_base + GT_INT_STATUS); + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static int gt_clockevents_init(struct clock_event_device *clk) +{ + int cpu = smp_processor_id(); + + clk->name = "arm_global_timer"; + clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + clk->set_mode = gt_clockevent_set_mode; + clk->set_next_event = gt_clockevent_set_next_event; + clk->cpumask = cpumask_of(cpu); + clk->rating = 300; + clk->irq = gt_ppi; + clockevents_config_and_register(clk, gt_clk_rate, + 1, 0xffffffff); + enable_percpu_irq(clk->irq, IRQ_TYPE_NONE); + return 0; +} + +static void gt_clockevents_stop(struct clock_event_device *clk) +{ + gt_clockevent_set_mode(CLOCK_EVT_MODE_UNUSED, clk); + disable_percpu_irq(clk->irq); +} + +static cycle_t gt_clocksource_read(struct clocksource *cs) +{ + return gt_counter_read(); +} + +static struct clocksource gt_clocksource = { + .name = "arm_global_timer", + .rating = 300, + .read = gt_clocksource_read, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK +static u32 notrace gt_sched_clock_read(void) +{ + return gt_counter_read(); +} +#endif + +static void __init gt_clocksource_init(void) +{ + writel(0, gt_base + GT_CONTROL); + writel(0, gt_base + GT_COUNTER0); + writel(0, gt_base + GT_COUNTER1); + /* enables timer on all the cores */ + writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL); + +#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK + setup_sched_clock(gt_sched_clock_read, 32, gt_clk_rate); +#endif + clocksource_register_hz(>_clocksource, gt_clk_rate); +} + +static int gt_cpu_notify(struct notifier_block *self, unsigned long action, + void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_STARTING: + gt_clockevents_init(this_cpu_ptr(gt_evt)); + break; + case CPU_DYING: + gt_clockevents_stop(this_cpu_ptr(gt_evt)); + break; + } + + return NOTIFY_OK; +} +static struct notifier_block gt_cpu_nb = { + .notifier_call = gt_cpu_notify, +}; + +static void __init global_timer_of_register(struct device_node *np) +{ + struct clk *gt_clk; + int err = 0; + + /* + * In r2p0 the comparators for each processor with the global timer + * fire when the timer value is greater than or equal to. In previous + * revisions the comparators fired when the timer value was equal to. + */ + if ((read_cpuid_id() & 0xf0000f) < 0x200000) { + pr_warn("global-timer: non support for this cpu version.\n"); + return; + } + + gt_ppi = irq_of_parse_and_map(np, 0); + if (!gt_ppi) { + pr_warn("global-timer: unable to parse irq\n"); + return; + } + + gt_base = of_iomap(np, 0); + if (!gt_base) { + pr_warn("global-timer: invalid base address\n"); + return; + } + + gt_clk = of_clk_get(np, 0); + if (!IS_ERR(gt_clk)) { + err = clk_prepare_enable(gt_clk); + if (err) + goto out_unmap; + } else { + pr_warn("global-timer: clk not found\n"); + err = -EINVAL; + goto out_unmap; + } + + gt_clk_rate = clk_get_rate(gt_clk); + gt_evt = alloc_percpu(struct clock_event_device); + if (!gt_evt) { + pr_warn("global-timer: can't allocate memory\n"); + err = -ENOMEM; + goto out_clk; + } + + err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt, + "gt", gt_evt); + if (err) { + pr_warn("global-timer: can't register interrupt %d (%d)\n", + gt_ppi, err); + goto out_free; + } + + err = register_cpu_notifier(>_cpu_nb); + if (err) { + pr_warn("global-timer: unable to register cpu notifier.\n"); + goto out_irq; + } + + /* Immediately configure the timer on the boot CPU */ + gt_clocksource_init(); + gt_clockevents_init(this_cpu_ptr(gt_evt)); + + return; + +out_irq: + free_percpu_irq(gt_ppi, gt_evt); +out_free: + free_percpu(gt_evt); +out_clk: + clk_disable_unprepare(gt_clk); +out_unmap: + iounmap(gt_base); + WARN(err, "ARM Global timer register failed (%d)\n", err); +} + +/* Only tested on r2p2 and r3p0 */ +CLOCKSOURCE_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer", + global_timer_of_register); diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c index 350f49356458..ba3d85904c9a 100644 --- a/drivers/clocksource/bcm_kona_timer.c +++ b/drivers/clocksource/bcm_kona_timer.c @@ -103,16 +103,10 @@ static const struct of_device_id bcm_timer_ids[] __initconst = { {}, }; -static void __init kona_timers_init(void) +static void __init kona_timers_init(struct device_node *node) { - struct device_node *node; u32 freq; - node = of_find_matching_node(NULL, bcm_timer_ids); - - if (!node) - panic("No timer"); - if (!of_property_read_u32(node, "clock-frequency", &freq)) arch_timer_rate = freq; else @@ -199,13 +193,12 @@ static struct irqaction kona_timer_irq = { .handler = kona_timer_interrupt, }; -static void __init kona_timer_init(void) +static void __init kona_timer_init(struct device_node *node) { - kona_timers_init(); + kona_timers_init(node); kona_timer_clockevents_init(); setup_irq(timers.tmr_irq, &kona_timer_irq); kona_timer_set_next_event((arch_timer_rate / HZ), NULL); } -CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer", - kona_timer_init); +CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init); diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c index 685bc60e210a..4cbe28c74631 100644 --- a/drivers/clocksource/cadence_ttc_timer.c +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -51,6 +51,8 @@ #define TTC_CNT_CNTRL_DISABLE_MASK 0x1 +#define TTC_CLK_CNTRL_CSRC_MASK (1 << 5) /* clock source */ + /* * Setup the timers to use pre-scaling, using a fixed value for now that will * work across most input frequency, but it may need to be more dynamic @@ -396,8 +398,9 @@ static void __init ttc_timer_init(struct device_node *timer) { unsigned int irq; void __iomem *timer_baseaddr; - struct clk *clk; + struct clk *clk_cs, *clk_ce; static int initialized; + int clksel; if (initialized) return; @@ -421,14 +424,24 @@ static void __init ttc_timer_init(struct device_node *timer) BUG(); } - clk = of_clk_get_by_name(timer, "cpu_1x"); - if (IS_ERR(clk)) { + clksel = __raw_readl(timer_baseaddr + TTC_CLK_CNTRL_OFFSET); + clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK); + clk_cs = of_clk_get(timer, clksel); + if (IS_ERR(clk_cs)) { + pr_err("ERROR: timer input clock not found\n"); + BUG(); + } + + clksel = __raw_readl(timer_baseaddr + 4 + TTC_CLK_CNTRL_OFFSET); + clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK); + clk_ce = of_clk_get(timer, clksel); + if (IS_ERR(clk_ce)) { pr_err("ERROR: timer input clock not found\n"); BUG(); } - ttc_setup_clocksource(clk, timer_baseaddr); - ttc_setup_clockevent(clk, timer_baseaddr + 4, irq); + ttc_setup_clocksource(clk_cs, timer_baseaddr); + ttc_setup_clockevent(clk_ce, timer_baseaddr + 4, irq); pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq); } diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c index 0a7fb2440e29..a9fd4ad25674 100644 --- a/drivers/clocksource/clksrc-dbx500-prcmu.c +++ b/drivers/clocksource/clksrc-dbx500-prcmu.c @@ -10,7 +10,7 @@ * DBx500-PRCMU Timer * The PRCMU has 5 timers which are available in a always-on * power domain. We use the Timer 4 for our always-on clock - * source on DB8500 and Timer 3 on DB5500. + * source on DB8500. */ #include <linux/clockchips.h> #include <linux/clksrc-dbx500-prcmu.h> @@ -29,15 +29,14 @@ static void __iomem *clksrc_dbx500_timer_base; -static cycle_t clksrc_dbx500_prcmu_read(struct clocksource *cs) +static cycle_t notrace clksrc_dbx500_prcmu_read(struct clocksource *cs) { + void __iomem *base = clksrc_dbx500_timer_base; u32 count, count2; do { - count = readl(clksrc_dbx500_timer_base + - PRCMU_TIMER_DOWNCOUNT); - count2 = readl(clksrc_dbx500_timer_base + - PRCMU_TIMER_DOWNCOUNT); + count = readl_relaxed(base + PRCMU_TIMER_DOWNCOUNT); + count2 = readl_relaxed(base + PRCMU_TIMER_DOWNCOUNT); } while (count2 != count); /* Negate because the timer is a decrementing counter */ diff --git a/drivers/clocksource/dummy_timer.c b/drivers/clocksource/dummy_timer.c index 1f55f9620338..b3eb582d6a6f 100644 --- a/drivers/clocksource/dummy_timer.c +++ b/drivers/clocksource/dummy_timer.c @@ -25,7 +25,7 @@ static void dummy_timer_set_mode(enum clock_event_mode mode, */ } -static void __cpuinit dummy_timer_setup(void) +static void dummy_timer_setup(void) { int cpu = smp_processor_id(); struct clock_event_device *evt = __this_cpu_ptr(&dummy_timer_evt); @@ -41,7 +41,7 @@ static void __cpuinit dummy_timer_setup(void) clockevents_register_device(evt); } -static int __cpuinit dummy_timer_cpu_notify(struct notifier_block *self, +static int dummy_timer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { if ((action & ~CPU_TASKS_FROZEN) == CPU_STARTING) @@ -50,7 +50,7 @@ static int __cpuinit dummy_timer_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } -static struct notifier_block dummy_timer_cpu_nb __cpuinitdata = { +static struct notifier_block dummy_timer_cpu_nb = { .notifier_call = dummy_timer_cpu_notify, }; diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index d9a1e8d51751..4cbae4f762b1 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -20,19 +20,43 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> +#include <linux/clk.h> #include <linux/sched_clock.h> static void timer_get_base_and_rate(struct device_node *np, void __iomem **base, u32 *rate) { + struct clk *timer_clk; + struct clk *pclk; + *base = of_iomap(np, 0); if (!*base) panic("Unable to map regs for %s", np->name); + /* + * Not all implementations use a periphal clock, so don't panic + * if it's not present + */ + pclk = of_clk_get_by_name(np, "pclk"); + if (!IS_ERR(pclk)) + if (clk_prepare_enable(pclk)) + pr_warn("pclk for %s is present, but could not be activated\n", + np->name); + + timer_clk = of_clk_get_by_name(np, "timer"); + if (IS_ERR(timer_clk)) + goto try_clock_freq; + + if (!clk_prepare_enable(timer_clk)) { + *rate = clk_get_rate(timer_clk); + return; + } + +try_clock_freq: if (of_property_read_u32(np, "clock-freq", rate) && of_property_read_u32(np, "clock-frequency", rate)) - panic("No clock-frequency property for %s", np->name); + panic("No clock nor clock-frequency property for %s", np->name); } static void add_clockevent(struct device_node *event_timer) @@ -55,6 +79,9 @@ static void add_clockevent(struct device_node *event_timer) dw_apb_clockevent_register(ced); } +static void __iomem *sched_io_base; +static u32 sched_rate; + static void add_clocksource(struct device_node *source_timer) { void __iomem *iobase; @@ -69,9 +96,15 @@ static void add_clocksource(struct device_node *source_timer) dw_apb_clocksource_start(cs); dw_apb_clocksource_register(cs); -} -static void __iomem *sched_io_base; + /* + * Fallback to use the clocksource as sched_clock if no separate + * timer is found. sched_io_base then points to the current_value + * register of the clocksource timer. + */ + sched_io_base = iobase + 0x04; + sched_rate = rate; +} static u32 read_sched_clock(void) { @@ -87,39 +120,37 @@ static const struct of_device_id sptimer_ids[] __initconst = { static void init_sched_clock(void) { struct device_node *sched_timer; - u32 rate; sched_timer = of_find_matching_node(NULL, sptimer_ids); - if (!sched_timer) - panic("No RTC for sched clock to use"); + if (sched_timer) { + timer_get_base_and_rate(sched_timer, &sched_io_base, + &sched_rate); + of_node_put(sched_timer); + } - timer_get_base_and_rate(sched_timer, &sched_io_base, &rate); - of_node_put(sched_timer); - - setup_sched_clock(read_sched_clock, 32, rate); + setup_sched_clock(read_sched_clock, 32, sched_rate); } -static const struct of_device_id osctimer_ids[] __initconst = { - { .compatible = "picochip,pc3x2-timer" }, - { .compatible = "snps,dw-apb-timer-osc" }, - {}, -}; - -void __init dw_apb_timer_init(void) +static int num_called; +static void __init dw_apb_timer_init(struct device_node *timer) { - struct device_node *event_timer, *source_timer; - - event_timer = of_find_matching_node(NULL, osctimer_ids); - if (!event_timer) - panic("No timer for clockevent"); - add_clockevent(event_timer); - - source_timer = of_find_matching_node(event_timer, osctimer_ids); - if (!source_timer) - panic("No timer for clocksource"); - add_clocksource(source_timer); - - of_node_put(source_timer); - - init_sched_clock(); + switch (num_called) { + case 0: + pr_debug("%s: found clockevent timer\n", __func__); + add_clockevent(timer); + of_node_put(timer); + break; + case 1: + pr_debug("%s: found clocksource timer\n", __func__); + add_clocksource(timer); + of_node_put(timer); + init_sched_clock(); + break; + default: + break; + } + + num_called++; } +CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); +CLOCKSOURCE_OF_DECLARE(apb_timer, "snps,dw-apb-timer-osc", dw_apb_timer_init); diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 1c3f5a652044..5b34768f4d7c 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -398,19 +398,7 @@ static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction mct_tick0_event_irq = { - .name = "mct_tick0_irq", - .flags = IRQF_TIMER | IRQF_NOBALANCING, - .handler = exynos4_mct_tick_isr, -}; - -static struct irqaction mct_tick1_event_irq = { - .name = "mct_tick1_irq", - .flags = IRQF_TIMER | IRQF_NOBALANCING, - .handler = exynos4_mct_tick_isr, -}; - -static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt) +static int exynos4_local_timer_setup(struct clock_event_device *evt) { struct mct_clock_event_device *mevt; unsigned int cpu = smp_processor_id(); @@ -432,16 +420,15 @@ static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt) exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET); if (mct_int_type == MCT_INT_SPI) { - if (cpu == 0) { - mct_tick0_event_irq.dev_id = mevt; - evt->irq = mct_irqs[MCT_L0_IRQ]; - setup_irq(evt->irq, &mct_tick0_event_irq); - } else { - mct_tick1_event_irq.dev_id = mevt; - evt->irq = mct_irqs[MCT_L1_IRQ]; - setup_irq(evt->irq, &mct_tick1_event_irq); - irq_set_affinity(evt->irq, cpumask_of(1)); + evt->irq = mct_irqs[MCT_L0_IRQ + cpu]; + if (request_irq(evt->irq, exynos4_mct_tick_isr, + IRQF_TIMER | IRQF_NOBALANCING, + evt->name, mevt)) { + pr_err("exynos-mct: cannot register IRQ %d\n", + evt->irq); + return -EIO; } + irq_set_affinity(evt->irq, cpumask_of(cpu)); } else { enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0); } @@ -449,20 +436,16 @@ static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt) return 0; } -static void __cpuinit exynos4_local_timer_stop(struct clock_event_device *evt) +static void exynos4_local_timer_stop(struct clock_event_device *evt) { - unsigned int cpu = smp_processor_id(); evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); if (mct_int_type == MCT_INT_SPI) - if (cpu == 0) - remove_irq(evt->irq, &mct_tick0_event_irq); - else - remove_irq(evt->irq, &mct_tick1_event_irq); + free_irq(evt->irq, this_cpu_ptr(&percpu_mct_tick)); else disable_percpu_irq(mct_irqs[MCT_L0_IRQ]); } -static int __cpuinit exynos4_mct_cpu_notify(struct notifier_block *self, +static int exynos4_mct_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { struct mct_clock_event_device *mevt; @@ -485,7 +468,7 @@ static int __cpuinit exynos4_mct_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } -static struct notifier_block exynos4_mct_cpu_nb __cpuinitdata = { +static struct notifier_block exynos4_mct_cpu_nb = { .notifier_call = exynos4_mct_cpu_notify, }; diff --git a/drivers/clocksource/metag_generic.c b/drivers/clocksource/metag_generic.c index ade7513a11d1..9e4db41abe3c 100644 --- a/drivers/clocksource/metag_generic.c +++ b/drivers/clocksource/metag_generic.c @@ -109,7 +109,7 @@ unsigned long long sched_clock(void) return ticks << HARDWARE_TO_NS_SHIFT; } -static void __cpuinit arch_timer_setup(unsigned int cpu) +static void arch_timer_setup(unsigned int cpu) { unsigned int txdivtime; struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); @@ -154,7 +154,7 @@ static void __cpuinit arch_timer_setup(unsigned int cpu) } } -static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self, +static int arch_timer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { int cpu = (long)hcpu; @@ -169,7 +169,7 @@ static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } -static struct notifier_block __cpuinitdata arch_timer_cpu_nb = { +static struct notifier_block arch_timer_cpu_nb = { .notifier_call = arch_timer_cpu_notify, }; @@ -184,6 +184,8 @@ int __init metag_generic_timer_init(void) #ifdef CONFIG_METAG_META21 hwtimer_freq = get_coreclock() / (metag_in32(EXPAND_TIMER_DIV) + 1); #endif + pr_info("Timer frequency: %u Hz\n", hwtimer_freq); + clocksource_register_hz(&clocksource_metag, hwtimer_freq); setup_irq(tbisig_map(TBID_SIGNUM_TRT), &metag_timer_irq); diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c index 8864c17841c8..7d2c2c56f73c 100644 --- a/drivers/clocksource/nomadik-mtu.c +++ b/drivers/clocksource/nomadik-mtu.c @@ -13,6 +13,9 @@ #include <linux/io.h> #include <linux/clockchips.h> #include <linux/clocksource.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> #include <linux/clk.h> #include <linux/jiffies.h> #include <linux/delay.h> @@ -188,22 +191,15 @@ static struct irqaction nmdk_timer_irq = { .dev_id = &nmdk_clkevt, }; -void __init nmdk_timer_init(void __iomem *base, int irq) +static void __init __nmdk_timer_init(void __iomem *base, int irq, + struct clk *pclk, struct clk *clk) { unsigned long rate; - struct clk *clk0, *pclk0; mtu_base = base; - pclk0 = clk_get_sys("mtu0", "apb_pclk"); - BUG_ON(IS_ERR(pclk0)); - BUG_ON(clk_prepare(pclk0) < 0); - BUG_ON(clk_enable(pclk0) < 0); - - clk0 = clk_get_sys("mtu0", NULL); - BUG_ON(IS_ERR(clk0)); - BUG_ON(clk_prepare(clk0) < 0); - BUG_ON(clk_enable(clk0) < 0); + BUG_ON(clk_prepare_enable(pclk)); + BUG_ON(clk_prepare_enable(clk)); /* * Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz @@ -213,7 +209,7 @@ void __init nmdk_timer_init(void __iomem *base, int irq) * to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer * with 16 gives too low timer resolution. */ - rate = clk_get_rate(clk0); + rate = clk_get_rate(clk); if (rate > 32000000) { rate /= 16; clk_prescale = MTU_CRn_PRESCALE_16; @@ -247,3 +243,43 @@ void __init nmdk_timer_init(void __iomem *base, int irq) mtu_delay_timer.freq = rate; register_current_timer_delay(&mtu_delay_timer); } + +void __init nmdk_timer_init(void __iomem *base, int irq) +{ + struct clk *clk0, *pclk0; + + pclk0 = clk_get_sys("mtu0", "apb_pclk"); + BUG_ON(IS_ERR(pclk0)); + clk0 = clk_get_sys("mtu0", NULL); + BUG_ON(IS_ERR(clk0)); + + __nmdk_timer_init(base, irq, pclk0, clk0); +} + +static void __init nmdk_timer_of_init(struct device_node *node) +{ + struct clk *pclk; + struct clk *clk; + void __iomem *base; + int irq; + + base = of_iomap(node, 0); + if (!base) + panic("Can't remap registers"); + + pclk = of_clk_get_by_name(node, "apb_pclk"); + if (IS_ERR(pclk)) + panic("could not get apb_pclk"); + + clk = of_clk_get_by_name(node, "timclk"); + if (IS_ERR(clk)) + panic("could not get timclk"); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) + panic("Can't parse IRQ"); + + __nmdk_timer_init(base, irq, pclk, clk); +} +CLOCKSOURCE_OF_DECLARE(nomadik_mtu, "st,nomadik-mtu", + nmdk_timer_of_init); diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c index f86542002ee1..847cab6f6e31 100644 --- a/drivers/clocksource/time-armada-370-xp.c +++ b/drivers/clocksource/time-armada-370-xp.c @@ -161,7 +161,7 @@ static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id) /* * Setup the local clock events for a CPU. */ -static int __cpuinit armada_370_xp_timer_setup(struct clock_event_device *evt) +static int armada_370_xp_timer_setup(struct clock_event_device *evt) { u32 u; int cpu = smp_processor_id(); @@ -188,13 +188,13 @@ static int __cpuinit armada_370_xp_timer_setup(struct clock_event_device *evt) return 0; } -static void __cpuinit armada_370_xp_timer_stop(struct clock_event_device *evt) +static void armada_370_xp_timer_stop(struct clock_event_device *evt) { evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); disable_percpu_irq(evt->irq); } -static int __cpuinit armada_370_xp_timer_cpu_notify(struct notifier_block *self, +static int armada_370_xp_timer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { /* @@ -213,7 +213,7 @@ static int __cpuinit armada_370_xp_timer_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } -static struct notifier_block armada_370_xp_timer_cpu_nb __cpuinitdata = { +static struct notifier_block armada_370_xp_timer_cpu_nb = { .notifier_call = armada_370_xp_timer_cpu_notify, }; diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c new file mode 100644 index 000000000000..ecbeb6810215 --- /dev/null +++ b/drivers/clocksource/time-orion.c @@ -0,0 +1,150 @@ +/* + * Marvell Orion SoC timer handling. + * + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * + * 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. + * + * Timer 0 is used as free-running clocksource, while timer 1 is + * used as clock_event_device. + */ + +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/spinlock.h> +#include <asm/sched_clock.h> + +#define TIMER_CTRL 0x00 +#define TIMER0_EN BIT(0) +#define TIMER0_RELOAD_EN BIT(1) +#define TIMER1_EN BIT(2) +#define TIMER1_RELOAD_EN BIT(3) +#define TIMER0_RELOAD 0x10 +#define TIMER0_VAL 0x14 +#define TIMER1_RELOAD 0x18 +#define TIMER1_VAL 0x1c + +#define ORION_ONESHOT_MIN 1 +#define ORION_ONESHOT_MAX 0xfffffffe + +static void __iomem *timer_base; +static DEFINE_SPINLOCK(timer_ctrl_lock); + +/* + * Thread-safe access to TIMER_CTRL register + * (shared with watchdog timer) + */ +void orion_timer_ctrl_clrset(u32 clr, u32 set) +{ + spin_lock(&timer_ctrl_lock); + writel((readl(timer_base + TIMER_CTRL) & ~clr) | set, + timer_base + TIMER_CTRL); + spin_unlock(&timer_ctrl_lock); +} +EXPORT_SYMBOL(orion_timer_ctrl_clrset); + +/* + * Free-running clocksource handling. + */ +static u32 notrace orion_read_sched_clock(void) +{ + return ~readl(timer_base + TIMER0_VAL); +} + +/* + * Clockevent handling. + */ +static u32 ticks_per_jiffy; + +static int orion_clkevt_next_event(unsigned long delta, + struct clock_event_device *dev) +{ + /* setup and enable one-shot timer */ + writel(delta, timer_base + TIMER1_VAL); + orion_timer_ctrl_clrset(TIMER1_RELOAD_EN, TIMER1_EN); + + return 0; +} + +static void orion_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *dev) +{ + if (mode == CLOCK_EVT_MODE_PERIODIC) { + /* setup and enable periodic timer at 1/HZ intervals */ + writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD); + writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL); + orion_timer_ctrl_clrset(0, TIMER1_RELOAD_EN | TIMER1_EN); + } else { + /* disable timer */ + orion_timer_ctrl_clrset(TIMER1_RELOAD_EN | TIMER1_EN, 0); + } +} + +static struct clock_event_device orion_clkevt = { + .name = "orion_event", + .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, + .shift = 32, + .rating = 300, + .set_next_event = orion_clkevt_next_event, + .set_mode = orion_clkevt_mode, +}; + +static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id) +{ + orion_clkevt.event_handler(&orion_clkevt); + return IRQ_HANDLED; +} + +static struct irqaction orion_clkevt_irq = { + .name = "orion_event", + .flags = IRQF_TIMER, + .handler = orion_clkevt_irq_handler, +}; + +static void __init orion_timer_init(struct device_node *np) +{ + struct clk *clk; + int irq; + + /* timer registers are shared with watchdog timer */ + timer_base = of_iomap(np, 0); + if (!timer_base) + panic("%s: unable to map resource\n", np->name); + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) + panic("%s: unable to get clk\n", np->name); + clk_prepare_enable(clk); + + /* we are only interested in timer1 irq */ + irq = irq_of_parse_and_map(np, 1); + if (irq <= 0) + panic("%s: unable to parse timer1 irq\n", np->name); + + /* setup timer0 as free-running clocksource */ + writel(~0, timer_base + TIMER0_VAL); + writel(~0, timer_base + TIMER0_RELOAD); + orion_timer_ctrl_clrset(0, TIMER0_RELOAD_EN | TIMER0_EN); + clocksource_mmio_init(timer_base + TIMER0_VAL, "orion_clocksource", + clk_get_rate(clk), 300, 32, + clocksource_mmio_readl_down); + setup_sched_clock(orion_read_sched_clock, 32, clk_get_rate(clk)); + + /* setup timer1 as clockevent timer */ + if (setup_irq(irq, &orion_clkevt_irq)) + panic("%s: unable to setup irq\n", np->name); + + ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ; + orion_clkevt.cpumask = cpumask_of(0); + orion_clkevt.irq = irq; + clockevents_config_and_register(&orion_clkevt, clk_get_rate(clk), + ORION_ONESHOT_MIN, ORION_ONESHOT_MAX); +} +CLOCKSOURCE_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init); diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c index 01b9683557b2..09a17d9a6594 100644 --- a/drivers/clocksource/timer-marco.c +++ b/drivers/clocksource/timer-marco.c @@ -175,7 +175,7 @@ static struct irqaction sirfsoc_timer1_irq = { .handler = sirfsoc_timer_interrupt, }; -static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce) +static int sirfsoc_local_timer_setup(struct clock_event_device *ce) { int cpu = smp_processor_id(); struct irqaction *action; @@ -204,7 +204,7 @@ static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce) return 0; } -static void __cpuinit sirfsoc_local_timer_stop(struct clock_event_device *ce) +static void sirfsoc_local_timer_stop(struct clock_event_device *ce) { int cpu = smp_processor_id(); @@ -216,8 +216,8 @@ static void __cpuinit sirfsoc_local_timer_stop(struct clock_event_device *ce) remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq); } -static int __cpuinit sirfsoc_cpu_notify(struct notifier_block *self, - unsigned long action, void *hcpu) +static int sirfsoc_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) { /* * Grab cpu pointer in each case to avoid spurious @@ -235,7 +235,7 @@ static int __cpuinit sirfsoc_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } -static struct notifier_block sirfsoc_cpu_nb __cpuinitdata = { +static struct notifier_block sirfsoc_cpu_nb = { .notifier_call = sirfsoc_cpu_notify, }; |