summaryrefslogtreecommitdiff
path: root/drivers/esoc
diff options
context:
space:
mode:
authorArun KS <arunks@codeaurora.org>2017-01-16 17:47:03 +0530
committerArun KS <arunks@codeaurora.org>2017-04-30 19:08:54 +0530
commitfe1cc57aafd720bfbe5bc270804ac11667dbffa2 (patch)
tree34028cc35e09be92980e44f3a425f855000323a4 /drivers/esoc
parent5c846e4f7c3c31ab503d9a6b1a5bd485edf68275 (diff)
esoc: Add support for autoboot
Some of the external SoC are flash based and can boot independently. Extend esoc driver to support such auto boot esocs. This patch also adds support for primary esoc. Primary esoc are esoc that control secondary esoc such as modems. Primary esoc have control over reset/poweroff of secondary esoc. Secondary esoc don't have control over reset/poweroff of primary esoc. In general modems are considered as secondary esoc while apps processor is considered as primary esoc. Change-Id: Id02417fcd122ac108cf75d3381ee7955f0f8f783 Signed-off-by: Arun KS <arunks@codeaurora.org> Signed-off-by: Srivatsa Vaddagiri <vatsa@codeaurora.org>
Diffstat (limited to 'drivers/esoc')
-rw-r--r--drivers/esoc/esoc-mdm-4x.c66
-rw-r--r--drivers/esoc/esoc-mdm-drv.c36
-rw-r--r--drivers/esoc/esoc-mdm-pon.c8
-rw-r--r--drivers/esoc/esoc.h5
4 files changed, 96 insertions, 19 deletions
diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c
index 543bad093090..ac8d8002bcd3 100644
--- a/drivers/esoc/esoc-mdm-4x.c
+++ b/drivers/esoc/esoc-mdm-4x.c
@@ -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);
- gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
- dev_dbg(mdm->dev, "set ap2mdm errfatal to force reset\n");
- msleep(mdm->ramdump_delay_ms);
+ 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");
+ 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;
@@ -420,7 +445,7 @@ static irqreturn_t mdm_errfatal(int irq, void *dev_id)
goto mdm_pwroff_irq;
esoc = mdm->esoc;
dev_err(dev, "%s: mdm sent errfatal interrupt\n",
- __func__);
+ __func__);
/* disable irq ?*/
esoc_clink_evt_notify(ESOC_ERR_FATAL, esoc);
return IRQ_HANDLED;
@@ -441,11 +466,25 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id)
return IRQ_HANDLED;
dev = mdm->dev;
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;
@@ -453,6 +492,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;
}
@@ -582,13 +623,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__);
@@ -621,9 +670,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);
diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c
index 8697428eceb2..266eaccf8b69 100644
--- a/drivers/esoc/esoc-mdm-drv.c
+++ b/drivers/esoc/esoc-mdm-drv.c
@@ -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);
}
diff --git a/drivers/esoc/esoc-mdm-pon.c b/drivers/esoc/esoc-mdm-pon.c
index acda06485364..a54c3e2715dd 100644
--- a/drivers/esoc/esoc-mdm-pon.c
+++ b/drivers/esoc/esoc-mdm-pon.c
@@ -1,4 +1,4 @@
-/* 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);
diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h
index 86aa1a918dfb..6d9d0aa3272f 100644
--- a/drivers/esoc/esoc.h
+++ b/drivers/esoc/esoc.h
@@ -60,6 +60,9 @@ 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;
@@ -79,6 +82,8 @@ struct esoc_clink {
struct subsys_desc subsys;
struct subsys_device *subsys_dev;
struct device_node *np;
+ bool auto_boot;
+ bool primary;
};
/**