--- /dev/null
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ventana Micro Systems Inc.
+ *
+ * Authors:
+ * Anup Patel <apatel@ventanamicro.com>
+ */
+
+#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,
+};