Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e4df586 authored by Hanumant Singh's avatar Hanumant Singh Committed by Gerrit - the friendly Code Review server
Browse files

esoc: mdm-4x: Refactor implementation to separate out PON



Separate out Power handling logic of external modems.
Most external modems will differ only in how you power on,
warm reset, and cold reset the modem. The IPC logic can
be reused.

Change-Id: I9e5f16a5481a8aa9bfaf7e1482d50563354f661b
Signed-off-by: default avatarHanumant Singh <hanumant@codeaurora.org>
parent 50e0173f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4,5 +4,5 @@ ccflags-$(CONFIG_ESOC_DEBUG) := -DDEBUG
obj-$(CONFIG_ESOC)	+= esoc_bus.o
obj-$(CONFIG_ESOC_DEV)	+= esoc_dev.o
obj-$(CONFIG_ESOC_CLIENT)	+= esoc_client.o
obj-$(CONFIG_ESOC_MDM_4x)	+= esoc-mdm-4x.o
obj-$(CONFIG_ESOC_MDM_4x)	+= esoc-mdm-pon.o esoc-mdm-4x.o
obj-$(CONFIG_ESOC_MDM_DRV)	+= esoc-mdm-drv.o
+38 −192
Original line number Diff line number Diff line
@@ -12,58 +12,9 @@

#include <linux/coresight.h>
#include <linux/coresight-cti.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <soc/qcom/sysmon.h>
#include "esoc.h"

#define MDM_PBLRDY_CNT			20
#define INVALID_GPIO			(-1)
#define MDM_GPIO(mdm, i)		(mdm->gpios[i])
#define MDM9x25_LABEL			"MDM9x25"
#define MDM9x25_HSIC			"HSIC"
#define MDM9x35_LABEL			"MDM9x35"
#define MDM9x35_PCIE			"PCIe"
#define MDM9x35_DUAL_LINK		"HSIC+PCIe"
#define MDM9x35_HSIC			"HSIC"
#define MDM2AP_STATUS_TIMEOUT_MS	120000L
#define MDM_MODEM_TIMEOUT		3000
#define DEF_RAMDUMP_TIMEOUT		120000
#define DEF_RAMDUMP_DELAY		2000
#define RD_BUF_SIZE			100
#define SFR_MAX_RETRIES			10
#define SFR_RETRY_INTERVAL		1000
#define MDM_DBG_OFFSET			0x934
#define MDM_DBG_MODE			0x53444247
#define MDM_CTI_NAME			"coresight-cti-rpm-cpu0"
#define MDM_CTI_TRIG			0
#define MDM_CTI_CH			0

enum mdm_gpio {
	AP2MDM_WAKEUP = 0,
	AP2MDM_STATUS,
	AP2MDM_SOFT_RESET,
	AP2MDM_VDD_MIN,
	AP2MDM_CHNLRDY,
	AP2MDM_ERRFATAL,
	AP2MDM_VDDMIN,
	AP2MDM_PMIC_PWR_EN,
	MDM2AP_WAKEUP,
	MDM2AP_ERRFATAL,
	MDM2AP_PBLRDY,
	MDM2AP_STATUS,
	MDM2AP_VDDMIN,
	MDM_LINK_DETECT,
	NUM_GPIOS,
};
#include "esoc-mdm.h"

