diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h index 2090aa21755f..9e740d21f82f 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_com.h +++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h @@ -359,6 +359,7 @@ struct ep_pcie_dev_t { bool m2_autonomous; bool mhi_soc_reset_en; bool aoss_rst_clear; + bool avoid_reboot_in_d3hot; u32 dbi_base_reg; u32 slv_space_reg; u32 phy_status_reg; diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index 13f49bc235d1..94149541e3f5 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -3190,6 +3193,49 @@ int ep_pcie_core_config_db_routing(struct ep_pcie_db_config chdb_cfg, return 0; } +static int ep_pcie_core_panic_reboot_callback(struct notifier_block *nb, + unsigned long reason, void *arg) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + u32 mhi_syserr = BIT(2)|(0xff << 8); + unsigned long irqsave_flags; + + if (!ep_pcie_dev.avoid_reboot_in_d3hot) + goto out; + + /* If the device is in D3hot state, bring it to D0 */ + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + if (dev->l23_ready && atomic_read(&dev->perst_deast)) { + + EP_PCIE_INFO(dev, + "PCIe V%d got %s notification while in D3hot\n", + dev->rev, reason ? "reboot":"panic/die"); + + /* Set MHI to SYSERR state */ + if (dev->config_mmio_init) + ep_pcie_write_reg(dev->mmio, PCIE20_MHISTATUS, + mhi_syserr); + /* Bring device out of D3hot */ + ep_pcie_core_issue_inband_pme(); + } + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); + +out: + return NOTIFY_DONE; +} + +static struct notifier_block ep_pcie_core_reboot_notifier = { + .notifier_call = ep_pcie_core_panic_reboot_callback, +}; + +static struct notifier_block ep_pcie_core_die_notifier = { + .notifier_call = ep_pcie_core_panic_reboot_callback, +}; + +static struct notifier_block ep_pcie_core_panic_notifier = { + .notifier_call = ep_pcie_core_panic_reboot_callback, +}; + struct ep_pcie_hw hw_drv = { .register_event = ep_pcie_core_register_event, .deregister_event = ep_pcie_core_deregister_event, @@ -3361,6 +3407,13 @@ static int ep_pcie_probe(struct platform_device *pdev) "PCIe V%d: MHI M2 autonomous is %s enabled\n", ep_pcie_dev.rev, ep_pcie_dev.m2_autonomous ? "" : "not"); + ep_pcie_dev.avoid_reboot_in_d3hot = + of_property_read_bool((&pdev->dev)->of_node, + "qcom,avoid-reboot-in-d3hot"); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: PME during reboot/panic (in D3hot) is %s needed\n", + ep_pcie_dev.rev, ep_pcie_dev.avoid_reboot_in_d3hot ? "" : "not"); + ret = of_property_read_u32((&pdev->dev)->of_node, "qcom,mhi-soc-reset-offset", &ep_pcie_dev.mhi_soc_reset_offset); @@ -3453,6 +3506,12 @@ static int ep_pcie_probe(struct platform_device *pdev) if (ret && !ep_pcie_debug_keep_resource) goto irq_deinit; + register_reboot_notifier(&ep_pcie_core_reboot_notifier); + /* Handler for wilful crash like BUG_ON */ + register_die_notifier(&ep_pcie_core_die_notifier); + atomic_notifier_chain_register(&panic_notifier_list, + &ep_pcie_core_panic_notifier); + qcom_edma_init(&pdev->dev); if (!ep_pcie_dev.perst_enum) @@ -3476,6 +3535,11 @@ static int __exit ep_pcie_remove(struct platform_device *pdev) { pr_debug("%s\n", __func__); + unregister_reboot_notifier(&ep_pcie_core_reboot_notifier); + unregister_die_notifier(&ep_pcie_core_die_notifier); + atomic_notifier_chain_unregister(&panic_notifier_list, + &ep_pcie_core_panic_notifier); + ep_pcie_irq_deinit(&ep_pcie_dev); ep_pcie_vreg_deinit(&ep_pcie_dev); ep_pcie_pipe_clk_deinit(&ep_pcie_dev);