Loading drivers/esoc/esoc-mdm-4x.c +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 Loading Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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) Loading @@ -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; } Loading Loading @@ -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__); Loading Loading @@ -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); Loading Loading @@ -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"); Loading Loading @@ -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"); Loading Loading @@ -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"); Loading Loading @@ -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, }; Loading drivers/esoc/esoc-mdm-drv.c +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 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 @@ -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); Loading Loading @@ -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); } Loading @@ -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; } Loading Loading @@ -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); } 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*/ msleep(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 +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 Loading Loading @@ -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 Loading @@ -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; Loading @@ -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); }; Loading drivers/esoc/esoc_bus.c +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 Loading Loading @@ -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 Loading
drivers/esoc/esoc-mdm-4x.c +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 Loading Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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) Loading @@ -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; } Loading Loading @@ -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__); Loading Loading @@ -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); Loading Loading @@ -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"); Loading Loading @@ -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"); Loading Loading @@ -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"); Loading Loading @@ -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, }; Loading
drivers/esoc/esoc-mdm-drv.c +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 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 @@ -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); Loading Loading @@ -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); } Loading @@ -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; } Loading Loading @@ -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); } 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*/ msleep(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 +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 Loading Loading @@ -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 Loading @@ -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; Loading @@ -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); }; Loading
drivers/esoc/esoc_bus.c +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 Loading Loading @@ -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