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. These sysfs nodes provide reset reason and reset level information on production software. Signed-off-by: Sandipan Patra <spatra@xxxxxxxxxx> --- Changes since V1: 1. Fully parameterized the registers for SoC generations to take the path unconditionally. 2. Changed the string to const char * 3. Instead of using val, changed them to value to comply with format. 4. Tegra20 : pmc reset status not used Tegra30 till Tegra210 : named as legacy version Tegra186 and + : named as Tegra186 5. No sysfs node to be exposed, when soc does not support. 6. Changed the log level to dev_warn as they are not fatal errors. drivers/soc/tegra/pmc.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index ab719fa..18ee90b 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,6 @@ #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_POR 0 #define PMC_RST_STATUS_WATCHDOG 1 #define PMC_RST_STATUS_SENSOR 2 @@ -151,6 +151,11 @@ struct tegra_pmc_regs { unsigned int dpd_status; unsigned int dpd2_req; unsigned int dpd2_status; + unsigned int rst_status; + unsigned int rst_source_shift; + unsigned int rst_source_mask; + unsigned int rst_level_shift; + unsigned int rst_level_mask; }; struct tegra_pmc_soc { @@ -175,6 +180,42 @@ struct tegra_pmc_soc { void (*setup_irq_polarity)(struct tegra_pmc *pmc, struct device_node *np, bool invert); + + const char *const *reset_sources; + unsigned int num_reset_sources; + const char *const *reset_levels; + unsigned int num_reset_levels; +}; + +static const char *const tegra186_reset_sources[] = { + "SYS_RESET", + "AOWDT", + "MCCPLEXWDT", + "BPMPWDT", + "SCEWDT", + "SPEWDT", + "APEWDT", + "BCCPLEXWDT", + "SENSOR", + "AOTAG", + "VFSENSOR", + "SWREST", + "SC7", + "HSM", + "CORESIGHT" +}; + +static const char *const tegra186_reset_levels[] = { + "L0", "L1", "L2", "WARM" +}; + +static const char *const tegra_legacy_reset_sources[] = { + "POWER_ON_RESET", + "WATCHDOG", + "SENSOR", + "SW_MAIN", + "LP0", + "AOTAG" }; /** @@ -662,6 +703,31 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid) } #endif /* CONFIG_SMP */ +/** + * tegra_pmc_get_system_reset_reason() - last reset reason status + */ +static const char *tegra_pmc_get_system_reset_reason(void) +{ + u32 value, rst_src; + + value = tegra_pmc_readl(pmc->soc->regs->rst_status); + rst_src = (value & pmc->soc->regs->rst_source_mask) >> + pmc->soc->regs->rst_source_shift; + + return pmc->soc->reset_sources[rst_src]; +} + +static const char *tegra_pmc_get_system_reset_level(void) +{ + u32 value, rst_lvl; + + value = tegra_pmc_readl(pmc->soc->regs->rst_status); + rst_lvl = (value & pmc->soc->regs->rst_level_mask) >> + pmc->soc->regs->rst_level_shift; + + return pmc->soc->reset_levels[rst_lvl]; +} + static int tegra_pmc_restart_notify(struct notifier_block *this, unsigned long action, void *data) { @@ -1543,6 +1609,40 @@ static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc) return err; } +static ssize_t 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(reset_reason); + +static ssize_t 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(reset_level); + +static void tegra_pmc_reset_sysfs_init(struct device *dev) +{ + + if (pmc->soc->reset_sources) { + if (device_create_file(dev, &dev_attr_reset_reason)) { + dev_warn(dev, + "Failed to create sysfs node - tegra_reset_reason\n"); + } + } + + if (pmc->soc->reset_levels) { + if (device_create_file(dev, &dev_attr_reset_level)) { + dev_warn(dev, + "Failed to create sysfs node - tegra_reset_level\n"); + } + } +} + static int tegra_pmc_probe(struct platform_device *pdev) { void __iomem *base; @@ -1612,6 +1712,8 @@ static int tegra_pmc_probe(struct platform_device *pdev) tegra_pmc_init_tsense_reset(pmc); + tegra_pmc_reset_sysfs_init(&pdev->dev); + if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_powergate_debugfs_init(); if (err < 0) @@ -1678,6 +1780,11 @@ static const struct tegra_pmc_regs tegra20_pmc_regs = { .dpd_status = 0x1bc, .dpd2_req = 0x1c0, .dpd2_status = 0x1c4, + .rst_status = 0x1b4, + .rst_source_shift = 0x0, + .rst_source_mask = 0x7, + .rst_level_shift = 0x0, + .rst_level_mask = 0x0, }; static void tegra20_pmc_init(struct tegra_pmc *pmc) @@ -1735,6 +1842,10 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .reset_sources = NULL, + .num_reset_sources = 0, + .reset_levels = NULL, + .num_reset_levels = 0, }; static const char * const tegra30_powergates[] = { @@ -1776,6 +1887,10 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .reset_sources = tegra_legacy_reset_sources, + .num_reset_sources = 5, + .reset_levels = NULL, + .num_reset_levels = 0, }; static const char * const tegra114_powergates[] = { @@ -1821,6 +1936,10 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .reset_sources = tegra_legacy_reset_sources, + .num_reset_sources = 5, + .reset_levels = NULL, + .num_reset_levels = 0, }; static const char * const tegra124_powergates[] = { @@ -1926,6 +2045,10 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .reset_sources = tegra_legacy_reset_sources, + .num_reset_sources = 5, + .reset_levels = NULL, + .num_reset_levels = 0, }; static const char * const tegra210_powergates[] = { @@ -2027,6 +2150,10 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .reset_sources = tegra_legacy_reset_sources, + .num_reset_sources = 5, + .reset_levels = NULL, + .num_reset_levels = 0, }; #define TEGRA186_IO_PAD_TABLE(_pad) \ @@ -2084,6 +2211,12 @@ static const struct tegra_pmc_regs tegra186_pmc_regs = { .dpd_status = 0x78, .dpd2_req = 0x7c, .dpd2_status = 0x80, + .rst_status = 0x70, + .rst_source_shift = 0x2, + .rst_source_mask = 0x3C, + .rst_level_shift = 0x0, + .rst_level_mask = 0x3, + }; static void tegra186_pmc_setup_irq_polarity(struct tegra_pmc *pmc, @@ -2136,6 +2269,10 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .regs = &tegra186_pmc_regs, .init = NULL, .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, + .reset_sources = tegra186_reset_sources, + .num_reset_sources = 14, + .reset_levels = tegra186_reset_levels, + .num_reset_levels = 3, }; static const struct of_device_id tegra_pmc_match[] = { -- 2.7.4