// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
 */

#define pr_fmt(fmt)    "%s: " fmt, __func__

#include <linux/delay.h>
#include <linux/dma-buf.h>
#include <linux/dma-heap.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/highmem.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/qseecom.h>
#include <linux/qtee_shmbridge.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/soc/qcom/mdt_loader.h>
#include <linux/qseecom_kernel.h>
#include <linux/soc/qcom/slatecom_interface.h>
#include <linux/suspend.h>
#include "qcom_common.h"
#include "remoteproc_internal.h"


#define SECURE_APP		"slateapp"
#define INVALID_GPIO		-1
#define NUM_GPIOS		3

#define RESULT_SUCCESS		0
#define RESULT_FAILURE		-1

/* Slate Ramdump Size 8MB + 3MB (ITCM) */
#define SLATE_RAMDUMP_SZ 0XB00000
#define SLATE_MINIDUMP_SZ (0x400*40)
#define SLATE_RAMDUMP		3
#define SLATE_MINIDUMP		4

#define SLATE_CRASH_IN_TWM	-2

#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))

static struct workqueue_struct *slate_reset_wq;

/* tzapp command list.*/
enum slate_tz_commands {
	SLATE_RPROC_RAMDUMP,
	SLATE_RPROC_IMAGE_LOAD,
	SLATE_RPROC_AUTH_MDT,
	SLATE_RPROC_DLOAD_CONT,
	SLATE_RPROC_GET_SLATE_VERSION,
	SLATE_RPROC_SHUTDOWN,
	SLATE_RPROC_DUMPINFO,
	SLATE_RPROC_UP_INFO,
	SLATE_RPROC_RESET,
};

/* tzapp bg request.*/
struct tzapp_slate_req {
	uint64_t address_fw;
	uint32_t size_fw;
	uint8_t tzapp_slate_cmd;
} __packed;

/* tzapp bg response.*/
struct tzapp_slate_rsp {
	uint32_t tzapp_slate_cmd;
	uint32_t slate_info_len;
	int32_t status;
	uint32_t slate_info[100];
} __packed;

/**
 * struct pil_mdt - Representation of <name>.mdt file in memory
 * @hdr: ELF32 header
 * @phdr: ELF32 program headers
 */
struct pil_mdt {
	struct elf32_hdr hdr;
	struct elf32_phdr phdr[];
};

/**
 * struct qcom_slate
 * @dev: Device pointer
 * @rproc: Remoteproc handle for slate
 * @firmware_name: FW image file name
 * @ssr_subdev: SSR subdevice to be registered with remoteproc
 * @ssr_name: SSR subdevice name used as reference in remoteproc
 * @glink_subdev: GLINK subdevice to be registered with remoteproc
 * @sysmon: sysmon subdevice to be registered with remoteproc
 * @sysmon_name: sysmon subdevice name used as reference in remoteproc
 * @ssctl_id: instance id of the ssctl QMI service
 * @reboot_nb: notifier block to handle reboot scenarios
 * @address_fw: address where firmware binaries loaded in DMA
 * @size_fw: size of slate firmware binaries in DMA
 * @qseecom_handle: handle of TZ app
 * @cmd_status: qseecom command status
 * @app_status: status of tz app loading
 * @is_ready: Is slate chip up
 * @err_ready: The error ready signal
 * @shutdown_done: The shutdown done signal
 * @region_start: DMA handle for loading FW
 * @region_end: DMA address indicating end of DMA buffer
 * @region: CPU address for DMA buffer
 * @is_region_allocated: Is DMA buffer allocated
 * @region_size: DMA buffer size for FW
 * @gpios: arry to hold all gpio handler
 * @status_irq: irq to indicate slate status
 * @slate_queue: private queue to schedule worker thread for bottom half
 * @restart_work: work struct for executing ssr
 * @mem_phys: pa of DMA buffer for ramdump
 * @mem_region: va of DMA buffer for ramdump
 * @mem_size: DMA buffer size for ramdump
 */
struct qcom_slate {
	struct device *dev;

	struct rproc *rproc;

	const char *firmware_name;

	struct qcom_rproc_ssr ssr_subdev;
	const char *ssr_name;

	struct qcom_rproc_glink glink_subdev;
	struct qcom_sysmon *sysmon;
	const char *sysmon_name;
	int ssctl_id;

	struct notifier_block reboot_nb;

	phys_addr_t address_fw;
	size_t size_fw;

	struct qseecom_handle *qseecom_handle;
	u32 cmd_status;
	int app_status;
	bool is_ready;
	struct completion err_ready;
	struct completion shutdown_done;

	phys_addr_t region_start;
	phys_addr_t region_end;
	void *region;
	bool is_region_allocated;
	size_t region_size;

	unsigned int gpios[NUM_GPIOS];
	int status_irq;
	struct workqueue_struct *slate_queue;
	struct work_struct restart_work;

	phys_addr_t mem_phys;
	void *mem_region;
	size_t mem_size;

	struct wakeup_source rproc_slate_ws;
};

static irqreturn_t slate_status_change(int irq, void *dev_id);
struct mutex cmdsync_lock;

static ssize_t txn_id_show(struct device *dev, struct device_attribute *attr,
				char *buf)
{
	struct platform_device *pdev = container_of(dev,
						struct platform_device, dev);
	struct qcom_slate *slate_data =
			(struct qcom_slate *)platform_get_drvdata(pdev);

	return sysfs_emit(buf, "%zu\n",
			qcom_sysmon_get_txn_id(slate_data->sysmon));
}
static DEVICE_ATTR_RO(txn_id);

