soc/tegra: pmc: Add sysfs entries for reset info
Implement read-only reset_reason and reset_level sysfs attributes that can be used to query the reset reason and level at runtime. Signed-off-by: Sandipan Patra <spatra@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
parent
3bb2f843c0
commit
5f84bb1a40
|
@ -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@google.com>
|
||||
|
@ -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 tegra30_reset_sources[] = {
|
||||
"POWER_ON_RESET",
|
||||
"WATCHDOG",
|
||||
"SENSOR",
|
||||
"SW_MAIN",
|
||||
"LP0",
|
||||
"AOTAG"
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1527,6 +1568,56 @@ 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)
|
||||
{
|
||||
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 sprintf(buf, "%s\n", pmc->soc->reset_sources[rst_src]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(reset_reason);
|
||||
|
||||
static ssize_t reset_level_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
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 sprintf(buf, "%s\n", pmc->soc->reset_levels[rst_lvl]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(reset_level);
|
||||
|
||||
static void tegra_pmc_reset_sysfs_init(struct tegra_pmc *pmc)
|
||||
{
|
||||
struct device *dev = pmc->dev;
|
||||
int err = 0;
|
||||
|
||||
if (pmc->soc->reset_sources) {
|
||||
err = device_create_file(dev, &dev_attr_reset_reason);
|
||||
if (err < 0)
|
||||
dev_warn(dev,
|
||||
"failed to create attr \"reset_reason\": %d\n",
|
||||
err);
|
||||
}
|
||||
|
||||
if (pmc->soc->reset_levels) {
|
||||
err = device_create_file(dev, &dev_attr_reset_level);
|
||||
if (err < 0)
|
||||
dev_warn(dev,
|
||||
"failed to create attr \"reset_level\": %d\n",
|
||||
err);
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra_pmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
|
@ -1596,6 +1687,8 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||
|
||||
tegra_pmc_init_tsense_reset(pmc);
|
||||
|
||||
tegra_pmc_reset_sysfs_init(pmc);
|
||||
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
|
||||
err = tegra_powergate_debugfs_init();
|
||||
if (err < 0)
|
||||
|
@ -1662,6 +1755,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)
|
||||
|
@ -1719,6 +1817,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[] = {
|
||||
|
@ -1760,6 +1862,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 = tegra30_reset_sources,
|
||||
.num_reset_sources = 5,
|
||||
.reset_levels = NULL,
|
||||
.num_reset_levels = 0,
|
||||
};
|
||||
|
||||
static const char * const tegra114_powergates[] = {
|
||||
|
@ -1805,6 +1911,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 = tegra30_reset_sources,
|
||||
.num_reset_sources = 5,
|
||||
.reset_levels = NULL,
|
||||
.num_reset_levels = 0,
|
||||
};
|
||||
|
||||
static const char * const tegra124_powergates[] = {
|
||||
|
@ -1910,6 +2020,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 = tegra30_reset_sources,
|
||||
.num_reset_sources = 5,
|
||||
.reset_levels = NULL,
|
||||
.num_reset_levels = 0,
|
||||
};
|
||||
|
||||
static const char * const tegra210_powergates[] = {
|
||||
|
@ -2011,6 +2125,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 = tegra30_reset_sources,
|
||||
.num_reset_sources = 5,
|
||||
.reset_levels = NULL,
|
||||
.num_reset_levels = 0,
|
||||
};
|
||||
|
||||
#define TEGRA186_IO_PAD_TABLE(_pad) \
|
||||
|
@ -2068,6 +2186,11 @@ 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,
|
||||
|
@ -2120,6 +2243,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[] = {
|
||||
|
|
Loading…
Reference in New Issue