203 lines
5.5 KiB
C
203 lines
5.5 KiB
C
/* Copyright 2013-2014 IBM Corp.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
* implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/* FSP Environmental and Power Warnings (EPOW) support */
|
|
|
|
#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(int16_t *out_epow,
|
|
int16_t *length)
|
|
{
|
|
int i;
|
|
int n_epow_class;
|
|
|
|
/*
|
|
* 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 (*length >= OPAL_SYSEPOW_MAX) {
|
|
n_epow_class = OPAL_SYSEPOW_MAX;
|
|
*length = OPAL_SYSEPOW_MAX;
|
|
} else {
|
|
n_epow_class = *length;
|
|
}
|
|
|
|
/* Transfer EPOW Status */
|
|
for (i = 0; i < n_epow_class; i++)
|
|
out_epow[i] = 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");
|
|
}
|