diff options
-rw-r--r-- | doc/opal-api/opal-led-get-set-114-115.txt | 36 | ||||
-rw-r--r-- | hw/fsp/fsp-leds.c | 230 | ||||
-rw-r--r-- | hw/fsp/fsp-leds.h | 1 | ||||
-rw-r--r-- | include/opal.h | 16 |
4 files changed, 270 insertions, 13 deletions
diff --git a/doc/opal-api/opal-led-get-set-114-115.txt b/doc/opal-api/opal-led-get-set-114-115.txt new file mode 100644 index 0000000..4644adc --- /dev/null +++ b/doc/opal-api/opal-led-get-set-114-115.txt @@ -0,0 +1,36 @@ +Service Indicators (LEDS) +------------------------- + +The service indicator is one element of an overall hardware service strategy +where end user simplicity is a high priority. The goal is system firmware or +operating system code to isolate hardware failures to the failing FRU and +automatically activate the fault indicator associated with the failing FRU. +The end user then needs only to look for the FRU with the active fault +indicator to know which part to replace. + +Different types of indicators handled by LED code: + - System attention indicator (Check log indicator) + Indicates there is a problem with the system that needs attention. + - Identify + Helps the user locate/identify a particular FRU or resource in the + system. + - Fault + Indicates there is a problem with the FRU or resource at the + location with which the indicator is associated. + + +Note: + - For more information regarding service indicator refer to PAPR spec + (Service Indicators chapter). + +There are two OPAL calls relating to LED operations. + +OPAL_LEDS_GET_INDICATOR +----------------------- + Returns LED state for the given location code. + +OPAL_LEDS_SET_INDICATOR +----------------------- + Sets LED state for the given location code. + +See hw/fsp/fsp-leds.c for more deatails. diff --git a/hw/fsp/fsp-leds.c b/hw/fsp/fsp-leds.c index 07785b7..bc0c663 100644 --- a/hw/fsp/fsp-leds.c +++ b/hw/fsp/fsp-leds.c @@ -25,6 +25,8 @@ #include <lock.h> #include <errorlog.h> #include <opal-api.h> +#include <opal.h> +#include <opal-msg.h> #include "fsp-leds.h" @@ -81,6 +83,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 +102,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 +191,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 @@ -276,10 +285,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), @@ -295,6 +303,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 msg and spcn command */ @@ -350,6 +368,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); return rc; @@ -434,6 +456,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); @@ -483,7 +509,7 @@ static int process_led_state_change(void) * in the command queue it sets 'spcn_cmd_complete' 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; @@ -501,6 +527,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); @@ -920,7 +947,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); @@ -929,7 +956,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); @@ -1055,6 +1082,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 * @@ -1433,4 +1647,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 3b31838..622e1d5 100644 --- a/include/opal.h +++ b/include/opal.h @@ -165,7 +165,9 @@ #define OPAL_FLASH_WRITE 111 #define OPAL_FLASH_ERASE 112 #define OPAL_PRD_MSG 113 -#define OPAL_LAST 113 +#define OPAL_LEDS_GET_INDICATOR 114 +#define OPAL_LEDS_SET_INDICATOR 115 +#define OPAL_LAST 115 /* Device tree flags */ @@ -378,14 +380,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 { |