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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux