diff mbox

[21/22] FSP/LEDS: Add OPAL interfaces for accessing or modifying the LED states

Message ID 20150205084300.12859.87451.stgit@localhost.localdomain
State Changes Requested
Headers show

Commit Message

Vasant Hegde Feb. 5, 2015, 8:43 a.m. UTC
From: Anshuman Khandual <khandual@linux.vnet.ibm.com>

This patch adds and registers the following two new OPAL interfaces
for the LED subsystem. With the help of these new OPAL calls, the
host will be able to set or query the state of various LEDs on the
system at any given location code (as passed in the device tree).
As of now, it supports two kinds of LEDs on the same location code
one is "Identify LED" and the other being the "Fault LED".

	(1) OPAL_LEDS_GET_INDICATOR     fsp_opal_leds_get_ind
	(2) OPAL_LEDS_SET_INDICATOR     fsp_opal_leds_set_ind

The signtaure of these OPAL interfaces are like the following

(1) fsp_opal_leds_get_ind(loc_code, led_mask, led_value, led_max_type)

	The host will pass the location code of the LED (loc_code) and
maximum number of LED types it understands (led_max_type). Sapphire
updates the led_mask with set bits pointing to LED types whose status
is availbale and updates the led_value with actual status. Sapphire
checks the led_max_type to understand whether host is newer or older
compared to itself. In the case where the Sapphire is newer compared
to host (Sapphire's led_max_type > host's led_max_type), it will update
led_mask and led_value according to max_led_type requested by the host.
When the host is newer compared to the Sapphire (host's led_max_type >
Sapphire's led_max_type), Sapphire updates led_max_type to the maximum
number of LED type it understands and updates led_mask, led_value based
on that maximum value.

(2) fsp_opal_leds_set_ind(loc_code, led_mask, led_value, led_max_type)

	The host will pass the location code of the LED types, mask,
value and maximum number of LED types it understands. Sapphire will
update LED status for all the LED types mentioned in the mask with
their value mentioned. Sapphire checks the 'led_max_type' to understand
whether the host is newer or older compared to itself. In case where
the Sapphire is newer compared to the host (Sapphire's led_max_type >
host's led_max_type), it updates LED status based on max_led_type
requested from the host. When the host is newer compared to the Sapphire
(host's led_max_type > Sapphire's led_max_type), Sapphire updates
'led_max_type' to the maximum number of LED type it understands and
then it updates LED status based on that updated  maximum value of LED
types. Host needs to check the returned updated value of max_led_type
to figure out which part of it's request got served and which ones got
ignored.


Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
---
 hw/fsp/fsp-leds.c |  229 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 hw/fsp/fsp-leds.h |    1 
 include/opal.h    |   16 ++--
 3 files changed, 233 insertions(+), 13 deletions(-)

Comments

Stewart Smith Feb. 18, 2015, 12:30 a.m. UTC | #1
Vasant Hegde <hegdevasant@linux.vnet.ibm.com> writes:
> From: Anshuman Khandual <khandual@linux.vnet.ibm.com>
>
> This patch adds and registers the following two new OPAL interfaces
> for the LED subsystem. With the help of these new OPAL calls, the
> host will be able to set or query the state of various LEDs on the
> system at any given location code (as passed in the device tree).
> As of now, it supports two kinds of LEDs on the same location code
> one is "Identify LED" and the other being the "Fault LED".
>
> 	(1) OPAL_LEDS_GET_INDICATOR     fsp_opal_leds_get_ind
> 	(2) OPAL_LEDS_SET_INDICATOR     fsp_opal_leds_set_ind
>
> The signtaure of these OPAL interfaces are like the following
>
> (1) fsp_opal_leds_get_ind(loc_code, led_mask, led_value, led_max_type)
>
> 	The host will pass the location code of the LED (loc_code) and
> maximum number of LED types it understands (led_max_type). Sapphire
> updates the led_mask with set bits pointing to LED types whose status
> is availbale and updates the led_value with actual status. Sapphire
> checks the led_max_type to understand whether host is newer or older
> compared to itself. In the case where the Sapphire is newer compared
> to host (Sapphire's led_max_type > host's led_max_type), it will update
> led_mask and led_value according to max_led_type requested by the host.
> When the host is newer compared to the Sapphire (host's led_max_type >
> Sapphire's led_max_type), Sapphire updates led_max_type to the maximum
> number of LED type it understands and updates led_mask, led_value based
> on that maximum value.
>
> (2) fsp_opal_leds_set_ind(loc_code, led_mask, led_value, led_max_type)
>
> 	The host will pass the location code of the LED types, mask,
> value and maximum number of LED types it understands. Sapphire will
> update LED status for all the LED types mentioned in the mask with
> their value mentioned. Sapphire checks the 'led_max_type' to understand
> whether the host is newer or older compared to itself. In case where
> the Sapphire is newer compared to the host (Sapphire's led_max_type >
> host's led_max_type), it updates LED status based on max_led_type
> requested from the host. When the host is newer compared to the Sapphire
> (host's led_max_type > Sapphire's led_max_type), Sapphire updates
> 'led_max_type' to the maximum number of LED type it understands and
> then it updates LED status based on that updated  maximum value of LED
> types. Host needs to check the returned updated value of max_led_type
> to figure out which part of it's request got served and which ones got
> ignored.

Please add API docs to doc/opal-api/
diff mbox

Patch

diff --git a/hw/fsp/fsp-leds.c b/hw/fsp/fsp-leds.c
index ba9e3bf..1353b78 100644
--- a/hw/fsp/fsp-leds.c
+++ b/hw/fsp/fsp-leds.c
@@ -25,6 +25,7 @@ 
 #include <lock.h>
 #include <errorlog.h>
 #include <opal.h>
+#include <opal-msg.h>
 
 #include "fsp-leds.h"
 
@@ -81,6 +82,7 @@  static int replay = 0;
 static void fsp_read_leds_data_complete(struct fsp_msg *msg);
 static int process_led_state_change(void);
 
+
 DEFINE_LOG_ENTRY(OPAL_RC_LED_SPCN, OPAL_PLATFORM_ERR_EVT, OPAL_LED,
 		OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL,
 		OPAL_NA, NULL);
@@ -99,6 +101,7 @@  DEFINE_LOG_ENTRY(OPAL_RC_LED_STATE, OPAL_PLATFORM_ERR_EVT, OPAL_LED,
 DEFINE_LOG_ENTRY(OPAL_RC_LED_SUPPORT, OPAL_PLATFORM_ERR_EVT, OPAL_LED,
 		OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA, NULL);
 
+
 /* Find descendent LED record with CEC location code in CEC list */
 static struct fsp_led_data *fsp_find_cec_led(char *loc_code)
 {
@@ -187,6 +190,11 @@  static bool is_enclosure_led(char *loc_code)
 	return true;
 }
 
+static inline void opal_led_update_complete(u64 async_token, u64 result)
+{
+	opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, async_token, result);
+}
+
 /*
  * Update both the local LED lists to reflect upon led state changes
  * occured with the recent SPCN command. Subsequent LED requests will
@@ -284,10 +292,9 @@  static void fsp_spcn_set_led_completion(struct fsp_msg *msg)
 
 	/*
 	 * LED state update request came as part of FSP async message
-	 * FSP_CMD_SET_LED_STATE, hence need to send response message.
+	 * FSP_CMD_SET_LED_STATE, we need to send response message.
 	 *
-	 * Also if SPCN command failed, then identify the command and
-	 * roll back changes.
+	 * Also if SPCN command failed, then roll back changes.
 	 */
 	if (status != FSP_STATUS_SUCCESS) {
 		log_simple_error(&e_info(OPAL_RC_LED_SPCN),
@@ -303,6 +310,16 @@  static void fsp_spcn_set_led_completion(struct fsp_msg *msg)
 	if (spcn_cmd->cmd_src == SPCN_SRC_FSP)
 		fsp_set_led_response(cmd);
 
+	/* OPAL initiated SPCN command */
+	if (spcn_cmd->cmd_src == SPCN_SRC_OPAL) {
+		if (status != FSP_STATUS_SUCCESS)
+			opal_led_update_complete(spcn_cmd->async_token,
+						 OPAL_INTERNAL_ERROR);
+		else
+			opal_led_update_complete(spcn_cmd->async_token,
+						 OPAL_SUCCESS);
+	}
+
 	unlock(&led_lock);
 
 	free_spcn_cmd(spcn_cmd);
@@ -359,6 +376,10 @@  static int fsp_msg_set_led_state(struct led_set_cmd *spcn_cmd)
 			fsp_set_led_response(cmd);
 		}
 
+		if (spcn_cmd->cmd_src == SPCN_SRC_OPAL)
+			opal_led_update_complete(spcn_cmd->async_token,
+						 OPAL_INTERNAL_ERROR);
+
 		unlock(&led_lock);
 		free_spcn_cmd(spcn_cmd);
 		return rc;
@@ -443,6 +464,10 @@  update_fail:
 
 		if (spcn_cmd->cmd_src == SPCN_SRC_FSP)
 			fsp_set_led_response(cmd);
+
+		if (spcn_cmd->cmd_src == SPCN_SRC_OPAL)
+			opal_led_update_complete(spcn_cmd->async_token,
+						 OPAL_INTERNAL_ERROR);
 	}
 
 	unlock(&led_lock);
@@ -492,7 +517,7 @@  static int process_led_state_change(void)
  * in the command queue it sets 'spcn_cmd_cmplt' as true again.
  */
 static int queue_led_state_change(char *loc_code, u8 command,
-				  u8 state, int cmd_src)
+				  u8 state, int cmd_src, uint64_t async_token)
 {
 	struct led_set_cmd *cmd;
 	int rc = 0;
@@ -510,6 +535,7 @@  static int queue_led_state_change(char *loc_code, u8 command,
 	cmd->command = command;
 	cmd->state = state;
 	cmd->cmd_src = cmd_src;
+	cmd->async_token = async_token;
 
 	/* Add to the queue */
 	lock(&spcn_cmd_lock);
@@ -929,7 +955,7 @@  static void fsp_set_led_state(struct fsp_msg *msg)
 				continue;
 
 			rc = queue_led_state_change(led->loc_code, command,
-						    state, SPCN_SRC_FSP);
+						    state, SPCN_SRC_FSP, 0);
 			if (rc != 0)
 				fsp_set_led_response(FSP_RSP_SET_LED_STATE |
 						     FSP_STATUS_GENERIC_ERROR);
@@ -938,7 +964,7 @@  static void fsp_set_led_state(struct fsp_msg *msg)
 	case SET_IND_SINGLE_LOC_CODE:
 		/* Set led state for single descendent led */
 		rc = queue_led_state_change(req.loc_code,
-					    command, state, SPCN_SRC_FSP);
+					    command, state, SPCN_SRC_FSP, 0);
 		if (rc != 0)
 			fsp_set_led_response(FSP_RSP_SET_LED_STATE |
 					     FSP_STATUS_GENERIC_ERROR);
@@ -1064,6 +1090,193 @@  static struct fsp_client fsp_indicator_client = {
 	.message = fsp_indicator_message,
 };
 
+
+/*
+ * fsp_opal_leds_get_ind (OPAL_LEDS_GET_INDICATOR)
+ *
+ * Argument	 Description				Updated By
+ * --------	 -----------				----------
+ * loc_code	 Location code of the LEDs		(Host)
+ * led_mask	 LED types whose status is available	(OPAL)
+ * led_value	 Status of the available LED types	(OPAL)
+ * max_led_type  Maximum number of supported LED types	(Host/OPAL)
+ *
+ * The host will pass the location code of the LED types (loc_code) and
+ * maximum number of LED types it understands (max_led_type). OPAL will
+ * update the 'led_mask' with set bits pointing to LED types whose status
+ * is available and updates the 'led_value' with actual status. OPAL checks
+ * the 'max_led_type' to understand whether the host is newer or older
+ * compared to itself. In the case where the OPAL is newer compared
+ * to host (OPAL's max_led_type > host's max_led_type), it will update
+ * led_mask and led_value according to max_led_type requested by the host.
+ * When the host is newer compared to the OPAL (host's max_led_type >
+ * OPAL's max_led_type), OPAL updates 'max_led_type' to the maximum
+ * number of LED type it understands and updates 'led_mask', 'led_value'
+ * based on that maximum value of LED types.
+ */
+static int64_t fsp_opal_leds_get_ind(char *loc_code, u64 *led_mask,
+				     u64 *led_value, u64 *max_led_type)
+{
+	bool supported = true;
+	int64_t max;
+	struct fsp_led_data *led;
+
+	/* FSP not present */
+	if (!fsp_present())
+		return OPAL_HARDWARE;
+
+	/* LED support not available */
+	if (led_support != LED_STATE_PRESENT)
+		return OPAL_HARDWARE;
+
+	/* Adjust max LED type */
+	if (*max_led_type > OPAL_SLOT_LED_TYPE_MAX) {
+		supported = false;
+		*max_led_type = OPAL_SLOT_LED_TYPE_MAX;
+	}
+
+	/* Invalid parameter */
+	max = *max_led_type;
+	if (max <= 0)
+		return OPAL_PARAMETER;
+
+	/* LED not found */
+	led = fsp_find_cec_led(loc_code);
+	if (!led)
+		return OPAL_PARAMETER;
+
+	*led_mask = 0;
+	*led_value = 0;
+
+	/* Identify LED */
+	--max;
+	*led_mask |= OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_ID;
+	if (led->status & SPCN_LED_IDENTIFY_MASK)
+		*led_value |=
+			OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_ID;
+
+	/* Fault LED */
+	if (!max)
+		return OPAL_SUCCESS;
+
+	--max;
+	*led_mask |= OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_FAULT;
+	if (led->status & SPCN_LED_FAULT_MASK)
+		*led_value |=
+			OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_FAULT;
+
+	/* OPAL doesn't support all the LED type requested by payload */
+	if (!supported)
+		return OPAL_PARTIAL;
+
+	return OPAL_SUCCESS;
+}
+
+/*
+ * fsp_opal_leds_set_ind (OPAL_LEDS_SET_INDICATOR)
+ *
+ * Argument	 Description				Updated By
+ * --------	 -----------				----------
+ * loc_code	 Location code of the LEDs		(Host)
+ * led_mask	 LED types whose status will be updated	(Host)
+ * led_value	 Requested status of various LED types	(Host)
+ * max_led_type  Maximum number of supported LED types	(Host/OPAL)
+ *
+ * The host will pass the location code of the LED types, mask, value
+ * and maximum number of LED types it understands. OPAL will update
+ * LED status for all the LED types mentioned in the mask with their
+ * value mentioned. OPAL checks the 'max_led_type' to understand
+ * whether the host is newer or older compared to itself. In case where
+ * the OPAL is newer compared to the host (OPAL's max_led_type >
+ * host's max_led_type), it updates LED status based on max_led_type
+ * requested from the host. When the host is newer compared to the OPAL
+ * (host's max_led_type > OPAL's max_led_type), OPAL updates
+ * 'max_led_type' to the maximum number of LED type it understands and
+ * then it updates LED status based on that updated  maximum value of LED
+ * types. Host needs to check the returned updated value of max_led_type
+ * to figure out which part of it's request got served and which ones got
+ * ignored.
+ */
+static int64_t fsp_opal_leds_set_ind(uint64_t async_token,
+				     char *loc_code, const u64 led_mask,
+				     const u64 led_value, u64 *max_led_type)
+{
+	bool supported = true;
+	int command, state, rc = OPAL_SUCCESS;
+	int64_t max;
+	struct fsp_led_data *led;
+
+	/* FSP not present */
+	if (!fsp_present())
+		return OPAL_HARDWARE;
+
+	/* LED support not available */
+	if (led_support != LED_STATE_PRESENT)
+		return OPAL_HARDWARE;
+
+	/* Adjust max LED type */
+	if (*max_led_type > OPAL_SLOT_LED_TYPE_MAX) {
+		supported = false;
+		*max_led_type = OPAL_SLOT_LED_TYPE_MAX;
+	}
+
+	max = *max_led_type;
+	/* Invalid parameter */
+	if (max <= 0)
+		return OPAL_PARAMETER;
+
+	/* LED not found */
+	led = fsp_find_cec_led(loc_code);
+	if (!led)
+		return OPAL_PARAMETER;
+
+	/* Indentify LED mask */
+	--max;
+
+	if ((led_mask >> OPAL_SLOT_LED_TYPE_ID) & OPAL_SLOT_LED_STATE_ON) {
+		supported = true;
+
+		command = LED_COMMAND_IDENTIFY;
+		state = LED_STATE_OFF;
+		if ((led_value >> OPAL_SLOT_LED_TYPE_ID)
+					& OPAL_SLOT_LED_STATE_ON)
+			state = LED_STATE_ON;
+
+		rc = queue_led_state_change(loc_code, command,
+					    state, SPCN_SRC_OPAL, async_token);
+	}
+
+	if (!max)
+		goto success;
+
+	/* Fault LED mask */
+	--max;
+	if ((led_mask >> OPAL_SLOT_LED_TYPE_FAULT) & OPAL_SLOT_LED_STATE_ON) {
+		supported = true;
+
+		command = LED_COMMAND_FAULT;
+		state = LED_STATE_OFF;
+		if ((led_value >> OPAL_SLOT_LED_TYPE_FAULT)
+					& OPAL_SLOT_LED_STATE_ON)
+			state = LED_STATE_ON;
+
+		rc = queue_led_state_change(loc_code, command,
+					    state, SPCN_SRC_OPAL, async_token);
+	}
+
+success:
+	/* Unsupported LED type */
+	if (!supported)
+		return OPAL_UNSUPPORTED;
+
+	if (rc == OPAL_SUCCESS)
+		rc = OPAL_ASYNC_COMPLETION;
+	else
+		rc = OPAL_INTERNAL_ERROR;
+
+	return rc;
+}
+
 /*
  * create_led_device_node
  *
@@ -1443,4 +1656,8 @@  void fsp_led_init(void)
 	/* Handle FSP initiated async LED commands */
 	fsp_register_client(&fsp_indicator_client, FSP_MCLASS_INDICATOR);
 	prlog(PR_TRACE, PREFIX "FSP async command client registered\n");
+
+	opal_register(OPAL_LEDS_GET_INDICATOR, fsp_opal_leds_get_ind, 4);
+	opal_register(OPAL_LEDS_SET_INDICATOR, fsp_opal_leds_set_ind, 5);
+	prlog(PR_TRACE, PREFIX "LED OPAL interface registered\n");
 }
diff --git a/hw/fsp/fsp-leds.h b/hw/fsp/fsp-leds.h
index 98b0f01..8837104 100644
--- a/hw/fsp/fsp-leds.h
+++ b/hw/fsp/fsp-leds.h
@@ -120,6 +120,7 @@  struct led_set_cmd {
 	u8	command;
 	u8	state;
 	u16	ckpt_status;		/* Checkpointed status */
+	u64	async_token;		/* OPAL async token */
 	enum	spcn_cmd_src cmd_src;	/* OPAL or FSP based */
 	struct	list_node link;
 };
diff --git a/include/opal.h b/include/opal.h
index 6c9b13a..3b4eb5c 100644
--- a/include/opal.h
+++ b/include/opal.h
@@ -154,7 +154,9 @@ 
 #define OPAL_IPMI_SEND				107
 #define OPAL_IPMI_RECV				108
 #define OPAL_I2C_REQUEST			109
-#define OPAL_LAST				109
+#define OPAL_LEDS_GET_INDICATOR			110
+#define OPAL_LEDS_SET_INDICATOR			111
+#define OPAL_LAST				111
 
 #ifndef __ASSEMBLY__
 
@@ -356,14 +358,14 @@  enum OpalPciMaskAction {
 };
 
 enum OpalSlotLedType {
-	OPAL_SLOT_LED_ID_TYPE = 0,
-	OPAL_SLOT_LED_FAULT_TYPE = 1
+	OPAL_SLOT_LED_TYPE_ID = 0,	/* IDENTIFY LED */
+	OPAL_SLOT_LED_TYPE_FAULT = 1,	/* FAULT LED */
+	OPAL_SLOT_LED_TYPE_MAX = 2
 };
 
-enum OpalLedAction {
-	OPAL_TURN_OFF_LED = 0,
-	OPAL_TURN_ON_LED = 1,
-	OPAL_QUERY_LED_STATE_AFTER_BUSY = 2
+enum OpalSlotLedState {
+	OPAL_SLOT_LED_STATE_OFF = 0,	/* LED is OFF */
+	OPAL_SLOT_LED_STATE_ON = 1	/* LED is ON */
 };
 
 enum OpalEpowStatus {