aboutsummaryrefslogtreecommitdiff
path: root/hw/fsp/fsp-epow.c
blob: 8869e91e61905980494cde57caf74f980a60db90 (plain)
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");
}