xref: /openbmc/linux/arch/arm64/include/asm/arch_timer.h (revision f6dc1576cd517440313c9551b6ffa3d7e389c7c7)
11aee5d7aSMark Rutland /*
21aee5d7aSMark Rutland  * arch/arm64/include/asm/arch_timer.h
31aee5d7aSMark Rutland  *
41aee5d7aSMark Rutland  * Copyright (C) 2012 ARM Ltd.
51aee5d7aSMark Rutland  * Author: Marc Zyngier <marc.zyngier@arm.com>
61aee5d7aSMark Rutland  *
71aee5d7aSMark Rutland  * This program is free software: you can redistribute it and/or modify
81aee5d7aSMark Rutland  * it under the terms of the GNU General Public License version 2 as
91aee5d7aSMark Rutland  * published by the Free Software Foundation.
101aee5d7aSMark Rutland  *
111aee5d7aSMark Rutland  * This program is distributed in the hope that it will be useful,
121aee5d7aSMark Rutland  * but WITHOUT ANY WARRANTY; without even the implied warranty of
131aee5d7aSMark Rutland  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141aee5d7aSMark Rutland  * GNU General Public License for more details.
151aee5d7aSMark Rutland  *
161aee5d7aSMark Rutland  * You should have received a copy of the GNU General Public License
171aee5d7aSMark Rutland  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
181aee5d7aSMark Rutland  */
191aee5d7aSMark Rutland #ifndef __ASM_ARCH_TIMER_H
201aee5d7aSMark Rutland #define __ASM_ARCH_TIMER_H
211aee5d7aSMark Rutland 
221aee5d7aSMark Rutland #include <asm/barrier.h>
23cd5f22d7SMark Rutland #include <asm/sysreg.h>
241aee5d7aSMark Rutland 
25082471a8SPaul Walmsley #include <linux/bug.h>
261aee5d7aSMark Rutland #include <linux/init.h>
27*f6dc1576SScott Wood #include <linux/jump_label.h>
281aee5d7aSMark Rutland #include <linux/types.h>
291aee5d7aSMark Rutland 
301aee5d7aSMark Rutland #include <clocksource/arm_arch_timer.h>
311aee5d7aSMark Rutland 
32*f6dc1576SScott Wood #if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
33*f6dc1576SScott Wood extern struct static_key_false arch_timer_read_ool_enabled;
34*f6dc1576SScott Wood #define needs_fsl_a008585_workaround() \
35*f6dc1576SScott Wood 	static_branch_unlikely(&arch_timer_read_ool_enabled)
36*f6dc1576SScott Wood #else
37*f6dc1576SScott Wood #define needs_fsl_a008585_workaround()  false
38*f6dc1576SScott Wood #endif
39*f6dc1576SScott Wood 
40*f6dc1576SScott Wood u32 __fsl_a008585_read_cntp_tval_el0(void);
41*f6dc1576SScott Wood u32 __fsl_a008585_read_cntv_tval_el0(void);
42*f6dc1576SScott Wood u64 __fsl_a008585_read_cntvct_el0(void);
43*f6dc1576SScott Wood 
44*f6dc1576SScott Wood /*
45*f6dc1576SScott Wood  * The number of retries is an arbitrary value well beyond the highest number
46*f6dc1576SScott Wood  * of iterations the loop has been observed to take.
47*f6dc1576SScott Wood  */
48*f6dc1576SScott Wood #define __fsl_a008585_read_reg(reg) ({			\
49*f6dc1576SScott Wood 	u64 _old, _new;					\
50*f6dc1576SScott Wood 	int _retries = 200;				\
51*f6dc1576SScott Wood 							\
52*f6dc1576SScott Wood 	do {						\
53*f6dc1576SScott Wood 		_old = read_sysreg(reg);		\
54*f6dc1576SScott Wood 		_new = read_sysreg(reg);		\
55*f6dc1576SScott Wood 		_retries--;				\
56*f6dc1576SScott Wood 	} while (unlikely(_old != _new) && _retries);	\
57*f6dc1576SScott Wood 							\
58*f6dc1576SScott Wood 	WARN_ON_ONCE(!_retries);			\
59*f6dc1576SScott Wood 	_new;						\
60*f6dc1576SScott Wood })
61*f6dc1576SScott Wood 
62*f6dc1576SScott Wood #define arch_timer_reg_read_stable(reg) 		\
63*f6dc1576SScott Wood ({							\
64*f6dc1576SScott Wood 	u64 _val;					\
65*f6dc1576SScott Wood 	if (needs_fsl_a008585_workaround())		\
66*f6dc1576SScott Wood 		_val = __fsl_a008585_read_##reg();	\
67*f6dc1576SScott Wood 	else						\
68*f6dc1576SScott Wood 		_val = read_sysreg(reg);		\
69*f6dc1576SScott Wood 	_val;						\
70*f6dc1576SScott Wood })
71*f6dc1576SScott Wood 
72e09f3cc0SStephen Boyd /*
73e09f3cc0SStephen Boyd  * These register accessors are marked inline so the compiler can
74e09f3cc0SStephen Boyd  * nicely work out which register we want, and chuck away the rest of
75e09f3cc0SStephen Boyd  * the code.
76e09f3cc0SStephen Boyd  */
77e09f3cc0SStephen Boyd static __always_inline
7860faddf6SStephen Boyd void arch_timer_reg_write_cp15(int access, enum arch_timer_reg reg, u32 val)
791aee5d7aSMark Rutland {
801aee5d7aSMark Rutland 	if (access == ARCH_TIMER_PHYS_ACCESS) {
811aee5d7aSMark Rutland 		switch (reg) {
821aee5d7aSMark Rutland 		case ARCH_TIMER_REG_CTRL:
83cd5f22d7SMark Rutland 			write_sysreg(val, cntp_ctl_el0);
841aee5d7aSMark Rutland 			break;
851aee5d7aSMark Rutland 		case ARCH_TIMER_REG_TVAL:
86cd5f22d7SMark Rutland 			write_sysreg(val, cntp_tval_el0);
871aee5d7aSMark Rutland 			break;
881aee5d7aSMark Rutland 		}
891aee5d7aSMark Rutland 	} else if (access == ARCH_TIMER_VIRT_ACCESS) {
901aee5d7aSMark Rutland 		switch (reg) {
911aee5d7aSMark Rutland 		case ARCH_TIMER_REG_CTRL:
92cd5f22d7SMark Rutland 			write_sysreg(val, cntv_ctl_el0);
931aee5d7aSMark Rutland 			break;
941aee5d7aSMark Rutland 		case ARCH_TIMER_REG_TVAL:
95cd5f22d7SMark Rutland 			write_sysreg(val, cntv_tval_el0);
961aee5d7aSMark Rutland 			break;
971aee5d7aSMark Rutland 		}
981aee5d7aSMark Rutland 	}
991aee5d7aSMark Rutland 
1001aee5d7aSMark Rutland 	isb();
1011aee5d7aSMark Rutland }
1021aee5d7aSMark Rutland 
103e09f3cc0SStephen Boyd static __always_inline
10460faddf6SStephen Boyd u32 arch_timer_reg_read_cp15(int access, enum arch_timer_reg reg)
1051aee5d7aSMark Rutland {
1061aee5d7aSMark Rutland 	if (access == ARCH_TIMER_PHYS_ACCESS) {
1071aee5d7aSMark Rutland 		switch (reg) {
1081aee5d7aSMark Rutland 		case ARCH_TIMER_REG_CTRL:
109cd5f22d7SMark Rutland 			return read_sysreg(cntp_ctl_el0);
1101aee5d7aSMark Rutland 		case ARCH_TIMER_REG_TVAL:
111*f6dc1576SScott Wood 			return arch_timer_reg_read_stable(cntp_tval_el0);
1121aee5d7aSMark Rutland 		}
1131aee5d7aSMark Rutland 	} else if (access == ARCH_TIMER_VIRT_ACCESS) {
1141aee5d7aSMark Rutland 		switch (reg) {
1151aee5d7aSMark Rutland 		case ARCH_TIMER_REG_CTRL:
116cd5f22d7SMark Rutland 			return read_sysreg(cntv_ctl_el0);
1171aee5d7aSMark Rutland 		case ARCH_TIMER_REG_TVAL:
118*f6dc1576SScott Wood 			return arch_timer_reg_read_stable(cntv_tval_el0);
1191aee5d7aSMark Rutland 		}
1201aee5d7aSMark Rutland 	}
1211aee5d7aSMark Rutland 
122cd5f22d7SMark Rutland 	BUG();
1231aee5d7aSMark Rutland }
1241aee5d7aSMark Rutland 
1251aee5d7aSMark Rutland static inline u32 arch_timer_get_cntfrq(void)
1261aee5d7aSMark Rutland {
127cd5f22d7SMark Rutland 	return read_sysreg(cntfrq_el0);
1281aee5d7aSMark Rutland }
1291aee5d7aSMark Rutland 
13046efe547SSudeep KarkadaNagesha static inline u32 arch_timer_get_cntkctl(void)
1311aee5d7aSMark Rutland {
132cd5f22d7SMark Rutland 	return read_sysreg(cntkctl_el1);
13346efe547SSudeep KarkadaNagesha }
13446efe547SSudeep KarkadaNagesha 
13546efe547SSudeep KarkadaNagesha static inline void arch_timer_set_cntkctl(u32 cntkctl)
13646efe547SSudeep KarkadaNagesha {
137cd5f22d7SMark Rutland 	write_sysreg(cntkctl, cntkctl_el1);
13846efe547SSudeep KarkadaNagesha }
13946efe547SSudeep KarkadaNagesha 
1400b46b8a7SSonny Rao static inline u64 arch_counter_get_cntpct(void)
1410b46b8a7SSonny Rao {
1420b46b8a7SSonny Rao 	/*
1430b46b8a7SSonny Rao 	 * AArch64 kernel and user space mandate the use of CNTVCT.
1440b46b8a7SSonny Rao 	 */
1450b46b8a7SSonny Rao 	BUG();
1460b46b8a7SSonny Rao 	return 0;
1470b46b8a7SSonny Rao }
1480b46b8a7SSonny Rao 
1491aee5d7aSMark Rutland static inline u64 arch_counter_get_cntvct(void)
1501aee5d7aSMark Rutland {
1511aee5d7aSMark Rutland 	isb();
152*f6dc1576SScott Wood 	return arch_timer_reg_read_stable(cntvct_el0);
1531aee5d7aSMark Rutland }
1541aee5d7aSMark Rutland 
1550583fe47SRob Herring static inline int arch_timer_arch_init(void)
1560583fe47SRob Herring {
1570583fe47SRob Herring 	return 0;
1580583fe47SRob Herring }
1590583fe47SRob Herring 
1601aee5d7aSMark Rutland #endif
161