diff --git a/MAINTAINERS b/MAINTAINERS
index 56be0bfad00c..920f515dd555 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -752,6 +752,12 @@ T:	git https://source.denx.de/u-boot/custodians/u-boot-coldfire.git
 F:	arch/m68k/
 F:	doc/arch/m68k.rst
 
+CYCLIC
+M:	Stefan Roese <sr@denx.de>
+S:	Maintained
+F:	common/cyclic.c
+F:	include/cyclic.h
+
 DFU
 M:	Lukasz Majewski <lukma@denx.de>
 S:	Maintained
diff --git a/common/Kconfig b/common/Kconfig
index a96842a5c11d..1b04904fd688 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -551,6 +551,28 @@ config DISPLAY_BOARDINFO_LATE
 
 menu "Start-up hooks"
 
+config CYCLIC
+	bool "General-purpose cyclic execution mechanism"
+	help
+	  This enables a general-purpose cyclic execution infrastructure,
+	  to allow "small" (run-time wise) functions to be executed at
+	  a specified frequency. Things like LED blinking or watchdog
+	  triggering are examples for such tasks.
+
+	  See doc/develop/cyclic.rst for more information.
+
+if CYCLIC
+
+config CYCLIC_MAX_CPU_TIME_US
+	int "Sets the max allowed time for a cyclic function in us"
+	default 1000
+	help
+	  The max allowed time for a cyclic function in us. If a functions
+	  takes longer than this duration this function will get unregistered
+	  automatically.
+
+endif # CYCLIC
+
 config EVENT
 	bool "General-purpose event-handling mechanism"
 	default y if SANDBOX
diff --git a/common/Makefile b/common/Makefile
index 75c24e324927..a5f00ec371c1 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -89,6 +89,7 @@ obj-y += malloc_simple.o
 endif
 endif
 
+obj-$(CONFIG_$(SPL_TPL_)CYCLIC) += cyclic.o
 obj-$(CONFIG_$(SPL_TPL_)EVENT) += event.o
 
 obj-$(CONFIG_$(SPL_TPL_)HASH) += hash.o