/**
 * get_cmd_rsp_buffers() - Function sets cmd & rsp buffer pointers and
 *                         aligns buffer lengths
 * @hdl:	index of qseecom_handle
 * @cmd:	req buffer - set to qseecom_handle.sbuf
 * @cmd_len:	ptr to req buffer len
 * @rsp:	rsp buffer - set to qseecom_handle.sbuf + offset
 * @rsp_len:	ptr to rsp buffer len
 *
 * Return: Success always .
 */

static int get_cmd_rsp_buffers(struct qseecom_handle *handle, void **cmd,
			uint32_t *cmd_len, void **rsp, uint32_t *rsp_len)
{
	*cmd = handle->sbuf;
	if (*cmd_len & QSEECOM_ALIGN_MASK)
		*cmd_len = QSEECOM_ALIGN(*cmd_len);

	*rsp = handle->sbuf + *cmd_len;
	if (*rsp_len & QSEECOM_ALIGN_MASK)
		*rsp_len = QSEECOM_ALIGN(*rsp_len);

	return 0;
}

/**
 * load_slate_tzapp() - Called to load TZ app.
 * @pbd: struct containing private SLATE data.
 *
 * Return: 0 on success. Error code on failure.
 */

static int load_slate_tzapp(struct qcom_slate *pbd)
{
	int rc;

	/* return success if already loaded */
	if (pbd->qseecom_handle && !pbd->app_status)
		return 0;
	/* Load the APP */
	rc = qseecom_start_app(&pbd->qseecom_handle, SECURE_APP, SZ_4K);
	if (rc < 0) {
		dev_err(pbd->dev, "SLATE TZ app load failure\n");
		pbd->app_status = RESULT_FAILURE;
		return -EIO;
	}
	pbd->app_status = RESULT_SUCCESS;
	return 0;
}

/**
 * slate_tzapp_comm() - Function called to communicate with TZ APP.
 * @pbd: struct containing private SLATE data.
 * @req: struct containing command and parameters.
 *
 * Return: 0 on success. Error code on failure.
 */

static long slate_tzapp_comm(struct qcom_slate *pbd,
				struct tzapp_slate_req *req)
{
	struct tzapp_slate_req *slate_tz_req;
	struct tzapp_slate_rsp *slate_tz_rsp;
	int rc, req_len, rsp_len, i;
	unsigned char *ascii;

	/* Fill command structure */
	req_len = sizeof(struct tzapp_slate_req);
	rsp_len = sizeof(struct tzapp_slate_rsp);

	mutex_lock(&cmdsync_lock);
	rc = get_cmd_rsp_buffers(pbd->qseecom_handle,
		(void **)&slate_tz_req, &req_len,
		(void **)&slate_tz_rsp, &rsp_len);
	if (rc)
		goto end;

	slate_tz_req->tzapp_slate_cmd = req->tzapp_slate_cmd;
	slate_tz_req->address_fw = req->address_fw;
	slate_tz_req->size_fw = req->size_fw;
	rc = qseecom_send_command(pbd->qseecom_handle,
		(void *)slate_tz_req, req_len, (void *)slate_tz_rsp, rsp_len);

	mutex_unlock(&cmdsync_lock);
	pr_debug("SLATE PIL qseecom returned with value 0x%x and status 0x%x\n",
		rc, slate_tz_rsp->status);
	if (rc || slate_tz_rsp->status)
		pbd->cmd_status = slate_tz_rsp->status;
	else
		pbd->cmd_status = 0;
	/* if last command sent was SLATE_VERSION print the version */
	if (req->tzapp_slate_cmd == SLATE_RPROC_GET_SLATE_VERSION) {
		pr_info("SLATE FW version\n");
		for (i = 0; i < slate_tz_rsp->slate_info_len; i++) {
			pr_info("Version 0x%x\n", slate_tz_rsp->slate_info[i]);
			ascii = (unsigned char *)&slate_tz_rsp->slate_info[i];
		}
	}
	return rc;
end:
	mutex_unlock(&cmdsync_lock);
	return rc;
}

/**
 * slate_restart_work() - schedule by interrupt handler to carry
 * out ssr sequence
 * @work: work struct.
 *
 * Return: none.
 */
static void slate_restart_work(struct work_struct *work)
{

	struct qcom_slate *slate_data =
		container_of(work, struct qcom_slate, restart_work);
	struct rproc *slate_rproc = slate_data->rproc;

	/* Trigger  apps crash if recovery is disabled */
	BUG_ON(slate_rproc->recovery_disabled);

	/* If recovery is enabled, go for recovery path */
	pr_debug("Slate is crashed! Starting recovery...\n");
	rproc_report_crash(slate_rproc, RPROC_FATAL_ERROR);

}

