msm: ep_pcie: Send pme from panic/reboot handler while in D3hot

Send pme from panic/reboot handler if the device is in D3hot to
bring device state back to D0. This helps PBL to take retrain
the link gracefully as PBL didn't have ability to bring the device
our of D3hot state. Also send MHI to syserr, so that MHI host
wont timeout on M0ack.

Change-Id: I3de45cab30caa332bd00abe8b1ed1487d7889d5f
Signed-off-by: Veerabhadrarao Badiganti <quic_vbadigan@quicinc.com>
This commit is contained in:
Veerabhadrarao Badiganti 2021-09-22 23:23:59 +05:30
commit 9bc84cff6e
2 changed files with 65 additions and 0 deletions

View file

@ -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;

View file

@ -21,6 +21,9 @@
#include <linux/of_gpio.h>
#include <linux/clk/qcom.h>
#include <linux/reset.h>
#include <linux/reboot.h>
#include <linux/notifier.h>
#include <linux/kdebug.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/interconnect.h>
@ -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);