summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/opal-api/opal-led-get-set-114-115.txt19
-rw-r--r--hw/fsp/fsp-leds.c177
-rw-r--r--include/fsp.h12
-rw-r--r--include/opal-api.h3
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 {
OpenPOWER on IntegriCloud