/* drivers/soc/qcom/smp2p_spinlock_test.c
 *
 * Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * 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.
 */
#include <linux/debugfs.h>
#include <linux/ctype.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/remote_spinlock.h>
#include <soc/qcom/smem.h>
#include "smem_private.h"
#include "smp2p_private.h"
#include "smp2p_test_common.h"

#define RS_END_THIEF_PID_BIT 20
#define RS_END_THIEF_MASK 0x00f00000

/* Spinlock commands used for testing Apps<->RPM spinlocks. */
enum RPM_SPINLOCK_CMDS {
	RPM_CMD_INVALID,
	RPM_CMD_START,
	RPM_CMD_LOCKED,
	RPM_CMD_UNLOCKED,
	RPM_CMD_END,
};

/* Shared structure for testing Apps<->RPM spinlocks. */
struct rpm_spinlock_test {
	uint32_t apps_cmd;
	uint32_t apps_lock_count;
	uint32_t rpm_cmd;
	uint32_t rpm_lock_count;
};

static uint32_t ut_remote_spinlock_run_time = 1;

/**
 * smp2p_ut_remote_spinlock_core - Verify remote spinlock.
 *
 * @s:           Pointer to output file
 * @remote_pid:  Remote processor to test
 * @use_trylock: Use trylock to prevent an Apps deadlock if the
 *               remote spinlock fails.
 */
