// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2013-2015, 2017-2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
 */

#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/reboot.h>
#include <linux/of.h>
#include <linux/esoc_client.h>
#include "esoc-mdm.h"
#include "mdm-dbg.h"

/* Default number of powerup trial requests per session */
#define ESOC_DEF_PON_REQ	3
#define ESOC_MAX_PON_TRIES	5
#define BOOT_FAIL_ACTION_DEF	BOOT_FAIL_ACTION_PANIC
#define S3_RESET_DELAY_MS	1000

enum esoc_pon_state {
	PON_INIT,
	PON_SUCCESS,
	PON_RETRY,
	PON_FAIL
};

enum {
	PWR_OFF = 0x1,
	SHUTDOWN,
	RESET,
	PEER_CRASH,
	IN_DEBUG,
	CRASH,
	PWR_ON,
	BOOT,
	RUN,
};

struct mdm_drv_data {
	const char *fw;
};

struct mdm_drv {
	unsigned int mode;
	struct esoc_eng cmd_eng;
	struct completion pon_done;
	struct completion ssr_ready;
	struct completion req_eng_wait;
	struct esoc_clink *esoc_clink;
	enum esoc_pon_state pon_state;
	struct workqueue_struct *mdm_queue;
	struct work_struct ssr_work;
	struct notifier_block esoc_restart;
	struct mutex poff_lock;
	atomic_t boot_fail_action;
	atomic_t n_pon_tries;
};
#define to_mdm_drv(d) container_of(d, struct mdm_drv, cmd_eng)

static void esoc_client_link_power_off(struct esoc_clink *esoc_clink, unsigned int flags);
static void esoc_client_link_mdm_crash(struct esoc_clink *esoc_clink);

int esoc_set_boot_fail_action(struct esoc_clink *esoc_clink, u32 action)
{
	struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);

	if (action >= BOOT_FAIL_ACTION_LAST) {
		esoc_mdm_log("Unknown boot fail action requested: %u\n", action);
		return -EINVAL;
	}

	if (!mdm_drv) {
		esoc_mdm_log("esoc-mdm driver not present\n");
		return -EAGAIN;
	}

	atomic_set(&mdm_drv->boot_fail_action, action);
	esoc_mdm_log("Boot fail action configured to %u\n", action);

	return 0;
}

int esoc_set_n_pon_tries(struct esoc_clink *esoc_clink, u32 n_tries)
{
	struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);

	if (n_tries > ESOC_MAX_PON_TRIES) {
		esoc_mdm_log("Num PON tries requested (%u) is over the limit: %u\n", n_tries,
			     ESOC_MAX_PON_TRIES);
	}

	if (!mdm_drv) {
		esoc_mdm_log("esoc-mdm driver not present\n");
		return -EAGAIN;
	}

	atomic_set(&mdm_drv->n_pon_tries, n_tries);
	esoc_mdm_log("Num PON tries configured to %u\n", n_tries);

	return 0;
}

