162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat
462306a36Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "drm/drm_drv.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "msm_gpu.h"
1062306a36Sopenharmony_ci#include "msm_gem.h"
1162306a36Sopenharmony_ci#include "msm_mmu.h"
1262306a36Sopenharmony_ci#include "msm_fence.h"
1362306a36Sopenharmony_ci#include "msm_gpu_trace.h"
1462306a36Sopenharmony_ci#include "adreno/adreno_gpu.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <generated/utsrelease.h>
1762306a36Sopenharmony_ci#include <linux/string_helpers.h>
1862306a36Sopenharmony_ci#include <linux/devcoredump.h>
1962306a36Sopenharmony_ci#include <linux/sched/task.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * Power Management:
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int enable_pwrrail(struct msm_gpu *gpu)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct drm_device *dev = gpu->dev;
2862306a36Sopenharmony_ci	int ret = 0;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (gpu->gpu_reg) {
3162306a36Sopenharmony_ci		ret = regulator_enable(gpu->gpu_reg);
3262306a36Sopenharmony_ci		if (ret) {
3362306a36Sopenharmony_ci			DRM_DEV_ERROR(dev->dev, "failed to enable 'gpu_reg': %d\n", ret);
3462306a36Sopenharmony_ci			return ret;
3562306a36Sopenharmony_ci		}
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (gpu->gpu_cx) {
3962306a36Sopenharmony_ci		ret = regulator_enable(gpu->gpu_cx);
4062306a36Sopenharmony_ci		if (ret) {
4162306a36Sopenharmony_ci			DRM_DEV_ERROR(dev->dev, "failed to enable 'gpu_cx': %d\n", ret);
4262306a36Sopenharmony_ci			return ret;
4362306a36Sopenharmony_ci		}
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return 0;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int disable_pwrrail(struct msm_gpu *gpu)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	if (gpu->gpu_cx)
5262306a36Sopenharmony_ci		regulator_disable(gpu->gpu_cx);
5362306a36Sopenharmony_ci	if (gpu->gpu_reg)
5462306a36Sopenharmony_ci		regulator_disable(gpu->gpu_reg);
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int enable_clk(struct msm_gpu *gpu)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	if (gpu->core_clk && gpu->fast_rate)
6162306a36Sopenharmony_ci		dev_pm_opp_set_rate(&gpu->pdev->dev, gpu->fast_rate);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* Set the RBBM timer rate to 19.2Mhz */
6462306a36Sopenharmony_ci	if (gpu->rbbmtimer_clk)
6562306a36Sopenharmony_ci		clk_set_rate(gpu->rbbmtimer_clk, 19200000);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return clk_bulk_prepare_enable(gpu->nr_clocks, gpu->grp_clks);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int disable_clk(struct msm_gpu *gpu)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	clk_bulk_disable_unprepare(gpu->nr_clocks, gpu->grp_clks);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/*
7562306a36Sopenharmony_ci	 * Set the clock to a deliberately low rate. On older targets the clock
7662306a36Sopenharmony_ci	 * speed had to be non zero to avoid problems. On newer targets this
7762306a36Sopenharmony_ci	 * will be rounded down to zero anyway so it all works out.
7862306a36Sopenharmony_ci	 */
7962306a36Sopenharmony_ci	if (gpu->core_clk)
8062306a36Sopenharmony_ci		dev_pm_opp_set_rate(&gpu->pdev->dev, 27000000);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (gpu->rbbmtimer_clk)
8362306a36Sopenharmony_ci		clk_set_rate(gpu->rbbmtimer_clk, 0);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return 0;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int enable_axi(struct msm_gpu *gpu)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	return clk_prepare_enable(gpu->ebi1_clk);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int disable_axi(struct msm_gpu *gpu)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	clk_disable_unprepare(gpu->ebi1_clk);
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ciint msm_gpu_pm_resume(struct msm_gpu *gpu)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int ret;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	DBG("%s", gpu->name);
10462306a36Sopenharmony_ci	trace_msm_gpu_resume(0);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	ret = enable_pwrrail(gpu);
10762306a36Sopenharmony_ci	if (ret)
10862306a36Sopenharmony_ci		return ret;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	ret = enable_clk(gpu);
11162306a36Sopenharmony_ci	if (ret)
11262306a36Sopenharmony_ci		return ret;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	ret = enable_axi(gpu);
11562306a36Sopenharmony_ci	if (ret)
11662306a36Sopenharmony_ci		return ret;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	msm_devfreq_resume(gpu);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	gpu->needs_hw_init = true;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciint msm_gpu_pm_suspend(struct msm_gpu *gpu)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	int ret;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	DBG("%s", gpu->name);
13062306a36Sopenharmony_ci	trace_msm_gpu_suspend(0);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	msm_devfreq_suspend(gpu);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ret = disable_axi(gpu);
13562306a36Sopenharmony_ci	if (ret)
13662306a36Sopenharmony_ci		return ret;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	ret = disable_clk(gpu);
13962306a36Sopenharmony_ci	if (ret)
14062306a36Sopenharmony_ci		return ret;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	ret = disable_pwrrail(gpu);
14362306a36Sopenharmony_ci	if (ret)
14462306a36Sopenharmony_ci		return ret;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	gpu->suspend_count++;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return 0;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_civoid msm_gpu_show_fdinfo(struct msm_gpu *gpu, struct msm_file_private *ctx,
15262306a36Sopenharmony_ci			 struct drm_printer *p)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	drm_printf(p, "drm-engine-gpu:\t%llu ns\n", ctx->elapsed_ns);
15562306a36Sopenharmony_ci	drm_printf(p, "drm-cycles-gpu:\t%llu\n", ctx->cycles);
15662306a36Sopenharmony_ci	drm_printf(p, "drm-maxfreq-gpu:\t%u Hz\n", gpu->fast_rate);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ciint msm_gpu_hw_init(struct msm_gpu *gpu)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	int ret;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&gpu->lock));
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (!gpu->needs_hw_init)
16662306a36Sopenharmony_ci		return 0;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	disable_irq(gpu->irq);
16962306a36Sopenharmony_ci	ret = gpu->funcs->hw_init(gpu);
17062306a36Sopenharmony_ci	if (!ret)
17162306a36Sopenharmony_ci		gpu->needs_hw_init = false;
17262306a36Sopenharmony_ci	enable_irq(gpu->irq);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return ret;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci#ifdef CONFIG_DEV_COREDUMP
17862306a36Sopenharmony_cistatic ssize_t msm_gpu_devcoredump_read(char *buffer, loff_t offset,
17962306a36Sopenharmony_ci		size_t count, void *data, size_t datalen)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct msm_gpu *gpu = data;
18262306a36Sopenharmony_ci	struct drm_print_iterator iter;
18362306a36Sopenharmony_ci	struct drm_printer p;
18462306a36Sopenharmony_ci	struct msm_gpu_state *state;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	state = msm_gpu_crashstate_get(gpu);
18762306a36Sopenharmony_ci	if (!state)
18862306a36Sopenharmony_ci		return 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	iter.data = buffer;
19162306a36Sopenharmony_ci	iter.offset = 0;
19262306a36Sopenharmony_ci	iter.start = offset;
19362306a36Sopenharmony_ci	iter.remain = count;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	p = drm_coredump_printer(&iter);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	drm_printf(&p, "---\n");
19862306a36Sopenharmony_ci	drm_printf(&p, "kernel: " UTS_RELEASE "\n");
19962306a36Sopenharmony_ci	drm_printf(&p, "module: " KBUILD_MODNAME "\n");
20062306a36Sopenharmony_ci	drm_printf(&p, "time: %lld.%09ld\n",
20162306a36Sopenharmony_ci		state->time.tv_sec, state->time.tv_nsec);
20262306a36Sopenharmony_ci	if (state->comm)
20362306a36Sopenharmony_ci		drm_printf(&p, "comm: %s\n", state->comm);
20462306a36Sopenharmony_ci	if (state->cmd)
20562306a36Sopenharmony_ci		drm_printf(&p, "cmdline: %s\n", state->cmd);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	gpu->funcs->show(gpu, state, &p);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	msm_gpu_crashstate_put(gpu);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return count - iter.remain;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic void msm_gpu_devcoredump_free(void *data)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct msm_gpu *gpu = data;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	msm_gpu_crashstate_put(gpu);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic void msm_gpu_crashstate_get_bo(struct msm_gpu_state *state,
22262306a36Sopenharmony_ci		struct drm_gem_object *obj, u64 iova, bool full)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct msm_gpu_state_bo *state_bo = &state->bos[state->nr_bos];
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Don't record write only objects */
22762306a36Sopenharmony_ci	state_bo->size = obj->size;
22862306a36Sopenharmony_ci	state_bo->iova = iova;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(state_bo->name) != sizeof(to_msm_bo(obj)->name));
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	memcpy(state_bo->name, to_msm_bo(obj)->name, sizeof(state_bo->name));
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (full) {
23562306a36Sopenharmony_ci		void *ptr;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		state_bo->data = kvmalloc(obj->size, GFP_KERNEL);
23862306a36Sopenharmony_ci		if (!state_bo->data)
23962306a36Sopenharmony_ci			goto out;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		msm_gem_lock(obj);
24262306a36Sopenharmony_ci		ptr = msm_gem_get_vaddr_active(obj);
24362306a36Sopenharmony_ci		msm_gem_unlock(obj);
24462306a36Sopenharmony_ci		if (IS_ERR(ptr)) {
24562306a36Sopenharmony_ci			kvfree(state_bo->data);
24662306a36Sopenharmony_ci			state_bo->data = NULL;
24762306a36Sopenharmony_ci			goto out;
24862306a36Sopenharmony_ci		}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		memcpy(state_bo->data, ptr, obj->size);
25162306a36Sopenharmony_ci		msm_gem_put_vaddr(obj);
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ciout:
25462306a36Sopenharmony_ci	state->nr_bos++;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void msm_gpu_crashstate_capture(struct msm_gpu *gpu,
25862306a36Sopenharmony_ci		struct msm_gem_submit *submit, char *comm, char *cmd)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct msm_gpu_state *state;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/* Check if the target supports capturing crash state */
26362306a36Sopenharmony_ci	if (!gpu->funcs->gpu_state_get)
26462306a36Sopenharmony_ci		return;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* Only save one crash state at a time */
26762306a36Sopenharmony_ci	if (gpu->crashstate)
26862306a36Sopenharmony_ci		return;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	state = gpu->funcs->gpu_state_get(gpu);
27162306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(state))
27262306a36Sopenharmony_ci		return;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* Fill in the additional crash state information */
27562306a36Sopenharmony_ci	state->comm = kstrdup(comm, GFP_KERNEL);
27662306a36Sopenharmony_ci	state->cmd = kstrdup(cmd, GFP_KERNEL);
27762306a36Sopenharmony_ci	state->fault_info = gpu->fault_info;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (submit) {
28062306a36Sopenharmony_ci		int i;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		state->bos = kcalloc(submit->nr_bos,
28362306a36Sopenharmony_ci			sizeof(struct msm_gpu_state_bo), GFP_KERNEL);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		for (i = 0; state->bos && i < submit->nr_bos; i++) {
28662306a36Sopenharmony_ci			msm_gpu_crashstate_get_bo(state, submit->bos[i].obj,
28762306a36Sopenharmony_ci						  submit->bos[i].iova,
28862306a36Sopenharmony_ci						  should_dump(submit, i));
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Set the active crash state to be dumped on failure */
29362306a36Sopenharmony_ci	gpu->crashstate = state;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* FIXME: Release the crashstate if this errors out? */
29662306a36Sopenharmony_ci	dev_coredumpm(gpu->dev->dev, THIS_MODULE, gpu, 0, GFP_KERNEL,
29762306a36Sopenharmony_ci		msm_gpu_devcoredump_read, msm_gpu_devcoredump_free);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci#else
30062306a36Sopenharmony_cistatic void msm_gpu_crashstate_capture(struct msm_gpu *gpu,
30162306a36Sopenharmony_ci		struct msm_gem_submit *submit, char *comm, char *cmd)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci#endif
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/*
30762306a36Sopenharmony_ci * Hangcheck detection for locked gpu:
30862306a36Sopenharmony_ci */
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic struct msm_gem_submit *
31162306a36Sopenharmony_cifind_submit(struct msm_ringbuffer *ring, uint32_t fence)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct msm_gem_submit *submit;
31462306a36Sopenharmony_ci	unsigned long flags;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	spin_lock_irqsave(&ring->submit_lock, flags);
31762306a36Sopenharmony_ci	list_for_each_entry(submit, &ring->submits, node) {
31862306a36Sopenharmony_ci		if (submit->seqno == fence) {
31962306a36Sopenharmony_ci			spin_unlock_irqrestore(&ring->submit_lock, flags);
32062306a36Sopenharmony_ci			return submit;
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ring->submit_lock, flags);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	return NULL;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic void retire_submits(struct msm_gpu *gpu);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic void get_comm_cmdline(struct msm_gem_submit *submit, char **comm, char **cmd)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct msm_file_private *ctx = submit->queue->ctx;
33362306a36Sopenharmony_ci	struct task_struct *task;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&submit->gpu->lock));
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Note that kstrdup will return NULL if argument is NULL: */
33862306a36Sopenharmony_ci	*comm = kstrdup(ctx->comm, GFP_KERNEL);
33962306a36Sopenharmony_ci	*cmd  = kstrdup(ctx->cmdline, GFP_KERNEL);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	task = get_pid_task(submit->pid, PIDTYPE_PID);
34262306a36Sopenharmony_ci	if (!task)
34362306a36Sopenharmony_ci		return;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (!*comm)
34662306a36Sopenharmony_ci		*comm = kstrdup(task->comm, GFP_KERNEL);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (!*cmd)
34962306a36Sopenharmony_ci		*cmd = kstrdup_quotable_cmdline(task, GFP_KERNEL);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	put_task_struct(task);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void recover_worker(struct kthread_work *work)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct msm_gpu *gpu = container_of(work, struct msm_gpu, recover_work);
35762306a36Sopenharmony_ci	struct drm_device *dev = gpu->dev;
35862306a36Sopenharmony_ci	struct msm_drm_private *priv = dev->dev_private;
35962306a36Sopenharmony_ci	struct msm_gem_submit *submit;
36062306a36Sopenharmony_ci	struct msm_ringbuffer *cur_ring = gpu->funcs->active_ring(gpu);
36162306a36Sopenharmony_ci	char *comm = NULL, *cmd = NULL;
36262306a36Sopenharmony_ci	int i;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	mutex_lock(&gpu->lock);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	DRM_DEV_ERROR(dev->dev, "%s: hangcheck recover!\n", gpu->name);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	submit = find_submit(cur_ring, cur_ring->memptrs->fence + 1);
36962306a36Sopenharmony_ci	if (submit) {
37062306a36Sopenharmony_ci		/* Increment the fault counts */
37162306a36Sopenharmony_ci		submit->queue->faults++;
37262306a36Sopenharmony_ci		if (submit->aspace)
37362306a36Sopenharmony_ci			submit->aspace->faults++;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		get_comm_cmdline(submit, &comm, &cmd);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		if (comm && cmd) {
37862306a36Sopenharmony_ci			DRM_DEV_ERROR(dev->dev, "%s: offending task: %s (%s)\n",
37962306a36Sopenharmony_ci				gpu->name, comm, cmd);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci			msm_rd_dump_submit(priv->hangrd, submit,
38262306a36Sopenharmony_ci				"offending task: %s (%s)", comm, cmd);
38362306a36Sopenharmony_ci		} else {
38462306a36Sopenharmony_ci			msm_rd_dump_submit(priv->hangrd, submit, NULL);
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci	} else {
38762306a36Sopenharmony_ci		/*
38862306a36Sopenharmony_ci		 * We couldn't attribute this fault to any particular context,
38962306a36Sopenharmony_ci		 * so increment the global fault count instead.
39062306a36Sopenharmony_ci		 */
39162306a36Sopenharmony_ci		gpu->global_faults++;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/* Record the crash state */
39562306a36Sopenharmony_ci	pm_runtime_get_sync(&gpu->pdev->dev);
39662306a36Sopenharmony_ci	msm_gpu_crashstate_capture(gpu, submit, comm, cmd);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	kfree(cmd);
39962306a36Sopenharmony_ci	kfree(comm);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/*
40262306a36Sopenharmony_ci	 * Update all the rings with the latest and greatest fence.. this
40362306a36Sopenharmony_ci	 * needs to happen after msm_rd_dump_submit() to ensure that the
40462306a36Sopenharmony_ci	 * bo's referenced by the offending submit are still around.
40562306a36Sopenharmony_ci	 */
40662306a36Sopenharmony_ci	for (i = 0; i < gpu->nr_rings; i++) {
40762306a36Sopenharmony_ci		struct msm_ringbuffer *ring = gpu->rb[i];
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		uint32_t fence = ring->memptrs->fence;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		/*
41262306a36Sopenharmony_ci		 * For the current (faulting?) ring/submit advance the fence by
41362306a36Sopenharmony_ci		 * one more to clear the faulting submit
41462306a36Sopenharmony_ci		 */
41562306a36Sopenharmony_ci		if (ring == cur_ring)
41662306a36Sopenharmony_ci			ring->memptrs->fence = ++fence;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		msm_update_fence(ring->fctx, fence);
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (msm_gpu_active(gpu)) {
42262306a36Sopenharmony_ci		/* retire completed submits, plus the one that hung: */
42362306a36Sopenharmony_ci		retire_submits(gpu);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		gpu->funcs->recover(gpu);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		/*
42862306a36Sopenharmony_ci		 * Replay all remaining submits starting with highest priority
42962306a36Sopenharmony_ci		 * ring
43062306a36Sopenharmony_ci		 */
43162306a36Sopenharmony_ci		for (i = 0; i < gpu->nr_rings; i++) {
43262306a36Sopenharmony_ci			struct msm_ringbuffer *ring = gpu->rb[i];
43362306a36Sopenharmony_ci			unsigned long flags;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci			spin_lock_irqsave(&ring->submit_lock, flags);
43662306a36Sopenharmony_ci			list_for_each_entry(submit, &ring->submits, node)
43762306a36Sopenharmony_ci				gpu->funcs->submit(gpu, submit);
43862306a36Sopenharmony_ci			spin_unlock_irqrestore(&ring->submit_lock, flags);
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	pm_runtime_put(&gpu->pdev->dev);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	mutex_unlock(&gpu->lock);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	msm_gpu_retire(gpu);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic void fault_worker(struct kthread_work *work)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct msm_gpu *gpu = container_of(work, struct msm_gpu, fault_work);
45262306a36Sopenharmony_ci	struct msm_gem_submit *submit;
45362306a36Sopenharmony_ci	struct msm_ringbuffer *cur_ring = gpu->funcs->active_ring(gpu);
45462306a36Sopenharmony_ci	char *comm = NULL, *cmd = NULL;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	mutex_lock(&gpu->lock);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	submit = find_submit(cur_ring, cur_ring->memptrs->fence + 1);
45962306a36Sopenharmony_ci	if (submit && submit->fault_dumped)
46062306a36Sopenharmony_ci		goto resume_smmu;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	if (submit) {
46362306a36Sopenharmony_ci		get_comm_cmdline(submit, &comm, &cmd);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		/*
46662306a36Sopenharmony_ci		 * When we get GPU iova faults, we can get 1000s of them,
46762306a36Sopenharmony_ci		 * but we really only want to log the first one.
46862306a36Sopenharmony_ci		 */
46962306a36Sopenharmony_ci		submit->fault_dumped = true;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/* Record the crash state */
47362306a36Sopenharmony_ci	pm_runtime_get_sync(&gpu->pdev->dev);
47462306a36Sopenharmony_ci	msm_gpu_crashstate_capture(gpu, submit, comm, cmd);
47562306a36Sopenharmony_ci	pm_runtime_put_sync(&gpu->pdev->dev);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	kfree(cmd);
47862306a36Sopenharmony_ci	kfree(comm);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ciresume_smmu:
48162306a36Sopenharmony_ci	memset(&gpu->fault_info, 0, sizeof(gpu->fault_info));
48262306a36Sopenharmony_ci	gpu->aspace->mmu->funcs->resume_translation(gpu->aspace->mmu);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	mutex_unlock(&gpu->lock);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic void hangcheck_timer_reset(struct msm_gpu *gpu)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct msm_drm_private *priv = gpu->dev->dev_private;
49062306a36Sopenharmony_ci	mod_timer(&gpu->hangcheck_timer,
49162306a36Sopenharmony_ci			round_jiffies_up(jiffies + msecs_to_jiffies(priv->hangcheck_period)));
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic bool made_progress(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	if (ring->hangcheck_progress_retries >= DRM_MSM_HANGCHECK_PROGRESS_RETRIES)
49762306a36Sopenharmony_ci		return false;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (!gpu->funcs->progress)
50062306a36Sopenharmony_ci		return false;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (!gpu->funcs->progress(gpu, ring))
50362306a36Sopenharmony_ci		return false;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ring->hangcheck_progress_retries++;
50662306a36Sopenharmony_ci	return true;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic void hangcheck_handler(struct timer_list *t)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct msm_gpu *gpu = from_timer(gpu, t, hangcheck_timer);
51262306a36Sopenharmony_ci	struct drm_device *dev = gpu->dev;
51362306a36Sopenharmony_ci	struct msm_ringbuffer *ring = gpu->funcs->active_ring(gpu);
51462306a36Sopenharmony_ci	uint32_t fence = ring->memptrs->fence;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (fence != ring->hangcheck_fence) {
51762306a36Sopenharmony_ci		/* some progress has been made.. ya! */
51862306a36Sopenharmony_ci		ring->hangcheck_fence = fence;
51962306a36Sopenharmony_ci		ring->hangcheck_progress_retries = 0;
52062306a36Sopenharmony_ci	} else if (fence_before(fence, ring->fctx->last_fence) &&
52162306a36Sopenharmony_ci			!made_progress(gpu, ring)) {
52262306a36Sopenharmony_ci		/* no progress and not done.. hung! */
52362306a36Sopenharmony_ci		ring->hangcheck_fence = fence;
52462306a36Sopenharmony_ci		ring->hangcheck_progress_retries = 0;
52562306a36Sopenharmony_ci		DRM_DEV_ERROR(dev->dev, "%s: hangcheck detected gpu lockup rb %d!\n",
52662306a36Sopenharmony_ci				gpu->name, ring->id);
52762306a36Sopenharmony_ci		DRM_DEV_ERROR(dev->dev, "%s:     completed fence: %u\n",
52862306a36Sopenharmony_ci				gpu->name, fence);
52962306a36Sopenharmony_ci		DRM_DEV_ERROR(dev->dev, "%s:     submitted fence: %u\n",
53062306a36Sopenharmony_ci				gpu->name, ring->fctx->last_fence);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		kthread_queue_work(gpu->worker, &gpu->recover_work);
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* if still more pending work, reset the hangcheck timer: */
53662306a36Sopenharmony_ci	if (fence_after(ring->fctx->last_fence, ring->hangcheck_fence))
53762306a36Sopenharmony_ci		hangcheck_timer_reset(gpu);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	/* workaround for missing irq: */
54062306a36Sopenharmony_ci	msm_gpu_retire(gpu);
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci/*
54462306a36Sopenharmony_ci * Performance Counters:
54562306a36Sopenharmony_ci */
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/* called under perf_lock */
54862306a36Sopenharmony_cistatic int update_hw_cntrs(struct msm_gpu *gpu, uint32_t ncntrs, uint32_t *cntrs)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	uint32_t current_cntrs[ARRAY_SIZE(gpu->last_cntrs)];
55162306a36Sopenharmony_ci	int i, n = min(ncntrs, gpu->num_perfcntrs);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* read current values: */
55462306a36Sopenharmony_ci	for (i = 0; i < gpu->num_perfcntrs; i++)
55562306a36Sopenharmony_ci		current_cntrs[i] = gpu_read(gpu, gpu->perfcntrs[i].sample_reg);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* update cntrs: */
55862306a36Sopenharmony_ci	for (i = 0; i < n; i++)
55962306a36Sopenharmony_ci		cntrs[i] = current_cntrs[i] - gpu->last_cntrs[i];
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/* save current values: */
56262306a36Sopenharmony_ci	for (i = 0; i < gpu->num_perfcntrs; i++)
56362306a36Sopenharmony_ci		gpu->last_cntrs[i] = current_cntrs[i];
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return n;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic void update_sw_cntrs(struct msm_gpu *gpu)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	ktime_t time;
57162306a36Sopenharmony_ci	uint32_t elapsed;
57262306a36Sopenharmony_ci	unsigned long flags;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	spin_lock_irqsave(&gpu->perf_lock, flags);
57562306a36Sopenharmony_ci	if (!gpu->perfcntr_active)
57662306a36Sopenharmony_ci		goto out;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	time = ktime_get();
57962306a36Sopenharmony_ci	elapsed = ktime_to_us(ktime_sub(time, gpu->last_sample.time));
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	gpu->totaltime += elapsed;
58262306a36Sopenharmony_ci	if (gpu->last_sample.active)
58362306a36Sopenharmony_ci		gpu->activetime += elapsed;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	gpu->last_sample.active = msm_gpu_active(gpu);
58662306a36Sopenharmony_ci	gpu->last_sample.time = time;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ciout:
58962306a36Sopenharmony_ci	spin_unlock_irqrestore(&gpu->perf_lock, flags);
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_civoid msm_gpu_perfcntr_start(struct msm_gpu *gpu)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	unsigned long flags;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	pm_runtime_get_sync(&gpu->pdev->dev);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	spin_lock_irqsave(&gpu->perf_lock, flags);
59962306a36Sopenharmony_ci	/* we could dynamically enable/disable perfcntr registers too.. */
60062306a36Sopenharmony_ci	gpu->last_sample.active = msm_gpu_active(gpu);
60162306a36Sopenharmony_ci	gpu->last_sample.time = ktime_get();
60262306a36Sopenharmony_ci	gpu->activetime = gpu->totaltime = 0;
60362306a36Sopenharmony_ci	gpu->perfcntr_active = true;
60462306a36Sopenharmony_ci	update_hw_cntrs(gpu, 0, NULL);
60562306a36Sopenharmony_ci	spin_unlock_irqrestore(&gpu->perf_lock, flags);
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_civoid msm_gpu_perfcntr_stop(struct msm_gpu *gpu)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	gpu->perfcntr_active = false;
61162306a36Sopenharmony_ci	pm_runtime_put_sync(&gpu->pdev->dev);
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci/* returns -errno or # of cntrs sampled */
61562306a36Sopenharmony_ciint msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
61662306a36Sopenharmony_ci		uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	unsigned long flags;
61962306a36Sopenharmony_ci	int ret;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	spin_lock_irqsave(&gpu->perf_lock, flags);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (!gpu->perfcntr_active) {
62462306a36Sopenharmony_ci		ret = -EINVAL;
62562306a36Sopenharmony_ci		goto out;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	*activetime = gpu->activetime;
62962306a36Sopenharmony_ci	*totaltime = gpu->totaltime;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	gpu->activetime = gpu->totaltime = 0;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	ret = update_hw_cntrs(gpu, ncntrs, cntrs);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ciout:
63662306a36Sopenharmony_ci	spin_unlock_irqrestore(&gpu->perf_lock, flags);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	return ret;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci/*
64262306a36Sopenharmony_ci * Cmdstream submission/retirement:
64362306a36Sopenharmony_ci */
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic void retire_submit(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
64662306a36Sopenharmony_ci		struct msm_gem_submit *submit)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	int index = submit->seqno % MSM_GPU_SUBMIT_STATS_COUNT;
64962306a36Sopenharmony_ci	volatile struct msm_gpu_submit_stats *stats;
65062306a36Sopenharmony_ci	u64 elapsed, clock = 0, cycles;
65162306a36Sopenharmony_ci	unsigned long flags;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	stats = &ring->memptrs->stats[index];
65462306a36Sopenharmony_ci	/* Convert 19.2Mhz alwayson ticks to nanoseconds for elapsed time */
65562306a36Sopenharmony_ci	elapsed = (stats->alwayson_end - stats->alwayson_start) * 10000;
65662306a36Sopenharmony_ci	do_div(elapsed, 192);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	cycles = stats->cpcycles_end - stats->cpcycles_start;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	/* Calculate the clock frequency from the number of CP cycles */
66162306a36Sopenharmony_ci	if (elapsed) {
66262306a36Sopenharmony_ci		clock = cycles * 1000;
66362306a36Sopenharmony_ci		do_div(clock, elapsed);
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	submit->queue->ctx->elapsed_ns += elapsed;
66762306a36Sopenharmony_ci	submit->queue->ctx->cycles     += cycles;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	trace_msm_gpu_submit_retired(submit, elapsed, clock,
67062306a36Sopenharmony_ci		stats->alwayson_start, stats->alwayson_end);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	msm_submit_retire(submit);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&gpu->pdev->dev);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	spin_lock_irqsave(&ring->submit_lock, flags);
67762306a36Sopenharmony_ci	list_del(&submit->node);
67862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ring->submit_lock, flags);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	/* Update devfreq on transition from active->idle: */
68162306a36Sopenharmony_ci	mutex_lock(&gpu->active_lock);
68262306a36Sopenharmony_ci	gpu->active_submits--;
68362306a36Sopenharmony_ci	WARN_ON(gpu->active_submits < 0);
68462306a36Sopenharmony_ci	if (!gpu->active_submits) {
68562306a36Sopenharmony_ci		msm_devfreq_idle(gpu);
68662306a36Sopenharmony_ci		pm_runtime_put_autosuspend(&gpu->pdev->dev);
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	mutex_unlock(&gpu->active_lock);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	msm_gem_submit_put(submit);
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic void retire_submits(struct msm_gpu *gpu)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	int i;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/* Retire the commits starting with highest priority */
69962306a36Sopenharmony_ci	for (i = 0; i < gpu->nr_rings; i++) {
70062306a36Sopenharmony_ci		struct msm_ringbuffer *ring = gpu->rb[i];
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		while (true) {
70362306a36Sopenharmony_ci			struct msm_gem_submit *submit = NULL;
70462306a36Sopenharmony_ci			unsigned long flags;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci			spin_lock_irqsave(&ring->submit_lock, flags);
70762306a36Sopenharmony_ci			submit = list_first_entry_or_null(&ring->submits,
70862306a36Sopenharmony_ci					struct msm_gem_submit, node);
70962306a36Sopenharmony_ci			spin_unlock_irqrestore(&ring->submit_lock, flags);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci			/*
71262306a36Sopenharmony_ci			 * If no submit, we are done.  If submit->fence hasn't
71362306a36Sopenharmony_ci			 * been signalled, then later submits are not signalled
71462306a36Sopenharmony_ci			 * either, so we are also done.
71562306a36Sopenharmony_ci			 */
71662306a36Sopenharmony_ci			if (submit && dma_fence_is_signaled(submit->hw_fence)) {
71762306a36Sopenharmony_ci				retire_submit(gpu, ring, submit);
71862306a36Sopenharmony_ci			} else {
71962306a36Sopenharmony_ci				break;
72062306a36Sopenharmony_ci			}
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	wake_up_all(&gpu->retire_event);
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic void retire_worker(struct kthread_work *work)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	struct msm_gpu *gpu = container_of(work, struct msm_gpu, retire_work);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	retire_submits(gpu);
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci/* call from irq handler to schedule work to retire bo's */
73562306a36Sopenharmony_civoid msm_gpu_retire(struct msm_gpu *gpu)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	int i;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	for (i = 0; i < gpu->nr_rings; i++)
74062306a36Sopenharmony_ci		msm_update_fence(gpu->rb[i]->fctx, gpu->rb[i]->memptrs->fence);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	kthread_queue_work(gpu->worker, &gpu->retire_work);
74362306a36Sopenharmony_ci	update_sw_cntrs(gpu);
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci/* add bo's to gpu's ring, and kick gpu: */
74762306a36Sopenharmony_civoid msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	struct msm_ringbuffer *ring = submit->ring;
75062306a36Sopenharmony_ci	unsigned long flags;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&gpu->lock));
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	pm_runtime_get_sync(&gpu->pdev->dev);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	msm_gpu_hw_init(gpu);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	submit->seqno = submit->hw_fence->seqno;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	update_sw_cntrs(gpu);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/*
76362306a36Sopenharmony_ci	 * ring->submits holds a ref to the submit, to deal with the case
76462306a36Sopenharmony_ci	 * that a submit completes before msm_ioctl_gem_submit() returns.
76562306a36Sopenharmony_ci	 */
76662306a36Sopenharmony_ci	msm_gem_submit_get(submit);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	spin_lock_irqsave(&ring->submit_lock, flags);
76962306a36Sopenharmony_ci	list_add_tail(&submit->node, &ring->submits);
77062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ring->submit_lock, flags);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/* Update devfreq on transition from idle->active: */
77362306a36Sopenharmony_ci	mutex_lock(&gpu->active_lock);
77462306a36Sopenharmony_ci	if (!gpu->active_submits) {
77562306a36Sopenharmony_ci		pm_runtime_get(&gpu->pdev->dev);
77662306a36Sopenharmony_ci		msm_devfreq_active(gpu);
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci	gpu->active_submits++;
77962306a36Sopenharmony_ci	mutex_unlock(&gpu->active_lock);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	gpu->funcs->submit(gpu, submit);
78262306a36Sopenharmony_ci	gpu->cur_ctx_seqno = submit->queue->ctx->seqno;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	pm_runtime_put(&gpu->pdev->dev);
78562306a36Sopenharmony_ci	hangcheck_timer_reset(gpu);
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci/*
78962306a36Sopenharmony_ci * Init/Cleanup:
79062306a36Sopenharmony_ci */
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic irqreturn_t irq_handler(int irq, void *data)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	struct msm_gpu *gpu = data;
79562306a36Sopenharmony_ci	return gpu->funcs->irq(gpu);
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic int get_clocks(struct platform_device *pdev, struct msm_gpu *gpu)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	int ret = devm_clk_bulk_get_all(&pdev->dev, &gpu->grp_clks);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	if (ret < 1) {
80362306a36Sopenharmony_ci		gpu->nr_clocks = 0;
80462306a36Sopenharmony_ci		return ret;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	gpu->nr_clocks = ret;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	gpu->core_clk = msm_clk_bulk_get_clock(gpu->grp_clks,
81062306a36Sopenharmony_ci		gpu->nr_clocks, "core");
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	gpu->rbbmtimer_clk = msm_clk_bulk_get_clock(gpu->grp_clks,
81362306a36Sopenharmony_ci		gpu->nr_clocks, "rbbmtimer");
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	return 0;
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci/* Return a new address space for a msm_drm_private instance */
81962306a36Sopenharmony_cistruct msm_gem_address_space *
82062306a36Sopenharmony_cimsm_gpu_create_private_address_space(struct msm_gpu *gpu, struct task_struct *task)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct msm_gem_address_space *aspace = NULL;
82362306a36Sopenharmony_ci	if (!gpu)
82462306a36Sopenharmony_ci		return NULL;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/*
82762306a36Sopenharmony_ci	 * If the target doesn't support private address spaces then return
82862306a36Sopenharmony_ci	 * the global one
82962306a36Sopenharmony_ci	 */
83062306a36Sopenharmony_ci	if (gpu->funcs->create_private_address_space) {
83162306a36Sopenharmony_ci		aspace = gpu->funcs->create_private_address_space(gpu);
83262306a36Sopenharmony_ci		if (!IS_ERR(aspace))
83362306a36Sopenharmony_ci			aspace->pid = get_pid(task_pid(task));
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(aspace))
83762306a36Sopenharmony_ci		aspace = msm_gem_address_space_get(gpu->aspace);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return aspace;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ciint msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
84362306a36Sopenharmony_ci		struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs,
84462306a36Sopenharmony_ci		const char *name, struct msm_gpu_config *config)
84562306a36Sopenharmony_ci{
84662306a36Sopenharmony_ci	struct msm_drm_private *priv = drm->dev_private;
84762306a36Sopenharmony_ci	int i, ret, nr_rings = config->nr_rings;
84862306a36Sopenharmony_ci	void *memptrs;
84962306a36Sopenharmony_ci	uint64_t memptrs_iova;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs)))
85262306a36Sopenharmony_ci		gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	gpu->dev = drm;
85562306a36Sopenharmony_ci	gpu->funcs = funcs;
85662306a36Sopenharmony_ci	gpu->name = name;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	gpu->worker = kthread_create_worker(0, "gpu-worker");
85962306a36Sopenharmony_ci	if (IS_ERR(gpu->worker)) {
86062306a36Sopenharmony_ci		ret = PTR_ERR(gpu->worker);
86162306a36Sopenharmony_ci		gpu->worker = NULL;
86262306a36Sopenharmony_ci		goto fail;
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	sched_set_fifo_low(gpu->worker->task);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	mutex_init(&gpu->active_lock);
86862306a36Sopenharmony_ci	mutex_init(&gpu->lock);
86962306a36Sopenharmony_ci	init_waitqueue_head(&gpu->retire_event);
87062306a36Sopenharmony_ci	kthread_init_work(&gpu->retire_work, retire_worker);
87162306a36Sopenharmony_ci	kthread_init_work(&gpu->recover_work, recover_worker);
87262306a36Sopenharmony_ci	kthread_init_work(&gpu->fault_work, fault_worker);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	priv->hangcheck_period = DRM_MSM_HANGCHECK_DEFAULT_PERIOD;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	/*
87762306a36Sopenharmony_ci	 * If progress detection is supported, halve the hangcheck timer
87862306a36Sopenharmony_ci	 * duration, as it takes two iterations of the hangcheck handler
87962306a36Sopenharmony_ci	 * to detect a hang.
88062306a36Sopenharmony_ci	 */
88162306a36Sopenharmony_ci	if (funcs->progress)
88262306a36Sopenharmony_ci		priv->hangcheck_period /= 2;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	timer_setup(&gpu->hangcheck_timer, hangcheck_handler, 0);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	spin_lock_init(&gpu->perf_lock);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	/* Map registers: */
89062306a36Sopenharmony_ci	gpu->mmio = msm_ioremap(pdev, config->ioname);
89162306a36Sopenharmony_ci	if (IS_ERR(gpu->mmio)) {
89262306a36Sopenharmony_ci		ret = PTR_ERR(gpu->mmio);
89362306a36Sopenharmony_ci		goto fail;
89462306a36Sopenharmony_ci	}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	/* Get Interrupt: */
89762306a36Sopenharmony_ci	gpu->irq = platform_get_irq(pdev, 0);
89862306a36Sopenharmony_ci	if (gpu->irq < 0) {
89962306a36Sopenharmony_ci		ret = gpu->irq;
90062306a36Sopenharmony_ci		goto fail;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, gpu->irq, irq_handler,
90462306a36Sopenharmony_ci			IRQF_TRIGGER_HIGH, "gpu-irq", gpu);
90562306a36Sopenharmony_ci	if (ret) {
90662306a36Sopenharmony_ci		DRM_DEV_ERROR(drm->dev, "failed to request IRQ%u: %d\n", gpu->irq, ret);
90762306a36Sopenharmony_ci		goto fail;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	ret = get_clocks(pdev, gpu);
91162306a36Sopenharmony_ci	if (ret)
91262306a36Sopenharmony_ci		goto fail;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	gpu->ebi1_clk = msm_clk_get(pdev, "bus");
91562306a36Sopenharmony_ci	DBG("ebi1_clk: %p", gpu->ebi1_clk);
91662306a36Sopenharmony_ci	if (IS_ERR(gpu->ebi1_clk))
91762306a36Sopenharmony_ci		gpu->ebi1_clk = NULL;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	/* Acquire regulators: */
92062306a36Sopenharmony_ci	gpu->gpu_reg = devm_regulator_get(&pdev->dev, "vdd");
92162306a36Sopenharmony_ci	DBG("gpu_reg: %p", gpu->gpu_reg);
92262306a36Sopenharmony_ci	if (IS_ERR(gpu->gpu_reg))
92362306a36Sopenharmony_ci		gpu->gpu_reg = NULL;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	gpu->gpu_cx = devm_regulator_get(&pdev->dev, "vddcx");
92662306a36Sopenharmony_ci	DBG("gpu_cx: %p", gpu->gpu_cx);
92762306a36Sopenharmony_ci	if (IS_ERR(gpu->gpu_cx))
92862306a36Sopenharmony_ci		gpu->gpu_cx = NULL;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	gpu->pdev = pdev;
93162306a36Sopenharmony_ci	platform_set_drvdata(pdev, &gpu->adreno_smmu);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	msm_devfreq_init(gpu);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	gpu->aspace = gpu->funcs->create_address_space(gpu, pdev);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	if (gpu->aspace == NULL)
93962306a36Sopenharmony_ci		DRM_DEV_INFO(drm->dev, "%s: no IOMMU, fallback to VRAM carveout!\n", name);
94062306a36Sopenharmony_ci	else if (IS_ERR(gpu->aspace)) {
94162306a36Sopenharmony_ci		ret = PTR_ERR(gpu->aspace);
94262306a36Sopenharmony_ci		goto fail;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	memptrs = msm_gem_kernel_new(drm,
94662306a36Sopenharmony_ci		sizeof(struct msm_rbmemptrs) * nr_rings,
94762306a36Sopenharmony_ci		check_apriv(gpu, MSM_BO_WC), gpu->aspace, &gpu->memptrs_bo,
94862306a36Sopenharmony_ci		&memptrs_iova);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (IS_ERR(memptrs)) {
95162306a36Sopenharmony_ci		ret = PTR_ERR(memptrs);
95262306a36Sopenharmony_ci		DRM_DEV_ERROR(drm->dev, "could not allocate memptrs: %d\n", ret);
95362306a36Sopenharmony_ci		goto fail;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	msm_gem_object_set_name(gpu->memptrs_bo, "memptrs");
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (nr_rings > ARRAY_SIZE(gpu->rb)) {
95962306a36Sopenharmony_ci		DRM_DEV_INFO_ONCE(drm->dev, "Only creating %zu ringbuffers\n",
96062306a36Sopenharmony_ci			ARRAY_SIZE(gpu->rb));
96162306a36Sopenharmony_ci		nr_rings = ARRAY_SIZE(gpu->rb);
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	/* Create ringbuffer(s): */
96562306a36Sopenharmony_ci	for (i = 0; i < nr_rings; i++) {
96662306a36Sopenharmony_ci		gpu->rb[i] = msm_ringbuffer_new(gpu, i, memptrs, memptrs_iova);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci		if (IS_ERR(gpu->rb[i])) {
96962306a36Sopenharmony_ci			ret = PTR_ERR(gpu->rb[i]);
97062306a36Sopenharmony_ci			DRM_DEV_ERROR(drm->dev,
97162306a36Sopenharmony_ci				"could not create ringbuffer %d: %d\n", i, ret);
97262306a36Sopenharmony_ci			goto fail;
97362306a36Sopenharmony_ci		}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		memptrs += sizeof(struct msm_rbmemptrs);
97662306a36Sopenharmony_ci		memptrs_iova += sizeof(struct msm_rbmemptrs);
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	gpu->nr_rings = nr_rings;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	refcount_set(&gpu->sysprof_active, 1);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	return 0;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_cifail:
98662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(gpu->rb); i++)  {
98762306a36Sopenharmony_ci		msm_ringbuffer_destroy(gpu->rb[i]);
98862306a36Sopenharmony_ci		gpu->rb[i] = NULL;
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	msm_gem_kernel_put(gpu->memptrs_bo, gpu->aspace);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	platform_set_drvdata(pdev, NULL);
99462306a36Sopenharmony_ci	return ret;
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_civoid msm_gpu_cleanup(struct msm_gpu *gpu)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	int i;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	DBG("%s", gpu->name);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) {
100462306a36Sopenharmony_ci		msm_ringbuffer_destroy(gpu->rb[i]);
100562306a36Sopenharmony_ci		gpu->rb[i] = NULL;
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	msm_gem_kernel_put(gpu->memptrs_bo, gpu->aspace);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(gpu->aspace)) {
101162306a36Sopenharmony_ci		gpu->aspace->mmu->funcs->detach(gpu->aspace->mmu);
101262306a36Sopenharmony_ci		msm_gem_address_space_put(gpu->aspace);
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (gpu->worker) {
101662306a36Sopenharmony_ci		kthread_destroy_worker(gpu->worker);
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	msm_devfreq_cleanup(gpu);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	platform_set_drvdata(gpu->pdev, NULL);
102262306a36Sopenharmony_ci}
1023