static void smp2p_ut_remote_spinlock_core(struct seq_file *s, int remote_pid,
		bool use_trylock)
{
	int failed = 0;
	unsigned lock_count = 0;
	struct msm_smp2p_out *handle = NULL;
	int ret;
	uint32_t test_request;
	uint32_t test_response;
	struct mock_cb_data cb_out;
	struct mock_cb_data cb_in;
	unsigned long flags;
	unsigned n;
	bool have_lock;
	bool timeout;
	int failed_tmp;
	int spinlock_owner;
	remote_spinlock_t *smem_spinlock;
	unsigned long end;

	seq_printf(s, "Running %s for '%s' remote pid %d\n",
		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);

	cb_out.initialized = false;
	cb_in.initialized = false;
	mock_cb_data_init(&cb_out);
	mock_cb_data_init(&cb_in);
	do {
		smem_spinlock = smem_get_remote_spinlock();
		UT_ASSERT_PTR(smem_spinlock, !=, NULL);

		/* Open output entry */
		ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
			&cb_out.nb, &handle);
		UT_ASSERT_INT(ret, ==, 0);
		UT_ASSERT_INT(
			(int)wait_for_completion_timeout(
					&cb_out.cb_completion, HZ * 2),
			>, 0);
		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
		UT_ASSERT_INT(cb_out.event_open, ==, 1);

		/* Open inbound entry */
		ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
				&cb_in.nb);
		UT_ASSERT_INT(ret, ==, 0);
		UT_ASSERT_INT(
			(int)wait_for_completion_timeout(
					&cb_in.cb_completion, HZ * 2),
			>, 0);
		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
		UT_ASSERT_INT(cb_in.event_open, ==, 1);

		/* Send start */
		mock_cb_data_reset(&cb_in);
		mock_cb_data_reset(&cb_out);
		test_request = 0x0;
		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
		SMP2P_SET_RMT_DATA(test_request, 0x0);
		ret = msm_smp2p_out_write(handle, test_request);
		UT_ASSERT_INT(ret, ==, 0);

		UT_ASSERT_INT(
			(int)wait_for_completion_timeout(
					&cb_in.cb_completion, HZ * 2),
			>, 0);
		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
		ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
				&test_response);
		UT_ASSERT_INT(ret, ==, 0);

		test_response = SMP2P_GET_RMT_CMD(test_response);
		if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
				test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
			/* invalid response from remote - abort test */
			test_request = 0x0;
			SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
			SMP2P_SET_RMT_DATA(test_request, 0x0);
			ret = msm_smp2p_out_write(handle, test_request);
			UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
					test_response);
		}

		/* Run spinlock test */
		if (use_trylock)
			seq_puts(s, "\tUsing remote_spin_trylock\n");
		else
			seq_puts(s, "\tUsing remote_spin_lock\n");

		flags = 0;
		have_lock = false;
		timeout = false;
		spinlock_owner = 0;
		test_request = 0x0;
		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
		end = jiffies + (ut_remote_spinlock_run_time * HZ);
		if (ut_remote_spinlock_run_time < 300) {
				seq_printf(s, "\tRunning test for %u seconds; ",
					ut_remote_spinlock_run_time);
				seq_puts(s,
					"on physical hardware please run >= 300 seconds by doing 'echo 300 >  ut_remote_spinlock_time'\n");
		}
		while (time_is_after_jiffies(end)) {
			/* try to acquire spinlock */
			if (use_trylock) {
				unsigned long j_start = jiffies;
				while (!remote_spin_trylock_irqsave(
						smem_spinlock, flags)) {
					if (jiffies_to_msecs(jiffies - j_start)
							> 1000) {
						seq_puts(s,
							"\tFail: Timeout trying to get the lock\n");
						timeout = true;
						break;
					}
				}
				if (timeout)
					break;
			} else {
				remote_spin_lock_irqsave(smem_spinlock, flags);
			}
			have_lock = true;
			++lock_count;

			/* tell the remote side that we have the lock */
			SMP2P_SET_RMT_DATA(test_request, lock_count);
			SMP2P_SET_RMT_CMD(test_request,
					SMP2P_LB_CMD_RSPIN_LOCKED);
			ret = msm_smp2p_out_write(handle, test_request);
			UT_ASSERT_INT(ret, ==, 0);

			/* verify the other side doesn't say it has the lock */
			for (n = 0; n < 1000; ++n) {
				spinlock_owner =
					remote_spin_owner(smem_spinlock);
				if (spinlock_owner != SMEM_APPS) {
					/* lock stolen by remote side */
					seq_puts(s, "\tFail: Remote side: ");
					seq_printf(s, "%d stole lock pid: %d\n",
						remote_pid, spinlock_owner);
					failed = true;
					break;
				}
				spinlock_owner = 0;

				ret = msm_smp2p_in_read(remote_pid,
					SMP2P_RLPB_ENTRY_NAME, &test_response);
				UT_ASSERT_INT(ret, ==, 0);
				test_response =
					SMP2P_GET_RMT_CMD(test_response);
				UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_UNLOCKED, ==,
					test_response);
			}
			if (failed)
				break;

			/* tell remote side we are unlocked and release lock */
			SMP2P_SET_RMT_CMD(test_request,
					SMP2P_LB_CMD_RSPIN_UNLOCKED);
			(void)msm_smp2p_out_write(handle, test_request);
			have_lock = false;
			remote_spin_unlock_irqrestore(smem_spinlock, flags);
		}
		if (have_lock)
			remote_spin_unlock_irqrestore(smem_spinlock, flags);

		/* End test */
		mock_cb_data_reset(&cb_in);
		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
		SMP2P_SET_RMT_DATA(test_request, lock_count |
				(spinlock_owner << RS_END_THIEF_PID_BIT));
		(void)msm_smp2p_out_write(handle, test_request);

		failed_tmp = failed;
		failed = false;
		do {
			UT_ASSERT_INT(
				(int)wait_for_completion_timeout(
					&cb_in.cb_completion, HZ * 2),
				>, 0);
			reinit_completion(&cb_in.cb_completion);
			ret = msm_smp2p_in_read(remote_pid,
					SMP2P_RLPB_ENTRY_NAME, &test_response);
			UT_ASSERT_INT(ret, ==, 0);
		} while (!failed &&
			SMP2P_GET_RMT_CMD(test_response) !=
			SMP2P_LB_CMD_RSPIN_END);
		if (failed)
			break;
		failed = failed_tmp;

		test_response = SMP2P_GET_RMT_DATA(test_response);
		seq_puts(s, "\tLocked spinlock ");
		seq_printf(s, "local %u times; remote %u times",
			lock_count,
			test_response & ((1 << RS_END_THIEF_PID_BIT) - 1)
			);
		if (test_response & RS_END_THIEF_MASK) {
			seq_puts(s, "Remote side reporting lock stolen by ");
			seq_printf(s, "pid %d.\n",
				SMP2P_GET_BITS(test_response,
					RS_END_THIEF_MASK,
					RS_END_THIEF_PID_BIT));
			failed = 1;
		}
		seq_puts(s, "\n");

		/* Cleanup */
		ret = msm_smp2p_out_close(&handle);
		UT_ASSERT_INT(ret, ==, 0);
		UT_ASSERT_PTR(handle, ==, NULL);
		ret = msm_smp2p_in_unregister(remote_pid,
				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
		UT_ASSERT_INT(ret, ==, 0);

		if (!failed && !timeout)
			seq_puts(s, "\tOK\n");
	} while (0);

	if (failed) {
		if (handle) {
			/* send end command */
			test_request = 0;
			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
			SMP2P_SET_RMT_DATA(test_request, lock_count);
			(void)msm_smp2p_out_write(handle, test_request);
			(void)msm_smp2p_out_close(&handle);
		}
		(void)msm_smp2p_in_unregister(remote_pid,
				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);

		pr_err("%s: Failed\n", __func__);
		seq_puts(s, "\tFailed\n");
	}
}