static int esoc_msm_restart_handler(struct notifier_block *nb, unsigned long action, void *data)
{
	struct mdm_drv *mdm_drv = container_of(nb, struct mdm_drv, esoc_restart);
	struct esoc_clink *esoc_clink = mdm_drv->esoc_clink;
	const struct esoc_clink_ops *const clink_ops = esoc_clink->clink_ops;

	if (action == SYS_RESTART) {
		if (mdm_dbg_stall_notify(ESOC_PRIMARY_REBOOT))
			return NOTIFY_OK;
		mutex_lock(&mdm_drv->poff_lock);
		if (mdm_drv->mode == PWR_OFF) {
			esoc_mdm_log("Reboot notifier: mdm already powered-off\n");
			mutex_unlock(&mdm_drv->poff_lock);
			return NOTIFY_OK;
		}
		esoc_client_link_power_off(esoc_clink, ESOC_HOOK_MDM_DOWN);
		esoc_mdm_log("Reboot notifier: Notifying esoc of cold reboot\n");
		dev_dbg(&esoc_clink->dev, "Notifying esoc of cold reboot\n");
		clink_ops->notify(ESOC_PRIMARY_REBOOT, esoc_clink);
		mdm_drv->mode = PWR_OFF;
		mutex_unlock(&mdm_drv->poff_lock);
	}
	return NOTIFY_OK;
}
static void mdm_handle_clink_evt(enum esoc_evt evt, struct esoc_eng *eng)
{
	struct mdm_drv *mdm_drv = to_mdm_drv(eng);
	bool unexpected_state = false;

	switch (evt) {
	case ESOC_INVALID_STATE:
		esoc_mdm_log("ESOC_INVALID_STATE: Calling complete with state: PON_FAIL\n");
		mdm_drv->pon_state = PON_FAIL;
		complete(&mdm_drv->pon_done);
		complete(&mdm_drv->ssr_ready);
		break;
	case ESOC_BOOT_STATE:
		if (mdm_drv->mode == PWR_OFF) {
			esoc_mdm_log("ESOC_BOOT_STATE: Observed status high from modem.\n");
			mdm_drv->mode = BOOT;
		}
		break;
	case ESOC_RUN_STATE:
		esoc_mdm_log("ESOC_RUN_STATE: Calling complete with state: PON_SUCCESS\n");
		mdm_drv->pon_state = PON_SUCCESS;
		mdm_drv->mode = RUN;
		complete(&mdm_drv->pon_done);
		complete(&mdm_drv->ssr_ready);
		break;
	case ESOC_RETRY_PON_EVT:
		esoc_mdm_log("ESOC_RETRY_PON_EVT: Calling complete with state: PON_RETRY\n");
		mdm_drv->pon_state = PON_RETRY;
		complete(&mdm_drv->pon_done);
		complete(&mdm_drv->ssr_ready);
		break;
	case ESOC_UNEXPECTED_RESET:
		esoc_mdm_log("evt_state: ESOC_UNEXPECTED_RESET\n");
		unexpected_state = true;
	case ESOC_ERR_FATAL:
		if (!unexpected_state)
			esoc_mdm_log("evt_state: ESOC_ERR_FATAL\n");

		/*
		 * Ignore all modem errfatals if the status is not up
		 * or modem in run state.
		 */
		if (mdm_drv->mode <= CRASH) {
			esoc_mdm_log("Modem in crash state or not booted. Ignoring.\n");
			return;
		}
		esoc_mdm_log("Setting crash flag\n");
		mdm_drv->mode = CRASH;
		queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work);
		break;
	case ESOC_REQ_ENG_ON:
		esoc_mdm_log("evt_state: ESOC_REQ_ENG_ON; Registered a req engine\n");
		complete(&mdm_drv->req_eng_wait);
		break;
	default:
		break;
	}
}

static void mdm_ssr_fn(struct work_struct *work)
{
	struct mdm_drv *mdm_drv = container_of(work, struct mdm_drv, ssr_work);
	struct mdm_ctrl *mdm = get_esoc_clink_data(mdm_drv->esoc_clink);

	/* Wait for pon to complete. Start SSR only if pon is success */
	wait_for_completion(&mdm_drv->ssr_ready);
	if (mdm_drv->pon_state != PON_SUCCESS) {
		esoc_mdm_log("Got errfatal but ignoring as boot failed\n");
		return;
	}

	esoc_client_link_mdm_crash(mdm_drv->esoc_clink);
	mdm_wait_for_status_low(mdm, false);
	esoc_mdm_log("Starting SSR work\n");

	/*
	 * If restarting esoc fails, the SSR framework triggers a kernel panic
	 */
	esoc_clink_request_ssr(mdm_drv->esoc_clink);
}

static void esoc_client_link_power_on(struct esoc_clink *esoc_clink, unsigned int flags)
{
	int i;
	struct esoc_client_hook *client_hook;

	dev_dbg(&esoc_clink->dev, "Calling power_on hooks\n");
	esoc_mdm_log("Calling power_on hooks with flags: 0x%x\n", flags);

	for (i = 0; i < ESOC_MAX_HOOKS; i++) {
		client_hook = esoc_clink->client_hook[i];
		if (client_hook && client_hook->esoc_link_power_on)
			client_hook->esoc_link_power_on(client_hook->priv, flags);
	}
}

static void esoc_client_link_power_off(struct esoc_clink *esoc_clink, unsigned int flags)
{
	int i;
	struct esoc_client_hook *client_hook;

	dev_dbg(&esoc_clink->dev, "Calling power_off hooks\n");
	esoc_mdm_log("Calling power_off hooks with flags: 0x%x\n", flags);

	for (i = 0; i < ESOC_MAX_HOOKS; i++) {
		client_hook = esoc_clink->client_hook[i];
		if (client_hook && client_hook->esoc_link_power_off)
			client_hook->esoc_link_power_off(client_hook->priv, flags);
	}
}

