1. Implementation of: tegra_pmc_get_system_reset_reason and tegra_pmc_get_system_reset_level These APIs provide information about tegra reset reason and level respectively. 2. sysfs entries: /sys/devices/platform/<address>.pmc/tegra_reset_reason and /sys/devices/platform/<address>.pmc/tegra_reset_level are implemented in readonly mode to fetch tegra reset reason and tegra reset level information. Signed-off-by: Sandipan Patra <spatra@xxxxxxxxxx> --- drivers/soc/tegra/pmc.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index ab719fa..ac74fee 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -2,6 +2,7 @@ * drivers/soc/tegra/pmc.c * * Copyright (c) 2010 Google, Inc + * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross <ccross@xxxxxxxxxx> @@ -92,7 +93,8 @@ #define PMC_SENSOR_CTRL_SCRATCH_WRITE BIT(2) #define PMC_SENSOR_CTRL_ENABLE_RST BIT(1) -#define PMC_RST_STATUS 0x1b4 +#define PMC_RST_STATUS_T30 0x1b4 +#define PMC_RST_STATUS_T186 0x70 #define PMC_RST_STATUS_POR 0 #define PMC_RST_STATUS_WATCHDOG 1 #define PMC_RST_STATUS_SENSOR 2 @@ -125,6 +127,13 @@ #define GPU_RG_CNTRL 0x2d4 +#define PMC_RST_LEVEL_SHIFT_T186 0x0 +#define PMC_RST_LEVEL_MASK_T186 0x3 +#define PMC_RST_SOURCE_MASK_T186 0x3C +#define PMC_RST_SOURCE_SHIFT_T186 0x2 +#define PMC_RST_LEVEL_MASK_T30 0x7 + + /* Tegra186 and later */ #define WAKE_AOWAKE_CTRL 0x4f4 #define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) @@ -153,6 +162,12 @@ struct tegra_pmc_regs { unsigned int dpd2_status; }; +enum tegra_pmc_reset_status_type { + PMC_RESET_STATUS_TYPE_NONE, + PMC_RESET_STATUS_TYPE_T30, + PMC_RESET_STATUS_TYPE_T186 +}; + struct tegra_pmc_soc { unsigned int num_powergates; const char *const *powergates; @@ -163,6 +178,7 @@ struct tegra_pmc_soc { bool has_gpu_clamps; bool needs_mbist_war; bool has_impl_33v_pwr; + enum tegra_pmc_reset_status_type pmc_reset_status; const struct tegra_io_pad_soc *io_pads; unsigned int num_io_pads; @@ -662,6 +678,71 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid) } #endif /* CONFIG_SMP */ +/** + * tegra_pmc_get_system_reset_reason() - last reset reason status + */ +static char *tegra_pmc_get_system_reset_reason(void) +{ + u32 val, rst_src; + + if (pmc->soc->pmc_reset_status == PMC_RESET_STATUS_TYPE_T30) { + val = tegra_pmc_readl(PMC_RST_STATUS_T30); + rst_src = val & PMC_RST_LEVEL_MASK_T30; + switch (rst_src) { + case 0: return "TEGRA_POWER_ON_RESET"; + case 1: return "TEGRA_WATCHDOG"; + case 2: return "TEGRA_SENSOR"; + case 3: return "TEGRA_SOFTWARE_RESET"; + case 4: return "TEGRA_LP0"; + case 5: return "TEGRA_AOTAG"; + default: return "TEGRA_RESET_REASON_UNSPECIFIED"; + } + } else if (pmc->soc->pmc_reset_status == PMC_RESET_STATUS_TYPE_T186) { + val = tegra_pmc_readl(PMC_RST_STATUS_T186); + rst_src = (val & PMC_RST_SOURCE_MASK_T186) >> + PMC_RST_SOURCE_SHIFT_T186; + switch (rst_src) { + case 0: return "TEGRA_POWER_ON_RESET"; + case 1: return "TEGRA_AO_WATCHDOG"; + case 2: return "TEGRA_DENVER_WATCHDOG"; + case 3: return "TEGRA_BPMP_WATCHDOG"; + case 4: return "TEGRA_SCE_WATCHDOG"; + case 5: return "TEGRA_SPE_WATCHDOG"; + case 6: return "TEGRA_APE_WATCHDOG"; + case 7: return "TEGRA_A57_WATCHDOG"; + case 8: return "TEGRA_SENSOR"; + case 9: return "TEGRA_AOTAG"; + case 10: return "TEGRA_VFSENSOR"; + case 11: return "TEGRA_SOFTWARE_RESET"; + case 12: return "TEGRA_SC7"; + case 13: return "TEGRA_HSM"; + case 14: return "TEGRA_CSITE"; + default: return "TEGRA_RESET_REASON_UNSPECIFIED"; + } + } else { + return "TEGRA_RESET_REASON_NOT_SUPPORTED"; + } +} + +static char *tegra_pmc_get_system_reset_level(void) +{ + u32 val, rst_lvl; + + if (pmc->soc->pmc_reset_status == PMC_RESET_STATUS_TYPE_T186) { + val = tegra_pmc_readl(PMC_RST_STATUS_T186); + rst_lvl = (val & PMC_RST_LEVEL_MASK_T186) >> + PMC_RST_LEVEL_SHIFT_T186; + switch (rst_lvl) { + case 0: return "TEGRA_RESET_LEVEL_L0"; + case 1: return "TEGRA_RESET_LEVEL_L1"; + case 2: return "TEGRA_RESET_LEVEL_L2"; + default: return "TEGRA_RESET_LEVEL_UNSPECIFIED"; + } + } else { + return "TEGRA_RESET_LEVEL_NOT_SUPPORTED"; + } +} + static int tegra_pmc_restart_notify(struct notifier_block *this, unsigned long action, void *data) { @@ -1543,6 +1624,46 @@ static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc) return err; } +static void tegra_pmc_show_reset_status(struct device *dev) +{ + dev_info(dev, "reset source: %s\n", + tegra_pmc_get_system_reset_reason()); + dev_info(dev, "reset level: %s\n", + tegra_pmc_get_system_reset_level()); +} + +static ssize_t tegra_reset_reason_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", tegra_pmc_get_system_reset_reason()); +} + +static DEVICE_ATTR_RO(tegra_reset_reason); + +static ssize_t tegra_reset_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", tegra_pmc_get_system_reset_level()); +} + +static DEVICE_ATTR_RO(tegra_reset_level); + +static void tegra_pmc_reset_sysfs_init(struct device *dev) +{ + int error; + + error = device_create_file(dev, &dev_attr_tegra_reset_reason); + if (error) { + dev_err(dev, + "Failed to create sysfs node - tegra_reset_reason\n"); + } + error = device_create_file(dev, &dev_attr_tegra_reset_level); + if (error) { + dev_err(dev, + "Failed to create sysfs node - tegra_reset_level\n"); + } +} + static int tegra_pmc_probe(struct platform_device *pdev) { void __iomem *base; @@ -1612,6 +1733,9 @@ static int tegra_pmc_probe(struct platform_device *pdev) tegra_pmc_init_tsense_reset(pmc); + tegra_pmc_show_reset_status(&pdev->dev); + tegra_pmc_reset_sysfs_init(&pdev->dev); + if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_powergate_debugfs_init(); if (err < 0) @@ -1728,6 +1852,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { .cpu_powergates = NULL, .has_tsense_reset = false, .has_gpu_clamps = false, + .pmc_reset_status = PMC_RESET_STATUS_TYPE_NONE, .num_io_pads = 0, .io_pads = NULL, .num_pin_descs = 0, @@ -1769,6 +1894,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { .has_tsense_reset = true, .has_gpu_clamps = false, .has_impl_33v_pwr = false, + .pmc_reset_status = PMC_RESET_STATUS_TYPE_T30, .num_io_pads = 0, .io_pads = NULL, .num_pin_descs = 0, @@ -1814,6 +1940,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { .has_tsense_reset = true, .has_gpu_clamps = false, .has_impl_33v_pwr = false, + .pmc_reset_status = PMC_RESET_STATUS_TYPE_T30, .num_io_pads = 0, .io_pads = NULL, .num_pin_descs = 0, @@ -1919,6 +2046,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { .has_tsense_reset = true, .has_gpu_clamps = true, .has_impl_33v_pwr = false, + .pmc_reset_status = PMC_RESET_STATUS_TYPE_T30, .num_io_pads = ARRAY_SIZE(tegra124_io_pads), .io_pads = tegra124_io_pads, .num_pin_descs = ARRAY_SIZE(tegra124_pin_descs), @@ -2019,6 +2147,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { .has_tsense_reset = true, .has_gpu_clamps = true, .has_impl_33v_pwr = false, + .pmc_reset_status = PMC_RESET_STATUS_TYPE_T30, .needs_mbist_war = true, .num_io_pads = ARRAY_SIZE(tegra210_io_pads), .io_pads = tegra210_io_pads, @@ -2129,6 +2258,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .has_tsense_reset = false, .has_gpu_clamps = false, .has_impl_33v_pwr = true, + .pmc_reset_status = PMC_RESET_STATUS_TYPE_T186, .num_io_pads = ARRAY_SIZE(tegra186_io_pads), .io_pads = tegra186_io_pads, .num_pin_descs = ARRAY_SIZE(tegra186_pin_descs), -- 2.7.4