/**
 * smp2p_ut_remote_spinlock_pid - Verify remote spinlock for a processor.
 *
 * @s:           Pointer to output file
 * @pid:         Processor to test
 * @use_trylock: Use trylock to prevent an Apps deadlock if the
 *               remote spinlock fails.
 */
static void smp2p_ut_remote_spinlock_pid(struct seq_file *s, int pid,
		bool use_trylock)
{
	struct smp2p_interrupt_config *int_cfg;

	int_cfg = smp2p_get_interrupt_config();
	if (!int_cfg) {
		seq_puts(s, "Remote processor config unavailable\n");
		return;
	}

	if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
		return;

	msm_smp2p_deinit_rmt_lpb_proc(pid);
	smp2p_ut_remote_spinlock_core(s, pid, use_trylock);
	msm_smp2p_init_rmt_lpb_proc(pid);
}

/**
 * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
 *
 * @s:   pointer to output file
 */
static void smp2p_ut_remote_spinlock(struct seq_file *s)
{
	int pid;

	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
		smp2p_ut_remote_spinlock_pid(s, pid, false);
}

/**
 * smp2p_ut_remote_spin_trylock - Verify remote trylock for all processors.
 *
 * @s:   Pointer to output file
 */
static void smp2p_ut_remote_spin_trylock(struct seq_file *s)
{
	int pid;

	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
		smp2p_ut_remote_spinlock_pid(s, pid, true);
}

/**
 * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
 *
 * @s:   pointer to output file
 *
 * This test verifies inbound and outbound functionality for all
 * configured remote processor.
 */
static void smp2p_ut_remote_spinlock_modem(struct seq_file *s)
{
	smp2p_ut_remote_spinlock_pid(s, SMP2P_MODEM_PROC, false);
}

static void smp2p_ut_remote_spinlock_adsp(struct seq_file *s)
{
	smp2p_ut_remote_spinlock_pid(s, SMP2P_AUDIO_PROC, false);
}

static void smp2p_ut_remote_spinlock_dsps(struct seq_file *s)
{
	smp2p_ut_remote_spinlock_pid(s, SMP2P_SENSOR_PROC, false);
}

static void smp2p_ut_remote_spinlock_wcnss(struct seq_file *s)
{
	smp2p_ut_remote_spinlock_pid(s, SMP2P_WIRELESS_PROC, false);
}

static void smp2p_ut_remote_spinlock_tz(struct seq_file *s)
{
	smp2p_ut_remote_spinlock_pid(s, SMP2P_TZ_PROC, false);
}

/**
 * smp2p_ut_remote_spinlock_rpm - Verify remote spinlock.
 *
 * @s:   pointer to output file
 * @remote_pid:  Remote processor to test
 */
