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