static irqreturn_t slate_status_change(int irq, void *dev_id)
{
	bool value;
	struct qcom_slate *drvdata = (struct qcom_slate *)dev_id;
	struct tzapp_slate_req slate_tz_req;
	int ret = 0;

	if (!drvdata)
		return IRQ_HANDLED;

	value = gpio_get_value(drvdata->gpios[0]);
	update_s2a_status(value);

	if (value && !drvdata->is_ready) {
		dev_info(drvdata->dev,
			"SLATE services are up and running: irq state changed 0->1\n");
		__pm_relax(&drvdata->rproc_slate_ws);
		drvdata->is_ready = true;
		complete(&drvdata->err_ready);
		slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_UP_INFO;
		ret = slate_tzapp_comm(drvdata, &slate_tz_req);
		if (ret || drvdata->cmd_status) {
			dev_err(drvdata->dev, "%s: SLATE RPROC get slate version failed error %d\n",
				__func__, drvdata->cmd_status);
			return IRQ_NONE;
		}
	} else if (!value && drvdata->is_ready) {
		dev_err(drvdata->dev,
			"SLATE got unexpected reset: irq state changed 1->0\n");
			/* skip dump collection and return with shutdown completion signal */
			if (is_slate_unload_only()) {
				dev_info(drvdata->dev,
					"Received shutdown_only request\n");
				complete(&drvdata->shutdown_done);
				return IRQ_HANDLED;
			}
		__pm_stay_awake(&drvdata->rproc_slate_ws);
		queue_work(drvdata->slate_queue, &drvdata->restart_work);
	} else {
		dev_err(drvdata->dev, "SLATE status irq: unknown status\n");
	}

	return IRQ_HANDLED;
}

/**
 * setup_slate_gpio_irq() - called in probe to configure input/
 * output gpio.
 * @drvdata: private data struct for SLATE.
 *
 * Return: 0 on success. Error code on failure.
 */
static int setup_slate_gpio_irq(struct platform_device *pdev,
				struct qcom_slate *drvdata)
{
	int ret = -1;
	int irq, i;

	if (gpio_request(drvdata->gpios[0], "SLATE2AP_STATUS")) {
		dev_err(&pdev->dev,
			"%s Failed to configure SLATE2AP_STATUS gpio\n",
				__func__);
		goto err;
	}
	gpio_direction_input(drvdata->gpios[0]);
	/* SLATE2AP STATUS IRQ */
	irq = gpio_to_irq(drvdata->gpios[0]);
	if (irq < 0) {
		dev_err(&pdev->dev,
		"%s: bad SLATE2AP_STATUS IRQ resource, err = %d\n",
			__func__, irq);
		goto err;
	}

	drvdata->status_irq = irq;
	ret = devm_request_threaded_irq(drvdata->dev, drvdata->status_irq,
		NULL, slate_status_change,
		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
		"slate2ap_status", drvdata);

	if (ret < 0) {
		dev_err(drvdata->dev,
			"%s: SLATE2AP_STATUS IRQ#%d re registration failed, err=%d\n",
			__func__, drvdata->status_irq, ret);
		goto err;
	}

	ret = irq_set_irq_wake(drvdata->status_irq, true);
	if (ret < 0) {
		dev_err(drvdata->dev,
			"%s: SLATE2AP_STATUS IRQ#%d set wakeup capable failed, err=%d\n",
			__func__, drvdata->status_irq, ret);
		goto err;
	}

	/* AP2SLATE STATUS IRQ */
	if (gpio_request(drvdata->gpios[1], "AP2SLATE_STATUS")) {
		dev_err(&pdev->dev,
			"%s Failed to configure AP2SLATE_STATUS gpio\n",
				__func__);
		goto err;
	}
	/*
	 * Put status gpio in default high state which will
	 * make transition to low on any sudden reset case of msm
	 */
	gpio_direction_output(drvdata->gpios[1], 1);
	/* Inform SLATE that AP is up */
	gpio_set_value(drvdata->gpios[1], 1);
	return 0;
err:
	for (i = 0; i < NUM_GPIOS; ++i) {
		if (gpio_is_valid(drvdata->gpios[i]))
			gpio_free(drvdata->gpios[i]);
	}
	return ret;
}

/**
 * slate_dt_parse_gpio() - called in probe to parse gpio's
 * @drvdata: private data struct for SLATE.
 *
 * Return: 0 on success. Error code on failure.
 */
static int slate_dt_parse_gpio(struct platform_device *pdev,
				struct qcom_slate *drvdata)
{
	int i, val;

	for (i = 0; i < NUM_GPIOS; i++)
		drvdata->gpios[i] = INVALID_GPIO;

	val = of_get_named_gpio(pdev->dev.of_node,
					"qcom,slate2ap-status-gpio", 0);
	if (val >= 0) {
		drvdata->gpios[0] = val;
		update_s2a_status(gpio_get_value(drvdata->gpios[0]));
	}
	else {
		pr_err("SLATE status gpio not found, error=%d\n", val);
		return -EINVAL;
	}

	val = of_get_named_gpio(pdev->dev.of_node,
					"qcom,ap2slate-status-gpio", 0);
	if (val >= 0)
		drvdata->gpios[1] = val;
	else {
		pr_err("ap2slate status gpio not found, error=%d\n", val);
		return -EINVAL;
	}

	return 0;
}

/**
 * slate_auth_and_xfer() - Called by start operation of remoteproc framework
 * to signal tz app to authenticate and boot slate chip.
 * @slate_data: struct containing private <slate> data.
 *
 * Return: 0 on success. Error code on failure.
 */