static void smp2p_ut_remote_spinlock_rpm(struct seq_file *s)
{
	int failed = 0;
	unsigned long flags;
	unsigned n;
	unsigned test_num;
	struct rpm_spinlock_test *data_ptr;
	remote_spinlock_t *smem_spinlock;
	bool have_lock;

	seq_printf(s, "Running %s for Apps<->RPM Test\n",
		   __func__);
	do {
		smem_spinlock = smem_get_remote_spinlock();
		UT_ASSERT_PTR(smem_spinlock, !=, NULL);

		data_ptr = smem_alloc(SMEM_ID_VENDOR0,
				sizeof(struct rpm_spinlock_test), 0,
				SMEM_ANY_HOST_FLAG);
		UT_ASSERT_PTR(0, !=, data_ptr);

		/* Send start */
		writel_relaxed(0, &data_ptr->apps_lock_count);
		writel_relaxed(RPM_CMD_START, &data_ptr->apps_cmd);

		seq_puts(s, "\tWaiting for RPM to start test\n");
		for (n = 0; n < 1000; ++n) {
			if (readl_relaxed(&data_ptr->rpm_cmd) !=
					RPM_CMD_INVALID)
				break;
			usleep_range(1000, 1200);
		}
		if (readl_relaxed(&data_ptr->rpm_cmd) == RPM_CMD_INVALID) {
			/* timeout waiting for RPM */
			writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
			UT_ASSERT_INT(RPM_CMD_LOCKED, !=, RPM_CMD_INVALID);
		}

		/* Run spinlock test */
		flags = 0;
		have_lock = false;
		for (test_num = 0; !failed && test_num < 10000; ++test_num) {
			/* acquire spinlock */
			remote_spin_lock_irqsave(smem_spinlock, flags);
			have_lock = true;
			data_ptr->apps_lock_count++;
			writel_relaxed(data_ptr->apps_lock_count,
				&data_ptr->apps_lock_count);
			writel_relaxed(RPM_CMD_LOCKED, &data_ptr->apps_cmd);
			/*
			 * Ensure that the remote side sees our lock has
			 * been acquired before we start polling their status.
			 */
			wmb();

			/* verify the other side doesn't say it has the lock */
			for (n = 0; n < 1000; ++n) {
				UT_ASSERT_HEX(RPM_CMD_UNLOCKED, ==,
					readl_relaxed(&data_ptr->rpm_cmd));
			}
			if (failed)
				break;

			/* release spinlock */
			have_lock = false;
			writel_relaxed(RPM_CMD_UNLOCKED, &data_ptr->apps_cmd);
			/*
			 * Ensure that our status-update write was committed
			 * before we unlock the spinlock.
			 */
			wmb();
			remote_spin_unlock_irqrestore(smem_spinlock, flags);
		}
		if (have_lock)
			remote_spin_unlock_irqrestore(smem_spinlock, flags);

		/* End test */
		writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
		seq_printf(s, "\tLocked spinlock local %u remote %u\n",
				readl_relaxed(&data_ptr->apps_lock_count),
				readl_relaxed(&data_ptr->rpm_lock_count));

		if (!failed)
			seq_puts(s, "\tOK\n");
	} while (0);

	if (failed) {
		pr_err("%s: Failed\n", __func__);
		seq_puts(s, "\tFailed\n");
	}
}

struct rmt_spinlock_work_item {
	struct work_struct work;
	struct completion try_lock;
	struct completion locked;
	bool has_locked;
};

static void ut_remote_spinlock_ssr_worker(struct work_struct *work)
{
	remote_spinlock_t *smem_spinlock;
	unsigned long flags;
	struct rmt_spinlock_work_item *work_item =
		container_of(work, struct rmt_spinlock_work_item, work);

	work_item->has_locked = false;
	complete(&work_item->try_lock);
	smem_spinlock = smem_get_remote_spinlock();
	if (!smem_spinlock) {
		pr_err("%s Failed\n", __func__);
		return;
	}

	remote_spin_lock_irqsave(smem_spinlock, flags);
	remote_spin_unlock_irqrestore(smem_spinlock, flags);
	work_item->has_locked = true;
	complete(&work_item->locked);
}

/**
 * smp2p_ut_remote_spinlock_ssr - Verify remote spinlock.
 *
 * @s:   pointer to output file
 */
static void smp2p_ut_remote_spinlock_ssr(struct seq_file *s)
{
	int failed = 0;
	unsigned long flags;
	remote_spinlock_t *smem_spinlock;
	int spinlock_owner = 0;

	struct workqueue_struct *ws = NULL;
	struct rmt_spinlock_work_item work_item = { .has_locked = false };

	seq_printf(s, " Running %s Test\n",
		   __func__);
	do {
		smem_spinlock = smem_get_remote_spinlock();
		UT_ASSERT_PTR(smem_spinlock, !=, NULL);

		ws = create_singlethread_workqueue("ut_remote_spinlock_ssr");
		UT_ASSERT_PTR(ws, !=, NULL);
		INIT_WORK(&work_item.work, ut_remote_spinlock_ssr_worker);
		init_completion(&work_item.try_lock);
		init_completion(&work_item.locked);

		remote_spin_lock_irqsave(smem_spinlock, flags);
		/* Unlock local spin lock and hold HW spinlock */
		spin_unlock_irqrestore(&((smem_spinlock)->local), flags);

		queue_work(ws, &work_item.work);
		UT_ASSERT_INT(
			(int)wait_for_completion_timeout(
					&work_item.try_lock, HZ * 2), >, 0);
		UT_ASSERT_INT((int)work_item.has_locked, ==, 0);
		spinlock_owner = remote_spin_owner(smem_spinlock);
		UT_ASSERT_INT(spinlock_owner, ==, SMEM_APPS);
		remote_spin_release_all(SMEM_APPS);

		UT_ASSERT_INT(
			(int)wait_for_completion_timeout(
					&work_item.locked, HZ * 2), >, 0);

		if (!failed)
			seq_puts(s, "\tOK\n");
	} while (0);

	if (failed) {
		pr_err("%s: Failed\n", __func__);
		seq_puts(s, "\tFailed\n");
	}
}

