summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra_simon.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2014-03-25 22:19:01 -0700
committerYu-Huan Hsu <yhsu@nvidia.com>2014-03-27 18:17:23 -0700
commitf6ff707e3d9b54810d68a0104b0f34af43d28602 (patch)
treef829f77dfb6ce85124339ab287d0cdfb349bf737 /arch/arm/mach-tegra/tegra_simon.c
parentd6eec3880984d812780a52d49f4ee9d67b55ef89 (diff)
ARM: tegra: power: Add SiMon grading WDT
Added SiMon grading watch-dog timer for each domain. It is running while domain grade is above zero. If WDT expires domain grade is reset to default zero. Since it is possible to modify grade concurrently by grading and WDT callbacks, added domain grade lock to serialize grade updates in both callbacks as well as in debugfs grade write operation. Bug 1343366 Change-Id: Id907878c478b5ac581905a04a600c37505721dae Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/387442 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra_simon.c')
-rw-r--r--arch/arm/mach-tegra/tegra_simon.c80
1 files changed, 64 insertions, 16 deletions
diff --git a/arch/arm/mach-tegra/tegra_simon.c b/arch/arm/mach-tegra/tegra_simon.c
index 701950fbccdb..7593ab064aa6 100644
--- a/arch/arm/mach-tegra/tegra_simon.c
+++ b/arch/arm/mach-tegra/tegra_simon.c
@@ -38,6 +38,7 @@ static RAW_NOTIFIER_HEAD(simon_nh);
static void tegra_simon_grade_notify(struct work_struct *work);
static u32 grading_sec = TEGRA_SIMON_GRADING_INTERVAL_SEC;
+static u32 timeout_sec = TEGRA_SIMON_GRADING_TIMEOUT_SEC;
static struct tegra_simon_grader simon_graders[TEGRA_SIMON_DOMAIN_NUM] = {
[TEGRA_SIMON_DOMAIN_CPU] = {
@@ -60,6 +61,52 @@ static void settle_delay(struct tegra_simon_grader *grader)
usleep_range(us, us + 100);
}
+static inline void mod_wdt_on_grade(struct tegra_simon_grader *grader)
+{
+ if (grader->grade) {
+ /* restart WDT while at high grade */
+ struct timespec ts = {timeout_sec, 0};
+ mod_timer(&grader->grade_wdt,
+ jiffies + timespec_to_jiffies(&ts));
+ }
+}
+
+static void tegra_simon_reset_grade(unsigned long data)
+{
+ unsigned long flags;
+ struct tegra_simon_grader *grader = (struct tegra_simon_grader *)data;
+
+ pr_info("%s: %s grade = 0\n", __func__, grader->domain_name);
+
+ spin_lock_irqsave(&grader->grade_lock, flags);
+ grader->grade = 0;
+ spin_unlock_irqrestore(&grader->grade_lock, flags);
+
+ schedule_work(&grader->grade_update_work);
+}
+
+static void tegra_simon_grade_set(struct tegra_simon_grader *grader,
+ int grade, bool restart)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&grader->grade_lock, flags);
+
+ /* once low grade is detected, stop grading (unless restart request) */
+ grader->stop_grading = !grade && !restart;
+
+ if (grader->grade == grade) {
+ mod_wdt_on_grade(grader);
+ spin_unlock_irqrestore(&grader->grade_lock, flags);
+ return;
+ }
+ grader->grade = grade;
+ mod_wdt_on_grade(grader);
+ spin_unlock_irqrestore(&grader->grade_lock, flags);
+
+ schedule_work(&grader->grade_update_work);
+}
+
/*
* GPU grading is implemented within vdd_gpu post-change notification chain that
* guarantees constant voltage during grading. First grading after boot can be
@@ -111,13 +158,7 @@ static int tegra_simon_gpu_grading_cb(
}
grader->last_grading = now;
- if (grader->grade != grade) {
- /* once return to low grade, stay until next boot */
- if (grade == 0)
- grader->stop_grading = true;
- set_mb(grader->grade, grade);
- schedule_work(&grader->grade_update_work);
- }
+ tegra_simon_grade_set(grader, grade, false);
pr_info("%s: graded %s: v = %d, t = %lu, grade = %d\n",
__func__, grader->domain_name, mv, t, grade);
return NOTIFY_OK;
@@ -129,6 +170,9 @@ static int __init tegra_simon_init_gpu(void)
struct tegra_simon_grader *grader =
&simon_graders[TEGRA_SIMON_DOMAIN_GPU];
+ spin_lock_init(&grader->grade_lock);
+ setup_timer(&grader->grade_wdt, tegra_simon_reset_grade,
+ (unsigned long)grader);
INIT_WORK(&grader->grade_update_work, tegra_simon_grade_notify);
grader->tzd = thermal_zone_device_find_by_name("GPU-therm");
@@ -225,13 +269,7 @@ static int tegra_simon_cpu_grading_cb(
tegra_cl_dvfs_clamp_at_vmin(cld, false);
grader->last_grading = now;
- if (grader->grade != grade) {
- /* once return to low grade, stay until next boot */
- if (grade == 0)
- grader->stop_grading = true;
- set_mb(grader->grade, grade);
- schedule_work(&grader->grade_update_work);
- }
+ tegra_simon_grade_set(grader, grade, false);
pr_info("%s: graded %s: v = %d, t = %lu, grade = %d\n",
__func__, grader->domain_name, mv, t, grade);
return NOTIFY_OK;
@@ -244,6 +282,9 @@ static int __init tegra_simon_init_cpu(void)
struct clk *c;
int r;
+ spin_lock_init(&grader->grade_lock);
+ setup_timer(&grader->grade_wdt, tegra_simon_reset_grade,
+ (unsigned long)grader);
INIT_WORK(&grader->grade_update_work, tegra_simon_grade_notify);
grader->tzd = thermal_zone_device_find_by_name("CPU-therm");
@@ -369,15 +410,18 @@ static int grade_get(void *data, u64 *val)
}
static int grade_set(void *data, u64 val)
{
+ int grade = (int)val;
struct tegra_simon_grader *grader = data;
if (grader->domain >= TEGRA_SIMON_DOMAIN_NUM)
return -EINVAL;
- if (grader->grade != (int)val) {
+ if (!grader->desc && (grader->grade != grade)) {
grader->stop_grading = false;
- grader->grade = (int)val;
+ grader->grade = grade;
grade_notify(grader);
+ } else if (grader->desc) {
+ tegra_simon_grade_set(grader, grade, true);
}
return 0;
}
@@ -417,6 +461,10 @@ static int __init simon_debugfs_init(void)
&grading_sec))
goto err_out;
+ if (!debugfs_create_u32("timeout_sec", S_IWUSR | S_IRUGO, dir,
+ &timeout_sec))
+ goto err_out;
+
for (i = 0; i < TEGRA_SIMON_DOMAIN_NUM; i++) {
grader = &simon_graders[i];