From patchwork Tue Mar 4 03:00:26 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 25635 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-qc0-f198.google.com (mail-qc0-f198.google.com [209.85.216.198]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 008DC206F3 for ; Tue, 4 Mar 2014 03:00:42 +0000 (UTC) Received: by mail-qc0-f198.google.com with SMTP id r5sf9547829qcx.9 for ; Mon, 03 Mar 2014 19:00:42 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:sender:precedence:list-id:x-original-sender :x-original-authentication-results:mailing-list:list-post:list-help :list-archive:list-unsubscribe; bh=UVLyHdFpoLfEh8HPNeQOwMhjRJQwaccnTpYOrpd+/uo=; b=c2pLhP6hR+j52PdXAKZYaaFOaVWrXGpJx3esD6c85ULku/5R9d6bK7bwFUk55h+J2m Ywu9MTFWa+ItLuTX0LTMz9wFAKYo1bwvk7a013dK1GebGl40/xTYUJedovytnFBNp5XP Zz0oghhGxslbkb6waJ2CF/o6kcI/HGXsTo8VhHw173kDXU9KAfk2P35M6/2ulISyun/H qy3sb47QN6x1a8a62q/r5SFjqqDvawOUQE4bduAs3pUP6/ORfHrSfWLj+D4PvMwWBiq3 bph+yHoQm+16BKuEjr8rVGONJXr3h2xJgNq/ynv6VBtp6gJRN5tSSXUmH2IYjEE4aW98 WAEA== X-Gm-Message-State: ALoCoQnK8eEa57hWOqzcxtAZJDCK5yz50BI5LhbFKc1yPbgWM1MLWEmuorFyRWKMFwBUU7wytVb3 X-Received: by 10.58.120.76 with SMTP id la12mr10474029veb.21.1393902042748; Mon, 03 Mar 2014 19:00:42 -0800 (PST) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.44.101 with SMTP id f92ls2326634qga.61.gmail; Mon, 03 Mar 2014 19:00:42 -0800 (PST) X-Received: by 10.52.137.143 with SMTP id qi15mr242755vdb.39.1393902042629; Mon, 03 Mar 2014 19:00:42 -0800 (PST) Received: from mail-ve0-f175.google.com (mail-ve0-f175.google.com [209.85.128.175]) by mx.google.com with ESMTPS id wm4si5402367vcb.79.2014.03.03.19.00.42 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 03 Mar 2014 19:00:42 -0800 (PST) Received-SPF: neutral (google.com: 209.85.128.175 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.128.175; Received: by mail-ve0-f175.google.com with SMTP id oz11so3858640veb.34 for ; Mon, 03 Mar 2014 19:00:42 -0800 (PST) X-Received: by 10.58.132.203 with SMTP id ow11mr20028738veb.1.1393902042543; Mon, 03 Mar 2014 19:00:42 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.220.130.193 with SMTP id u1csp97109vcs; Mon, 3 Mar 2014 19:00:42 -0800 (PST) X-Received: by 10.66.155.7 with SMTP id vs7mr3284671pab.42.1393902041391; Mon, 03 Mar 2014 19:00:41 -0800 (PST) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id xz2si12528821pbb.299.2014.03.03.19.00.40; Mon, 03 Mar 2014 19:00:40 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of cpufreq-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756089AbaCDDAj (ORCPT + 6 others); Mon, 3 Mar 2014 22:00:39 -0500 Received: from mail-pd0-f173.google.com ([209.85.192.173]:34075 "EHLO mail-pd0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756058AbaCDDAi (ORCPT ); Mon, 3 Mar 2014 22:00:38 -0500 Received: by mail-pd0-f173.google.com with SMTP id z10so4516807pdj.4 for ; Mon, 03 Mar 2014 19:00:38 -0800 (PST) X-Received: by 10.68.66.103 with SMTP id e7mr9902840pbt.120.1393902037983; Mon, 03 Mar 2014 19:00:37 -0800 (PST) Received: from localhost (z88l218.static.ctm.net. [202.175.88.218]) by mx.google.com with ESMTPSA id op3sm41698439pbc.40.2014.03.03.19.00.33 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Mon, 03 Mar 2014 19:00:37 -0800 (PST) From: Viresh Kumar To: rjw@rjwysocki.net Cc: linaro-kernel@lists.linaro.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, nm@ti.com, swarren@wwwdotorg.org, kgene.kim@samsung.com, jinchoi@broadcom.com, tianyu.lan@intel.com, sebastian.capella@linaro.org, jhbird.choi@samsung.com, Viresh Kumar Subject: [PATCH V6 Resend 1/5] cpufreq: suspend governors on system suspend/hibernate Date: Tue, 4 Mar 2014 11:00:26 +0800 Message-Id: X-Mailer: git-send-email 1.7.12.rc2.18.g61b472e Sender: cpufreq-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: cpufreq@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: viresh.kumar@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.128.175 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , This patch adds cpufreq suspend/resume calls to dpm_{suspend|resume}() for handling suspend/resume of cpufreq governors. Lan Tianyu (Intel) & Jinhyuk Choi (Broadcom) found an issue where tunables configuration for clusters/sockets with non-boot CPUs was getting lost after suspend/resume, as we were notifying governors with CPUFREQ_GOV_POLICY_EXIT on removal of the last cpu for that policy and so deallocating memory for tunables. This is fixed by this patch as we don't allow any operation on governors after device suspend and before device resume now. We could have added these callbacks at dpm_{suspend|resume}_noirq() level but the problem here is that most of the devices (i.e. devices with ->suspend() callbacks) have already been suspended by now and so if drivers want to change frequency before suspending, then it might not be possible for many platforms (which depend on other peripherals like i2c, regulators, etc). Reported-and-tested-by: Lan Tianyu Reported-by: Jinhyuk Choi Signed-off-by: Viresh Kumar --- V5->V6: 1-2-3/7 merged into 1/5 drivers/base/power/main.c | 5 +++ drivers/cpufreq/cpufreq.c | 111 +++++++++++++++++++++++----------------------- include/linux/cpufreq.h | 8 ++++ 3 files changed, 69 insertions(+), 55 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 42355e4..86d5e4f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -866,6 +867,8 @@ void dpm_resume(pm_message_t state) mutex_unlock(&dpm_list_mtx); async_synchronize_full(); dpm_show_time(starttime, state, NULL); + + cpufreq_resume(); } /** @@ -1434,6 +1437,8 @@ int dpm_suspend(pm_message_t state) might_sleep(); + cpufreq_suspend(); + mutex_lock(&dpm_list_mtx); pm_transition = state; async_error = 0; diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 56b7b1b..2e43c08 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include @@ -47,6 +47,9 @@ static LIST_HEAD(cpufreq_policy_list); static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor); #endif +/* Flag to suspend/resume CPUFreq governors */ +static bool cpufreq_suspended; + static inline bool has_target(void) { return cpufreq_driver->target_index || cpufreq_driver->target; @@ -1576,82 +1579,77 @@ static struct subsys_interface cpufreq_interface = { }; /** - * cpufreq_bp_suspend - Prepare the boot CPU for system suspend. + * cpufreq_suspend() - Suspend CPUFreq governors * - * This function is only executed for the boot processor. The other CPUs - * have been put offline by means of CPU hotplug. + * Called during system wide Suspend/Hibernate cycles for suspending governors + * as some platforms can't change frequency after this point in suspend cycle. + * Because some of the devices (like: i2c, regulators, etc) they use for + * changing frequency are suspended quickly after this point. */ -static int cpufreq_bp_suspend(void) +void cpufreq_suspend(void) { - int ret = 0; - - int cpu = smp_processor_id(); struct cpufreq_policy *policy; - pr_debug("suspending cpu %u\n", cpu); + if (!cpufreq_driver) + return; - /* If there's no policy for the boot CPU, we have nothing to do. */ - policy = cpufreq_cpu_get(cpu); - if (!policy) - return 0; + if (!has_target()) + return; - if (cpufreq_driver->suspend) { - ret = cpufreq_driver->suspend(policy); - if (ret) - printk(KERN_ERR "cpufreq: suspend failed in ->suspend " - "step on CPU %u\n", policy->cpu); + pr_debug("%s: Suspending Governors\n", __func__); + + list_for_each_entry(policy, &cpufreq_policy_list, policy_list) { + if (__cpufreq_governor(policy, CPUFREQ_GOV_STOP)) + pr_err("%s: Failed to stop governor for policy: %p\n", + __func__, policy); + else if (cpufreq_driver->suspend + && cpufreq_driver->suspend(policy)) + pr_err("%s: Failed to suspend driver: %p\n", __func__, + policy); } - cpufreq_cpu_put(policy); - return ret; + cpufreq_suspended = true; } /** - * cpufreq_bp_resume - Restore proper frequency handling of the boot CPU. + * cpufreq_resume() - Resume CPUFreq governors * - * 1.) resume CPUfreq hardware support (cpufreq_driver->resume()) - * 2.) schedule call cpufreq_update_policy() ASAP as interrupts are - * restored. It will verify that the current freq is in sync with - * what we believe it to be. This is a bit later than when it - * should be, but nonethteless it's better than calling - * cpufreq_driver->get() here which might re-enable interrupts... - * - * This function is only executed for the boot CPU. The other CPUs have not - * been turned on yet. + * Called during system wide Suspend/Hibernate cycle for resuming governors that + * are suspended with cpufreq_suspend(). */ -static void cpufreq_bp_resume(void) +void cpufreq_resume(void) { - int ret = 0; - - int cpu = smp_processor_id(); struct cpufreq_policy *policy; - pr_debug("resuming cpu %u\n", cpu); + if (!cpufreq_driver) + return; - /* If there's no policy for the boot CPU, we have nothing to do. */ - policy = cpufreq_cpu_get(cpu); - if (!policy) + if (!has_target()) return; - if (cpufreq_driver->resume) { - ret = cpufreq_driver->resume(policy); - if (ret) { - printk(KERN_ERR "cpufreq: resume failed in ->resume " - "step on CPU %u\n", policy->cpu); - goto fail; - } - } + pr_debug("%s: Resuming Governors\n", __func__); - schedule_work(&policy->update); + cpufreq_suspended = false; -fail: - cpufreq_cpu_put(policy); -} + list_for_each_entry(policy, &cpufreq_policy_list, policy_list) { + if (__cpufreq_governor(policy, CPUFREQ_GOV_START) + || __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS)) + pr_err("%s: Failed to start governor for policy: %p\n", + __func__, policy); + else if (cpufreq_driver->resume + && cpufreq_driver->resume(policy)) + pr_err("%s: Failed to resume driver: %p\n", __func__, + policy); -static struct syscore_ops cpufreq_syscore_ops = { - .suspend = cpufreq_bp_suspend, - .resume = cpufreq_bp_resume, -}; + /* + * schedule call cpufreq_update_policy() for boot CPU, i.e. last + * policy in list. It will verify that the current freq is in + * sync with what we believe it to be. + */ + if (list_is_last(&policy->policy_list, &cpufreq_policy_list)) + schedule_work(&policy->update); + } +} /** * cpufreq_get_current_driver - return current driver's name @@ -1868,6 +1866,10 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, struct cpufreq_governor *gov = NULL; #endif + /* Don't start any governor operations if we are entering suspend */ + if (cpufreq_suspended) + return 0; + if (policy->governor->max_transition_latency && policy->cpuinfo.transition_latency > policy->governor->max_transition_latency) { @@ -2407,7 +2409,6 @@ static int __init cpufreq_core_init(void) cpufreq_global_kobject = kobject_create(); BUG_ON(!cpufreq_global_kobject); - register_syscore_ops(&cpufreq_syscore_ops); return 0; } diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 4d89e0e..94ed907 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -296,6 +296,14 @@ cpufreq_verify_within_cpu_limits(struct cpufreq_policy *policy) policy->cpuinfo.max_freq); } +#ifdef CONFIG_CPU_FREQ +void cpufreq_suspend(void); +void cpufreq_resume(void); +#else +static inline void cpufreq_suspend(void) {} +static inline void cpufreq_resume(void) {} +#endif + /********************************************************************* * CPUFREQ NOTIFIER INTERFACE * *********************************************************************/