enum gpio_update_config {
	GPIO_UPDATE_BOOTING_CONFIG = 1,
@@ -76,48 +27,6 @@ enum irq_mask {
	IRQ_PBLRDY = 0x4,
};

struct mdm_ctrl {
	unsigned gpios[NUM_GPIOS];
	spinlock_t status_lock;
	struct workqueue_struct *mdm_queue;
	struct delayed_work mdm2ap_status_check_work;
	struct work_struct mdm_status_work;
	struct work_struct restart_reason_work;
	struct completion debug_done;
	struct device *dev;
	struct pinctrl *pinctrl;
	struct pinctrl_state *gpio_state_booting;
	struct pinctrl_state *gpio_state_running;
	struct pinctrl_state *gpio_state_active;
	struct pinctrl_state *gpio_state_suspend;
	int mdm2ap_status_valid_old_config;
	int soft_reset_inverted;
	int errfatal_irq;
	int status_irq;
	int pblrdy_irq;
	int debug;
	int init;
	bool debug_fail;
	unsigned int dump_timeout_ms;
	unsigned int ramdump_delay_ms;
	struct esoc_clink *esoc;
	bool get_restart_reason;
	unsigned long irq_mask;
	bool ready;
	bool dual_interface;
	u32 status;
	void __iomem *dbg_addr;
	bool dbg_mode;
	struct coresight_cti *cti;
	int trig_cnt;
};

struct mdm_ops {
	struct esoc_clink_ops *clink_ops;
	int (*config_hw)(struct mdm_ctrl *mdm,
				const struct esoc_clink_ops const *ops,
						struct platform_device *pdev);
};

static struct gpio_map {
	const char *name;
@@ -143,7 +52,6 @@ static const int required_gpios[] = {
	AP2MDM_ERRFATAL,
	MDM2AP_STATUS,
	AP2MDM_STATUS,
	AP2MDM_SOFT_RESET
};

static void mdm_debug_gpio_show(struct mdm_ctrl *mdm)
@@ -263,66 +171,6 @@ static void mdm_trigger_dbg(struct mdm_ctrl *mdm)
	}
}

/* This function can be called from atomic context. */
static void mdm_toggle_soft_reset(struct mdm_ctrl *mdm)
{
	int soft_reset_direction_assert = 0,
	    soft_reset_direction_de_assert = 1;

	if (mdm->soft_reset_inverted) {
		soft_reset_direction_assert = 1;
		soft_reset_direction_de_assert = 0;
	}
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			soft_reset_direction_assert);
	/*
	 * Allow PS hold assert to be detected
	 */
	usleep_range(8000, 9000);
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			soft_reset_direction_de_assert);
}

static void mdm_do_first_power_on(struct mdm_ctrl *mdm)
{
	int i;
	int pblrdy;
	struct device *dev = mdm->dev;

	dev_dbg(dev, "Powering on modem for the first time\n");
	mdm_toggle_soft_reset(mdm);
	/* Add a delay to allow PON sequence to complete*/
	msleep(50);
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1);
	for (i = 0; i  < MDM_PBLRDY_CNT; i++) {
		pblrdy = gpio_get_value(MDM_GPIO(mdm, MDM2AP_PBLRDY));
		if (pblrdy)
			break;
		usleep_range(5000, 6000);
	}
	dev_dbg(dev, "pblrdy i:%d\n", i);
	msleep(200);
}

static void mdm_power_down(struct mdm_ctrl *mdm)
{
	struct device *dev = mdm->dev;
	int soft_reset_direction = mdm->soft_reset_inverted ? 1 : 0;
	/* Assert the soft reset line whether mdm2ap_status went low or not */
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
					soft_reset_direction);
	dev_dbg(dev, "Doing a hard reset\n");
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
						soft_reset_direction);
	/*
	* Currently, there is a debounce timer on the charm PMIC. It is
	* necessary to hold the PMIC RESET low for 400ms
	* for the reset to fully take place. Sleep here to ensure the
	* reset has occured before the function exits.
	*/
	msleep(400);
}