static int slate_auth_and_xfer(struct qcom_slate *slate_data)
{
	struct tzapp_slate_req slate_tz_req;
	uint32_t ns_vmids[] = {VMID_HLOS};
	uint32_t ns_vm_perms[] = {PERM_READ | PERM_WRITE};
	u64 shm_bridge_handle;
	int ret;

	ret = qtee_shmbridge_register(slate_data->address_fw, slate_data->size_fw,
			ns_vmids, ns_vm_perms, 1, PERM_READ | PERM_WRITE,
			&shm_bridge_handle);

	if (ret) {
		dev_err(slate_data->dev,
				"%s: Failed to create shm bridge [%d]\n",
				__func__, ret);
		return ret;
	}

	slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_IMAGE_LOAD;
	slate_tz_req.address_fw = slate_data->address_fw;
	slate_tz_req.size_fw = slate_data->size_fw;

	ret = slate_tzapp_comm(slate_data, &slate_tz_req);

	if (slate_data->cmd_status == SLATE_CRASH_IN_TWM) {
		slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_DLOAD_CONT;
		ret = slate_tzapp_comm(slate_data, &slate_tz_req);
	}

	if (ret || slate_data->cmd_status) {
		dev_err(slate_data->dev,
				"%s: Firmware image authentication failed\n",
				__func__);
		ret = slate_data->cmd_status;
		goto tzapp_comm_failed;
	}

	/* slate Transfer of image is complete, free up the memory */
	pr_debug("Firmware authentication and transfer done\n");

tzapp_comm_failed:
	qtee_shmbridge_deregister(shm_bridge_handle);
	return ret;
}

/**
 * wait_for_err_ready() - Called in power_up to wait for error ready.
 * Signal waiting function.
 * @slate_data: SLATE RPROC private structure.
 *
 * Return: 0 on success. Error code on failure.
 */
static int wait_for_err_ready(struct qcom_slate *slate_data)
{
	int ret;

	if ((!slate_data->status_irq))
		return 0;

	ret = wait_for_completion_timeout(&slate_data->err_ready,
			msecs_to_jiffies(10000));
	if (!ret) {
		pr_err("[%s]: Error ready timed out\n",
			slate_data->firmware_name);
		return -ETIMEDOUT;
	}
	return 0;
}

/**
 * wait_for_shutdown_done() - Called in power_down to wait for shutdown done.
 * Signal waiting function.
 * @slate_data: SLATE RPROC private structure.
 *
 * Return: 0 on success. Error code on failure.
 */
static int wait_for_shutdown_done(struct qcom_slate *slate_data)
{
	int ret;

	if ((!slate_data->status_irq))
		return 0;

	ret = wait_for_completion_timeout(&slate_data->shutdown_done,
			msecs_to_jiffies(10000));
	if (!ret) {
		pr_err("[%s]: shutdown done timed out\n",
			slate_data->firmware_name);
		return -ETIMEDOUT;
	}
	return 0;
}

void send_reset_signal(struct qcom_slate *slate_data)
{
	struct tzapp_slate_req slate_tz_req;
	int ret;

	slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_RESET;
	slate_tz_req.address_fw = 0;
	slate_tz_req.size_fw = 0;

	ret = slate_tzapp_comm(slate_data, &slate_tz_req);
	if (ret || slate_data->cmd_status)
		dev_err(slate_data->dev,
			"%s: Failed to send reset signal to tzapp\n",
			__func__);
}

int slate_flash_mode(struct qcom_slate *slate_data)
{
	int ret;
	int retry_attempt = 2;

	do {
		ret = wait_for_err_ready(slate_data);
		if (!ret)
			return RESULT_SUCCESS;
		dev_err(slate_data->dev,
			"[%s:%d]: Timed out waiting for error ready: %s!\n",
			current->comm, current->pid, slate_data->firmware_name);

		pr_debug("Retry booting slate, Mode: Flash, attempt: %d\n",
				retry_attempt);
		send_reset_signal(slate_data);
		retry_attempt -= 1;
	} while (retry_attempt);

	return ret;
}

static int slate_start(struct rproc *rproc)
{
	struct qcom_slate *slate_data = (struct qcom_slate *)rproc->priv;
	struct tzapp_slate_req slate_tz_req;
	int ret = 0;

	if (!slate_data) {
		dev_err(slate_data->dev, "%s Invalid slate pointer !!\n",
			__func__);
		return -EINVAL;
	}

	/* Slate is booted from flash */
	if (get_slate_boot_mode()) {
		if (gpio_get_value(slate_data->gpios[0])) {
			pr_info("Slate is booted up!! Mode: FLASH\n");
			slate_data->is_ready = true;
			/* Update dump configuration when it boots from FLASH Mode. */
			slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_UP_INFO;
			ret = slate_tzapp_comm(slate_data, &slate_tz_req);
			if (ret || slate_data->cmd_status)
				dev_err(slate_data->dev,
				"%s: failed to update dump info %d\n",
				__func__, slate_data->cmd_status);
			return RESULT_SUCCESS;
		} else
			return RESULT_FAILURE;
	}

	slate_data->address_fw = slate_data->region_start;
	slate_data->size_fw = slate_data->region_end - slate_data->region_start;
	pr_debug("SLATE PIL loads firmware blobs at 0x%x with size 0x%x\n",
		slate_data->address_fw, slate_data->size_fw);

	ret = slate_auth_and_xfer(slate_data);
	if (ret) {
		dev_err(slate_data->dev, "%s slate TZ app load failure\n",
					__func__);
		return ret;
	}

	ret = wait_for_err_ready(slate_data);
	if (ret) {
		dev_err(slate_data->dev,
			"[%s:%d]: Timed out waiting for error ready: %s!\n",
			current->comm, current->pid, slate_data->firmware_name);
		return ret;
	}

	pr_err("Slate is booted up!! Mode: HOST\n");

	dma_free_coherent(slate_data->dev, slate_data->region_size,
		slate_data->region, slate_data->region_start);
	slate_data->is_region_allocated = false;
	slate_data->region = NULL;
	slate_data->region_start = 0;
	slate_data->region_end = 0;
	slate_data->region_size = 0;
	return 0;
}

