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