static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev) { int ret = -1; int irq; struct device *dev = mdm->dev; struct device_node *node = pdev->dev.of_node; ret = of_property_read_u32(node, "qcom,ramdump-timeout-ms", &mdm->dump_timeout_ms); if (ret) mdm->dump_timeout_ms = DEF_RAMDUMP_TIMEOUT; ret = of_property_read_u32(node, "qcom,ramdump-delay-ms", &mdm->ramdump_delay_ms); if (ret) mdm->ramdump_delay_ms = DEF_RAMDUMP_DELAY; /* Multilple gpio_request calls are allowed */ 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 */ if (gpio_request(MDM_GPIO(mdm, AP2MDM_ERRFATAL), "AP2MDM_ERRFATAL")) dev_err(dev, "%s Failed to configure AP2MDM_ERRFATAL gpio\n", __func__); if (gpio_request(MDM_GPIO(mdm, MDM2AP_STATUS), "MDM2AP_STATUS")) { dev_err(dev, "%s Failed to configure MDM2AP_STATUS gpio\n", __func__); goto fatal_err; } if (gpio_request(MDM_GPIO(mdm, MDM2AP_ERRFATAL), "MDM2AP_ERRFATAL")) { dev_err(dev, "%s Failed to configure MDM2AP_ERRFATAL gpio\n", __func__); goto fatal_err; } if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) { if (gpio_request(MDM_GPIO(mdm, MDM2AP_PBLRDY), "MDM2AP_PBLRDY")) { dev_err(dev, "Cannot configure MDM2AP_PBLRDY gpio\n"); goto fatal_err; } } if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET))) { if (gpio_request(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), "AP2MDM_SOFT_RESET")) { dev_err(dev, "Cannot config AP2MDM_SOFT_RESET gpio\n"); goto fatal_err; } } if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_WAKEUP))) { if (gpio_request(MDM_GPIO(mdm, AP2MDM_WAKEUP), "AP2MDM_WAKEUP")) { dev_err(dev, "Cannot configure AP2MDM_WAKEUP gpio\n"); goto fatal_err; } } if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY))) { if (gpio_request(MDM_GPIO(mdm, AP2MDM_CHNLRDY), "AP2MDM_CHNLRDY")) { dev_err(dev, "Cannot configure AP2MDM_CHNLRDY gpio\n"); goto fatal_err; } } ret = of_property_read_u32(node, "qcom,sysmon-subsys-id", &mdm->sysmon_subsys_id); if (ret < 0) dev_info(dev, "sysmon_subsys_id not set.\n"); 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); gpio_direction_input(MDM_GPIO(mdm, MDM2AP_STATUS)); gpio_direction_input(MDM_GPIO(mdm, MDM2AP_ERRFATAL)); /* ERR_FATAL irq. */ irq = platform_get_irq_byname(pdev, "err_fatal_irq"); if (irq < 0) { dev_err(dev, "bad MDM2AP_ERRFATAL IRQ resource\n"); goto errfatal_err; } ret = request_irq(irq, mdm_errfatal, IRQF_TRIGGER_RISING , "mdm errfatal", mdm); if (ret < 0) { dev_err(dev, "%s: MDM2AP_ERRFATAL IRQ#%d request failed,\n", __func__, irq); goto errfatal_err; } mdm->errfatal_irq = irq; errfatal_err: /* status irq */ irq = platform_get_irq_byname(pdev, "status_irq"); if (irq < 0) { dev_err(dev, "%s: bad MDM2AP_STATUS IRQ resource, err = %d\n", __func__, irq); goto status_err; } ret = request_threaded_irq(irq, NULL, mdm_status_change, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mdm status", mdm); if (ret < 0) { dev_err(dev, "%s: MDM2AP_STATUS IRQ#%d request failed, err=%d", __func__, irq, ret); goto status_err; } mdm->status_irq = irq; status_err: if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) { irq = platform_get_irq_byname(pdev, "plbrdy_irq"); if (irq < 0) { dev_err(dev, "%s: MDM2AP_PBLRDY IRQ request failed\n", __func__); goto pblrdy_err; } ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "mdm pbl ready", mdm); if (ret < 0) { dev_err(dev, "MDM2AP_PBL IRQ#%d request failed %d\n", irq, ret); goto pblrdy_err; } mdm->pblrdy_irq = irq; } mdm_disable_irqs(mdm); pblrdy_err: return 0; fatal_err: mdm_deconfigure_ipc(mdm); return ret; }
static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc) { int ret; unsigned long end_time; bool status_down = false; struct mdm_ctrl *mdm = get_esoc_clink_data(esoc); struct device *dev = mdm->dev; switch (cmd) { case ESOC_PWR_ON: gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); mdm_enable_irqs(mdm); mdm->init = 1; mdm_do_first_power_on(mdm); break; case ESOC_PWR_OFF: mdm_disable_irqs(mdm); mdm->debug = 0; mdm->ready = false; ret = sysmon_send_shutdown(mdm->sysmon_subsys_id); device_lock(dev); if (ret) dev_err(mdm->dev, "Graceful shutdown fail, ret = %d\n", ret); else { dev_info(mdm->dev, "Waiting for status gpio go low\n"); status_down = false; end_time = jiffies + msecs_to_jiffies(10000); while (time_before(jiffies, end_time)) { if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0) { dev_dbg(dev, "Status went low\n"); status_down = true; break; } msleep(100); } if (status_down) dev_info(dev, "shutdown successful\n"); else dev_err(mdm->dev, "graceful poff ipc fail\n"); } /* * Force a shutdown of the mdm. This is required in order * to prevent the mdm from immediately powering back on * after the shutdown */ gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0); esoc_clink_queue_request(ESOC_REQ_SHUTDOWN, esoc); mdm_power_down(mdm); mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG); device_unlock(dev); break; case ESOC_RESET: mdm_toggle_soft_reset(mdm); break; case ESOC_PREPARE_DEBUG: /* * disable all irqs except request irq (pblrdy) * force a reset of the mdm by signaling * an APQ crash, wait till mdm is ready for ramdumps. */ mdm->ready = false; cancel_delayed_work(&mdm->mdm2ap_status_check_work); gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); dev_info(mdm->dev, "set ap2mdm errfatal to force reset\n"); msleep(mdm->ramdump_delay_ms); break; case ESOC_EXE_DEBUG: mdm->debug = 1; mdm_toggle_soft_reset(mdm); /* * wait for ramdumps to be collected * then power down the mdm and switch gpios to booting * config */ if (!wait_for_completion_timeout(&mdm->debug_done, msecs_to_jiffies(mdm->dump_timeout_ms))) { dev_err(mdm->dev, "ramdump collection timedout\n"); mdm->debug = 0; return -ETIMEDOUT; } if (mdm->debug_fail) { dev_err(mdm->dev, "unable to collect ramdumps\n"); mdm->debug = 0; return -EIO; } dev_dbg(mdm->dev, "ramdump collection done\n"); mdm->debug = 0; init_completion(&mdm->debug_done); break; case ESOC_EXIT_DEBUG: /* * Deassert APQ to mdm err fatal * Power on the mdm */ gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); dev_dbg(mdm->dev, "exiting debug state after power on\n"); mdm->get_restart_reason = true; break; default: return -EINVAL; }; return 0; }
static void mdm_notify(enum esoc_notify notify, struct esoc_clink *esoc) { bool status_down; uint64_t timeout; uint64_t now; struct mdm_ctrl *mdm = get_esoc_clink_data(esoc); struct device *dev = mdm->dev; switch (notify) { case ESOC_IMG_XFER_DONE: dev_info(dev, "%s ESOC_IMG_XFER_DONE\n", __func__); if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0) schedule_delayed_work(&mdm->mdm2ap_status_check_work, msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS)); break; case ESOC_BOOT_DONE: esoc_clink_evt_notify(ESOC_RUN_STATE, esoc); break; case ESOC_IMG_XFER_RETRY: mdm->init = 1; mdm_toggle_soft_reset(mdm); break; case ESOC_IMG_XFER_FAIL: esoc_clink_evt_notify(ESOC_BOOT_FAIL, esoc); break; case ESOC_UPGRADE_AVAILABLE: break; case ESOC_DEBUG_DONE: mdm->debug_fail = false; mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG); complete(&mdm->debug_done); break; case ESOC_DEBUG_FAIL: mdm->debug_fail = true; complete(&mdm->debug_done); break; case ESOC_PRIMARY_CRASH: mdm_disable_irqs(mdm); status_down = false; dev_info(dev, "signal apq err fatal for graceful restart\n"); gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); timeout = local_clock(); do_div(timeout, NSEC_PER_MSEC); timeout += MDM_MODEM_TIMEOUT; do { if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0) { status_down = true; break; } now = local_clock(); do_div(now, NSEC_PER_MSEC); } while (!time_after64(now, timeout)); if (!status_down) { dev_err(mdm->dev, "%s MDM2AP status did not go low\n", __func__); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), !!mdm->soft_reset_inverted); /* * allow PS hold assert to be detected. * pmic requires 6ms for crash reset case. */ mdelay(6); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), !mdm->soft_reset_inverted); } break; case ESOC_PRIMARY_REBOOT: dev_info(mdm->dev, "Triggering mdm cold reset"); mdm->ready = 0; gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), !!mdm->soft_reset_inverted); mdelay(300); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), !mdm->soft_reset_inverted); break; }; return; }
static void mdm_notify(enum esoc_notify notify, struct esoc_clink *esoc) { bool status_down; uint64_t timeout; uint64_t now; struct mdm_ctrl *mdm = get_esoc_clink_data(esoc); struct device *dev = mdm->dev; int ret; int max_spin = 20; switch (notify) { case ESOC_IMG_XFER_DONE: dev_info(dev, "%s ESOC_IMG_XFER_DONE\n", __func__); if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0) schedule_delayed_work(&mdm->mdm2ap_status_check_work, msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS)); break; case ESOC_BOOT_DONE: esoc_clink_evt_notify(ESOC_RUN_STATE, esoc); break; case ESOC_IMG_XFER_RETRY: mdm->init = 1; mdm_toggle_soft_reset(mdm); break; case ESOC_IMG_XFER_FAIL: esoc_clink_evt_notify(ESOC_INVALID_STATE, esoc); break; case ESOC_BOOT_FAIL: esoc_clink_evt_notify(ESOC_INVALID_STATE, esoc); break; case ESOC_UPGRADE_AVAILABLE: break; case ESOC_DEBUG_DONE: mdm->debug_fail = false; mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG); complete(&mdm->debug_done); break; case ESOC_DEBUG_FAIL: mdm->debug_fail = true; complete(&mdm->debug_done); break; case ESOC_PRIMARY_CRASH: mdm_disable_irqs(mdm); status_down = false; dev_info(dev, "signal apq err fatal for graceful restart\n"); gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); gpio_set_value(MDM_GPIO(mdm, AP2MDM_VDDMIN), 1); timeout = local_clock(); do_div(timeout, NSEC_PER_MSEC); timeout += MDM_MODEM_TIMEOUT; do { if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0) { status_down = true; break; } now = local_clock(); do_div(now, NSEC_PER_MSEC); } while (!time_after64(now, timeout)); if (!status_down) { dev_err(mdm->dev, "%s MDM2AP status did not go low\n", __func__); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), !!mdm->soft_reset_inverted); /* * allow PS hold assert to be detected. * pmic requires 6ms for crash reset case. */ mdelay(6); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), !mdm->soft_reset_inverted); } break; case ESOC_PRIMARY_REBOOT: exynos_pcie_disable_irq(0); mdm_disable_irqs(mdm); dev_info(mdm->dev, "Triggering mdm cold reset"); mdm->ready = 0; while (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) && max_spin--) { msleep(100); dev_info(mdm->dev, "gpio_get_value(MDM2AP_STATUS) : %d\n", gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS))); } gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), !!mdm->soft_reset_inverted); mdelay(300); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), !mdm->soft_reset_inverted); break; case ESOC_DIAG_DISABLE: dev_info(mdm->dev, "Send diag_disable noti\n"); ret = sysmon_send_diag_disable_noti(mdm->sysmon_subsys_id); if (ret < 0) dev_err(mdm->dev, "sending diag_disable noti is failed, ret = %d\n", ret); else dev_info(mdm->dev, "sending diag_disable noti is succeed.\n"); break; case ESOC_FORCE_CPCRASH: dev_err(mdm->dev, "Force CP Crash\n"); gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); gpio_set_value(MDM_GPIO(mdm, AP2MDM_VDDMIN), 1); break; case ESOC_CP_SILENT_RESET: dev_err(mdm->dev, "Force CP Silent Reset\n"); set_silent_reset(); gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); gpio_set_value(MDM_GPIO(mdm, AP2MDM_VDDMIN), 1); break; }; return; }