/**
* slate_coredump() - Called by SSR framework to save dump of SLATE internal
* memory, SLATE PIL does allocate region from dynamic memory and pass this
* region to tz to dump memory content of SLATE.
* @rproc: remoteproc handle for slate.
*
* Return: 0 on success. Error code on failure.
*/

static void slate_coredump(struct rproc *rproc)
{
	struct qcom_slate *slate_data = (struct qcom_slate *)rproc->priv;
	struct tzapp_slate_req slate_tz_req;
	uint32_t ns_vmids[] = {VMID_HLOS};
	uint32_t ns_vm_perms[] = {PERM_READ | PERM_WRITE};
	u64 shm_bridge_handle;
	void *region;
	phys_addr_t start_addr;
	uint32_t dump_info;
	unsigned long size = SLATE_RAMDUMP_SZ;
	unsigned long attr = 0;
	int ret = 0;

	pr_err("Setup for Coredump.\n");

	if (!slate_data->qseecom_handle) {
		ret = load_slate_tzapp(slate_data);
		if (ret) {
			dev_err(slate_data->dev,
				"%s: SLATE TZ app load failure\n",
				__func__);
			return;
		}
	}

	/* This check is added here to make slate dump collection
	 * decision in RTOS/TWM mode exit. Only way for kernel to know slate state
	 * info(crashed/running)in RTOS/TWM exit is by reading S2A irq line.
	 * When S2A is pulled LOW, it is interpreted as slate crashed state and
	 * slate dump needs to be collected. Once dumps are collected TZ will
	 * automatically send SLATE_RESET CMD.
	 * When S2A is pulled HIGH, it is interpreted as slate running state and
	 * dump should not be collected. At this point it is necessary
	 * to send SLATE_RESET CMD to bring slate out of RTOS slate.
	 * This check does not disturb SSR/system dump collection.
	 */

	if (is_twm_exit()) {
		/* reset_cmd signals shutdown on slate, lets ack s2a irq for same
		 * otherwise it will haunt after slate boot up and crash system.
		 */
		if (!gpio_get_value(slate_data->gpios[0])) {
			pr_info("TWM Exit: Collect Dump, slate is CRASHED..!!\n");
			/* We are assuming that Slate TZapp has started
			 * subsystem_ramdump service.
			 * Introducing delay here for subsystem_ramdump
			 * service to start.
			 */
			msleep(5000);
		} else {
			enable_irq(slate_data->status_irq);
			slate_data->is_ready = true;
			pr_info("TWM Exit: Skip dump collection, slate is RUNNING ..!!\n");
			/* Send RESET CMD to bring slate out of RTOS state */
			slate_data->cmd_status = 0;
			slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_RESET;
			ret = slate_tzapp_comm(slate_data, &slate_tz_req);
			if (ret || slate_data->cmd_status) {
				dev_err(slate_data->dev,
					"%s: Failed to send reset signal to tzapp\n",
					__func__);
				goto rtos_out;
			}
			/* reset_cmd signals shutdown on slate, lets ack s2a irq for same
			 * otherwise it will haunt after slate boot up and crash system.
			 */
			/* By this time if S2A is not pulled then wait for it to go LOW */
			if (gpio_get_value(slate_data->gpios[0])) {
				ret = wait_for_shutdown_done(slate_data);
				if (ret) {
					dev_err(slate_data->dev,
					"[%s:%d]: Timed out waiting for error ready: %s!\n",
					current->comm, current->pid, slate_data->firmware_name);
				}
			}
			goto rtos_out;
		}
	}
	rproc_coredump_cleanup(rproc);

	slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_DUMPINFO;
	ret = slate_tzapp_comm(slate_data, &slate_tz_req);
	dump_info = slate_data->cmd_status;

	if (dump_info == SLATE_RAMDUMP)
		size = SLATE_RAMDUMP_SZ;
	else if (dump_info == SLATE_MINIDUMP)
		size = SLATE_MINIDUMP_SZ;
	else {
		dev_err(slate_data->dev,
			"%s: SLATE RPROC ramdump collection failed\n",
			__func__);
		return;
	}

	region = dma_alloc_attrs(slate_data->dev, size,
			&start_addr, GFP_KERNEL, attr);

	if (region == NULL) {
		dev_dbg(slate_data->dev,
			"fail to allocate ramdump region of size %zx\n",
			size);
		return;
	}
	slate_data->mem_phys = start_addr;
	slate_data->mem_size = size;
	slate_data->mem_region = region;

	ret = qtee_shmbridge_register(start_addr, size, ns_vmids,
		ns_vm_perms, 1, PERM_READ | PERM_WRITE, &shm_bridge_handle);

	if (ret) {
		pr_err("Failed to create shm bridge. ret=[%d]\n",
			__func__, ret);
		goto dma_free;
	}

	slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_RAMDUMP;
	slate_tz_req.address_fw = start_addr;
	slate_tz_req.size_fw = size;
	ret = slate_tzapp_comm(slate_data, &slate_tz_req);
	if (ret != 0 || slate_data->cmd_status) {
		dev_dbg(slate_data->dev,
			"%s: SLATE RPROC ramdmp collection failed\n",
			__func__);
		goto shm_free;
	}

	dma_sync_single_for_cpu(slate_data->dev, slate_data->mem_phys, size, DMA_FROM_DEVICE);

	pr_debug("Add coredump segment!\n");
	ret = rproc_coredump_add_custom_segment(rproc, start_addr, size,
			NULL, NULL);

	if (ret) {
		dev_err(slate_data->dev, "failed to add rproc_segment: %d\n",
			ret);
		rproc_coredump_cleanup(slate_data->rproc);
		goto shm_free;
	}

	rproc_coredump(rproc);

shm_free:
	qtee_shmbridge_deregister(shm_bridge_handle);
dma_free:
	dma_free_attrs(slate_data->dev, size, region,
			start_addr, attr);
	return;
rtos_out:
	disable_irq(slate_data->status_irq);
	slate_data->is_ready = false;
}

