/* 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 #include #include #include #include #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) { 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) { 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 * dont 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"); }