Loading Documentation/devicetree/bindings/arm/msm/mdm-modem.txt +4 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,10 @@ Optional driver parameters: on behalf of the subsystem driver. - qcom,mdm-link-info: a string indicating additional info about the physical link. For example: "devID_domain.bus.slot" in case of PCIe. - qcom,mdm-auto-boot: Boolean. To indicate this instance of esoc boots independently. - qcom,mdm-statusline-not-a-powersource: Boolean. If set, status line to esoc device is not a power source. - qcom,mdm-userspace-handle-shutdown: Boolean. If set, userspace handles shutdown requests. Example: mdm0: qcom,mdm0 { Loading drivers/esoc/esoc-mdm-4x.c +75 −22 Original line number Diff line number Diff line Loading @@ -88,12 +88,10 @@ static void mdm_enable_irqs(struct mdm_ctrl *mdm) return; if (mdm->irq_mask & IRQ_ERRFATAL) { enable_irq(mdm->errfatal_irq); irq_set_irq_wake(mdm->errfatal_irq, 1); mdm->irq_mask &= ~IRQ_ERRFATAL; } if (mdm->irq_mask & IRQ_STATUS) { enable_irq(mdm->status_irq); irq_set_irq_wake(mdm->status_irq, 1); mdm->irq_mask &= ~IRQ_STATUS; } if (mdm->irq_mask & IRQ_PBLRDY) { Loading @@ -107,12 +105,10 @@ static void mdm_disable_irqs(struct mdm_ctrl *mdm) if (!mdm) return; if (!(mdm->irq_mask & IRQ_ERRFATAL)) { irq_set_irq_wake(mdm->errfatal_irq, 0); disable_irq_nosync(mdm->errfatal_irq); mdm->irq_mask |= IRQ_ERRFATAL; } if (!(mdm->irq_mask & IRQ_STATUS)) { irq_set_irq_wake(mdm->status_irq, 0); disable_irq_nosync(mdm->status_irq); mdm->irq_mask |= IRQ_STATUS; } Loading Loading @@ -179,27 +175,49 @@ 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; if (!esoc->userspace_handle_shutdown) { ret = sysmon_send_shutdown(&esoc->subsys); if (ret) { dev_err(mdm->dev, "sysmon shutdown fail, ret = %d\n", ret); dev_err(mdm->dev, "sysmon shutdown fail, ret = %d\n", ret); graceful_shutdown = false; goto force_poff; } } else { esoc_clink_queue_request(ESOC_REQ_SEND_SHUTDOWN, esoc); } dev_dbg(mdm->dev, "Waiting for status gpio go low\n"); status_down = false; end_time = jiffies + msecs_to_jiffies(10000); Loading Loading @@ -229,11 +247,16 @@ static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc) 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 * after the shutdown * after the shutdown. Avoid setting status to 0, if line is * monitored by multiple mdms(might be wrongly interpreted as * a primary crash). */ if (esoc->statusline_not_a_powersource == false) gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0); esoc_clink_queue_request(ESOC_REQ_SHUTDOWN, esoc); mdm_power_down(mdm); Loading @@ -250,9 +273,12 @@ static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc) */ 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; Loading Loading @@ -380,6 +406,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; Loading Loading @@ -422,6 +450,7 @@ static irqreturn_t mdm_errfatal(int irq, void *dev_id) esoc = mdm->esoc; dev_err(dev, "%s: mdm sent errfatal interrupt\n", __func__); subsys_set_crash_status(esoc->subsys_dev, true); /* disable irq ?*/ esoc_clink_evt_notify(ESOC_ERR_FATAL, esoc); return IRQ_HANDLED; Loading @@ -442,11 +471,26 @@ 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"); subsys_set_crash_status(esoc->subsys_dev, true); 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; Loading @@ -454,6 +498,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; } Loading Loading @@ -582,13 +628,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__); Loading Loading @@ -621,9 +675,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); Loading @@ -646,6 +697,7 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev) goto errfatal_err; } mdm->errfatal_irq = irq; irq_set_irq_wake(mdm->errfatal_irq, 1); errfatal_err: /* status irq */ Loading @@ -664,6 +716,7 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev) goto status_err; } mdm->status_irq = irq; irq_set_irq_wake(mdm->status_irq, 1); status_err: if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) { irq = platform_get_irq_byname(pdev, "plbrdy_irq"); Loading drivers/esoc/esoc-mdm-drv.c +28 −8 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -74,7 +75,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); Loading Loading @@ -164,8 +172,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); } Loading @@ -190,8 +199,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; } Loading Loading @@ -219,10 +237,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); } Loading drivers/esoc/esoc-mdm-pon.c +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 Loading Loading @@ -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*/ mdelay(50); Loading Loading @@ -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); Loading drivers/esoc/esoc.h +10 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,12 @@ 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. * @statusline_not_a_powersource: True if status line to esoc is not a * power source. * @userspace_handle_shutdown: True if user space handles shutdown requests. */ struct esoc_clink { const char *name; Loading @@ -79,6 +85,10 @@ struct esoc_clink { struct subsys_desc subsys; struct subsys_device *subsys_dev; struct device_node *np; bool auto_boot; bool primary; bool statusline_not_a_powersource; bool userspace_handle_shutdown; }; /** Loading Loading
Documentation/devicetree/bindings/arm/msm/mdm-modem.txt +4 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,10 @@ Optional driver parameters: on behalf of the subsystem driver. - qcom,mdm-link-info: a string indicating additional info about the physical link. For example: "devID_domain.bus.slot" in case of PCIe. - qcom,mdm-auto-boot: Boolean. To indicate this instance of esoc boots independently. - qcom,mdm-statusline-not-a-powersource: Boolean. If set, status line to esoc device is not a power source. - qcom,mdm-userspace-handle-shutdown: Boolean. If set, userspace handles shutdown requests. Example: mdm0: qcom,mdm0 { Loading
drivers/esoc/esoc-mdm-4x.c +75 −22 Original line number Diff line number Diff line Loading @@ -88,12 +88,10 @@ static void mdm_enable_irqs(struct mdm_ctrl *mdm) return; if (mdm->irq_mask & IRQ_ERRFATAL) { enable_irq(mdm->errfatal_irq); irq_set_irq_wake(mdm->errfatal_irq, 1); mdm->irq_mask &= ~IRQ_ERRFATAL; } if (mdm->irq_mask & IRQ_STATUS) { enable_irq(mdm->status_irq); irq_set_irq_wake(mdm->status_irq, 1); mdm->irq_mask &= ~IRQ_STATUS; } if (mdm->irq_mask & IRQ_PBLRDY) { Loading @@ -107,12 +105,10 @@ static void mdm_disable_irqs(struct mdm_ctrl *mdm) if (!mdm) return; if (!(mdm->irq_mask & IRQ_ERRFATAL)) { irq_set_irq_wake(mdm->errfatal_irq, 0); disable_irq_nosync(mdm->errfatal_irq); mdm->irq_mask |= IRQ_ERRFATAL; } if (!(mdm->irq_mask & IRQ_STATUS)) { irq_set_irq_wake(mdm->status_irq, 0); disable_irq_nosync(mdm->status_irq); mdm->irq_mask |= IRQ_STATUS; } Loading Loading @@ -179,27 +175,49 @@ 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; if (!esoc->userspace_handle_shutdown) { ret = sysmon_send_shutdown(&esoc->subsys); if (ret) { dev_err(mdm->dev, "sysmon shutdown fail, ret = %d\n", ret); dev_err(mdm->dev, "sysmon shutdown fail, ret = %d\n", ret); graceful_shutdown = false; goto force_poff; } } else { esoc_clink_queue_request(ESOC_REQ_SEND_SHUTDOWN, esoc); } dev_dbg(mdm->dev, "Waiting for status gpio go low\n"); status_down = false; end_time = jiffies + msecs_to_jiffies(10000); Loading Loading @@ -229,11 +247,16 @@ static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc) 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 * after the shutdown * after the shutdown. Avoid setting status to 0, if line is * monitored by multiple mdms(might be wrongly interpreted as * a primary crash). */ if (esoc->statusline_not_a_powersource == false) gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0); esoc_clink_queue_request(ESOC_REQ_SHUTDOWN, esoc); mdm_power_down(mdm); Loading @@ -250,9 +273,12 @@ static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc) */ 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; Loading Loading @@ -380,6 +406,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; Loading Loading @@ -422,6 +450,7 @@ static irqreturn_t mdm_errfatal(int irq, void *dev_id) esoc = mdm->esoc; dev_err(dev, "%s: mdm sent errfatal interrupt\n", __func__); subsys_set_crash_status(esoc->subsys_dev, true); /* disable irq ?*/ esoc_clink_evt_notify(ESOC_ERR_FATAL, esoc); return IRQ_HANDLED; Loading @@ -442,11 +471,26 @@ 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"); subsys_set_crash_status(esoc->subsys_dev, true); 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; Loading @@ -454,6 +498,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; } Loading Loading @@ -582,13 +628,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__); Loading Loading @@ -621,9 +675,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); Loading @@ -646,6 +697,7 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev) goto errfatal_err; } mdm->errfatal_irq = irq; irq_set_irq_wake(mdm->errfatal_irq, 1); errfatal_err: /* status irq */ Loading @@ -664,6 +716,7 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev) goto status_err; } mdm->status_irq = irq; irq_set_irq_wake(mdm->status_irq, 1); status_err: if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) { irq = platform_get_irq_byname(pdev, "plbrdy_irq"); Loading
drivers/esoc/esoc-mdm-drv.c +28 −8 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -74,7 +75,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); Loading Loading @@ -164,8 +172,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); } Loading @@ -190,8 +199,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; } Loading Loading @@ -219,10 +237,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); } Loading
drivers/esoc/esoc-mdm-pon.c +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 Loading Loading @@ -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*/ mdelay(50); Loading Loading @@ -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); Loading
drivers/esoc/esoc.h +10 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,12 @@ 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. * @statusline_not_a_powersource: True if status line to esoc is not a * power source. * @userspace_handle_shutdown: True if user space handles shutdown requests. */ struct esoc_clink { const char *name; Loading @@ -79,6 +85,10 @@ struct esoc_clink { struct subsys_desc subsys; struct subsys_device *subsys_dev; struct device_node *np; bool auto_boot; bool primary; bool statusline_not_a_powersource; bool userspace_handle_shutdown; }; /** Loading