Skip to content

Commit

Permalink
arm64: Generic timers support
Browse files Browse the repository at this point in the history
This patch adds support for the ARM generic timers with A64 instructions
for accessing the timer registers. It uses the physical counter as the
clock source and the virtual counter as sched_clock.

The timer frequency can be specified via DT or read from the CNTFRQ_EL0
register. The physical counter is also accessible from user space
allowing fast gettimeofday() implementation.

Signed-off-by: Marc Zyngier <[email protected]>
Signed-off-by: Will Deacon <[email protected]>
Signed-off-by: Catalin Marinas <[email protected]>
Acked-by: Tony Lindgren <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
Acked-by: Olof Johansson <[email protected]>
Acked-by: Santosh Shilimkar <[email protected]>
Acked-by: Arnd Bergmann <[email protected]>
  • Loading branch information
Marc Zyngier authored and ctmarinas committed Sep 17, 2012
1 parent 257cb25 commit 985c067
Show file tree
Hide file tree
Showing 7 changed files with 453 additions and 0 deletions.
100 changes: 100 additions & 0 deletions arch/arm64/include/asm/arm_generic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* arch/arm64/include/asm/arm_generic.h
*
* Copyright (C) 2012 ARM Ltd.
* Author: Marc Zyngier <[email protected]>
*
* 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.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ASM_ARM_GENERIC_H
#define __ASM_ARM_GENERIC_H

#include <linux/clocksource.h>

#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
#define ARCH_TIMER_CTRL_IMASK (1 << 1)
#define ARCH_TIMER_CTRL_ISTATUS (1 << 2)

#define ARCH_TIMER_REG_CTRL 0
#define ARCH_TIMER_REG_FREQ 1
#define ARCH_TIMER_REG_TVAL 2

static inline void arch_timer_reg_write(int reg, u32 val)
{
switch (reg) {
case ARCH_TIMER_REG_CTRL:
asm volatile("msr cntp_ctl_el0, %0" : : "r" (val));
break;
case ARCH_TIMER_REG_TVAL:
asm volatile("msr cntp_tval_el0, %0" : : "r" (val));
break;
default:
BUILD_BUG();
}

isb();
}

static inline u32 arch_timer_reg_read(int reg)
{
u32 val;

switch (reg) {
case ARCH_TIMER_REG_CTRL:
asm volatile("mrs %0, cntp_ctl_el0" : "=r" (val));
break;
case ARCH_TIMER_REG_FREQ:
asm volatile("mrs %0, cntfrq_el0" : "=r" (val));
break;
case ARCH_TIMER_REG_TVAL:
asm volatile("mrs %0, cntp_tval_el0" : "=r" (val));
break;
default:
BUILD_BUG();
}

return val;
}

static inline void __cpuinit arch_counter_enable_user_access(void)
{
u32 cntkctl;

/* Disable user access to the timers and the virtual counter. */
asm volatile("mrs %0, cntkctl_el1" : "=r" (cntkctl));
cntkctl &= ~((3 << 8) | (1 << 1));

/* Enable user access to the physical counter and frequency. */
cntkctl |= 1;
asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl));
}

static inline cycle_t arch_counter_get_cntpct(void)
{
cycle_t cval;

asm volatile("mrs %0, cntpct_el0" : "=r" (cval));

return cval;
}

static inline cycle_t arch_counter_get_cntvct(void)
{
cycle_t cval;

asm volatile("mrs %0, cntvct_el0" : "=r" (cval));

return cval;
}

#endif
29 changes: 29 additions & 0 deletions arch/arm64/include/asm/timex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2012 ARM Ltd.
*
* 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.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ASM_TIMEX_H
#define __ASM_TIMEX_H

/*
* Use the current timer as a cycle counter since this is what we use for
* the delay loop.
*/
#define get_cycles() ({ cycles_t c; read_current_timer(&c); c; })

#include <asm-generic/timex.h>

#define ARCH_HAS_READ_CURRENT_TIMER

#endif
65 changes: 65 additions & 0 deletions arch/arm64/kernel/time.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Based on arch/arm/kernel/time.c
*
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
* Modifications for ARM (C) 1994-2001 Russell King
* Copyright (C) 2012 ARM Ltd.
*
* 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.
*
* 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, see <http://www.gnu.org/licenses/>.
*/

#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/timex.h>
#include <linux/errno.h>
#include <linux/profile.h>
#include <linux/syscore_ops.h>
#include <linux/timer.h>
#include <linux/irq.h>

#include <clocksource/arm_generic.h>

#include <asm/thread_info.h>
#include <asm/stacktrace.h>

#ifdef CONFIG_SMP
unsigned long profile_pc(struct pt_regs *regs)
{
struct stackframe frame;

if (!in_lock_functions(regs->pc))
return regs->pc;

frame.fp = regs->regs[29];
frame.sp = regs->sp;
frame.pc = regs->pc;
do {
int ret = unwind_frame(&frame);
if (ret < 0)
return 0;
} while (in_lock_functions(frame.pc));

return frame.pc;
}
EXPORT_SYMBOL(profile_pc);
#endif

void __init time_init(void)
{
arm_generic_timer_init();
}
5 changes: 5 additions & 0 deletions drivers/clocksource/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,8 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
default y
help
Use the always on PRCMU Timer as sched_clock

config CLKSRC_ARM_GENERIC
def_bool y if ARM64
help
This option enables support for the ARM generic timer.
1 change: 1 addition & 0 deletions drivers/clocksource/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o
Loading

0 comments on commit 985c067

Please sign in to comment.