aboutsummaryrefslogtreecommitdiff
path: root/hw/prd.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/prd.c')
-rw-r--r--hw/prd.c130
1 files changed, 129 insertions, 1 deletions
diff --git a/hw/prd.c b/hw/prd.c
index 87e1bae..042d977 100644
--- a/hw/prd.c
+++ b/hw/prd.c
@@ -39,6 +39,7 @@ static uint64_t ipoll_status[MAX_CHIPS];
static uint8_t _prd_msg_buf[sizeof(struct opal_prd_msg) +
sizeof(struct prd_fw_msg)];
static struct opal_prd_msg *prd_msg = (struct opal_prd_msg *)&_prd_msg_buf;
+static struct opal_prd_msg *prd_msg_fsp_req;
static bool prd_msg_inuse, prd_active;
static struct dt_node *prd_node;
static bool prd_enabled = false;
@@ -112,6 +113,10 @@ static void prd_msg_consumed(void *data, int status __unused)
event = EVENT_OCC_RESET;
break;
case OPAL_PRD_MSG_TYPE_FIRMWARE_RESPONSE:
+ if (prd_msg_fsp_req) {
+ free(prd_msg_fsp_req);
+ prd_msg_fsp_req = NULL;
+ }
break;
case OPAL_PRD_MSG_TYPE_SBE_PASSTHROUGH:
proc = msg->sbe_passthrough.chip;
@@ -309,6 +314,48 @@ void prd_fsp_occ_load_start(uint32_t proc)
prd_event(proc, EVENT_FSP_OCC_LOAD_START);
}
+void prd_fw_resp_fsp_response(int status)
+{
+ struct prd_fw_msg *fw_resp;
+ uint64_t fw_resp_len_old;
+ int rc;
+ uint16_t hdr_size;
+ enum opal_msg_type msg_type = OPAL_MSG_PRD2;
+
+ lock(&events_lock);
+
+ /* In case of failure, return code is passed via generic_resp */
+ if (status != 0) {
+ fw_resp = (struct prd_fw_msg *)prd_msg_fsp_req->fw_resp.data;
+ fw_resp->type = cpu_to_be64(PRD_FW_MSG_TYPE_RESP_GENERIC);
+ fw_resp->generic_resp.status = cpu_to_be64(status);
+
+ fw_resp_len_old = prd_msg_fsp_req->fw_resp.len;
+ prd_msg_fsp_req->fw_resp.len = cpu_to_be64(PRD_FW_MSG_BASE_SIZE +
+ sizeof(fw_resp->generic_resp));
+
+ /* Update prd message size */
+ hdr_size = be16_to_cpu(prd_msg_fsp_req->hdr.size);
+ hdr_size -= fw_resp_len_old;
+ hdr_size += be64_to_cpu(prd_msg_fsp_req->fw_resp.len);
+ prd_msg_fsp_req->hdr.size = cpu_to_be16(hdr_size);
+ }
+
+ /*
+ * If prd message size is <= OPAL_MSG_FIXED_PARAMS_SIZE then use
+ * OPAL_MSG_PRD to pass data to kernel. So that it works fine on
+ * older kernel (which does not support OPAL_MSG_PRD2).
+ */
+ if (prd_msg_fsp_req->hdr.size < OPAL_MSG_FIXED_PARAMS_SIZE)
+ msg_type = OPAL_MSG_PRD;
+
+ rc = _opal_queue_msg(msg_type, prd_msg_fsp_req, prd_msg_consumed,
+ prd_msg_fsp_req->hdr.size, prd_msg_fsp_req);
+ if (!rc)
+ prd_msg_inuse = true;
+ unlock(&events_lock);
+}
+
/* incoming message handlers */
static int prd_msg_handle_attn_ack(struct opal_prd_msg *msg)
{
@@ -366,9 +413,10 @@ static int prd_msg_handle_fini(void)
static int prd_msg_handle_firmware_req(struct opal_prd_msg *msg)
{
- unsigned long fw_req_len, fw_resp_len;
+ unsigned long fw_req_len, fw_resp_len, data_len;
struct prd_fw_msg *fw_req, *fw_resp;
int rc;
+ uint64_t resp_msg_size;
fw_req_len = be64_to_cpu(msg->fw_req.req_len);
fw_resp_len = be64_to_cpu(msg->fw_req.resp_len);
@@ -417,6 +465,86 @@ static int prd_msg_handle_firmware_req(struct opal_prd_msg *msg)
prd_msg->hdr.size = cpu_to_be16(sizeof(*prd_msg));
rc = 0;
break;
+ case PRD_FW_MSG_TYPE_HBRT_FSP:
+ /*
+ * HBRT -> FSP messages are serialized. Just to be sure check
+ * whether fsp_req message is free or not.
+ */
+ if (prd_msg_fsp_req) {
+ prlog(PR_DEBUG, "PRD: HBRT - FSP message is busy\n");
+ rc = OPAL_BUSY;
+ break;
+ }
+
+ /*
+ * FSP interface doesn't tell us the response data size.
+ * Hence pass response length = request length.
+ */
+ resp_msg_size = sizeof(msg->hdr) + sizeof(msg->token) +
+ sizeof(msg->fw_resp) + fw_req_len;
+
+ if (resp_msg_size > OPAL_PRD_MSG_SIZE_MAX) {
+ prlog(PR_DEBUG, "PRD: HBRT - FSP response size (0x%llx)"
+ " is bigger than prd interface can handle\n",
+ resp_msg_size);
+ rc = OPAL_INTERNAL_ERROR;
+ break;
+ }
+
+ /*
+ * We will use fsp_queue_msg() to pass HBRT data to FSP.
+ * We cannot directly map kernel passed data as kernel
+ * will release the memory as soon as we return the control.
+ * Also FSP uses same memory to pass response to HBRT. Hence
+ * lets copy data to local memory. Then pass this memory to
+ * FSP via TCE mapping.
+ */
+ prd_msg_fsp_req = zalloc(resp_msg_size);
+ if (!prd_msg_fsp_req) {
+ prlog(PR_DEBUG, "PRD: Failed to allocate memory "
+ "for HBRT - FSP message\n");
+ rc = OPAL_RESOURCE;
+ break;
+ }
+
+ /* Update message header */
+ prd_msg_fsp_req->hdr.type = OPAL_PRD_MSG_TYPE_FIRMWARE_RESPONSE;
+ prd_msg_fsp_req->hdr.size = cpu_to_be16(resp_msg_size);
+ prd_msg_fsp_req->token = 0;
+ prd_msg_fsp_req->fw_resp.len = fw_req_len;
+
+ /* copy HBRT data to local memory */
+ fw_resp = (struct prd_fw_msg *)prd_msg_fsp_req->fw_resp.data;
+ memcpy(fw_resp, fw_req, fw_req_len);
+
+ /* Update response type */
+ fw_resp->type = cpu_to_be64(PRD_FW_MSG_TYPE_HBRT_FSP);
+
+ /* Get MBOX message size */
+ data_len = fw_req_len - PRD_FW_MSG_BASE_SIZE;
+
+ /* We have to wait until FSP responds */
+ prd_msg_inuse = false;
+ /* Unlock to avoid recursive lock issue */
+ unlock(&events_lock);
+
+ /* Send message to FSP */
+ rc = hservice_send_hbrt_msg(&(fw_resp->mbox_msg), data_len);
+
+ /*
+ * Callback handler from hservice_send_hbrt_msg will take
+ * care of sending response to HBRT. So just send return
+ * code to Linux.
+ */
+ if (rc == OPAL_SUCCESS)
+ return rc;
+
+ lock(&events_lock);
+ if (prd_msg_fsp_req) {
+ free(prd_msg_fsp_req);
+ prd_msg_fsp_req = NULL;
+ }
+ break;
default:
prlog(PR_DEBUG, "PRD: Unsupported fw_request type : 0x%llx\n",
be64_to_cpu(fw_req->type));