summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Li <louli@nvidia.com>2014-06-19 07:36:23 +0000
committerMandar Padmawar <mpadmawar@nvidia.com>2014-06-25 00:49:00 -0700
commit19fc07fedae665f645554998473d454c697415cb (patch)
tree14615820ea51a9823efe362bc2dbe053564f4c20
parent6f8ed4fceb58f2bb86e0c60adba36496d300cbf5 (diff)
arm: tegra: therm: avoid reg op after clk disabled
Register operations could be triggered via sysfs or debugfs from userspace even after soctherm clock disabled, it would cause a device hard hang. Added protection to avoid such a case. Bug 200009946 Change-Id: Ic0bb57bc9a2e9fdf39e8259436f618f1f68ae4c0 Signed-off-by: Louis Li <louli@nvidia.com> Reviewed-on: http://git-master/r/425205 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/tegra11_soctherm.c57
1 files changed, 51 insertions, 6 deletions
diff --git a/arch/arm/mach-tegra/tegra11_soctherm.c b/arch/arm/mach-tegra/tegra11_soctherm.c
index c230f7798c7d..b669bfba6e10 100644
--- a/arch/arm/mach-tegra/tegra11_soctherm.c
+++ b/arch/arm/mach-tegra/tegra11_soctherm.c
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-tegra/tegra11_soctherm.c
*
- * Copyright (C) 2011-2013 NVIDIA Corporation
+ * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -371,11 +371,6 @@ static const void __iomem *clk_reset_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
#define pmc_writel(value, reg) __raw_writel(value, (u32)pmc_base + (reg))
#define pmc_readl(reg) __raw_readl((u32)pmc_base + (reg))
-#define soctherm_writel(value, reg) \
- __raw_writel(value, (u32)reg_soctherm_base + (reg))
-#define soctherm_readl(reg) \
- __raw_readl((u32)reg_soctherm_base + (reg))
-
static struct soctherm_platform_data plat_data;
/*
@@ -472,6 +467,9 @@ static const unsigned long default_tsensor_clk_rate = 500000;
static int sensor2therm_a[TSENSE_SIZE];
static int sensor2therm_b[TSENSE_SIZE];
+static u32 clock_enabled;
+struct mutex clock_lock;
+
static const struct soctherm_throttle_dev throttle_defaults[] = {
[THROTTLE_LIGHT] = {
.dividend = 229, /* 20% throttling */
@@ -487,6 +485,32 @@ static const struct soctherm_throttle_dev throttle_defaults[] = {
},
};
+static inline void soctherm_writel(u32 value, u32 reg)
+{
+ mutex_lock(&clock_lock);
+ if (unlikely(clock_enabled == 0)) {
+ pr_warn("soctherm: cannot write reg when clock is disabled!\n");
+ mutex_unlock(&clock_lock);
+ return;
+ }
+ __raw_writel(value, (u32)reg_soctherm_base + reg);
+ mutex_unlock(&clock_lock);
+}
+
+static inline u32 soctherm_readl(u32 reg)
+{
+ u32 ret;
+ mutex_lock(&clock_lock);
+ if (unlikely(clock_enabled == 0)) {
+ pr_warn("soctherm: cannot read reg when clock is disabled!\n");
+ mutex_unlock(&clock_lock);
+ return 0;
+ }
+ ret = __raw_readl((u32)reg_soctherm_base + reg);
+ mutex_unlock(&clock_lock);
+ return ret;
+}
+
static inline s64 div64_s64_precise(s64 a, s32 b)
{
s64 r, al;
@@ -1197,7 +1221,15 @@ static int soctherm_clk_enable(bool enable)
if (enable) {
clk_enable(soctherm_clk);
clk_enable(tsensor_clk);
+ clock_enabled = 1;
} else {
+ /*
+ * need a lock here in case that we try to disable clock while
+ * register operation is ongoing
+ */
+ mutex_lock(&clock_lock);
+ clock_enabled = 0;
+ mutex_unlock(&clock_lock);
clk_disable(soctherm_clk);
clk_disable(tsensor_clk);
}
@@ -1499,6 +1531,9 @@ int __init tegra11_soctherm_init(struct soctherm_platform_data *data)
{
int err;
+ clock_enabled = 0;
+ mutex_init(&clock_lock);
+
register_pm_notifier(&soctherm_nb);
if (!data)
@@ -1537,6 +1572,16 @@ static int regs_show(struct seq_file *s, void *data)
seq_printf(s, "-----TSENSE (precision %s convert %s)-----\n",
PRECISION_TO_STR(), read_hw_temp ? "HW" : "SW");
+
+ if (unlikely(clock_enabled == 0)) {
+ seq_puts(s, "SOC_THERM is SUSPENDED\n");
+ return 0;
+ }
+
+ /*
+ * It is not guaranteed that the following reg ops will always succeed,
+ * because the clock still might be disabled after the above judgement.
+ */
for (i = 0; i < TSENSE_SIZE; i++) {
r = soctherm_readl(TS_TSENSE_REG_OFFSET(TS_CPU0_CONFIG1, i));
state = REG_GET(r, TS_CPU0_CONFIG1_EN);