/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2024 Ventana Micro Systems Inc. * * Authors: * Rahul Pathak */ #include #include #include #include #include #include #include #include /* struct rpmi_sysreset: RPMI System Reset Context */ struct rpmi_sysreset { int warm_reset_support; struct mbox_chan *chan; }; static struct rpmi_sysreset sysreset_ctx; static int rpmi_system_reset_type_check(u32 reset_type) { int ret; struct rpmi_sysrst_get_reset_attributes_resp resp; ret = rpmi_normal_request_with_status(sysreset_ctx.chan, RPMI_SYSRST_SRV_GET_ATTRIBUTES, &reset_type, rpmi_u32_count(reset_type), rpmi_u32_count(reset_type), &resp, rpmi_u32_count(resp), rpmi_u32_count(resp)); if (ret) { return 0; } return (resp.flags & RPMI_SYSRST_ATTRS_FLAGS_RESETTYPE_MASK) ? 1 : 0; } /** * rpmi_do_system_reset: Do system reset * * @reset_type: RPMI System Reset Type */ static void rpmi_do_system_reset(u32 reset_type) { int ret; ret = rpmi_posted_request(sysreset_ctx.chan, RPMI_SYSRST_SRV_SYSTEM_RESET, &reset_type, rpmi_u32_count(reset_type), rpmi_u32_count(reset_type)); if (ret) sbi_printf("system reset failed [type: %d]: ret: %d\n", reset_type, ret); } /** * rpmi_system_reset_check: Check the support for * various reset types * * @type: SBI System Reset Type * @reason: Reason for system reset */ static int rpmi_system_reset_check(u32 type, u32 reason) { switch (type) { case SBI_SRST_RESET_TYPE_SHUTDOWN: case SBI_SRST_RESET_TYPE_COLD_REBOOT: return 1; case SBI_SRST_RESET_TYPE_WARM_REBOOT: return sysreset_ctx.warm_reset_support; default: return 0; } } static void rpmi_system_reset(u32 type, u32 reason) { u32 reset_type; switch (type) { case SBI_SRST_RESET_TYPE_SHUTDOWN: reset_type = RPMI_SYSRST_TYPE_SHUTDOWN; break; case SBI_SRST_RESET_TYPE_COLD_REBOOT: reset_type = RPMI_SYSRST_TYPE_COLD_REBOOT; break; case SBI_SRST_RESET_TYPE_WARM_REBOOT: reset_type = RPMI_SYSRST_TYPE_WARM_REBOOT; break; default: return; } rpmi_do_system_reset(reset_type); } static struct sbi_system_reset_device rpmi_reset_dev = { .name = "rpmi-system-reset", .system_reset_check = rpmi_system_reset_check, .system_reset = rpmi_system_reset, }; static int rpmi_reset_init(const void *fdt, int nodeoff, const struct fdt_match *match) { int ret; /* If channel already available then do nothing. */ if (sysreset_ctx.chan) return 0; /* * If channel request failed then other end does not support * system reset group so do nothing. */ ret = fdt_mailbox_request_chan(fdt, nodeoff, 0, &sysreset_ctx.chan); if (ret) return SBI_ENODEV; sysreset_ctx.warm_reset_support = rpmi_system_reset_type_check(RPMI_SYSRST_TYPE_WARM_REBOOT); sbi_system_reset_add_device(&rpmi_reset_dev); return SBI_OK; } static const struct fdt_match rpmi_reset_match[] = { { .compatible = "riscv,rpmi-system-reset" }, {}, }; const struct fdt_driver fdt_reset_rpmi = { .match_table = rpmi_reset_match, .init = rpmi_reset_init, .experimental = true, };