diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-09 06:35:05 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-09 06:35:05 -0400 |
commit | 47137c6ba1bcde30215795f9594cea770946456b (patch) | |
tree | 3a7a04e6ecffcb188b6708ccdc3f02b70f07d7c4 /drivers/clocksource | |
parent | afa3536be88b435a057cb727b48fd3d760a497d2 (diff) | |
parent | 867f667fb9c6734e06cc24e96fc7f06a7e772084 (diff) | |
download | linux-47137c6ba1bcde30215795f9594cea770946456b.tar.gz linux-47137c6ba1bcde30215795f9594cea770946456b.tar.bz2 linux-47137c6ba1bcde30215795f9594cea770946456b.zip |
Merge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer updates from Thomas Gleixner:
"Nothing really exciting this time:
- a few fixlets in the NOHZ code
- a new ARM SoC timer abomination. One should expect that we have
enough of them already, but they insist on inventing new ones.
- the usual bunch of ARM SoC timer updates. That feels like herding
cats"
* 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
clocksource: arm_arch_timer: Consolidate arch_timer_evtstrm_enable
clocksource: arm_arch_timer: Enable counter access for 32-bit ARM
clocksource: arm_arch_timer: Change clocksource name if CP15 unavailable
clocksource: sirf: Disable counter before re-setting it
clocksource: cadence_ttc: Add support for 32bit mode
clocksource: tcb_clksrc: Sanitize IRQ request
clocksource: arm_arch_timer: Discard unavailable timers correctly
clocksource: vf_pit_timer: Support shutdown mode
ARM: meson6: clocksource: Add Meson6 timer support
ARM: meson: documentation: Add timer documentation
clocksource: sh_tmu: Document r8a7779 binding
clocksource: sh_mtu2: Document r7s72100 binding
clocksource: sh_cmt: Document SoC specific bindings
timerfd: Remove an always true check
nohz: Avoid tick's double reprogramming in highres mode
nohz: Fix spurious periodic tick behaviour in low-res dynticks mode
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/Kconfig | 3 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/arm_arch_timer.c | 64 | ||||
-rw-r--r-- | drivers/clocksource/cadence_ttc_timer.c | 15 | ||||
-rw-r--r-- | drivers/clocksource/meson6_timer.c | 167 | ||||
-rw-r--r-- | drivers/clocksource/timer-marco.c | 5 | ||||
-rw-r--r-- | drivers/clocksource/vf_pit_timer.c | 4 |
7 files changed, 247 insertions, 12 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 82a2ebe41e27..90420600e1eb 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -30,6 +30,9 @@ config ARMADA_370_XP_TIMER bool select CLKSRC_OF +config MESON6_TIMER + bool + config ORION_TIMER select CLKSRC_OF select CLKSRC_MMIO diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index e566f6c7ded4..756f6f10efa0 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o obj-$(CONFIG_ARCH_U300) += timer-u300.o obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o +obj-$(CONFIG_MESON6_TIMER) += meson6_timer.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 5163ec13429d..2133f9d59d06 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -299,6 +299,21 @@ static void __arch_timer_setup(unsigned type, clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff); } +static void arch_timer_evtstrm_enable(int divider) +{ + u32 cntkctl = arch_timer_get_cntkctl(); + + cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK; + /* Set the divider and enable virtual event stream */ + cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT) + | ARCH_TIMER_VIRT_EVT_EN; + arch_timer_set_cntkctl(cntkctl); + elf_hwcap |= HWCAP_EVTSTRM; +#ifdef CONFIG_COMPAT + compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM; +#endif +} + static void arch_timer_configure_evtstream(void) { int evt_stream_div, pos; @@ -312,6 +327,23 @@ static void arch_timer_configure_evtstream(void) arch_timer_evtstrm_enable(min(pos, 15)); } +static void arch_counter_set_user_access(void) +{ + u32 cntkctl = arch_timer_get_cntkctl(); + + /* Disable user access to the timers and the physical counter */ + /* Also disable virtual event stream */ + cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN + | ARCH_TIMER_USR_VT_ACCESS_EN + | ARCH_TIMER_VIRT_EVT_EN + | ARCH_TIMER_USR_PCT_ACCESS_EN); + + /* Enable user access to the virtual counter */ + cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; + + arch_timer_set_cntkctl(cntkctl); +} + static int arch_timer_setup(struct clock_event_device *clk) { __arch_timer_setup(ARCH_CP15_TIMER, clk); @@ -429,11 +461,19 @@ static void __init arch_counter_register(unsigned type) u64 start_count; /* Register the CP15 based counter if we have one */ - if (type & ARCH_CP15_TIMER) + if (type & ARCH_CP15_TIMER) { arch_timer_read_counter = arch_counter_get_cntvct; - else + } else { arch_timer_read_counter = arch_counter_get_cntvct_mem; + /* If the clocksource name is "arch_sys_counter" the + * VDSO will attempt to read the CP15-based counter. + * Ensure this does not happen when CP15-based + * counter is not available. + */ + clocksource_counter.name = "arch_mem_counter"; + } + start_count = arch_timer_read_counter(); clocksource_register_hz(&clocksource_counter, arch_timer_rate); cyclecounter.mult = clocksource_counter.mult; @@ -616,17 +656,29 @@ static const struct of_device_id arch_timer_mem_of_match[] __initconst = { {}, }; +static bool __init +arch_timer_probed(int type, const struct of_device_id *matches) +{ + struct device_node *dn; + bool probed = false; + + dn = of_find_matching_node(NULL, matches); + if (dn && of_device_is_available(dn) && (arch_timers_present & type)) + probed = true; + of_node_put(dn); + + return probed; +} + static void __init arch_timer_common_init(void) { unsigned mask = ARCH_CP15_TIMER | ARCH_MEM_TIMER; /* Wait until both nodes are probed if we have two timers */ if ((arch_timers_present & mask) != mask) { - if (of_find_matching_node(NULL, arch_timer_mem_of_match) && - !(arch_timers_present & ARCH_MEM_TIMER)) + if (!arch_timer_probed(ARCH_MEM_TIMER, arch_timer_mem_of_match)) return; - if (of_find_matching_node(NULL, arch_timer_of_match) && - !(arch_timers_present & ARCH_CP15_TIMER)) + if (!arch_timer_probed(ARCH_CP15_TIMER, arch_timer_of_match)) return; } diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c index 7a08811df9aa..510c8a1d37b3 100644 --- a/drivers/clocksource/cadence_ttc_timer.c +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -25,7 +25,7 @@ #include <linux/sched_clock.h> /* - * This driver configures the 2 16-bit count-up timers as follows: + * This driver configures the 2 16/32-bit count-up timers as follows: * * T1: Timer 1, clocksource for generic timekeeping * T2: Timer 2, clockevent source for hrtimers @@ -321,7 +321,8 @@ static int ttc_rate_change_clocksource_cb(struct notifier_block *nb, return NOTIFY_DONE; } -static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base) +static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base, + u32 timer_width) { struct ttc_timer_clocksource *ttccs; int err; @@ -351,7 +352,7 @@ static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base) ttccs->cs.name = "ttc_clocksource"; ttccs->cs.rating = 200; ttccs->cs.read = __ttc_clocksource_read; - ttccs->cs.mask = CLOCKSOURCE_MASK(16); + ttccs->cs.mask = CLOCKSOURCE_MASK(timer_width); ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; /* @@ -372,7 +373,8 @@ static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base) } ttc_sched_clock_val_reg = base + TTC_COUNT_VAL_OFFSET; - sched_clock_register(ttc_sched_clock_read, 16, ttccs->ttc.freq / PRESCALE); + sched_clock_register(ttc_sched_clock_read, timer_width, + ttccs->ttc.freq / PRESCALE); } static int ttc_rate_change_clockevent_cb(struct notifier_block *nb, @@ -467,6 +469,7 @@ static void __init ttc_timer_init(struct device_node *timer) struct clk *clk_cs, *clk_ce; static int initialized; int clksel; + u32 timer_width = 16; if (initialized) return; @@ -490,6 +493,8 @@ static void __init ttc_timer_init(struct device_node *timer) BUG(); } + of_property_read_u32(timer, "timer-width", &timer_width); + clksel = readl_relaxed(timer_baseaddr + TTC_CLK_CNTRL_OFFSET); clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK); clk_cs = of_clk_get(timer, clksel); @@ -506,7 +511,7 @@ static void __init ttc_timer_init(struct device_node *timer) BUG(); } - ttc_setup_clocksource(clk_cs, timer_baseaddr); + ttc_setup_clocksource(clk_cs, timer_baseaddr, timer_width); 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/meson6_timer.c b/drivers/clocksource/meson6_timer.c new file mode 100644 index 000000000000..5c15cba41dca --- /dev/null +++ b/drivers/clocksource/meson6_timer.c @@ -0,0 +1,167 @@ +/* + * Amlogic Meson6 SoCs timer handling. + * + * Copyright (C) 2014 Carlo Caione <carlo@caione.org> + * + * Based on code from Amlogic, Inc + * + * 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 <linux/clk.h> +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqreturn.h> +#include <linux/sched_clock.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +#define CED_ID 0 +#define CSD_ID 4 + +#define TIMER_ISA_MUX 0 +#define TIMER_ISA_VAL(t) (((t) + 1) << 2) + +#define TIMER_INPUT_BIT(t) (2 * (t)) +#define TIMER_ENABLE_BIT(t) (16 + (t)) +#define TIMER_PERIODIC_BIT(t) (12 + (t)) + +#define TIMER_CED_INPUT_MASK (3UL << TIMER_INPUT_BIT(CED_ID)) +#define TIMER_CSD_INPUT_MASK (7UL << TIMER_INPUT_BIT(CSD_ID)) + +#define TIMER_CED_UNIT_1US 0 +#define TIMER_CSD_UNIT_1US 1 + +static void __iomem *timer_base; + +static u64 notrace meson6_timer_sched_read(void) +{ + return (u64)readl(timer_base + TIMER_ISA_VAL(CSD_ID)); +} + +static void meson6_clkevt_time_stop(unsigned char timer) +{ + u32 val = readl(timer_base + TIMER_ISA_MUX); + + writel(val & ~TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX); +} + +static void meson6_clkevt_time_setup(unsigned char timer, unsigned long delay) +{ + writel(delay, timer_base + TIMER_ISA_VAL(timer)); +} + +static void meson6_clkevt_time_start(unsigned char timer, bool periodic) +{ + u32 val = readl(timer_base + TIMER_ISA_MUX); + + if (periodic) + val |= TIMER_PERIODIC_BIT(timer); + else + val &= ~TIMER_PERIODIC_BIT(timer); + + writel(val | TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX); +} + +static void meson6_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + meson6_clkevt_time_stop(CED_ID); + meson6_clkevt_time_setup(CED_ID, USEC_PER_SEC/HZ - 1); + meson6_clkevt_time_start(CED_ID, true); + break; + case CLOCK_EVT_MODE_ONESHOT: + meson6_clkevt_time_stop(CED_ID); + meson6_clkevt_time_start(CED_ID, false); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + meson6_clkevt_time_stop(CED_ID); + break; + } +} + +static int meson6_clkevt_next_event(unsigned long evt, + struct clock_event_device *unused) +{ + meson6_clkevt_time_stop(CED_ID); + meson6_clkevt_time_setup(CED_ID, evt); + meson6_clkevt_time_start(CED_ID, false); + + return 0; +} + +static struct clock_event_device meson6_clockevent = { + .name = "meson6_tick", + .rating = 400, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = meson6_clkevt_mode, + .set_next_event = meson6_clkevt_next_event, +}; + +static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = (struct clock_event_device *)dev_id; + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction meson6_timer_irq = { + .name = "meson6_timer", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = meson6_timer_interrupt, + .dev_id = &meson6_clockevent, +}; + +static void __init meson6_timer_init(struct device_node *node) +{ + u32 val; + int ret, irq; + + timer_base = of_io_request_and_map(node, 0, "meson6-timer"); + if (IS_ERR(timer_base)) + panic("Can't map registers"); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) + panic("Can't parse IRQ"); + + /* Set 1us for timer E */ + val = readl(timer_base + TIMER_ISA_MUX); + val &= ~TIMER_CSD_INPUT_MASK; + val |= TIMER_CSD_UNIT_1US << TIMER_INPUT_BIT(CSD_ID); + writel(val, timer_base + TIMER_ISA_MUX); + + sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC); + clocksource_mmio_init(timer_base + TIMER_ISA_VAL(CSD_ID), node->name, + 1000 * 1000, 300, 32, clocksource_mmio_readl_up); + + /* Timer A base 1us */ + val &= ~TIMER_CED_INPUT_MASK; + val |= TIMER_CED_UNIT_1US << TIMER_INPUT_BIT(CED_ID); + writel(val, timer_base + TIMER_ISA_MUX); + + /* Stop the timer A */ + meson6_clkevt_time_stop(CED_ID); + + ret = setup_irq(irq, &meson6_timer_irq); + if (ret) + pr_warn("failed to setup irq %d\n", irq); + + meson6_clockevent.cpumask = cpu_possible_mask; + meson6_clockevent.irq = irq; + + clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC, + 1, 0xfffe); +} +CLOCKSOURCE_OF_DECLARE(meson6, "amlogic,meson6-timer", + meson6_timer_init); diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c index 330e93064692..caf7a2030461 100644 --- a/drivers/clocksource/timer-marco.c +++ b/drivers/clocksource/timer-marco.c @@ -63,7 +63,7 @@ static inline void sirfsoc_timer_count_disable(int idx) /* enable count and interrupt */ static inline void sirfsoc_timer_count_enable(int idx) { - writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x7, + writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x3, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx); } @@ -103,6 +103,9 @@ static int sirfsoc_timer_set_next_event(unsigned long delta, { int cpu = smp_processor_id(); + /* disable timer first, then modify the related registers */ + sirfsoc_timer_count_disable(cpu); + writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 + 4 * cpu); writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 + diff --git a/drivers/clocksource/vf_pit_timer.c b/drivers/clocksource/vf_pit_timer.c index a918bc481c52..b45ac6229b57 100644 --- a/drivers/clocksource/vf_pit_timer.c +++ b/drivers/clocksource/vf_pit_timer.c @@ -93,6 +93,10 @@ static void pit_set_mode(enum clock_event_mode mode, case CLOCK_EVT_MODE_PERIODIC: pit_set_next_event(cycle_per_jiffy, evt); break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + pit_timer_disable(); + break; default: break; } |