aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/hw/fsp/fsp-epow.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/hw/fsp/fsp-epow.c')
-rw-r--r--roms/skiboot/hw/fsp/fsp-epow.c192
1 files changed, 192 insertions, 0 deletions
diff --git a/roms/skiboot/hw/fsp/fsp-epow.c b/roms/skiboot/hw/fsp/fsp-epow.c
new file mode 100644
index 000000000..8869e91e6
--- /dev/null
+++ b/roms/skiboot/hw/fsp/fsp-epow.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * FSP Environmental and Power Warnings (EPOW) support
+ *
+ * Copyright 2013-2016 IBM Corp.
+ */
+
+#define pr_fmt(fmt) "FSP-EPOW: " fmt
+
+#include <fsp.h>
+#include <device.h>
+#include <lock.h>
+#include <opal-msg.h>
+#include <opal-api.h>
+
+#include "fsp-epow.h"
+
+/*
+ * System EPOW status
+ *
+ * This value is exported to the host. Each individual element in this
+ * array [0...(OPAL_SYSEPOW_MAX-1)] contains bitwise EPOW event info
+ * corresponding to particular defined EPOW sub class. For example.
+ * opal_epow_status[OPAL_SYSEPOW_POWER] will reflect power related EPOW events.
+ */
+static int16_t epow_status[OPAL_SYSEPOW_MAX];
+
+/* EPOW lock */
+static struct lock epow_lock = LOCK_UNLOCKED;
+
+/* Process FSP sent EPOW based information */
+static void epow_process_ex1_event(u8 *epow)
+{
+ memset(epow_status, 0, sizeof(epow_status));
+
+ if (epow[4] == EPOW_TMP_INT) {
+ prlog(PR_INFO, "Internal temp above normal\n");
+ epow_status[OPAL_SYSEPOW_TEMP] = OPAL_SYSTEMP_INT;
+
+ } else if (epow[4] == EPOW_TMP_AMB) {
+ prlog(PR_INFO, "Ambient temp above normal\n");
+ epow_status[OPAL_SYSEPOW_TEMP] = OPAL_SYSTEMP_AMB;
+
+ } else if (epow[4] == EPOW_ON_UPS) {
+ prlog(PR_INFO, "System running on UPS power\n");
+ epow_status[OPAL_SYSEPOW_POWER] = OPAL_SYSPOWER_UPS;
+
+ }
+}
+
+/* Process EPOW event */
+static void fsp_process_epow(struct fsp_msg *msg, int epow_type)
+{
+ int rc;
+ u8 epow[8];
+ bool epow_changed = false;
+ int16_t old_epow_status[OPAL_SYSEPOW_MAX];
+
+ /* Basic EPOW signature */
+ if (msg->data.bytes[0] != 0xF2) {
+ /**
+ * @fwts-label EPOWSignatureMismatch
+ * @fwts-advice Bug in skiboot/FSP code for EPOW event handling
+ */
+ prlog(PR_ERR, "Signature mismatch\n");
+ return;
+ }
+
+ lock(&epow_lock);
+
+ /* Copy over and clear system EPOW status */
+ memcpy(old_epow_status, epow_status, sizeof(old_epow_status));
+
+ switch(epow_type) {
+ case EPOW_NORMAL:
+ case EPOW_EX2:
+ break;
+ case EPOW_EX1:
+ epow[0] = msg->data.bytes[0];
+ epow[1] = msg->data.bytes[1];
+ epow[2] = msg->data.bytes[2];
+ epow[3] = msg->data.bytes[3];
+ epow[4] = msg->data.bytes[4];
+
+ epow_process_ex1_event(epow);
+ break;
+ default:
+ prlog(PR_WARNING, "Unknown EPOW event notification\n");
+ break;
+ }
+
+ if (memcmp(epow_status, old_epow_status, sizeof(epow_status)))
+ epow_changed = true;
+
+ unlock(&epow_lock);
+
+ /* Send OPAL message notification */
+ if (epow_changed) {
+ rc = opal_queue_msg(OPAL_MSG_EPOW, NULL, NULL);
+ if (rc) {
+ /**
+ * @fwts-label EPOWMessageQueueFailed
+ * @fwts-advice Queueing a message from OPAL to FSP
+ * failed. This is likely due to either an OPAL bug
+ * or the FSP going away.
+ */
+ prlog(PR_ERR, "OPAL EPOW message queuing failed\n");
+ return;
+ }
+ prlog(PR_INFO, "Notified host about EPOW event\n");
+ }
+}
+
+/*
+ * EPOW OPAL interface
+ *
+ * The host requests for the system EPOW status through this
+ * OPAl call, where it passes a buffer with a give length.
+ * Sapphire fills the buffer with updated system EPOW status
+ * and then updates the length variable back to reflect the
+ * number of EPOW sub classes it has updated the buffer with.
+ */
+static int64_t fsp_opal_get_epow_status(__be16 *out_epow, __be16 *length)
+{
+ int i;
+ int n_epow_class;
+ int l = be16_to_cpu(*length);
+
+ /*
+ * There can be situations where the host and the Sapphire versions
+ * don't match with eact other and hence the expected system EPOW status
+ * details. Newer hosts might be expecting status for more number of EPOW
+ * sub classes which Sapphire may not know about and older hosts might be
+ * expecting status for EPOW sub classes which is a subset of what
+ * Sapphire really knows about. Both these situations are handled here.
+ *
+ * (A) Host version >= Sapphire version
+ *
+ * Sapphire sends out EPOW status for sub classes it knows about
+ * and keeps the status. Updates the length variable for the host.
+ *
+ * (B) Host version < Sapphire version
+ *
+ * Sapphire sends out EPOW status for sub classes host knows about
+ * and can interpret correctly.
+ */
+ if (l >= OPAL_SYSEPOW_MAX) {
+ n_epow_class = OPAL_SYSEPOW_MAX;
+ *length = cpu_to_be16(OPAL_SYSEPOW_MAX);
+ } else {
+ n_epow_class = l;
+ }
+
+ /* Transfer EPOW Status */
+ for (i = 0; i < n_epow_class; i++)
+ out_epow[i] = cpu_to_be16(epow_status[i]);
+
+ return OPAL_SUCCESS;
+}
+
+/* Handle EPOW sub-commands from FSP */
+static bool fsp_epow_message(u32 cmd_sub_mod, struct fsp_msg *msg)
+{
+ switch(cmd_sub_mod) {
+ case FSP_CMD_PANELSTATUS:
+ fsp_process_epow(msg, EPOW_NORMAL);
+ return true;
+ case FSP_CMD_PANELSTATUS_EX1:
+ fsp_process_epow(msg, EPOW_EX1);
+ return true;
+ case FSP_CMD_PANELSTATUS_EX2:
+ fsp_process_epow(msg, EPOW_EX2);
+ return true;
+ }
+ return false;
+}
+
+static struct fsp_client fsp_epow_client = {
+ .message = fsp_epow_message,
+};
+
+void fsp_epow_init(void)
+{
+ struct dt_node *np;
+
+ fsp_register_client(&fsp_epow_client, FSP_MCLASS_SERVICE);
+ opal_register(OPAL_GET_EPOW_STATUS, fsp_opal_get_epow_status, 2);
+ np = dt_new(opal_node, "epow");
+ dt_add_property_strings(np, "compatible", "ibm,opal-v3-epow");
+ dt_add_property_strings(np, "epow-classes", "power", "temperature", "cooling");
+ prlog(PR_INFO, "FSP EPOW support initialized\n");
+}