| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (C) 2023-2024, Advanced Micro Devices, Inc. | |
| */ | |
| #include <drm/amdxdna_accel.h> | |
| #include <drm/drm_cache.h> | |
| #include <drm/drm_device.h> | |
| #include <drm/drm_gem.h> | |
| #include <drm/drm_gem_shmem_helper.h> | |
| #include <drm/drm_print.h> | |
| #include <drm/gpu_scheduler.h> | |
| #include <linux/bitfield.h> | |
| #include <linux/errno.h> | |
| #include <linux/pci.h> | |
| #include <linux/types.h> | |
| #include <linux/xarray.h> | |
| #include "aie.h" | |
| #include "aie2_msg_priv.h" | |
| #include "aie2_pci.h" | |
| #include "amdxdna_ctx.h" | |
| #include "amdxdna_gem.h" | |
| #include "amdxdna_mailbox.h" | |
| #include "amdxdna_mailbox_helper.h" | |
| #include "amdxdna_pci_drv.h" | |
| #define EXEC_MSG_OPS(xdna) ((xdna)->dev_handle->exec_msg_ops) | |
| int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev) | |
| { | |
| DECLARE_AIE_MSG(suspend, MSG_OP_SUSPEND); | |
| int ret; | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) { | |
| XDNA_ERR(ndev->aie.xdna, "Failed to suspend fw, ret %d", ret); | |
| return ret; | |
| } | |
| return aie_psp_waitmode_poll(ndev->aie.psp_hdl); | |
| } | |
| int aie2_resume_fw(struct amdxdna_dev_hdl *ndev) | |
| { | |
| DECLARE_AIE_MSG(suspend, MSG_OP_RESUME); | |
| return aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| } | |
| int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value) | |
| { | |
| DECLARE_AIE_MSG(set_runtime_cfg, MSG_OP_SET_RUNTIME_CONFIG); | |
| int ret; | |
| req.type = type; | |
| req.value = value; | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) { | |
| XDNA_ERR(ndev->aie.xdna, "Failed to set runtime config, ret %d", ret); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value) | |
| { | |
| DECLARE_AIE_MSG(get_runtime_cfg, MSG_OP_GET_RUNTIME_CONFIG); | |
| int ret; | |
| req.type = type; | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) { | |
| XDNA_ERR(ndev->aie.xdna, "Failed to get runtime config, ret %d", ret); | |
| return ret; | |
| } | |
| *value = resp.value; | |
| return 0; | |
| } | |
| int aie2_assign_mgmt_pasid(struct amdxdna_dev_hdl *ndev, u16 pasid) | |
| { | |
| DECLARE_AIE_MSG(assign_mgmt_pasid, MSG_OP_ASSIGN_MGMT_PASID); | |
| req.pasid = pasid; | |
| return aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| } | |
| int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, | |
| struct amdxdna_drm_query_aie_version *version) | |
| { | |
| DECLARE_AIE_MSG(aie_version_info, MSG_OP_QUERY_AIE_VERSION); | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| int ret; | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) | |
| return ret; | |
| XDNA_DBG(xdna, "Query AIE version - major: %u minor: %u completed", | |
| resp.major, resp.minor); | |
| version->major = resp.major; | |
| version->minor = resp.minor; | |
| return 0; | |
| } | |
| int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, | |
| struct amdxdna_drm_query_aie_metadata *metadata) | |
| { | |
| DECLARE_AIE_MSG(aie_tile_info, MSG_OP_QUERY_AIE_TILE_INFO); | |
| int ret; | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) | |
| return ret; | |
| metadata->col_size = resp.info.size; | |
| metadata->cols = resp.info.cols; | |
| metadata->rows = resp.info.rows; | |
| metadata->version.major = resp.info.major; | |
| metadata->version.minor = resp.info.minor; | |
| metadata->core.row_count = resp.info.core_rows; | |
| metadata->core.row_start = resp.info.core_row_start; | |
| metadata->core.dma_channel_count = resp.info.core_dma_channels; | |
| metadata->core.lock_count = resp.info.core_locks; | |
| metadata->core.event_reg_count = resp.info.core_events; | |
| metadata->mem.row_count = resp.info.mem_rows; | |
| metadata->mem.row_start = resp.info.mem_row_start; | |
| metadata->mem.dma_channel_count = resp.info.mem_dma_channels; | |
| metadata->mem.lock_count = resp.info.mem_locks; | |
| metadata->mem.event_reg_count = resp.info.mem_events; | |
| metadata->shim.row_count = resp.info.shim_rows; | |
| metadata->shim.row_start = resp.info.shim_row_start; | |
| metadata->shim.dma_channel_count = resp.info.shim_dma_channels; | |
| metadata->shim.lock_count = resp.info.shim_locks; | |
| metadata->shim.event_reg_count = resp.info.shim_events; | |
| return 0; | |
| } | |
| int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev, | |
| struct amdxdna_fw_ver *fw_ver) | |
| { | |
| DECLARE_AIE_MSG(firmware_version, MSG_OP_GET_FIRMWARE_VERSION); | |
| int ret; | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) | |
| return ret; | |
| fw_ver->major = resp.major; | |
| fw_ver->minor = resp.minor; | |
| fw_ver->sub = resp.sub; | |
| fw_ver->build = resp.build; | |
| return 0; | |
| } | |
| static int aie2_destroy_context_req(struct amdxdna_dev_hdl *ndev, u32 id) | |
| { | |
| DECLARE_AIE_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT); | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| int ret; | |
| req.context_id = id; | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret && ret != -ENODEV) | |
| XDNA_WARN(xdna, "Destroy context failed, ret %d", ret); | |
| else if (ret == -ENODEV) | |
| XDNA_DBG(xdna, "Destroy context: device already stopped"); | |
| return ret; | |
| } | |
| static u32 aie2_get_context_priority(struct amdxdna_dev_hdl *ndev, | |
| struct amdxdna_hwctx *hwctx) | |
| { | |
| if (!AIE_FEATURE_ON(&ndev->aie, AIE2_PREEMPT)) | |
| return PRIORITY_HIGH; | |
| switch (hwctx->qos.priority) { | |
| case AMDXDNA_QOS_REALTIME_PRIORITY: | |
| return PRIORITY_REALTIME; | |
| case AMDXDNA_QOS_HIGH_PRIORITY: | |
| return PRIORITY_HIGH; | |
| case AMDXDNA_QOS_NORMAL_PRIORITY: | |
| return PRIORITY_NORMAL; | |
| case AMDXDNA_QOS_LOW_PRIORITY: | |
| return PRIORITY_LOW; | |
| default: | |
| return PRIORITY_HIGH; | |
| } | |
| } | |
| int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx) | |
| { | |
| DECLARE_AIE_MSG(create_ctx, MSG_OP_CREATE_CONTEXT); | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| struct xdna_mailbox_chann_res x2i; | |
| struct xdna_mailbox_chann_res i2x; | |
| struct cq_pair *cq_pair; | |
| u32 intr_reg; | |
| int ret; | |
| req.aie_type = 1; | |
| req.start_col = hwctx->start_col; | |
| req.num_col = hwctx->num_col; | |
| req.num_unused_col = hwctx->num_unused_col; | |
| req.num_cq_pairs_requested = 1; | |
| req.pasid = amdxdna_pasid_on(hwctx->client) ? hwctx->client->pasid : 0; | |
| req.context_priority = aie2_get_context_priority(ndev, hwctx); | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) | |
| return ret; | |
| hwctx->fw_ctx_id = resp.context_id; | |
| if (WARN_ON_ONCE(hwctx->fw_ctx_id == -1)) | |
| return -EINVAL; | |
| if (ndev->force_preempt_enabled) { | |
| ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FORCE_PREEMPT, &hwctx->fw_ctx_id); | |
| if (ret) { | |
| XDNA_ERR(xdna, "failed to enable force preempt %d", ret); | |
| goto del_ctx_req; | |
| } | |
| } | |
| cq_pair = &resp.cq_pair[0]; | |
| x2i.mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->x2i_q.head_addr); | |
| x2i.mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->x2i_q.tail_addr); | |
| x2i.rb_start_addr = AIE2_SRAM_OFF(ndev, cq_pair->x2i_q.buf_addr); | |
| x2i.rb_size = cq_pair->x2i_q.buf_size; | |
| i2x.mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->i2x_q.head_addr); | |
| i2x.mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->i2x_q.tail_addr); | |
| i2x.rb_start_addr = AIE2_SRAM_OFF(ndev, cq_pair->i2x_q.buf_addr); | |
| i2x.rb_size = cq_pair->i2x_q.buf_size; | |
| ret = pci_irq_vector(to_pci_dev(xdna->ddev.dev), resp.msix_id); | |
| if (ret == -EINVAL) { | |
| XDNA_ERR(xdna, "Alloc IRQ failed %d", ret); | |
| goto del_ctx_req; | |
| } | |
| intr_reg = i2x.mb_head_ptr_reg + 4; | |
| hwctx->priv->mbox_chann = xdna_mailbox_alloc_channel(ndev->mbox); | |
| if (!hwctx->priv->mbox_chann) { | |
| XDNA_ERR(xdna, "Not able to create channel"); | |
| ret = -EINVAL; | |
| goto del_ctx_req; | |
| } | |
| ret = xdna_mailbox_start_channel(hwctx->priv->mbox_chann, &x2i, &i2x, | |
| intr_reg, ret); | |
| if (ret) { | |
| XDNA_ERR(xdna, "Not able to create channel"); | |
| ret = -EINVAL; | |
| goto free_channel; | |
| } | |
| ndev->hwctx_num++; | |
| XDNA_DBG(xdna, "Mailbox channel irq: %d, msix_id: %d", ret, resp.msix_id); | |
| XDNA_DBG(xdna, "Created fw ctx %d pasid %d", hwctx->fw_ctx_id, hwctx->client->pasid); | |
| return 0; | |
| free_channel: | |
| xdna_mailbox_free_channel(hwctx->priv->mbox_chann); | |
| del_ctx_req: | |
| aie2_destroy_context_req(ndev, hwctx->fw_ctx_id); | |
| return ret; | |
| } | |
| int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx) | |
| { | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| int ret; | |
| if (!hwctx->priv->mbox_chann) | |
| return 0; | |
| xdna_mailbox_stop_channel(hwctx->priv->mbox_chann); | |
| ret = aie2_destroy_context_req(ndev, hwctx->fw_ctx_id); | |
| xdna_mailbox_free_channel(hwctx->priv->mbox_chann); | |
| XDNA_DBG(xdna, "Destroyed fw ctx %d", hwctx->fw_ctx_id); | |
| hwctx->priv->mbox_chann = NULL; | |
| hwctx->fw_ctx_id = -1; | |
| ndev->hwctx_num--; | |
| return ret; | |
| } | |
| static int aie2_send_host_buf_msgs(struct amdxdna_dev_hdl *ndev, u32 context_id, | |
| u64 addr, u64 size, u32 initial_opcode) | |
| { | |
| DECLARE_AIE_MSG(map_host_buffer, MSG_OP_MAP_HOST_BUFFER); | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| size_t chunk_size; | |
| int ret; | |
| chunk_size = xdna->dev_info->dev_mem_size; | |
| if (!size || !IS_ALIGNED(size, chunk_size)) { | |
| XDNA_ERR(xdna, "Invalid size 0x%llx for chunk 0x%lx", | |
| size, chunk_size); | |
| return -EINVAL; | |
| } | |
| msg.opcode = initial_opcode; | |
| do { | |
| req.context_id = context_id; | |
| req.buf_addr = addr; | |
| req.buf_size = chunk_size; | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) { | |
| XDNA_ERR(xdna, "fw ctx %d addr 0x%llx size 0x%lx", | |
| context_id, addr, chunk_size); | |
| return ret; | |
| } | |
| XDNA_DBG(xdna, "fw ctx %d host buf op 0x%x addr 0x%llx size 0x%lx", | |
| context_id, msg.opcode, addr, chunk_size); | |
| addr += chunk_size; | |
| size -= chunk_size; | |
| msg.opcode = MSG_OP_ADD_HOST_BUFFER; | |
| } while (size); | |
| return 0; | |
| } | |
| int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size) | |
| { | |
| return aie2_send_host_buf_msgs(ndev, context_id, addr, size, | |
| MSG_OP_MAP_HOST_BUFFER); | |
| } | |
| int aie2_add_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size) | |
| { | |
| if (!AIE_FEATURE_ON(&ndev->aie, AIE2_ADD_HOST_BUFFER)) | |
| return -EOPNOTSUPP; | |
| return aie2_send_host_buf_msgs(ndev, context_id, addr, size, | |
| MSG_OP_ADD_HOST_BUFFER); | |
| } | |
| static int amdxdna_hwctx_col_map(struct amdxdna_hwctx *hwctx, void *arg) | |
| { | |
| u32 *bitmap = arg; | |
| *bitmap |= GENMASK(hwctx->start_col + hwctx->num_col - 1, hwctx->start_col); | |
| return 0; | |
| } | |
| int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, | |
| u32 size, u32 *cols_filled) | |
| { | |
| DECLARE_AIE_MSG(aie_column_info, MSG_OP_QUERY_COL_STATUS); | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| u32 buf_sz, aie_bitmap = 0; | |
| struct amdxdna_client *client; | |
| dma_addr_t dma_addr; | |
| u8 *buff_addr; | |
| int ret; | |
| buf_sz = ndev->aie.metadata.cols * ndev->aie.metadata.col_size; | |
| buff_addr = amdxdna_alloc_msg_buffer(xdna, &buf_sz, &dma_addr); | |
| if (IS_ERR(buff_addr)) | |
| return PTR_ERR(buff_addr); | |
| /* Go through each hardware context and mark the AIE columns that are active */ | |
| list_for_each_entry(client, &xdna->client_list, node) | |
| amdxdna_hwctx_walk(client, &aie_bitmap, amdxdna_hwctx_col_map); | |
| *cols_filled = 0; | |
| req.dump_buff_addr = dma_addr; | |
| req.dump_buff_size = buf_sz; | |
| req.num_cols = hweight32(aie_bitmap); | |
| req.aie_bitmap = aie_bitmap; | |
| drm_clflush_virt_range(buff_addr, req.dump_buff_size); /* device can access */ | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) { | |
| XDNA_ERR(xdna, "Error during NPU query, status %d", ret); | |
| goto fail; | |
| } | |
| XDNA_DBG(xdna, "Query NPU status completed"); | |
| if (buf_sz < resp.size) { | |
| ret = -EINVAL; | |
| XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", buf_sz, resp.size); | |
| goto fail; | |
| } | |
| size = min(size, resp.size); | |
| if (copy_to_user(buf, buff_addr, size)) { | |
| ret = -EFAULT; | |
| XDNA_ERR(xdna, "Failed to copy NPU status to user space"); | |
| goto fail; | |
| } | |
| *cols_filled = aie_bitmap; | |
| fail: | |
| amdxdna_free_msg_buffer(xdna, buf_sz, buff_addr, dma_addr); | |
| return ret; | |
| } | |
| int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev, | |
| char __user *buf, u32 size, | |
| struct amdxdna_drm_query_telemetry_header *header) | |
| { | |
| DECLARE_AIE_MSG(get_telemetry, MSG_OP_GET_TELEMETRY); | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| dma_addr_t dma_addr; | |
| u32 buf_sz; | |
| u8 *addr; | |
| int ret; | |
| if (header->type >= MAX_TELEMETRY_TYPE) | |
| return -EINVAL; | |
| buf_sz = min(size, SZ_4M); | |
| addr = amdxdna_alloc_msg_buffer(xdna, &buf_sz, &dma_addr); | |
| if (IS_ERR(addr)) | |
| return PTR_ERR(addr); | |
| req.buf_addr = dma_addr; | |
| req.buf_size = buf_sz; | |
| req.type = header->type; | |
| drm_clflush_virt_range(addr, req.buf_size); /* device can access */ | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) { | |
| XDNA_ERR(xdna, "Query telemetry failed, status %d", ret); | |
| goto free_buf; | |
| } | |
| if (buf_sz < resp.size) { | |
| ret = -EINVAL; | |
| XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", buf_sz, resp.size); | |
| goto free_buf; | |
| } | |
| size = min(size, resp.size); | |
| if (copy_to_user(buf, addr, size)) { | |
| ret = -EFAULT; | |
| XDNA_ERR(xdna, "Failed to copy telemetry to user space"); | |
| goto free_buf; | |
| } | |
| header->major = resp.major; | |
| header->minor = resp.minor; | |
| free_buf: | |
| amdxdna_free_msg_buffer(xdna, buf_sz, addr, dma_addr); | |
| return ret; | |
| } | |
| int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, u32 size, | |
| void *handle, int (*cb)(void*, void __iomem *, size_t)) | |
| { | |
| struct async_event_msg_req req = { 0 }; | |
| struct xdna_mailbox_msg msg = { | |
| .send_data = (u8 *)&req, | |
| .send_size = sizeof(req), | |
| .handle = handle, | |
| .opcode = MSG_OP_REGISTER_ASYNC_EVENT_MSG, | |
| .notify_cb = cb, | |
| }; | |
| req.buf_addr = addr; | |
| req.buf_size = size; | |
| XDNA_DBG(ndev->aie.xdna, "Register addr 0x%llx size 0x%x", addr, size); | |
| return xdna_mailbox_send_msg(ndev->aie.mgmt_chann, &msg, TX_TIMEOUT); | |
| } | |
| int aie2_config_cu(struct amdxdna_hwctx *hwctx, | |
| int (*notify_cb)(void *, void __iomem *, size_t)) | |
| { | |
| struct mailbox_channel *chann = hwctx->priv->mbox_chann; | |
| struct amdxdna_dev *xdna = hwctx->client->xdna; | |
| u32 shift = xdna->dev_info->dev_mem_buf_shift; | |
| struct config_cu_req req = { 0 }; | |
| struct xdna_mailbox_msg msg; | |
| struct drm_gem_object *gobj; | |
| struct amdxdna_gem_obj *abo; | |
| int i; | |
| if (!chann) | |
| return -ENODEV; | |
| if (!hwctx->cus) | |
| return 0; | |
| if (hwctx->cus->num_cus > MAX_NUM_CUS) { | |
| XDNA_DBG(xdna, "Exceed maximum CU %d", MAX_NUM_CUS); | |
| return -EINVAL; | |
| } | |
| for (i = 0; i < hwctx->cus->num_cus; i++) { | |
| struct amdxdna_cu_config *cu = &hwctx->cus->cu_configs[i]; | |
| if (XDNA_MBZ_DBG(xdna, cu->pad, sizeof(cu->pad))) | |
| return -EINVAL; | |
| gobj = drm_gem_object_lookup(hwctx->client->filp, cu->cu_bo); | |
| if (!gobj) { | |
| XDNA_ERR(xdna, "Lookup GEM object failed"); | |
| return -EINVAL; | |
| } | |
| abo = to_xdna_obj(gobj); | |
| if (abo->type != AMDXDNA_BO_DEV) { | |
| drm_gem_object_put(gobj); | |
| XDNA_ERR(xdna, "Invalid BO type"); | |
| return -EINVAL; | |
| } | |
| req.cfgs[i] = FIELD_PREP(AIE2_MSG_CFG_CU_PDI_ADDR, | |
| amdxdna_gem_dev_addr(abo) >> shift); | |
| req.cfgs[i] |= FIELD_PREP(AIE2_MSG_CFG_CU_FUNC, cu->cu_func); | |
| XDNA_DBG(xdna, "CU %d full addr 0x%llx, cfg 0x%x", i, | |
| amdxdna_gem_dev_addr(abo), req.cfgs[i]); | |
| drm_gem_object_put(gobj); | |
| } | |
| req.num_cus = hwctx->cus->num_cus; | |
| msg.send_data = (u8 *)&req; | |
| msg.send_size = sizeof(req); | |
| msg.handle = hwctx; | |
| msg.opcode = MSG_OP_CONFIG_CU; | |
| msg.notify_cb = notify_cb; | |
| return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); | |
| } | |
| static int aie2_init_exec_cu_req(struct amdxdna_gem_obj *cmd_bo, void *req, | |
| size_t *size, u32 *msg_op) | |
| { | |
| struct execute_buffer_req *cu_req = req; | |
| u32 cmd_len; | |
| void *cmd; | |
| cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); | |
| if (cmd_len > sizeof(cu_req->payload)) | |
| return -EINVAL; | |
| cu_req->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo); | |
| if (cu_req->cu_idx == INVALID_CU_IDX) | |
| return -EINVAL; | |
| memcpy(cu_req->payload, cmd, cmd_len); | |
| *size = sizeof(*cu_req); | |
| *msg_op = MSG_OP_EXECUTE_BUFFER_CF; | |
| return 0; | |
| } | |
| static int aie2_init_exec_dpu_req(struct amdxdna_gem_obj *cmd_bo, void *req, | |
| size_t *size, u32 *msg_op) | |
| { | |
| struct exec_dpu_req *dpu_req = req; | |
| struct amdxdna_cmd_start_npu *sn; | |
| u32 cmd_len; | |
| sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); | |
| if (cmd_len - sizeof(*sn) > sizeof(dpu_req->payload)) | |
| return -EINVAL; | |
| dpu_req->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo); | |
| if (dpu_req->cu_idx == INVALID_CU_IDX) | |
| return -EINVAL; | |
| dpu_req->inst_buf_addr = sn->buffer; | |
| dpu_req->inst_size = sn->buffer_size; | |
| dpu_req->inst_prop_cnt = sn->prop_count; | |
| memcpy(dpu_req->payload, sn->prop_args, cmd_len - sizeof(*sn)); | |
| *size = sizeof(*dpu_req); | |
| *msg_op = MSG_OP_EXEC_DPU; | |
| return 0; | |
| } | |
| static void aie2_init_exec_chain_req(void *req, u64 slot_addr, size_t size, u32 cmd_cnt) | |
| { | |
| struct cmd_chain_req *chain_req = req; | |
| chain_req->buf_addr = slot_addr; | |
| chain_req->buf_size = size; | |
| chain_req->count = cmd_cnt; | |
| } | |
| static void aie2_init_npu_chain_req(void *req, u64 slot_addr, size_t size, u32 cmd_cnt) | |
| { | |
| struct cmd_chain_npu_req *npu_chain_req = req; | |
| npu_chain_req->flags = 0; | |
| npu_chain_req->reserved = 0; | |
| npu_chain_req->buf_addr = slot_addr; | |
| npu_chain_req->buf_size = size; | |
| npu_chain_req->count = cmd_cnt; | |
| } | |
| static int | |
| aie2_cmdlist_fill_cf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size) | |
| { | |
| struct cmd_chain_slot_execbuf_cf *cf_slot = slot; | |
| u32 cmd_len; | |
| void *cmd; | |
| cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); | |
| if (*size < sizeof(*cf_slot) + cmd_len) | |
| return -EINVAL; | |
| cf_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo); | |
| if (cf_slot->cu_idx == INVALID_CU_IDX) | |
| return -EINVAL; | |
| cf_slot->arg_cnt = cmd_len / sizeof(u32); | |
| memcpy(cf_slot->args, cmd, cmd_len); | |
| /* Accurate slot size to hint firmware to do necessary copy */ | |
| *size = sizeof(*cf_slot) + cmd_len; | |
| return 0; | |
| } | |
| static int | |
| aie2_cmdlist_fill_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size) | |
| { | |
| struct cmd_chain_slot_dpu *dpu_slot = slot; | |
| struct amdxdna_cmd_start_npu *sn; | |
| u32 cmd_len; | |
| u32 arg_sz; | |
| sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); | |
| arg_sz = cmd_len - sizeof(*sn); | |
| if (cmd_len < sizeof(*sn) || arg_sz > MAX_DPU_ARGS_SIZE) | |
| return -EINVAL; | |
| if (*size < sizeof(*dpu_slot) + arg_sz) | |
| return -EINVAL; | |
| dpu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo); | |
| if (dpu_slot->cu_idx == INVALID_CU_IDX) | |
| return -EINVAL; | |
| dpu_slot->inst_buf_addr = sn->buffer; | |
| dpu_slot->inst_size = sn->buffer_size; | |
| dpu_slot->inst_prop_cnt = sn->prop_count; | |
| dpu_slot->arg_cnt = arg_sz / sizeof(u32); | |
| memcpy(dpu_slot->args, sn->prop_args, arg_sz); | |
| /* Accurate slot size to hint firmware to do necessary copy */ | |
| *size = sizeof(*dpu_slot) + arg_sz; | |
| return 0; | |
| } | |
| static int aie2_cmdlist_unsupp(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size) | |
| { | |
| return -EOPNOTSUPP; | |
| } | |
| static u32 aie2_get_chain_msg_op(u32 cmd_op) | |
| { | |
| switch (cmd_op) { | |
| case ERT_START_CU: | |
| return MSG_OP_CHAIN_EXEC_BUFFER_CF; | |
| case ERT_START_NPU: | |
| return MSG_OP_CHAIN_EXEC_DPU; | |
| default: | |
| break; | |
| } | |
| return MSG_OP_MAX_OPCODE; | |
| } | |
| static struct aie2_exec_msg_ops legacy_exec_message_ops = { | |
| .init_cu_req = aie2_init_exec_cu_req, | |
| .init_dpu_req = aie2_init_exec_dpu_req, | |
| .init_chain_req = aie2_init_exec_chain_req, | |
| .fill_cf_slot = aie2_cmdlist_fill_cf, | |
| .fill_dpu_slot = aie2_cmdlist_fill_dpu, | |
| .fill_preempt_slot = aie2_cmdlist_unsupp, | |
| .fill_elf_slot = aie2_cmdlist_unsupp, | |
| .get_chain_msg_op = aie2_get_chain_msg_op, | |
| }; | |
| static int | |
| aie2_cmdlist_fill_npu_cf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size) | |
| { | |
| struct cmd_chain_slot_npu *npu_slot = slot; | |
| u32 cmd_len; | |
| void *cmd; | |
| cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); | |
| if (*size < sizeof(*npu_slot) + cmd_len) | |
| return -EINVAL; | |
| memset(npu_slot, 0, sizeof(*npu_slot)); | |
| npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo); | |
| if (npu_slot->cu_idx == INVALID_CU_IDX) | |
| return -EINVAL; | |
| npu_slot->type = EXEC_NPU_TYPE_NON_ELF; | |
| npu_slot->arg_cnt = cmd_len / sizeof(u32); | |
| memcpy(npu_slot->args, cmd, cmd_len); | |
| *size = sizeof(*npu_slot) + cmd_len; | |
| return 0; | |
| } | |
| static int | |
| aie2_cmdlist_fill_npu_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size) | |
| { | |
| struct cmd_chain_slot_npu *npu_slot = slot; | |
| struct amdxdna_cmd_start_npu *sn; | |
| u32 cmd_len; | |
| u32 arg_sz; | |
| sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); | |
| arg_sz = cmd_len - sizeof(*sn); | |
| if (cmd_len < sizeof(*sn) || arg_sz > MAX_NPU_ARGS_SIZE) | |
| return -EINVAL; | |
| if (*size < sizeof(*npu_slot) + arg_sz) | |
| return -EINVAL; | |
| memset(npu_slot, 0, sizeof(*npu_slot)); | |
| npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo); | |
| if (npu_slot->cu_idx == INVALID_CU_IDX) | |
| return -EINVAL; | |
| npu_slot->type = EXEC_NPU_TYPE_PARTIAL_ELF; | |
| npu_slot->inst_buf_addr = sn->buffer; | |
| npu_slot->inst_size = sn->buffer_size; | |
| npu_slot->inst_prop_cnt = sn->prop_count; | |
| npu_slot->arg_cnt = arg_sz / sizeof(u32); | |
| memcpy(npu_slot->args, sn->prop_args, arg_sz); | |
| *size = sizeof(*npu_slot) + arg_sz; | |
| return 0; | |
| } | |
| static int | |
| aie2_cmdlist_fill_npu_preempt(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size) | |
| { | |
| struct cmd_chain_slot_npu *npu_slot = slot; | |
| struct amdxdna_cmd_preempt_data *pd; | |
| u32 cmd_len; | |
| u32 arg_sz; | |
| pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); | |
| arg_sz = cmd_len - sizeof(*pd); | |
| if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE) | |
| return -EINVAL; | |
| if (*size < sizeof(*npu_slot) + arg_sz) | |
| return -EINVAL; | |
| memset(npu_slot, 0, sizeof(*npu_slot)); | |
| npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo); | |
| if (npu_slot->cu_idx == INVALID_CU_IDX) | |
| return -EINVAL; | |
| npu_slot->type = EXEC_NPU_TYPE_PREEMPT; | |
| npu_slot->inst_buf_addr = pd->inst_buf; | |
| npu_slot->save_buf_addr = pd->save_buf; | |
| npu_slot->restore_buf_addr = pd->restore_buf; | |
| npu_slot->inst_size = pd->inst_size; | |
| npu_slot->save_size = pd->save_size; | |
| npu_slot->restore_size = pd->restore_size; | |
| npu_slot->inst_prop_cnt = pd->inst_prop_cnt; | |
| npu_slot->arg_cnt = arg_sz / sizeof(u32); | |
| memcpy(npu_slot->args, pd->prop_args, arg_sz); | |
| *size = sizeof(*npu_slot) + arg_sz; | |
| return 0; | |
| } | |
| static int | |
| aie2_cmdlist_fill_npu_elf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size) | |
| { | |
| struct cmd_chain_slot_npu *npu_slot = slot; | |
| struct amdxdna_cmd_preempt_data *pd; | |
| u32 cmd_len; | |
| u32 arg_sz; | |
| pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); | |
| arg_sz = cmd_len - sizeof(*pd); | |
| if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE) | |
| return -EINVAL; | |
| if (*size < sizeof(*npu_slot) + arg_sz) | |
| return -EINVAL; | |
| memset(npu_slot, 0, sizeof(*npu_slot)); | |
| npu_slot->type = EXEC_NPU_TYPE_ELF; | |
| npu_slot->inst_buf_addr = pd->inst_buf; | |
| npu_slot->save_buf_addr = pd->save_buf; | |
| npu_slot->restore_buf_addr = pd->restore_buf; | |
| npu_slot->inst_size = pd->inst_size; | |
| npu_slot->save_size = pd->save_size; | |
| npu_slot->restore_size = pd->restore_size; | |
| npu_slot->inst_prop_cnt = pd->inst_prop_cnt; | |
| npu_slot->arg_cnt = 1; | |
| npu_slot->args[0] = AIE2_EXEC_BUFFER_KERNEL_OP_TXN; | |
| *size = struct_size(npu_slot, args, npu_slot->arg_cnt); | |
| return 0; | |
| } | |
| static u32 aie2_get_npu_chain_msg_op(u32 cmd_op) | |
| { | |
| return MSG_OP_CHAIN_EXEC_NPU; | |
| } | |
| static struct aie2_exec_msg_ops npu_exec_message_ops = { | |
| .init_cu_req = aie2_init_exec_cu_req, | |
| .init_dpu_req = aie2_init_exec_dpu_req, | |
| .init_chain_req = aie2_init_npu_chain_req, | |
| .fill_cf_slot = aie2_cmdlist_fill_npu_cf, | |
| .fill_dpu_slot = aie2_cmdlist_fill_npu_dpu, | |
| .fill_preempt_slot = aie2_cmdlist_fill_npu_preempt, | |
| .fill_elf_slot = aie2_cmdlist_fill_npu_elf, | |
| .get_chain_msg_op = aie2_get_npu_chain_msg_op, | |
| }; | |
| static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo, | |
| size_t *size, u32 *msg_op) | |
| { | |
| struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(cmd_abo)->dev); | |
| int ret; | |
| u32 op; | |
| op = amdxdna_cmd_get_op(cmd_abo); | |
| switch (op) { | |
| case ERT_START_CU: | |
| ret = EXEC_MSG_OPS(xdna)->init_cu_req(cmd_abo, req, size, msg_op); | |
| if (ret) { | |
| XDNA_DBG(xdna, "Init CU req failed ret %d", ret); | |
| return ret; | |
| } | |
| break; | |
| case ERT_START_NPU: | |
| ret = EXEC_MSG_OPS(xdna)->init_dpu_req(cmd_abo, req, size, msg_op); | |
| if (ret) { | |
| XDNA_DBG(xdna, "Init DPU req failed ret %d", ret); | |
| return ret; | |
| } | |
| break; | |
| default: | |
| XDNA_ERR(xdna, "Unsupported op %d", op); | |
| ret = -EOPNOTSUPP; | |
| break; | |
| } | |
| return ret; | |
| } | |
| static int | |
| aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo, | |
| size_t *size, u32 *cmd_op) | |
| { | |
| struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(cmd_abo)->dev); | |
| int ret; | |
| u32 op; | |
| op = amdxdna_cmd_get_op(cmd_abo); | |
| if (*cmd_op == ERT_INVALID_CMD) | |
| *cmd_op = op; | |
| else if (op != *cmd_op) | |
| return -EINVAL; | |
| switch (op) { | |
| case ERT_START_CU: | |
| ret = EXEC_MSG_OPS(xdna)->fill_cf_slot(cmd_abo, slot, size); | |
| break; | |
| case ERT_START_NPU: | |
| ret = EXEC_MSG_OPS(xdna)->fill_dpu_slot(cmd_abo, slot, size); | |
| break; | |
| case ERT_START_NPU_PREEMPT: | |
| if (!AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_PREEMPT)) | |
| return -EOPNOTSUPP; | |
| ret = EXEC_MSG_OPS(xdna)->fill_preempt_slot(cmd_abo, slot, size); | |
| break; | |
| case ERT_START_NPU_PREEMPT_ELF: | |
| if (!AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_PREEMPT)) | |
| return -EOPNOTSUPP; | |
| ret = EXEC_MSG_OPS(xdna)->fill_elf_slot(cmd_abo, slot, size); | |
| break; | |
| default: | |
| XDNA_INFO(xdna, "Unsupported op %d", op); | |
| ret = -EOPNOTSUPP; | |
| break; | |
| } | |
| return ret; | |
| } | |
| void aie2_msg_init(struct amdxdna_dev_hdl *ndev) | |
| { | |
| if (AIE_FEATURE_ON(&ndev->aie, AIE2_NPU_COMMAND)) | |
| ndev->exec_msg_ops = &npu_exec_message_ops; | |
| else | |
| ndev->exec_msg_ops = &legacy_exec_message_ops; | |
| } | |
| static inline struct amdxdna_gem_obj * | |
| aie2_cmdlist_get_cmd_buf(struct amdxdna_sched_job *job) | |
| { | |
| int idx = get_job_idx(job->seq); | |
| return job->hwctx->priv->cmd_buf[idx]; | |
| } | |
| int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, | |
| int (*notify_cb)(void *, void __iomem *, size_t)) | |
| { | |
| struct mailbox_channel *chann = hwctx->priv->mbox_chann; | |
| struct amdxdna_dev *xdna = hwctx->client->xdna; | |
| struct amdxdna_gem_obj *cmd_abo = job->cmd_bo; | |
| struct xdna_mailbox_msg msg; | |
| union exec_req req; | |
| int ret; | |
| if (!chann) | |
| return -ENODEV; | |
| ret = aie2_init_exec_req(&req, cmd_abo, &msg.send_size, &msg.opcode); | |
| if (ret) | |
| return ret; | |
| msg.handle = job; | |
| msg.notify_cb = notify_cb; | |
| msg.send_data = (u8 *)&req; | |
| print_hex_dump_debug("cmd: ", DUMP_PREFIX_OFFSET, 16, 4, &req, | |
| 0x40, false); | |
| ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); | |
| if (ret) { | |
| XDNA_ERR(xdna, "Send message failed"); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx, | |
| struct amdxdna_sched_job *job, | |
| int (*notify_cb)(void *, void __iomem *, size_t)) | |
| { | |
| struct amdxdna_gem_obj *cmdbuf_abo = aie2_cmdlist_get_cmd_buf(job); | |
| struct mailbox_channel *chann = hwctx->priv->mbox_chann; | |
| struct amdxdna_client *client = hwctx->client; | |
| struct amdxdna_gem_obj *cmd_abo = job->cmd_bo; | |
| void *cmd_buf = amdxdna_gem_vmap(cmdbuf_abo); | |
| struct amdxdna_dev *xdna = client->xdna; | |
| struct amdxdna_cmd_chain *payload; | |
| struct xdna_mailbox_msg msg; | |
| union exec_chain_req req; | |
| u32 payload_len, ccnt; | |
| u32 offset = 0; | |
| size_t size; | |
| int ret; | |
| u32 op; | |
| u32 i; | |
| if (!cmd_buf) | |
| return -ENOMEM; | |
| op = amdxdna_cmd_get_op(cmd_abo); | |
| payload = amdxdna_cmd_get_payload(cmd_abo, &payload_len); | |
| if (op != ERT_CMD_CHAIN) { | |
| XDNA_DBG(xdna, "Invalid op code %d", op); | |
| return -EINVAL; | |
| } | |
| if (!payload) { | |
| XDNA_DBG(xdna, "Failed to get command payload"); | |
| return -EINVAL; | |
| } | |
| ccnt = payload->command_count; | |
| if (!ccnt || payload_len < struct_size(payload, data, ccnt)) { | |
| XDNA_DBG(xdna, "Invalid command count %d", ccnt); | |
| return -EINVAL; | |
| } | |
| op = ERT_INVALID_CMD; | |
| for (i = 0; i < ccnt; i++) { | |
| u32 boh = (u32)(payload->data[i]); | |
| struct amdxdna_gem_obj *abo; | |
| abo = amdxdna_gem_get_obj(client, boh, AMDXDNA_BO_SHARE); | |
| if (!abo) { | |
| XDNA_ERR(xdna, "Failed to find cmd BO %d", boh); | |
| return -ENOENT; | |
| } | |
| size = cmdbuf_abo->mem.size - offset; | |
| ret = aie2_cmdlist_fill_slot(cmd_buf + offset, abo, &size, &op); | |
| amdxdna_gem_put_obj(abo); | |
| if (ret) | |
| return ret; | |
| offset += size; | |
| } | |
| XDNA_DBG(xdna, "Total %d commands:", ccnt); | |
| print_hex_dump_debug("cmdbufs: ", DUMP_PREFIX_OFFSET, 16, 4, | |
| cmd_buf, offset, false); | |
| msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op); | |
| if (msg.opcode == MSG_OP_MAX_OPCODE) | |
| return -EOPNOTSUPP; | |
| /* The offset is the accumulated total size of the cmd buffer */ | |
| EXEC_MSG_OPS(xdna)->init_chain_req(&req, amdxdna_gem_dev_addr(cmdbuf_abo), | |
| offset, ccnt); | |
| drm_clflush_virt_range(cmd_buf, offset); | |
| msg.handle = job; | |
| msg.notify_cb = notify_cb; | |
| msg.send_data = (u8 *)&req; | |
| msg.send_size = sizeof(req); | |
| print_hex_dump_debug("cmdlist msg: ", DUMP_PREFIX_OFFSET, 16, 4, | |
| &req, msg.send_size, false); | |
| ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); | |
| if (ret) { | |
| XDNA_ERR(xdna, "Send message failed"); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx, | |
| struct amdxdna_sched_job *job, | |
| int (*notify_cb)(void *, void __iomem *, size_t)) | |
| { | |
| struct amdxdna_gem_obj *cmdbuf_abo = aie2_cmdlist_get_cmd_buf(job); | |
| struct mailbox_channel *chann = hwctx->priv->mbox_chann; | |
| struct amdxdna_dev *xdna = hwctx->client->xdna; | |
| struct amdxdna_gem_obj *cmd_abo = job->cmd_bo; | |
| void *cmd_buf = amdxdna_gem_vmap(cmdbuf_abo); | |
| struct xdna_mailbox_msg msg; | |
| union exec_chain_req req; | |
| u32 op = ERT_INVALID_CMD; | |
| size_t size; | |
| int ret; | |
| if (!cmd_buf) | |
| return -ENOMEM; | |
| size = cmdbuf_abo->mem.size; | |
| ret = aie2_cmdlist_fill_slot(cmd_buf, cmd_abo, &size, &op); | |
| if (ret) | |
| return ret; | |
| print_hex_dump_debug("cmdbuf: ", DUMP_PREFIX_OFFSET, 16, 4, cmd_buf, size, false); | |
| msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op); | |
| if (msg.opcode == MSG_OP_MAX_OPCODE) | |
| return -EOPNOTSUPP; | |
| EXEC_MSG_OPS(xdna)->init_chain_req(&req, amdxdna_gem_dev_addr(cmdbuf_abo), size, 1); | |
| drm_clflush_virt_range(cmd_buf, size); | |
| msg.handle = job; | |
| msg.notify_cb = notify_cb; | |
| msg.send_data = (u8 *)&req; | |
| msg.send_size = sizeof(req); | |
| print_hex_dump_debug("cmdlist msg: ", DUMP_PREFIX_OFFSET, 16, 4, | |
| &req, msg.send_size, false); | |
| ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); | |
| if (ret) { | |
| XDNA_ERR(hwctx->client->xdna, "Send message failed"); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, | |
| int (*notify_cb)(void *, void __iomem *, size_t)) | |
| { | |
| struct mailbox_channel *chann = hwctx->priv->mbox_chann; | |
| struct amdxdna_gem_obj *abo = to_xdna_obj(job->bos[0]); | |
| struct amdxdna_dev *xdna = hwctx->client->xdna; | |
| struct xdna_mailbox_msg msg; | |
| struct sync_bo_req req; | |
| int ret = 0; | |
| req.src_addr = 0; | |
| req.dst_addr = amdxdna_dev_bo_offset(abo); | |
| req.size = abo->mem.size; | |
| /* Device to Host */ | |
| req.type = FIELD_PREP(AIE2_MSG_SYNC_BO_SRC_TYPE, SYNC_BO_DEV_MEM) | | |
| FIELD_PREP(AIE2_MSG_SYNC_BO_DST_TYPE, SYNC_BO_HOST_MEM); | |
| XDNA_DBG(xdna, "sync %d bytes src(0x%llx) to dst(0x%llx) completed", | |
| req.size, req.src_addr, req.dst_addr); | |
| msg.handle = job; | |
| msg.notify_cb = notify_cb; | |
| msg.send_data = (u8 *)&req; | |
| msg.send_size = sizeof(req); | |
| msg.opcode = MSG_OP_SYNC_BO; | |
| ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); | |
| if (ret) { | |
| XDNA_ERR(xdna, "Send message failed"); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, | |
| int (*notify_cb)(void *, void __iomem *, size_t)) | |
| { | |
| struct mailbox_channel *chann = hwctx->priv->mbox_chann; | |
| struct amdxdna_gem_obj *abo = to_xdna_obj(job->bos[0]); | |
| struct amdxdna_dev *xdna = hwctx->client->xdna; | |
| struct config_debug_bo_req req; | |
| struct xdna_mailbox_msg msg; | |
| if (job->drv_cmd->opcode == ATTACH_DEBUG_BO) | |
| req.config = DEBUG_BO_REGISTER; | |
| else | |
| req.config = DEBUG_BO_UNREGISTER; | |
| req.offset = amdxdna_dev_bo_offset(abo); | |
| req.size = abo->mem.size; | |
| XDNA_DBG(xdna, "offset 0x%llx size 0x%llx config %d", | |
| req.offset, req.size, req.config); | |
| msg.handle = job; | |
| msg.notify_cb = notify_cb; | |
| msg.send_data = (u8 *)&req; | |
| msg.send_size = sizeof(req); | |
| msg.opcode = MSG_OP_CONFIG_DEBUG_BO; | |
| return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); | |
| } | |
| int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id, | |
| struct app_health_report *report) | |
| { | |
| DECLARE_AIE_MSG(get_app_health, MSG_OP_GET_APP_HEALTH); | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| struct app_health_report *buf; | |
| dma_addr_t dma_addr; | |
| u32 buf_size; | |
| int ret; | |
| if (!AIE_FEATURE_ON(&ndev->aie, AIE2_APP_HEALTH)) { | |
| XDNA_DBG(xdna, "App health feature not supported"); | |
| return -EOPNOTSUPP; | |
| } | |
| buf_size = sizeof(*report); | |
| buf = amdxdna_alloc_msg_buffer(xdna, &buf_size, &dma_addr); | |
| if (IS_ERR(buf)) { | |
| XDNA_ERR(xdna, "Failed to allocate buffer for app health"); | |
| return PTR_ERR(buf); | |
| } | |
| req.buf_addr = dma_addr; | |
| req.context_id = context_id; | |
| req.buf_size = buf_size; | |
| drm_clflush_virt_range(buf, req.buf_size); | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) { | |
| XDNA_ERR(xdna, "Get app health failed, ret %d status 0x%x", ret, resp.status); | |
| goto free_buf; | |
| } | |
| /* Copy the report to caller's buffer */ | |
| memcpy(report, buf, sizeof(*report)); | |
| free_buf: | |
| amdxdna_free_msg_buffer(xdna, buf_size, buf, dma_addr); | |
| return ret; | |
| } | |
| static int | |
| aie2_runtime_update_ctx_prop(struct amdxdna_dev_hdl *ndev, | |
| struct amdxdna_hwctx *ctx, u32 type, u32 value) | |
| { | |
| DECLARE_AIE_MSG(update_property, MSG_OP_UPDATE_PROPERTY); | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| int ret; | |
| if (!AIE_FEATURE_ON(&ndev->aie, AIE2_UPDATE_PROPERTY)) | |
| return -EOPNOTSUPP; | |
| if (ctx) | |
| req.context_id = ctx->fw_ctx_id; | |
| else | |
| req.context_id = AIE2_UPDATE_PROPERTY_ALL_CTX; | |
| req.time_quota_us = value; | |
| req.type = type; | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) { | |
| XDNA_ERR(xdna, "%s update property failed, type %d ret %d", | |
| ctx ? ctx->name : "ctx.all", type, ret); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| int aie2_update_prop_time_quota(struct amdxdna_dev_hdl *ndev, u32 us) | |
| { | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| int ret; | |
| ret = aie2_runtime_update_ctx_prop(ndev, NULL, UPDATE_PROPERTY_TIME_QUOTA, us); | |
| if (ret == -EOPNOTSUPP) { | |
| XDNA_DBG(xdna, "update time quota not support, skipped"); | |
| ret = 0; | |
| } else if (!ret) { | |
| XDNA_DBG(xdna, "Ctx exec time quantum updated to %u us", us); | |
| } | |
| return ret; | |
| } | |
| int aie2_get_dev_revision(struct amdxdna_dev_hdl *ndev, enum aie2_dev_revision *rev) | |
| { | |
| DECLARE_AIE_MSG(get_dev_revision, MSG_OP_GET_DEV_REVISION); | |
| struct amdxdna_dev *xdna = ndev->aie.xdna; | |
| int ret; | |
| if (!AIE_FEATURE_ON(&ndev->aie, AIE2_GET_DEV_REVISION)) | |
| return -EOPNOTSUPP; | |
| ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); | |
| if (ret) | |
| return ret; | |
| *rev = resp.rev; | |
| if (*rev < AIE2_DEV_REVISION_STXA || *rev >= AIE2_DEV_REVISION_UNKN) { | |
| XDNA_ERR(xdna, "Unknown device revision: %d (raw fuse: 0x%x)", | |
| *rev, resp.raw_fuse_data); | |
| return -EINVAL; | |
| } | |
| XDNA_DBG(xdna, "Device revision: %d (raw fuse: 0x%x)", *rev, resp.raw_fuse_data); | |
| return 0; | |
| } |