xref: /linux/drivers/accel/amdxdna/aie4_ctx.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
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