119361279SDavid Zhang // SPDX-License-Identifier: GPL-2.0 219361279SDavid Zhang /* 319361279SDavid Zhang * Copyright (C) 2026, Advanced Micro Devices, Inc. 419361279SDavid Zhang */ 519361279SDavid Zhang 619361279SDavid Zhang #include <drm/amdxdna_accel.h> 719361279SDavid Zhang #include <drm/drm_device.h> 819361279SDavid Zhang #include <drm/drm_gem.h> 919361279SDavid Zhang #include <drm/drm_gem_shmem_helper.h> 1019361279SDavid Zhang #include <drm/drm_print.h> 1119361279SDavid Zhang #include <drm/gpu_scheduler.h> 1219361279SDavid Zhang #include <linux/types.h> 1319361279SDavid Zhang 1419361279SDavid Zhang #include "aie.h" 1519361279SDavid Zhang #include "aie4_host_queue.h" 1619361279SDavid Zhang #include "aie4_msg_priv.h" 1719361279SDavid Zhang #include "aie4_pci.h" 1819361279SDavid Zhang #include "amdxdna_ctx.h" 1919361279SDavid Zhang #include "amdxdna_gem.h" 2019361279SDavid Zhang #include "amdxdna_mailbox.h" 2119361279SDavid Zhang #include "amdxdna_mailbox_helper.h" 2219361279SDavid Zhang #include "amdxdna_pci_drv.h" 2319361279SDavid Zhang 2419361279SDavid Zhang static irqreturn_t cert_comp_isr(int irq, void *p) 2519361279SDavid Zhang { 2619361279SDavid Zhang struct cert_comp *cert_comp = p; 2719361279SDavid Zhang 2819361279SDavid Zhang wake_up_all(&cert_comp->waitq); 2919361279SDavid Zhang return IRQ_HANDLED; 3019361279SDavid Zhang } 3119361279SDavid Zhang 3219361279SDavid Zhang static struct cert_comp *aie4_lookup_cert_comp(struct amdxdna_dev_hdl *ndev, u32 msix_idx) 3319361279SDavid Zhang { 3419361279SDavid Zhang struct amdxdna_dev *xdna = ndev->aie.xdna; 3519361279SDavid Zhang struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev); 3619361279SDavid Zhang struct cert_comp *cert_comp; 3719361279SDavid Zhang int ret; 3819361279SDavid Zhang 3919361279SDavid Zhang guard(mutex)(&ndev->cert_comp_lock); 4019361279SDavid Zhang 4119361279SDavid Zhang cert_comp = xa_load(&ndev->cert_comp_xa, msix_idx); 4219361279SDavid Zhang if (cert_comp) { 4319361279SDavid Zhang kref_get(&cert_comp->kref); 4419361279SDavid Zhang return cert_comp; 4519361279SDavid Zhang } 4619361279SDavid Zhang 4719361279SDavid Zhang cert_comp = kzalloc_obj(*cert_comp); 4819361279SDavid Zhang if (!cert_comp) 4919361279SDavid Zhang return NULL; 5019361279SDavid Zhang 5119361279SDavid Zhang cert_comp->ndev = ndev; 5219361279SDavid Zhang cert_comp->msix_idx = msix_idx; 5319361279SDavid Zhang init_waitqueue_head(&cert_comp->waitq); 5419361279SDavid Zhang kref_init(&cert_comp->kref); 5519361279SDavid Zhang 5619361279SDavid Zhang ret = pci_irq_vector(pdev, cert_comp->msix_idx); 5719361279SDavid Zhang if (ret < 0) { 5819361279SDavid Zhang XDNA_ERR(xdna, "MSI-X idx %u is invalid, ret:%d", msix_idx, ret); 5919361279SDavid Zhang goto free_cert_comp; 6019361279SDavid Zhang } 6119361279SDavid Zhang cert_comp->irq = ret; 6219361279SDavid Zhang 6319361279SDavid Zhang ret = request_irq(cert_comp->irq, cert_comp_isr, 0, "xdna_hsa", cert_comp); 6419361279SDavid Zhang if (ret) { 6519361279SDavid Zhang XDNA_ERR(xdna, "request irq %d failed %d", cert_comp->irq, ret); 6619361279SDavid Zhang goto free_cert_comp; 6719361279SDavid Zhang } 6819361279SDavid Zhang 6919361279SDavid Zhang ret = xa_err(xa_store(&ndev->cert_comp_xa, msix_idx, cert_comp, GFP_KERNEL)); 7019361279SDavid Zhang if (ret) { 7119361279SDavid Zhang XDNA_ERR(xdna, "store cert_comp for msix idx %d failed %d", msix_idx, ret); 7219361279SDavid Zhang goto free_irq; 7319361279SDavid Zhang } 7419361279SDavid Zhang 7519361279SDavid Zhang return cert_comp; 7619361279SDavid Zhang 7719361279SDavid Zhang free_irq: 7819361279SDavid Zhang free_irq(cert_comp->irq, cert_comp); 7919361279SDavid Zhang free_cert_comp: 8019361279SDavid Zhang kfree(cert_comp); 8119361279SDavid Zhang return NULL; 8219361279SDavid Zhang } 8319361279SDavid Zhang 8419361279SDavid Zhang static void cert_comp_release(struct kref *kref) 8519361279SDavid Zhang { 8619361279SDavid Zhang struct cert_comp *cert_comp = container_of(kref, struct cert_comp, kref); 8719361279SDavid Zhang struct amdxdna_dev_hdl *ndev = cert_comp->ndev; 8819361279SDavid Zhang 8919361279SDavid Zhang drm_WARN_ON(&ndev->aie.xdna->ddev, !mutex_is_locked(&ndev->cert_comp_lock)); 9019361279SDavid Zhang 9119361279SDavid Zhang xa_erase(&ndev->cert_comp_xa, cert_comp->msix_idx); 9219361279SDavid Zhang free_irq(cert_comp->irq, cert_comp); 9319361279SDavid Zhang kfree(cert_comp); 9419361279SDavid Zhang } 9519361279SDavid Zhang 9619361279SDavid Zhang static void aie4_put_cert_comp(struct cert_comp *cert_comp) 9719361279SDavid Zhang { 9819361279SDavid Zhang struct amdxdna_dev_hdl *ndev; 9919361279SDavid Zhang 10019361279SDavid Zhang ndev = cert_comp->ndev; 10119361279SDavid Zhang guard(mutex)(&ndev->cert_comp_lock); 10219361279SDavid Zhang kref_put(&cert_comp->kref, cert_comp_release); 10319361279SDavid Zhang } 10419361279SDavid Zhang 10519361279SDavid Zhang static int aie4_msg_destroy_context(struct amdxdna_dev_hdl *ndev, u32 hw_context_id) 10619361279SDavid Zhang { 10719361279SDavid Zhang DECLARE_AIE_MSG(aie4_msg_destroy_hw_context, AIE4_MSG_OP_DESTROY_HW_CONTEXT); 10819361279SDavid Zhang 10919361279SDavid Zhang req.hw_context_id = hw_context_id; 11019361279SDavid Zhang return aie_send_mgmt_msg_wait(&ndev->aie, &msg); 11119361279SDavid Zhang } 11219361279SDavid Zhang 11319361279SDavid Zhang static int aie4_hwctx_create(struct amdxdna_hwctx *hwctx) 11419361279SDavid Zhang { 11519361279SDavid Zhang DECLARE_AIE_MSG(aie4_msg_create_hw_context, AIE4_MSG_OP_CREATE_HW_CONTEXT); 11619361279SDavid Zhang struct amdxdna_client *client = hwctx->client; 11719361279SDavid Zhang struct amdxdna_hwctx_priv *priv = hwctx->priv; 11819361279SDavid Zhang struct amdxdna_dev *xdna = hwctx->client->xdna; 11919361279SDavid Zhang struct amdxdna_dev_hdl *ndev = xdna->dev_handle; 12019361279SDavid Zhang int ret; 12119361279SDavid Zhang 12219361279SDavid Zhang drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); 12319361279SDavid Zhang 12419361279SDavid Zhang if (!ndev->partition_id || !hwctx->num_tiles) { 12519361279SDavid Zhang XDNA_ERR(xdna, "invalid request partition_id %d, num_tiles %d", 12619361279SDavid Zhang ndev->partition_id, hwctx->num_tiles); 12719361279SDavid Zhang return -EINVAL; 12819361279SDavid Zhang } 12919361279SDavid Zhang 13019361279SDavid Zhang req.partition_id = ndev->partition_id; 13119361279SDavid Zhang req.request_num_tiles = hwctx->num_tiles; 13219361279SDavid Zhang req.pasid = FIELD_PREP(AIE4_MSG_PASID, client->pasid) | 13319361279SDavid Zhang FIELD_PREP(AIE4_MSG_PASID_VLD, 1); 13419361279SDavid Zhang req.priority_band = hwctx->qos.priority; 13519361279SDavid Zhang 13619361279SDavid Zhang req.hsa_addr_high = upper_32_bits(amdxdna_gem_dev_addr(priv->umq_bo)); 13719361279SDavid Zhang req.hsa_addr_low = lower_32_bits(amdxdna_gem_dev_addr(priv->umq_bo)); 13819361279SDavid Zhang 13919361279SDavid Zhang XDNA_DBG(xdna, "pasid 0x%x, num_tiles %d, hsa[0x%x 0x%x]", 14019361279SDavid Zhang req.pasid, req.request_num_tiles, req.hsa_addr_high, req.hsa_addr_low); 14119361279SDavid Zhang 14219361279SDavid Zhang ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); 14319361279SDavid Zhang if (ret) { 14419361279SDavid Zhang XDNA_ERR(xdna, "create ctx failed: %d", ret); 14519361279SDavid Zhang return ret; 14619361279SDavid Zhang } 14719361279SDavid Zhang 14819361279SDavid Zhang XDNA_DBG(xdna, "resp msix: %d, ctx id: %d, doorbell: %d", 14919361279SDavid Zhang resp.job_complete_msix_idx, 15019361279SDavid Zhang resp.hw_context_id, 15119361279SDavid Zhang resp.doorbell_offset); 15219361279SDavid Zhang 15319361279SDavid Zhang /* setup interrupt completion per msix index */ 15419361279SDavid Zhang priv->cert_comp = aie4_lookup_cert_comp(ndev, resp.job_complete_msix_idx); 15519361279SDavid Zhang if (!priv->cert_comp) { 15619361279SDavid Zhang aie4_msg_destroy_context(ndev, resp.hw_context_id); 15719361279SDavid Zhang return -EINVAL; 15819361279SDavid Zhang } 15919361279SDavid Zhang 16019361279SDavid Zhang priv->hw_ctx_id = resp.hw_context_id; 16119361279SDavid Zhang hwctx->doorbell_offset = resp.doorbell_offset; 16219361279SDavid Zhang 16319361279SDavid Zhang return 0; 16419361279SDavid Zhang } 16519361279SDavid Zhang 16619361279SDavid Zhang static void aie4_hwctx_destroy(struct amdxdna_hwctx *hwctx) 16719361279SDavid Zhang { 16819361279SDavid Zhang struct amdxdna_client *client = hwctx->client; 16919361279SDavid Zhang struct amdxdna_hwctx_priv *priv = hwctx->priv; 17019361279SDavid Zhang struct amdxdna_dev *xdna = client->xdna; 17119361279SDavid Zhang struct amdxdna_dev_hdl *ndev = xdna->dev_handle; 17219361279SDavid Zhang 17319361279SDavid Zhang drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); 17419361279SDavid Zhang 17519361279SDavid Zhang aie4_msg_destroy_context(ndev, priv->hw_ctx_id); 17619361279SDavid Zhang aie4_put_cert_comp(priv->cert_comp); 17719361279SDavid Zhang } 17819361279SDavid Zhang 17919361279SDavid Zhang static void aie4_hwctx_umq_fini(struct amdxdna_hwctx *hwctx) 18019361279SDavid Zhang { 18119361279SDavid Zhang if (hwctx->priv && hwctx->priv->umq_bo) 18219361279SDavid Zhang amdxdna_gem_put_obj(hwctx->priv->umq_bo); 18319361279SDavid Zhang } 18419361279SDavid Zhang 18519361279SDavid Zhang static int aie4_hwctx_umq_init(struct amdxdna_hwctx *hwctx) 18619361279SDavid Zhang { 18719361279SDavid Zhang struct amdxdna_hwctx_priv *priv = hwctx->priv; 18819361279SDavid Zhang struct amdxdna_dev *xdna = hwctx->client->xdna; 18919361279SDavid Zhang struct amdxdna_gem_obj *umq_bo; 19019361279SDavid Zhang struct host_queue_header *qhdr; 19119361279SDavid Zhang int ret; 19219361279SDavid Zhang 19319361279SDavid Zhang umq_bo = amdxdna_gem_get_obj(hwctx->client, hwctx->umq_bo_hdl, AMDXDNA_BO_SHARE); 19419361279SDavid Zhang if (!umq_bo) { 19519361279SDavid Zhang XDNA_ERR(xdna, "cannot find umq_bo handle %d", hwctx->umq_bo_hdl); 19619361279SDavid Zhang return -ENOENT; 19719361279SDavid Zhang } 19819361279SDavid Zhang if (umq_bo->mem.size < sizeof(*qhdr)) { 19919361279SDavid Zhang XDNA_ERR(xdna, "umq_bo size is too small"); 20019361279SDavid Zhang ret = -EINVAL; 20119361279SDavid Zhang goto put_umq_bo; 20219361279SDavid Zhang } 20319361279SDavid Zhang 20419361279SDavid Zhang /* get kva address for host queue read index and write index */ 20519361279SDavid Zhang qhdr = amdxdna_gem_vmap(umq_bo); 20619361279SDavid Zhang if (!qhdr) { 20719361279SDavid Zhang ret = -ENOMEM; 20819361279SDavid Zhang goto put_umq_bo; 20919361279SDavid Zhang } 21019361279SDavid Zhang 21119361279SDavid Zhang priv->umq_bo = umq_bo; 21219361279SDavid Zhang priv->umq_read_index = &qhdr->read_index; 21319361279SDavid Zhang priv->umq_write_index = &qhdr->write_index; 21419361279SDavid Zhang 21519361279SDavid Zhang return 0; 21619361279SDavid Zhang 21719361279SDavid Zhang put_umq_bo: 21819361279SDavid Zhang amdxdna_gem_put_obj(umq_bo); 21919361279SDavid Zhang return ret; 22019361279SDavid Zhang } 22119361279SDavid Zhang 22219361279SDavid Zhang int aie4_hwctx_init(struct amdxdna_hwctx *hwctx) 22319361279SDavid Zhang { 22419361279SDavid Zhang struct amdxdna_client *client = hwctx->client; 22519361279SDavid Zhang struct amdxdna_dev *xdna = client->xdna; 22619361279SDavid Zhang struct amdxdna_hwctx_priv *priv; 22719361279SDavid Zhang int ret; 22819361279SDavid Zhang 22919361279SDavid Zhang priv = kzalloc_obj(*priv); 23019361279SDavid Zhang if (!priv) 23119361279SDavid Zhang return -ENOMEM; 23219361279SDavid Zhang hwctx->priv = priv; 23319361279SDavid Zhang 23419361279SDavid Zhang ret = aie4_hwctx_umq_init(hwctx); 23519361279SDavid Zhang if (ret) 23619361279SDavid Zhang goto free_priv; 23719361279SDavid Zhang 23819361279SDavid Zhang ret = aie4_hwctx_create(hwctx); 23919361279SDavid Zhang if (ret) 24019361279SDavid Zhang goto umq_fini; 24119361279SDavid Zhang 24219361279SDavid Zhang XDNA_DBG(xdna, "hwctx %s init completed", hwctx->name); 24319361279SDavid Zhang return 0; 24419361279SDavid Zhang 24519361279SDavid Zhang umq_fini: 24619361279SDavid Zhang aie4_hwctx_umq_fini(hwctx); 24719361279SDavid Zhang free_priv: 24819361279SDavid Zhang kfree(priv); 24919361279SDavid Zhang hwctx->priv = NULL; 25019361279SDavid Zhang return ret; 25119361279SDavid Zhang } 25219361279SDavid Zhang 25319361279SDavid Zhang void aie4_hwctx_fini(struct amdxdna_hwctx *hwctx) 25419361279SDavid Zhang { 25519361279SDavid Zhang aie4_hwctx_destroy(hwctx); 25619361279SDavid Zhang aie4_hwctx_umq_fini(hwctx); 25719361279SDavid Zhang kfree(hwctx->priv); 25819361279SDavid Zhang } 259*91f4da82SDavid Zhang 260*91f4da82SDavid Zhang static inline bool valid_queue_index(u64 read, u64 write, u32 capacity) 261*91f4da82SDavid Zhang { 262*91f4da82SDavid Zhang return (write >= read) && ((write - read) <= capacity); 263*91f4da82SDavid Zhang } 264*91f4da82SDavid Zhang 265*91f4da82SDavid Zhang static u64 get_read_index(struct amdxdna_hwctx *hwctx) 266*91f4da82SDavid Zhang { 267*91f4da82SDavid Zhang u64 wi = READ_ONCE(*hwctx->priv->umq_write_index); 268*91f4da82SDavid Zhang u64 ri = READ_ONCE(*hwctx->priv->umq_read_index); 269*91f4da82SDavid Zhang struct amdxdna_dev *xdna = hwctx->client->xdna; 270*91f4da82SDavid Zhang 271*91f4da82SDavid Zhang /* 272*91f4da82SDavid Zhang * CERT cannot update read index as uint64 atomically. Driver may read 273*91f4da82SDavid Zhang * half-updated read index when it has bits in high 32bit. In case read 274*91f4da82SDavid Zhang * index is not valid, wait for some time and retry once. It should 275*91f4da82SDavid Zhang * allow CERT to complete the read index update. 276*91f4da82SDavid Zhang */ 277*91f4da82SDavid Zhang if (!valid_queue_index(ri, wi, CTX_MAX_CMDS)) { 278*91f4da82SDavid Zhang XDNA_WARN(xdna, "Invalid index, ri %llu, wi %llu", ri, wi); 279*91f4da82SDavid Zhang usleep_range(100, 200); 280*91f4da82SDavid Zhang ri = READ_ONCE(*hwctx->priv->umq_read_index); 281*91f4da82SDavid Zhang if (!valid_queue_index(ri, wi, CTX_MAX_CMDS)) { 282*91f4da82SDavid Zhang XDNA_ERR(xdna, "Invalid index after retry, ri %llu, wi %llu", ri, wi); 283*91f4da82SDavid Zhang ri = 0; 284*91f4da82SDavid Zhang } 285*91f4da82SDavid Zhang } 286*91f4da82SDavid Zhang 287*91f4da82SDavid Zhang return ri; 288*91f4da82SDavid Zhang } 289*91f4da82SDavid Zhang 290*91f4da82SDavid Zhang static inline bool check_cmd_done(struct amdxdna_hwctx *hwctx, u64 seq) 291*91f4da82SDavid Zhang { 292*91f4da82SDavid Zhang u64 read_idx = get_read_index(hwctx); 293*91f4da82SDavid Zhang 294*91f4da82SDavid Zhang return read_idx > seq; 295*91f4da82SDavid Zhang } 296*91f4da82SDavid Zhang 297*91f4da82SDavid Zhang int aie4_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq, u32 timeout) 298*91f4da82SDavid Zhang { 299*91f4da82SDavid Zhang unsigned long wait_jifs = MAX_SCHEDULE_TIMEOUT; 300*91f4da82SDavid Zhang struct amdxdna_hwctx_priv *priv = hwctx->priv; 301*91f4da82SDavid Zhang struct cert_comp *cert_comp = priv->cert_comp; 302*91f4da82SDavid Zhang long ret; 303*91f4da82SDavid Zhang 304*91f4da82SDavid Zhang if (timeout) 305*91f4da82SDavid Zhang wait_jifs = msecs_to_jiffies(timeout); 306*91f4da82SDavid Zhang 307*91f4da82SDavid Zhang ret = wait_event_interruptible_timeout(cert_comp->waitq, 308*91f4da82SDavid Zhang (check_cmd_done(hwctx, seq)), 309*91f4da82SDavid Zhang wait_jifs); 310*91f4da82SDavid Zhang 311*91f4da82SDavid Zhang if (!ret) 312*91f4da82SDavid Zhang ret = -ETIME; 313*91f4da82SDavid Zhang 314*91f4da82SDavid Zhang return ret <= 0 ? ret : 0; 315*91f4da82SDavid Zhang } 316*91f4da82SDavid Zhang 317*91f4da82SDavid Zhang int aie4_hwctx_valid_doorbell(struct amdxdna_client *client, u32 vm_pgoff) 318*91f4da82SDavid Zhang { 319*91f4da82SDavid Zhang struct amdxdna_hwctx *hwctx; 320*91f4da82SDavid Zhang unsigned long hwctx_id; 321*91f4da82SDavid Zhang int idx; 322*91f4da82SDavid Zhang 323*91f4da82SDavid Zhang idx = srcu_read_lock(&client->hwctx_srcu); 324*91f4da82SDavid Zhang amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { 325*91f4da82SDavid Zhang if (vm_pgoff == (hwctx->doorbell_offset >> PAGE_SHIFT)) { 326*91f4da82SDavid Zhang srcu_read_unlock(&client->hwctx_srcu, idx); 327*91f4da82SDavid Zhang return 1; 328*91f4da82SDavid Zhang } 329*91f4da82SDavid Zhang } 330*91f4da82SDavid Zhang srcu_read_unlock(&client->hwctx_srcu, idx); 331*91f4da82SDavid Zhang 332*91f4da82SDavid Zhang return 0; 333*91f4da82SDavid Zhang } 334