/**
 * slate_prepare() - Called by rproc_boot. This loads tz app.
 * @rproc: struct caontaining private slate data.
 *
 * Return: 0 on success. Error code on failure.
 */
static int slate_prepare(struct rproc *rproc)
{
	struct qcom_slate *slate_data = (struct qcom_slate *)rproc->priv;
	int ret = 0;

	if (!slate_data) {
		dev_err(slate_data->dev, "%s Invalid slate pointer !!\n",
			__func__);
		return -EINVAL;
	}
	if (slate_data->app_status != RESULT_SUCCESS) {
		ret = load_slate_tzapp(slate_data);
		if (ret) {
			dev_err(slate_data->dev,
			"%s: slate TZ app load failure\n", __func__);
			return ret;
		}
	}
	pr_debug("slateapp loaded\n");

	return ret;
}

static int slate_alloc_mem(struct qcom_slate *slate_data, size_t aligned_size)
{
	slate_data->region = dma_alloc_coherent(slate_data->dev, aligned_size,
			&slate_data->region_start, GFP_KERNEL);
	if (!slate_data->region)
		return -ENOMEM;

	return 0;
}

static int slate_alloc_region(struct qcom_slate *slate_data, phys_addr_t min_addr,
				phys_addr_t max_addr, size_t align)
{
	size_t size = max_addr - min_addr;
	size_t aligned_size;
	int ret;

	/* Don't reallocate due to fragmentation concerns, just sanity check */
	if (slate_data->is_region_allocated) {
		if (WARN(slate_data->region_end - slate_data->region_start < size,
			"Can't reuse PIL memory, too small\n"))
			return -ENOMEM;
		return 0;
	}

	if (align >= SZ_4M)
		aligned_size = ALIGN(size, SZ_4M);
	else if (align >= SZ_1M)
		aligned_size = ALIGN(size, SZ_1M);
	else
		aligned_size = ALIGN(size, SZ_4K);

	ret = slate_alloc_mem(slate_data, aligned_size);
	if (ret) {
		dev_err(slate_data->dev,
				"%s: Failed to allocate relocatable region\n",
				__func__);
		slate_data->region_start = 0;
		slate_data->region_end = 0;
		return ret;
	}

	slate_data->is_region_allocated = true;
	slate_data->region_end = slate_data->region_start + size;
	slate_data->region_size = aligned_size;

	return 0;
}

static bool segment_is_relocatable(const struct elf32_phdr *p)
{
	return !!(p->p_flags & BIT(27));
}

static int segment_is_loadable(const struct elf32_phdr *p)
{
	return (p->p_type == PT_LOAD) && !segment_is_hash(p->p_flags) &&
		p->p_memsz;
}

static int slate_setup_region(struct qcom_slate *slate_data, const struct pil_mdt *mdt)
{
	const struct elf32_phdr *phdr;
	phys_addr_t min_addr_r, min_addr_n, max_addr_r, max_addr_n, start, end;
	size_t align = 0;
	int i, ret = 0;
	bool relocatable = false;

	min_addr_n = min_addr_r = (phys_addr_t)ULLONG_MAX;
	max_addr_n = max_addr_r = 0;

	/* Find the image limits */
	for (i = 0; i < mdt->hdr.e_phnum; i++) {
		phdr = &mdt->phdr[i];
		if (!segment_is_loadable(phdr))
			continue;

		start = phdr->p_paddr;
		end = start + phdr->p_memsz;

		if (segment_is_relocatable(phdr)) {
			min_addr_r = min(min_addr_r, start);
			max_addr_r = max(max_addr_r, end);
			/*
			 * Lowest relocatable segment dictates alignment of
			 * relocatable region
			 */
			if (min_addr_r == start)
				align = phdr->p_align;
			relocatable = true;
		} else {
			min_addr_n = min(min_addr_n, start);
			max_addr_n = max(max_addr_n, end);
		}
	}

	/* Align the max address to the next 4K boundary to satisfy iommus and
	 * XPUs that operate on 4K chunks.
	 */

	max_addr_n = ALIGN(max_addr_n, SZ_4K);
	max_addr_r = ALIGN(max_addr_r, SZ_4K);

	if (relocatable) {
		ret = slate_alloc_region(slate_data, min_addr_r, max_addr_r, align);
	} else {
		slate_data->region_start = min_addr_n;
		slate_data->region_end = max_addr_n;
	}

	return ret;
}

static int slate_auth_metadata(struct qcom_slate *slate_data, const u8 *metadata, size_t size)
{
	struct tzapp_slate_req slate_tz_req;
	struct qtee_shm shm;
	int ret = 0;

	ret = qtee_shmbridge_allocate_shm(size, &shm);
	if (ret) {
		pr_err("Shmbridge memory allocation failed\n");
		return ret;
	}

	/* Make sure there are no mapping in PKMAP and fixmap */
	kmap_flush_unused();

	memcpy(shm.vaddr, metadata, size);

	slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_AUTH_MDT;
	slate_tz_req.address_fw = shm.paddr;
	slate_tz_req.size_fw = size;

	ret = slate_tzapp_comm(slate_data, &slate_tz_req);
	if (ret || slate_data->cmd_status) {
		dev_err(slate_data->dev,
			"%s: Metadata loading is failed\n",
			__func__);
		ret = slate_data->cmd_status;
		goto tzapp_com_failed;
	}
	pr_debug("Metadata loaded successfully\n");

tzapp_com_failed:
	qtee_shmbridge_free_shm(&shm);
	return ret;
}

