// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2023-2024, Advanced Micro Devices, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aie2_msg_priv.h" #include "aie2_pci.h" #include "aie2_solver.h" #include "amdxdna_ctx.h" #include "amdxdna_gem.h" #include "amdxdna_mailbox.h" #include "amdxdna_pci_drv.h" #include "amdxdna_pm.h" static int aie2_max_col = XRS_MAX_COL; module_param(aie2_max_col, uint, 0600); MODULE_PARM_DESC(aie2_max_col, "Maximum column could be used"); #define DEFAULT_TIME_QUANTUM 30000 /* microseconds */ static char *npu_fw[] = { "npu_7.sbin", "npu.sbin" }; /* * The management mailbox channel is allocated by firmware. * The related register and ring buffer information is on SRAM BAR. * This struct is the register layout. */ #define MGMT_MBOX_MAGIC 0x55504e5f /* _NPU */ struct mgmt_mbox_chann_info { __u32 x2i_tail; __u32 x2i_head; __u32 x2i_buf; __u32 x2i_buf_sz; __u32 i2x_tail; __u32 i2x_head; __u32 i2x_buf; __u32 i2x_buf_sz; __u32 magic; __u32 msi_id; __u32 prot_major; __u32 prot_minor; __u32 rsvd[4]; }; static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev) { struct mgmt_mbox_chann_info info_regs; struct xdna_mailbox_chann_res *i2x; struct xdna_mailbox_chann_res *x2i; u32 addr, off; u32 *reg; int ret; int i; /* * Once firmware is alive, it will write management channel * information in SRAM BAR and write the address of that information * at FW_ALIVE_OFF offset in SRMA BAR. * * Read a non-zero value from FW_ALIVE_OFF implies that firmware * is alive. */ ret = readx_poll_timeout(readl, SRAM_GET_ADDR(ndev, FW_ALIVE_OFF), addr, addr, AIE_INTERVAL, AIE_TIMEOUT); if (ret || !addr) return -ETIME; off = AIE2_SRAM_OFF(ndev, addr); reg = (u32 *)&info_regs; for (i = 0; i < sizeof(info_regs) / sizeof(u32); i++) reg[i] = readl(ndev->sram_base + off + i * sizeof(u32)); if (info_regs.magic != MGMT_MBOX_MAGIC) { XDNA_ERR(ndev->aie.xdna, "Invalid mbox magic 0x%x", info_regs.magic); ret = -EINVAL; goto done; } i2x = &ndev->aie.mgmt_i2x; x2i = &ndev->aie.mgmt_x2i; i2x->mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.i2x_head); i2x->mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.i2x_tail); i2x->rb_start_addr = AIE2_SRAM_OFF(ndev, info_regs.i2x_buf); i2x->rb_size = info_regs.i2x_buf_sz; x2i->mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.x2i_head); x2i->mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.x2i_tail); x2i->rb_start_addr = AIE2_SRAM_OFF(ndev, info_regs.x2i_buf); x2i->rb_size = info_regs.x2i_buf_sz; ndev->aie.mgmt_chan_idx = info_regs.msi_id; ndev->aie.mgmt_prot_major = info_regs.prot_major; ndev->aie.mgmt_prot_minor = info_regs.prot_minor; ret = aie_check_protocol(&ndev->aie, ndev->aie.mgmt_prot_major, ndev->aie.mgmt_prot_minor); done: aie_dump_mgmt_chann_debug(&ndev->aie); /* Must clear address at FW_ALIVE_OFF */ writel(0, SRAM_GET_ADDR(ndev, FW_ALIVE_OFF)); return ret; } int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev, enum rt_config_category category, u32 *val) { const struct rt_config *cfg; u32 value; int ret; for (cfg = ndev->priv->rt_config; cfg->type; cfg++) { if (cfg->category != category) continue; if (cfg->feature_mask && bitmap_subset(&cfg->feature_mask, &ndev->aie.feature_mask, AIE2_FEATURE_MAX)) continue; value = val ? *val : cfg->value; ret = aie2_set_runtime_cfg(ndev, cfg->type, value); if (ret) { XDNA_ERR(ndev->aie.xdna, "Set type %d value %d failed", cfg->type, value); return ret; } } return 0; } static int aie2_xdna_reset(struct amdxdna_dev_hdl *ndev) { int ret; ret = aie2_suspend_fw(ndev); if (ret) { XDNA_ERR(ndev->aie.xdna, "Suspend firmware failed"); return ret; } ret = aie2_resume_fw(ndev); if (ret) { XDNA_ERR(ndev->aie.xdna, "Resume firmware failed"); return ret; } return 0; } static int aie2_mgmt_fw_init(struct amdxdna_dev_hdl *ndev) { int ret; ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_INIT, NULL); if (ret) { XDNA_ERR(ndev->aie.xdna, "Runtime config failed"); return ret; } ret = aie2_assign_mgmt_pasid(ndev, 0); if (ret) { XDNA_ERR(ndev->aie.xdna, "Can not assign PASID"); return ret; } ret = aie2_update_prop_time_quota(ndev, DEFAULT_TIME_QUANTUM); if (ret) { XDNA_ERR(ndev->aie.xdna, "Failed to update execution time quantum"); return ret; } ret = aie2_xdna_reset(ndev); if (ret) { XDNA_ERR(ndev->aie.xdna, "Reset firmware failed"); return ret; } return 0; } static int aie2_mgmt_fw_query(struct amdxdna_dev_hdl *ndev) { int ret; ret = aie2_query_firmware_version(ndev, &ndev->aie.xdna->fw_ver); if (ret) { XDNA_ERR(ndev->aie.xdna, "query firmware version failed"); return ret; } ret = aie2_query_aie_version(ndev, &ndev->version); if (ret) { XDNA_ERR(ndev->aie.xdna, "Query AIE version failed"); return ret; } ret = aie2_query_aie_metadata(ndev, &ndev->aie.metadata); if (ret) { XDNA_ERR(ndev->aie.xdna, "Query AIE metadata failed"); return ret; } ndev->total_col = min(aie2_max_col, ndev->aie.metadata.cols); return 0; } static void aie2_mgmt_fw_fini(struct amdxdna_dev_hdl *ndev) { if (aie2_suspend_fw(ndev)) XDNA_ERR(ndev->aie.xdna, "Suspend_fw failed"); XDNA_DBG(ndev->aie.xdna, "Firmware suspended"); } static int aie2_xrs_load(void *cb_arg, struct xrs_action_load *action) { struct amdxdna_hwctx *hwctx = cb_arg; struct amdxdna_dev *xdna; int ret; xdna = hwctx->client->xdna; hwctx->start_col = action->part.start_col; hwctx->num_unused_col = action->part.ncols - hwctx->num_col; hwctx->num_col = action->part.ncols; ret = aie2_create_context(xdna->dev_handle, hwctx); if (ret) XDNA_ERR(xdna, "create context failed, ret %d", ret); return ret; } static int aie2_xrs_unload(void *cb_arg) { struct amdxdna_hwctx *hwctx = cb_arg; struct amdxdna_dev *xdna; int ret; xdna = hwctx->client->xdna; ret = aie2_destroy_context(xdna->dev_handle, hwctx); if (ret) XDNA_ERR(xdna, "destroy context failed, ret %d", ret); return ret; } static int aie2_xrs_set_dft_dpm_level(struct drm_device *ddev, u32 dpm_level) { struct amdxdna_dev *xdna = to_xdna_dev(ddev); struct amdxdna_dev_hdl *ndev; drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); ndev = xdna->dev_handle; ndev->dft_dpm_level = dpm_level; if (ndev->pw_mode != POWER_MODE_DEFAULT || ndev->dpm_level == dpm_level) return 0; return aie2_pm_set_dpm(ndev, dpm_level); } static struct xrs_action_ops aie2_xrs_actions = { .load = aie2_xrs_load, .unload = aie2_xrs_unload, .set_dft_dpm_level = aie2_xrs_set_dft_dpm_level, }; static void aie2_smu_fini(struct amdxdna_dev_hdl *ndev) { ndev->priv->hw_ops->set_dpm(ndev, 0); aie_smu_fini(ndev->aie.smu_hdl); } static void aie2_hw_stop(struct amdxdna_dev *xdna) { struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev); struct amdxdna_dev_hdl *ndev = xdna->dev_handle; if (ndev->dev_status <= AIE2_DEV_INIT) { XDNA_ERR(xdna, "device is already stopped"); return; } aie2_runtime_cfg(ndev, AIE2_RT_CFG_CLK_GATING, NULL); aie2_mgmt_fw_fini(ndev); aie_destroy_chann(&ndev->aie, &ndev->aie.mgmt_chann); drmm_kfree(&xdna->ddev, ndev->mbox); ndev->mbox = NULL; aie_psp_stop(ndev->aie.psp_hdl); aie2_smu_fini(ndev); aie2_error_async_events_free(ndev); pci_disable_device(pdev); ndev->dev_status = AIE2_DEV_INIT; } static int aie2_hw_start(struct amdxdna_dev *xdna) { struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev); struct amdxdna_dev_hdl *ndev = xdna->dev_handle; struct xdna_mailbox_res mbox_res; u32 xdna_mailbox_intr_reg; int mgmt_mb_irq, ret; if (ndev->dev_status >= AIE2_DEV_START) { XDNA_INFO(xdna, "device is already started"); return 0; } ret = pci_enable_device(pdev); if (ret) { XDNA_ERR(xdna, "failed to enable device, ret %d", ret); return ret; } pci_set_master(pdev); mbox_res.ringbuf_base = ndev->sram_base; mbox_res.ringbuf_size = pci_resource_len(pdev, xdna->dev_info->sram_bar); mbox_res.mbox_base = ndev->mbox_base; mbox_res.mbox_size = MBOX_SIZE(ndev); mbox_res.name = "xdna_mailbox"; ndev->mbox = xdnam_mailbox_create(&xdna->ddev, &mbox_res); if (!ndev->mbox) { XDNA_ERR(xdna, "failed to create mailbox device"); ret = -ENODEV; goto disable_dev; } ndev->aie.mgmt_chann = xdna_mailbox_alloc_channel(ndev->mbox); if (!ndev->aie.mgmt_chann) { XDNA_ERR(xdna, "failed to alloc channel"); ret = -ENODEV; goto disable_dev; } ret = aie_smu_init(ndev->aie.smu_hdl); if (ret) { XDNA_ERR(xdna, "failed to init smu, ret %d", ret); goto free_channel; } ret = aie_psp_start(ndev->aie.psp_hdl); if (ret) { XDNA_ERR(xdna, "failed to start psp, ret %d", ret); goto fini_smu; } ret = aie2_get_mgmt_chann_info(ndev); if (ret) { XDNA_ERR(xdna, "firmware is not alive"); goto stop_psp; } mgmt_mb_irq = pci_irq_vector(pdev, ndev->aie.mgmt_chan_idx); if (mgmt_mb_irq < 0) { ret = mgmt_mb_irq; XDNA_ERR(xdna, "failed to alloc irq vector, ret %d", ret); goto stop_psp; } xdna_mailbox_intr_reg = ndev->aie.mgmt_i2x.mb_head_ptr_reg + 4; ret = xdna_mailbox_start_channel(ndev->aie.mgmt_chann, &ndev->aie.mgmt_x2i, &ndev->aie.mgmt_i2x, xdna_mailbox_intr_reg, mgmt_mb_irq); if (ret) { XDNA_ERR(xdna, "failed to start management mailbox channel"); ret = -EINVAL; goto stop_psp; } ret = aie2_mgmt_fw_init(ndev); if (ret) { XDNA_ERR(xdna, "initial mgmt firmware failed, ret %d", ret); goto stop_fw; } ret = aie2_pm_init(ndev); if (ret) { XDNA_ERR(xdna, "failed to init pm, ret %d", ret); goto stop_fw; } ret = aie2_mgmt_fw_query(ndev); if (ret) { XDNA_ERR(xdna, "failed to query fw, ret %d", ret); goto stop_fw; } ret = aie2_error_async_events_alloc(ndev); if (ret) { XDNA_ERR(xdna, "Allocate async events failed, ret %d", ret); goto stop_fw; } WRITE_ONCE(ndev->last_signal_ts, jiffies); ndev->dev_status = AIE2_DEV_START; return 0; stop_fw: aie2_suspend_fw(ndev); xdna_mailbox_stop_channel(ndev->aie.mgmt_chann); stop_psp: aie_psp_stop(ndev->aie.psp_hdl); fini_smu: aie2_smu_fini(ndev); free_channel: xdna_mailbox_free_channel(ndev->aie.mgmt_chann); ndev->aie.mgmt_chann = NULL; disable_dev: pci_disable_device(pdev); return ret; } static int aie2_hw_suspend(struct amdxdna_dev *xdna) { struct amdxdna_client *client; list_for_each_entry(client, &xdna->client_list, node) aie2_hwctx_suspend(client); aie2_hw_stop(xdna); return 0; } static int aie2_hw_resume(struct amdxdna_dev *xdna) { struct amdxdna_client *client; int ret; ret = aie2_hw_start(xdna); if (ret) { XDNA_ERR(xdna, "Start hardware failed, %d", ret); return ret; } list_for_each_entry(client, &xdna->client_list, node) { ret = aie2_hwctx_resume(client); if (ret) break; } return ret; } static int aie2_init(struct amdxdna_dev *xdna) { struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev); void __iomem *tbl[PCI_NUM_RESOURCES] = {0}; struct init_config xrs_cfg = { 0 }; struct amdxdna_dev_hdl *ndev; struct psp_config psp_conf = { 0 }; struct smu_config smu_conf; const struct firmware *fw; unsigned long bars = 0; char *fw_full_path; int i, nvec, ret; if (!hypervisor_is_type(X86_HYPER_NATIVE)) { XDNA_ERR(xdna, "Running under hypervisor not supported"); return -EINVAL; } if (!xdna->group) { XDNA_ERR(xdna, "Running without IOMMU not supported"); return -EINVAL; } ndev = drmm_kzalloc(&xdna->ddev, sizeof(*ndev), GFP_KERNEL); if (!ndev) return -ENOMEM; ndev->priv = xdna->dev_info->dev_priv; ndev->aie.xdna = xdna; for (i = 0; i < ARRAY_SIZE(npu_fw); i++) { fw_full_path = kasprintf(GFP_KERNEL, "%s%s", ndev->priv->fw_path, npu_fw[i]); if (!fw_full_path) return -ENOMEM; ret = firmware_request_nowarn(&fw, fw_full_path, &pdev->dev); kfree(fw_full_path); if (!ret) { XDNA_INFO(xdna, "Load firmware %s%s", ndev->priv->fw_path, npu_fw[i]); break; } } if (ret) { XDNA_ERR(xdna, "failed to request_firmware %s, ret %d", ndev->priv->fw_path, ret); return ret; } ret = pcim_enable_device(pdev); if (ret) { XDNA_ERR(xdna, "pcim enable device failed, ret %d", ret); goto release_fw; } for (i = 0; i < PSP_MAX_REGS; i++) set_bit(PSP_REG_BAR(ndev, i), &bars); for (i = 0; i < SMU_MAX_REGS; i++) set_bit(SMU_REG_BAR(ndev, i), &bars); set_bit(xdna->dev_info->sram_bar, &bars); set_bit(xdna->dev_info->mbox_bar, &bars); for (i = 0; i < PCI_NUM_RESOURCES; i++) { if (!test_bit(i, &bars)) continue; tbl[i] = pcim_iomap(pdev, i, 0); if (!tbl[i]) { XDNA_ERR(xdna, "map bar %d failed", i); ret = -ENOMEM; goto release_fw; } } ndev->sram_base = tbl[xdna->dev_info->sram_bar]; ndev->mbox_base = tbl[xdna->dev_info->mbox_bar]; ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (ret) { XDNA_ERR(xdna, "Failed to set DMA mask: %d", ret); goto release_fw; } nvec = pci_msix_vec_count(pdev); if (nvec <= 0) { XDNA_ERR(xdna, "does not get number of interrupt vector"); ret = -EINVAL; goto release_fw; } ret = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX); if (ret < 0) { XDNA_ERR(xdna, "failed to alloc irq vectors, ret %d", ret); goto release_fw; } psp_conf.fw_size = fw->size; psp_conf.fw_buf = fw->data; psp_conf.arg2_mask = GENMASK(23, 0); psp_conf.notify_val = 1; for (i = 0; i < PSP_MAX_REGS; i++) psp_conf.psp_regs[i] = tbl[PSP_REG_BAR(ndev, i)] + PSP_REG_OFF(ndev, i); ndev->aie.psp_hdl = aiem_psp_create(&xdna->ddev, &psp_conf); if (!ndev->aie.psp_hdl) { XDNA_ERR(xdna, "failed to create psp"); ret = -ENOMEM; goto release_fw; } for (i = 0; i < SMU_MAX_REGS; i++) smu_conf.smu_regs[i] = tbl[SMU_REG_BAR(ndev, i)] + SMU_REG_OFF(ndev, i); ndev->aie.smu_hdl = aiem_smu_create(&xdna->ddev, &smu_conf); if (!ndev->aie.smu_hdl) { XDNA_ERR(xdna, "failed to create smu"); ret = -ENOMEM; goto release_fw; } xdna->dev_handle = ndev; ret = aie2_hw_start(xdna); if (ret) { XDNA_ERR(xdna, "start npu failed, ret %d", ret); goto release_fw; } xrs_cfg.clk_list.num_levels = ndev->max_dpm_level + 1; for (i = 0; i < xrs_cfg.clk_list.num_levels; i++) xrs_cfg.clk_list.cu_clk_list[i] = ndev->priv->dpm_clk_tbl[i].hclk; xrs_cfg.sys_eff_factor = 2; xrs_cfg.ddev = &xdna->ddev; xrs_cfg.actions = &aie2_xrs_actions; xrs_cfg.total_col = ndev->total_col; xdna->xrs_hdl = xrsm_init(&xrs_cfg); if (!xdna->xrs_hdl) { XDNA_ERR(xdna, "Initialize resolver failed"); ret = -EINVAL; goto stop_hw; } release_firmware(fw); aie2_msg_init(ndev); amdxdna_vbnv_init(xdna); amdxdna_pm_init(xdna); return 0; stop_hw: aie2_hw_stop(xdna); release_fw: release_firmware(fw); return ret; } static void aie2_fini(struct amdxdna_dev *xdna) { amdxdna_pm_fini(xdna); aie2_hw_stop(xdna); } static int aie2_get_aie_status(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_drm_query_aie_status status = {}; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; u32 buf_sz; int ret; ndev = xdna->dev_handle; buf_sz = min(args->buffer_size, sizeof(status)); if (copy_from_user(&status, u64_to_user_ptr(args->buffer), buf_sz)) { XDNA_ERR(xdna, "Failed to copy AIE request into kernel"); return -EFAULT; } ret = aie2_query_status(ndev, u64_to_user_ptr(status.buffer), status.buffer_size, &status.cols_filled); if (ret) { XDNA_ERR(xdna, "Failed to get AIE status info. Ret: %d", ret); return ret; } if (copy_to_user(u64_to_user_ptr(args->buffer), &status, buf_sz)) { XDNA_ERR(xdna, "Failed to copy AIE request info to user space"); return -EFAULT; } return 0; } static int aie2_get_aie_version(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_drm_query_aie_version version; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; u32 buf_sz; ndev = xdna->dev_handle; version.major = ndev->version.major; version.minor = ndev->version.minor; buf_sz = min(args->buffer_size, sizeof(version)); if (copy_to_user(u64_to_user_ptr(args->buffer), &version, buf_sz)) return -EFAULT; return 0; } static int aie2_get_firmware_version(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_drm_query_firmware_version version; struct amdxdna_dev *xdna = client->xdna; u32 buf_sz; version.major = xdna->fw_ver.major; version.minor = xdna->fw_ver.minor; version.patch = xdna->fw_ver.sub; version.build = xdna->fw_ver.build; buf_sz = min(args->buffer_size, sizeof(version)); if (copy_to_user(u64_to_user_ptr(args->buffer), &version, buf_sz)) return -EFAULT; return 0; } static int aie2_get_power_mode(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_drm_get_power_mode mode = {}; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; u32 buf_sz; ndev = xdna->dev_handle; mode.power_mode = ndev->pw_mode; buf_sz = min(args->buffer_size, sizeof(mode)); if (copy_to_user(u64_to_user_ptr(args->buffer), &mode, buf_sz)) return -EFAULT; return 0; } static int aie2_get_clock_metadata(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_drm_query_clock_metadata *clock; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; int ret = 0; u32 buf_sz; ndev = xdna->dev_handle; clock = kzalloc_obj(*clock); if (!clock) return -ENOMEM; aie2_update_counters(ndev); snprintf(clock->mp_npu_clock.name, sizeof(clock->mp_npu_clock.name), "MP-NPU Clock"); clock->mp_npu_clock.freq_mhz = ndev->npuclk_freq; snprintf(clock->h_clock.name, sizeof(clock->h_clock.name), "H Clock"); clock->h_clock.freq_mhz = ndev->hclk_freq; buf_sz = min(args->buffer_size, sizeof(*clock)); if (copy_to_user(u64_to_user_ptr(args->buffer), clock, buf_sz)) ret = -EFAULT; kfree(clock); return ret; } static int aie2_get_sensors(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_dev_hdl *ndev = client->xdna->dev_handle; struct amdxdna_drm_query_sensor sensor = {}; struct amd_pmf_npu_metrics npu_metrics; u32 sensors_count = 0, i; int ret; ret = AIE2_GET_PMF_NPU_METRICS(&npu_metrics); if (ret) return ret; sensor.type = AMDXDNA_SENSOR_TYPE_POWER; sensor.input = npu_metrics.npu_power; sensor.unitm = -3; scnprintf(sensor.label, sizeof(sensor.label), "Total Power"); scnprintf(sensor.units, sizeof(sensor.units), "mW"); if (args->buffer_size < sizeof(sensor)) goto out; if (copy_to_user(u64_to_user_ptr(args->buffer), &sensor, sizeof(sensor))) return -EFAULT; args->buffer_size -= sizeof(sensor); sensors_count++; for (i = 0; i < min_t(u32, ndev->total_col, 8); i++) { memset(&sensor, 0, sizeof(sensor)); sensor.input = npu_metrics.npu_busy[i]; sensor.type = AMDXDNA_SENSOR_TYPE_COLUMN_UTILIZATION; sensor.unitm = 0; scnprintf(sensor.label, sizeof(sensor.label), "Column %d Utilization", i); scnprintf(sensor.units, sizeof(sensor.units), "%%"); if (args->buffer_size < sizeof(sensor)) goto out; if (copy_to_user(u64_to_user_ptr(args->buffer) + sensors_count * sizeof(sensor), &sensor, sizeof(sensor))) return -EFAULT; args->buffer_size -= sizeof(sensor); sensors_count++; } out: args->buffer_size = sensors_count * sizeof(sensor); return 0; } static int aie2_hwctx_status_cb(struct amdxdna_hwctx *hwctx, void *arg) { struct amdxdna_drm_hwctx_entry *tmp __free(kfree) = NULL; struct amdxdna_drm_get_array *array_args = arg; struct amdxdna_drm_hwctx_entry __user *buf; struct app_health_report report; struct amdxdna_dev_hdl *ndev; u32 size; int ret; if (!array_args->num_element) return -EINVAL; tmp = kzalloc_obj(*tmp); if (!tmp) return -ENOMEM; tmp->pid = hwctx->client->pid; tmp->context_id = hwctx->id; tmp->start_col = hwctx->start_col; tmp->num_col = hwctx->num_col; tmp->command_submissions = hwctx->priv->seq; tmp->command_completions = hwctx->priv->completed; tmp->pasid = hwctx->client->pasid; tmp->heap_usage = hwctx->client->heap_usage; tmp->priority = hwctx->qos.priority; tmp->gops = hwctx->qos.gops; tmp->fps = hwctx->qos.fps; tmp->dma_bandwidth = hwctx->qos.dma_bandwidth; tmp->latency = hwctx->qos.latency; tmp->frame_exec_time = hwctx->qos.frame_exec_time; tmp->state = AMDXDNA_HWCTX_STATE_ACTIVE; ndev = hwctx->client->xdna->dev_handle; ret = aie2_query_app_health(ndev, hwctx->fw_ctx_id, &report); if (!ret) { /* Fill in app health report fields */ tmp->txn_op_idx = report.txn_op_id; tmp->ctx_pc = report.ctx_pc; tmp->fatal_error_type = report.fatal_info.fatal_type; tmp->fatal_error_exception_type = report.fatal_info.exception_type; tmp->fatal_error_exception_pc = report.fatal_info.exception_pc; tmp->fatal_error_app_module = report.fatal_info.app_module; } buf = u64_to_user_ptr(array_args->buffer); size = min(sizeof(*tmp), array_args->element_size); if (copy_to_user(buf, tmp, size)) return -EFAULT; array_args->buffer += size; array_args->num_element--; return 0; } static int aie2_get_hwctx_status(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_drm_get_array array_args; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_client *tmp_client; int ret; drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); array_args.element_size = sizeof(struct amdxdna_drm_query_hwctx); array_args.buffer = args->buffer; array_args.num_element = args->buffer_size / array_args.element_size; list_for_each_entry(tmp_client, &xdna->client_list, node) { ret = amdxdna_hwctx_walk(tmp_client, &array_args, aie2_hwctx_status_cb); if (ret) break; } args->buffer_size -= (u32)(array_args.buffer - args->buffer); return 0; } static int aie2_query_resource_info(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_drm_get_resource_info res_info; const struct amdxdna_dev_priv *priv; struct amdxdna_dev_hdl *ndev; struct amdxdna_dev *xdna; u32 buf_sz; xdna = client->xdna; ndev = xdna->dev_handle; priv = ndev->priv; aie2_update_counters(ndev); res_info.npu_clk_max = priv->dpm_clk_tbl[ndev->max_dpm_level].hclk; res_info.npu_tops_max = ndev->max_tops; res_info.npu_task_max = priv->hwctx_limit; res_info.npu_tops_curr = ndev->curr_tops; res_info.npu_task_curr = ndev->hwctx_num; buf_sz = min(args->buffer_size, sizeof(res_info)); if (copy_to_user(u64_to_user_ptr(args->buffer), &res_info, buf_sz)) return -EFAULT; return 0; } static int aie2_fill_hwctx_map(struct amdxdna_hwctx *hwctx, void *arg) { struct amdxdna_dev *xdna = hwctx->client->xdna; u32 *map = arg; if (hwctx->fw_ctx_id >= xdna->dev_handle->priv->hwctx_limit) { XDNA_ERR(xdna, "Invalid fw ctx id %d/%d ", hwctx->fw_ctx_id, xdna->dev_handle->priv->hwctx_limit); return -EINVAL; } map[hwctx->fw_ctx_id] = hwctx->id; return 0; } static int aie2_get_telemetry(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_drm_query_telemetry_header *header __free(kfree) = NULL; u32 telemetry_data_sz, header_sz, elem_num; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_client *tmp_client; int ret; elem_num = xdna->dev_handle->priv->hwctx_limit; header_sz = struct_size(header, map, elem_num); if (args->buffer_size <= header_sz) { XDNA_ERR(xdna, "Invalid buffer size"); return -EINVAL; } telemetry_data_sz = args->buffer_size - header_sz; header = kzalloc(header_sz, GFP_KERNEL); if (!header) return -ENOMEM; if (copy_from_user(header, u64_to_user_ptr(args->buffer), sizeof(*header))) { XDNA_ERR(xdna, "Failed to copy telemetry header from user"); return -EFAULT; } header->map_num_elements = elem_num; list_for_each_entry(tmp_client, &xdna->client_list, node) { ret = amdxdna_hwctx_walk(tmp_client, &header->map, aie2_fill_hwctx_map); if (ret) return ret; } ret = aie2_query_telemetry(xdna->dev_handle, u64_to_user_ptr(args->buffer + header_sz), telemetry_data_sz, header); if (ret) { XDNA_ERR(xdna, "Query telemetry failed ret %d", ret); return ret; } if (copy_to_user(u64_to_user_ptr(args->buffer), header, header_sz)) { XDNA_ERR(xdna, "Copy header failed"); return -EFAULT; } return 0; } static int aie2_get_preempt_state(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_drm_attribute_state state = {}; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; u32 buf_sz; ndev = xdna->dev_handle; if (args->param == DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE) state.state = ndev->force_preempt_enabled; else if (args->param == DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE) state.state = ndev->frame_boundary_preempt; buf_sz = min(args->buffer_size, sizeof(state)); if (copy_to_user(u64_to_user_ptr(args->buffer), &state, buf_sz)) return -EFAULT; return 0; } static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev = xdna->dev_handle; int ret, idx; if (!drm_dev_enter(&xdna->ddev, &idx)) return -ENODEV; ret = amdxdna_pm_resume_get_locked(xdna); if (ret) goto dev_exit; switch (args->param) { case DRM_AMDXDNA_QUERY_AIE_STATUS: ret = aie2_get_aie_status(client, args); break; case DRM_AMDXDNA_QUERY_AIE_METADATA: ret = amdxdna_get_metadata(&ndev->aie, client, args); break; case DRM_AMDXDNA_QUERY_AIE_VERSION: ret = aie2_get_aie_version(client, args); break; case DRM_AMDXDNA_QUERY_CLOCK_METADATA: ret = aie2_get_clock_metadata(client, args); break; case DRM_AMDXDNA_QUERY_SENSORS: ret = aie2_get_sensors(client, args); break; case DRM_AMDXDNA_QUERY_HW_CONTEXTS: ret = aie2_get_hwctx_status(client, args); break; case DRM_AMDXDNA_QUERY_FIRMWARE_VERSION: ret = aie2_get_firmware_version(client, args); break; case DRM_AMDXDNA_GET_POWER_MODE: ret = aie2_get_power_mode(client, args); break; case DRM_AMDXDNA_QUERY_TELEMETRY: ret = aie2_get_telemetry(client, args); break; case DRM_AMDXDNA_QUERY_RESOURCE_INFO: ret = aie2_query_resource_info(client, args); break; case DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE: case DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE: ret = aie2_get_preempt_state(client, args); break; default: XDNA_ERR(xdna, "Not supported request parameter %u", args->param); ret = -EOPNOTSUPP; } amdxdna_pm_suspend_put(xdna); XDNA_DBG(xdna, "Got param %d", args->param); dev_exit: drm_dev_exit(idx); return ret; } static int aie2_query_ctx_status_array(struct amdxdna_client *client, struct amdxdna_drm_get_array *args) { struct amdxdna_drm_get_array array_args; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_client *tmp_client; int ret; drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); if (args->element_size > SZ_4K || args->num_element > SZ_1K) { XDNA_DBG(xdna, "Invalid element size %d or number of element %d", args->element_size, args->num_element); return -EINVAL; } array_args.element_size = min(args->element_size, sizeof(struct amdxdna_drm_hwctx_entry)); array_args.buffer = args->buffer; array_args.num_element = args->num_element * args->element_size / array_args.element_size; list_for_each_entry(tmp_client, &xdna->client_list, node) { ret = amdxdna_hwctx_walk(tmp_client, &array_args, aie2_hwctx_status_cb); if (ret) break; } args->element_size = array_args.element_size; args->num_element = (u32)((array_args.buffer - args->buffer) / args->element_size); return 0; } static int aie2_get_array(struct amdxdna_client *client, struct amdxdna_drm_get_array *args) { struct amdxdna_dev *xdna = client->xdna; int ret, idx; if (!drm_dev_enter(&xdna->ddev, &idx)) return -ENODEV; ret = amdxdna_pm_resume_get_locked(xdna); if (ret) goto dev_exit; switch (args->param) { case DRM_AMDXDNA_HW_CONTEXT_ALL: ret = aie2_query_ctx_status_array(client, args); break; case DRM_AMDXDNA_HW_LAST_ASYNC_ERR: ret = aie2_get_array_async_error(xdna->dev_handle, args); break; case DRM_AMDXDNA_BO_USAGE: ret = amdxdna_drm_get_bo_usage(&xdna->ddev, args); break; default: XDNA_ERR(xdna, "Not supported request parameter %u", args->param); ret = -EOPNOTSUPP; } amdxdna_pm_suspend_put(xdna); XDNA_DBG(xdna, "Got param %d", args->param); dev_exit: drm_dev_exit(idx); return ret; } static int aie2_set_power_mode(struct amdxdna_client *client, struct amdxdna_drm_set_state *args) { struct amdxdna_drm_set_power_mode power_state; enum amdxdna_power_mode_type power_mode; struct amdxdna_dev *xdna = client->xdna; if (copy_from_user(&power_state, u64_to_user_ptr(args->buffer), sizeof(power_state))) { XDNA_ERR(xdna, "Failed to copy power mode request into kernel"); return -EFAULT; } if (XDNA_MBZ_DBG(xdna, power_state.pad, sizeof(power_state.pad))) return -EINVAL; power_mode = power_state.power_mode; if (power_mode > POWER_MODE_TURBO) { XDNA_ERR(xdna, "Invalid power mode %d", power_mode); return -EINVAL; } return aie2_pm_set_mode(xdna->dev_handle, power_mode); } static int aie2_set_preempt_state(struct amdxdna_client *client, struct amdxdna_drm_set_state *args) { struct amdxdna_dev_hdl *ndev = client->xdna->dev_handle; struct amdxdna_drm_attribute_state state; u32 val; int ret; if (copy_from_user(&state, u64_to_user_ptr(args->buffer), sizeof(state))) return -EFAULT; if (state.state > 1) return -EINVAL; if (XDNA_MBZ_DBG(client->xdna, state.pad, sizeof(state.pad))) return -EINVAL; if (args->param == DRM_AMDXDNA_SET_FORCE_PREEMPT) { ndev->force_preempt_enabled = state.state; } else if (args->param == DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT) { val = state.state; ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT, &val); if (ret) return ret; ndev->frame_boundary_preempt = state.state; } return 0; } static int aie2_set_state(struct amdxdna_client *client, struct amdxdna_drm_set_state *args) { struct amdxdna_dev *xdna = client->xdna; int ret, idx; if (!drm_dev_enter(&xdna->ddev, &idx)) return -ENODEV; ret = amdxdna_pm_resume_get_locked(xdna); if (ret) goto dev_exit; switch (args->param) { case DRM_AMDXDNA_SET_POWER_MODE: ret = aie2_set_power_mode(client, args); break; case DRM_AMDXDNA_SET_FORCE_PREEMPT: case DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT: ret = aie2_set_preempt_state(client, args); break; default: XDNA_ERR(xdna, "Not supported request parameter %u", args->param); ret = -EOPNOTSUPP; break; } amdxdna_pm_suspend_put(xdna); dev_exit: drm_dev_exit(idx); return ret; } static int aie2_get_dev_rev(struct amdxdna_dev *xdna, u32 *rev) { struct amdxdna_dev_hdl *ndev = xdna->dev_handle; enum aie2_dev_revision aie2_rev; int ret; drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); ret = aie2_get_dev_revision(ndev, &aie2_rev); if (!ret) *rev = (u32)aie2_rev; return ret; } const struct amdxdna_dev_ops aie2_ops = { .init = aie2_init, .fini = aie2_fini, .resume = aie2_hw_resume, .suspend = aie2_hw_suspend, .get_aie_info = aie2_get_info, .set_aie_state = aie2_set_state, .hwctx_init = aie2_hwctx_init, .hwctx_fini = aie2_hwctx_fini, .hwctx_config = aie2_hwctx_config, .hwctx_sync_debug_bo = aie2_hwctx_sync_debug_bo, .cmd_submit = aie2_cmd_submit, .hmm_invalidate = aie2_hmm_invalidate, .get_array = aie2_get_array, .get_dev_revision = aie2_get_dev_rev, .hwctx_heap_expand = aie2_hwctx_heap_expand, };