esoc: Add support to retry the powerup of external modem

Due to recent hardware limitations, the requirement is to do
the warm reset of the modem twice. Once for collecting the
dumps and the other for transferring the firmware images.
If during the process, the driver receives a failed notification,
then power-off the modem. Hence, add code to support these
functionalities.

Change-Id: I4923b6e89474a44bdde477ccbd0c4e73b9648aae
Signed-off-by: Raghavendra Rao Ananta <rananta@codeaurora.org>
This commit is contained in:
Raghavendra Rao Ananta 2018-05-14 15:17:27 -07:00
parent 910830987d
commit ae96f0f13e
4 changed files with 99 additions and 48 deletions

View File

@ -101,7 +101,7 @@ static void mdm_enable_irqs(struct mdm_ctrl *mdm)
}
}
static void mdm_disable_irqs(struct mdm_ctrl *mdm)
void mdm_disable_irqs(struct mdm_ctrl *mdm)
{
if (!mdm)
return;
@ -374,6 +374,9 @@ static void mdm_notify(enum esoc_notify notify, struct esoc_clink *esoc)
case ESOC_BOOT_FAIL:
esoc_clink_evt_notify(ESOC_INVALID_STATE, esoc);
break;
case ESOC_PON_RETRY:
esoc_clink_evt_notify(ESOC_RETRY_PON_EVT, esoc);
break;
case ESOC_UPGRADE_AVAILABLE:
break;
case ESOC_DEBUG_DONE:

View File

@ -16,8 +16,19 @@
#include <linux/of.h>
#include <linux/esoc_client.h>
#include "esoc.h"
#include "esoc-mdm.h"
#include "mdm-dbg.h"
/* Maximum number of powerup trial requests per session */
#define ESOC_MAX_PON_REQ 2
enum esoc_pon_state {
PON_INIT,
PON_SUCCESS,
PON_RETRY,
PON_FAIL
};
enum {
PWR_OFF = 0x1,
PWR_ON,
@ -33,10 +44,10 @@ enum {
struct mdm_drv {
unsigned int mode;
struct esoc_eng cmd_eng;
struct completion boot_done;
struct completion pon_done;
struct completion req_eng_wait;
struct esoc_clink *esoc_clink;
bool boot_fail;
enum esoc_pon_state pon_state;
struct workqueue_struct *mdm_queue;
struct work_struct ssr_work;
struct notifier_block esoc_restart;
@ -66,18 +77,22 @@ static void mdm_handle_clink_evt(enum esoc_evt evt,
switch (evt) {
case ESOC_INVALID_STATE:
mdm_drv->boot_fail = true;
complete(&mdm_drv->boot_done);
mdm_drv->pon_state = PON_FAIL;
complete(&mdm_drv->pon_done);
break;
case ESOC_RUN_STATE:
mdm_drv->boot_fail = false;
mdm_drv->pon_state = PON_SUCCESS;
mdm_drv->mode = RUN,
complete(&mdm_drv->boot_done);
complete(&mdm_drv->pon_done);
break;
case ESOC_RETRY_PON_EVT:
mdm_drv->pon_state = PON_RETRY;
complete(&mdm_drv->pon_done);
break;
case ESOC_UNEXPECTED_RESET:
case ESOC_ERR_FATAL:
/*
* Modem can crash while we are waiting for boot_done during
* Modem can crash while we are waiting for pon_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
@ -205,6 +220,18 @@ static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys,
return 0;
}
static void mdm_subsys_retry_powerup_cleanup(struct esoc_clink *esoc_clink)
{
struct mdm_ctrl *mdm = get_esoc_clink_data(esoc_clink);
struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
esoc_client_link_power_off(esoc_clink, false);
mdm_disable_irqs(mdm);
mdm_drv->pon_state = PON_INIT;
reinit_completion(&mdm_drv->pon_done);
reinit_completion(&mdm_drv->req_eng_wait);
}
static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
{
int ret;
@ -213,49 +240,65 @@ 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;
struct mdm_ctrl *mdm = get_esoc_clink_data(esoc_clink);
int timeout = INT_MAX;
u8 pon_trial = 1;
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);
}
if (mdm_drv->mode == PWR_OFF) {
if (mdm_dbg_stall_cmd(ESOC_PWR_ON))
return -EBUSY;
ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
if (ret) {
dev_err(&esoc_clink->dev, "pwr on fail\n");
return ret;
do {
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);
}
esoc_client_link_power_on(esoc_clink, false);
} else if (mdm_drv->mode == IN_DEBUG) {
ret = clink_ops->cmd_exe(ESOC_EXIT_DEBUG, esoc_clink);
if (ret) {
dev_err(&esoc_clink->dev, "cannot exit debug mode\n");
return ret;
if (mdm_drv->mode == PWR_OFF) {
if (mdm_dbg_stall_cmd(ESOC_PWR_ON))
return -EBUSY;
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, false);
} else if (mdm_drv->mode == IN_DEBUG) {
ret = clink_ops->cmd_exe(ESOC_EXIT_DEBUG, esoc_clink);
if (ret) {
dev_err(&esoc_clink->dev,
"cannot exit debug mode\n");
return ret;
}
mdm_drv->mode = PWR_OFF;
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, true);
}
mdm_drv->mode = PWR_OFF;
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, true);
}
/*
* 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;
}
/*
* 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->pon_done, timeout);
if (mdm_drv->pon_state == PON_FAIL || ret <= 0) {
dev_err(&esoc_clink->dev, "booting failed\n");
mdm_subsys_retry_powerup_cleanup(esoc_clink);
mdm_power_down(mdm);
return -EIO;
} else if (mdm_drv->pon_state == PON_RETRY) {
pon_trial++;
mdm_subsys_retry_powerup_cleanup(esoc_clink);
} else if (mdm_drv->pon_state == PON_SUCCESS) {
break;
}
} while (pon_trial <= ESOC_MAX_PON_REQ);
return 0;
}
@ -314,12 +357,12 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
goto queue_err;
}
esoc_set_drv_data(esoc_clink, mdm_drv);
init_completion(&mdm_drv->boot_done);
init_completion(&mdm_drv->pon_done);
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->boot_fail = false;
mdm_drv->pon_state = PON_INIT;
mdm_drv->esoc_restart.notifier_call = esoc_msm_restart_handler;
ret = register_reboot_notifier(&mdm_drv->esoc_restart);
if (ret)

View File

@ -123,6 +123,8 @@ struct mdm_ops {
struct platform_device *pdev);
};
void mdm_disable_irqs(struct mdm_ctrl *mdm);
static inline int mdm_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
{
return mdm->pon_ops->soft_reset(mdm, atomic);

View File

@ -27,6 +27,7 @@ struct esoc_link_data {
#define ESOC_REQ_SEND_SHUTDOWN ESOC_REQ_SEND_SHUTDOWN
#define ESOC_REQ_CRASH_SHUTDOWN ESOC_REQ_CRASH_SHUTDOWN
#define ESOC_PON_RETRY ESOC_PON_RETRY
enum esoc_evt {
ESOC_RUN_STATE = 0x1,
@ -38,6 +39,7 @@ enum esoc_evt {
ESOC_CMD_ENG_ON,
ESOC_CMD_ENG_OFF,
ESOC_INVALID_STATE,
ESOC_RETRY_PON_EVT,
};
enum esoc_cmd {
@ -61,6 +63,7 @@ enum esoc_notify {
ESOC_DEBUG_FAIL,
ESOC_PRIMARY_CRASH,
ESOC_PRIMARY_REBOOT,
ESOC_PON_RETRY,
};
enum esoc_req {