static void esoc_client_link_mdm_crash(struct esoc_clink *esoc_clink)
{
	int i;
	struct esoc_client_hook *client_hook;

	dev_dbg(&esoc_clink->dev, "Calling mdm_crash hooks\n");
	esoc_mdm_log("Calling mdm_crash hooks\n");

	for (i = 0; i < ESOC_MAX_HOOKS; i++) {
		client_hook = esoc_clink->client_hook[i];
		if (client_hook && client_hook->esoc_link_mdm_crash)
			client_hook->esoc_link_mdm_crash(client_hook->priv);
	}
}

static unsigned long mdm_rproc_panic(struct rproc *rproc)
{
	struct esoc_clink *esoc_clink = container_of(rproc->dev.parent, struct esoc_clink, dev);
	const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;

	esoc_mdm_log("MDM crashed notification from SSR\n");

	if (mdm_dbg_stall_notify(ESOC_PRIMARY_CRASH))
		return 0;
	clink_ops->notify(ESOC_PRIMARY_CRASH, esoc_clink);
	return 0;
}

static int mdm_rproc_shutdown(struct rproc *rproc)
{
	int ret = 0;
	struct esoc_clink *esoc_clink = container_of(rproc->dev.parent, struct esoc_clink, dev);
	struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
	const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
	struct mdm_ctrl *mdm = get_esoc_clink_data(mdm_drv->esoc_clink);

	esoc_mdm_log("Shutdown request from SSR\n");

	mutex_lock(&mdm_drv->poff_lock);
	if (mdm_drv->mode == CRASH || mdm_drv->mode == PEER_CRASH) {
		esoc_mdm_log("Shutdown in crash mode\n");
		mdm_wait_for_status_low(mdm, false);
		if (mdm_dbg_stall_cmd(ESOC_PREPARE_DEBUG)) {
			/* We want to mask debug command.
			 * In this case return success
			 * to move to next stage
			 */
			goto unlock;
		}

		esoc_clink_queue_request(ESOC_REQ_CRASH_SHUTDOWN, esoc_clink);
		esoc_client_link_power_off(esoc_clink, ESOC_HOOK_MDM_CRASH);

		esoc_mdm_log("Executing the ESOC_PREPARE_DEBUG command\n");
		ret = clink_ops->cmd_exe(ESOC_PREPARE_DEBUG, esoc_clink);
		if (ret) {
			esoc_mdm_log("ESOC_PREPARE_DEBUG command failed\n");
			dev_err(&esoc_clink->dev, "failed to enter debug\n");
			goto unlock;
		}
		mdm_drv->mode = IN_DEBUG;
	} else {
		esoc_mdm_log("Graceful shutdown mode\n");
		if (mdm_drv->mode == PWR_OFF) {
			esoc_mdm_log("mdm already powered-off\n");
			goto unlock;
		}
		if (!qcom_sysmon_shutdown_acked(esoc_clink->rproc_sysmon)) {
			esoc_mdm_log(
			"Executing the ESOC_FORCE_PWR_OFF command\n");
			ret = clink_ops->cmd_exe(ESOC_FORCE_PWR_OFF,
							esoc_clink);
		} else {
			if (mdm_dbg_stall_cmd(ESOC_PWR_OFF)) {
				/* Since power off command is masked
				 * we return success, and leave the state
				 * of the command engine as is.
				 */
				goto unlock;
			}
			dev_dbg(&esoc_clink->dev, "Sending sysmon-shutdown\n");
			esoc_mdm_log("Executing the ESOC_PWR_OFF command\n");
			ret = clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink);
		}
		if (ret) {
			esoc_mdm_log(
			"Executing the ESOC_PWR_OFF command failed\n");
			dev_err(&esoc_clink->dev, "failed to exe power off\n");
			goto unlock;
		}
		esoc_client_link_power_off(esoc_clink, ESOC_HOOK_MDM_DOWN);
		/* Pull the reset line low to turn off the device */
		clink_ops->cmd_exe(ESOC_FORCE_PWR_OFF, esoc_clink);
		mdm_drv->mode = PWR_OFF;
	}
	esoc_mdm_log("Shutdown completed\n");

