aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/hw/fsp/fsp-dpo.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/hw/fsp/fsp-dpo.c')
-rw-r--r--roms/skiboot/hw/fsp/fsp-dpo.c154
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");
+}