/**
 * smp2p_ut_remote_spinlock_track_core - Verify remote spinlock.
 *
 * @s:           Pointer to output file
 * @remote_pid:  Remote processor to test
 *
 * This test has the remote subsystem grab the lock, and then has the local
 * subsystem attempt to grab the lock using the trylock() API. It then verifies
 * that the ID in the hw_spinlocks array matches the owner of the lock.
 */
static void smp2p_ut_remote_spinlock_track_core(struct seq_file *s,
		int remote_pid)
{
	int failed = 0;
	struct msm_smp2p_out *handle = NULL;
	int ret;
	uint32_t test_request;
	uint32_t test_response;
	struct mock_cb_data cb_out;
	struct mock_cb_data cb_in;
	unsigned long flags;
	int stored_value;
	remote_spinlock_t *smem_spinlock;

	seq_printf(s, "Running %s for '%s' remote pid %d\n",
		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);

	cb_out.initialized = false;
	cb_in.initialized = false;
	mock_cb_data_init(&cb_out);
	mock_cb_data_init(&cb_in);
	do {
		smem_spinlock = smem_get_remote_spinlock();
		UT_ASSERT_PTR(smem_spinlock, !=, NULL);

		/* Open output entry */
		ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
			&cb_out.nb, &handle);
		UT_ASSERT_INT(ret, ==, 0);
		UT_ASSERT_INT(
			(int)wait_for_completion_timeout(
					&cb_out.cb_completion, HZ * 2),
			>, 0);
		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
		UT_ASSERT_INT(cb_out.event_open, ==, 1);

		/* Open inbound entry */
		ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
				&cb_in.nb);
		UT_ASSERT_INT(ret, ==, 0);
		UT_ASSERT_INT(
			(int)wait_for_completion_timeout(
					&cb_in.cb_completion, HZ * 2),
			>, 0);
		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
		UT_ASSERT_INT(cb_in.event_open, ==, 1);

		/* Send start */
		mock_cb_data_reset(&cb_in);
		mock_cb_data_reset(&cb_out);
		test_request = 0x0;
		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
		SMP2P_SET_RMT_DATA(test_request, 0x0);
		ret = msm_smp2p_out_write(handle, test_request);
		UT_ASSERT_INT(ret, ==, 0);

		UT_ASSERT_INT(
			(int)wait_for_completion_timeout(
					&cb_in.cb_completion, HZ * 2),
			>, 0);
		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
		ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
				&test_response);
		UT_ASSERT_INT(ret, ==, 0);

		test_response = SMP2P_GET_RMT_CMD(test_response);
		if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
				test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
			/* invalid response from remote - abort test */
			test_request = 0x0;
			SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
			SMP2P_SET_RMT_DATA(test_request, 0x0);
			ret = msm_smp2p_out_write(handle, test_request);
			UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
					test_response);
		}

		/* Run spinlock test */
		flags = 0;
		test_request = 0x0;
		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);

		/* try to acquire spinlock */
		remote_spin_trylock_irqsave(smem_spinlock, flags);
		/*
		 * Need to check against the locking token (PID + 1)
		 * because the remote_spin_owner() API only returns the
		 * PID.
		 */
		stored_value = remote_spin_get_hw_spinlocks_element(
				smem_spinlock);
		UT_ASSERT_INT(stored_value, ==,
			remote_spin_owner(smem_spinlock) + 1);
		UT_ASSERT_INT(stored_value, ==, remote_pid + 1);

		/* End test */
		test_request = 0x0;
		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
		SMP2P_SET_RMT_DATA(test_request, 0x0);
		(void)msm_smp2p_out_write(handle, test_request);

		/* Cleanup */
		ret = msm_smp2p_out_close(&handle);
		UT_ASSERT_INT(ret, ==, 0);
		UT_ASSERT_PTR(handle, ==, NULL);
		ret = msm_smp2p_in_unregister(remote_pid,
				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
		UT_ASSERT_INT(ret, ==, 0);

		if (!failed)
			seq_puts(s, "\tOK\n");
	} while (0);

	if (failed) {
		if (handle) {
			/* send end command */
			test_request = 0x0;
			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
			SMP2P_SET_RMT_DATA(test_request, 0x0);
			(void)msm_smp2p_out_write(handle, test_request);
			(void)msm_smp2p_out_close(&handle);
		}
		(void)msm_smp2p_in_unregister(remote_pid,
				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);

		pr_err("%s: Failed\n", __func__);
		seq_puts(s, "\tFailed\n");
	}
}