unlock:
	mutex_unlock(&mdm_drv->poff_lock);
	return ret;
}

static void mdm_subsys_retry_powerup_cleanup(struct esoc_clink *esoc_clink, unsigned int poff_flags)
{
	struct mdm_ctrl *mdm = get_esoc_clink_data(esoc_clink);
	struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);

	esoc_mdm_log("Doing cleanup\n");

	esoc_client_link_power_off(esoc_clink, poff_flags);
	mdm_disable_irqs(mdm);
	mdm_drv->pon_state = PON_INIT;
	mdm_drv->mode = PWR_OFF;
	reinit_completion(&mdm_drv->pon_done);
	reinit_completion(&mdm_drv->ssr_ready);
	reinit_completion(&mdm_drv->req_eng_wait);
}

/* Returns 0 to proceed towards another retry, or an error code to quit */
static int mdm_handle_boot_fail(struct esoc_clink *esoc_clink, u8 *pon_trial)
{
	struct mdm_ctrl *mdm = get_esoc_clink_data(esoc_clink);
	struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);

	if (*pon_trial == atomic_read(&mdm_drv->n_pon_tries)) {
		esoc_mdm_log("Reached max. number of boot trials\n");
		atomic_set(&mdm_drv->boot_fail_action, BOOT_FAIL_ACTION_PANIC);
	}

	switch (atomic_read(&mdm_drv->boot_fail_action)) {
	case BOOT_FAIL_ACTION_RETRY:
		mdm_subsys_retry_powerup_cleanup(esoc_clink, 0);
		esoc_mdm_log("Request to retry a warm reset\n");
		(*pon_trial)++;
		break;
	/*
	 * Issue a shutdown here and rerun the powerup again.
	 * This way it becomes a cold reset. Else, we end up
	 * issuing a cold reset & a warm reset back to back.
	 */
	case BOOT_FAIL_ACTION_COLD_RESET:
		mdm_subsys_retry_powerup_cleanup(esoc_clink, ESOC_HOOK_MDM_DOWN);
		esoc_mdm_log("Doing cold reset by power-down and warm reset\n");
		(*pon_trial)++;
		mdm_power_down(mdm);
		break;
	case BOOT_FAIL_ACTION_S3_RESET:
		mdm_subsys_retry_powerup_cleanup(esoc_clink, ESOC_HOOK_MDM_DOWN);
		esoc_mdm_log("Doing an S3 reset\n");
		(*pon_trial)++;
		mdm_power_down(mdm);
		msleep(S3_RESET_DELAY_MS);
		break;
	case BOOT_FAIL_ACTION_PANIC:
		esoc_mdm_log("Calling panic!!\n");
		panic("Panic requested on external modem boot failure\n");
		break;
	case BOOT_FAIL_ACTION_NOP:
		esoc_mdm_log("Leaving the modem in its curent state\n");
		mdm_drv->mode = PWR_OFF;
		return -EIO;
	case BOOT_FAIL_ACTION_SHUTDOWN:
	default:
		mdm_subsys_retry_powerup_cleanup(esoc_clink, ESOC_HOOK_MDM_DOWN);
		esoc_mdm_log("Shutdown the modem and quit\n");
		mdm_power_down(mdm);
		return -EIO;
	}

	return 0;
}

