/* This file is provided under a dual BSD/GPLv2 license. When using or redistributing this file, you may do so under either license. GPL LICENSE SUMMARY Copyright(c) 2005-2012 Intel Corporation. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called LICENSE.GPL. Contact Information: Intel Corporation 2200 Mission College Blvd. Santa Clara, CA 97052 */ #include #include #include "aep_core.h" #include "aep_host_ipc_defs.h" #include "aep_ipc_config.h" #include "aep_types.h" #define AEP_IPC_TIMEOUT_US 1000000 /* 1 sec timeout */ /** * Function: is_aep_ready * */ aep_result_t is_aep_ready(host_ipc_handler *hipc, bool *is_cleared) { uint32_t temp = 0; uint32_t mask = (CONFIG_IPC_REGISTER_STAT_AEP_READY | CONFIG_IPC_REGISTER_STAT_AEP_DONE); temp = readl(hipc->dev_base + CONFIG_AEP_IPC_REGISTER_STATUS_OFFSET); *is_cleared = ((temp & mask) == mask) ? true : false; return AEP_SUCCESS; } /** @brief

Description:

Private function which waits until it IPC status bit Done and Ready are set. */ aep_ipc_ret_t wait_for_aep_fw_ready(host_ipc_handler *hipc) { uint32_t timeout = 0; /** Wait until AEP signals message received */ while ((readl(hipc->dev_base + CONFIG_AEP_IPC_REGISTER_STATUS_OFFSET) & AEP_DBL_READY) != AEP_DBL_READY ) { udelay(1); if (timeout >= AEP_IPC_TIMEOUT_US) { printk(KERN_ERR "AEP: Error - aep_send_command() failed on timeout,\ never got 'Ready' and 'Done'\n" ); return -1; } timeout++; } return 0; } /* Write to AEP IPC mailbox and send interrupt to AEP by writing to doorbell */ void HostIPC_StartSynchronousMessage(host_ipc_handler *hipc, const ipc_synchronous_message *syncm, int ipl_size ) { int i; uint32_t nsize; nsize = ipl_size; /* copy the message */ for (i = 0; i < (int)nsize/4 ; i++) { writel(syncm->data_payload[i], hipc->dev_base + CONFIG_IPC_SYNC_MESSAGE_OFFSET + (i << 2) ); } mod_timer(&hipc->timer, jiffies + 10 * HZ); writel(syncm->commandId, hipc->dev_base + REG_AEP_DBL); } aep_ipc_ret_t HostIPC_ReadSynchronousResponse(host_ipc_handler *hipc, void *message_buf, int offset, int message_buf_size ) { int i; uint32_t nsize; uint32_t *syncm; nsize = message_buf_size; syncm = (uint32_t *)message_buf; if (nsize > 64) nsize = 64; for (i = 0; i < (int)nsize/4 ; i++) { syncm[i] = readl(hipc->dev_base + CONFIG_IPC_READ_SYNC_MESSAGE_OFFSET + offset + (i << 2) ); } return AEP_IPC_RET_SUCCESS; } aep_ipc_ret_t hostIPC_SendResponse(host_ipc_handler *hipc, aep_fw_cmd_t ipc_cmd, aep_ipc_sizes_t io_sizes, ipl_t *ipl ) { ipc_synchronous_message sm; bool is_ready = false; aep_ipc_ret_t rc = AEP_IPC_RET_SUCCESS; /*Acquire semaphore to send response to a message sent by AEP*/ down(&hipc->sync_reponse_sema); do { is_aep_ready(hipc, &is_ready); if (is_ready) { memset(&sm, 0, sizeof(sm)); sm.commandId = ipc_cmd; if (NULL != ipl) memcpy(&sm.data_payload, ipl, io_sizes.ipl_size); down(&hipc->sync_msg_sema); HostIPC_StartSynchronousMessage(hipc, &sm, sizeof(sm)); up(&hipc->sync_msg_sema); } else { /* wait for interrupt event to occur */ wait_for_aep_fw_ready(hipc); } } while (!is_ready); /*Release semahore*/ up(&hipc->sync_reponse_sema); return rc; } aep_ipc_ret_t hostIPC_SendCmd(host_ipc_handler *hipc, aep_fw_cmd_t ipc_cmd, aep_ipc_sizes_t io_sizes, ipl_t *ipl, opl_t *opl ) { ipc_synchronous_message sm; uint32_t status; DECLARE_COMPLETION_ONSTACK(complete); bool is_ready = false; aep_ipc_ret_t rc = AEP_IPC_RET_SUCCESS; /*Acquire semaphore to send IPC cmd request to AEP*/ down(&hipc->sync_request_sema); do { is_aep_ready(hipc, &is_ready); if (is_ready) { memset(&sm, 0, sizeof(sm)); sm.commandId = ipc_cmd; if (NULL != ipl) memcpy(&sm.data_payload, ipl, io_sizes.ipl_size); /* Acquire spin lock for writing to IPC registers */ down(&hipc->sync_msg_sema); HostIPC_StartSynchronousMessage(hipc, &sm, sizeof(sm)); up(&hipc->sync_msg_sema); } else { /* wait for interrupt event to occur */ wait_for_aep_fw_ready(hipc); } } while (!is_ready); hipc->done_data = &complete; wait_for_completion(&complete); rc = hipc->error; if (AEP_IPC_RET_SUCCESS == rc) { HostIPC_ReadSynchronousResponse(hipc, &status, 0, sizeof(uint32_t)); if (NULL != opl) HostIPC_ReadSynchronousResponse(hipc, opl, 4, io_sizes.opl_size); rc = status; } /* Set IPC status to done and ready to receive next message from AEP*/ writel(AEP_DBL_DONE, hipc->dev_base + CONFIG_HOST_IPC_REGISTER_STATUS_OFFSET); /* Release semaphore */ up(&hipc->sync_request_sema); return rc; }