diff options
author | Louis Li <louli@nvidia.com> | 2014-06-19 07:36:23 +0000 |
---|---|---|
committer | Mandar Padmawar <mpadmawar@nvidia.com> | 2014-06-25 00:49:00 -0700 |
commit | 19fc07fedae665f645554998473d454c697415cb (patch) | |
tree | 14615820ea51a9823efe362bc2dbe053564f4c20 | |
parent | 6f8ed4fceb58f2bb86e0c60adba36496d300cbf5 (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.c | 57 |
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); |