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

Commit caa60aa8 authored by Raghavendra Rao Ananta's avatar Raghavendra Rao Ananta
Browse files

esoc: Add support for sdxpoorwills



Add support for sdxpoorwills, using mdm9x55 data as a starting point.

Change-Id: Ic1abf2997781e84ab20270d974794ed37c20d62a
Signed-off-by: default avatarRaghavendra Rao Ananta <rananta@codeaurora.org>
parent 40c1b6f6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@ to be reset.
Required Properties:
- compatible:	The bus devices need to be compatible with
		"qcom,mdm2-modem", "qcom,ext-mdm9x25", "qcom,ext-mdm9x35", "qcom, ext-mdm9x45",
		"qcom,ext-mdm9x55".
		"qcom,ext-mdm9x55", "qcom,ext-sdxpoorwills".

Required named gpio properties:
- qcom,mdm2ap-errfatal-gpio: gpio for the external modem to indicate to the apps processor
+2 −2
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ config ESOC_DEBUG
	  allow logging of different esoc driver traces.

config ESOC_MDM_4x
	bool "Add support for external mdm9x25/mdm9x35/mdm9x55"
	bool "Add support for external modem"
	help
	  In some Qualcomm Technologies, Inc. boards, an external modem such as
	  mdm9x25 or mdm9x35 is connected to a primary msm. The primary soc can
@@ -49,7 +49,7 @@ config ESOC_MDM_DRV
	tristate "Command engine for 4x series external modems"
	help
	  Provides a command engine to control the behavior of an external modem
	  such as mdm9x25/mdm9x35/mdm9x55/QSC. Allows the primary soc to put the
	  such as mdm9x25/mdm9x35/mdm9x55/sdxpoorwills/QSC. Allows the primary soc to put the
	  external modem in a specific mode. Also listens for events on the
	  external modem.

+133 −1
Original line number Diff line number Diff line
/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2015, 2017-2018, 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
@@ -794,6 +794,28 @@ static int mdm_pinctrl_init(struct mdm_ctrl *mdm)
	mdm->gpio_state_running = NULL;
	return retval;
}

static void mdm_release_ipc_gpio(struct mdm_ctrl *mdm)
{
	int i;

	if (!mdm)
		return;

	for (i = 0; i < NUM_GPIOS; ++i)
		if (gpio_is_valid(MDM_GPIO(mdm, i)))
			gpio_free(MDM_GPIO(mdm, i));
}

static void mdm_free_irq(struct mdm_ctrl *mdm)
{
	if (!mdm)
		return;

	free_irq(mdm->errfatal_irq, mdm);
	free_irq(mdm->status_irq, mdm);
}

static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
					const struct mdm_ops *ops,
					struct platform_device *pdev)
@@ -1028,6 +1050,108 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
	return 0;
}

static int sdxpoorwills_setup_hw(struct mdm_ctrl *mdm,
				const struct mdm_ops *ops,
				struct platform_device *pdev)
{
	int ret;
	struct device_node *node;
	struct esoc_clink *esoc;
	const struct esoc_clink_ops *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_OR_NULL(esoc)) {
		dev_err(mdm->dev, "cannot allocate esoc device\n");
		return PTR_ERR(esoc);
	}

	esoc->pdev = pdev;

	mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
	if (!mdm->mdm_queue) {
		dev_err(mdm->dev, "could not create mdm_queue\n");
		return -ENOMEM;
	}

	mdm->irq_mask = 0;
	mdm->ready = false;

	ret = mdm_dt_parse_gpios(mdm);
	if (ret) {
		dev_err(mdm->dev, "Failed to parse DT gpios\n");
		goto err_destroy_wrkq;
	}

	ret = mdm_pon_dt_init(mdm);
	if (ret) {
		dev_err(mdm->dev, "Failed to parse PON DT gpio\n");
		goto err_destroy_wrkq;
	}

	ret = mdm_pinctrl_init(mdm);
	if (ret) {
		dev_err(mdm->dev, "Failed to init pinctrl\n");
		goto err_destroy_wrkq;
	}

	ret = mdm_pon_setup(mdm);
	if (ret) {
		dev_err(mdm->dev, "Failed to setup PON\n");
		goto err_destroy_wrkq;
	}

	ret = mdm_configure_ipc(mdm, pdev);
	if (ret) {
		dev_err(mdm->dev, "Failed to configure the ipc\n");
		goto err_release_ipc;
	}

	esoc->name = SDXPOORWILLS_LABEL;
	esoc->link_name = SDXPOORWILLS_PCIE;

	ret = of_property_read_string(node, "qcom,mdm-link-info",
					&esoc->link_info);
	if (ret)
		dev_info(mdm->dev, "esoc link info missing\n");

	esoc->clink_ops = clink_ops;
	esoc->parent = mdm->dev;
	esoc->owner = THIS_MODULE;
	esoc->np = pdev->dev.of_node;
	set_esoc_clink_data(esoc, mdm);

	ret = esoc_clink_register(esoc);
	if (ret) {
		dev_err(mdm->dev, "esoc registration failed\n");
		goto err_free_irq;
	}
	dev_dbg(mdm->dev, "esoc registration done\n");

	init_completion(&mdm->debug_done);
	INIT_WORK(&mdm->mdm_status_work, mdm_status_fn);
	INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason);
	INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check);
	mdm->get_restart_reason = false;
	mdm->debug_fail = false;
	mdm->esoc = esoc;
	mdm->init = 0;

	return 0;

