diff options
author | Alex Frid <afrid@nvidia.com> | 2014-03-25 22:19:01 -0700 |
---|---|---|
committer | Yu-Huan Hsu <yhsu@nvidia.com> | 2014-03-27 18:17:23 -0700 |
commit | f6ff707e3d9b54810d68a0104b0f34af43d28602 (patch) | |
tree | f829f77dfb6ce85124339ab287d0cdfb349bf737 /arch/arm/mach-tegra/tegra_simon.c | |
parent | d6eec3880984d812780a52d49f4ee9d67b55ef89 (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.c | 80 |
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]; |