// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/*
 *
 * (C) COPYRIGHT 2012-2023 ARM Limited. All rights reserved.
 *
 * This program is free software and is provided to you under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation, and any use by you of this program is subject to the terms
 * of such GNU license.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you can access it online at
 * http://www.gnu.org/licenses/gpl-2.0.html.
 *
 */

#include <mali_kbase.h>
#include <device/mali_kbase_device.h>
#include "mali_kbase_debug_job_fault.h"

#if IS_ENABLED(CONFIG_DEBUG_FS)

/*GPU_CONTROL_REG(r)*/
static int gpu_control_reg_snapshot[] = {
	GPU_ID,
	SHADER_READY_LO,
	SHADER_READY_HI,
	TILER_READY_LO,
	TILER_READY_HI,
	L2_READY_LO,
	L2_READY_HI
};

/* JOB_CONTROL_REG(r) */
static int job_control_reg_snapshot[] = {
	JOB_IRQ_MASK,
	JOB_IRQ_STATUS
};

/* JOB_SLOT_REG(n,r) */
static int job_slot_reg_snapshot[] = {
	JS_HEAD_LO,
	JS_HEAD_HI,
	JS_TAIL_LO,
	JS_TAIL_HI,
	JS_AFFINITY_LO,
	JS_AFFINITY_HI,
	JS_CONFIG,
	JS_STATUS,
	JS_HEAD_NEXT_LO,
	JS_HEAD_NEXT_HI,
	JS_AFFINITY_NEXT_LO,
	JS_AFFINITY_NEXT_HI,
	JS_CONFIG_NEXT
};

/*MMU_CONTROL_REG(r)*/
static int mmu_reg_snapshot[] = {
	MMU_IRQ_MASK,
	MMU_IRQ_STATUS
};

/* MMU_AS_REG(n,r) */
static int as_reg_snapshot[] = {
	AS_TRANSTAB_LO,
	AS_TRANSTAB_HI,
	AS_TRANSCFG_LO,
	AS_TRANSCFG_HI,
	AS_MEMATTR_LO,
	AS_MEMATTR_HI,
	AS_FAULTSTATUS,
	AS_FAULTADDRESS_LO,
	AS_FAULTADDRESS_HI,
	AS_STATUS
};

bool kbase_debug_job_fault_reg_snapshot_init(struct kbase_context *kctx,
		int reg_range)
{
	int i, j;
	int offset = 0;
	int slot_number;
	int as_number;

	if (kctx->reg_dump == NULL)
		return false;

	slot_number = kctx->kbdev->gpu_props.num_job_slots;
	as_number = kctx->kbdev->gpu_props.num_address_spaces;

	/* get the GPU control registers*/
	for (i = 0; i < sizeof(gpu_control_reg_snapshot)/4; i++) {
		kctx->reg_dump[offset] =
				GPU_CONTROL_REG(gpu_control_reg_snapshot[i]);
		offset += 2;
	}

	/* get the Job control registers*/
	for (i = 0; i < sizeof(job_control_reg_snapshot)/4; i++) {
		kctx->reg_dump[offset] =
				JOB_CONTROL_REG(job_control_reg_snapshot[i]);
		offset += 2;
	}

	/* get the Job Slot registers*/
	for (j = 0; j < slot_number; j++)	{
		for (i = 0; i < sizeof(job_slot_reg_snapshot)/4; i++) {
			kctx->reg_dump[offset] =
			JOB_SLOT_REG(j, job_slot_reg_snapshot[i]);
			offset += 2;
		}
	}

	/* get the MMU registers*/
	for (i = 0; i < sizeof(mmu_reg_snapshot)/4; i++) {
		kctx->reg_dump[offset] = MMU_CONTROL_REG(mmu_reg_snapshot[i]);
		offset += 2;
	}

	/* get the Address space registers*/
	for (j = 0; j < as_number; j++) {
		for (i = 0; i < sizeof(as_reg_snapshot)/4; i++) {
			kctx->reg_dump[offset] = MMU_STAGE1_REG(MMU_AS_REG(j, as_reg_snapshot[i]));
			offset += 2;
		}
	}

	WARN_ON(offset >= (reg_range*2/4));

	/* set the termination flag*/
	kctx->reg_dump[offset] = REGISTER_DUMP_TERMINATION_FLAG;
	kctx->reg_dump[offset + 1] = REGISTER_DUMP_TERMINATION_FLAG;

	dev_dbg(kctx->kbdev->dev, "kbase_job_fault_reg_snapshot_init:%d\n",
			offset);

	return true;
}

bool kbase_job_fault_get_reg_snapshot(struct kbase_context *kctx)
{
	int offset = 0;

	if (kctx->reg_dump == NULL)
		return false;

	while (kctx->reg_dump[offset] != REGISTER_DUMP_TERMINATION_FLAG) {
		kctx->reg_dump[offset+1] =
				kbase_reg_read(kctx->kbdev,
						kctx->reg_dump[offset]);
		offset += 2;
	}
	return true;
}


#endif