static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc)
{
	unsigned long end_time;
@@ -391,7 +239,7 @@ force_poff:
		mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG);
		break;
	case ESOC_RESET:
		mdm_toggle_soft_reset(mdm);
		mdm_toggle_soft_reset(mdm, false);
		break;
	case ESOC_PREPARE_DEBUG:
		/*
@@ -408,7 +256,7 @@ force_poff:
	case ESOC_EXE_DEBUG:
		mdm->debug = 1;
		mdm->trig_cnt = 0;
		mdm_toggle_soft_reset(mdm);
		mdm_toggle_soft_reset(mdm, false);
		/*
		 * wait for ramdumps to be collected
		 * then power down the mdm and switch gpios to booting
@@ -506,7 +354,7 @@ static void mdm_notify(enum esoc_notify notify, struct esoc_clink *esoc)
		break;
	case ESOC_IMG_XFER_RETRY:
		mdm->init = 1;
		mdm_toggle_soft_reset(mdm);
		mdm_toggle_soft_reset(mdm, false);
		break;
	case ESOC_IMG_XFER_FAIL:
		esoc_clink_evt_notify(ESOC_INVALID_STATE, esoc);
@@ -546,25 +394,14 @@ static void mdm_notify(enum esoc_notify notify, struct esoc_clink *esoc)
		if (!status_down) {
			dev_err(mdm->dev, "%s MDM2AP status did not go low\n",
								__func__);
			gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
					      !!mdm->soft_reset_inverted);
			/*
			 * allow PS hold assert to be detected.
			 * pmic requires 6ms for crash reset case.
			 */
			mdelay(6);
			gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
					      !mdm->soft_reset_inverted);
			mdm_toggle_soft_reset(mdm, true);
		}
		break;
	case ESOC_PRIMARY_REBOOT:
		dev_dbg(mdm->dev, "Triggering mdm cold reset");
		mdm->ready = 0;
		gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
				!!mdm->soft_reset_inverted);
		mdelay(300);
		gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
				!mdm->soft_reset_inverted);
		mdm_disable_irqs(mdm);
		mdm->debug = 0;
		mdm->ready = false;
		mdm_cold_reset(mdm);
		break;
	};
	return;
@@ -699,7 +536,6 @@ static int mdm_dt_parse_gpios(struct mdm_ctrl *mdm)
{
	int i, val, rc = 0;
	struct device_node *node = mdm->dev->of_node;
	enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;

	for (i = 0; i < NUM_GPIOS; i++)
		mdm->gpios[i] = INVALID_GPIO;
@@ -710,13 +546,6 @@ static int mdm_dt_parse_gpios(struct mdm_ctrl *mdm)
			MDM_GPIO(mdm, gpio_map[i].index) = val;
	}
	/* These two are special because they can be inverted. */
	val = of_get_named_gpio_flags(node, "qcom,ap2mdm-soft-reset-gpio",
						0, &flags);
	if (val >= 0) {
		MDM_GPIO(mdm, AP2MDM_SOFT_RESET) = val;
		if (flags & OF_GPIO_ACTIVE_LOW)
			mdm->soft_reset_inverted = 1;
	}
	/* Verify that the required gpios have valid values */
	for (i = 0; i < ARRAY_SIZE(required_gpios); i++) {
		if (MDM_GPIO(mdm, required_gpios[i]) == INVALID_GPIO) {
@@ -767,13 +596,6 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev)
			goto fatal_err;
		}
	}
	if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET))) {
		if (gpio_request(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
					 "AP2MDM_SOFT_RESET")) {
			dev_err(dev, "Cannot config AP2MDM_SOFT_RESET gpio\n");
			goto fatal_err;
		}
	}
	if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_WAKEUP))) {
		if (gpio_request(MDM_GPIO(mdm, AP2MDM_WAKEUP),
					"AP2MDM_WAKEUP")) {
@@ -909,13 +731,16 @@ err_state_active:
	return retval;
}
static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
					struct esoc_clink_ops const *ops,
					const struct mdm_ops *ops,
					struct platform_device *pdev)
{
	int ret;
	struct esoc_clink *esoc;
	const struct esoc_clink_ops *const clink_ops = ops->clink_ops;
	const struct mdm_pon_ops *pon_ops = ops->pon_ops;

	mdm->dev = &pdev->dev;
	mdm->pon_ops = pon_ops;
	esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
	if (IS_ERR(esoc)) {
		dev_err(mdm->dev, "cannot allocate esoc device\n");
@@ -932,10 +757,18 @@ static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
	if (ret)
		return ret;
	dev_err(mdm->dev, "parsing gpio done\n");
	ret = mdm_pon_dt_init(mdm);
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "pon dt init done\n");
	ret = mdm_pinctrl_init(mdm);
	if (ret)
		return ret;
	dev_err(mdm->dev, "pinctrl init done\n");
	ret = mdm_pon_setup(mdm);
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "pon setup done\n");
	ret = mdm_configure_ipc(mdm, pdev);
	if (ret)
		return ret;
@@ -943,7 +776,7 @@ static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
	dev_err(mdm->dev, "ipc configure done\n");
	esoc->name = MDM9x25_LABEL;
	esoc->link_name = MDM9x25_HSIC;
	esoc->clink_ops = ops;
	esoc->clink_ops = clink_ops;
	esoc->parent = mdm->dev;
	esoc->owner = THIS_MODULE;
	esoc->np = pdev->dev.of_node;