diff --git a/common/cyclic.c b/common/cyclic.c
new file mode 100644
index 000000000000..6e7ebbe7b218
--- /dev/null
+++ b/common/cyclic.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * A general-purpose cyclic execution infrastructure, to allow "small"
+ * (run-time wise) functions to be executed at a specified frequency.
+ * Things like LED blinking or watchdog triggering are examples for such
+ * tasks.
+ *
+ * Copyright (C) 2022 Stefan Roese <sr@denx.de>
+ */
+
+#include <cyclic.h>
+#include <log.h>
+#include <linker_lists.h>
+#include <malloc.h>
+#include <time.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+
+struct list_head cyclic_list;
+static bool cyclic_ready;
+static bool cyclic_running;
+
+struct cyclic_struct *cyclic_register(cyclic_func_t func, uint64_t delay_us,
+				      const char *name, void *ctx)
+{
+	struct cyclic_struct *cyclic;
+
+	if (!cyclic_ready) {
+		pr_debug("Cyclic IF not ready yet\n");
+		return NULL;
+	}
+
+	cyclic = calloc(1, sizeof(struct cyclic_struct));
+	if (!cyclic) {
+		pr_debug("Memory allocation error\n");
+		return NULL;
+	}
+
+	/* Store values in struct */
+	cyclic->func = func;
+	cyclic->ctx = ctx;
+	cyclic->name = strdup(name);
+	cyclic->delay_us = delay_us;
+	cyclic->start_time_us = timer_get_us();
+	list_add_tail(&cyclic->list, &cyclic_list);
+
+	return cyclic;
+}
+
+int cyclic_unregister(struct cyclic_struct *cyclic)
+{
+	list_del(&cyclic->list);
+	free(cyclic);
+
+	return 0;
+}
+
+void cyclic_run(void)
+{
+	struct cyclic_struct *cyclic, *tmp;
+	uint64_t now, cpu_time;
+
+	/* Prevent recursion */
+	if (cyclic_running)
+		return;
+
+	cyclic_running = true;
+	list_for_each_entry_safe(cyclic, tmp, &cyclic_list, list) {
+		/*
+		 * Check if this cyclic function needs to get called, e.g.
+		 * do not call the cyclic func too often
+		 */
+		now = timer_get_us();
+		if (time_after_eq64(now, cyclic->next_call)) {
+			/* Call cyclic function and account it's cpu-time */
+			cyclic->next_call = now + cyclic->delay_us;
+			cyclic->func(cyclic->ctx);
+			cyclic->run_cnt++;
+			cpu_time = timer_get_us() - now;
+			cyclic->cpu_time_us += cpu_time;
+
+			/* Check if cpu-time exceeds max allowed time */
+			if (cpu_time > CONFIG_CYCLIC_MAX_CPU_TIME_US) {
+				pr_err("cyclic function %s took too long: %lldus vs %dus max, disabling\n",
+				       cyclic->name, cpu_time,
+				       CONFIG_CYCLIC_MAX_CPU_TIME_US);
+
+				/* Unregister this cyclic function */
+				cyclic_unregister(cyclic);
+			}
+		}
+	}
+	cyclic_running = false;
+}
+
+int cyclic_uninit(void)
+{
+	struct cyclic_struct *cyclic, *tmp;
+
+	list_for_each_entry_safe(cyclic, tmp, &cyclic_list, list)
+		cyclic_unregister(cyclic);
+
+	return 0;
+}
+
+int cyclic_init(void)
+{
+	INIT_LIST_HEAD(&cyclic_list);
+	cyclic_ready = true;
+
+	return 0;
+}
diff --git a/include/cyclic.h b/include/cyclic.h
new file mode 100644
index 000000000000..d07c04458a4f
--- /dev/null
+++ b/include/cyclic.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * A general-purpose cyclic execution infrastructure, to allow "small"
+ * (run-time wise) functions to be executed at a specified frequency.
+ * Things like LED blinking or watchdog triggering are examples for such
+ * tasks.
+ *
+ * Copyright (C) 2022 Stefan Roese <sr@denx.de>
+ */
+
+#ifndef __cyclic_h
+#define __cyclic_h
+
+#include <linux/list.h>
+#include <asm/types.h>
+
+struct cyclic_struct {
+	void (*func)(void *ctx);
+	void *ctx;
+	char *name;
+	uint64_t delay_us;
+	uint64_t start_time_us;
+	uint64_t cpu_time_us;
+	uint64_t run_cnt;
+	uint64_t next_call;
+	struct list_head list;
+};
+
+/** Function type for cyclic functions */
+typedef void (*cyclic_func_t)(void *ctx);
+
+/**
+ * cyclic_register - Register a new cyclic function
+ *
+ * @func: Function to call periodically
+ * @delay_us: Delay is us after which this function shall get executed
+ * @name: Cyclic function name/id
+ * @ctx: Context to pass to the function
+ * @return: pointer to cyclic_struct if OK, NULL on error
+ */
+struct cyclic_struct *cyclic_register(cyclic_func_t func, uint64_t delay_us,
+				      const char *name, void *ctx);
+
+/**
+ * cyclic_unregister - Unregister a cyclic function
+ *
+ * @cyclic: Pointer to cyclic_struct of the function that shall be removed
+ * @return: 0 if OK, -ve on error
+ */
+int cyclic_unregister(struct cyclic_struct *cyclic);
+
+#if defined(CONFIG_CYCLIC)
+/**
+ * cyclic_init() - Set up cyclic functions
+ *
+ * Init a list of cyclic functions, so that these can be added as needed
+ */
+int cyclic_init(void);
+
+/**
+ * cyclic_uninit() - Clean up cyclic functions
+ *
+ * This removes all cyclic functions
+ */
+int cyclic_uninit(void);
+
+void cyclic_run(void);
+#else
+static inline void cyclic_run(void)
+{
+}
+
+static inline int cyclic_init(void)
+{
+	return 0;
+}
+
+static inline int cyclic_uninit(void)
+{
+	return 0;
+}
+#endif
+
+#endif