/**
 * smp2p_ut_remote_spinlock_track - Verify PID tracking for modem.
 *
 * @s:	Pointer to output file
 * @pid:		The processor to test
 */
static void smp2p_ut_remote_spinlock_track(struct seq_file *s, int pid)
{
	struct smp2p_interrupt_config *int_cfg;

	int_cfg = smp2p_get_interrupt_config();
	if (!int_cfg) {
		seq_puts(s, "Remote processor config unavailable\n");
		return;
	}

	if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
		return;

	msm_smp2p_deinit_rmt_lpb_proc(pid);
	smp2p_ut_remote_spinlock_track_core(s, pid);
	msm_smp2p_init_rmt_lpb_proc(pid);
}

/**
 * smp2p_ut_remote_spinlock_track - Verify PID tracking for all processors.
 *
 * @s:	Pointer to output file
 *
 * This test verifies PID tracking for all configured remote processors.
 */
static void smp2p_ut_remote_spinlock_track_modem(struct seq_file *s)
{
	smp2p_ut_remote_spinlock_track(s, SMP2P_MODEM_PROC);
}

static void smp2p_ut_remote_spinlock_track_adsp(struct seq_file *s)
{
	smp2p_ut_remote_spinlock_track(s, SMP2P_AUDIO_PROC);
}

static void smp2p_ut_remote_spinlock_track_dsps(struct seq_file *s)
{
	smp2p_ut_remote_spinlock_track(s, SMP2P_SENSOR_PROC);
}

static void smp2p_ut_remote_spinlock_track_wcnss(struct seq_file *s)
{
	smp2p_ut_remote_spinlock_track(s, SMP2P_WIRELESS_PROC);
}

static void smp2p_ut_remote_spinlock_track_tz(struct seq_file *s)
{
	smp2p_ut_remote_spinlock_track(s, SMP2P_TZ_PROC);
}

static int __init smp2p_debugfs_init(void)
{
	/*
	 * Add Unit Test entries.
	 *
	 * The idea with unit tests is that you can run all of them
	 * from ADB shell by doing:
	 *  adb shell
	 *  cat ut*
	 *
	 * And if particular tests fail, you can then repeatedly run the
	 * failing tests as you debug and resolve the failing test.
	 */
	smp2p_debug_create("ut_remote_spinlock",
		smp2p_ut_remote_spinlock);
	smp2p_debug_create("ut_remote_spin_trylock",
		smp2p_ut_remote_spin_trylock);
	smp2p_debug_create("ut_remote_spinlock_modem",
		smp2p_ut_remote_spinlock_modem);
	smp2p_debug_create("ut_remote_spinlock_adsp",
		smp2p_ut_remote_spinlock_adsp);
	smp2p_debug_create("ut_remote_spinlock_dsps",
		smp2p_ut_remote_spinlock_dsps);
	smp2p_debug_create("ut_remote_spinlock_wcnss",
		smp2p_ut_remote_spinlock_wcnss);
	smp2p_debug_create("ut_remote_spinlock_tz",
		smp2p_ut_remote_spinlock_tz);
	smp2p_debug_create("ut_remote_spinlock_rpm",
		smp2p_ut_remote_spinlock_rpm);
	smp2p_debug_create_u32("ut_remote_spinlock_time",
		&ut_remote_spinlock_run_time);
	smp2p_debug_create("ut_remote_spinlock_ssr",
		&smp2p_ut_remote_spinlock_ssr);
	smp2p_debug_create("ut_remote_spinlock_track_modem",
		&smp2p_ut_remote_spinlock_track_modem);
	smp2p_debug_create("ut_remote_spinlock_track_adsp",
		&smp2p_ut_remote_spinlock_track_adsp);
	smp2p_debug_create("ut_remote_spinlock_track_dsps",
		&smp2p_ut_remote_spinlock_track_dsps);
	smp2p_debug_create("ut_remote_spinlock_track_wcnss",
		&smp2p_ut_remote_spinlock_track_wcnss);
	smp2p_debug_create("ut_remote_spinlock_track_tz",
		&smp2p_ut_remote_spinlock_track_tz);
	return 0;
}
module_init(smp2p_debugfs_init);
