Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[linux-2.6-block.git] / drivers / clocksource / arm_arch_timer.c
index b9243e2328b49e4309f7279a56acea1afe8723eb..aa4ec53281cea585214c3a5f8b4faf941e7e7bd3 100644 (file)
@@ -326,6 +326,48 @@ static u64 notrace arm64_1188873_read_cntvct_el0(void)
 }
 #endif
 
+#ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1
+/*
+ * The low bits of the counter registers are indeterminate while bit 10 or
+ * greater is rolling over. Since the counter value can jump both backward
+ * (7ff -> 000 -> 800) and forward (7ff -> fff -> 800), ignore register values
+ * with all ones or all zeros in the low bits. Bound the loop by the maximum
+ * number of CPU cycles in 3 consecutive 24 MHz counter periods.
+ */
+#define __sun50i_a64_read_reg(reg) ({                                  \
+       u64 _val;                                                       \
+       int _retries = 150;                                             \
+                                                                       \
+       do {                                                            \
+               _val = read_sysreg(reg);                                \
+               _retries--;                                             \
+       } while (((_val + 1) & GENMASK(9, 0)) <= 1 && _retries);        \
+                                                                       \
+       WARN_ON_ONCE(!_retries);                                        \
+       _val;                                                           \
+})
+
+static u64 notrace sun50i_a64_read_cntpct_el0(void)
+{
+       return __sun50i_a64_read_reg(cntpct_el0);
+}
+
+static u64 notrace sun50i_a64_read_cntvct_el0(void)
+{
+       return __sun50i_a64_read_reg(cntvct_el0);
+}
+
+static u32 notrace sun50i_a64_read_cntp_tval_el0(void)
+{
+       return read_sysreg(cntp_cval_el0) - sun50i_a64_read_cntpct_el0();
+}
+
+static u32 notrace sun50i_a64_read_cntv_tval_el0(void)
+{
+       return read_sysreg(cntv_cval_el0) - sun50i_a64_read_cntvct_el0();
+}
+#endif
+
 #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
 DEFINE_PER_CPU(const struct arch_timer_erratum_workaround *, timer_unstable_counter_workaround);
 EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
@@ -423,6 +465,19 @@ static const struct arch_timer_erratum_workaround ool_workarounds[] = {
                .read_cntvct_el0 = arm64_1188873_read_cntvct_el0,
        },
 #endif
+#ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1
+       {
+               .match_type = ate_match_dt,
+               .id = "allwinner,erratum-unknown1",
+               .desc = "Allwinner erratum UNKNOWN1",
+               .read_cntp_tval_el0 = sun50i_a64_read_cntp_tval_el0,
+               .read_cntv_tval_el0 = sun50i_a64_read_cntv_tval_el0,
+               .read_cntpct_el0 = sun50i_a64_read_cntpct_el0,
+               .read_cntvct_el0 = sun50i_a64_read_cntvct_el0,
+               .set_next_event_phys = erratum_set_next_event_tval_phys,
+               .set_next_event_virt = erratum_set_next_event_tval_virt,
+       },
+#endif
 };
 
 typedef bool (*ate_match_fn_t)(const struct arch_timer_erratum_workaround *,