summaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/efi.c
diff options
context:
space:
mode:
authorJean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>2017-03-03 13:34:04 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2017-03-09 07:36:16 +0100
commit637d6dfef2e67bc91cacd1954f465df160fa5207 (patch)
tree21d2af8370797a696ae5b9963f52f391f343ff59 /drivers/clocksource/efi.c
parentda11bd9d6028b811732e98bc8cbf3e00b7a1f2b3 (diff)
downloadbarebox-637d6dfef2e67bc91cacd1954f465df160fa5207.tar.gz
barebox-637d6dfef2e67bc91cacd1954f465df160fa5207.tar.xz
efi: clocksoure: add EFI event timer
with this we can be hw generic If the EFI implement timestamp protocol we could use instead of event but even EDK2 Never Ever compile it for any target. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/clocksource/efi.c')
-rw-r--r--drivers/clocksource/efi.c110
1 files changed, 110 insertions, 0 deletions
diff --git a/drivers/clocksource/efi.c b/drivers/clocksource/efi.c
new file mode 100644
index 0000000000..89906c452e
--- /dev/null
+++ b/drivers/clocksource/efi.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 Jean-Christophe PLAGNIOL-VILLARD <plagnio@jcrosoft.com>
+ *
+ * Under GPL v2
+ */
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <clock.h>
+#include <efi.h>
+#include <efi/efi.h>
+#include <efi/efi-device.h>
+#include <linux/err.h>
+
+static uint64_t ticks = 1;
+static void *efi_cs_evt;
+
+static uint64_t efi_cs_read(void)
+{
+ return ticks;
+}
+
+static void efi_cs_inc(void *event, void *ctx)
+{
+ ticks++;
+}
+
+/* count ticks during a 1dms */
+static uint64_t ticks_freq(void)
+{
+ uint64_t ticks_start, ticks_end;
+
+ ticks_start = ticks;
+ BS->stall(1000);
+ ticks_end = ticks;
+
+ return (ticks_end - ticks_start) * 1000;
+}
+
+/* count ticks during a 20ms delay as on qemu x86_64 the max is 100Hz */
+static uint64_t ticks_freq_x86(void)
+{
+ uint64_t ticks_start, ticks_end;
+
+ ticks_start = ticks;
+ BS->stall(20 * 1000);
+ ticks_end = ticks;
+
+ return (ticks_end - ticks_start) * 50;
+}
+
+static int efi_cs_init(struct clocksource *cs)
+{
+ efi_status_t efiret;
+ uint64_t freq;
+
+ efiret = BS->create_event(EFI_EVT_TIMER | EFI_EVT_NOTIFY_SIGNAL,
+ EFI_TPL_CALLBACK, efi_cs_inc, NULL, &efi_cs_evt);
+
+ if (EFI_ERROR(efiret))
+ return -efi_errno(efiret);
+
+ efiret = BS->set_timer(efi_cs_evt, EFI_TIMER_PERIODIC, 10);
+ if (EFI_ERROR(efiret)) {
+ BS->close_event(efi_cs_evt);
+ return -efi_errno(efiret);
+ }
+
+ freq = 1000 * 1000;
+ if (ticks_freq() < 800 * 1000) {
+ uint64_t nb_100ns;
+
+ freq = ticks_freq_x86();
+ nb_100ns = 10 * 1000 * 1000 / freq;
+ pr_warn("EFI Event timer too slow freq = %llu Hz\n", freq);
+ efiret = BS->set_timer(efi_cs_evt, EFI_TIMER_PERIODIC, nb_100ns);
+ if (EFI_ERROR(efiret)) {
+ BS->close_event(efi_cs_evt);
+ return -efi_errno(efiret);
+ }
+ }
+
+ cs->mult = clocksource_hz2mult(freq, cs->shift);
+
+ return 0;
+}
+
+static struct clocksource efi_cs = {
+ .read = efi_cs_read,
+ .mask = CLOCKSOURCE_MASK(64),
+ .shift = 0,
+ .init = efi_cs_init,
+};
+
+static int efi_cs_probe(struct device_d *dev)
+{
+ return init_clock(&efi_cs);
+}
+
+static struct driver_d efi_cs_driver = {
+ .name = "efi-cs",
+ .probe = efi_cs_probe,
+};
+
+static int efi_cs_initcall(void)
+{
+ return platform_driver_register(&efi_cs_driver);
+}
+/* for efi the time must be init at core initcall level */
+core_initcall(efi_cs_initcall);