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> 27f6dc1576SScott Wood #include <linux/jump_label.h> 286acc71ccSMarc Zyngier #include <linux/smp.h> 291aee5d7aSMark Rutland #include <linux/types.h> 301aee5d7aSMark Rutland 311aee5d7aSMark Rutland #include <clocksource/arm_arch_timer.h> 321aee5d7aSMark Rutland 3316d10ef2SDing Tianhong #if IS_ENABLED(CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND) 34f6dc1576SScott Wood extern struct static_key_false arch_timer_read_ool_enabled; 3516d10ef2SDing Tianhong #define needs_unstable_timer_counter_workaround() \ 36f6dc1576SScott Wood static_branch_unlikely(&arch_timer_read_ool_enabled) 37f6dc1576SScott Wood #else 3816d10ef2SDing Tianhong #define needs_unstable_timer_counter_workaround() false 39f6dc1576SScott Wood #endif 40f6dc1576SScott Wood 41651bb2e9SMarc Zyngier enum arch_timer_erratum_match_type { 42651bb2e9SMarc Zyngier ate_match_dt, 430064030cSMarc Zyngier ate_match_local_cap_id, 44*5a38bcacSMarc Zyngier ate_match_acpi_oem_info, 45651bb2e9SMarc Zyngier }; 46f6dc1576SScott Wood 4701d3e3ffSMarc Zyngier struct clock_event_device; 4801d3e3ffSMarc Zyngier 4916d10ef2SDing Tianhong struct arch_timer_erratum_workaround { 50651bb2e9SMarc Zyngier enum arch_timer_erratum_match_type match_type; 51651bb2e9SMarc Zyngier const void *id; 52651bb2e9SMarc Zyngier const char *desc; 5316d10ef2SDing Tianhong u32 (*read_cntp_tval_el0)(void); 5416d10ef2SDing Tianhong u32 (*read_cntv_tval_el0)(void); 5516d10ef2SDing Tianhong u64 (*read_cntvct_el0)(void); 5601d3e3ffSMarc Zyngier int (*set_next_event_phys)(unsigned long, struct clock_event_device *); 5701d3e3ffSMarc Zyngier int (*set_next_event_virt)(unsigned long, struct clock_event_device *); 5816d10ef2SDing Tianhong }; 5916d10ef2SDing Tianhong 606acc71ccSMarc Zyngier DECLARE_PER_CPU(const struct arch_timer_erratum_workaround *, 616acc71ccSMarc Zyngier timer_unstable_counter_workaround); 62f6dc1576SScott Wood 63f6dc1576SScott Wood #define arch_timer_reg_read_stable(reg) \ 64f6dc1576SScott Wood ({ \ 65f6dc1576SScott Wood u64 _val; \ 666acc71ccSMarc Zyngier if (needs_unstable_timer_counter_workaround()) { \ 676acc71ccSMarc Zyngier const struct arch_timer_erratum_workaround *wa; \ 686acc71ccSMarc Zyngier preempt_disable(); \ 696acc71ccSMarc Zyngier wa = __this_cpu_read(timer_unstable_counter_workaround); \ 706acc71ccSMarc Zyngier if (wa && wa->read_##reg) \ 716acc71ccSMarc Zyngier _val = wa->read_##reg(); \ 72f6dc1576SScott Wood else \ 73f6dc1576SScott Wood _val = read_sysreg(reg); \ 746acc71ccSMarc Zyngier preempt_enable(); \ 756acc71ccSMarc Zyngier } else { \ 766acc71ccSMarc Zyngier _val = read_sysreg(reg); \ 776acc71ccSMarc Zyngier } \ 78f6dc1576SScott Wood _val; \ 79f6dc1576SScott Wood }) 80f6dc1576SScott Wood 81e09f3cc0SStephen Boyd /* 82e09f3cc0SStephen Boyd * These register accessors are marked inline so the compiler can 83e09f3cc0SStephen Boyd * nicely work out which register we want, and chuck away the rest of 84e09f3cc0SStephen Boyd * the code. 85e09f3cc0SStephen Boyd */ 86e09f3cc0SStephen Boyd static __always_inline 8760faddf6SStephen Boyd void arch_timer_reg_write_cp15(int access, enum arch_timer_reg reg, u32 val) 881aee5d7aSMark Rutland { 891aee5d7aSMark Rutland if (access == ARCH_TIMER_PHYS_ACCESS) { 901aee5d7aSMark Rutland switch (reg) { 911aee5d7aSMark Rutland case ARCH_TIMER_REG_CTRL: 92cd5f22d7SMark Rutland write_sysreg(val, cntp_ctl_el0); 931aee5d7aSMark Rutland break; 941aee5d7aSMark Rutland case ARCH_TIMER_REG_TVAL: 95cd5f22d7SMark Rutland write_sysreg(val, cntp_tval_el0); 961aee5d7aSMark Rutland break; 971aee5d7aSMark Rutland } 981aee5d7aSMark Rutland } else if (access == ARCH_TIMER_VIRT_ACCESS) { 991aee5d7aSMark Rutland switch (reg) { 1001aee5d7aSMark Rutland case ARCH_TIMER_REG_CTRL: 101cd5f22d7SMark Rutland write_sysreg(val, cntv_ctl_el0); 1021aee5d7aSMark Rutland break; 1031aee5d7aSMark Rutland case ARCH_TIMER_REG_TVAL: 104cd5f22d7SMark Rutland write_sysreg(val, cntv_tval_el0); 1051aee5d7aSMark Rutland break; 1061aee5d7aSMark Rutland } 1071aee5d7aSMark Rutland } 1081aee5d7aSMark Rutland 1091aee5d7aSMark Rutland isb(); 1101aee5d7aSMark Rutland } 1111aee5d7aSMark Rutland 112e09f3cc0SStephen Boyd static __always_inline 11360faddf6SStephen Boyd u32 arch_timer_reg_read_cp15(int access, enum arch_timer_reg reg) 1141aee5d7aSMark Rutland { 1151aee5d7aSMark Rutland if (access == ARCH_TIMER_PHYS_ACCESS) { 1161aee5d7aSMark Rutland switch (reg) { 1171aee5d7aSMark Rutland case ARCH_TIMER_REG_CTRL: 118cd5f22d7SMark Rutland return read_sysreg(cntp_ctl_el0); 1191aee5d7aSMark Rutland case ARCH_TIMER_REG_TVAL: 120f6dc1576SScott Wood return arch_timer_reg_read_stable(cntp_tval_el0); 1211aee5d7aSMark Rutland } 1221aee5d7aSMark Rutland } else if (access == ARCH_TIMER_VIRT_ACCESS) { 1231aee5d7aSMark Rutland switch (reg) { 1241aee5d7aSMark Rutland case ARCH_TIMER_REG_CTRL: 125cd5f22d7SMark Rutland return read_sysreg(cntv_ctl_el0); 1261aee5d7aSMark Rutland case ARCH_TIMER_REG_TVAL: 127f6dc1576SScott Wood return arch_timer_reg_read_stable(cntv_tval_el0); 1281aee5d7aSMark Rutland } 1291aee5d7aSMark Rutland } 1301aee5d7aSMark Rutland 131cd5f22d7SMark Rutland BUG(); 1321aee5d7aSMark Rutland } 1331aee5d7aSMark Rutland 1341aee5d7aSMark Rutland static inline u32 arch_timer_get_cntfrq(void) 1351aee5d7aSMark Rutland { 136cd5f22d7SMark Rutland return read_sysreg(cntfrq_el0); 1371aee5d7aSMark Rutland } 1381aee5d7aSMark Rutland 13946efe547SSudeep KarkadaNagesha static inline u32 arch_timer_get_cntkctl(void) 1401aee5d7aSMark Rutland { 141cd5f22d7SMark Rutland return read_sysreg(cntkctl_el1); 14246efe547SSudeep KarkadaNagesha } 14346efe547SSudeep KarkadaNagesha 14446efe547SSudeep KarkadaNagesha static inline void arch_timer_set_cntkctl(u32 cntkctl) 14546efe547SSudeep KarkadaNagesha { 146cd5f22d7SMark Rutland write_sysreg(cntkctl, cntkctl_el1); 14746efe547SSudeep KarkadaNagesha } 14846efe547SSudeep KarkadaNagesha 1490b46b8a7SSonny Rao static inline u64 arch_counter_get_cntpct(void) 1500b46b8a7SSonny Rao { 1510b46b8a7SSonny Rao /* 1520b46b8a7SSonny Rao * AArch64 kernel and user space mandate the use of CNTVCT. 1530b46b8a7SSonny Rao */ 1540b46b8a7SSonny Rao BUG(); 1550b46b8a7SSonny Rao return 0; 1560b46b8a7SSonny Rao } 1570b46b8a7SSonny Rao 1581aee5d7aSMark Rutland static inline u64 arch_counter_get_cntvct(void) 1591aee5d7aSMark Rutland { 1601aee5d7aSMark Rutland isb(); 161f6dc1576SScott Wood return arch_timer_reg_read_stable(cntvct_el0); 1621aee5d7aSMark Rutland } 1631aee5d7aSMark Rutland 1640583fe47SRob Herring static inline int arch_timer_arch_init(void) 1650583fe47SRob Herring { 1660583fe47SRob Herring return 0; 1670583fe47SRob Herring } 1680583fe47SRob Herring 1691aee5d7aSMark Rutland #endif 170