162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020-2023 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/genalloc.h> 762306a36Sopenharmony_ci#include <linux/highmem.h> 862306a36Sopenharmony_ci#include <linux/kthread.h> 962306a36Sopenharmony_ci#include <linux/wait.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "ivpu_drv.h" 1262306a36Sopenharmony_ci#include "ivpu_gem.h" 1362306a36Sopenharmony_ci#include "ivpu_hw.h" 1462306a36Sopenharmony_ci#include "ivpu_hw_reg_io.h" 1562306a36Sopenharmony_ci#include "ivpu_ipc.h" 1662306a36Sopenharmony_ci#include "ivpu_jsm_msg.h" 1762306a36Sopenharmony_ci#include "ivpu_pm.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define IPC_MAX_RX_MSG 128 2062306a36Sopenharmony_ci#define IS_KTHREAD() (get_current()->flags & PF_KTHREAD) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct ivpu_ipc_tx_buf { 2362306a36Sopenharmony_ci struct ivpu_ipc_hdr ipc; 2462306a36Sopenharmony_ci struct vpu_jsm_msg jsm; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct ivpu_ipc_rx_msg { 2862306a36Sopenharmony_ci struct list_head link; 2962306a36Sopenharmony_ci struct ivpu_ipc_hdr *ipc_hdr; 3062306a36Sopenharmony_ci struct vpu_jsm_msg *jsm_msg; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void ivpu_ipc_msg_dump(struct ivpu_device *vdev, char *c, 3462306a36Sopenharmony_ci struct ivpu_ipc_hdr *ipc_hdr, u32 vpu_addr) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci ivpu_dbg(vdev, IPC, 3762306a36Sopenharmony_ci "%s: vpu:0x%x (data_addr:0x%08x, data_size:0x%x, channel:0x%x, src_node:0x%x, dst_node:0x%x, status:0x%x)", 3862306a36Sopenharmony_ci c, vpu_addr, ipc_hdr->data_addr, ipc_hdr->data_size, ipc_hdr->channel, 3962306a36Sopenharmony_ci ipc_hdr->src_node, ipc_hdr->dst_node, ipc_hdr->status); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void ivpu_jsm_msg_dump(struct ivpu_device *vdev, char *c, 4362306a36Sopenharmony_ci struct vpu_jsm_msg *jsm_msg, u32 vpu_addr) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci u32 *payload = (u32 *)&jsm_msg->payload; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci ivpu_dbg(vdev, JSM, 4862306a36Sopenharmony_ci "%s: vpu:0x%08x (type:0x%x, status:0x%x, id: 0x%x, result: 0x%x, payload:0x%x 0x%x 0x%x 0x%x 0x%x)\n", 4962306a36Sopenharmony_ci c, vpu_addr, jsm_msg->type, jsm_msg->status, jsm_msg->request_id, jsm_msg->result, 5062306a36Sopenharmony_ci payload[0], payload[1], payload[2], payload[3], payload[4]); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void 5462306a36Sopenharmony_ciivpu_ipc_rx_mark_free(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr, 5562306a36Sopenharmony_ci struct vpu_jsm_msg *jsm_msg) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci ipc_hdr->status = IVPU_IPC_HDR_FREE; 5862306a36Sopenharmony_ci if (jsm_msg) 5962306a36Sopenharmony_ci jsm_msg->status = VPU_JSM_MSG_FREE; 6062306a36Sopenharmony_ci wmb(); /* Flush WC buffers for message statuses */ 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void ivpu_ipc_mem_fini(struct ivpu_device *vdev) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci ivpu_bo_free_internal(ipc->mem_rx); 6862306a36Sopenharmony_ci ivpu_bo_free_internal(ipc->mem_tx); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int 7262306a36Sopenharmony_ciivpu_ipc_tx_prepare(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, 7362306a36Sopenharmony_ci struct vpu_jsm_msg *req) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 7662306a36Sopenharmony_ci struct ivpu_ipc_tx_buf *tx_buf; 7762306a36Sopenharmony_ci u32 tx_buf_vpu_addr; 7862306a36Sopenharmony_ci u32 jsm_vpu_addr; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci tx_buf_vpu_addr = gen_pool_alloc(ipc->mm_tx, sizeof(*tx_buf)); 8162306a36Sopenharmony_ci if (!tx_buf_vpu_addr) { 8262306a36Sopenharmony_ci ivpu_err(vdev, "Failed to reserve IPC buffer, size %ld\n", 8362306a36Sopenharmony_ci sizeof(*tx_buf)); 8462306a36Sopenharmony_ci return -ENOMEM; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci tx_buf = ivpu_to_cpu_addr(ipc->mem_tx, tx_buf_vpu_addr); 8862306a36Sopenharmony_ci if (drm_WARN_ON(&vdev->drm, !tx_buf)) { 8962306a36Sopenharmony_ci gen_pool_free(ipc->mm_tx, tx_buf_vpu_addr, sizeof(*tx_buf)); 9062306a36Sopenharmony_ci return -EIO; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci jsm_vpu_addr = tx_buf_vpu_addr + offsetof(struct ivpu_ipc_tx_buf, jsm); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (tx_buf->ipc.status != IVPU_IPC_HDR_FREE) 9662306a36Sopenharmony_ci ivpu_warn(vdev, "IPC message vpu:0x%x not released by firmware\n", 9762306a36Sopenharmony_ci tx_buf_vpu_addr); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (tx_buf->jsm.status != VPU_JSM_MSG_FREE) 10062306a36Sopenharmony_ci ivpu_warn(vdev, "JSM message vpu:0x%x not released by firmware\n", 10162306a36Sopenharmony_ci jsm_vpu_addr); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci memset(tx_buf, 0, sizeof(*tx_buf)); 10462306a36Sopenharmony_ci tx_buf->ipc.data_addr = jsm_vpu_addr; 10562306a36Sopenharmony_ci /* TODO: Set data_size to actual JSM message size, not union of all messages */ 10662306a36Sopenharmony_ci tx_buf->ipc.data_size = sizeof(*req); 10762306a36Sopenharmony_ci tx_buf->ipc.channel = cons->channel; 10862306a36Sopenharmony_ci tx_buf->ipc.src_node = 0; 10962306a36Sopenharmony_ci tx_buf->ipc.dst_node = 1; 11062306a36Sopenharmony_ci tx_buf->ipc.status = IVPU_IPC_HDR_ALLOCATED; 11162306a36Sopenharmony_ci tx_buf->jsm.type = req->type; 11262306a36Sopenharmony_ci tx_buf->jsm.status = VPU_JSM_MSG_ALLOCATED; 11362306a36Sopenharmony_ci tx_buf->jsm.payload = req->payload; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci req->request_id = atomic_inc_return(&ipc->request_id); 11662306a36Sopenharmony_ci tx_buf->jsm.request_id = req->request_id; 11762306a36Sopenharmony_ci cons->request_id = req->request_id; 11862306a36Sopenharmony_ci wmb(); /* Flush WC buffers for IPC, JSM msgs */ 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci cons->tx_vpu_addr = tx_buf_vpu_addr; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ivpu_jsm_msg_dump(vdev, "TX", &tx_buf->jsm, jsm_vpu_addr); 12362306a36Sopenharmony_ci ivpu_ipc_msg_dump(vdev, "TX", &tx_buf->ipc, tx_buf_vpu_addr); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void ivpu_ipc_tx_release(struct ivpu_device *vdev, u32 vpu_addr) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (vpu_addr) 13362306a36Sopenharmony_ci gen_pool_free(ipc->mm_tx, vpu_addr, sizeof(struct ivpu_ipc_tx_buf)); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void ivpu_ipc_tx(struct ivpu_device *vdev, u32 vpu_addr) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci ivpu_hw_reg_ipc_tx_set(vdev, vpu_addr); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_civoid 14262306a36Sopenharmony_ciivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, u32 channel) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci INIT_LIST_HEAD(&cons->link); 14762306a36Sopenharmony_ci cons->channel = channel; 14862306a36Sopenharmony_ci cons->tx_vpu_addr = 0; 14962306a36Sopenharmony_ci cons->request_id = 0; 15062306a36Sopenharmony_ci spin_lock_init(&cons->rx_msg_lock); 15162306a36Sopenharmony_ci INIT_LIST_HEAD(&cons->rx_msg_list); 15262306a36Sopenharmony_ci init_waitqueue_head(&cons->rx_msg_wq); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci spin_lock_irq(&ipc->cons_list_lock); 15562306a36Sopenharmony_ci list_add_tail(&cons->link, &ipc->cons_list); 15662306a36Sopenharmony_ci spin_unlock_irq(&ipc->cons_list_lock); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_civoid ivpu_ipc_consumer_del(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 16262306a36Sopenharmony_ci struct ivpu_ipc_rx_msg *rx_msg, *r; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci spin_lock_irq(&ipc->cons_list_lock); 16562306a36Sopenharmony_ci list_del(&cons->link); 16662306a36Sopenharmony_ci spin_unlock_irq(&ipc->cons_list_lock); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci spin_lock_irq(&cons->rx_msg_lock); 16962306a36Sopenharmony_ci list_for_each_entry_safe(rx_msg, r, &cons->rx_msg_list, link) { 17062306a36Sopenharmony_ci list_del(&rx_msg->link); 17162306a36Sopenharmony_ci ivpu_ipc_rx_mark_free(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg); 17262306a36Sopenharmony_ci atomic_dec(&ipc->rx_msg_count); 17362306a36Sopenharmony_ci kfree(rx_msg); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci spin_unlock_irq(&cons->rx_msg_lock); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ivpu_ipc_tx_release(vdev, cons->tx_vpu_addr); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int 18162306a36Sopenharmony_ciivpu_ipc_send(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, struct vpu_jsm_msg *req) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 18462306a36Sopenharmony_ci int ret; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci mutex_lock(&ipc->lock); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (!ipc->on) { 18962306a36Sopenharmony_ci ret = -EAGAIN; 19062306a36Sopenharmony_ci goto unlock; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = ivpu_ipc_tx_prepare(vdev, cons, req); 19462306a36Sopenharmony_ci if (ret) 19562306a36Sopenharmony_ci goto unlock; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ivpu_ipc_tx(vdev, cons->tx_vpu_addr); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciunlock: 20062306a36Sopenharmony_ci mutex_unlock(&ipc->lock); 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ciint ivpu_ipc_receive(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, 20562306a36Sopenharmony_ci struct ivpu_ipc_hdr *ipc_buf, 20662306a36Sopenharmony_ci struct vpu_jsm_msg *ipc_payload, unsigned long timeout_ms) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 20962306a36Sopenharmony_ci struct ivpu_ipc_rx_msg *rx_msg; 21062306a36Sopenharmony_ci int wait_ret, ret = 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci wait_ret = wait_event_timeout(cons->rx_msg_wq, 21362306a36Sopenharmony_ci (IS_KTHREAD() && kthread_should_stop()) || 21462306a36Sopenharmony_ci !list_empty(&cons->rx_msg_list), 21562306a36Sopenharmony_ci msecs_to_jiffies(timeout_ms)); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (IS_KTHREAD() && kthread_should_stop()) 21862306a36Sopenharmony_ci return -EINTR; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (wait_ret == 0) 22162306a36Sopenharmony_ci return -ETIMEDOUT; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci spin_lock_irq(&cons->rx_msg_lock); 22462306a36Sopenharmony_ci rx_msg = list_first_entry_or_null(&cons->rx_msg_list, struct ivpu_ipc_rx_msg, link); 22562306a36Sopenharmony_ci if (!rx_msg) { 22662306a36Sopenharmony_ci spin_unlock_irq(&cons->rx_msg_lock); 22762306a36Sopenharmony_ci return -EAGAIN; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci list_del(&rx_msg->link); 23062306a36Sopenharmony_ci spin_unlock_irq(&cons->rx_msg_lock); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (ipc_buf) 23362306a36Sopenharmony_ci memcpy(ipc_buf, rx_msg->ipc_hdr, sizeof(*ipc_buf)); 23462306a36Sopenharmony_ci if (rx_msg->jsm_msg) { 23562306a36Sopenharmony_ci u32 size = min_t(int, rx_msg->ipc_hdr->data_size, sizeof(*ipc_payload)); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (rx_msg->jsm_msg->result != VPU_JSM_STATUS_SUCCESS) { 23862306a36Sopenharmony_ci ivpu_dbg(vdev, IPC, "IPC resp result error: %d\n", rx_msg->jsm_msg->result); 23962306a36Sopenharmony_ci ret = -EBADMSG; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (ipc_payload) 24362306a36Sopenharmony_ci memcpy(ipc_payload, rx_msg->jsm_msg, size); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci ivpu_ipc_rx_mark_free(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg); 24762306a36Sopenharmony_ci atomic_dec(&ipc->rx_msg_count); 24862306a36Sopenharmony_ci kfree(rx_msg); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return ret; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int 25462306a36Sopenharmony_ciivpu_ipc_send_receive_internal(struct ivpu_device *vdev, struct vpu_jsm_msg *req, 25562306a36Sopenharmony_ci enum vpu_ipc_msg_type expected_resp_type, 25662306a36Sopenharmony_ci struct vpu_jsm_msg *resp, u32 channel, 25762306a36Sopenharmony_ci unsigned long timeout_ms) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct ivpu_ipc_consumer cons; 26062306a36Sopenharmony_ci int ret; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ivpu_ipc_consumer_add(vdev, &cons, channel); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ret = ivpu_ipc_send(vdev, &cons, req); 26562306a36Sopenharmony_ci if (ret) { 26662306a36Sopenharmony_ci ivpu_warn(vdev, "IPC send failed: %d\n", ret); 26762306a36Sopenharmony_ci goto consumer_del; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci ret = ivpu_ipc_receive(vdev, &cons, NULL, resp, timeout_ms); 27162306a36Sopenharmony_ci if (ret) { 27262306a36Sopenharmony_ci ivpu_warn(vdev, "IPC receive failed: type 0x%x, ret %d\n", req->type, ret); 27362306a36Sopenharmony_ci goto consumer_del; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (resp->type != expected_resp_type) { 27762306a36Sopenharmony_ci ivpu_warn(vdev, "Invalid JSM response type: 0x%x\n", resp->type); 27862306a36Sopenharmony_ci ret = -EBADE; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ciconsumer_del: 28262306a36Sopenharmony_ci ivpu_ipc_consumer_del(vdev, &cons); 28362306a36Sopenharmony_ci return ret; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ciint ivpu_ipc_send_receive(struct ivpu_device *vdev, struct vpu_jsm_msg *req, 28762306a36Sopenharmony_ci enum vpu_ipc_msg_type expected_resp_type, 28862306a36Sopenharmony_ci struct vpu_jsm_msg *resp, u32 channel, 28962306a36Sopenharmony_ci unsigned long timeout_ms) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct vpu_jsm_msg hb_req = { .type = VPU_JSM_MSG_QUERY_ENGINE_HB }; 29262306a36Sopenharmony_ci struct vpu_jsm_msg hb_resp; 29362306a36Sopenharmony_ci int ret, hb_ret; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret = ivpu_rpm_get(vdev); 29662306a36Sopenharmony_ci if (ret < 0) 29762306a36Sopenharmony_ci return ret; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ret = ivpu_ipc_send_receive_internal(vdev, req, expected_resp_type, resp, 30062306a36Sopenharmony_ci channel, timeout_ms); 30162306a36Sopenharmony_ci if (ret != -ETIMEDOUT) 30262306a36Sopenharmony_ci goto rpm_put; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci hb_ret = ivpu_ipc_send_receive_internal(vdev, &hb_req, VPU_JSM_MSG_QUERY_ENGINE_HB_DONE, 30562306a36Sopenharmony_ci &hb_resp, VPU_IPC_CHAN_ASYNC_CMD, 30662306a36Sopenharmony_ci vdev->timeout.jsm); 30762306a36Sopenharmony_ci if (hb_ret == -ETIMEDOUT) { 30862306a36Sopenharmony_ci ivpu_hw_diagnose_failure(vdev); 30962306a36Sopenharmony_ci ivpu_pm_schedule_recovery(vdev); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cirpm_put: 31362306a36Sopenharmony_ci ivpu_rpm_put(vdev); 31462306a36Sopenharmony_ci return ret; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic bool 31862306a36Sopenharmony_ciivpu_ipc_match_consumer(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, 31962306a36Sopenharmony_ci struct ivpu_ipc_hdr *ipc_hdr, struct vpu_jsm_msg *jsm_msg) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci if (cons->channel != ipc_hdr->channel) 32262306a36Sopenharmony_ci return false; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!jsm_msg || jsm_msg->request_id == cons->request_id) 32562306a36Sopenharmony_ci return true; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return false; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void 33162306a36Sopenharmony_ciivpu_ipc_dispatch(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, 33262306a36Sopenharmony_ci struct ivpu_ipc_hdr *ipc_hdr, struct vpu_jsm_msg *jsm_msg) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 33562306a36Sopenharmony_ci struct ivpu_ipc_rx_msg *rx_msg; 33662306a36Sopenharmony_ci unsigned long flags; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci lockdep_assert_held(&ipc->cons_list_lock); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci rx_msg = kzalloc(sizeof(*rx_msg), GFP_ATOMIC); 34162306a36Sopenharmony_ci if (!rx_msg) { 34262306a36Sopenharmony_ci ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg); 34362306a36Sopenharmony_ci return; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci atomic_inc(&ipc->rx_msg_count); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci rx_msg->ipc_hdr = ipc_hdr; 34962306a36Sopenharmony_ci rx_msg->jsm_msg = jsm_msg; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci spin_lock_irqsave(&cons->rx_msg_lock, flags); 35262306a36Sopenharmony_ci list_add_tail(&rx_msg->link, &cons->rx_msg_list); 35362306a36Sopenharmony_ci spin_unlock_irqrestore(&cons->rx_msg_lock, flags); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci wake_up(&cons->rx_msg_wq); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ciint ivpu_ipc_irq_handler(struct ivpu_device *vdev) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 36162306a36Sopenharmony_ci struct ivpu_ipc_consumer *cons; 36262306a36Sopenharmony_ci struct ivpu_ipc_hdr *ipc_hdr; 36362306a36Sopenharmony_ci struct vpu_jsm_msg *jsm_msg; 36462306a36Sopenharmony_ci unsigned long flags; 36562306a36Sopenharmony_ci bool dispatched; 36662306a36Sopenharmony_ci u32 vpu_addr; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * Driver needs to purge all messages from IPC FIFO to clear IPC interrupt. 37062306a36Sopenharmony_ci * Without purge IPC FIFO to 0 next IPC interrupts won't be generated. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci while (ivpu_hw_reg_ipc_rx_count_get(vdev)) { 37362306a36Sopenharmony_ci vpu_addr = ivpu_hw_reg_ipc_rx_addr_get(vdev); 37462306a36Sopenharmony_ci if (vpu_addr == REG_IO_ERROR) { 37562306a36Sopenharmony_ci ivpu_err(vdev, "Failed to read IPC rx addr register\n"); 37662306a36Sopenharmony_ci return -EIO; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ipc_hdr = ivpu_to_cpu_addr(ipc->mem_rx, vpu_addr); 38062306a36Sopenharmony_ci if (!ipc_hdr) { 38162306a36Sopenharmony_ci ivpu_warn(vdev, "IPC msg 0x%x out of range\n", vpu_addr); 38262306a36Sopenharmony_ci continue; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci ivpu_ipc_msg_dump(vdev, "RX", ipc_hdr, vpu_addr); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci jsm_msg = NULL; 38762306a36Sopenharmony_ci if (ipc_hdr->channel != IVPU_IPC_CHAN_BOOT_MSG) { 38862306a36Sopenharmony_ci jsm_msg = ivpu_to_cpu_addr(ipc->mem_rx, ipc_hdr->data_addr); 38962306a36Sopenharmony_ci if (!jsm_msg) { 39062306a36Sopenharmony_ci ivpu_warn(vdev, "JSM msg 0x%x out of range\n", ipc_hdr->data_addr); 39162306a36Sopenharmony_ci ivpu_ipc_rx_mark_free(vdev, ipc_hdr, NULL); 39262306a36Sopenharmony_ci continue; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci ivpu_jsm_msg_dump(vdev, "RX", jsm_msg, ipc_hdr->data_addr); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (atomic_read(&ipc->rx_msg_count) > IPC_MAX_RX_MSG) { 39862306a36Sopenharmony_ci ivpu_warn(vdev, "IPC RX msg dropped, msg count %d\n", IPC_MAX_RX_MSG); 39962306a36Sopenharmony_ci ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg); 40062306a36Sopenharmony_ci continue; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci dispatched = false; 40462306a36Sopenharmony_ci spin_lock_irqsave(&ipc->cons_list_lock, flags); 40562306a36Sopenharmony_ci list_for_each_entry(cons, &ipc->cons_list, link) { 40662306a36Sopenharmony_ci if (ivpu_ipc_match_consumer(vdev, cons, ipc_hdr, jsm_msg)) { 40762306a36Sopenharmony_ci ivpu_ipc_dispatch(vdev, cons, ipc_hdr, jsm_msg); 40862306a36Sopenharmony_ci dispatched = true; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci spin_unlock_irqrestore(&ipc->cons_list_lock, flags); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (!dispatched) { 41562306a36Sopenharmony_ci ivpu_dbg(vdev, IPC, "IPC RX msg 0x%x dropped (no consumer)\n", vpu_addr); 41662306a36Sopenharmony_ci ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ciint ivpu_ipc_init(struct ivpu_device *vdev) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 42662306a36Sopenharmony_ci int ret = -ENOMEM; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ipc->mem_tx = ivpu_bo_alloc_internal(vdev, 0, SZ_16K, DRM_IVPU_BO_WC); 42962306a36Sopenharmony_ci if (!ipc->mem_tx) 43062306a36Sopenharmony_ci return ret; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci ipc->mem_rx = ivpu_bo_alloc_internal(vdev, 0, SZ_16K, DRM_IVPU_BO_WC); 43362306a36Sopenharmony_ci if (!ipc->mem_rx) 43462306a36Sopenharmony_ci goto err_free_tx; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci ipc->mm_tx = devm_gen_pool_create(vdev->drm.dev, __ffs(IVPU_IPC_ALIGNMENT), 43762306a36Sopenharmony_ci -1, "TX_IPC_JSM"); 43862306a36Sopenharmony_ci if (IS_ERR(ipc->mm_tx)) { 43962306a36Sopenharmony_ci ret = PTR_ERR(ipc->mm_tx); 44062306a36Sopenharmony_ci ivpu_err(vdev, "Failed to create gen pool, %pe\n", ipc->mm_tx); 44162306a36Sopenharmony_ci goto err_free_rx; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci ret = gen_pool_add(ipc->mm_tx, ipc->mem_tx->vpu_addr, ipc->mem_tx->base.size, -1); 44562306a36Sopenharmony_ci if (ret) { 44662306a36Sopenharmony_ci ivpu_err(vdev, "gen_pool_add failed, ret %d\n", ret); 44762306a36Sopenharmony_ci goto err_free_rx; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci INIT_LIST_HEAD(&ipc->cons_list); 45162306a36Sopenharmony_ci spin_lock_init(&ipc->cons_list_lock); 45262306a36Sopenharmony_ci drmm_mutex_init(&vdev->drm, &ipc->lock); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci ivpu_ipc_reset(vdev); 45562306a36Sopenharmony_ci return 0; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cierr_free_rx: 45862306a36Sopenharmony_ci ivpu_bo_free_internal(ipc->mem_rx); 45962306a36Sopenharmony_cierr_free_tx: 46062306a36Sopenharmony_ci ivpu_bo_free_internal(ipc->mem_tx); 46162306a36Sopenharmony_ci return ret; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_civoid ivpu_ipc_fini(struct ivpu_device *vdev) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci ivpu_ipc_mem_fini(vdev); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_civoid ivpu_ipc_enable(struct ivpu_device *vdev) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci mutex_lock(&ipc->lock); 47462306a36Sopenharmony_ci ipc->on = true; 47562306a36Sopenharmony_ci mutex_unlock(&ipc->lock); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_civoid ivpu_ipc_disable(struct ivpu_device *vdev) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 48162306a36Sopenharmony_ci struct ivpu_ipc_consumer *cons, *c; 48262306a36Sopenharmony_ci unsigned long flags; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci mutex_lock(&ipc->lock); 48562306a36Sopenharmony_ci ipc->on = false; 48662306a36Sopenharmony_ci mutex_unlock(&ipc->lock); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci spin_lock_irqsave(&ipc->cons_list_lock, flags); 48962306a36Sopenharmony_ci list_for_each_entry_safe(cons, c, &ipc->cons_list, link) 49062306a36Sopenharmony_ci wake_up(&cons->rx_msg_wq); 49162306a36Sopenharmony_ci spin_unlock_irqrestore(&ipc->cons_list_lock, flags); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_civoid ivpu_ipc_reset(struct ivpu_device *vdev) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct ivpu_ipc_info *ipc = vdev->ipc; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci mutex_lock(&ipc->lock); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci memset(ipc->mem_tx->kvaddr, 0, ipc->mem_tx->base.size); 50162306a36Sopenharmony_ci memset(ipc->mem_rx->kvaddr, 0, ipc->mem_rx->base.size); 50262306a36Sopenharmony_ci wmb(); /* Flush WC buffers for TX and RX rings */ 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci mutex_unlock(&ipc->lock); 50562306a36Sopenharmony_ci} 506