| /* |
| * Copyright (c) 2020-2023, Intel Corporation. All rights reserved. |
| * Copyright (c) 2024-2025, Altera Corporation. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <lib/mmio.h> |
| #include <lib/spinlock.h> |
| #include <common/debug.h> |
| #include <drivers/delay_timer.h> |
| #include <platform_def.h> |
| |
| #include "socfpga_mailbox.h" |
| #include "socfpga_plat_def.h" |
| #include "socfpga_sip_svc.h" |
| #include "socfpga_system_manager.h" |
| |
| #if SIP_SVC_V3 |
| /* Function prototypes */ |
| void mailbox_init_v3(void); |
| static int mailbox_response_handler_fsm(void); |
| static inline void mailbox_free_cmd_desc(sdm_command_t *cmd_desc); |
| static sdm_response_t *mailbox_get_resp_desc(uint8_t client_id, uint8_t job_id, |
| uint8_t *index); |
| static sdm_command_t *mailbox_get_cmd_desc(uint8_t client_id, uint8_t job_id); |
| static inline void mailbox_free_resp_desc(uint8_t index); |
| static sdm_command_t *mailbox_get_free_cmd_desc(void); |
| static sdm_response_t *mailbox_get_resp_desc_cid(uint8_t client_id, |
| uint8_t *index); |
| static int mailbox_read_response_v3(uint8_t client_id, uint8_t *job_id, |
| uint32_t *header, uint32_t *resp, |
| uint32_t *resp_len, |
| uint8_t ignore_client_id); |
| static int mailbox_poll_response_v3(uint8_t client_id, uint8_t job_id, |
| uint32_t *resp, unsigned int *resp_len, |
| uint32_t urgent); |
| |
| static spinlock_t mbox_db_lock; /* Mailbox service data base lock */ |
| static spinlock_t mbox_write_lock; /* Hardware mailbox FIFO write lock */ |
| static spinlock_t mbox_read_lock; /* Hardware mailbox FIFO read lock */ |
| |
| static mailbox_service_t mbox_svc; /* Mailbox service data base */ |
| static uint8_t async_v1_job_id; |
| #endif /* #if SIP_SVC_V3 */ |
| |
| 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 = 20000U; |
| |
| do { |
| if (is_mailbox_cmdbuf_empty(cin)) { |
| break; |
| } |
| udelay(50U); |
| } 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 = 20000U; |
| |
| VERBOSE("MBOX: 0x%x\n", data); |
| |
| 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; |
| } |
| udelay(50U); |
| } 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; |
| } |
| |
| /* |
| * Function description: Write the command header, and its payload one by one |
| * into the mailbox command buffer. Along with this, check for mailbox buffer |
| * full condition and trigger doorbell to SDM if the command buffer is full. |
| */ |
| 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; |
| |
| #if SIP_SVC_V3 |
| spin_lock(&mbox_write_lock); |
| #endif |
| |
| cmd_free_offset = mmio_read_32(MBOX_OFFSET + MBOX_CIN); |
| sdm_read_offset = mmio_read_32(MBOX_OFFSET + MBOX_COUT); |
| |
| /* Write the command header here */ |
| ret = write_mailbox_cmd_buffer(&cmd_free_offset, sdm_read_offset, |
| header_cmd, &is_doorbell_triggered); |
| if (ret != 0) { |
| goto restart_mailbox; |
| } |
| |
| /* Write the payload here w.r.to args and its len - one by one. */ |
| 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); |
| |
| #if SIP_SVC_V3 |
| spin_unlock(&mbox_write_lock); |
| #endif |
| 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"); |
| } |
| } |
| |
| #if SIP_SVC_V3 |
| spin_unlock(&mbox_write_lock); |
| #endif |
| return MBOX_TIMEOUT; |
| } |
| |
| int mailbox_read_response(unsigned int *job_id, uint32_t *response, |
| unsigned int *resp_len) |
| { |
| #if SIP_SVC_V3 |
| return mailbox_read_response_v3(MBOX_ATF_CLIENT_ID, |
| (uint8_t *)job_id, NULL, |
| response, resp_len, |
| 0); |
| #else |
| 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("SDM response: Return Code: 0x%x\n", MBOX_RESP_ERR(resp_data)); |
| return -MBOX_RESP_ERR(resp_data); |
| } |
| |
| return MBOX_RET_OK; |
| } |
| |
| return MBOX_NO_RESPONSE; |
| #endif |
| } |
| |
| int mailbox_read_response_async(unsigned int *job_id, uint32_t *header, |
| uint32_t *response, unsigned int *resp_len, |
| uint8_t ignore_client_id) |
| { |
| #if SIP_SVC_V3 |
| /* Just to avoid the build warning */ |
| (void)mailbox_resp_ctr; |
| return mailbox_read_response_v3(MBOX_ATF_CLIENT_ID, |
| (uint8_t *)job_id, header, |
| response, resp_len, |
| ignore_client_id); |
| #else |
| uint32_t rin; |
| uint32_t rout; |
| uint32_t resp_data; |
| uint32_t ret_resp_len = 0; |
| uint8_t is_done = 0; |
| uint32_t resp_len_check = 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; |
| } |
| |
| resp_len_check = (uint32_t) *resp_len; |
| |
| if (resp_len_check > MBOX_DATA_MAX_LEN) { |
| return MBOX_RET_ERROR; |
| } |
| |
| memcpy_s((uint8_t *) response, *resp_len * MBOX_WORD_BYTE, |
| (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; |
| #endif |
| } |
| |
| int mailbox_poll_response(uint32_t job_id, uint32_t urgent, uint32_t *response, |
| unsigned int *resp_len) |
| { |
| #if SIP_SVC_V3 |
| return mailbox_poll_response_v3(MBOX_ATF_CLIENT_ID, (uint8_t)job_id, |
| response, resp_len, urgent); |
| #else |
| 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); |
| ERROR("MBOX: 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; |
| } |
| |
| /* Get the return response len from the response header. */ |
| ret_resp_len = MBOX_RESP_LEN(resp_data); |
| |
| /* Print the response header. */ |
| VERBOSE("MBOX: RespHdr: cid %d, jid %d, len %d, err_code 0x%x\n", |
| MBOX_RESP_CLIENT_ID(resp_data), |
| MBOX_RESP_JOB_ID(resp_data), |
| MBOX_RESP_LEN(resp_data), |
| MBOX_RESP_ERR(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("SDM response: Return Code: 0x%x\n", MBOX_RESP_ERR(resp_data)); |
| return -MBOX_RESP_ERR(resp_data); |
| } |
| |
| return MBOX_RET_OK; |
| } |
| |
| sdm_loop--; |
| } |
| |
| INFO("Timed out waiting for SDM\n"); |
| return MBOX_TIMEOUT; |
| #endif |
| } |
| |
| 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; |
| } |
| |
| #if SIP_SVC_V3 |
| async_v1_job_id = (uint8_t)*job_id; |
| #endif |
| *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_RIE_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) |
| { |
| uint32_t response[1], qspi_clk, reg; |
| unsigned int resp_len = ARRAY_SIZE(response); |
| |
| mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_QSPI_DIRECT, NULL, 0U, |
| CMD_CASUAL, response, &resp_len); |
| |
| qspi_clk = response[0]; |
| INFO("QSPI ref clock: %u\n", qspi_clk); |
| |
| /* |
| * Store QSPI ref clock frequency in BOOT_SCRATCH_COLD_0 register for |
| * later boot loader (i.e. u-boot) use. |
| * The frequency is stored in kHz and occupies BOOT_SCRATCH_COLD_0 |
| * register bits[27:0]. |
| */ |
| qspi_clk /= 1000; |
| reg = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_0)); |
| reg &= ~SYSMGR_QSPI_REFCLK_MASK; |
| reg |= qspi_clk & SYSMGR_QSPI_REFCLK_MASK; |
| mmio_write_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_0), reg); |
| } |
| |
| 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, 0U, 0U, |
| CMD_CASUAL, NULL, NULL); |
| } |
| |
| void mailbox_reset_warm(uint32_t reset_type) |
| { |
| mailbox_set_int(MBOX_INT_FLAG_COE | MBOX_INT_FLAG_RIE); |
| |
| reset_type = 0x01; // Warm reset header data must be 1 |
| mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_REBOOT_HPS, &reset_type, 1U, |
| 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_get_device_info(uint32_t *resp_buf, unsigned int resp_buf_len) |
| { |
| return mailbox_send_cmd(MBOX_JOB_ID, MBOX_RSU_GET_DEVICE_INFO, NULL, 0U, |
| CMD_CASUAL, resp_buf, |
| &resp_buf_len); |
| } |
| |
| 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; |
| } |
| |
| #if SIP_SVC_V3 |
| /* Initialize the mailbox version3 implementation, and in V3 we |
| * are interested in only RIE interrupt |
| */ |
| mailbox_init_v3(); |
| mailbox_set_int(MBOX_INT_FLAG_RIE); |
| #else |
| mailbox_set_int(MBOX_INT_FLAG_COE | MBOX_INT_FLAG_RIE | |
| MBOX_INT_FLAG_UAE); |
| #endif |
| |
| return MBOX_RET_OK; |
| } |
| |
| int mailbox_send_fpga_config_comp(void) |
| { |
| int ret; |
| |
| ret = mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_FPGA_CONFIG_COMP, NULL, 0U, |
| CMD_CASUAL, NULL, NULL); |
| |
| if (ret != 0) { |
| INFO("SOCFPGA: FPGA configuration complete response, Return Code: 0x%x\n", |
| MBOX_RESP_ERR(-ret)); |
| return MBOX_RET_ERROR; |
| } |
| |
| return MBOX_RET_OK; |
| } |
| |
| int intel_mailbox_get_config_status(uint32_t cmd, bool init_done, |
| uint32_t *err_states) |
| { |
| 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 (err_states != NULL) |
| *err_states = res; |
| |
| if (res == MBOX_CFGSTAT_VAB_BS_PREAUTH) { |
| return MBOX_CFGSTAT_STATE_CONFIG; |
| } |
| |
| 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) { |
| return MBOX_CFGSTAT_STATE_ERROR_HARDWARE; |
| } |
| |
| 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, NULL); |
| |
| if ((ret != MBOX_RET_OK) && (ret != MBOX_CFGSTAT_STATE_CONFIG)) { |
| ret = intel_mailbox_get_config_status(MBOX_CONFIG_STATUS, |
| false, NULL); |
| } |
| |
| 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); |
| } |
| |
| int mailbox_seu_err_status(uint32_t *resp_buf, unsigned int resp_buf_len) |
| { |
| |
| return mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_SEU_ERR_READ, NULL, 0U, |
| CMD_CASUAL, resp_buf, |
| &resp_buf_len); |
| } |
| |
| int mailbox_safe_inject_seu_err(uint32_t *arg, unsigned int len) |
| { |
| return mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_SAFE_INJECT_SEU_ERR, arg, len, |
| CMD_CASUAL, NULL, NULL); |
| } |
| |
| #if SIP_SVC_V3 |
| static int mailbox_fill_cmd_desc(uint8_t client_id, uint8_t job_id, |
| uint32_t *resp_buff) |
| { |
| sdm_command_t *cmd_desc = NULL; |
| |
| /* Get a free command descriptor */ |
| cmd_desc = mailbox_get_free_cmd_desc(); |
| if (cmd_desc == NULL) { |
| return MBOX_BUFFER_FULL; |
| } |
| |
| /* Record all the given values for the command. */ |
| cmd_desc->client_id = client_id; |
| cmd_desc->job_id = job_id; |
| cmd_desc->cb = NULL; |
| cmd_desc->cb_args = resp_buff; |
| cmd_desc->cb_args_len = 0U; |
| |
| return MBOX_RET_OK; |
| } |
| |
| /* Returns the command descriptor based on the client and job ID. */ |
| static sdm_command_t *mailbox_get_cmd_desc(uint8_t client_id, uint8_t job_id) |
| { |
| spin_lock(&mbox_db_lock); |
| for (uint32_t count = 0; count < MBOX_SVC_CMD_QUEUE_SIZE; count++) { |
| if ((mbox_svc.cmd_queue[count].client_id == client_id) && |
| (mbox_svc.cmd_queue[count].job_id == job_id) && |
| (mbox_svc.cmd_queue[count].flags & MBOX_SVC_CMD_IS_USED)) { |
| spin_unlock(&mbox_db_lock); |
| return &(mbox_svc.cmd_queue[count]); |
| } |
| } |
| |
| spin_unlock(&mbox_db_lock); |
| VERBOSE("MBOX: Command descriptor not found for cid %d, jid %d\n", |
| client_id, job_id); |
| |
| return NULL; |
| } |
| |
| /* Returns the response descriptor based on only client ID. */ |
| static sdm_response_t *mailbox_get_resp_desc_cid(uint8_t client_id, uint8_t *index) |
| { |
| spin_lock(&mbox_db_lock); |
| |
| for (uint32_t count = 0; count < MBOX_SVC_RESP_QUEUE_SIZE; count++) { |
| if ((mbox_svc.resp_queue[count].client_id == client_id) && |
| (mbox_svc.resp_queue[count].flags & FLAG_SDM_RESPONSE_IS_VALID)) { |
| *index = count; |
| /* |
| * Once we get the valid response descriptor, get the |
| * job ID and mark up the bitmaps. |
| */ |
| uint8_t job_id = mbox_svc.resp_queue[count].job_id; |
| uint8_t transaction_id = MBOX_GET_TRANS_ID(client_id, job_id); |
| |
| mbox_svc.received_bitmap[transaction_id / MBOX_TID_BITMAP_SIZE] &= |
| ~(1ULL << (transaction_id % MBOX_TID_BITMAP_SIZE)); |
| mbox_svc.interrupt_bitmap[transaction_id / MBOX_TID_BITMAP_SIZE] &= |
| ~(1ULL << (transaction_id % MBOX_TID_BITMAP_SIZE)); |
| spin_unlock(&mbox_db_lock); |
| return &(mbox_svc.resp_queue[count]); |
| } |
| } |
| |
| spin_unlock(&mbox_db_lock); |
| VERBOSE("MBOX: Response descriptor not found for cid %d\n", client_id); |
| |
| return NULL; |
| } |
| |
| /* Returns the response descriptor based on the client and job ID. */ |
| static sdm_response_t *mailbox_get_resp_desc(uint8_t client_id, uint8_t job_id, uint8_t *index) |
| { |
| spin_lock(&mbox_db_lock); |
| /* |
| * Let's first check whether we have a response bitmap set for the given |
| * client ID and job ID. |
| */ |
| uint8_t transaction_id = MBOX_GET_TRANS_ID(client_id, job_id); |
| |
| if ((mbox_svc.received_bitmap[transaction_id / MBOX_TID_BITMAP_SIZE] & |
| (1ULL << (transaction_id % MBOX_TID_BITMAP_SIZE))) == 0) { |
| spin_unlock(&mbox_db_lock); |
| VERBOSE("MBOX: Response bitmap not set for cid %d, jid %d, bitmap 0x%16lx\n", |
| client_id, job_id, mbox_svc.received_bitmap[transaction_id / 64]); |
| return NULL; |
| } |
| |
| for (uint32_t count = 0; count < MBOX_SVC_RESP_QUEUE_SIZE; count++) { |
| if (mbox_svc.resp_queue[count].flags & FLAG_SDM_RESPONSE_IS_VALID) { |
| if ((mbox_svc.resp_queue[count].client_id == client_id) && |
| (mbox_svc.resp_queue[count].job_id == job_id)) { |
| *index = count; |
| mbox_svc.received_bitmap[transaction_id / MBOX_TID_BITMAP_SIZE] &= |
| ~(1ULL << (transaction_id % MBOX_TID_BITMAP_SIZE)); |
| mbox_svc.interrupt_bitmap[transaction_id / MBOX_TID_BITMAP_SIZE] &= |
| ~(1ULL << (transaction_id % MBOX_TID_BITMAP_SIZE)); |
| spin_unlock(&mbox_db_lock); |
| return &(mbox_svc.resp_queue[count]); |
| } |
| } |
| } |
| |
| spin_unlock(&mbox_db_lock); |
| VERBOSE("MBOX: Response descriptor not found for cid %d, jid %d\n", |
| client_id, job_id); |
| |
| return NULL; |
| } |
| |
| static int32_t mailbox_get_free_resp_desc(void) |
| { |
| spin_lock(&mbox_db_lock); |
| static uint32_t free_index = MBOX_SVC_RESP_QUEUE_SIZE - 1; |
| uint32_t count = 0U, try = 0U; |
| |
| for (try = 0; try < MBOX_SVC_RESP_QUEUE_SIZE; try++) { |
| free_index = (free_index + 1) % MBOX_SVC_RESP_QUEUE_SIZE; |
| if ((mbox_svc.resp_queue[free_index].flags & |
| FLAG_SDM_RESPONSE_IS_USED) != 0U) { |
| count = free_index; |
| spin_unlock(&mbox_db_lock); |
| return count; |
| } |
| } |
| |
| /* No free descriptors are available */ |
| spin_unlock(&mbox_db_lock); |
| VERBOSE("MBOX: No free response descriptors are available\n"); |
| |
| return MBOX_BUFFER_FULL; |
| } |
| |
| static sdm_command_t *mailbox_get_free_cmd_desc(void) |
| { |
| spin_lock(&mbox_db_lock); |
| static uint32_t free_index; |
| |
| /* Rollover the command queue free index */ |
| if (free_index == (MBOX_SVC_CMD_QUEUE_SIZE - 1)) { |
| free_index = 0U; |
| } |
| |
| for (; free_index < MBOX_SVC_CMD_QUEUE_SIZE; free_index++) { |
| if ((mbox_svc.cmd_queue[free_index].flags & |
| MBOX_SVC_CMD_IS_USED) != 0U) { |
| mbox_svc.cmd_queue[free_index].flags |= MBOX_SVC_CMD_IS_USED; |
| spin_unlock(&mbox_db_lock); |
| return &(mbox_svc.cmd_queue[free_index]); |
| } |
| } |
| |
| /* No free command descriptors are available */ |
| spin_unlock(&mbox_db_lock); |
| VERBOSE("MBOX: No free command descriptors available\n"); |
| |
| return NULL; |
| } |
| |
| static inline void mailbox_free_cmd_desc(sdm_command_t *cmd_desc) |
| { |
| if (cmd_desc == NULL) { |
| return; |
| } |
| |
| spin_lock(&mbox_db_lock); |
| memset((void *)cmd_desc, 0, sizeof(sdm_command_t)); |
| spin_unlock(&mbox_db_lock); |
| } |
| |
| static inline void mailbox_free_resp_desc(uint8_t index) |
| { |
| if (index >= MBOX_SVC_RESP_QUEUE_SIZE) { |
| return; |
| } |
| |
| spin_lock(&mbox_db_lock); |
| memset((void *)&mbox_svc.resp_queue[index], 0, sizeof(sdm_response_t)); |
| spin_unlock(&mbox_db_lock); |
| } |
| |
| /* |
| * This function serves the V1 _sync_read and _async_read functionality, and it |
| * is introduced as part of V3 framework to keep backward compatible with V1 |
| * clients. |
| */ |
| static int mailbox_read_response_v3(uint8_t client_id, uint8_t *job_id, |
| uint32_t *header, uint32_t *resp, |
| uint32_t *resp_len, |
| uint8_t ignore_client_id) |
| { |
| uint8_t di = 0U; |
| int status = MBOX_RET_OK; |
| sdm_response_t *resp_desc = NULL; |
| sdm_command_t *cmd_desc = NULL; |
| |
| /* |
| * In the V1, the client ID is always MBOX_ATF_CLIENT_ID and in this |
| * routine we will collect the response which only belongs to this |
| * client ID. So safe to ignore this input. |
| */ |
| (void)ignore_client_id; |
| |
| /* Clear the SDM doorbell interrupt */ |
| if (mmio_read_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM) == 1U) |
| mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM, 0U); |
| |
| /* Fill the command descriptor index and get the same */ |
| status = mailbox_fill_cmd_desc(client_id, async_v1_job_id, resp); |
| if (status != MBOX_RET_OK) { |
| return status; |
| } |
| |
| cmd_desc = mailbox_get_cmd_desc(client_id, async_v1_job_id); |
| |
| /* Get the response from SDM, just go through one cycle */ |
| status = mailbox_response_handler_fsm(); |
| if (status != MBOX_RET_OK) { |
| mailbox_free_cmd_desc(cmd_desc); |
| *resp_len = 0U; |
| return status; |
| } |
| |
| /* Check the local response queue with the given client ID */ |
| resp_desc = mailbox_get_resp_desc_cid(client_id, &di); |
| if (resp_desc == NULL) { |
| mailbox_free_cmd_desc(cmd_desc); |
| *resp_len = 0U; |
| return MBOX_NO_RESPONSE; |
| } |
| |
| /* Update the received mailbox response length, job ID and header */ |
| *job_id = resp_desc->job_id; |
| *resp_len = resp_desc->rcvd_resp_len; |
| if (header != NULL) { |
| *header = resp_desc->header; |
| } |
| |
| /* Check the mailbox response error code */ |
| if (MBOX_RESP_ERR(resp_desc->header) > 0U) { |
| INFO("MBOX: Error in async response: %x\n", resp_desc->header); |
| status = -MBOX_RESP_ERR(resp_desc->header); |
| } |
| |
| /* Free up the response and command descriptors */ |
| mailbox_free_resp_desc(di); |
| mailbox_free_cmd_desc(cmd_desc); |
| |
| return status; |
| } |
| |
| int mailbox_send_cmd_async_v3(uint8_t client_id, uint8_t job_id, uint32_t cmd, |
| uint32_t *args, uint32_t args_len, uint8_t cmd_flag, |
| sdm_command_callback cb, uint32_t *cb_args, |
| uint32_t cb_args_len) |
| { |
| int status = 0; |
| sdm_command_t *cmd_desc = NULL; |
| |
| VERBOSE("MBOX: cid: %d, jid: %d, cmd: %d, cmd_flag: %d\n", |
| client_id, job_id, cmd, cmd_flag); |
| |
| if (IS_CMD_SET(cmd_flag, URGENT)) { |
| mmio_write_32(MBOX_OFFSET + MBOX_URG, cmd); |
| mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_TO_SDM, 1U); |
| } else { |
| /* Get a free command descriptor */ |
| cmd_desc = mailbox_get_free_cmd_desc(); |
| if (cmd_desc == NULL) { |
| return MBOX_BUFFER_FULL; |
| } |
| |
| /* Record all the given values for the command. */ |
| cmd_desc->client_id = client_id; |
| cmd_desc->job_id = job_id; |
| cmd_desc->cb = cb; |
| cmd_desc->cb_args = cb_args; |
| cmd_desc->cb_args_len = cb_args_len; |
| |
| /* Push the command to mailbox FIFO */ |
| status = fill_mailbox_circular_buffer( |
| MBOX_FRAME_CMD_HEADER(client_id, job_id, |
| args_len, IS_CMD_SET(cmd_flag, INDIRECT), cmd), |
| args, |
| args_len); |
| |
| if (status != MBOX_RET_OK) { |
| INFO("MBOX: Failed to push the command to mailbox FIFO\n"); |
| /* Free the command descriptor. */ |
| mailbox_free_cmd_desc(cmd_desc); |
| } |
| } |
| |
| INFO("MBOX: %s: status: %d\n", __func__, status); |
| |
| return status; |
| } |
| |
| static int mailbox_poll_response_v3(uint8_t client_id, uint8_t job_id, |
| uint32_t *resp, unsigned int *resp_len, |
| uint32_t urgent) |
| { |
| unsigned int timeout = 40U; |
| unsigned int sdm_loop = 255U; |
| bool is_cmd_desc_fill = false; |
| uint8_t di = 0U; |
| sdm_response_t *resp_desc = NULL; |
| sdm_command_t *cmd_desc = NULL; |
| |
| 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) { |
| INFO("%s: Timed out waiting for SDM intr\n", __func__); |
| 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); |
| ERROR("MBOX: Mailbox did not get UA"); |
| return MBOX_RET_ERROR; |
| } |
| |
| /* Fill the command descriptor index and get the same. */ |
| if (!is_cmd_desc_fill) { |
| if (mailbox_fill_cmd_desc(client_id, job_id, resp) != |
| MBOX_RET_OK) { |
| return MBOX_BUFFER_FULL; |
| } |
| |
| cmd_desc = mailbox_get_cmd_desc(client_id, job_id); |
| is_cmd_desc_fill = true; |
| } |
| |
| /* Since it is sync call, will try to read till it time out */ |
| (void)mailbox_response_handler_fsm(); |
| |
| /* Check the response queue with the given client ID and job ID */ |
| resp_desc = mailbox_get_resp_desc(client_id, job_id, &di); |
| if (resp_desc != NULL) { |
| VERBOSE("%s: Resp received for cid %d, jid %d\n", |
| __func__, resp_desc->client_id, resp_desc->job_id); |
| |
| uint16_t header = resp_desc->header; |
| |
| /* Update the return response length */ |
| if (resp_len != NULL) { |
| *resp_len = resp_desc->rcvd_resp_len; |
| } |
| |
| /* Free the response and command descriptor */ |
| mailbox_free_resp_desc(di); |
| mailbox_free_cmd_desc(cmd_desc); |
| |
| if (MBOX_RESP_ERR(header) > 0U) { |
| INFO("%s: SDM err code: 0x%x\n", __func__, |
| MBOX_RESP_ERR(header)); |
| return -MBOX_RESP_ERR(header); |
| } |
| |
| VERBOSE("%s: MBOX_RET_OK\n", __func__); |
| return MBOX_RET_OK; |
| } |
| sdm_loop--; |
| } |
| |
| INFO("%s: Timed out waiting for SDM\n", __func__); |
| return MBOX_TIMEOUT; |
| } |
| |
| /* SDM response parser handler state machine. */ |
| static void mailbox_response_parser(void) |
| { |
| int di = -1; /* Descriptor index */ |
| uint32_t rin; |
| uint32_t rout; |
| |
| switch (mbox_svc.next_resp_state) { |
| case SRS_WAIT_FOR_RESP: |
| { |
| mbox_svc.resp_state = SRS_WAIT_FOR_RESP; |
| |
| rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN); |
| rout = mmio_read_32(MBOX_OFFSET + MBOX_ROUT); |
| if (rin != rout) { |
| mbox_svc.next_resp_state = SRS_WAIT_FOR_HEADER; |
| } |
| |
| break; |
| } |
| |
| case SRS_WAIT_FOR_HEADER: |
| { |
| mbox_svc.resp_state = SRS_WAIT_FOR_HEADER; |
| uint32_t resp_hdr; |
| uint8_t trans_id = 0U; |
| |
| rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN); |
| rout = mmio_read_32(MBOX_OFFSET + MBOX_ROUT); |
| if (rin != rout) { |
| /* Read the header and dequeue from the queue. */ |
| resp_hdr = mmio_read_32(MBOX_ENTRY_TO_ADDR(RESP, (rout)++)); |
| rout %= MBOX_RESP_BUFFER_SIZE; |
| mmio_write_32(MBOX_OFFSET + MBOX_ROUT, rout); |
| |
| /* Allocate a new response descriptor */ |
| di = mailbox_get_free_resp_desc(); |
| if (di != -1) { |
| mbox_svc.curr_di = di; |
| mbox_svc.resp_queue[di].header = resp_hdr; |
| mbox_svc.resp_queue[di].client_id = MBOX_RESP_CLIENT_ID(resp_hdr); |
| mbox_svc.resp_queue[di].job_id = MBOX_RESP_JOB_ID(resp_hdr); |
| mbox_svc.resp_queue[di].resp_len = MBOX_RESP_LEN(resp_hdr); |
| mbox_svc.resp_queue[di].flags |= (FLAG_SDM_RESPONSE_IS_USED | |
| FLAG_SDM_RESPONSE_IS_IN_PROGRESS); |
| mbox_svc.resp_queue[di].err_code = MBOX_RESP_ERR(resp_hdr); |
| trans_id = MBOX_RESP_TRANSACTION_ID(resp_hdr); |
| |
| VERBOSE("MBOX: Resp Hdr: cid %d, jid %d, len %d, err_code 0x%x\n", |
| mbox_svc.resp_queue[di].client_id, |
| mbox_svc.resp_queue[di].job_id, |
| mbox_svc.resp_queue[di].resp_len, |
| mbox_svc.resp_queue[di].err_code); |
| |
| /* Check if the response is an argument response */ |
| if (mbox_svc.resp_queue[di].resp_len > 0) { |
| mbox_svc.next_resp_state = SRS_WAIT_FOR_ARGUMENTS; |
| } else { |
| VERBOSE("MBOX: Received complete response with no args\n"); |
| /* Non argument response, done */ |
| mbox_svc.resp_queue[mbox_svc.curr_di].flags |= |
| FLAG_SDM_RESPONSE_IS_VALID; |
| |
| /* Go back to waiting for new response */ |
| mbox_svc.next_resp_state = SRS_WAIT_FOR_RESP; |
| mbox_svc.curr_di = -1; |
| |
| /* Mark the transaction ID as received */ |
| spin_lock(&mbox_db_lock); |
| mbox_svc.received_bitmap[trans_id / MBOX_TID_BITMAP_SIZE] |= |
| (1ULL << (trans_id % MBOX_TID_BITMAP_SIZE)); |
| spin_unlock(&mbox_db_lock); |
| } |
| } else { |
| mbox_svc.next_resp_state = SRS_SYNC_ERROR; |
| } |
| } |
| break; |
| } |
| |
| case SRS_WAIT_FOR_ARGUMENTS: |
| { |
| mbox_svc.resp_state = SRS_WAIT_FOR_ARGUMENTS; |
| uint16_t mbox_resp_len = mbox_svc.resp_queue[mbox_svc.curr_di].resp_len; |
| uint32_t *read_buff = NULL; |
| uint16_t read_len = 0U; |
| uint16_t read_max_len = 0U; |
| uint32_t timeout = 0U; |
| |
| /* Determine where to copy the buffer. */ |
| sdm_command_t *cmd_desc = mailbox_get_cmd_desc( |
| mbox_svc.resp_queue[mbox_svc.curr_di].client_id, |
| mbox_svc.resp_queue[mbox_svc.curr_di].job_id); |
| if (cmd_desc != NULL && cmd_desc->cb_args != NULL) { |
| read_buff = cmd_desc->cb_args; |
| read_max_len = mbox_resp_len; |
| } else { |
| read_buff = (uint32_t *)mbox_svc.resp_queue[mbox_svc.curr_di].resp_data; |
| read_max_len = MBOX_SVC_MAX_RESP_DATA_SIZE; |
| } |
| |
| rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN); |
| rout = mmio_read_32(MBOX_OFFSET + MBOX_ROUT); |
| |
| while ((read_len < mbox_resp_len) && (rin != rout) && (read_len < read_max_len)) { |
| timeout = 10000U; |
| |
| /* Copy the response data to the buffer */ |
| read_buff[read_len++] = mmio_read_32(MBOX_ENTRY_TO_ADDR(RESP, (rout)++)); |
| |
| VERBOSE("MBOX: 0x%x\n", read_buff[read_len - 1]); |
| |
| /* Update the read out buffer index */ |
| rout %= MBOX_RESP_BUFFER_SIZE; |
| mmio_write_32(MBOX_OFFSET + MBOX_ROUT, rout); |
| |
| /* |
| * The response buffer is of 16 words size, this loop checks |
| * if the response buffer is empty and if empty trigger an |
| * interrupt to SDM and wait for the response buffer to fill |
| */ |
| do { |
| if (read_len == mbox_resp_len) |
| break; |
| |
| rin = mmio_read_32(MBOX_OFFSET + MBOX_RIN); |
| if (rout == rin) { |
| mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_TO_SDM, 1U); |
| udelay(100); |
| } else { |
| break; |
| } |
| timeout--; |
| } while ((read_len < mbox_resp_len) && (timeout != 0U)); |
| |
| if (timeout == 0U) { |
| INFO("MBOX: Timeout waiting for response data\n"); |
| mbox_svc.next_resp_state = SRS_SYNC_ERROR; |
| break; |
| } |
| } |
| |
| /* Check if we have received all the arguments */ |
| mbox_svc.resp_queue[mbox_svc.curr_di].rcvd_resp_len = read_len; |
| if (mbox_resp_len == read_len) { |
| uint8_t transaction_id = |
| ((mbox_svc.resp_queue[mbox_svc.curr_di].client_id << 4) | |
| (mbox_svc.resp_queue[mbox_svc.curr_di].job_id)); |
| VERBOSE("MBOX: Received all the response data len %d, transaction_id %d\n", |
| read_len, transaction_id); |
| |
| spin_lock(&mbox_db_lock); |
| mbox_svc.received_bitmap[transaction_id / MBOX_TID_BITMAP_SIZE] |= |
| (1ULL << (transaction_id % MBOX_TID_BITMAP_SIZE)); |
| spin_unlock(&mbox_db_lock); |
| |
| mbox_svc.resp_queue[mbox_svc.curr_di].flags |= FLAG_SDM_RESPONSE_IS_VALID; |
| mbox_svc.next_resp_state = SRS_WAIT_FOR_RESP; |
| mbox_svc.curr_di = -1; |
| } else { |
| mbox_svc.next_resp_state = SRS_SYNC_ERROR; |
| VERBOSE("MBOX: Received partial response data len %d, max len %d\n", |
| read_len, read_max_len); |
| } |
| break; |
| } |
| |
| case SRS_SYNC_ERROR: |
| { |
| mbox_svc.resp_state = SRS_SYNC_ERROR; |
| INFO("MBOX: Error in response handling\n"); |
| break; |
| } |
| |
| default: |
| break; |
| } /* switch */ |
| } |
| |
| static int mailbox_response_handler_fsm(void) |
| { |
| int status = MBOX_RET_OK; |
| |
| spin_lock(&mbox_read_lock); |
| /* Mailbox peripheral response parser */ |
| do { |
| /* Iterate till the state machine transition ends */ |
| mailbox_response_parser(); |
| |
| /* Note down if there is any error in the response parsing */ |
| if (mbox_svc.next_resp_state == SRS_SYNC_ERROR) { |
| status = MBOX_RET_ERROR; |
| } |
| |
| } while (mbox_svc.resp_state != mbox_svc.next_resp_state); |
| spin_unlock(&mbox_read_lock); |
| |
| return status; |
| } |
| |
| int mailbox_response_poll_on_intr_v3(uint8_t *client_id, uint8_t *job_id, |
| uint64_t *bitmap) |
| { |
| uint32_t i = 0U; |
| int status = MBOX_RET_OK; |
| |
| /* Clear the SDM doorbell interrupt immediately */ |
| if (mmio_read_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM) == 1U) { |
| mmio_write_32(MBOX_OFFSET + MBOX_DOORBELL_FROM_SDM, 0U); |
| } |
| |
| /* Check mailbox FIFO for any pending responses available to read. */ |
| status = mailbox_response_handler_fsm(); |
| if (status != MBOX_RET_OK) { |
| return status; |
| } |
| |
| /* |
| * Once we read the complete mailbox FIFO, let's mark up the bitmap for |
| * available responses with respect to each transcation IDs. |
| */ |
| status = MBOX_NO_RESPONSE; |
| spin_lock(&mbox_db_lock); |
| for (i = 0; i < MBOX_MAX_TIDS_BITMAP; i++) { |
| bitmap[i] = mbox_svc.interrupt_bitmap[i] ^ mbox_svc.received_bitmap[i]; |
| if (bitmap[i] != 0 && status == MBOX_NO_RESPONSE) { |
| status = MBOX_RET_OK; |
| } |
| |
| mbox_svc.interrupt_bitmap[i] = mbox_svc.received_bitmap[i]; |
| } |
| spin_unlock(&mbox_db_lock); |
| |
| return status; |
| } |
| |
| int mailbox_response_poll_v3(uint8_t client_id, uint8_t job_id, |
| uint64_t *ret_args, uint32_t *ret_args_len) |
| { |
| sdm_command_t *cmd_desc = NULL; |
| sdm_response_t *resp_desc = NULL; |
| uint8_t di = 0U; |
| int status = MBOX_RET_OK; |
| |
| /* |
| * Let's first check the local response queue with the given |
| * client ID and job ID |
| */ |
| resp_desc = mailbox_get_resp_desc(client_id, job_id, &di); |
| if (resp_desc == NULL) { |
| /* Not available in the local queue, let's read mailbox FIFO */ |
| status = mailbox_response_handler_fsm(); |
| if (status != MBOX_RET_OK) { |
| return status; |
| } |
| |
| resp_desc = mailbox_get_resp_desc(client_id, job_id, &di); |
| } |
| cmd_desc = mailbox_get_cmd_desc(client_id, job_id); |
| |
| if (cmd_desc != NULL && resp_desc != NULL) { |
| VERBOSE("MBOX: Resp found for cid %d, jid %d\n", client_id, job_id); |
| |
| /* Command callback function */ |
| *ret_args_len = cmd_desc->cb(resp_desc, cmd_desc, ret_args); |
| |
| /* Free the command and response descriptors. */ |
| mailbox_free_cmd_desc(cmd_desc); |
| mailbox_free_resp_desc(di); |
| |
| return MBOX_RET_OK; |
| } |
| |
| INFO("MBOX: No resp found for cid: %d, jid: %d\n", client_id, job_id); |
| |
| return MBOX_NO_RESPONSE; |
| } |
| |
| void mailbox_init_v3(void) |
| { |
| uint32_t count; |
| |
| memset((void *)&mbox_svc, 0, sizeof(mbox_svc)); |
| |
| mbox_svc.next_resp_state = SRS_WAIT_FOR_RESP; |
| mbox_svc.resp_state = SRS_WAIT_FOR_RESP; |
| |
| /* Free all entries from the response queue. */ |
| for (count = 0; count < MBOX_SVC_RESP_QUEUE_SIZE; count++) { |
| mbox_svc.resp_queue[count].flags = 0; |
| } |
| |
| /* Free all entries from the command queue. */ |
| for (count = 0; count < MBOX_SVC_CMD_QUEUE_SIZE; count++) { |
| mbox_svc.cmd_queue[count].flags = 0; |
| } |
| |
| mbox_svc.curr_di = -1; |
| } |
| #endif /* #if SIP_SVC_V3 */ |