@@ -966,14 +799,17 @@ static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
}

static int mdm9x35_setup_hw(struct mdm_ctrl *mdm,
					struct esoc_clink_ops const *ops,
					const struct mdm_ops *ops,
					struct platform_device *pdev)
{
	int ret;
	struct device_node *node;
	struct esoc_clink *esoc;
	const struct esoc_clink_ops *const clink_ops = ops->clink_ops;
	const struct mdm_pon_ops *pon_ops = ops->pon_ops;

	mdm->dev = &pdev->dev;
	mdm->pon_ops = pon_ops;
	node = pdev->dev.of_node;
	esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
	if (IS_ERR(esoc)) {
@@ -991,10 +827,18 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm,
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "parsing gpio done\n");
	ret = mdm_pon_dt_init(mdm);
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "pon dt init done\n");
	ret = mdm_pinctrl_init(mdm);
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "pinctrl init done\n");
	ret = mdm_pon_setup(mdm);
	if (ret)
		return ret;
	dev_dbg(mdm->dev, "pon setup done\n");
	ret = mdm_configure_ipc(mdm, pdev);
	if (ret)
		return ret;
@@ -1020,7 +864,7 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm,
		esoc->link_name = MDM9x35_DUAL_LINK;
	else
		esoc->link_name = MDM9x35_HSIC;
	esoc->clink_ops = ops;
	esoc->clink_ops = clink_ops;
	esoc->parent = mdm->dev;
	esoc->owner = THIS_MODULE;
	esoc->np = pdev->dev.of_node;
@@ -1051,11 +895,13 @@ static struct esoc_clink_ops mdm_cops = {
static struct mdm_ops mdm9x25_ops = {
	.clink_ops = &mdm_cops,
	.config_hw = mdm9x25_setup_hw,
	.pon_ops = &mdm9x25_pon_ops,
};

static struct mdm_ops mdm9x35_ops = {
	.clink_ops = &mdm_cops,
	.config_hw = mdm9x35_setup_hw,
	.pon_ops = &mdm9x35_pon_ops,
};

static const struct of_device_id mdm_dt_match[] = {
@@ -1081,7 +927,7 @@ static int mdm_probe(struct platform_device *pdev)
	mdm = devm_kzalloc(&pdev->dev, sizeof(*mdm), GFP_KERNEL);
	if (IS_ERR(mdm))
		return PTR_ERR(mdm);
	return mdm_ops->config_hw(mdm, mdm_ops->clink_ops, pdev);
	return mdm_ops->config_hw(mdm, mdm_ops, pdev);
}

static struct platform_driver mdm_driver = {
+156 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014-2015, 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 "esoc-mdm.h"

/* This function can be called from atomic context. */
static int mdm4x_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
{
	int soft_reset_direction_assert = 0,
	    soft_reset_direction_de_assert = 1;

	if (mdm->soft_reset_inverted) {
		soft_reset_direction_assert = 1;
		soft_reset_direction_de_assert = 0;
	}
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			soft_reset_direction_assert);
	/*
	 * Allow PS hold assert to be detected
	 */
	if (!atomic)
		usleep_range(8000, 9000);
	else
		mdelay(6);
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			soft_reset_direction_de_assert);
	return 0;
}

static int mdm4x_do_first_power_on(struct mdm_ctrl *mdm)
{
	int i;
	int pblrdy;
	struct device *dev = mdm->dev;

	dev_dbg(dev, "Powering on modem for the first time\n");
	mdm4x_toggle_soft_reset(mdm, false);
	/* Add a delay to allow PON sequence to complete*/
	msleep(50);
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1);
	if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) {
		for (i = 0; i  < MDM_PBLRDY_CNT; i++) {
			pblrdy = gpio_get_value(MDM_GPIO(mdm, MDM2AP_PBLRDY));
			if (pblrdy)
				break;
			usleep_range(5000, 6000);
		}
		dev_dbg(dev, "pblrdy i:%d\n", i);
		msleep(200);
	}
	/*
	 * No PBLRDY gpio associated with this modem
	 * Send request for image. Let userspace confirm establishment of
	 * link to external modem.
	 */
	else
		esoc_clink_queue_request(ESOC_REQ_IMG, mdm->esoc);
	return 0;
}

