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

Commit b47b33d3 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "esoc: Add support for autoboot"

parents b718b40a 1d7f09e4
Loading
Loading
Loading
Loading
+73 −14
Original line number Diff line number Diff line
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-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
@@ -179,19 +179,37 @@ static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc)
	struct device *dev = mdm->dev;
	int ret;
	bool graceful_shutdown = false;
	u32 status, err_fatal;

	switch (cmd) {
	case ESOC_PWR_ON:
		if (esoc->auto_boot) {
			/*
			 * If esoc has already booted, we would have missed
			 * status change interrupt. Read status and err_fatal
			 * signals to arrive at the state of esoc.
			 */
			esoc->clink_ops->get_status(&status, esoc);
			esoc->clink_ops->get_err_fatal(&err_fatal, esoc);
			if (err_fatal)
				return -EIO;
			if (status && !mdm->ready) {
				mdm->ready = true;
				esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc);
			}
		}
		gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
		mdm_enable_irqs(mdm);
		mdm->init = 1;
		mdm_do_first_power_on(mdm);
		mdm_enable_irqs(mdm);
		break;
	case ESOC_PWR_OFF:
		mdm_disable_irqs(mdm);
		mdm->debug = 0;
		mdm->ready = false;
		mdm->trig_cnt = 0;
		if (esoc->primary)
			break;
		graceful_shutdown = true;
		ret = sysmon_send_shutdown(&esoc->subsys);
		if (ret) {
@@ -228,6 +246,8 @@ force_poff:
				esoc->subsys.sysmon_shutdown_ret);
		}

		if (esoc->primary)
			break;
		/*
		 * Force a shutdown of the mdm. This is required in order
		 * to prevent the mdm from immediately powering back on
@@ -249,9 +269,12 @@ force_poff:
		 */
		mdm->ready = false;
		cancel_delayed_work(&mdm->mdm2ap_status_check_work);
		if (!mdm->esoc->auto_boot) {
			gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
		dev_dbg(mdm->dev, "set ap2mdm errfatal to force reset\n");
			dev_dbg(mdm->dev,
				"set ap2mdm errfatal to force reset\n");
			msleep(mdm->ramdump_delay_ms);
		}
		break;
	case ESOC_EXE_DEBUG:
		mdm->debug = 1;
@@ -378,6 +401,8 @@ static void mdm_notify(enum esoc_notify notify, struct esoc_clink *esoc)
		status_down = false;
		dev_dbg(dev, "signal apq err fatal for graceful restart\n");
		gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
		if (esoc->primary)
			break;
		timeout = local_clock();
		do_div(timeout, NSEC_PER_MSEC);
		timeout += MDM_MODEM_TIMEOUT;
@@ -440,11 +465,25 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id)
	if (!mdm)
		return IRQ_HANDLED;
	esoc = mdm->esoc;
	/*
	 * On auto boot devices, there is a possibility of receiving
	 * status change interrupt before esoc_clink structure is
	 * initialized. Ignore them.
	 */
	if (!esoc)
		return IRQ_HANDLED;
	value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS));
	if (value == 0 && mdm->ready) {
		dev_err(dev, "unexpected reset external modem\n");
		esoc_clink_evt_notify(ESOC_UNEXPECTED_RESET, esoc);
	} else if (value == 1) {
		/*
		 * In auto_boot cases, bailout early if mdm
		 * is up already.
		 */
		 if (esoc->auto_boot && mdm->ready)
			return IRQ_HANDLED;

		cancel_delayed_work(&mdm->mdm2ap_status_check_work);
		dev_dbg(dev, "status = 1: mdm is now ready\n");
		mdm->ready = true;
@@ -452,6 +491,8 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id)
		queue_work(mdm->mdm_queue, &mdm->mdm_status_work);
		if (mdm->get_restart_reason)
			queue_work(mdm->mdm_queue, &mdm->restart_reason_work);
		if (esoc->auto_boot)
			esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc);
	}
	return IRQ_HANDLED;
}
@@ -480,7 +521,7 @@ static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
	return IRQ_HANDLED;
}

static int mdm_get_status(u32 *status, struct esoc_clink *esoc)
static void mdm_get_status(u32 *status, struct esoc_clink *esoc)
{
	struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);

@@ -488,7 +529,16 @@ static int mdm_get_status(u32 *status, struct esoc_clink *esoc)
		*status = 0;
	else
		*status = 1;
	return 0;
}

