diff options
Diffstat (limited to 'roms/skiboot/hw/fsp/fsp-epow.c')
-rw-r--r-- | roms/skiboot/hw/fsp/fsp-epow.c | 192 |
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"); +} |