static int mdm4x_power_down(struct mdm_ctrl *mdm)
{
	struct device *dev = mdm->dev;
	int soft_reset_direction = mdm->soft_reset_inverted ? 1 : 0;
	/* Assert the soft reset line whether mdm2ap_status went low or not */
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
					soft_reset_direction);
	dev_dbg(dev, "Doing a hard reset\n");
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
						soft_reset_direction);
	/*
	* Currently, there is a debounce timer on the charm PMIC. It is
	* necessary to hold the PMIC RESET low for 400ms
	* for the reset to fully take place. Sleep here to ensure the
	* reset has occurred before the function exits.
	*/
	msleep(400);
	return 0;
}

static void mdm4x_cold_reset(struct mdm_ctrl *mdm)
{
	dev_dbg(mdm->dev, "Triggering mdm cold reset");
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			!!mdm->soft_reset_inverted);
	msleep(300);
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			!mdm->soft_reset_inverted);
}

static int mdm4x_pon_dt_init(struct mdm_ctrl *mdm)
{
	int val;
	struct device_node *node = mdm->dev->of_node;
	enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;

	val = of_get_named_gpio_flags(node, "qcom,ap2mdm-soft-reset-gpio",
						0, &flags);
	if (val >= 0) {
		MDM_GPIO(mdm, AP2MDM_SOFT_RESET) = val;
		if (flags & OF_GPIO_ACTIVE_LOW)
			mdm->soft_reset_inverted = 1;
		return 0;
	} else
		return -EIO;
}

static int mdm4x_pon_setup(struct mdm_ctrl *mdm)
{
	struct device *dev = mdm->dev;

	if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET))) {
		if (gpio_request(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
					 "AP2MDM_SOFT_RESET")) {
			dev_err(dev, "Cannot config AP2MDM_SOFT_RESET gpio\n");
			return -EIO;
		}
	}
	return 0;
}

struct mdm_pon_ops mdm9x25_pon_ops = {
	.pon = mdm4x_do_first_power_on,
	.soft_reset = mdm4x_toggle_soft_reset,
	.poff_force = mdm4x_power_down,
	.cold_reset = mdm4x_cold_reset,
	.dt_init = mdm4x_pon_dt_init,
	.setup = mdm4x_pon_setup,
};

struct mdm_pon_ops mdm9x35_pon_ops = {
	.pon = mdm4x_do_first_power_on,
	.soft_reset = mdm4x_toggle_soft_reset,
	.poff_force = mdm4x_power_down,
	.cold_reset = mdm4x_cold_reset,
	.dt_init = mdm4x_pon_dt_init,
	.setup = mdm4x_pon_setup,
};

struct mdm_pon_ops mdm9x45_pon_ops = {
	.pon = mdm4x_do_first_power_on,
	.soft_reset = mdm4x_toggle_soft_reset,
	.poff_force = mdm4x_power_down,
	.cold_reset = mdm4x_cold_reset,
	.dt_init = mdm4x_pon_dt_init,
	.setup = mdm4x_pon_setup,
};
+153 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014-2015, 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.
 */

#ifndef __ESOC_MDM_H__
#define __ESOC_MDM_H__

#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include "esoc.h"

#define MDM_PBLRDY_CNT			20
#define INVALID_GPIO			(-1)
#define MDM_GPIO(mdm, i)		(mdm->gpios[i])
#define MDM9x25_LABEL			"MDM9x25"
#define MDM9x25_HSIC			"HSIC"
#define MDM9x35_LABEL			"MDM9x35"
#define MDM9x35_PCIE			"PCIe"
#define MDM9x35_DUAL_LINK		"HSIC+PCIe"
#define MDM9x35_HSIC			"HSIC"
#define MDM9x45_LABEL			"MDM9x45"
#define MDM9x45_PCIE			"PCIe"
#define MDM2AP_STATUS_TIMEOUT_MS	120000L
#define MDM_MODEM_TIMEOUT		3000
#define DEF_RAMDUMP_TIMEOUT		120000
#define DEF_RAMDUMP_DELAY		2000
#define RD_BUF_SIZE			100
#define SFR_MAX_RETRIES			10
#define SFR_RETRY_INTERVAL		1000
#define MDM_DBG_OFFSET			0x934
#define MDM_DBG_MODE			0x53444247
#define MDM_CTI_NAME			"coresight-cti-rpm-cpu0"
#define MDM_CTI_TRIG			0
#define MDM_CTI_CH			0

