xref: /linux/drivers/accel/ethosu/ethosu_job.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
15a5e9c02SRob Herring (Arm) // SPDX-License-Identifier: GPL-2.0-only OR MIT
25a5e9c02SRob Herring (Arm) /* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
35a5e9c02SRob Herring (Arm) /* Copyright 2025 Arm, Ltd. */
45a5e9c02SRob Herring (Arm) 
55a5e9c02SRob Herring (Arm) #include <linux/bitfield.h>
65a5e9c02SRob Herring (Arm) #include <linux/genalloc.h>
75a5e9c02SRob Herring (Arm) #include <linux/interrupt.h>
85a5e9c02SRob Herring (Arm) #include <linux/iopoll.h>
95a5e9c02SRob Herring (Arm) #include <linux/platform_device.h>
105a5e9c02SRob Herring (Arm) #include <linux/pm_runtime.h>
115a5e9c02SRob Herring (Arm) 
125a5e9c02SRob Herring (Arm) #include <drm/drm_file.h>
135a5e9c02SRob Herring (Arm) #include <drm/drm_gem.h>
145a5e9c02SRob Herring (Arm) #include <drm/drm_gem_dma_helper.h>
15*f6e8dc9eSJani Nikula #include <drm/drm_print.h>
165a5e9c02SRob Herring (Arm) #include <drm/ethosu_accel.h>
175a5e9c02SRob Herring (Arm) 
185a5e9c02SRob Herring (Arm) #include "ethosu_device.h"
195a5e9c02SRob Herring (Arm) #include "ethosu_drv.h"
205a5e9c02SRob Herring (Arm) #include "ethosu_gem.h"
215a5e9c02SRob Herring (Arm) #include "ethosu_job.h"
225a5e9c02SRob Herring (Arm) 
235a5e9c02SRob Herring (Arm) #define JOB_TIMEOUT_MS 500
245a5e9c02SRob Herring (Arm) 
255a5e9c02SRob Herring (Arm) static struct ethosu_job *to_ethosu_job(struct drm_sched_job *sched_job)
265a5e9c02SRob Herring (Arm) {
275a5e9c02SRob Herring (Arm) 	return container_of(sched_job, struct ethosu_job, base);
285a5e9c02SRob Herring (Arm) }
295a5e9c02SRob Herring (Arm) 
305a5e9c02SRob Herring (Arm) static const char *ethosu_fence_get_driver_name(struct dma_fence *fence)
315a5e9c02SRob Herring (Arm) {
325a5e9c02SRob Herring (Arm) 	return "ethosu";
335a5e9c02SRob Herring (Arm) }
345a5e9c02SRob Herring (Arm) 
355a5e9c02SRob Herring (Arm) static const char *ethosu_fence_get_timeline_name(struct dma_fence *fence)
365a5e9c02SRob Herring (Arm) {
375a5e9c02SRob Herring (Arm) 	return "ethosu-npu";
385a5e9c02SRob Herring (Arm) }
395a5e9c02SRob Herring (Arm) 
405a5e9c02SRob Herring (Arm) static const struct dma_fence_ops ethosu_fence_ops = {
415a5e9c02SRob Herring (Arm) 	.get_driver_name = ethosu_fence_get_driver_name,
425a5e9c02SRob Herring (Arm) 	.get_timeline_name = ethosu_fence_get_timeline_name,
435a5e9c02SRob Herring (Arm) };
445a5e9c02SRob Herring (Arm) 
455a5e9c02SRob Herring (Arm) static void ethosu_job_hw_submit(struct ethosu_device *dev, struct ethosu_job *job)
465a5e9c02SRob Herring (Arm) {
475a5e9c02SRob Herring (Arm) 	struct drm_gem_dma_object *cmd_bo = to_drm_gem_dma_obj(job->cmd_bo);
485a5e9c02SRob Herring (Arm) 	struct ethosu_validated_cmdstream_info *cmd_info = to_ethosu_bo(job->cmd_bo)->info;
495a5e9c02SRob Herring (Arm) 
505a5e9c02SRob Herring (Arm) 	for (int i = 0; i < job->region_cnt; i++) {
515a5e9c02SRob Herring (Arm) 		struct drm_gem_dma_object *bo;
525a5e9c02SRob Herring (Arm) 		int region = job->region_bo_num[i];
535a5e9c02SRob Herring (Arm) 
545a5e9c02SRob Herring (Arm) 		bo = to_drm_gem_dma_obj(job->region_bo[i]);
555a5e9c02SRob Herring (Arm) 		writel_relaxed(lower_32_bits(bo->dma_addr), dev->regs + NPU_REG_BASEP(region));
565a5e9c02SRob Herring (Arm) 		writel_relaxed(upper_32_bits(bo->dma_addr), dev->regs + NPU_REG_BASEP_HI(region));
575a5e9c02SRob Herring (Arm) 		dev_dbg(dev->base.dev, "Region %d base addr = %pad\n", region, &bo->dma_addr);
585a5e9c02SRob Herring (Arm) 	}
595a5e9c02SRob Herring (Arm) 
605a5e9c02SRob Herring (Arm) 	if (job->sram_size) {
615a5e9c02SRob Herring (Arm) 		writel_relaxed(lower_32_bits(dev->sramphys),
625a5e9c02SRob Herring (Arm) 			       dev->regs + NPU_REG_BASEP(ETHOSU_SRAM_REGION));
635a5e9c02SRob Herring (Arm) 		writel_relaxed(upper_32_bits(dev->sramphys),
645a5e9c02SRob Herring (Arm) 			       dev->regs + NPU_REG_BASEP_HI(ETHOSU_SRAM_REGION));
655a5e9c02SRob Herring (Arm) 		dev_dbg(dev->base.dev, "Region %d base addr = %pad (SRAM)\n",
665a5e9c02SRob Herring (Arm) 			ETHOSU_SRAM_REGION, &dev->sramphys);
675a5e9c02SRob Herring (Arm) 	}
685a5e9c02SRob Herring (Arm) 
695a5e9c02SRob Herring (Arm) 	writel_relaxed(lower_32_bits(cmd_bo->dma_addr), dev->regs + NPU_REG_QBASE);
705a5e9c02SRob Herring (Arm) 	writel_relaxed(upper_32_bits(cmd_bo->dma_addr), dev->regs + NPU_REG_QBASE_HI);
715a5e9c02SRob Herring (Arm) 	writel_relaxed(cmd_info->cmd_size, dev->regs + NPU_REG_QSIZE);
725a5e9c02SRob Herring (Arm) 
735a5e9c02SRob Herring (Arm) 	writel(CMD_TRANSITION_TO_RUN, dev->regs + NPU_REG_CMD);
745a5e9c02SRob Herring (Arm) 
755a5e9c02SRob Herring (Arm) 	dev_dbg(dev->base.dev,
765a5e9c02SRob Herring (Arm) 		"Submitted cmd at %pad to core\n", &cmd_bo->dma_addr);
775a5e9c02SRob Herring (Arm) }
785a5e9c02SRob Herring (Arm) 
795a5e9c02SRob Herring (Arm) static int ethosu_acquire_object_fences(struct ethosu_job *job)
805a5e9c02SRob Herring (Arm) {
815a5e9c02SRob Herring (Arm) 	int i, ret;
825a5e9c02SRob Herring (Arm) 	struct drm_gem_object **bos = job->region_bo;
835a5e9c02SRob Herring (Arm) 	struct ethosu_validated_cmdstream_info *info = to_ethosu_bo(job->cmd_bo)->info;
845a5e9c02SRob Herring (Arm) 
855a5e9c02SRob Herring (Arm) 	for (i = 0; i < job->region_cnt; i++) {
865a5e9c02SRob Herring (Arm) 		bool is_write;
875a5e9c02SRob Herring (Arm) 
885a5e9c02SRob Herring (Arm) 		if (!bos[i])
895a5e9c02SRob Herring (Arm) 			break;
905a5e9c02SRob Herring (Arm) 
915a5e9c02SRob Herring (Arm) 		ret = dma_resv_reserve_fences(bos[i]->resv, 1);
925a5e9c02SRob Herring (Arm) 		if (ret)
935a5e9c02SRob Herring (Arm) 			return ret;
945a5e9c02SRob Herring (Arm) 
955a5e9c02SRob Herring (Arm) 		is_write = info->output_region[job->region_bo_num[i]];
965a5e9c02SRob Herring (Arm) 		ret = drm_sched_job_add_implicit_dependencies(&job->base, bos[i],
975a5e9c02SRob Herring (Arm) 							      is_write);
985a5e9c02SRob Herring (Arm) 		if (ret)
995a5e9c02SRob Herring (Arm) 			return ret;
1005a5e9c02SRob Herring (Arm) 	}
1015a5e9c02SRob Herring (Arm) 
1025a5e9c02SRob Herring (Arm) 	return 0;
1035a5e9c02SRob Herring (Arm) }
1045a5e9c02SRob Herring (Arm) 
1055a5e9c02SRob Herring (Arm) static void ethosu_attach_object_fences(struct ethosu_job *job)
1065a5e9c02SRob Herring (Arm) {
1075a5e9c02SRob Herring (Arm) 	int i;
1085a5e9c02SRob Herring (Arm) 	struct dma_fence *fence = job->inference_done_fence;
1095a5e9c02SRob Herring (Arm) 	struct drm_gem_object **bos = job->region_bo;
1105a5e9c02SRob Herring (Arm) 	struct ethosu_validated_cmdstream_info *info = to_ethosu_bo(job->cmd_bo)->info;
1115a5e9c02SRob Herring (Arm) 
1125a5e9c02SRob Herring (Arm) 	for (i = 0; i < job->region_cnt; i++)
1135a5e9c02SRob Herring (Arm) 		if (info->output_region[job->region_bo_num[i]])
1145a5e9c02SRob Herring (Arm) 			dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);
1155a5e9c02SRob Herring (Arm) }
1165a5e9c02SRob Herring (Arm) 
1175a5e9c02SRob Herring (Arm) static int ethosu_job_push(struct ethosu_job *job)
1185a5e9c02SRob Herring (Arm) {
1195a5e9c02SRob Herring (Arm) 	struct ww_acquire_ctx acquire_ctx;
1205a5e9c02SRob Herring (Arm) 	int ret;
1215a5e9c02SRob Herring (Arm) 
1225a5e9c02SRob Herring (Arm) 	ret = drm_gem_lock_reservations(job->region_bo, job->region_cnt, &acquire_ctx);
1235a5e9c02SRob Herring (Arm) 	if (ret)
1245a5e9c02SRob Herring (Arm) 		return ret;
1255a5e9c02SRob Herring (Arm) 
1265a5e9c02SRob Herring (Arm) 	ret = ethosu_acquire_object_fences(job);
1275a5e9c02SRob Herring (Arm) 	if (ret)
1285a5e9c02SRob Herring (Arm) 		goto out;
1295a5e9c02SRob Herring (Arm) 
1305a5e9c02SRob Herring (Arm) 	ret = pm_runtime_resume_and_get(job->dev->base.dev);
1315a5e9c02SRob Herring (Arm) 	if (!ret) {
1325a5e9c02SRob Herring (Arm) 		guard(mutex)(&job->dev->sched_lock);
1335a5e9c02SRob Herring (Arm) 
1345a5e9c02SRob Herring (Arm) 		drm_sched_job_arm(&job->base);
1355a5e9c02SRob Herring (Arm) 		job->inference_done_fence = dma_fence_get(&job->base.s_fence->finished);
1365a5e9c02SRob Herring (Arm) 		kref_get(&job->refcount); /* put by scheduler job completion */
1375a5e9c02SRob Herring (Arm) 		drm_sched_entity_push_job(&job->base);
1385a5e9c02SRob Herring (Arm) 		ethosu_attach_object_fences(job);
1395a5e9c02SRob Herring (Arm) 	}
1405a5e9c02SRob Herring (Arm) 
1415a5e9c02SRob Herring (Arm) out:
1425a5e9c02SRob Herring (Arm) 	drm_gem_unlock_reservations(job->region_bo, job->region_cnt, &acquire_ctx);
1435a5e9c02SRob Herring (Arm) 	return ret;
1445a5e9c02SRob Herring (Arm) }
1455a5e9c02SRob Herring (Arm) 
1465a5e9c02SRob Herring (Arm) static void ethosu_job_cleanup(struct kref *ref)
1475a5e9c02SRob Herring (Arm) {
1485a5e9c02SRob Herring (Arm) 	struct ethosu_job *job = container_of(ref, struct ethosu_job,
1495a5e9c02SRob Herring (Arm) 						refcount);
1505a5e9c02SRob Herring (Arm) 	unsigned int i;
1515a5e9c02SRob Herring (Arm) 
1525a5e9c02SRob Herring (Arm) 	pm_runtime_put_autosuspend(job->dev->base.dev);
1535a5e9c02SRob Herring (Arm) 
1545a5e9c02SRob Herring (Arm) 	dma_fence_put(job->done_fence);
1555a5e9c02SRob Herring (Arm) 	dma_fence_put(job->inference_done_fence);
1565a5e9c02SRob Herring (Arm) 
1575a5e9c02SRob Herring (Arm) 	for (i = 0; i < job->region_cnt; i++)
1585a5e9c02SRob Herring (Arm) 		drm_gem_object_put(job->region_bo[i]);
1595a5e9c02SRob Herring (Arm) 
1605a5e9c02SRob Herring (Arm) 	drm_gem_object_put(job->cmd_bo);
1615a5e9c02SRob Herring (Arm) 
1625a5e9c02SRob Herring (Arm) 	kfree(job);
1635a5e9c02SRob Herring (Arm) }
1645a5e9c02SRob Herring (Arm) 
1655a5e9c02SRob Herring (Arm) static void ethosu_job_put(struct ethosu_job *job)
1665a5e9c02SRob Herring (Arm) {
1675a5e9c02SRob Herring (Arm) 	kref_put(&job->refcount, ethosu_job_cleanup);
1685a5e9c02SRob Herring (Arm) }
1695a5e9c02SRob Herring (Arm) 
1705a5e9c02SRob Herring (Arm) static void ethosu_job_free(struct drm_sched_job *sched_job)
1715a5e9c02SRob Herring (Arm) {
1725a5e9c02SRob Herring (Arm) 	struct ethosu_job *job = to_ethosu_job(sched_job);
1735a5e9c02SRob Herring (Arm) 
1745a5e9c02SRob Herring (Arm) 	drm_sched_job_cleanup(sched_job);
1755a5e9c02SRob Herring (Arm) 	ethosu_job_put(job);
1765a5e9c02SRob Herring (Arm) }
1775a5e9c02SRob Herring (Arm) 
1785a5e9c02SRob Herring (Arm) static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job)
1795a5e9c02SRob Herring (Arm) {
1805a5e9c02SRob Herring (Arm) 	struct ethosu_job *job = to_ethosu_job(sched_job);
1815a5e9c02SRob Herring (Arm) 	struct ethosu_device *dev = job->dev;
1825a5e9c02SRob Herring (Arm) 	struct dma_fence *fence = job->done_fence;
1835a5e9c02SRob Herring (Arm) 
1845a5e9c02SRob Herring (Arm) 	if (unlikely(job->base.s_fence->finished.error))
1855a5e9c02SRob Herring (Arm) 		return NULL;
1865a5e9c02SRob Herring (Arm) 
1875a5e9c02SRob Herring (Arm) 	dma_fence_init(fence, &ethosu_fence_ops, &dev->fence_lock,
1885a5e9c02SRob Herring (Arm) 		       dev->fence_context, ++dev->emit_seqno);
1895a5e9c02SRob Herring (Arm) 	dma_fence_get(fence);
1905a5e9c02SRob Herring (Arm) 
1915a5e9c02SRob Herring (Arm) 	scoped_guard(mutex, &dev->job_lock) {
1925a5e9c02SRob Herring (Arm) 		dev->in_flight_job = job;
1935a5e9c02SRob Herring (Arm) 		ethosu_job_hw_submit(dev, job);
1945a5e9c02SRob Herring (Arm) 	}
1955a5e9c02SRob Herring (Arm) 
1965a5e9c02SRob Herring (Arm) 	return fence;
1975a5e9c02SRob Herring (Arm) }
1985a5e9c02SRob Herring (Arm) 
1995a5e9c02SRob Herring (Arm) static void ethosu_job_handle_irq(struct ethosu_device *dev)
2005a5e9c02SRob Herring (Arm) {
2015a5e9c02SRob Herring (Arm) 	u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS);
2025a5e9c02SRob Herring (Arm) 
2035a5e9c02SRob Herring (Arm) 	if (status & (STATUS_BUS_STATUS | STATUS_CMD_PARSE_ERR)) {
2045a5e9c02SRob Herring (Arm) 		dev_err(dev->base.dev, "Error IRQ - %x\n", status);
2055a5e9c02SRob Herring (Arm) 		drm_sched_fault(&dev->sched);
2065a5e9c02SRob Herring (Arm) 		return;
2075a5e9c02SRob Herring (Arm) 	}
2085a5e9c02SRob Herring (Arm) 
2095a5e9c02SRob Herring (Arm) 	scoped_guard(mutex, &dev->job_lock) {
2105a5e9c02SRob Herring (Arm) 		if (dev->in_flight_job) {
2115a5e9c02SRob Herring (Arm) 			dma_fence_signal(dev->in_flight_job->done_fence);
2125a5e9c02SRob Herring (Arm) 			dev->in_flight_job = NULL;
2135a5e9c02SRob Herring (Arm) 		}
2145a5e9c02SRob Herring (Arm) 	}
2155a5e9c02SRob Herring (Arm) }
2165a5e9c02SRob Herring (Arm) 
2175a5e9c02SRob Herring (Arm) static irqreturn_t ethosu_job_irq_handler_thread(int irq, void *data)
2185a5e9c02SRob Herring (Arm) {
2195a5e9c02SRob Herring (Arm) 	struct ethosu_device *dev = data;
2205a5e9c02SRob Herring (Arm) 
2215a5e9c02SRob Herring (Arm) 	ethosu_job_handle_irq(dev);
2225a5e9c02SRob Herring (Arm) 
2235a5e9c02SRob Herring (Arm) 	return IRQ_HANDLED;
2245a5e9c02SRob Herring (Arm) }
2255a5e9c02SRob Herring (Arm) 
2265a5e9c02SRob Herring (Arm) static irqreturn_t ethosu_job_irq_handler(int irq, void *data)
2275a5e9c02SRob Herring (Arm) {
2285a5e9c02SRob Herring (Arm) 	struct ethosu_device *dev = data;
2295a5e9c02SRob Herring (Arm) 	u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS);
2305a5e9c02SRob Herring (Arm) 
2315a5e9c02SRob Herring (Arm) 	if (!(status & STATUS_IRQ_RAISED))
2325a5e9c02SRob Herring (Arm) 		return IRQ_NONE;
2335a5e9c02SRob Herring (Arm) 
2345a5e9c02SRob Herring (Arm) 	writel_relaxed(CMD_CLEAR_IRQ, dev->regs + NPU_REG_CMD);
2355a5e9c02SRob Herring (Arm) 	return IRQ_WAKE_THREAD;
2365a5e9c02SRob Herring (Arm) }
2375a5e9c02SRob Herring (Arm) 
2385a5e9c02SRob Herring (Arm) static enum drm_gpu_sched_stat ethosu_job_timedout(struct drm_sched_job *bad)
2395a5e9c02SRob Herring (Arm) {
2405a5e9c02SRob Herring (Arm) 	struct ethosu_job *job = to_ethosu_job(bad);
2415a5e9c02SRob Herring (Arm) 	struct ethosu_device *dev = job->dev;
2425a5e9c02SRob Herring (Arm) 	bool running;
2435a5e9c02SRob Herring (Arm) 	u32 *bocmds = to_drm_gem_dma_obj(job->cmd_bo)->vaddr;
2445a5e9c02SRob Herring (Arm) 	u32 cmdaddr;
2455a5e9c02SRob Herring (Arm) 
2465a5e9c02SRob Herring (Arm) 	cmdaddr = readl_relaxed(dev->regs + NPU_REG_QREAD);
2475a5e9c02SRob Herring (Arm) 	running = FIELD_GET(STATUS_STATE_RUNNING, readl_relaxed(dev->regs + NPU_REG_STATUS));
2485a5e9c02SRob Herring (Arm) 
2495a5e9c02SRob Herring (Arm) 	if (running) {
2505a5e9c02SRob Herring (Arm) 		int ret;
2515a5e9c02SRob Herring (Arm) 		u32 reg;
2525a5e9c02SRob Herring (Arm) 
2535a5e9c02SRob Herring (Arm) 		ret = readl_relaxed_poll_timeout(dev->regs + NPU_REG_QREAD,
2545a5e9c02SRob Herring (Arm) 						 reg,
2555a5e9c02SRob Herring (Arm) 						 reg != cmdaddr,
2565a5e9c02SRob Herring (Arm) 						 USEC_PER_MSEC, 100 * USEC_PER_MSEC);
2575a5e9c02SRob Herring (Arm) 
2585a5e9c02SRob Herring (Arm) 		/* If still running and progress is being made, just return */
2595a5e9c02SRob Herring (Arm) 		if (!ret)
2605a5e9c02SRob Herring (Arm) 			return DRM_GPU_SCHED_STAT_NO_HANG;
2615a5e9c02SRob Herring (Arm) 	}
2625a5e9c02SRob Herring (Arm) 
2635a5e9c02SRob Herring (Arm) 	dev_err(dev->base.dev, "NPU sched timed out: NPU %s, cmdstream offset 0x%x: 0x%x\n",
2645a5e9c02SRob Herring (Arm) 		running ? "running" : "stopped",
2655a5e9c02SRob Herring (Arm) 		cmdaddr, bocmds[cmdaddr / 4]);
2665a5e9c02SRob Herring (Arm) 
2675a5e9c02SRob Herring (Arm) 	drm_sched_stop(&dev->sched, bad);
2685a5e9c02SRob Herring (Arm) 
2695a5e9c02SRob Herring (Arm) 	scoped_guard(mutex, &dev->job_lock)
2705a5e9c02SRob Herring (Arm) 		dev->in_flight_job = NULL;
2715a5e9c02SRob Herring (Arm) 
2725a5e9c02SRob Herring (Arm) 	/* Proceed with reset now. */
2735a5e9c02SRob Herring (Arm) 	pm_runtime_force_suspend(dev->base.dev);
2745a5e9c02SRob Herring (Arm) 	pm_runtime_force_resume(dev->base.dev);
2755a5e9c02SRob Herring (Arm) 
2765a5e9c02SRob Herring (Arm) 	/* Restart the scheduler */
2775a5e9c02SRob Herring (Arm) 	drm_sched_start(&dev->sched, 0);
2785a5e9c02SRob Herring (Arm) 
2795a5e9c02SRob Herring (Arm) 	return DRM_GPU_SCHED_STAT_RESET;
2805a5e9c02SRob Herring (Arm) }
2815a5e9c02SRob Herring (Arm) 
2825a5e9c02SRob Herring (Arm) static const struct drm_sched_backend_ops ethosu_sched_ops = {
2835a5e9c02SRob Herring (Arm) 	.run_job = ethosu_job_run,
2845a5e9c02SRob Herring (Arm) 	.timedout_job = ethosu_job_timedout,
2855a5e9c02SRob Herring (Arm) 	.free_job = ethosu_job_free
2865a5e9c02SRob Herring (Arm) };
2875a5e9c02SRob Herring (Arm) 
2885a5e9c02SRob Herring (Arm) int ethosu_job_init(struct ethosu_device *edev)
2895a5e9c02SRob Herring (Arm) {
2905a5e9c02SRob Herring (Arm) 	struct device *dev = edev->base.dev;
2915a5e9c02SRob Herring (Arm) 	struct drm_sched_init_args args = {
2925a5e9c02SRob Herring (Arm) 		.ops = &ethosu_sched_ops,
2935a5e9c02SRob Herring (Arm) 		.num_rqs = DRM_SCHED_PRIORITY_COUNT,
2945a5e9c02SRob Herring (Arm) 		.credit_limit = 1,
2955a5e9c02SRob Herring (Arm) 		.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
2965a5e9c02SRob Herring (Arm) 		.name = dev_name(dev),
2975a5e9c02SRob Herring (Arm) 		.dev = dev,
2985a5e9c02SRob Herring (Arm) 	};
2995a5e9c02SRob Herring (Arm) 	int ret;
3005a5e9c02SRob Herring (Arm) 
3015a5e9c02SRob Herring (Arm) 	spin_lock_init(&edev->fence_lock);
3025a5e9c02SRob Herring (Arm) 	ret = devm_mutex_init(dev, &edev->job_lock);
3035a5e9c02SRob Herring (Arm) 	if (ret)
3045a5e9c02SRob Herring (Arm) 		return ret;
3055a5e9c02SRob Herring (Arm) 	ret = devm_mutex_init(dev, &edev->sched_lock);
3065a5e9c02SRob Herring (Arm) 	if (ret)
3075a5e9c02SRob Herring (Arm) 		return ret;
3085a5e9c02SRob Herring (Arm) 
3095a5e9c02SRob Herring (Arm) 	edev->irq = platform_get_irq(to_platform_device(dev), 0);
3105a5e9c02SRob Herring (Arm) 	if (edev->irq < 0)
3115a5e9c02SRob Herring (Arm) 		return edev->irq;
3125a5e9c02SRob Herring (Arm) 
3135a5e9c02SRob Herring (Arm) 	ret = devm_request_threaded_irq(dev, edev->irq,
3145a5e9c02SRob Herring (Arm) 					ethosu_job_irq_handler,
3155a5e9c02SRob Herring (Arm) 					ethosu_job_irq_handler_thread,
3165a5e9c02SRob Herring (Arm) 					IRQF_SHARED, KBUILD_MODNAME,
3175a5e9c02SRob Herring (Arm) 					edev);
3185a5e9c02SRob Herring (Arm) 	if (ret) {
3195a5e9c02SRob Herring (Arm) 		dev_err(dev, "failed to request irq\n");
3205a5e9c02SRob Herring (Arm) 		return ret;
3215a5e9c02SRob Herring (Arm) 	}
3225a5e9c02SRob Herring (Arm) 
3235a5e9c02SRob Herring (Arm) 	edev->fence_context = dma_fence_context_alloc(1);
3245a5e9c02SRob Herring (Arm) 
3255a5e9c02SRob Herring (Arm) 	ret = drm_sched_init(&edev->sched, &args);
3265a5e9c02SRob Herring (Arm) 	if (ret) {
3275a5e9c02SRob Herring (Arm) 		dev_err(dev, "Failed to create scheduler: %d\n", ret);
3285a5e9c02SRob Herring (Arm) 		goto err_sched;
3295a5e9c02SRob Herring (Arm) 	}
3305a5e9c02SRob Herring (Arm) 
3315a5e9c02SRob Herring (Arm) 	return 0;
3325a5e9c02SRob Herring (Arm) 
3335a5e9c02SRob Herring (Arm) err_sched:
3345a5e9c02SRob Herring (Arm) 	drm_sched_fini(&edev->sched);
3355a5e9c02SRob Herring (Arm) 	return ret;
3365a5e9c02SRob Herring (Arm) }
3375a5e9c02SRob Herring (Arm) 
3385a5e9c02SRob Herring (Arm) void ethosu_job_fini(struct ethosu_device *dev)
3395a5e9c02SRob Herring (Arm) {
3405a5e9c02SRob Herring (Arm) 	drm_sched_fini(&dev->sched);
3415a5e9c02SRob Herring (Arm) }
3425a5e9c02SRob Herring (Arm) 
3435a5e9c02SRob Herring (Arm) int ethosu_job_open(struct ethosu_file_priv *ethosu_priv)
3445a5e9c02SRob Herring (Arm) {
3455a5e9c02SRob Herring (Arm) 	struct ethosu_device *dev = ethosu_priv->edev;
3465a5e9c02SRob Herring (Arm) 	struct drm_gpu_scheduler *sched = &dev->sched;
3475a5e9c02SRob Herring (Arm) 	int ret;
3485a5e9c02SRob Herring (Arm) 
3495a5e9c02SRob Herring (Arm) 	ret = drm_sched_entity_init(&ethosu_priv->sched_entity,
3505a5e9c02SRob Herring (Arm) 				    DRM_SCHED_PRIORITY_NORMAL,
3515a5e9c02SRob Herring (Arm) 				    &sched, 1, NULL);
3525a5e9c02SRob Herring (Arm) 	return WARN_ON(ret);
3535a5e9c02SRob Herring (Arm) }
3545a5e9c02SRob Herring (Arm) 
3555a5e9c02SRob Herring (Arm) void ethosu_job_close(struct ethosu_file_priv *ethosu_priv)
3565a5e9c02SRob Herring (Arm) {
3575a5e9c02SRob Herring (Arm) 	struct drm_sched_entity *entity = &ethosu_priv->sched_entity;
3585a5e9c02SRob Herring (Arm) 
3595a5e9c02SRob Herring (Arm) 	drm_sched_entity_destroy(entity);
3605a5e9c02SRob Herring (Arm) }
3615a5e9c02SRob Herring (Arm) 
3625a5e9c02SRob Herring (Arm) static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file,
3635a5e9c02SRob Herring (Arm) 				   struct drm_ethosu_job *job)
3645a5e9c02SRob Herring (Arm) {
3655a5e9c02SRob Herring (Arm) 	struct ethosu_device *edev = to_ethosu_device(dev);
3665a5e9c02SRob Herring (Arm) 	struct ethosu_file_priv *file_priv = file->driver_priv;
3675a5e9c02SRob Herring (Arm) 	struct ethosu_job *ejob = NULL;
3685a5e9c02SRob Herring (Arm) 	struct ethosu_validated_cmdstream_info *cmd_info;
3695a5e9c02SRob Herring (Arm) 	int ret = 0;
3705a5e9c02SRob Herring (Arm) 
3715a5e9c02SRob Herring (Arm) 	/* BO region 2 is reserved if SRAM is used */
3725a5e9c02SRob Herring (Arm) 	if (job->region_bo_handles[ETHOSU_SRAM_REGION] && job->sram_size)
3735a5e9c02SRob Herring (Arm) 		return -EINVAL;
3745a5e9c02SRob Herring (Arm) 
3755a5e9c02SRob Herring (Arm) 	if (edev->npu_info.sram_size < job->sram_size)
3765a5e9c02SRob Herring (Arm) 		return -EINVAL;
3775a5e9c02SRob Herring (Arm) 
3785a5e9c02SRob Herring (Arm) 	ejob = kzalloc(sizeof(*ejob), GFP_KERNEL);
3795a5e9c02SRob Herring (Arm) 	if (!ejob)
3805a5e9c02SRob Herring (Arm) 		return -ENOMEM;
3815a5e9c02SRob Herring (Arm) 
3825a5e9c02SRob Herring (Arm) 	kref_init(&ejob->refcount);
3835a5e9c02SRob Herring (Arm) 
3845a5e9c02SRob Herring (Arm) 	ejob->dev = edev;
3855a5e9c02SRob Herring (Arm) 	ejob->sram_size = job->sram_size;
3865a5e9c02SRob Herring (Arm) 
3875a5e9c02SRob Herring (Arm) 	ejob->done_fence = kzalloc(sizeof(*ejob->done_fence), GFP_KERNEL);
3885a5e9c02SRob Herring (Arm) 	if (!ejob->done_fence) {
3895a5e9c02SRob Herring (Arm) 		ret = -ENOMEM;
3905a5e9c02SRob Herring (Arm) 		goto out_cleanup_job;
3915a5e9c02SRob Herring (Arm) 	}
3925a5e9c02SRob Herring (Arm) 
3935a5e9c02SRob Herring (Arm) 	ret = drm_sched_job_init(&ejob->base,
3945a5e9c02SRob Herring (Arm) 				 &file_priv->sched_entity,
3955a5e9c02SRob Herring (Arm) 				 1, NULL, file->client_id);
3965a5e9c02SRob Herring (Arm) 	if (ret)
3975a5e9c02SRob Herring (Arm) 		goto out_put_job;
3985a5e9c02SRob Herring (Arm) 
3995a5e9c02SRob Herring (Arm) 	ejob->cmd_bo = drm_gem_object_lookup(file, job->cmd_bo);
4005a5e9c02SRob Herring (Arm) 	if (!ejob->cmd_bo) {
4015a5e9c02SRob Herring (Arm) 		ret = -ENOENT;
4025a5e9c02SRob Herring (Arm) 		goto out_cleanup_job;
4035a5e9c02SRob Herring (Arm) 	}
4045a5e9c02SRob Herring (Arm) 	cmd_info = to_ethosu_bo(ejob->cmd_bo)->info;
4055a5e9c02SRob Herring (Arm) 	if (!cmd_info) {
4065a5e9c02SRob Herring (Arm) 		ret = -EINVAL;
4075a5e9c02SRob Herring (Arm) 		goto out_cleanup_job;
4085a5e9c02SRob Herring (Arm) 	}
4095a5e9c02SRob Herring (Arm) 
4105a5e9c02SRob Herring (Arm) 	for (int i = 0; i < NPU_BASEP_REGION_MAX; i++) {
4115a5e9c02SRob Herring (Arm) 		struct drm_gem_object *gem;
4125a5e9c02SRob Herring (Arm) 
4135a5e9c02SRob Herring (Arm) 		/* Can only omit a BO handle if the region is not used or used for SRAM */
4145a5e9c02SRob Herring (Arm) 		if (!job->region_bo_handles[i] &&
4155a5e9c02SRob Herring (Arm) 		    (!cmd_info->region_size[i] || (i == ETHOSU_SRAM_REGION && job->sram_size)))
4165a5e9c02SRob Herring (Arm) 			continue;
4175a5e9c02SRob Herring (Arm) 
4185a5e9c02SRob Herring (Arm) 		if (job->region_bo_handles[i] && !cmd_info->region_size[i]) {
4195a5e9c02SRob Herring (Arm) 			dev_err(dev->dev,
4205a5e9c02SRob Herring (Arm) 				"Cmdstream BO handle %d set for unused region %d\n",
4215a5e9c02SRob Herring (Arm) 				job->region_bo_handles[i], i);
4225a5e9c02SRob Herring (Arm) 			ret = -EINVAL;
4235a5e9c02SRob Herring (Arm) 			goto out_cleanup_job;
4245a5e9c02SRob Herring (Arm) 		}
4255a5e9c02SRob Herring (Arm) 
4265a5e9c02SRob Herring (Arm) 		gem = drm_gem_object_lookup(file, job->region_bo_handles[i]);
4275a5e9c02SRob Herring (Arm) 		if (!gem) {
4285a5e9c02SRob Herring (Arm) 			dev_err(dev->dev,
4295a5e9c02SRob Herring (Arm) 				"Invalid BO handle %d for region %d\n",
4305a5e9c02SRob Herring (Arm) 				job->region_bo_handles[i], i);
4315a5e9c02SRob Herring (Arm) 			ret = -ENOENT;
4325a5e9c02SRob Herring (Arm) 			goto out_cleanup_job;
4335a5e9c02SRob Herring (Arm) 		}
4345a5e9c02SRob Herring (Arm) 
4355a5e9c02SRob Herring (Arm) 		ejob->region_bo[ejob->region_cnt] = gem;
4365a5e9c02SRob Herring (Arm) 		ejob->region_bo_num[ejob->region_cnt] = i;
4375a5e9c02SRob Herring (Arm) 		ejob->region_cnt++;
4385a5e9c02SRob Herring (Arm) 
4395a5e9c02SRob Herring (Arm) 		if (to_ethosu_bo(gem)->info) {
4405a5e9c02SRob Herring (Arm) 			dev_err(dev->dev,
4415a5e9c02SRob Herring (Arm) 				"Cmdstream BO handle %d used for region %d\n",
4425a5e9c02SRob Herring (Arm) 				job->region_bo_handles[i], i);
4435a5e9c02SRob Herring (Arm) 			ret = -EINVAL;
4445a5e9c02SRob Herring (Arm) 			goto out_cleanup_job;
4455a5e9c02SRob Herring (Arm) 		}
4465a5e9c02SRob Herring (Arm) 
4475a5e9c02SRob Herring (Arm) 		/* Verify the command stream doesn't have accesses outside the BO */
4485a5e9c02SRob Herring (Arm) 		if (cmd_info->region_size[i] > gem->size) {
4495a5e9c02SRob Herring (Arm) 			dev_err(dev->dev,
4505a5e9c02SRob Herring (Arm) 				"cmd stream region %d size greater than BO size (%llu > %zu)\n",
4515a5e9c02SRob Herring (Arm) 				i, cmd_info->region_size[i], gem->size);
4525a5e9c02SRob Herring (Arm) 			ret = -EOVERFLOW;
4535a5e9c02SRob Herring (Arm) 			goto out_cleanup_job;
4545a5e9c02SRob Herring (Arm) 		}
4555a5e9c02SRob Herring (Arm) 	}
4565a5e9c02SRob Herring (Arm) 	ret = ethosu_job_push(ejob);
4575a5e9c02SRob Herring (Arm) 
4585a5e9c02SRob Herring (Arm) out_cleanup_job:
4595a5e9c02SRob Herring (Arm) 	if (ret)
4605a5e9c02SRob Herring (Arm) 		drm_sched_job_cleanup(&ejob->base);
4615a5e9c02SRob Herring (Arm) out_put_job:
4625a5e9c02SRob Herring (Arm) 	ethosu_job_put(ejob);
4635a5e9c02SRob Herring (Arm) 
4645a5e9c02SRob Herring (Arm) 	return ret;
4655a5e9c02SRob Herring (Arm) }
4665a5e9c02SRob Herring (Arm) 
4675a5e9c02SRob Herring (Arm) int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file)
4685a5e9c02SRob Herring (Arm) {
4695a5e9c02SRob Herring (Arm) 	struct drm_ethosu_submit *args = data;
4705a5e9c02SRob Herring (Arm) 	int ret = 0;
4715a5e9c02SRob Herring (Arm) 	unsigned int i = 0;
4725a5e9c02SRob Herring (Arm) 
4735a5e9c02SRob Herring (Arm) 	if (args->pad) {
4745a5e9c02SRob Herring (Arm) 		drm_dbg(dev, "Reserved field in drm_ethosu_submit struct should be 0.\n");
4755a5e9c02SRob Herring (Arm) 		return -EINVAL;
4765a5e9c02SRob Herring (Arm) 	}
4775a5e9c02SRob Herring (Arm) 
4785a5e9c02SRob Herring (Arm) 	struct drm_ethosu_job __free(kvfree) *jobs =
4795a5e9c02SRob Herring (Arm) 		kvmalloc_array(args->job_count, sizeof(*jobs), GFP_KERNEL);
4805a5e9c02SRob Herring (Arm) 	if (!jobs)
4815a5e9c02SRob Herring (Arm) 		return -ENOMEM;
4825a5e9c02SRob Herring (Arm) 
4835a5e9c02SRob Herring (Arm) 	if (copy_from_user(jobs,
4845a5e9c02SRob Herring (Arm) 			   (void __user *)(uintptr_t)args->jobs,
4855a5e9c02SRob Herring (Arm) 			   args->job_count * sizeof(*jobs))) {
4865a5e9c02SRob Herring (Arm) 		drm_dbg(dev, "Failed to copy incoming job array\n");
4875a5e9c02SRob Herring (Arm) 		return -EFAULT;
4885a5e9c02SRob Herring (Arm) 	}
4895a5e9c02SRob Herring (Arm) 
4905a5e9c02SRob Herring (Arm) 	for (i = 0; i < args->job_count; i++) {
4915a5e9c02SRob Herring (Arm) 		ret = ethosu_ioctl_submit_job(dev, file, &jobs[i]);
4925a5e9c02SRob Herring (Arm) 		if (ret)
4935a5e9c02SRob Herring (Arm) 			return ret;
4945a5e9c02SRob Herring (Arm) 	}
4955a5e9c02SRob Herring (Arm) 
4965a5e9c02SRob Herring (Arm) 	return 0;
4975a5e9c02SRob Herring (Arm) }
498