/* * Copyright (c) 2020-2022, Intel Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include "socfpga_mailbox.h" #include "socfpga_sip_svc.h" static mailbox_payload_t mailbox_resp_payload; static mailbox_container_t mailbox_resp_ctr = {0, 0, &mailbox_resp_payload}; static bool is_mailbox_cmdbuf_full(uint32_t cin) { uint32_t cout = mmio_read_32(MBOX_OFFSET + MBOX_COUT); return (((cin + 1U) % MBOX_CMD_BUFFER_SIZE) == cout); } static bool is_mailbox_cmdbuf_empty(uint32_t cin) { uint32_t cout = mmio_read_32(MBOX_OFFSET + MBOX_COUT); return (((cout + 1U) % MBOX_CMD_BUFFER_SIZE) == cin); } static int wait_for_mailbox_cmdbuf_empty(uint32_t cin) { unsigned int timeout = 200U; do { if (is_mailbox_cmdbuf_empty(cin)) { break; } mdelay(10U); } while (--timeout != 0U); if (timeout == 0U) { return MBOX_TIMEOUT; } return MBOX_RET_OK; } static int write_mailbox_cmd_buffer(uint32_t *cin, uint32_t cout, uint32_t data, bool *is_doorbell_triggered) { unsigned int timeout = 100U; do { if (is_mailbox_cmdbuf_full(*cin)) { if (!(*is_doorbell_triggered)) { mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_TO_SDM, 1U); *is_doorbell_triggered = true; } mdelay(10U); } else { mmio_write_32(MBOX_ENTRY_TO_ADDR(CMD, (*cin)++), data); *cin %= MBOX_CMD_BUFFER_SIZE; mmio_write_32(MBOX_OFFSET + MBOX_CIN, *cin); break; } } while (--timeout != 0U); if (timeout == 0U) { return MBOX_TIMEOUT; } if (*is_doorbell_triggered) { int ret = wait_for_mailbox_cmdbuf_empty(*cin); return ret; } return MBOX_RET_OK; } static int fill_mailbox_circular_buffer(uint32_t header_cmd, uint32_t *args, unsigned int len) { uint32_t sdm_read_offset, cmd_free_offset; unsigned int i; int ret; bool is_doorbell_triggered = false; cmd_free_offset = mmio_read_32(MBOX_OFFSET + MBOX_CIN); sdm_read_offset = mmio_read_32(MBOX_OFFSET + MBOX_COUT); ret = write_mailbox_cmd_buffer(&cmd_free_offset, sdm_read_offset, header_cmd, &is_doorbell_triggered); if (ret != 0) { goto restart_mailbox; } for (i = 0U; i < len; i++) { is_doorbell_triggered = false; ret = write_mailbox_cmd_buffer(&cmd_free_offset, sdm_read_offset, args[i], &is_doorbell_triggered); if (ret != 0) { goto restart_mailbox; } } mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_TO_SDM, 1U); return MBOX_RET_OK; restart_mailbox: /* * Attempt to restart mailbox if the driver not able to write * into mailbox command buffer */ if (MBOX_CMD_MASK(header_cmd) != MBOX_CMD_RESTART) { INFO("Mailbox timed out: Attempting mailbox reset\n"); ret = mailbox_init(); if (ret == MBOX_TIMEOUT) { INFO("Error: Mailbox fail to restart\n"); } } return MBOX_TIMEOUT; } int mailbox_read_response(unsigned int *job_id, uint32_t *response, unsigned int *resp_len) { uint32_t rin; uint32_t rout; uint32_t resp_data; unsigned int ret_resp_len; if (mmio_read_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM) == 1U) { mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM, 0U); } rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN); rout = mmio_read_32(MBOX_OFFSET + MBOX_ROUT); if (rout != rin) { resp_data = mmio_read_32(MBOX_ENTRY_TO_ADDR(RESP, (rout)++)); rout %= MBOX_RESP_BUFFER_SIZE; mmio_write_32(MBOX_OFFSET + MBOX_ROUT, rout); if (MBOX_RESP_CLIENT_ID(resp_data) != MBOX_ATF_CLIENT_ID) { return MBOX_WRONG_ID; } *job_id = MBOX_RESP_JOB_ID(resp_data); ret_resp_len = MBOX_RESP_LEN(resp_data); if (iterate_resp(ret_resp_len, response, resp_len) != MBOX_RET_OK) { return MBOX_TIMEOUT; } if (MBOX_RESP_ERR(resp_data) > 0U) { INFO("Error in response: %x\n", resp_data); return -MBOX_RESP_ERR(resp_data); } return MBOX_RET_OK; } return MBOX_NO_RESPONSE; } int mailbox_read_response_async(unsigned int *job_id, uint32_t *header, uint32_t *response, unsigned int *resp_len, uint8_t ignore_client_id) { uint32_t rin; uint32_t rout; uint32_t resp_data; uint32_t ret_resp_len = 0; uint8_t is_done = 0; if ((mailbox_resp_ctr.flag & MBOX_PAYLOAD_FLAG_BUSY) != 0) { ret_resp_len = MBOX_RESP_LEN( mailbox_resp_ctr.payload->header) - mailbox_resp_ctr.index; } if (mmio_read_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM) == 1U) { mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM, 0U); } rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN); rout = mmio_read_32(MBOX_OFFSET + MBOX_ROUT); while (rout != rin && !is_done) { resp_data = mmio_read_32(MBOX_ENTRY_TO_ADDR(RESP, (rout)++)); rout %= MBOX_RESP_BUFFER_SIZE; mmio_write_32(MBOX_OFFSET + MBOX_ROUT, rout); rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN); if ((mailbox_resp_ctr.flag & MBOX_PAYLOAD_FLAG_BUSY) != 0) { mailbox_resp_ctr.payload->data[mailbox_resp_ctr.index] = resp_data; mailbox_resp_ctr.index++; ret_resp_len--; } else { if (!ignore_client_id) { if (MBOX_RESP_CLIENT_ID(resp_data) != MBOX_ATF_CLIENT_ID) { *resp_len = 0; return MBOX_WRONG_ID; } } *job_id = MBOX_RESP_JOB_ID(resp_data); ret_resp_len = MBOX_RESP_LEN(resp_data); mailbox_resp_ctr.payload->header = resp_data; mailbox_resp_ctr.flag |= MBOX_PAYLOAD_FLAG_BUSY; } if (ret_resp_len == 0) { is_done = 1; } } if (is_done != 0) { /* copy header data to input address if applicable */ if (header != 0) { *header = mailbox_resp_ctr.payload->header; } /* copy response data to input buffer if applicable */ ret_resp_len = MBOX_RESP_LEN(mailbox_resp_ctr.payload->header); if ((ret_resp_len > 0) && (response != NULL) && (resp_len != NULL)) { if (*resp_len > ret_resp_len) { *resp_len = ret_resp_len; } memcpy((uint8_t *) response, (uint8_t *) mailbox_resp_ctr.payload->data, *resp_len * MBOX_WORD_BYTE); } /* reset async response param */ mailbox_resp_ctr.index = 0; mailbox_resp_ctr.flag = 0; if (MBOX_RESP_ERR(mailbox_resp_ctr.payload->header) > 0U) { INFO("Error in async response: %x\n", mailbox_resp_ctr.payload->header); return -MBOX_RESP_ERR(mailbox_resp_ctr.payload->header); } return MBOX_RET_OK; } *resp_len = 0; return (mailbox_resp_ctr.flag & MBOX_PAYLOAD_FLAG_BUSY) ? MBOX_BUSY : MBOX_NO_RESPONSE; } int mailbox_poll_response(uint32_t job_id, uint32_t urgent, uint32_t *response, unsigned int *resp_len) { unsigned int timeout = 40U; unsigned int sdm_loop = 255U; unsigned int ret_resp_len; uint32_t rin; uint32_t rout; uint32_t resp_data; while (sdm_loop != 0U) { do { if (mmio_read_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM) == 1U) { break; } mdelay(10U); } while (--timeout != 0U); if (timeout == 0U) { break; } mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM, 0U); if ((urgent & 1U) != 0U) { mdelay(5U); if ((mmio_read_32(MBOX_OFFSET + MBOX_STATUS) & MBOX_STATUS_UA_MASK) ^ (urgent & MBOX_STATUS_UA_MASK)) { mmio_write_32(MBOX_OFFSET + MBOX_URG, 0U); return MBOX_RET_OK; } mmio_write_32(MBOX_OFFSET + MBOX_URG, 0U); INFO("Error: Mailbox did not get UA"); return MBOX_RET_ERROR; } rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN); rout = mmio_read_32(MBOX_OFFSET + MBOX_ROUT); while (rout != rin) { resp_data = mmio_read_32(MBOX_ENTRY_TO_ADDR(RESP, (rout)++)); rout %= MBOX_RESP_BUFFER_SIZE; mmio_write_32(MBOX_OFFSET + MBOX_ROUT, rout); if (MBOX_RESP_CLIENT_ID(resp_data) != MBOX_ATF_CLIENT_ID || MBOX_RESP_JOB_ID(resp_data) != job_id) { continue; } ret_resp_len = MBOX_RESP_LEN(resp_data); if (iterate_resp(ret_resp_len, response, resp_len) != MBOX_RET_OK) { return MBOX_TIMEOUT; } if (MBOX_RESP_ERR(resp_data) > 0U) { INFO("Error in response: %x\n", resp_data); return -MBOX_RESP_ERR(resp_data); } return MBOX_RET_OK; } sdm_loop--; } INFO("Timed out waiting for SDM\n"); return MBOX_TIMEOUT; } int iterate_resp(uint32_t mbox_resp_len, uint32_t *resp_buf, unsigned int *resp_len) { unsigned int timeout, total_resp_len = 0U; uint32_t resp_data; uint32_t rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN); uint32_t rout = mmio_read_32(MBOX_OFFSET + MBOX_ROUT); while (mbox_resp_len > 0U) { timeout = 100U; mbox_resp_len--; resp_data = mmio_read_32(MBOX_ENTRY_TO_ADDR(RESP, (rout)++)); if ((resp_buf != NULL) && (resp_len != NULL) && (*resp_len != 0U)) { *(resp_buf + total_resp_len) = resp_data; *resp_len = *resp_len - 1; total_resp_len++; } rout %= MBOX_RESP_BUFFER_SIZE; mmio_write_32(MBOX_OFFSET + MBOX_ROUT, rout); do { rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN); if (rout == rin) { mdelay(10U); } else { break; } timeout--; } while ((mbox_resp_len > 0U) && (timeout != 0U)); if (timeout == 0U) { INFO("Timed out waiting for SDM\n"); return MBOX_TIMEOUT; } } if (resp_len) *resp_len = total_resp_len; return MBOX_RET_OK; } int mailbox_send_cmd_async_ext(uint32_t header_cmd, uint32_t *args, unsigned int len) { return fill_mailbox_circular_buffer(header_cmd, args, len); } int mailbox_send_cmd_async(uint32_t *job_id, uint32_t cmd, uint32_t *args, unsigned int len, unsigned int indirect) { int status; status = fill_mailbox_circular_buffer( MBOX_CLIENT_ID_CMD(MBOX_ATF_CLIENT_ID) | MBOX_JOB_ID_CMD(*job_id) | MBOX_CMD_LEN_CMD(len) | MBOX_INDIRECT(indirect) | cmd, args, len); if (status < 0) { return status; } *job_id = (*job_id + 1U) % MBOX_MAX_IND_JOB_ID; return MBOX_RET_OK; } int mailbox_send_cmd(uint32_t job_id, uint32_t cmd, uint32_t *args, unsigned int len, uint32_t urgent, uint32_t *response, unsigned int *resp_len) { int status = 0; if (urgent != 0U) { urgent |= mmio_read_32(MBOX_OFFSET + MBOX_STATUS) & MBOX_STATUS_UA_MASK; mmio_write_32(MBOX_OFFSET + MBOX_URG, cmd); mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_TO_SDM, 1U); } else { status = fill_mailbox_circular_buffer( MBOX_CLIENT_ID_CMD(MBOX_ATF_CLIENT_ID) | MBOX_JOB_ID_CMD(job_id) | MBOX_CMD_LEN_CMD(len) | cmd, args, len); } if (status != 0) { return status; } status = mailbox_poll_response(job_id, urgent, response, resp_len); return status; } void mailbox_clear_response(void) { mmio_write_32(MBOX_OFFSET + MBOX_ROUT, mmio_read_32(MBOX_OFFSET + MBOX_RIN)); } void mailbox_set_int(uint32_t interrupt) { mmio_write_32(MBOX_OFFSET+MBOX_INT, MBOX_COE_BIT(interrupt) | MBOX_UAE_BIT(interrupt)); } void mailbox_set_qspi_open(void) { mailbox_set_int(MBOX_INT_FLAG_COE | MBOX_INT_FLAG_RIE); mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_QSPI_OPEN, NULL, 0U, CMD_CASUAL, NULL, NULL); } void mailbox_set_qspi_direct(void) { mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_QSPI_DIRECT, NULL, 0U, CMD_CASUAL, NULL, NULL); } void mailbox_set_qspi_close(void) { mailbox_set_int(MBOX_INT_FLAG_COE | MBOX_INT_FLAG_RIE); mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_QSPI_CLOSE, NULL, 0U, CMD_CASUAL, NULL, NULL); } void mailbox_qspi_set_cs(uint32_t device_select) { uint32_t cs_setting; /* QSPI device select settings at 31:28 */ cs_setting = (device_select << 28); mailbox_set_int(MBOX_INT_FLAG_COE | MBOX_INT_FLAG_RIE); mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_QSPI_SET_CS, &cs_setting, 1U, CMD_CASUAL, NULL, NULL); } void mailbox_hps_qspi_enable(void) { mailbox_set_qspi_open(); mailbox_set_qspi_direct(); } void mailbox_reset_cold(void) { mailbox_set_int(MBOX_INT_FLAG_COE | MBOX_INT_FLAG_RIE); mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_REBOOT_HPS, NULL, 0U, CMD_CASUAL, NULL, NULL); } int mailbox_rsu_get_spt_offset(uint32_t *resp_buf, unsigned int resp_buf_len) { return mailbox_send_cmd(MBOX_JOB_ID, MBOX_GET_SUBPARTITION_TABLE, NULL, 0U, CMD_CASUAL, resp_buf, &resp_buf_len); } struct rsu_status_info { uint64_t current_image; uint64_t fail_image; uint32_t state; uint32_t version; uint32_t error_location; uint32_t error_details; uint32_t retry_counter; }; int mailbox_rsu_status(uint32_t *resp_buf, unsigned int resp_buf_len) { int ret; struct rsu_status_info *info = (struct rsu_status_info *)resp_buf; info->retry_counter = ~0U; ret = mailbox_send_cmd(MBOX_JOB_ID, MBOX_RSU_STATUS, NULL, 0U, CMD_CASUAL, resp_buf, &resp_buf_len); if (ret < 0) { return ret; } if (info->retry_counter != ~0U) { if ((info->version & RSU_VERSION_ACMF_MASK) == 0U) { info->version |= RSU_VERSION_ACMF; } } return ret; } int mailbox_rsu_update(uint32_t *flash_offset) { return mailbox_send_cmd(MBOX_JOB_ID, MBOX_RSU_UPDATE, flash_offset, 2U, CMD_CASUAL, NULL, NULL); } int mailbox_hps_stage_notify(uint32_t execution_stage) { return mailbox_send_cmd(MBOX_JOB_ID, MBOX_HPS_STAGE_NOTIFY, &execution_stage, 1U, CMD_CASUAL, NULL, NULL); } int mailbox_init(void) { int status; mailbox_set_int(MBOX_INT_FLAG_COE | MBOX_INT_FLAG_RIE | MBOX_INT_FLAG_UAE); mmio_write_32(MBOX_OFFSET + MBOX_URG, 0U); mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM, 0U); status = mailbox_send_cmd(0U, MBOX_CMD_RESTART, NULL, 0U, CMD_URGENT, NULL, NULL); if (status != 0) { return status; } mailbox_set_int(MBOX_INT_FLAG_COE | MBOX_INT_FLAG_RIE | MBOX_INT_FLAG_UAE); return MBOX_RET_OK; } int intel_mailbox_get_config_status(uint32_t cmd, bool init_done) { int status; uint32_t res, response[6]; unsigned int resp_len = ARRAY_SIZE(response); status = mailbox_send_cmd(MBOX_JOB_ID, cmd, NULL, 0U, CMD_CASUAL, response, &resp_len); if (status < 0) { return status; } res = response[RECONFIG_STATUS_STATE]; if ((res != 0U) && (res != MBOX_CFGSTAT_STATE_CONFIG)) { return res; } res = response[RECONFIG_STATUS_PIN_STATUS]; if ((res & PIN_STATUS_NSTATUS) == 0U) { return MBOX_CFGSTAT_STATE_ERROR_HARDWARE; } res = response[RECONFIG_STATUS_SOFTFUNC_STATUS]; if ((res & SOFTFUNC_STATUS_SEU_ERROR) != 0U) { ERROR("SoftFunction Status SEU ERROR\n"); } if ((res & SOFTFUNC_STATUS_CONF_DONE) == 0U) { return MBOX_CFGSTAT_STATE_CONFIG; } if (init_done && (res & SOFTFUNC_STATUS_INIT_DONE) == 0U) { return MBOX_CFGSTAT_STATE_CONFIG; } return MBOX_RET_OK; } int intel_mailbox_is_fpga_not_ready(void) { int ret = intel_mailbox_get_config_status(MBOX_RECONFIG_STATUS, true); if ((ret != MBOX_RET_OK) && (ret != MBOX_CFGSTAT_STATE_CONFIG)) { ret = intel_mailbox_get_config_status(MBOX_CONFIG_STATUS, false); } return ret; } int mailbox_hwmon_readtemp(uint32_t chan, uint32_t *resp_buf) { unsigned int resp_len = sizeof(resp_buf); return mailbox_send_cmd(MBOX_JOB_ID, MBOX_HWMON_READTEMP, &chan, 1U, CMD_CASUAL, resp_buf, &resp_len); } int mailbox_hwmon_readvolt(uint32_t chan, uint32_t *resp_buf) { unsigned int resp_len = sizeof(resp_buf); return mailbox_send_cmd(MBOX_JOB_ID, MBOX_HWMON_READVOLT, &chan, 1U, CMD_CASUAL, resp_buf, &resp_len); }