1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 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");
}
|