summaryrefslogtreecommitdiff
path: root/lib/utils
diff options
context:
space:
mode:
Diffstat (limited to 'lib/utils')
-rw-r--r--lib/utils/reset/Kconfig5
-rw-r--r--lib/utils/reset/fdt_reset_syscon.c167
-rw-r--r--lib/utils/reset/objects.mk4
3 files changed, 176 insertions, 0 deletions
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index 4748cc85..c0b96406 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -33,6 +33,11 @@ config FDT_RESET_SUNXI_WDT
bool "Sunxi WDT FDT reset driver"
default n
+config FDT_RESET_SYSCON
+ bool "Syscon FDT reset driver"
+ depends on FDT_REGMAP
+ default n
+
config FDT_RESET_THEAD
bool "T-HEAD FDT reset driver"
default n
diff --git a/lib/utils/reset/fdt_reset_syscon.c b/lib/utils/reset/fdt_reset_syscon.c
new file mode 100644
index 00000000..bae9ec58
--- /dev/null
+++ b/lib/utils/reset/fdt_reset_syscon.c
@@ -0,0 +1,167 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ventana Micro Systems Inc.
+ *
+ * Authors:
+ * Anup Patel <[email protected]>
+ */
+
+#include <libfdt.h>
+#include <sbi/sbi_ecall_interface.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_system.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/regmap/fdt_regmap.h>
+#include <sbi_utils/reset/fdt_reset.h>
+
+struct syscon_reset {
+ struct regmap *rmap;
+ u32 priority;
+ u32 offset;
+ u32 value;
+ u32 mask;
+};
+
+static struct syscon_reset poweroff;
+static struct syscon_reset reboot;
+
+static struct syscon_reset *syscon_reset_get(bool is_reboot, u32 type)
+{
+ struct syscon_reset *reset = NULL;
+
+ switch (type) {
+ case SBI_SRST_RESET_TYPE_SHUTDOWN:
+ if (!is_reboot)
+ reset = &poweroff;
+ break;
+ case SBI_SRST_RESET_TYPE_COLD_REBOOT:
+ case SBI_SRST_RESET_TYPE_WARM_REBOOT:
+ if (is_reboot)
+ reset = &reboot;
+ break;
+ }
+
+ if (reset && !reset->rmap)
+ reset = NULL;
+
+ return reset;
+}
+
+static void syscon_reset_exec(struct syscon_reset *reset)
+{
+ /* Issue the reset through regmap */
+ if (reset)
+ regmap_update_bits(reset->rmap, reset->offset,
+ reset->mask, reset->value);
+
+ /* hang !!! */
+ sbi_hart_hang();
+}
+
+static int syscon_poweroff_check(u32 type, u32 reason)
+{
+ struct syscon_reset *reset = syscon_reset_get(false, type);
+
+ return (reset) ? reset->priority : 0;
+}
+
+static void syscon_do_poweroff(u32 type, u32 reason)
+{
+ syscon_reset_exec(syscon_reset_get(false, type));
+}
+
+static struct sbi_system_reset_device syscon_poweroff = {
+ .name = "syscon-poweroff",
+ .system_reset_check = syscon_poweroff_check,
+ .system_reset = syscon_do_poweroff
+};
+
+static int syscon_reboot_check(u32 type, u32 reason)
+{
+ struct syscon_reset *reset = syscon_reset_get(true, type);
+
+ return (reset) ? reset->priority : 0;
+}
+
+static void syscon_do_reboot(u32 type, u32 reason)
+{
+ syscon_reset_exec(syscon_reset_get(true, type));
+}
+
+static struct sbi_system_reset_device syscon_reboot = {
+ .name = "syscon-reboot",
+ .system_reset_check = syscon_reboot_check,
+ .system_reset = syscon_do_reboot
+};
+
+static int syscon_reset_init(void *fdt, int nodeoff,
+ const struct fdt_match *match)
+{
+ int rc, len;
+ const fdt32_t *val, *mask;
+ bool is_reboot = (ulong)match->data;
+ struct syscon_reset *reset = (is_reboot) ? &reboot : &poweroff;
+
+ if (reset->rmap)
+ return SBI_EALREADY;
+
+ rc = fdt_regmap_get(fdt, nodeoff, &reset->rmap);
+ if (rc)
+ return rc;
+
+ val = fdt_getprop(fdt, nodeoff, "priority", &len);
+ reset->priority = (val && len > 0) ? fdt32_to_cpu(*val) : 192;
+
+ val = fdt_getprop(fdt, nodeoff, "offset", &len);
+ if (val && len > 0)
+ reset->offset = fdt32_to_cpu(*val);
+ else
+ return SBI_EINVAL;
+
+ val = fdt_getprop(fdt, nodeoff, "value", &len);
+ mask = fdt_getprop(fdt, nodeoff, "mask", &len);
+ if (!val && !mask)
+ return SBI_EINVAL;
+
+ if (!val) {
+ /* support old binding */
+ reset->value = fdt32_to_cpu(*mask);
+ reset->mask = 0xFFFFFFFF;
+ } else if (!mask) {
+ /* support value without mask */
+ reset->value = fdt32_to_cpu(*val);
+ reset->mask = 0xFFFFFFFF;
+ } else {
+ reset->value = fdt32_to_cpu(*val);
+ reset->mask = fdt32_to_cpu(*mask);
+ }
+
+ if (is_reboot)
+ sbi_system_reset_add_device(&syscon_reboot);
+ else
+ sbi_system_reset_add_device(&syscon_poweroff);
+
+ return 0;
+}
+
+static const struct fdt_match syscon_poweroff_match[] = {
+ { .compatible = "syscon-poweroff", .data = (const void *)false },
+ { },
+};
+
+struct fdt_reset fdt_syscon_poweroff = {
+ .match_table = syscon_poweroff_match,
+ .init = syscon_reset_init,
+};
+
+static const struct fdt_match syscon_reboot_match[] = {
+ { .compatible = "syscon-reboot", .data = (const void *)true },
+ { },
+};
+
+struct fdt_reset fdt_syscon_reboot = {
+ .match_table = syscon_reboot_match,
+ .init = syscon_reset_init,
+};
diff --git a/lib/utils/reset/objects.mk b/lib/utils/reset/objects.mk
index c9f851cd..17287a5e 100644
--- a/lib/utils/reset/objects.mk
+++ b/lib/utils/reset/objects.mk
@@ -26,6 +26,10 @@ libsbiutils-objs-$(CONFIG_FDT_RESET_SIFIVE_TEST) += reset/fdt_reset_sifive_test.
carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SUNXI_WDT) += fdt_reset_sunxi_wdt
libsbiutils-objs-$(CONFIG_FDT_RESET_SUNXI_WDT) += reset/fdt_reset_sunxi_wdt.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SYSCON) += fdt_syscon_poweroff
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SYSCON) += fdt_syscon_reboot
+libsbiutils-objs-$(CONFIG_FDT_RESET_SYSCON) += reset/fdt_reset_syscon.o
+
carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_THEAD) += fdt_reset_thead
libsbiutils-objs-$(CONFIG_FDT_RESET_THEAD) += reset/fdt_reset_thead.o
libsbiutils-objs-$(CONFIG_FDT_RESET_THEAD) += reset/fdt_reset_thead_asm.o