Loading Documentation/devicetree/bindings/arm/msm/mdm-modem.txt +2 −2 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ to be reset. Required Properties: - compatible: The bus devices need to be compatible with "qcom,mdm2-modem", "qcom,ext-mdm9x25", "qcom,ext-mdm9x35", "qcom,ext-mdm9x45", "qcom,ext-mdm9x55". "qcom,ext-mdm9x55", "qcom,ext-apq8096". Required named gpio properties: - qcom,mdm2ap-errfatal-gpio: gpio for the external modem to indicate to the apps processor Loading drivers/esoc/esoc-mdm-4x.c +237 −12 Original line number Diff line number Diff line 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 @@ -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; Loading @@ -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; } Loading Loading @@ -481,7 +522,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 @@ -489,7 +530,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 Loading @@ -573,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__); Loading Loading @@ -612,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); Loading Loading @@ -748,6 +803,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 @@ -818,6 +874,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 @@ -888,6 +945,80 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm, return 0; } static int mdm9x45_setup_hw(struct mdm_ctrl *mdm, const struct mdm_ops *ops, struct platform_device *pdev) { int ret; struct esoc_clink *esoc; const struct esoc_clink_ops *const clink_ops = ops->clink_ops; const struct mdm_pon_ops *pon_ops = ops->pon_ops; mdm->dev = &pdev->dev; mdm->pon_ops = pon_ops; esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); if (IS_ERR_OR_NULL(esoc)) { 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"); return -ENOMEM; } mdm->irq_mask = 0; mdm->ready = false; ret = mdm_dt_parse_gpios(mdm); if (ret) return ret; dev_err(mdm->dev, "parsing gpio done\n"); ret = mdm_pon_dt_init(mdm); if (ret) return ret; dev_dbg(mdm->dev, "pon dt init done\n"); ret = mdm_pinctrl_init(mdm); if (ret) return ret; dev_err(mdm->dev, "pinctrl init done\n"); ret = mdm_pon_setup(mdm); if (ret) return ret; dev_dbg(mdm->dev, "pon setup done\n"); ret = mdm_configure_ipc(mdm, pdev); if (ret) return ret; mdm_configure_debug(mdm); dev_err(mdm->dev, "ipc configure done\n"); esoc->name = MDM9x45_LABEL; esoc->link_name = MDM9x45_PCIE; esoc->clink_ops = clink_ops; esoc->parent = mdm->dev; esoc->owner = THIS_MODULE; esoc->np = pdev->dev.of_node; esoc->auto_boot = of_property_read_bool(esoc->np, "qcom,mdm-auto-boot"); set_esoc_clink_data(esoc, mdm); ret = esoc_clink_register(esoc); if (ret) { dev_err(mdm->dev, "esoc registration failed\n"); return ret; } dev_dbg(mdm->dev, "esoc registration done\n"); init_completion(&mdm->debug_done); INIT_WORK(&mdm->mdm_status_work, mdm_status_fn); INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason); INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check); mdm->get_restart_reason = false; mdm->debug_fail = false; mdm->esoc = esoc; mdm->init = 0; if (esoc->auto_boot) gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1); return 0; } static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, const struct mdm_ops *ops, struct platform_device *pdev) Loading @@ -906,6 +1037,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 @@ -963,9 +1095,86 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, return 0; } static int apq8096_setup_hw(struct mdm_ctrl *mdm, const struct mdm_ops *ops, struct platform_device *pdev) { int ret; struct device_node *node; struct esoc_clink *esoc; const struct esoc_clink_ops *const clink_ops = ops->clink_ops; const struct mdm_pon_ops *pon_ops = ops->pon_ops; mdm->dev = &pdev->dev; mdm->pon_ops = pon_ops; node = pdev->dev.of_node; esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); if (IS_ERR_OR_NULL(esoc)) { 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"); return -ENOMEM; } mdm->irq_mask = 0; mdm->ready = false; ret = mdm_dt_parse_gpios(mdm); if (ret) return ret; dev_dbg(mdm->dev, "parsing gpio done\n"); ret = mdm_pon_dt_init(mdm); if (ret) return ret; dev_dbg(mdm->dev, "pon dt init done\n"); ret = mdm_pinctrl_init(mdm); if (ret) return ret; dev_dbg(mdm->dev, "pinctrl init done\n"); ret = mdm_pon_setup(mdm); if (ret) return ret; dev_dbg(mdm->dev, "pon setup done\n"); ret = mdm_configure_ipc(mdm, pdev); if (ret) return ret; dev_dbg(mdm->dev, "ipc configure done\n"); esoc->name = APQ8096_LABEL; esoc->link_name = APQ8096_PCIE; esoc->clink_ops = clink_ops; esoc->parent = mdm->dev; esoc->owner = THIS_MODULE; esoc->np = pdev->dev.of_node; esoc->auto_boot = of_property_read_bool(esoc->np, "qcom,mdm-auto-boot"); esoc->primary = of_property_read_bool(esoc->np, "qcom,mdm-primary"); set_esoc_clink_data(esoc, mdm); ret = esoc_clink_register(esoc); if (ret) { dev_err(mdm->dev, "esoc registration failed\n"); return ret; } dev_dbg(mdm->dev, "esoc registration done\n"); init_completion(&mdm->debug_done); INIT_WORK(&mdm->mdm_status_work, mdm_status_fn); INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason); INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check); mdm->get_restart_reason = false; mdm->debug_fail = false; mdm->esoc = esoc; mdm->init = 0; gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); return 0; } 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 @@ -981,6 +1190,18 @@ static struct mdm_ops mdm9x35_ops = { .pon_ops = &mdm9x35_pon_ops, }; static struct mdm_ops mdm9x45_ops = { .clink_ops = &mdm_cops, .config_hw = mdm9x45_setup_hw, .pon_ops = &mdm9x45_pon_ops, }; static struct mdm_ops apq8096_ops = { .clink_ops = &mdm_cops, .config_hw = apq8096_setup_hw, .pon_ops = &apq8096_pon_ops, }; static struct mdm_ops mdm9x55_ops = { .clink_ops = &mdm_cops, .config_hw = mdm9x55_setup_hw, Loading @@ -992,8 +1213,12 @@ static const struct of_device_id mdm_dt_match[] = { .data = &mdm9x25_ops, }, { .compatible = "qcom,ext-mdm9x35", .data = &mdm9x35_ops, }, { .compatible = "qcom,ext-mdm9x45", .data = &mdm9x45_ops, }, { .compatible = "qcom,ext-mdm9x55", .data = &mdm9x55_ops, }, { .compatible = "qcom,ext-apq8096", .data = &apq8096_ops, }, {}, }; MODULE_DEVICE_TABLE(of, mdm_dt_match); Loading drivers/esoc/esoc-mdm-drv.c +36 −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 @@ -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 Loading @@ -286,6 +306,14 @@ static struct esoc_compat compat_table[] = { .name = "MDM9x55", .data = NULL, }, { .name = "MDM9x45", .data = NULL, }, { .name = "APQ8096", .data = NULL, }, }; static struct esoc_drv esoc_ssr_drv = { Loading drivers/esoc/esoc-mdm-pon.c +36 −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 @@ -152,6 +158,11 @@ static void mdm9x55_cold_reset(struct mdm_ctrl *mdm) !mdm->soft_reset_inverted); } static int apq8096_pon_dt_init(struct mdm_ctrl *mdm) { return 0; } static int mdm4x_pon_dt_init(struct mdm_ctrl *mdm) { int val; Loading Loading @@ -183,6 +194,21 @@ static int mdm4x_pon_setup(struct mdm_ctrl *mdm) return 0; } /* This function can be called from atomic context. */ static int apq8096_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic) { return 0; } static int apq8096_power_down(struct mdm_ctrl *mdm) { return 0; } static void apq8096_cold_reset(struct mdm_ctrl *mdm) { } struct mdm_pon_ops mdm9x25_pon_ops = { .pon = mdm4x_do_first_power_on, .soft_reset = mdm4x_toggle_soft_reset, Loading Loading @@ -218,3 +244,12 @@ struct mdm_pon_ops mdm9x55_pon_ops = { .dt_init = mdm4x_pon_dt_init, .setup = mdm4x_pon_setup, }; struct mdm_pon_ops apq8096_pon_ops = { .pon = mdm4x_do_first_power_on, .soft_reset = apq8096_toggle_soft_reset, .poff_force = apq8096_power_down, .cold_reset = apq8096_cold_reset, .dt_init = apq8096_pon_dt_init, .setup = mdm4x_pon_setup, }; drivers/esoc/esoc-mdm.h +4 −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 @@ -37,6 +37,8 @@ #define MDM9x45_PCIE "PCIe" #define MDM9x55_LABEL "MDM9x55" #define MDM9x55_PCIE "PCIe" #define APQ8096_LABEL "APQ8096" #define APQ8096_PCIE "PCIe" #define MDM2AP_STATUS_TIMEOUT_MS 120000L #define MDM_MODEM_TIMEOUT 3000 #define DEF_RAMDUMP_TIMEOUT 120000 Loading Loading @@ -153,4 +155,5 @@ extern struct mdm_pon_ops mdm9x25_pon_ops; extern struct mdm_pon_ops mdm9x35_pon_ops; extern struct mdm_pon_ops mdm9x45_pon_ops; extern struct mdm_pon_ops mdm9x55_pon_ops; extern struct mdm_pon_ops apq8096_pon_ops; #endif Loading
Documentation/devicetree/bindings/arm/msm/mdm-modem.txt +2 −2 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ to be reset. Required Properties: - compatible: The bus devices need to be compatible with "qcom,mdm2-modem", "qcom,ext-mdm9x25", "qcom,ext-mdm9x35", "qcom,ext-mdm9x45", "qcom,ext-mdm9x55". "qcom,ext-mdm9x55", "qcom,ext-apq8096". Required named gpio properties: - qcom,mdm2ap-errfatal-gpio: gpio for the external modem to indicate to the apps processor Loading
drivers/esoc/esoc-mdm-4x.c +237 −12 Original line number Diff line number Diff line 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 @@ -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; Loading @@ -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; } Loading Loading @@ -481,7 +522,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 @@ -489,7 +530,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 Loading @@ -573,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__); Loading Loading @@ -612,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); Loading Loading @@ -748,6 +803,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 @@ -818,6 +874,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 @@ -888,6 +945,80 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm, return 0; } static int mdm9x45_setup_hw(struct mdm_ctrl *mdm, const struct mdm_ops *ops, struct platform_device *pdev) { int ret; struct esoc_clink *esoc; const struct esoc_clink_ops *const clink_ops = ops->clink_ops; const struct mdm_pon_ops *pon_ops = ops->pon_ops; mdm->dev = &pdev->dev; mdm->pon_ops = pon_ops; esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); if (IS_ERR_OR_NULL(esoc)) { 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"); return -ENOMEM; } mdm->irq_mask = 0; mdm->ready = false; ret = mdm_dt_parse_gpios(mdm); if (ret) return ret; dev_err(mdm->dev, "parsing gpio done\n"); ret = mdm_pon_dt_init(mdm); if (ret) return ret; dev_dbg(mdm->dev, "pon dt init done\n"); ret = mdm_pinctrl_init(mdm); if (ret) return ret; dev_err(mdm->dev, "pinctrl init done\n"); ret = mdm_pon_setup(mdm); if (ret) return ret; dev_dbg(mdm->dev, "pon setup done\n"); ret = mdm_configure_ipc(mdm, pdev); if (ret) return ret; mdm_configure_debug(mdm); dev_err(mdm->dev, "ipc configure done\n"); esoc->name = MDM9x45_LABEL; esoc->link_name = MDM9x45_PCIE; esoc->clink_ops = clink_ops; esoc->parent = mdm->dev; esoc->owner = THIS_MODULE; esoc->np = pdev->dev.of_node; esoc->auto_boot = of_property_read_bool(esoc->np, "qcom,mdm-auto-boot"); set_esoc_clink_data(esoc, mdm); ret = esoc_clink_register(esoc); if (ret) { dev_err(mdm->dev, "esoc registration failed\n"); return ret; } dev_dbg(mdm->dev, "esoc registration done\n"); init_completion(&mdm->debug_done); INIT_WORK(&mdm->mdm_status_work, mdm_status_fn); INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason); INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check); mdm->get_restart_reason = false; mdm->debug_fail = false; mdm->esoc = esoc; mdm->init = 0; if (esoc->auto_boot) gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1); return 0; } static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, const struct mdm_ops *ops, struct platform_device *pdev) Loading @@ -906,6 +1037,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 @@ -963,9 +1095,86 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, return 0; } static int apq8096_setup_hw(struct mdm_ctrl *mdm, const struct mdm_ops *ops, struct platform_device *pdev) { int ret; struct device_node *node; struct esoc_clink *esoc; const struct esoc_clink_ops *const clink_ops = ops->clink_ops; const struct mdm_pon_ops *pon_ops = ops->pon_ops; mdm->dev = &pdev->dev; mdm->pon_ops = pon_ops; node = pdev->dev.of_node; esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); if (IS_ERR_OR_NULL(esoc)) { 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"); return -ENOMEM; } mdm->irq_mask = 0; mdm->ready = false; ret = mdm_dt_parse_gpios(mdm); if (ret) return ret; dev_dbg(mdm->dev, "parsing gpio done\n"); ret = mdm_pon_dt_init(mdm); if (ret) return ret; dev_dbg(mdm->dev, "pon dt init done\n"); ret = mdm_pinctrl_init(mdm); if (ret) return ret; dev_dbg(mdm->dev, "pinctrl init done\n"); ret = mdm_pon_setup(mdm); if (ret) return ret; dev_dbg(mdm->dev, "pon setup done\n"); ret = mdm_configure_ipc(mdm, pdev); if (ret) return ret; dev_dbg(mdm->dev, "ipc configure done\n"); esoc->name = APQ8096_LABEL; esoc->link_name = APQ8096_PCIE; esoc->clink_ops = clink_ops; esoc->parent = mdm->dev; esoc->owner = THIS_MODULE; esoc->np = pdev->dev.of_node; esoc->auto_boot = of_property_read_bool(esoc->np, "qcom,mdm-auto-boot"); esoc->primary = of_property_read_bool(esoc->np, "qcom,mdm-primary"); set_esoc_clink_data(esoc, mdm); ret = esoc_clink_register(esoc); if (ret) { dev_err(mdm->dev, "esoc registration failed\n"); return ret; } dev_dbg(mdm->dev, "esoc registration done\n"); init_completion(&mdm->debug_done); INIT_WORK(&mdm->mdm_status_work, mdm_status_fn); INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason); INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check); mdm->get_restart_reason = false; mdm->debug_fail = false; mdm->esoc = esoc; mdm->init = 0; gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); return 0; } 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 @@ -981,6 +1190,18 @@ static struct mdm_ops mdm9x35_ops = { .pon_ops = &mdm9x35_pon_ops, }; static struct mdm_ops mdm9x45_ops = { .clink_ops = &mdm_cops, .config_hw = mdm9x45_setup_hw, .pon_ops = &mdm9x45_pon_ops, }; static struct mdm_ops apq8096_ops = { .clink_ops = &mdm_cops, .config_hw = apq8096_setup_hw, .pon_ops = &apq8096_pon_ops, }; static struct mdm_ops mdm9x55_ops = { .clink_ops = &mdm_cops, .config_hw = mdm9x55_setup_hw, Loading @@ -992,8 +1213,12 @@ static const struct of_device_id mdm_dt_match[] = { .data = &mdm9x25_ops, }, { .compatible = "qcom,ext-mdm9x35", .data = &mdm9x35_ops, }, { .compatible = "qcom,ext-mdm9x45", .data = &mdm9x45_ops, }, { .compatible = "qcom,ext-mdm9x55", .data = &mdm9x55_ops, }, { .compatible = "qcom,ext-apq8096", .data = &apq8096_ops, }, {}, }; MODULE_DEVICE_TABLE(of, mdm_dt_match); Loading
drivers/esoc/esoc-mdm-drv.c +36 −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 @@ -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 Loading @@ -286,6 +306,14 @@ static struct esoc_compat compat_table[] = { .name = "MDM9x55", .data = NULL, }, { .name = "MDM9x45", .data = NULL, }, { .name = "APQ8096", .data = NULL, }, }; static struct esoc_drv esoc_ssr_drv = { Loading
drivers/esoc/esoc-mdm-pon.c +36 −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 @@ -152,6 +158,11 @@ static void mdm9x55_cold_reset(struct mdm_ctrl *mdm) !mdm->soft_reset_inverted); } static int apq8096_pon_dt_init(struct mdm_ctrl *mdm) { return 0; } static int mdm4x_pon_dt_init(struct mdm_ctrl *mdm) { int val; Loading Loading @@ -183,6 +194,21 @@ static int mdm4x_pon_setup(struct mdm_ctrl *mdm) return 0; } /* This function can be called from atomic context. */ static int apq8096_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic) { return 0; } static int apq8096_power_down(struct mdm_ctrl *mdm) { return 0; } static void apq8096_cold_reset(struct mdm_ctrl *mdm) { } struct mdm_pon_ops mdm9x25_pon_ops = { .pon = mdm4x_do_first_power_on, .soft_reset = mdm4x_toggle_soft_reset, Loading Loading @@ -218,3 +244,12 @@ struct mdm_pon_ops mdm9x55_pon_ops = { .dt_init = mdm4x_pon_dt_init, .setup = mdm4x_pon_setup, }; struct mdm_pon_ops apq8096_pon_ops = { .pon = mdm4x_do_first_power_on, .soft_reset = apq8096_toggle_soft_reset, .poff_force = apq8096_power_down, .cold_reset = apq8096_cold_reset, .dt_init = apq8096_pon_dt_init, .setup = mdm4x_pon_setup, };
drivers/esoc/esoc-mdm.h +4 −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 @@ -37,6 +37,8 @@ #define MDM9x45_PCIE "PCIe" #define MDM9x55_LABEL "MDM9x55" #define MDM9x55_PCIE "PCIe" #define APQ8096_LABEL "APQ8096" #define APQ8096_PCIE "PCIe" #define MDM2AP_STATUS_TIMEOUT_MS 120000L #define MDM_MODEM_TIMEOUT 3000 #define DEF_RAMDUMP_TIMEOUT 120000 Loading Loading @@ -153,4 +155,5 @@ extern struct mdm_pon_ops mdm9x25_pon_ops; extern struct mdm_pon_ops mdm9x35_pon_ops; extern struct mdm_pon_ops mdm9x45_pon_ops; extern struct mdm_pon_ops mdm9x55_pon_ops; extern struct mdm_pon_ops apq8096_pon_ops; #endif