diff options
Diffstat (limited to 'roms/skiboot/hw/fsp/fsp-dpo.c')
-rw-r--r-- | roms/skiboot/hw/fsp/fsp-dpo.c | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/roms/skiboot/hw/fsp/fsp-dpo.c b/roms/skiboot/hw/fsp/fsp-dpo.c new file mode 100644 index 000000000..91919f915 --- /dev/null +++ b/roms/skiboot/hw/fsp/fsp-dpo.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* + * FSP DPO (Delayed Power Off) event support + * + * Copyright 2013-2017 IBM Corp. + */ + +#define pr_fmt(fmt) "FSP-DPO: " fmt + +#include <skiboot.h> +#include <fsp.h> +#include <stdio.h> +#include <timebase.h> +#include <opal.h> +#include <opal-msg.h> + +#define DPO_CMD_SGN_BYTE0 0xf4 /* Byte[0] signature */ +#define DPO_CMD_SGN_BYTE1 0x20 /* Byte[1] signature */ +#define DPO_TIMEOUT 2700 /* 45 minutes in seconds */ + +bool fsp_dpo_pending; +static unsigned long fsp_dpo_init_tb; + +/* + * OPAL DPO interface + * + * Returns zero if DPO is not active, positive value indicating number + * of seconds remaining for a forced system shutdown. This will enable + * the host to schedule for shutdown voluntarily before timeout occurs. + */ +static int64_t fsp_opal_get_dpo_status(__be64 *dpo_timeout) +{ + if (!fsp_dpo_pending) { + *dpo_timeout = 0; + return OPAL_WRONG_STATE; + } + + *dpo_timeout = cpu_to_be64(DPO_TIMEOUT - tb_to_secs(mftb() - fsp_dpo_init_tb)); + return OPAL_SUCCESS; +} + +/* Process FSP DPO init message */ +static void fsp_process_dpo(struct fsp_msg *msg) +{ + struct fsp_msg *resp; + u32 cmd = FSP_RSP_INIT_DPO; + int rc; + + /* DPO message does not have the correct signatures */ + if ((msg->data.bytes[0] != DPO_CMD_SGN_BYTE0) + || (msg->data.bytes[1] != DPO_CMD_SGN_BYTE1)) { + prerror("Message signatures did not match\n"); + cmd |= FSP_STATUS_INVALID_CMD; + resp = fsp_mkmsg(cmd, 0); + if (resp == NULL) { + prerror("%s : Message allocation failed\n", __func__); + return; + } + if (fsp_queue_msg(resp, fsp_freemsg)) { + fsp_freemsg(resp); + prerror("%s : Failed to queue response " + "message\n", __func__); + } + return; + } + + /* OPAL is already in "DPO pending" state */ + if (fsp_dpo_pending) { + prlog(PR_INFO, "OPAL already in DPO pending state\n"); + cmd |= FSP_STATUS_INVALID_DPOSTATE; + resp = fsp_mkmsg(cmd, 0); + if (resp == NULL) { + prerror("%s : Message allocation failed\n", __func__); + return; + } + if (fsp_queue_msg(resp, fsp_freemsg)) { + fsp_freemsg(resp); + prerror("%s : Failed to queue response " + "message\n", __func__); + } + return; + } + + + /* Inform the host about DPO */ + rc = opal_queue_msg(OPAL_MSG_DPO, NULL, NULL); + if (rc) { + prerror("OPAL message queuing failed\n"); + cmd |= FSP_STATUS_GENERIC_ERROR; + resp = fsp_mkmsg(cmd, 0); + if (resp == NULL) { + prerror("%s : Message allocation failed\n", __func__); + return; + } + if (fsp_queue_msg(resp, fsp_freemsg)) { + fsp_freemsg(resp); + prerror("%s : Failed to queue response " + "message\n", __func__); + } + return; + } else + prlog(PR_INFO, "Notified host about DPO event\n"); + + /* Acknowledge the FSP on DPO */ + resp = fsp_mkmsg(cmd, 0); + if (resp == NULL) { + prerror("%s : Message allocation failed\n", __func__); + return; + } + if (fsp_queue_msg(resp, fsp_freemsg)) { + fsp_freemsg(resp); + prerror("%s : Failed to queue response message\n", __func__); + return; + } + + /* Record DPO init time and set DPO pending flag */ + fsp_dpo_init_tb = mftb(); + fsp_dpo_pending = true; + + /* + * OPAL is now in DPO pending state. After first detecting DPO + * condition from OPAL, the host will have 45 minutes to prepare + * the system for shutdown. The host must take all necessary actions + * required in that regard and at the end shutdown itself. The host + * shutdown sequence eventually will make the call OPAL_CEC_POWER_DOWN + * which in turn ask the FSP to shutdown the CEC. If the FSP does not + * receive the cec power down command from OPAL within 45 minutes, + * it will assume that the host and the OPAL has processed the DPO + * sequence successfully and hence force power off the system. + */ +} + +/* Handle DPO sub-command from FSP */ +static bool fsp_dpo_message(u32 cmd_sub_mod, struct fsp_msg *msg) +{ + if (cmd_sub_mod == FSP_CMD_INIT_DPO) { + prlog(PR_INFO, "Delayed Power Off (DPO) notification received\n"); + fsp_process_dpo(msg); + return true; + } + + return false; +} + +static struct fsp_client fsp_dpo_client = { + .message = fsp_dpo_message, +}; + +void fsp_dpo_init(void) +{ + fsp_register_client(&fsp_dpo_client, FSP_MCLASS_SERVICE); + opal_register(OPAL_GET_DPO_STATUS, fsp_opal_get_dpo_status, 1); + prlog(PR_INFO, "FSP DPO support initialized\n"); +} |