Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 985c0679 authored by Marc Zyngier's avatar Marc Zyngier Committed by Catalin Marinas
Browse files

arm64: Generic timers support



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: default avatarMarc Zyngier <marc.zyngier@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Acked-by: default avatarTony Lindgren <tony@atomide.com>
Acked-by: default avatarNicolas Pitre <nico@linaro.org>
Acked-by: default avatarOlof Johansson <olof@lixom.net>
Acked-by: default avatarSantosh Shilimkar <santosh.shilimkar@ti.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
parent 257cb251
Loading
Loading
Loading
Loading
+100 −0
Original line number Original line Diff line number Diff line
/*
 * arch/arm64/include/asm/arm_generic.h
 *
 * Copyright (C) 2012 ARM Ltd.
 * Author: Marc Zyngier <marc.zyngier@arm.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.
 *
 * 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 −0
Original line number Original line Diff line number Diff line
/*
 * 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 −0
Original line number Original line Diff line number Diff line
/*
 * 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 −0
Original line number Original line Diff line number Diff line
@@ -35,3 +35,8 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
	default y
	default y
	help
	help
	  Use the always on PRCMU Timer as sched_clock
	  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 −0
Original line number Original line Diff line number Diff line
@@ -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_DW_APB_TIMER_OF)	+= dw_apb_timer_of.o
obj-$(CONFIG_CLKSRC_DBX500_PRCMU)	+= clksrc-dbx500-prcmu.o
obj-$(CONFIG_CLKSRC_DBX500_PRCMU)	+= clksrc-dbx500-prcmu.o
obj-$(CONFIG_ARMADA_370_XP_TIMER)	+= time-armada-370-xp.o
obj-$(CONFIG_ARMADA_370_XP_TIMER)	+= time-armada-370-xp.o
obj-$(CONFIG_CLKSRC_ARM_GENERIC)	+= arm_generic.o
Loading