static int slate_load(struct rproc *rproc, const struct firmware *fw)
{
	struct qcom_slate *slate_data = (struct qcom_slate *)rproc->priv;
	int ret = 0;
	const struct pil_mdt *mdt;

	if (!slate_data) {
		dev_err(slate_data->dev, "%s Invalid slate pointer !!\n",
			__func__);
		return -EINVAL;
	}

	/* Enable status and err fetal irqs */
	enable_irq(slate_data->status_irq);

	/* boot slate from flash */
	if (get_slate_boot_mode()) {
		if (gpio_get_value(slate_data->gpios[0]))
			return RESULT_SUCCESS;
		ret = slate_flash_mode(slate_data);
		if (!ret)
			return RESULT_SUCCESS;
		dev_err(slate_data->dev,
			"%s: Failed to boot slate from flash\n",
			__func__);
		return ret;
	}

	mdt = (const struct pil_mdt *)fw->data;
	ret = slate_setup_region(slate_data, mdt);
	if (ret) {
		dev_err(slate_data->dev, "%s: slate memory_setup failure\n",
			__func__);
		return ret;
	}
	pr_debug("Loading from %pa tp %pa\n", &slate_data->region_start,
			&slate_data->region_end);

	ret = qcom_mdt_load_no_init(slate_data->dev, fw, rproc->firmware, 0,
		slate_data->region, slate_data->region_start,
		slate_data->region_size, NULL);
	if (ret) {
		dev_err(slate_data->dev, "%s: slate memory setup failure\n",
			__func__);
		return ret;
	}

	/* send the metadata */
	ret = slate_auth_metadata(slate_data, fw->data, fw->size);
	if (ret) {
		dev_err(slate_data->dev, "%s: slate TZ app load failure\n",
			__func__);
		return ret;
	}
	return ret;
}

static int slate_stop(struct rproc *rproc)
{
	struct qcom_slate *slate_data = rproc->priv;
	struct tzapp_slate_req slate_tz_req;
	int ret = RESULT_FAILURE;

	if (!slate_data) {
		dev_err(slate_data->dev, "%s Invalid slate pointer !!\n",
			__func__);
		return -EINVAL;
	}

	if (!slate_data->is_ready) {
		dev_err(slate_data->dev, "%s: Slate is not up!\n", __func__);
		return ret;
	}

	/* Dont send shutdown cmd if slate is crashed or offline state*/
	if (!gpio_get_value(slate_data->gpios[0]))
		goto out;

	ret = load_slate_tzapp(slate_data);
	if (ret) {
		dev_err(slate_data->dev,
			"%s: SLATE TZ app load failure\n",
			__func__);
		return ret;
	}
	slate_data->cmd_status = 0;
	slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_SHUTDOWN;
	slate_tz_req.address_fw = 0;
	slate_tz_req.size_fw = 0;

	ret = slate_tzapp_comm(slate_data, &slate_tz_req);
	if (ret || slate_data->cmd_status) {
		pr_debug("Slate pil shutdown failed\n");
		return ret;
	}

	/* wait for slate shutdown completion in exclusive slate shutdown request */
	if (is_slate_unload_only()) {
		ret = wait_for_shutdown_done(slate_data);
		if (ret) {
			dev_err(slate_data->dev,
			"[%s:%d]: Timed out waiting for error ready: %s!\n",
			current->comm, current->pid, slate_data->firmware_name);
			return ret;
		}
	}
out:
	disable_irq(slate_data->status_irq);
	slate_data->is_ready = false;

	return 0;
}

static void *slate_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
	struct qcom_slate *slate_data = (struct qcom_slate *)rproc->priv;
	int offset;

	offset = da - slate_data->mem_phys;
	if (offset < 0 || offset + len > slate_data->mem_size)
		return NULL;

	return slate_data->mem_region + offset;
}

static const struct rproc_ops slate_ops = {
	.prepare = slate_prepare,
	.get_boot_addr = rproc_elf_get_boot_addr,
	.load = slate_load,
	.start = slate_start,
	.stop = slate_stop,
	.coredump = slate_coredump,
	.da_to_va = slate_da_to_va,
};

static int slate_app_reboot_notify(struct notifier_block *nb,
		unsigned long action, void *data)
{
	struct qcom_slate *slate_data = container_of(nb,
					struct qcom_slate, reboot_nb);

	/* Disable irq if already SLATE is up */
	if (slate_data->is_ready) {
		disable_irq(slate_data->status_irq);
		slate_data->is_ready = false;
	}
	/* Toggle AP2SLATE err fetal gpio here to inform apps err fetal event */
	if (gpio_is_valid(slate_data->gpios[1])) {
		pr_debug("Sending reboot signal\n");
		gpio_set_value(slate_data->gpios[1], 1);
	}
	return NOTIFY_DONE;
}

#ifdef CONFIG_DEEPSLEEP
static int rproc_slate_suspend(struct device *dev)
{
	if (pm_suspend_via_firmware()) {
		struct qcom_slate *slate_data = dev_get_drvdata(dev);

		qseecom_shutdown_app(&slate_data->qseecom_handle);
		slate_data->qseecom_handle = NULL;
	}
	return 0;
}

