diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig index 4748cc8..c0b9640 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 0000000..bae9ec5 --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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 c9f851c..17287a5 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 diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig index b767290..5008c7e 100644 --- a/platform/generic/configs/defconfig +++ b/platform/generic/configs/defconfig @@ -26,6 +26,7 @@ CONFIG_FDT_RESET_GPIO=y CONFIG_FDT_RESET_HTIF=y CONFIG_FDT_RESET_SIFIVE_TEST=y CONFIG_FDT_RESET_SUNXI_WDT=y +CONFIG_FDT_RESET_SYSCON=y CONFIG_FDT_RESET_THEAD=y CONFIG_FDT_SERIAL=y CONFIG_FDT_SERIAL_CADENCE=y