diff options
Diffstat (limited to 'hw/fsp/fsp-leds.c')
-rw-r--r-- | hw/fsp/fsp-leds.c | 141 |
1 files changed, 113 insertions, 28 deletions
diff --git a/hw/fsp/fsp-leds.c b/hw/fsp/fsp-leds.c index 2ac9723e..ba21cbe8 100644 --- a/hw/fsp/fsp-leds.c +++ b/hw/fsp/fsp-leds.c @@ -64,16 +64,21 @@ static void *led_buffer; */ static struct list_head cec_ledq; /* CEC LED list */ static struct list_head encl_ledq; /* Enclosure LED list */ +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 bool spcn_cmd_complete = true; /* SPCN command complete */ /* Last SPCN command */ static u32 last_spcn_cmd; static int replay = 0; - +/* Forward declaration */ 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, @@ -281,7 +286,12 @@ static void fsp_spcn_set_led_completion(struct fsp_msg *msg) prerror(PREFIX "Failed to queue FSP_RSP_SET_LED_STATE\n"); } } + + /* free msg */ fsp_freemsg(msg); + + /* Process pending LED update request */ + process_led_state_change(); } /* @@ -298,7 +308,7 @@ static void fsp_spcn_set_led_completion(struct fsp_msg *msg) * char lc_code[LOC_CODE_SIZE]; *}; */ -static int fsp_msg_set_led_state(char *loc_code, bool command, bool state) +static int fsp_msg_set_led_state(struct led_set_cmd *spcn_cmd) { struct spcn_led_data sled; struct fsp_msg *msg = NULL; @@ -308,8 +318,8 @@ static int fsp_msg_set_led_state(char *loc_code, bool command, bool state) u32 cmd_hdr = 0; int rc = -1; - sled.lc_len = strlen(loc_code); - strncpy(sled.lc_code, loc_code, sled.lc_len); + sled.lc_len = strlen(spcn_cmd->loc_code); + strncpy(sled.lc_code, spcn_cmd->loc_code, sled.lc_len); /* Location code length + Location code + LED control */ data_len = LOC_CODE_LEN + sled.lc_len + LED_CONTROL_LEN; @@ -317,7 +327,7 @@ static int fsp_msg_set_led_state(char *loc_code, bool command, bool state) data_len; /* Fetch the current state of LED */ - led = fsp_find_cec_led(loc_code); + led = fsp_find_cec_led(spcn_cmd->loc_code); /* LED not present */ if (led == NULL) { @@ -349,35 +359,35 @@ static int fsp_msg_set_led_state(char *loc_code, bool command, bool state) sled.state = led->status; /* Update the exclussive LED bits */ - if (is_enclosure_led(loc_code)) { - if (command == LED_COMMAND_FAULT) { - if (state == LED_STATE_ON) + if (is_enclosure_led(spcn_cmd->loc_code)) { + if (spcn_cmd->command == LED_COMMAND_FAULT) { + if (spcn_cmd->state == LED_STATE_ON) led->excl_bit |= FSP_LED_EXCL_FAULT; - if (state == LED_STATE_OFF) + if (spcn_cmd->state == LED_STATE_OFF) led->excl_bit &= ~FSP_LED_EXCL_FAULT; } - if (command == LED_COMMAND_IDENTIFY) { - if (state == LED_STATE_ON) + if (spcn_cmd->command == LED_COMMAND_IDENTIFY) { + if (spcn_cmd->state == LED_STATE_ON) led->excl_bit |= FSP_LED_EXCL_IDENTIFY; - if (state == LED_STATE_OFF) + if (spcn_cmd->state == LED_STATE_OFF) led->excl_bit &= ~FSP_LED_EXCL_IDENTIFY; } } /* LED FAULT commad */ - if (command == LED_COMMAND_FAULT) { - if (state == LED_STATE_ON) + if (spcn_cmd->command == LED_COMMAND_FAULT) { + if (spcn_cmd->state == LED_STATE_ON) sled.state |= SPCN_LED_FAULT_MASK; - if (state == LED_STATE_OFF) + if (spcn_cmd->state == LED_STATE_OFF) sled.state &= ~SPCN_LED_FAULT_MASK; } /* LED IDENTIFY command */ - if (command == LED_COMMAND_IDENTIFY) { - if (state == LED_STATE_ON) + if (spcn_cmd->command == LED_COMMAND_IDENTIFY) { + if (spcn_cmd->state == LED_STATE_ON) sled.state |= SPCN_LED_IDENTIFY_MASK; - if (state == LED_STATE_OFF) + if (spcn_cmd->state == LED_STATE_OFF) sled.state &= ~SPCN_LED_IDENTIFY_MASK; } @@ -396,7 +406,7 @@ static int fsp_msg_set_led_state(char *loc_code, bool command, bool state) * set/reset an individual led (CEC or ENCL). */ lock(&led_lock); - update_led_list(loc_code, sled.state); + update_led_list(spcn_cmd->loc_code, sled.state); msg->user_data = led; unlock(&led_lock); @@ -407,6 +417,87 @@ static int fsp_msg_set_led_state(char *loc_code, bool command, bool state) } /* + * process_led_state_change + * + * If the command queue is empty, it sets the 'spcn_cmd_complete' as true + * and just returns. Else it pops one element from the command queue + * and processes the command for the requested LED state change. + */ +static int process_led_state_change(void) +{ + struct led_set_cmd *spcn_cmd; + int rc = 0; + + /* + * The command queue is empty. This will only + * happen during the SPCN command callback path + * in which case we set 'spcn_cmd_complete' as true. + */ + lock(&spcn_cmd_lock); + if (list_empty(&spcn_cmdq)) { + spcn_cmd_complete = true; + unlock(&spcn_cmd_lock); + return rc; + } + + spcn_cmd = list_pop(&spcn_cmdq, struct led_set_cmd, link); + unlock(&spcn_cmd_lock); + + rc = fsp_msg_set_led_state(spcn_cmd); + if (rc) + log_simple_error(&e_info(OPAL_RC_LED_STATE), + PREFIX "Set led state failed at LC=%s\n", + spcn_cmd->loc_code); + free(spcn_cmd); + return rc; +} + +/* + * queue_led_state_change + * + * FSP async command or OPAL based request for LED state change gets queued + * up in the command queue. If no previous SPCN command is pending, then it + * immediately pops up one element from the list and processes it. If previous + * SPCN commands are still pending then it just queues up and return. When the + * SPCN command callback gets to execute, it processes one element from the + * list and keeps the chain execution going. At last when there are no elements + * 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) +{ + struct led_set_cmd *cmd; + int rc = 0; + + /* New request node */ + cmd = zalloc(sizeof(struct led_set_cmd)); + if (!cmd) { + prlog(PR_ERR, PREFIX + "SPCN set command node allocation failed\n"); + return -1; + } + + /* Save the request */ + strncpy(cmd->loc_code, loc_code, strlen(loc_code)); + cmd->command = command; + cmd->state = state; + + /* Add to the queue */ + lock(&spcn_cmd_lock); + list_add_tail(&spcn_cmdq, &cmd->link); + + /* No previous SPCN command pending */ + if (spcn_cmd_complete) { + spcn_cmd_complete = false; + unlock(&spcn_cmd_lock); + rc = process_led_state_change(); + return rc; + } + + unlock(&spcn_cmd_lock); + return rc; +} + +/* * Write single location code information into the TCE outbound buffer * * Data layout @@ -819,19 +910,12 @@ static void fsp_set_led_state(struct fsp_msg *msg) if (!strcmp(led->loc_code, req.loc_code)) continue; - if (fsp_msg_set_led_state(led->loc_code, - command, state)) - log_simple_error(&e_info(OPAL_RC_LED_STATE), - PREFIX "Set led state failed at LC=%s\n", - led->loc_code); + queue_led_state_change(led->loc_code, command, state); } break; case SET_IND_SINGLE_LOC_CODE: /* Set led state for single descendent led */ - if (fsp_msg_set_led_state(req.loc_code, command, state)) - log_simple_error(&e_info(OPAL_RC_LED_STATE), - PREFIX "Set led state failed at LC=%s\n", - req.loc_code); + queue_led_state_change(req.loc_code, command, state); break; default: resp = fsp_mkmsg(FSP_RSP_SET_LED_STATE | @@ -1274,6 +1358,7 @@ void fsp_led_init(void) /* Init the master lists */ list_head_init(&cec_ledq); list_head_init(&encl_ledq); + list_head_init(&spcn_cmdq); fsp_leds_query_spcn(); prlog(PR_TRACE, PREFIX "Init completed\n"); |