blob: 91919f915509a5072d97c7ca1613e70da8bffa25 [file] [log] [blame]
// 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");
}