static int rproc_slate_resume(struct device *dev)
{
	return 0;
}
#endif

static int rproc_slate_driver_probe(struct platform_device *pdev)
{
	struct qcom_slate *slate;
	struct rproc *rproc;
	const char *fw_name;
	int ret;
	char md_dev_name[32];

	ret = of_property_read_string(pdev->dev.of_node,
			"qcom,firmware-name", &fw_name);
	if (ret)
		return ret;

	mutex_init(&cmdsync_lock);
	rproc = rproc_alloc(&pdev->dev, pdev->name, &slate_ops,
			fw_name, sizeof(*slate));
	if (!rproc) {
		dev_err(&pdev->dev, "unable to allocate remoteproc\n");
		mutex_destroy(&cmdsync_lock);
		return -ENOMEM;
	}

	rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);

	slate = (struct qcom_slate *)rproc->priv;

	slate->dev = &pdev->dev;

	/* Add wake lock */
	wakeup_source_add(&slate->rproc_slate_ws);

	/* Read GPIO configuration */
	ret = slate_dt_parse_gpio(pdev, slate);
	if (ret)
		goto free_rproc;

	ret = setup_slate_gpio_irq(pdev, slate);
	if (ret < 0)
		goto free_rproc;
	/* Disable S2A irq bydefault */
	disable_irq(slate->status_irq);

	slate->firmware_name = fw_name;
	slate->app_status = RESULT_FAILURE;
	rproc->dump_conf = RPROC_COREDUMP_ENABLED;
	rproc->recovery_disabled = true;
	rproc->auto_boot = false;
	slate->rproc = rproc;
	slate->sysmon_name = "slatefw";
	slate->ssr_name = "slatefw";
	slate->ssctl_id = -EINVAL;
	platform_set_drvdata(pdev, slate);

	/* Register SSR subdev to rproc*/
	qcom_add_ssr_subdev(rproc, &slate->ssr_subdev, slate->ssr_name);
	qcom_add_glink_subdev(rproc, &slate->glink_subdev, slate->ssr_name);

	slate->sysmon = qcom_add_sysmon_subdev(rproc, slate->sysmon_name,
			slate->ssctl_id);
	if (IS_ERR(slate->sysmon)) {
		ret = PTR_ERR(slate->sysmon);
		dev_err(slate->dev, "%s: Error while adding sysmon subdevice:[%d]\n",
				__func__, ret);
		goto free_rproc;
	}

	ret = device_create_file(slate->dev, &dev_attr_txn_id);
	if (ret)
		goto remove_subdev;

	/* Register callback for handling reboot */
	slate->reboot_nb.notifier_call = slate_app_reboot_notify;
	register_reboot_notifier(&slate->reboot_nb);

	slate->slate_queue = alloc_workqueue("slate_queue", 0, 0);
	if (!slate->slate_queue) {
		dev_err(slate->dev, "%s: creation of slate_queue failed\n",
				__func__);
		goto unregister_notify;
	}

	/* Initialize work queue for reset handler */
	INIT_WORK(&slate->restart_work, slate_restart_work);

	snprintf(md_dev_name, ARRAY_SIZE(md_dev_name),
			"%s-md", pdev->dev.of_node->name);

	/* Register with rproc */
	ret = rproc_add(rproc);
	if (ret)
		goto destroy_wq;

	init_completion(&slate->err_ready);
	init_completion(&slate->shutdown_done);
	pr_debug("Slate probe is completed\n");
	return 0;

destroy_wq:
	destroy_workqueue(slate_reset_wq);
unregister_notify:
	unregister_reboot_notifier(&slate->reboot_nb);
remove_subdev:
	qcom_remove_sysmon_subdev(slate->sysmon);
free_rproc:
	rproc_free(rproc);
	mutex_destroy(&cmdsync_lock);

	return ret;
}

static int rproc_slate_driver_remove(struct platform_device *pdev)
{
	struct qcom_slate *slate = platform_get_drvdata(pdev);

	if (slate_reset_wq)
		destroy_workqueue(slate_reset_wq);
	device_remove_file(slate->dev, &dev_attr_txn_id);
	qcom_remove_glink_subdev(slate->rproc, &slate->glink_subdev);
	qcom_remove_sysmon_subdev(slate->sysmon);
	qcom_remove_ssr_subdev(slate->rproc, &slate->ssr_subdev);
	unregister_reboot_notifier(&slate->reboot_nb);
	rproc_del(slate->rproc);
	rproc_free(slate->rproc);

	return 0;
}

static const struct dev_pm_ops rproc_slate_pm_ops = {
#ifdef CONFIG_DEEPSLEEP
	.suspend = rproc_slate_suspend,
	.resume = rproc_slate_resume,
#endif
};

static const struct of_device_id rproc_slate_match_table[] = {
	{.compatible = "qcom,rproc-slate"},
	{}
};
MODULE_DEVICE_TABLE(of, rproc_slate_match_table);

static struct platform_driver rproc_slate_driver = {
	.probe = rproc_slate_driver_probe,
	.remove = rproc_slate_driver_remove,
	.driver = {
		.name = "qcom-rproc-slate",
		.of_match_table = rproc_slate_match_table,
		.pm = &rproc_slate_pm_ops,
	},
};

module_platform_driver(rproc_slate_driver);
MODULE_DESCRIPTION("Support for booting QTI slate SoC");
MODULE_LICENSE("GPL v2");