static int mdm_rproc_powerup(struct rproc *rproc)
{
	int ret;
	struct esoc_clink *esoc_clink = container_of(rproc->dev.parent, struct esoc_clink, dev);
	struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
	const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
	int timeout = INT_MAX;
	u8 pon_trial = 0;

	esoc_mdm_log("Powerup request from SSR\n");

	do {
		esoc_mdm_log("Boot trial: %d\n", pon_trial);
		if (!esoc_clink->auto_boot &&
			!esoc_req_eng_enabled(esoc_clink)) {
			esoc_mdm_log("Wait for req eng registration\n");
			dev_dbg(&esoc_clink->dev, "Wait for req eng registration\n");
			wait_for_completion(&mdm_drv->req_eng_wait);
		}
		esoc_mdm_log("Req eng available\n");
		if (mdm_drv->mode == PWR_OFF) {
			esoc_mdm_log("In normal power-on mode\n");
			if (mdm_dbg_stall_cmd(ESOC_PWR_ON))
				return -EBUSY;
			esoc_mdm_log("Executing the ESOC_PWR_ON command\n");
			ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
			if (ret) {
				esoc_mdm_log("ESOC_PWR_ON command failed\n");
				dev_err(&esoc_clink->dev, "pwr on fail\n");
				return ret;
			}
			esoc_client_link_power_on(esoc_clink, 0);
		} else if (mdm_drv->mode == IN_DEBUG) {
			esoc_mdm_log("In SSR power-on mode\n");
			esoc_mdm_log("Executing the ESOC_EXIT_DEBUG command\n");
			ret = clink_ops->cmd_exe(ESOC_EXIT_DEBUG, esoc_clink);
			if (ret) {
				esoc_mdm_log(
				"ESOC_EXIT_DEBUG command failed\n");
				dev_err(&esoc_clink->dev, "cannot exit debug mode\n");
				return ret;
			}
			mdm_drv->mode = PWR_OFF;
			esoc_mdm_log("Executing the ESOC_PWR_ON command\n");
			ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
			if (ret) {
				dev_err(&esoc_clink->dev, "pwr on fail\n");
				return ret;
			}
			esoc_client_link_power_on(esoc_clink, ESOC_HOOK_MDM_CRASH);
		}

		/*
		 * In autoboot case, it is possible that we can forever wait for
		 * boot completion, when esoc fails to boot. This is because
		 * there is no helper application which can alert esoc driver
		 * about boot failure. Prevent going to wait forever in such
		 * case.
		 */
		if (esoc_clink->auto_boot)
			timeout = 10 * HZ;
		esoc_mdm_log("Modem turned-on. Waiting for pon_done notification..\n");
		ret = wait_for_completion_timeout(&mdm_drv->pon_done, msecs_to_jiffies(timeout));
		if (mdm_drv->pon_state == PON_FAIL || ret <= 0) {
			dev_err(&esoc_clink->dev, "booting failed\n");
			esoc_mdm_log("booting failed\n");
			ret = mdm_handle_boot_fail(esoc_clink, &pon_trial);
			if (ret)
				return ret;
		} else if (mdm_drv->pon_state == PON_RETRY) {
			esoc_mdm_log("Boot failed. Doing cleanup and attempting to retry\n");
			mdm_subsys_retry_powerup_cleanup(esoc_clink, 0);
		} else if (mdm_drv->pon_state == PON_SUCCESS) {
			break;
		}
	} while (pon_trial <= atomic_read(&mdm_drv->n_pon_tries));

	return 0;
}

static int mdm_rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
{
	return 0;
}

static int mdm_register_ssr(struct esoc_clink *esoc_clink)
{

	esoc_clink->ops.stop = mdm_rproc_shutdown;
	esoc_clink->ops.start = mdm_rproc_powerup;
	esoc_clink->ops.panic = mdm_rproc_panic;
	esoc_clink->ops.load = mdm_rproc_elf_load_segments;
	return esoc_clink_register_rproc(esoc_clink);
}