enum mdm_gpio {
	AP2MDM_WAKEUP = 0,
	AP2MDM_STATUS,
	AP2MDM_SOFT_RESET,
	AP2MDM_VDD_MIN,
	AP2MDM_CHNLRDY,
	AP2MDM_ERRFATAL,
	AP2MDM_VDDMIN,
	AP2MDM_PMIC_PWR_EN,
	MDM2AP_WAKEUP,
	MDM2AP_ERRFATAL,
	MDM2AP_PBLRDY,
	MDM2AP_STATUS,
	MDM2AP_VDDMIN,
	MDM_LINK_DETECT,
	NUM_GPIOS,
};

struct mdm_pon_ops;

struct mdm_ctrl {
	unsigned gpios[NUM_GPIOS];
	spinlock_t status_lock;
	struct workqueue_struct *mdm_queue;
	struct delayed_work mdm2ap_status_check_work;
	struct work_struct mdm_status_work;
	struct work_struct restart_reason_work;
	struct completion debug_done;
	struct device *dev;
	struct pinctrl *pinctrl;
	struct pinctrl_state *gpio_state_booting;
	struct pinctrl_state *gpio_state_running;
	struct pinctrl_state *gpio_state_active;
	struct pinctrl_state *gpio_state_suspend;
	int mdm2ap_status_valid_old_config;
	int soft_reset_inverted;
	int errfatal_irq;
	int status_irq;
	int pblrdy_irq;
	int debug;
	int init;
	bool debug_fail;
	unsigned int dump_timeout_ms;
	unsigned int ramdump_delay_ms;
	struct esoc_clink *esoc;
	bool get_restart_reason;
	unsigned long irq_mask;
	bool ready;
	bool dual_interface;
	u32 status;
	void __iomem *dbg_addr;
	bool dbg_mode;
	struct coresight_cti *cti;
	int trig_cnt;
	const struct mdm_pon_ops *pon_ops;
};

struct mdm_pon_ops {
	int (*pon)(struct mdm_ctrl *mdm);
	int (*soft_reset)(struct mdm_ctrl *mdm, bool atomic);
	int (*poff_force)(struct mdm_ctrl *mdm);
	int (*poff_cleanup)(struct mdm_ctrl *mdm);
	void (*cold_reset)(struct mdm_ctrl *mdm);
	int (*dt_init)(struct mdm_ctrl *mdm);
	int (*setup)(struct mdm_ctrl *mdm);
};

struct mdm_ops {
	struct esoc_clink_ops *clink_ops;
	struct mdm_pon_ops *pon_ops;
	int (*config_hw)(struct mdm_ctrl *mdm, const struct mdm_ops *ops,
					struct platform_device *pdev);
};

static inline int mdm_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
{
	return mdm->pon_ops->soft_reset(mdm, atomic);
}
static inline int mdm_do_first_power_on(struct mdm_ctrl *mdm)
{
	return mdm->pon_ops->pon(mdm);
}
static inline int mdm_power_down(struct mdm_ctrl *mdm)
{
	return mdm->pon_ops->poff_force(mdm);
}
static inline void mdm_cold_reset(struct mdm_ctrl *mdm)
{
	mdm->pon_ops->cold_reset(mdm);
}
static inline int mdm_pon_dt_init(struct mdm_ctrl *mdm)
{
	return mdm->pon_ops->dt_init(mdm);
}
static inline int mdm_pon_setup(struct mdm_ctrl *mdm)
{
	return mdm->pon_ops->setup(mdm);
}

extern struct mdm_pon_ops mdm9x25_pon_ops;
extern struct mdm_pon_ops mdm9x35_pon_ops;
extern struct mdm_pon_ops mdm9x45_pon_ops;
#endif