]>
Commit | Line | Data |
---|---|---|
1aee5d7a MR |
1 | /* |
2 | * arch/arm64/include/asm/arch_timer.h | |
3 | * | |
4 | * Copyright (C) 2012 ARM Ltd. | |
5 | * Author: Marc Zyngier <marc.zyngier@arm.com> | |
6 | * | |
7 | * This program is free software: you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | #ifndef __ASM_ARCH_TIMER_H | |
20 | #define __ASM_ARCH_TIMER_H | |
21 | ||
22 | #include <asm/barrier.h> | |
cd5f22d7 | 23 | #include <asm/sysreg.h> |
1aee5d7a | 24 | |
082471a8 | 25 | #include <linux/bug.h> |
1aee5d7a | 26 | #include <linux/init.h> |
f6dc1576 | 27 | #include <linux/jump_label.h> |
6acc71cc | 28 | #include <linux/smp.h> |
1aee5d7a MR |
29 | #include <linux/types.h> |
30 | ||
31 | #include <clocksource/arm_arch_timer.h> | |
32 | ||
16d10ef2 | 33 | #if IS_ENABLED(CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND) |
f6dc1576 | 34 | extern struct static_key_false arch_timer_read_ool_enabled; |
16d10ef2 | 35 | #define needs_unstable_timer_counter_workaround() \ |
f6dc1576 SW |
36 | static_branch_unlikely(&arch_timer_read_ool_enabled) |
37 | #else | |
16d10ef2 | 38 | #define needs_unstable_timer_counter_workaround() false |
f6dc1576 SW |
39 | #endif |
40 | ||
651bb2e9 MZ |
41 | enum arch_timer_erratum_match_type { |
42 | ate_match_dt, | |
0064030c | 43 | ate_match_local_cap_id, |
5a38bcac | 44 | ate_match_acpi_oem_info, |
651bb2e9 | 45 | }; |
f6dc1576 | 46 | |
01d3e3ff MZ |
47 | struct clock_event_device; |
48 | ||
16d10ef2 | 49 | struct arch_timer_erratum_workaround { |
651bb2e9 MZ |
50 | enum arch_timer_erratum_match_type match_type; |
51 | const void *id; | |
52 | const char *desc; | |
16d10ef2 DT |
53 | u32 (*read_cntp_tval_el0)(void); |
54 | u32 (*read_cntv_tval_el0)(void); | |
55 | u64 (*read_cntvct_el0)(void); | |
01d3e3ff MZ |
56 | int (*set_next_event_phys)(unsigned long, struct clock_event_device *); |
57 | int (*set_next_event_virt)(unsigned long, struct clock_event_device *); | |
16d10ef2 DT |
58 | }; |
59 | ||
6acc71cc MZ |
60 | DECLARE_PER_CPU(const struct arch_timer_erratum_workaround *, |
61 | timer_unstable_counter_workaround); | |
f6dc1576 | 62 | |
6acc71cc MZ |
63 | #define arch_timer_reg_read_stable(reg) \ |
64 | ({ \ | |
65 | u64 _val; \ | |
66 | if (needs_unstable_timer_counter_workaround()) { \ | |
67 | const struct arch_timer_erratum_workaround *wa; \ | |
adb4f11e | 68 | preempt_disable_notrace(); \ |
6acc71cc MZ |
69 | wa = __this_cpu_read(timer_unstable_counter_workaround); \ |
70 | if (wa && wa->read_##reg) \ | |
71 | _val = wa->read_##reg(); \ | |
72 | else \ | |
73 | _val = read_sysreg(reg); \ | |
adb4f11e | 74 | preempt_enable_notrace(); \ |
6acc71cc MZ |
75 | } else { \ |
76 | _val = read_sysreg(reg); \ | |
77 | } \ | |
78 | _val; \ | |
f6dc1576 SW |
79 | }) |
80 | ||
e09f3cc0 SB |
81 | /* |
82 | * These register accessors are marked inline so the compiler can | |
83 | * nicely work out which register we want, and chuck away the rest of | |
84 | * the code. | |
85 | */ | |
86 | static __always_inline | |
60faddf6 | 87 | void arch_timer_reg_write_cp15(int access, enum arch_timer_reg reg, u32 val) |
1aee5d7a MR |
88 | { |
89 | if (access == ARCH_TIMER_PHYS_ACCESS) { | |
90 | switch (reg) { | |
91 | case ARCH_TIMER_REG_CTRL: | |
cd5f22d7 | 92 | write_sysreg(val, cntp_ctl_el0); |
1aee5d7a MR |
93 | break; |
94 | case ARCH_TIMER_REG_TVAL: | |
cd5f22d7 | 95 | write_sysreg(val, cntp_tval_el0); |
1aee5d7a | 96 | break; |
1aee5d7a MR |
97 | } |
98 | } else if (access == ARCH_TIMER_VIRT_ACCESS) { | |
99 | switch (reg) { | |
100 | case ARCH_TIMER_REG_CTRL: | |
cd5f22d7 | 101 | write_sysreg(val, cntv_ctl_el0); |
1aee5d7a MR |
102 | break; |
103 | case ARCH_TIMER_REG_TVAL: | |
cd5f22d7 | 104 | write_sysreg(val, cntv_tval_el0); |
1aee5d7a | 105 | break; |
1aee5d7a | 106 | } |
1aee5d7a MR |
107 | } |
108 | ||
109 | isb(); | |
110 | } | |
111 | ||
e09f3cc0 | 112 | static __always_inline |
60faddf6 | 113 | u32 arch_timer_reg_read_cp15(int access, enum arch_timer_reg reg) |
1aee5d7a | 114 | { |
1aee5d7a MR |
115 | if (access == ARCH_TIMER_PHYS_ACCESS) { |
116 | switch (reg) { | |
117 | case ARCH_TIMER_REG_CTRL: | |
cd5f22d7 | 118 | return read_sysreg(cntp_ctl_el0); |
1aee5d7a | 119 | case ARCH_TIMER_REG_TVAL: |
f6dc1576 | 120 | return arch_timer_reg_read_stable(cntp_tval_el0); |
1aee5d7a MR |
121 | } |
122 | } else if (access == ARCH_TIMER_VIRT_ACCESS) { | |
123 | switch (reg) { | |
124 | case ARCH_TIMER_REG_CTRL: | |
cd5f22d7 | 125 | return read_sysreg(cntv_ctl_el0); |
1aee5d7a | 126 | case ARCH_TIMER_REG_TVAL: |
f6dc1576 | 127 | return arch_timer_reg_read_stable(cntv_tval_el0); |
1aee5d7a | 128 | } |
1aee5d7a MR |
129 | } |
130 | ||
cd5f22d7 | 131 | BUG(); |
1aee5d7a MR |
132 | } |
133 | ||
134 | static inline u32 arch_timer_get_cntfrq(void) | |
135 | { | |
cd5f22d7 | 136 | return read_sysreg(cntfrq_el0); |
1aee5d7a MR |
137 | } |
138 | ||
46efe547 | 139 | static inline u32 arch_timer_get_cntkctl(void) |
1aee5d7a | 140 | { |
cd5f22d7 | 141 | return read_sysreg(cntkctl_el1); |
46efe547 SH |
142 | } |
143 | ||
144 | static inline void arch_timer_set_cntkctl(u32 cntkctl) | |
145 | { | |
cd5f22d7 | 146 | write_sysreg(cntkctl, cntkctl_el1); |
46efe547 SH |
147 | } |
148 | ||
0b46b8a7 SR |
149 | static inline u64 arch_counter_get_cntpct(void) |
150 | { | |
151 | /* | |
152 | * AArch64 kernel and user space mandate the use of CNTVCT. | |
153 | */ | |
154 | BUG(); | |
155 | return 0; | |
156 | } | |
157 | ||
1aee5d7a MR |
158 | static inline u64 arch_counter_get_cntvct(void) |
159 | { | |
1aee5d7a | 160 | isb(); |
f6dc1576 | 161 | return arch_timer_reg_read_stable(cntvct_el0); |
1aee5d7a MR |
162 | } |
163 | ||
0583fe47 RH |
164 | static inline int arch_timer_arch_init(void) |
165 | { | |
166 | return 0; | |
167 | } | |
168 | ||
1aee5d7a | 169 | #endif |