diff mbox series

[V2] soc/tegra: pmc: Add sysfs entry to get reset info

Message ID 1538562290-21943-1-git-send-email-spatra@nvidia.com
State Superseded
Headers show
Series [V2] soc/tegra: pmc: Add sysfs entry to get reset info | expand

Commit Message

Sandipan Patra Oct. 3, 2018, 10:24 a.m. UTC
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@nvidia.com>
---
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(-)

Comments

Sandipan Patra Oct. 23, 2018, 4:51 a.m. UTC | #1
Sorry for the delayed reminder.
kindly help reviewing the patch.
All the V1 review comments are addressed on this patch.


Thanks & Regards,
Sandipan


-----Original Message-----
From: Sandipan Patra 
Sent: Wednesday, October 3, 2018 3:55 PM
To: Thierry Reding <treding@nvidia.com>; Jonathan Hunter <jonathanh@nvidia.com>; Mikko Perttunen <mperttunen@nvidia.com>
Cc: linux-tegra@vger.kernel.org; Bibek Basu <bbasu@nvidia.com>; Sandipan Patra <spatra@nvidia.com>
Subject: [Patch V2] soc/tegra: pmc: Add sysfs entry to get reset info

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@nvidia.com>
---
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@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 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
Thierry Reding Oct. 23, 2018, 9:48 a.m. UTC | #2
On Wed, Oct 03, 2018 at 03:54:50PM +0530, Sandipan Patra wrote:
> 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@nvidia.com>
> ---
> 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(-)

Hi Sandipan,

this looks almost ready now. Just a couple of cosmetic comments below.

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

While both variants are used in the kernel, the most common and
therefore idiomatic version is to put a space between '*' and
"const".

> +};
> +
> +static const char *const tegra186_reset_sources[] = {

Same here.

> +	"SYS_RESET",
> +	"AOWDT",
> +	"MCCPLEXWDT",
> +	"BPMPWDT",
> +	"SCEWDT",
> +	"SPEWDT",
> +	"APEWDT",
> +	"BCCPLEXWDT",
> +	"SENSOR",
> +	"AOTAG",
> +	"VFSENSOR",
> +	"SWREST",
> +	"SC7",
> +	"HSM",
> +	"CORESIGHT"
> +};
> +
> +static const char *const tegra186_reset_levels[] = {

And here.

> +	"L0", "L1", "L2", "WARM"
> +};
> +
> +static const char *const tegra_legacy_reset_sources[] = {

And here.

Also, in these kinds of situations we usually use a prefix that
identifies the oldest generation that supports a given interface or
certain properties. In this particular case that would make this:
tegra30_reset_sources.

That's more explicit than "legacy".

> +	"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];
> +}

These functions serve no other purpose than to be called by the sysfs
attributes, right? Couldn't we move the implementation into the
attributes to avoid these extra functions?

> +
>  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());
> +}

If you move the body of tegra_pmc_get_system_reset_level() into this
function, you can get at pmc via dev_get_drvdata(dev). That way you
don't rely on the global "pmc" variable.

> +static DEVICE_ATTR_RO(reset_level);
> +
> +static void tegra_pmc_reset_sysfs_init(struct device *dev)

I think it'd be better if this took a struct tegra_pmc * as a parameter.
You can still get at the struct device * from that, and you don't rely
on the global "pmc" variable.

> +{
> +

Gratuitous blank line.

> +	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");
> +		}
> +	}

Do we want these failures to be ignored, or do we want to propagate the
error code and fail probe if these attribute file can't be created?

In either case I think the error or warning messages should contain the
error code. Also, other error and warning messages in this file start
with a lower case letter, so please stick with that for consistency. The
attributes are now also called "reset_reason" and "reset_level", so the
messages should reflect that.

Taking into account all of the above, perhaps make this something like:

	err = device_create_file(...);
	if (err < 0)
		dev_warn(dev, "failed to create \"reset_reason\" attribute: %d\n", err);


> +}
> +
>  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,
> +

Gratuitous blank line.

One last thing: can the above Tegra186 code be used as-is for Tegra194
support as well? In most other places the PMC driver upstream uses the
Tegra186 parameters on Tegra194, so can we continue to do that or will
Tegra194 require different parameters for these reset bits?

Thierry
Sandipan Patra Oct. 24, 2018, 7:11 a.m. UTC | #3
Reply inline.

Thanks & Regards,
Sandipan

-----Original Message-----
From: Thierry Reding <thierry.reding@gmail.com> 
Sent: Tuesday, October 23, 2018 3:18 PM
To: Sandipan Patra <spatra@nvidia.com>
Cc: Thierry Reding <treding@nvidia.com>; Jonathan Hunter <jonathanh@nvidia.com>; Mikko Perttunen <mperttunen@nvidia.com>; linux-tegra@vger.kernel.org; Bibek Basu <bbasu@nvidia.com>
Subject: Re: [Patch V2] soc/tegra: pmc: Add sysfs entry to get reset info


One last thing: can the above Tegra186 code be used as-is for Tegra194 support as well? In most other places the PMC driver upstream uses the
Tegra186 parameters on Tegra194, so can we continue to do that or will
Tegra194 require different parameters for these reset bits?

[Reply]: T194 also has the similar reset bits as in T186 SoC.

Thierry
diff mbox series

Patch

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@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 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[] = {