static void mdm_get_err_fatal(u32 *status, struct esoc_clink *esoc)
{
	struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);

	if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_ERRFATAL)) == 0)
		*status = 0;
	else
		*status = 1;
}

static void mdm_configure_debug(struct mdm_ctrl *mdm)
@@ -499,7 +549,7 @@ static void mdm_configure_debug(struct mdm_ctrl *mdm)
	struct device_node *node = mdm->dev->of_node;

	addr = of_iomap(node, 0);
	if (IS_ERR(addr)) {
	if (IS_ERR_OR_NULL(addr)) {
		dev_err(mdm->dev, "failed to get debug base addres\n");
		return;
	}
@@ -572,13 +622,21 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev)
						&mdm->ramdump_delay_ms);
	if (ret)
		mdm->ramdump_delay_ms = DEF_RAMDUMP_DELAY;
	/* Multilple gpio_request calls are allowed */
	/*
	 * In certain scenarios, multiple esoc devices are monitoring
	 * same AP2MDM_STATUS line. But only one of them will have a
	 * successful gpio_request call. Initialize gpio only if request
	 * succeeds.
	 */
	if (gpio_request(MDM_GPIO(mdm, AP2MDM_STATUS), "AP2MDM_STATUS"))
		dev_err(dev, "Failed to configure AP2MDM_STATUS gpio\n");
	/* Multilple gpio_request calls are allowed */
	else
		gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
	if (gpio_request(MDM_GPIO(mdm, AP2MDM_ERRFATAL), "AP2MDM_ERRFATAL"))
		dev_err(dev, "%s Failed to configure AP2MDM_ERRFATAL gpio\n",
			   __func__);
	else
		gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
	if (gpio_request(MDM_GPIO(mdm, MDM2AP_STATUS), "MDM2AP_STATUS")) {
		dev_err(dev, "%s Failed to configure MDM2AP_STATUS gpio\n",
			   __func__);
@@ -611,9 +669,6 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev)
		}
	}

	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);

	if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY)))
		gpio_direction_output(MDM_GPIO(mdm, AP2MDM_CHNLRDY), 0);

@@ -747,6 +802,7 @@ static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
		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");
@@ -817,6 +873,7 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm,
		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");
@@ -905,6 +962,7 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
		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");
@@ -961,6 +1019,7 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
static struct esoc_clink_ops mdm_cops = {
	.cmd_exe = mdm_cmd_exe,
	.get_status = mdm_get_status,
	.get_err_fatal = mdm_get_err_fatal,
	.notify = mdm_notify,
};

+29 −9
Original line number Diff line number Diff line
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
/* 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
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/reboot.h>
#include <linux/of.h>
#include "esoc.h"
#include "mdm-dbg.h"

@@ -72,7 +73,14 @@ static void mdm_handle_clink_evt(enum esoc_evt evt,
		break;
	case ESOC_UNEXPECTED_RESET:
	case ESOC_ERR_FATAL:
		if (mdm_drv->mode == CRASH)
		/*
		 * Modem can crash while we are waiting for boot_done during
		 * a subsystem_get(). Setting mode to CRASH will prevent a
		 * subsequent subsystem_get() from entering poweron ops. Avoid
		 * this by seting mode to CRASH only if device was up and
		 * running.
		 */
		if (mdm_drv->mode == CRASH || mdm_drv->mode != RUN)
			return;
		mdm_drv->mode = CRASH;
		queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work);