err_free_irq:
	mdm_free_irq(mdm);
err_release_ipc:
	mdm_release_ipc_gpio(mdm);
err_destroy_wrkq:
	destroy_workqueue(mdm->mdm_queue);
	return ret;
}

static struct esoc_clink_ops mdm_cops = {
	.cmd_exe = mdm_cmd_exe,
	.get_status = mdm_get_status,
@@ -1053,6 +1177,12 @@ static struct mdm_ops mdm9x55_ops = {
	.pon_ops = &mdm9x55_pon_ops,
};

static struct mdm_ops sdxpoorwills_ops = {
	.clink_ops = &mdm_cops,
	.config_hw = sdxpoorwills_setup_hw,
	.pon_ops = &sdxpoorwills_pon_ops,
};

static const struct of_device_id mdm_dt_match[] = {
	{ .compatible = "qcom,ext-mdm9x25",
		.data = &mdm9x25_ops, },
@@ -1060,6 +1190,8 @@ static const struct of_device_id mdm_dt_match[] = {
		.data = &mdm9x35_ops, },
	{ .compatible = "qcom,ext-mdm9x55",
		.data = &mdm9x55_ops, },
	{ .compatible = "qcom,ext-sdxpoorwills",
		.data = &sdxpoorwills_ops, },
	{},
};
MODULE_DEVICE_TABLE(of, mdm_dt_match);
+5 −1
Original line number Diff line number Diff line
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2018, 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
@@ -309,6 +309,10 @@ static struct esoc_compat compat_table[] = {
		.name = "MDM9x55",
		.data = NULL,
	},
	{
		.name = "SDXPOORWILLS",
		.data = NULL,
	},
};

static struct esoc_drv esoc_ssr_drv = {
+60 −1
Original line number Diff line number Diff line
/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2015, 2017-2018, 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
@@ -60,6 +60,24 @@ static int mdm9x55_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
	return 0;
}

/* This function can be called from atomic context. */
static int sdxpoorwills_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
{
	int soft_reset_direction_assert = mdm->soft_reset_inverted;

	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			soft_reset_direction_assert);
	/*
	 * Allow PS hold assert to be detected
	 */
	if (!atomic)
		usleep_range(80000, 180000);
	else
		mdelay(100);
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			!soft_reset_direction_assert);
	return 0;
}

static int mdm4x_do_first_power_on(struct mdm_ctrl *mdm)
{
@@ -99,6 +117,7 @@ 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);
@@ -135,6 +154,27 @@ static int mdm9x55_power_down(struct mdm_ctrl *mdm)
	return 0;
}

static int sdxpoorwills_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_info(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 325ms
	 * for the reset to fully take place. Sleep here to ensure the
	 * reset has occurred before the function exits.
	 */
	mdelay(325);
	return 0;
}

static void mdm4x_cold_reset(struct mdm_ctrl *mdm)
{
	if (!gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET)))
@@ -158,6 +198,16 @@ static void mdm9x55_cold_reset(struct mdm_ctrl *mdm)
			!mdm->soft_reset_inverted);
}

static void sdxpoorwills_cold_reset(struct mdm_ctrl *mdm)
{
	dev_info(mdm->dev, "Triggering mdm cold reset");
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			!!mdm->soft_reset_inverted);
	mdelay(600);
	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;
@@ -215,3 +265,12 @@ struct mdm_pon_ops mdm9x55_pon_ops = {
	.dt_init = mdm4x_pon_dt_init,
	.setup = mdm4x_pon_setup,
};

struct mdm_pon_ops sdxpoorwills_pon_ops = {
	.pon = mdm4x_do_first_power_on,
	.soft_reset = sdxpoorwills_toggle_soft_reset,
	.poff_force = sdxpoorwills_power_down,
	.cold_reset = sdxpoorwills_cold_reset,
	.dt_init = mdm4x_pon_dt_init,
	.setup = mdm4x_pon_setup,
};
Loading