int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
{
	int ret;
	struct mdm_drv *mdm_drv;
	struct mdm_drv_data *data;
	struct esoc_eng *esoc_eng;

	data = esoc_device_get_match_data(&esoc_clink->dev);
	if (IS_ERR(data)) {
		dev_err(&esoc_clink->dev, "esoc match data failed: %d\n", PTR_ERR(data));
		return PTR_ERR(data);
	}

	esoc_clink->fw = data->fw;

	mdm_drv = devm_kzalloc(&esoc_clink->dev, sizeof(*mdm_drv), GFP_KERNEL);
	if (IS_ERR_OR_NULL(mdm_drv))
		return PTR_ERR(mdm_drv);
	esoc_eng = &mdm_drv->cmd_eng;
	esoc_eng->handle_clink_evt = mdm_handle_clink_evt;
	ret = esoc_clink_register_cmd_eng(esoc_clink, esoc_eng);
	if (ret) {
		dev_err(&esoc_clink->dev, "failed to register cmd engine\n");
		return ret;
	}
	mutex_init(&mdm_drv->poff_lock);
	ret = mdm_register_ssr(esoc_clink);
	if (ret)
		goto ssr_err;
	mdm_drv->mdm_queue = alloc_workqueue("mdm_drv_queue", 0, 0);
	if (!mdm_drv->mdm_queue) {
		dev_err(&esoc_clink->dev, "could not create mdm_queue\n");
		goto queue_err;
	}
	esoc_set_drv_data(esoc_clink, mdm_drv);
	init_completion(&mdm_drv->pon_done);
	init_completion(&mdm_drv->ssr_ready);
	init_completion(&mdm_drv->req_eng_wait);
	INIT_WORK(&mdm_drv->ssr_work, mdm_ssr_fn);
	mdm_drv->esoc_clink = esoc_clink;
	mdm_drv->mode = PWR_OFF;
	mdm_drv->pon_state = PON_INIT;
	atomic_set(&mdm_drv->boot_fail_action, BOOT_FAIL_ACTION_DEF);
	atomic_set(&mdm_drv->n_pon_tries, ESOC_DEF_PON_REQ);
	mdm_drv->esoc_restart.notifier_call = esoc_msm_restart_handler;
	ret = register_reboot_notifier(&mdm_drv->esoc_restart);
	if (ret)
		dev_err(&esoc_clink->dev, "register for reboot failed\n");

	ret = register_dbg_req_eng(esoc_clink);
	if (ret)
		dev_err(&esoc_clink->dev, "Failed to register esoc dbg req eng\n");
	return 0;
queue_err:
	esoc_clink_unregister_rproc(esoc_clink);
ssr_err:
	esoc_clink_unregister_cmd_eng(esoc_clink, esoc_eng);
	return ret;
}

int esoc_ssr_remove(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
{
	int ret;
	struct mdm_drv *mdm_drv;

	mdm_drv = esoc_get_drv_data(esoc_clink);
	esoc_set_drv_data(esoc_clink, NULL);
	esoc_clink_unregister_cmd_eng(esoc_clink, &mdm_drv->cmd_eng);
	esoc_clink_unregister_rproc(esoc_clink);
	if (mdm_drv->mdm_queue)
		destroy_workqueue(mdm_drv->mdm_queue);
	ret = unregister_reboot_notifier(&mdm_drv->esoc_restart);
	if (ret)
		dev_err(&esoc_clink->dev, "unregister reboot notifier failed\n");

	unregister_dbg_req_eng(esoc_clink);

	return 0;
}

struct mdm_drv_data mdm_9x55 = {
	.fw = "mdm9x55/sbl1.mbn",
};

struct mdm_drv_data sdx_50m = {
	.fw = "sdx50m/sbl1.mbn",
};

struct mdm_drv_data sdx_55m = {
	.fw = "sdx55m/sbl1.mbn",
};

struct mdm_drv_data sdx_lemur = {
	.fw = "sdx65m/xbl.elf",
};

struct mdm_drv_data sdx_pinn = {
	.fw = "sdx75m/xbl_s.melf",
};

struct mdm_drv_data sdx_baagha = {
	.fw = "sdx35m/xbl.elf",
};

struct esoc_compat compat_table[] = {
	{
		.name = "MDM9x55",
		.data = &mdm_9x55,
	},
	{
		.name = "SDX50M",
		.data = &sdx_50m,
	},
	{
		.name = "SDX55M",
		.data = &sdx_55m,
	},
	{
		.name = "SDXLEMUR",
		.data = &sdx_lemur,
	},
	{
		.name = "SDXPINN",
		.data = &sdx_pinn,
	},
	{
		.name = "SDXBAAGHA",
		.data = &sdx_baagha,
	},
};

static struct esoc_drv esoc_ssr_drv = {
	.owner = THIS_MODULE,
	.probe = esoc_ssr_probe,
	.remove = esoc_ssr_remove,
	.compat_table = compat_table,
	.compat_entries = ARRAY_SIZE(compat_table),
	.driver = {
		.name = "mdm-4x",
	},
};

int __init mdm_drv_init(void)
{
	int ret;

	ret = mdm_dbg_eng_init();
	if (ret) {
		debug_init_done = false;
		pr_err("esoc dbg engine failure\n");
	} else {
		debug_init_done = true;
		pr_debug("esoc dbg engine initialized\n");
	}

	ret = esoc_driver_register(&esoc_ssr_drv);
	if (ret)
		pr_err("esoc ssr driver registration failed\n");

	return ret;
}

void __exit mdm_drv_exit(void)
{
	esoc_driver_unregister(&esoc_ssr_drv);
	mdm_dbg_eng_exit();
}