@@ -161,8 +169,9 @@ static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
								subsys);
	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;

	if (!esoc_req_eng_enabled(esoc_clink)) {
	if (!esoc_clink->auto_boot && !esoc_req_eng_enabled(esoc_clink)) {
		dev_dbg(&esoc_clink->dev, "Wait for req eng registration\n");
		wait_for_completion(&mdm_drv->req_eng_wait);
	}
@@ -187,8 +196,17 @@ static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
			return ret;
		}
	}
	wait_for_completion(&mdm_drv->boot_done);
	if (mdm_drv->boot_fail) {

	/*
	 * 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;
	ret = wait_for_completion_timeout(&mdm_drv->boot_done, timeout);
	if (mdm_drv->boot_fail || ret <= 0) {
		dev_err(&esoc_clink->dev, "booting failed\n");
		return -EIO;
	}
@@ -216,10 +234,12 @@ static int mdm_subsys_ramdumps(int want_dumps,

static int mdm_register_ssr(struct esoc_clink *esoc_clink)
{
	esoc_clink->subsys.shutdown = mdm_subsys_shutdown;
	esoc_clink->subsys.ramdump = mdm_subsys_ramdumps;
	esoc_clink->subsys.powerup = mdm_subsys_powerup;
	esoc_clink->subsys.crash_shutdown = mdm_crash_shutdown;
	struct subsys_desc *subsys = &esoc_clink->subsys;

	subsys->shutdown = mdm_subsys_shutdown;
	subsys->ramdump = mdm_subsys_ramdumps;
	subsys->powerup = mdm_subsys_powerup;
	subsys->crash_shutdown = mdm_crash_shutdown;
	return esoc_clink_register_ssr(esoc_clink);
}

+7 −1
Original line number Diff line number Diff line
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-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
@@ -68,6 +68,9 @@ static int mdm4x_do_first_power_on(struct mdm_ctrl *mdm)
	struct device *dev = mdm->dev;

	dev_dbg(dev, "Powering on modem for the first time\n");
	if (mdm->esoc->auto_boot)
		return 0;

	mdm_toggle_soft_reset(mdm, false);
	/* Add a delay to allow PON sequence to complete*/
	msleep(50);
@@ -134,6 +137,9 @@ static int mdm9x55_power_down(struct mdm_ctrl *mdm)

static void mdm4x_cold_reset(struct mdm_ctrl *mdm)
{
	if (!gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET)))
		return;

	dev_dbg(mdm->dev, "Triggering mdm cold reset");
	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
			!!mdm->soft_reset_inverted);
+11 −2
Original line number Diff line number Diff line
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
/* 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
@@ -48,6 +48,7 @@ struct esoc_eng {
 * @link_name: name of the physical link.
 * @parent: parent device.
 * @dev: device for userspace interface.
 * @pdev: platform device to interface with SSR driver.
 * @id: id of the external device.
 * @owner: owner of the device.
 * @clink_ops: control operations for the control link
@@ -58,12 +59,16 @@ struct esoc_eng {
 * @subsys_desc: descriptor for subsystem restart
 * @subsys_dev: ssr device handle.
 * @np: device tree node for esoc_clink.
 * @auto_boot: boots independently.
 * @primary: primary esoc controls(reset/poweroff) all secondary
 *	 esocs, but not	otherway around.
 */
struct esoc_clink {
	const char *name;
	const char *link_name;
	struct device *parent;
	struct device dev;
	struct platform_device *pdev;
	unsigned int id;
	struct module *owner;
	const struct esoc_clink_ops const *clink_ops;
@@ -75,17 +80,21 @@ struct esoc_clink {
	struct subsys_desc subsys;
	struct subsys_device *subsys_dev;
	struct device_node *np;
	bool auto_boot;
	bool primary;
};

/**
 * struct esoc_clink_ops: Operations to control external soc
 * @cmd_exe: Execute control command
 * @get_status: Get current status, or response to previous command
 * @get_err_fatal: Get status of err fatal signal
 * @notify_esoc: notify external soc of events
 */
struct esoc_clink_ops {
	int (*cmd_exe)(enum esoc_cmd cmd, struct esoc_clink *dev);
	int (*get_status)(u32 *status, struct esoc_clink *dev);
	void (*get_status)(u32 *status, struct esoc_clink *dev);
	void (*get_err_fatal)(u32 *status, struct esoc_clink *dev);
	void (*notify)(enum esoc_notify notify, struct esoc_clink *dev);
};

+2 −2
Original line number Diff line number Diff line
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
/* 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
@@ -180,7 +180,7 @@ int esoc_clink_register_ssr(struct esoc_clink *esoc_clink)
	snprintf(subsys_name, len, "esoc%d", esoc_clink->id);
	esoc_clink->subsys.name = subsys_name;
	esoc_clink->dev.of_node = esoc_clink->np;
	esoc_clink->subsys.dev = &esoc_clink->dev;
	esoc_clink->subsys.dev = &esoc_clink->pdev->dev;
	esoc_clink->subsys_dev = subsys_register(&esoc_clink->subsys);
	if (IS_ERR(esoc_clink->subsys_dev)) {
		dev_err(&esoc_clink->dev, "failed to register ssr node\n");
Loading