diff options
Diffstat (limited to 'roms/skiboot/hw/prd.c')
-rw-r--r-- | roms/skiboot/hw/prd.c | 789 |
1 files changed, 789 insertions, 0 deletions
diff --git a/roms/skiboot/hw/prd.c b/roms/skiboot/hw/prd.c new file mode 100644 index 000000000..45d765457 --- /dev/null +++ b/roms/skiboot/hw/prd.c @@ -0,0 +1,789 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* + * PRD: Processor Runtime Diagnostics + * + * Copyright 2014-2019 IBM Corp. + */ + +#include <skiboot.h> +#include <opal.h> +#include <lock.h> +#include <xscom.h> +#include <chip.h> +#include <opal-msg.h> +#include <fsp.h> +#include <mem_region.h> +#include <prd-fw-msg.h> +#include <hostservices.h> + +enum events { + EVENT_ATTN = 1 << 0, + EVENT_OCC_ERROR = 1 << 1, + EVENT_OCC_RESET = 1 << 2, + EVENT_SBE_PASSTHROUGH = 1 << 3, + EVENT_FSP_OCC_RESET = 1 << 4, + EVENT_FSP_OCC_LOAD_START = 1 << 5, +}; + +static uint8_t events[MAX_CHIPS]; +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 struct opal_prd_msg *prd_msg_fsp_notify; +static bool prd_msg_inuse, prd_active; +static struct dt_node *prd_node; +static bool prd_enabled = false; + +/* Locking: + * + * The events lock serialises access to the events, ipoll_status, + * prd_msg_inuse, and prd_active variables. + * + * The ipoll_lock protects against concurrent updates to the ipoll registers. + * + * The ipoll_lock may be acquired with events_lock held. This order must + * be preserved. + */ +static struct lock events_lock = LOCK_UNLOCKED; +static struct lock ipoll_lock = LOCK_UNLOCKED; + +static uint64_t prd_ipoll_mask_reg; +static uint64_t prd_ipoll_status_reg; +static uint64_t prd_ipoll_mask; + +/* PRD registers */ +#define PRD_P8_IPOLL_REG_MASK 0x01020013 +#define PRD_P8_IPOLL_REG_STATUS 0x01020014 +#define PRD_P8_IPOLL_XSTOP PPC_BIT(0) /* Xstop for host/core/millicode */ +#define PRD_P8_IPOLL_RECOV PPC_BIT(1) /* Recoverable */ +#define PRD_P8_IPOLL_SPEC_ATTN PPC_BIT(2) /* Special attention */ +#define PRD_P8_IPOLL_HOST_ATTN PPC_BIT(3) /* Host attention */ +#define PRD_P8_IPOLL_MASK PPC_BITMASK(0, 3) + +#define PRD_P9_IPOLL_REG_MASK 0x000F0033 +#define PRD_P9_IPOLL_REG_STATUS 0x000F0034 +#define PRD_P9_IPOLL_XSTOP PPC_BIT(0) /* Xstop for host/core/millicode */ +#define PRD_P9_IPOLL_RECOV PPC_BIT(1) /* Recoverable */ +#define PRD_P9_IPOLL_SPEC_ATTN PPC_BIT(2) /* Special attention */ +#define PRD_P9_IPOLL_UNIT_CS PPC_BIT(3) /* Unit Xstop */ +#define PRD_P9_IPOLL_HOST_ATTN PPC_BIT(4) /* Host attention */ +#define PRD_P9_IPOLL_MASK_INTR PPC_BIT(5) /* Host interrupt */ +#define PRD_P9_IPOLL_MASK PPC_BITMASK(0, 5) + +static void send_next_pending_event(void); + +static void prd_msg_consumed(void *data, int status) +{ + struct opal_prd_msg *msg = data; + uint32_t proc; + int notify_status = OPAL_SUCCESS; + uint8_t event = 0; + + lock(&events_lock); + switch (msg->hdr.type) { + case OPAL_PRD_MSG_TYPE_ATTN: + proc = be64_to_cpu(msg->attn.proc); + + /* If other ipoll events have been received in the time + * between prd_msg creation and consumption, we'll need to + * raise a separate ATTN message for those. So, we only + * clear the event if we don't have any further ipoll_status + * bits. + */ + ipoll_status[proc] &= ~be64_to_cpu(msg->attn.ipoll_status); + if (!ipoll_status[proc]) + event = EVENT_ATTN; + + break; + case OPAL_PRD_MSG_TYPE_OCC_ERROR: + proc = be64_to_cpu(msg->occ_error.chip); + event = EVENT_OCC_ERROR; + break; + case OPAL_PRD_MSG_TYPE_OCC_RESET: + proc = be64_to_cpu(msg->occ_reset.chip); + 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_FIRMWARE_NOTIFY: + if (prd_msg_fsp_notify) { + free(prd_msg_fsp_notify); + prd_msg_fsp_notify = NULL; + } + if (status != 0) { + prlog(PR_DEBUG, + "PRD: Failed to send FSP -> HBRT message\n"); + notify_status = FSP_STATUS_GENERIC_ERROR; + } + if (platform.prd && platform.prd->msg_response) + platform.prd->msg_response(notify_status); + break; + case OPAL_PRD_MSG_TYPE_SBE_PASSTHROUGH: + proc = be64_to_cpu(msg->sbe_passthrough.chip); + event = EVENT_SBE_PASSTHROUGH; + break; + case OPAL_PRD_MSG_TYPE_FSP_OCC_RESET: + proc = be64_to_cpu(msg->occ_reset.chip); + event = EVENT_FSP_OCC_RESET; + break; + case OPAL_PRD_MSG_TYPE_FSP_OCC_LOAD_START: + proc = be64_to_cpu(msg->occ_reset.chip); + event = EVENT_FSP_OCC_LOAD_START; + break; + default: + prlog(PR_ERR, "PRD: invalid msg consumed, type: 0x%x\n", + msg->hdr.type); + } + + if (event) + events[proc] &= ~event; + prd_msg_inuse = false; + send_next_pending_event(); + unlock(&events_lock); +} + +/* + * OPAL_MSG_PRD interface can handle message size <= OPAL_MSG_FIXED_PARAMS_SIZE. + * But kernel prd driver had a bug where it will not copy partial data to user + * space. Use OPAL_MSG_PRD interface only if size is <= sizeof(opal_prg_msg). + */ +static inline int opal_queue_prd_msg(struct opal_prd_msg *msg) +{ + enum opal_msg_type msg_type = OPAL_MSG_PRD2; + + if (be16_to_cpu(msg->hdr.size) <= 0x20) + msg_type = OPAL_MSG_PRD; + + return _opal_queue_msg(msg_type, msg, prd_msg_consumed, + be16_to_cpu(msg->hdr.size), msg); +} + +static int populate_ipoll_msg(struct opal_prd_msg *msg, uint32_t proc) +{ + uint64_t ipoll_mask; + int rc; + + lock(&ipoll_lock); + rc = xscom_read(proc, prd_ipoll_mask_reg, &ipoll_mask); + unlock(&ipoll_lock); + + if (rc) { + prlog(PR_ERR, "PRD: Unable to read ipoll status (chip %d)!\n", + proc); + return -1; + } + + msg->attn.proc = cpu_to_be64(proc); + msg->attn.ipoll_status = cpu_to_be64(ipoll_status[proc]); + msg->attn.ipoll_mask = cpu_to_be64(ipoll_mask); + return 0; +} + +static void send_next_pending_event(void) +{ + struct proc_chip *chip; + uint32_t proc; + int rc; + uint8_t event; + + assert(!prd_msg_inuse); + + if (!prd_active) + return; + + event = 0; + + for_each_chip(chip) { + proc = chip->id; + if (events[proc]) { + event = events[proc]; + break; + } + } + + if (!event) + return; + + prd_msg->token = 0; + prd_msg->hdr.size = cpu_to_be16(sizeof(*prd_msg)); + + if (event & EVENT_ATTN) { + prd_msg->hdr.type = OPAL_PRD_MSG_TYPE_ATTN; + populate_ipoll_msg(prd_msg, proc); + } else if (event & EVENT_OCC_ERROR) { + prd_msg->hdr.type = OPAL_PRD_MSG_TYPE_OCC_ERROR; + prd_msg->occ_error.chip = cpu_to_be64(proc); + } else if (event & EVENT_OCC_RESET) { + prd_msg->hdr.type = OPAL_PRD_MSG_TYPE_OCC_RESET; + prd_msg->occ_reset.chip = cpu_to_be64(proc); + occ_msg_queue_occ_reset(); + } else if (event & EVENT_SBE_PASSTHROUGH) { + prd_msg->hdr.type = OPAL_PRD_MSG_TYPE_SBE_PASSTHROUGH; + prd_msg->sbe_passthrough.chip = cpu_to_be64(proc); + } else if (event & EVENT_FSP_OCC_RESET) { + prd_msg->hdr.type = OPAL_PRD_MSG_TYPE_FSP_OCC_RESET; + prd_msg->occ_reset.chip = cpu_to_be64(proc); + } else if (event & EVENT_FSP_OCC_LOAD_START) { + prd_msg->hdr.type = OPAL_PRD_MSG_TYPE_FSP_OCC_LOAD_START; + prd_msg->occ_reset.chip = cpu_to_be64(proc); + } + + /* + * We always need to handle PSI interrupts, but if the is PRD is + * disabled then we shouldn't propagate PRD events to the host. + */ + if (prd_enabled) { + rc = opal_queue_prd_msg(prd_msg); + if (!rc) + prd_msg_inuse = true; + } +} + +static void __prd_event(uint32_t proc, uint8_t event) +{ + events[proc] |= event; + if (!prd_msg_inuse) + send_next_pending_event(); +} + +static void prd_event(uint32_t proc, uint8_t event) +{ + lock(&events_lock); + __prd_event(proc, event); + unlock(&events_lock); +} + +static int __ipoll_update_mask(uint32_t proc, bool set, uint64_t bits) +{ + uint64_t mask; + int rc; + + rc = xscom_read(proc, prd_ipoll_mask_reg, &mask); + if (rc) + return rc; + + if (set) + mask |= bits; + else + mask &= ~bits; + + return xscom_write(proc, prd_ipoll_mask_reg, mask); +} + +static int ipoll_record_and_mask_pending(uint32_t proc) +{ + uint64_t status; + int rc; + + lock(&ipoll_lock); + rc = xscom_read(proc, prd_ipoll_status_reg, &status); + status &= prd_ipoll_mask; + if (!rc) + __ipoll_update_mask(proc, true, status); + unlock(&ipoll_lock); + + if (!rc) + ipoll_status[proc] |= status; + + return rc; +} + +/* Entry point for interrupts */ +void prd_psi_interrupt(uint32_t proc) +{ + int rc; + + lock(&events_lock); + + rc = ipoll_record_and_mask_pending(proc); + if (rc) + prlog(PR_ERR, "PRD: Failed to update IPOLL mask\n"); + + __prd_event(proc, EVENT_ATTN); + + unlock(&events_lock); +} + +void prd_tmgt_interrupt(uint32_t proc) +{ + prd_event(proc, EVENT_OCC_ERROR); +} + +void prd_occ_reset(uint32_t proc) +{ + prd_event(proc, EVENT_OCC_RESET); +} + +void prd_fsp_occ_reset(uint32_t proc) +{ + prd_event(proc, EVENT_FSP_OCC_RESET); +} + +void prd_sbe_passthrough(uint32_t proc) +{ + prd_event(proc, EVENT_SBE_PASSTHROUGH); +} + +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; + + 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 = be64_to_cpu(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); + } + + rc = opal_queue_prd_msg(prd_msg_fsp_req); + if (!rc) + prd_msg_inuse = true; + unlock(&events_lock); +} + +int prd_hbrt_fsp_msg_notify(void *data, u32 dsize) +{ + struct prd_fw_msg *fw_notify; + int size, fw_notify_size; + int rc = FSP_STATUS_GENERIC_ERROR; + + if (!prd_enabled) { + prlog(PR_NOTICE, "PRD: %s: PRD daemon is not ready\n", + __func__); + return rc; + } + + /* Calculate prd message size */ + fw_notify_size = PRD_FW_MSG_BASE_SIZE + dsize; + size = sizeof(prd_msg->hdr) + sizeof(prd_msg->token) + + sizeof(prd_msg->fw_notify) + fw_notify_size; + + if (size > OPAL_PRD_MSG_SIZE_MAX) { + prlog(PR_DEBUG, "PRD: FSP - HBRT notify message size (0x%x)" + " is bigger than prd interface can handle\n", size); + return rc; + } + + lock(&events_lock); + + /* FSP - HBRT messages are serialized */ + if (prd_msg_fsp_notify) { + prlog(PR_DEBUG, "PRD: FSP - HBRT notify message is busy\n"); + goto unlock_events; + } + + /* Handle message allocation */ + prd_msg_fsp_notify = zalloc(size); + if (!prd_msg_fsp_notify) { + prlog(PR_DEBUG, + "PRD: %s: Failed to allocate memory.\n", __func__); + goto unlock_events; + } + + prd_msg_fsp_notify->hdr.type = OPAL_PRD_MSG_TYPE_FIRMWARE_NOTIFY; + prd_msg_fsp_notify->hdr.size = cpu_to_be16(size); + prd_msg_fsp_notify->token = 0; + prd_msg_fsp_notify->fw_notify.len = cpu_to_be64(fw_notify_size); + fw_notify = (void *)prd_msg_fsp_notify->fw_notify.data; + fw_notify->type = cpu_to_be64(PRD_FW_MSG_TYPE_HBRT_FSP); + memcpy(&(fw_notify->mbox_msg), data, dsize); + + if (!prd_active) { + // save the message, we'll deliver it when prd starts + rc = FSP_STATUS_BUSY; + goto unlock_events; + } + + rc = opal_queue_prd_msg(prd_msg_fsp_notify); + if (!rc) + prd_msg_inuse = true; + +unlock_events: + unlock(&events_lock); + return rc; +} + +/* incoming message handlers */ +static int prd_msg_handle_attn_ack(struct opal_prd_msg *msg) +{ + int rc; + + lock(&ipoll_lock); + rc = __ipoll_update_mask(be64_to_cpu(msg->attn_ack.proc), false, + be64_to_cpu(msg->attn_ack.ipoll_ack) & prd_ipoll_mask); + unlock(&ipoll_lock); + + if (rc) + prlog(PR_ERR, "PRD: Unable to unmask ipoll!\n"); + + return rc; +} + +static int prd_msg_handle_init(struct opal_prd_msg *msg) +{ + struct proc_chip *chip; + + lock(&ipoll_lock); + for_each_chip(chip) { + __ipoll_update_mask(chip->id, false, + be64_to_cpu(msg->init.ipoll) & prd_ipoll_mask); + } + unlock(&ipoll_lock); + + /* we're transitioning from inactive to active; send any pending tmgt + * interrupts */ + lock(&events_lock); + prd_active = true; + + if (prd_msg_fsp_notify) { + if (!opal_queue_prd_msg(prd_msg_fsp_notify)) + prd_msg_inuse = true; + } + if (!prd_msg_inuse) + send_next_pending_event(); + unlock(&events_lock); + + return OPAL_SUCCESS; +} + +static int prd_msg_handle_fini(void) +{ + struct proc_chip *chip; + + lock(&events_lock); + prd_active = false; + unlock(&events_lock); + + lock(&ipoll_lock); + for_each_chip(chip) { + __ipoll_update_mask(chip->id, true, prd_ipoll_mask); + } + unlock(&ipoll_lock); + + return OPAL_SUCCESS; +} + +static int prd_msg_handle_firmware_req(struct opal_prd_msg *msg) +{ + 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); + fw_req = (struct prd_fw_msg *)msg->fw_req.data; + + /* do we have a full firmware message? */ + if (fw_req_len < sizeof(struct prd_fw_msg)) + return -EINVAL; + + /* does the total (outer) PRD message len provide enough data for the + * claimed (inner) FW message? + */ + if (be16_to_cpu(msg->hdr.size) < fw_req_len + + offsetof(struct opal_prd_msg, fw_req.data)) + return -EINVAL; + + /* is there enough response buffer for a base response? Type-specific + * responses may be larger, but anything less than BASE_SIZE is + * invalid. */ + if (fw_resp_len < PRD_FW_MSG_BASE_SIZE) + return -EINVAL; + + /* prepare a response message. */ + lock(&events_lock); + prd_msg_inuse = true; + prd_msg->token = 0; + prd_msg->hdr.type = OPAL_PRD_MSG_TYPE_FIRMWARE_RESPONSE; + fw_resp = (void *)prd_msg->fw_resp.data; + + switch (be64_to_cpu(fw_req->type)) { + case PRD_FW_MSG_TYPE_REQ_NOP: + fw_resp->type = cpu_to_be64(PRD_FW_MSG_TYPE_RESP_NOP); + prd_msg->fw_resp.len = cpu_to_be64(PRD_FW_MSG_BASE_SIZE); + prd_msg->hdr.size = cpu_to_be16(sizeof(*prd_msg)); + rc = 0; + break; + case PRD_FW_MSG_TYPE_ERROR_LOG: + if (platform.prd == NULL || + platform.prd->send_error_log == NULL) { + rc = OPAL_UNSUPPORTED; + break; + } + + rc = platform.prd->send_error_log(be32_to_cpu(fw_req->errorlog.plid), + be32_to_cpu(fw_req->errorlog.size), + fw_req->errorlog.data); + /* Return generic response to HBRT */ + fw_resp->type = cpu_to_be64(PRD_FW_MSG_TYPE_RESP_GENERIC); + fw_resp->generic_resp.status = cpu_to_be64(rc); + prd_msg->fw_resp.len = cpu_to_be64(PRD_FW_MSG_BASE_SIZE + + sizeof(fw_resp->generic_resp)); + prd_msg->hdr.size = cpu_to_be16(sizeof(*prd_msg)); + rc = 0; + break; + case PRD_FW_MSG_TYPE_HBRT_FSP: + if (platform.prd == NULL || + platform.prd->send_hbrt_msg == NULL) { + rc = OPAL_UNSUPPORTED; + break; + } + + /* + * 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 = cpu_to_be64(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 = platform.prd->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)); + rc = -ENOSYS; + } + + if (!rc) { + rc = opal_queue_prd_msg(prd_msg); + if (rc) + prd_msg_inuse = false; + } else { + prd_msg_inuse = false; + } + + unlock(&events_lock); + + return rc; +} + +/* Entry from the host above */ +static int64_t opal_prd_msg(struct opal_prd_msg *msg) +{ + int rc; + + /* fini is a little special: the kernel (which may not have the entire + * opal_prd_msg definition) can send a FINI message, so we don't check + * the full size */ + if (be16_to_cpu(msg->hdr.size) >= sizeof(struct opal_prd_msg_header) && + msg->hdr.type == OPAL_PRD_MSG_TYPE_FINI) + return prd_msg_handle_fini(); + + if (be16_to_cpu(msg->hdr.size) < sizeof(*msg)) + return OPAL_PARAMETER; + + switch (msg->hdr.type) { + case OPAL_PRD_MSG_TYPE_INIT: + rc = prd_msg_handle_init(msg); + break; + case OPAL_PRD_MSG_TYPE_ATTN_ACK: + rc = prd_msg_handle_attn_ack(msg); + break; + case OPAL_PRD_MSG_TYPE_OCC_RESET_NOTIFY: + rc = occ_msg_queue_occ_reset(); + break; + case OPAL_PRD_MSG_TYPE_FIRMWARE_REQUEST: + rc = prd_msg_handle_firmware_req(msg); + break; + case OPAL_PRD_MSG_TYPE_FSP_OCC_RESET_STATUS: + if (platform.prd == NULL || + platform.prd->fsp_occ_reset_status == NULL) { + rc = OPAL_UNSUPPORTED; + break; + } + rc = platform.prd->fsp_occ_reset_status( + be64_to_cpu(msg->fsp_occ_reset_status.chip), + be64_to_cpu(msg->fsp_occ_reset_status.status)); + break; + case OPAL_PRD_MSG_TYPE_CORE_SPECIAL_WAKEUP: + if (platform.prd == NULL || + platform.prd->wakeup == NULL) { + rc = OPAL_UNSUPPORTED; + break; + } + rc = platform.prd->wakeup(be32_to_cpu(msg->spl_wakeup.core), + be32_to_cpu(msg->spl_wakeup.mode)); + break; + case OPAL_PRD_MSG_TYPE_FSP_OCC_LOAD_START_STATUS: + if (platform.prd == NULL || + platform.prd->fsp_occ_load_start_status == NULL) { + rc = OPAL_UNSUPPORTED; + break; + } + rc = platform.prd->fsp_occ_load_start_status( + be64_to_cpu(msg->fsp_occ_reset_status.chip), + be64_to_cpu(msg->fsp_occ_reset_status.status)); + break; + default: + prlog(PR_DEBUG, "PRD: Unsupported prd message type : 0x%x\n", + msg->hdr.type); + rc = OPAL_UNSUPPORTED; + } + + return rc; +} + + +/* + * Initialise the Opal backend for the PRD daemon. This must be called from + * platform probe or init function. + */ +void prd_init(void) +{ + struct proc_chip *chip; + + switch (proc_gen) { + case proc_gen_p8: + prd_ipoll_mask_reg = PRD_P8_IPOLL_REG_MASK; + prd_ipoll_status_reg = PRD_P8_IPOLL_REG_STATUS; + prd_ipoll_mask = PRD_P8_IPOLL_MASK; + break; + case proc_gen_p9: + prd_ipoll_mask_reg = PRD_P9_IPOLL_REG_MASK; + prd_ipoll_status_reg = PRD_P9_IPOLL_REG_STATUS; + prd_ipoll_mask = PRD_P9_IPOLL_MASK; + break; + case proc_gen_p10: /* IPOLL regs are the same for p9 and p10 */ + prd_ipoll_mask_reg = PRD_P9_IPOLL_REG_MASK; + prd_ipoll_status_reg = PRD_P9_IPOLL_REG_STATUS; + prd_ipoll_mask = PRD_P9_IPOLL_MASK; + break; + default: + assert(0); + } + + /* mask everything */ + lock(&ipoll_lock); + for_each_chip(chip) { + __ipoll_update_mask(chip->id, true, prd_ipoll_mask); + } + unlock(&ipoll_lock); + + prd_enabled = true; + opal_register(OPAL_PRD_MSG, opal_prd_msg, 1); + + prd_node = dt_new(opal_node, "diagnostics"); + dt_add_property_strings(prd_node, "compatible", "ibm,opal-prd"); +} + +void prd_register_reserved_memory(void) +{ + struct mem_region *region; + + if (!prd_node) + return; + + lock(&mem_region_lock); + for (region = mem_region_next(NULL); region; + region = mem_region_next(region)) { + + if (region->type != REGION_FW_RESERVED) + continue; + + if (!region->node) + continue; + + if (!dt_find_property(region->node, "ibm,prd-label")) { + dt_add_property_string(region->node, "ibm,prd-label", + region->name); + } + } + unlock(&mem_region_lock); +} |