diff options
-rw-r--r-- | doc/opal-api/opal-led-get-set-114-115.txt | 19 | ||||
-rw-r--r-- | hw/fsp/fsp-leds.c | 177 | ||||
-rw-r--r-- | include/fsp.h | 12 | ||||
-rw-r--r-- | include/opal-api.h | 3 |
4 files changed, 209 insertions, 2 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 index 4644adc5..1d90ea40 100644 --- a/doc/opal-api/opal-led-get-set-114-115.txt +++ b/doc/opal-api/opal-led-get-set-114-115.txt @@ -19,6 +19,25 @@ Different types of indicators handled by LED code: location with which the indicator is associated. +LED Design: +----------- + When it comes to implementation we can classify LEDs into two + categories: + 1 - Hypervisor (OPAL) controlled LEDs (All identify & fault indicators) + During boot, we read/cache these LED details in OPAL (location code, + state, etc). We use cached data to serve read request from FSP/Host. + And we use SPCN passthrough MBOX command to update these LED state. + + 2 - Service processor (FSP) controlled LEDs (System Attention Indicator) + During boot, we read/cache this LED info using MBOX command. Later + anytime FSP updates this LED, it sends update system parameter + notification MBOX command. We use that data to update cached data. + LED update request is sent via set/reset attn MBOX command. + + LED update request: + Both FSP and Host will send LED update requests. We have to serialize + SPCN passthrough command. Hence we maintain local queue. + Note: - For more information regarding service indicator refer to PAPR spec (Service Indicators chapter). diff --git a/hw/fsp/fsp-leds.c b/hw/fsp/fsp-leds.c index 5aa6f206..83d4b382 100644 --- a/hw/fsp/fsp-leds.c +++ b/hw/fsp/fsp-leds.c @@ -70,6 +70,7 @@ static struct list_head spcn_cmdq; /* SPCN command queue */ /* LED lock */ static struct lock led_lock = LOCK_UNLOCKED; static struct lock spcn_cmd_lock = LOCK_UNLOCKED; +static struct lock sai_lock = LOCK_UNLOCKED; static bool spcn_cmd_complete = true; /* SPCN command complete */ @@ -201,6 +202,135 @@ static inline void opal_led_update_complete(u64 async_token, u64 result) opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, async_token, result); } +static inline bool is_sai_loc_code(char *loc_code) +{ + if (!strcmp(sai_data.loc_code, loc_code)) + return true; + + return false; +} + +/* Set/Reset System attention indicator */ +static void fsp_set_sai_complete(struct fsp_msg *msg) +{ + int ret = OPAL_SUCCESS; + int rc = msg->resp->word1 & 0xff00; + struct led_set_cmd *spcn_cmd = (struct led_set_cmd *)msg->user_data; + + if (rc) { + prlog(PR_ERR, PREFIX "Update SAI cmd failed [rc=%d].\n", rc); + ret = OPAL_INTERNAL_ERROR; + + /* Roll back */ + lock(&sai_lock); + sai_data.state = spcn_cmd->ckpt_status; + unlock(&sai_lock); + } + + if (spcn_cmd->cmd_src == SPCN_SRC_OPAL) + opal_led_update_complete(spcn_cmd->async_token, ret); + + /* free msg and spcn command */ + free(spcn_cmd); + fsp_freemsg(msg); + + /* Process pending LED update request */ + process_led_state_change(); +} + +static int fsp_set_sai(struct led_set_cmd *spcn_cmd) +{ + int rc = -ENOMEM; + uint32_t cmd = FSP_CMD_SA_INDICATOR; + struct fsp_msg *msg; + + /* + * FSP does not allow hypervisor to set real SAI, but we can + * reset real SAI. Also in our case only host can control + * LEDs, not guests. Hence we will set platform virtual SAI + * and reset real SAI. + */ + if (spcn_cmd->state == LED_STATE_ON) + cmd |= FSP_LED_SET_PLAT_SAI; + else + cmd |= FSP_LED_RESET_REAL_SAI; + + prlog(PR_TRACE, PREFIX + "Update SAI Indicator [cur : 0x%x, new : 0x%x].\n", + sai_data.state, spcn_cmd->state); + + msg = fsp_mkmsg(cmd, 0); + if (!msg) { + prlog(PR_ERR, PREFIX + "%s: Memory allocation failed.\n", __func__); + goto sai_fail; + } + + spcn_cmd->ckpt_status = sai_data.state; + msg->user_data = spcn_cmd; + rc = fsp_queue_msg(msg, fsp_set_sai_complete); + if (rc) { + fsp_freemsg(msg); + prlog(PR_ERR, PREFIX + "%s: Failed to queue the message\n", __func__); + goto sai_fail; + } + + lock(&sai_lock); + sai_data.state = spcn_cmd->state; + unlock(&sai_lock); + + return OPAL_SUCCESS; + +sai_fail: + if (spcn_cmd->cmd_src == SPCN_SRC_OPAL) + opal_led_update_complete(spcn_cmd->async_token, + OPAL_INTERNAL_ERROR); + + return OPAL_INTERNAL_ERROR; +} + +static void fsp_get_sai_complete(struct fsp_msg *msg) +{ + int rc = msg->resp->word1 & 0xff00; + + if (rc) { + prlog(PR_ERR, PREFIX + "Read real SAI cmd failed [rc = 0x%x].\n", rc); + } else { /* Update SAI state */ + lock(&sai_lock); + sai_data.state = msg->resp->data.words[0] & 0xff; + unlock(&sai_lock); + + prlog(PR_TRACE, PREFIX + "SAI initial state = 0x%x\n", sai_data.state); + } + + fsp_freemsg(msg); +} + +/* Read initial SAI state. */ +static void fsp_get_sai(void) +{ + int rc; + uint32_t cmd = FSP_CMD_SA_INDICATOR | FSP_LED_READ_REAL_SAI; + struct fsp_msg *msg; + + msg = fsp_mkmsg(cmd, 0); + if (!msg) { + prlog(PR_ERR, PREFIX + "%s: Memory allocation failed.\n", __func__); + return; + } + rc = fsp_queue_msg(msg, fsp_get_sai_complete); + if (rc) { + fsp_freemsg(msg); + prlog(PR_ERR, PREFIX + "%s: Failed to queue the message\n", __func__); + } +} + + /* * Update both the local LED lists to reflect upon led state changes * occured with the recent SPCN command. Subsequent LED requests will @@ -499,7 +629,11 @@ static int process_led_state_change(void) spcn_cmd = list_pop(&spcn_cmdq, struct led_set_cmd, link); unlock(&spcn_cmd_lock); - rc = fsp_msg_set_led_state(spcn_cmd); + if (is_sai_loc_code(spcn_cmd->loc_code)) + rc = fsp_set_sai(spcn_cmd); + else + rc = fsp_msg_set_led_state(spcn_cmd); + if (rc) { free(spcn_cmd); process_led_state_change(); @@ -1094,6 +1228,31 @@ static struct fsp_client fsp_indicator_client = { }; +static int fsp_opal_get_sai(u64 *led_mask, u64 *led_value) +{ + *led_mask |= OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_ATTN; + if (sai_data.state & OPAL_SLOT_LED_STATE_ON) + *led_value |= + OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_ATTN; + + return OPAL_SUCCESS; +} + +static int fsp_opal_set_sai(uint64_t async_token, char *loc_code, + const u64 led_mask, const u64 led_value) +{ + int state = LED_STATE_OFF; + + if (!((led_mask >> OPAL_SLOT_LED_TYPE_ATTN) & OPAL_SLOT_LED_STATE_ON)) + return OPAL_PARAMETER; + + if ((led_value >> OPAL_SLOT_LED_TYPE_ATTN) & OPAL_SLOT_LED_STATE_ON) + state = LED_STATE_ON; + + return queue_led_state_change(loc_code, 0, + state, SPCN_SRC_OPAL, async_token); +} + /* * fsp_opal_leds_get_ind (OPAL_LEDS_GET_INDICATOR) * @@ -1122,6 +1281,7 @@ static int64_t fsp_opal_leds_get_ind(char *loc_code, u64 *led_mask, { bool supported = true; int64_t max; + int rc; struct fsp_led_data *led; /* FSP not present */ @@ -1143,6 +1303,12 @@ static int64_t fsp_opal_leds_get_ind(char *loc_code, u64 *led_mask, if (max <= 0) return OPAL_PARAMETER; + /* Get System attention indicator state */ + if (is_sai_loc_code(loc_code)) { + rc = fsp_opal_get_sai(led_mask, led_value); + return rc; + } + /* LED not found */ led = fsp_find_cec_led(loc_code); if (!led) @@ -1228,6 +1394,14 @@ static int64_t fsp_opal_leds_set_ind(uint64_t async_token, if (max <= 0) return OPAL_PARAMETER; + /* Set System attention indicator state */ + if (is_sai_loc_code(loc_code)) { + supported = true; + rc = fsp_opal_set_sai(async_token, + loc_code, led_mask, led_value); + goto success; + } + /* LED not found */ led = fsp_find_cec_led(loc_code); if (!led) @@ -1719,6 +1893,7 @@ void fsp_led_init(void) /* Get System attention indicator state */ dt_get_sai_loc_code(); + fsp_get_sai(); /* Handle FSP initiated async LED commands */ fsp_register_client(&fsp_indicator_client, FSP_MCLASS_INDICATOR); diff --git a/include/fsp.h b/include/fsp.h index f461575f..c74996d4 100644 --- a/include/fsp.h +++ b/include/fsp.h @@ -338,6 +338,8 @@ #define FSP_RSP_ERRLOG_PHYP_ACK 0x0ce8800 /* FSP->HV */ #define FSP_CMD_ERRLOG_GET_PLID 0x0ce0900 /* FSP->HV: Get PLID */ #define FSP_RSP_ERRLOG_GET_PLID 0x0ce8900 /* HV->FSP */ +#define FSP_CMD_SA_INDICATOR 0x1ce1000 /* HV->FSP: read/update SAI */ +#define FSP_RSP_SA_INDICATOR 0x0ce9000 /* FSP->HV */ #define FSP_CMD_QUERY_SPARM 0x1ce1200 /* HV->FSP: System parameter query */ #define FSP_RSP_QUERY_SPARM 0x0ce9200 /* FSP->HV: System parameter resp */ #define FSP_CMD_SET_SPARM_1 0x1ce1301 /* HV->FSP: Set system parameter */ @@ -370,6 +372,16 @@ #define FSP_CMD_PANELSTATUS_EX1 0x0ce5c02 /* FSP->HV */ #define FSP_CMD_PANELSTATUS_EX2 0x0ce5c03 /* FSP->HV */ +/* SAI read/update sub commands */ +#define FSP_LED_RESET_REAL_SAI 0x00 +#define FSP_LED_READ_REAL_SAI 0x02 +#define FSP_LED_RESET_PARTITION_SAI 0x80 +#define FSP_LED_SET_PARTITION_SAI 0x81 +#define FSP_LED_READ_PARTITION_SAI 0x82 +#define FSP_LED_READ_PLAT_SAI 0x83 +#define FSP_LED_RESET_PLAT_SAI 0x84 +#define FSP_LED_SET_PLAT_SAI 0x85 + /* * Class 0xD2 */ diff --git a/include/opal-api.h b/include/opal-api.h index 1698311a..29421c19 100644 --- a/include/opal-api.h +++ b/include/opal-api.h @@ -376,7 +376,8 @@ enum OpalPciMaskAction { enum OpalSlotLedType { OPAL_SLOT_LED_TYPE_ID = 0, /* IDENTIFY LED */ OPAL_SLOT_LED_TYPE_FAULT = 1, /* FAULT LED */ - OPAL_SLOT_LED_TYPE_MAX = 2 + OPAL_SLOT_LED_TYPE_ATTN = 2, /* System Attention LED */ + OPAL_SLOT_LED_TYPE_MAX = 3 }; enum OpalSlotLedState { |