new file mode 100644
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "chip.h"
+#include "hw.h"
+#include "bus/pci/ipc.h"
+#include "fw/fw_msg.h"
+#include "fw/msg_cfm.h"
+#ifdef TRACE_SUPPORT
+#include "trace.h"
+#endif
+
+static void cl_msg_pci_fw_push(struct cl_hw *cl_hw, void *msg_buf, u16 len)
+{
+ /* Send a message to the embedded side */
+ int i;
+ u32 *src;
+ u32 *dst;
+ struct cl_ipc_host_env *ipc_env = cl_hw->ipc_env;
+
+ /* Copy the message into the IPC MSG buffer */
+ src = (u32 *)msg_buf;
+ dst = (u32 *)&ipc_env->shared->a2e_msg_buf;
+
+ /*
+ * Move the destination pointer forward by one word
+ * (due to the format of the firmware kernel messages)
+ */
+ dst++;
+
+ /* Align length of message to 4 */
+ len = ALIGN(len, sizeof(u32));
+
+ /* Copy the message in the IPC queue */
+ for (i = 0; i < len; i += sizeof(u32))
+ *dst++ = cpu_to_le32(*src++);
+
+ /* Trigger the irq to send the message to EMB */
+ cl_hw->ipc_host2xmac_trigger_set(cl_hw->chip, IPC_IRQ_A2E_MSG);
+}
+
+int cl_msg_pci_msg_fw_send(struct cl_hw *cl_hw, const void *msg_params,
+ bool background)
+{
+ struct fw_msg *msg = container_of((void *)msg_params, struct fw_msg, param);
+ u16 req_id = msg->msg_id;
+ u16 cfm_bit = cl_msg_cfm_set_bit(req_id);
+ int length = sizeof(struct fw_msg) + msg->param_len;
+ int error = 0;
+
+ if (!cl_hw->fw_active) {
+ cl_dbg_verbose(cl_hw, "Bypass %s (firmware not loaded)\n", MSG_ID_STR(req_id));
+ /* Free the message */
+ kfree(msg);
+ return -EBUSY;
+ }
+
+ if (test_bit(CL_DEV_FW_ERROR, &cl_hw->drv_flags)) {
+ cl_dbg_verbose(cl_hw, "Bypass %s (CL_DEV_FW_ERROR is set)\n", MSG_ID_STR(req_id));
+ /* Free the message */
+ kfree(msg);
+ return -EBUSY;
+ }
+
+ if (!test_bit(CL_DEV_STARTED, &cl_hw->drv_flags) &&
+ msg->msg_id != MM_RESET_REQ &&
+ msg->msg_id != MM_START_REQ) {
+ cl_dbg_verbose(cl_hw, "Bypass %s (CL_DEV_STARTED not set)\n", MSG_ID_STR(req_id));
+ /* Free the message */
+ kfree(msg);
+ return -EBUSY;
+ }
+
+ /* Lock msg tx of the correct msg buffer. */
+ error = mutex_lock_interruptible(&cl_hw->msg_tx_mutex);
+ if (error != 0) {
+ cl_dbg_verbose(cl_hw, "Bypass %s (mutex error %d)\n", MSG_ID_STR(req_id), error);
+ /* Free the message */
+ kfree(msg);
+ return error;
+ }
+
+ cl_hw->msg_background = background;
+
+ CFM_SET_BIT(cfm_bit, &cl_hw->cfm_flags);
+
+ cl_dbg_trace(cl_hw, "%s\n", MSG_ID_STR(req_id));
+
+ /* Push the message in the IPC */
+ cl_msg_pci_fw_push(cl_hw, msg, length);
+
+ /* Free the message */
+ kfree(msg);
+
+#ifdef TRACE_SUPPORT
+ trace_cl_trace_cl_msg_fw_send(cl_hw->idx, (int)req_id);
+#endif
+
+ return cl_msg_cfm_wait(cl_hw, cfm_bit, req_id);
+}
+