soc: tegra: add t21x panic handler

Some t21x bootloaders like cboot read PMC_SCRATCH37 to check if a
reboot was due to a kernel panic. Support this functionality with
a panic notifier. Original implementation relied on single bit 24
but a specific magic was chosen in later improvement to prevent
false positive from random data in scratch register during reboot.

Based on:
- https://gitlab.com/nvidia/nv-tegra/linux-4.9/-/commit/31b412a5fc80ed0fe33f3ad4e7cc779ce66446a2
- https://github.com/CTCaer/switch-l4t-kernel-4.9/commit/b74fee3edf8053b0b954643d3bd065596cee8aca

Co-developed-by: Nat Pettipaw <npettipaw@nvidia.com>
Co-developed-by: CTCaer <ctcaer@gmail.com>
This commit is contained in:
tmakin committed 2026-10-16 10:05:18 -05:00
1 parent 4d226a839b
commit 33b21c2d6c
1 file changed
+28
+28
View File
@@ -35,6 +35,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/panic_notifier.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
@@ -111,6 +112,8 @@
#define PMC_PWR_DET_VALUE 0xe4
#define KERNEL_PANIC_MAGIC 0x4E415054
#define PMC_USB_DEBOUNCE_DEL 0xec
#define PMC_USB_AO 0xf0
@@ -419,6 +422,7 @@ struct tegra_pmc_soc {
bool has_usb_sleepwalk;
bool supports_core_domain;
bool has_single_mmio_aperture;
bool has_panic_notifier;
};
/**
@@ -505,6 +509,7 @@ struct tegra_pmc {
unsigned long *wake_cntrl_level_map;
struct notifier_block reboot_notifier;
struct notifier_block panic_notifier;
struct syscore syscore;
/* Pending wake IRQ processing */
@@ -1310,6 +1315,13 @@ static int tegra_pmc_restart_handler(struct sys_off_data *data)
return NOTIFY_DONE;
}
static int tegra_pmc_panic_handler(struct notifier_block *this,
unsigned long action, void *data)
{
tegra_pmc_writel(pmc, KERNEL_PANIC_MAGIC, PMC_SCRATCH37);
return NOTIFY_DONE;
}
static int tegra_pmc_power_off_handler(struct sys_off_data *data)
{
struct tegra_pmc *pmc = data->cb_data;
@@ -3199,6 +3211,20 @@ static int tegra_pmc_probe(struct platform_device *pdev)
}
}
if (pmc->soc->has_panic_notifier) {
pmc->panic_notifier.notifier_call = tegra_pmc_panic_handler;
pmc->panic_notifier.priority = INT_MAX-1;
err = atomic_notifier_chain_register(&panic_notifier_list,
&pmc->panic_notifier);
if (err) {
dev_err(&pdev->dev,
"unable to register panic notifier, %d\n",
err);
return err;
}
}
err = devm_register_sys_off_handler(&pdev->dev,
SYS_OFF_MODE_RESTART,
SYS_OFF_PRIO_LOW,
@@ -4018,6 +4044,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
.has_blink_output = true,
.has_usb_sleepwalk = true,
.has_single_mmio_aperture = true,
.has_panic_notifier = true,
};
static const char * const tegra210b01_powergates[] = {
@@ -4079,6 +4106,7 @@ static const struct tegra_pmc_soc tegra210b01_pmc_soc = {
.has_blink_output = true,
.has_usb_sleepwalk = true,
.has_single_mmio_aperture = true,
.has_panic_notifier = true,
};
static const struct tegra_io_pad_soc tegra186_io_pads[] = {