@@ -26,6 +26,7 @@
#include <linux/acpi.h>
#include <asm/arch_timer.h>
+#include <asm/pvclock-abi.h>
#include <asm/virt.h>
#include <clocksource/arm_arch_timer.h>
@@ -84,6 +85,66 @@ static int __init early_evtstrm_cfg(char *buf)
}
early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
+/* PV-time LPT */
+#ifdef CONFIG_ARM64
+struct pvclock_vm_lpt_time *lpt_info;
+EXPORT_SYMBOL_GPL(lpt_info);
+DEFINE_STATIC_KEY_FALSE(pvclock_lpt_key_enabled);
+EXPORT_SYMBOL_GPL(pvclock_lpt_key_enabled);
+
+static bool has_pv_lpt_clock(void)
+{
+ struct arm_smccc_res res;
+
+ if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE)
+ return false;
+
+ arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
+ ARM_SMCCC_HV_PV_TIME_FEATURES, &res);
+ if (res.a0 != SMCCC_RET_SUCCESS)
+ return false;
+
+ arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_FEATURES,
+ ARM_SMCCC_HV_PV_TIME_LPT, &res);
+ return res.a0 == SMCCC_RET_SUCCESS;
+}
+
+static int pvclock_lpt_init(void)
+{
+ struct arm_smccc_res res;
+
+ if (!has_pv_lpt_clock())
+ return 0;
+
+ arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_LPT, 0, &res);
+ if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
+ return 0;
+
+ lpt_info = memremap(res.a0, sizeof(*lpt_info), MEMREMAP_WB);
+ if (!lpt_info) {
+ pr_warn("Failed to map pvclock LPT data structure\n");
+ return -EFAULT;
+ }
+
+ if (le32_to_cpu(lpt_info->revision) != 0 ||
+ le32_to_cpu(lpt_info->attributes) != 0) {
+ pr_warn_once("Unexpected revision or attributes "
+ "in pvclock LPT data structure\n");
+ return -EFAULT;
+ }
+
+ static_branch_enable(&pvclock_lpt_key_enabled);
+ pr_info("Using pvclock LPT\n");
+ return 0;
+}
+#else /* CONFIG_ARM64 */
+static int pvclock_lpt_init(void)
+{
+ return 0;
+}
+#endif /* CONFIG_ARM64 */
+
+
/*
* Architected system timer support.
*/
@@ -1285,6 +1346,10 @@ static int __init arch_timer_of_init(struct device_node *np)
arch_timer_populate_kvm_info();
+ ret = pvclock_lpt_init();
+ if (ret)
+ return ret;
+
rate = arch_timer_get_cntfrq();
arch_timer_of_configure_rate(rate, np);
@@ -1613,6 +1678,10 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
arch_timer_populate_kvm_info();
+ ret = pvclock_lpt_init();
+ if (ret)
+ return ret;
+
/*
* When probing via ACPI, we have no mechanism to override the sysreg
* CNTFRQ value. This *must* be correct.