From patchwork Mon Apr 20 15:02:02 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Axel Haslam X-Patchwork-Id: 6242701 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 4E969BF4A6 for ; Mon, 20 Apr 2015 15:02:24 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 25F8120456 for ; Mon, 20 Apr 2015 15:02:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 87A112045E for ; Mon, 20 Apr 2015 15:02:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751471AbbDTPCR (ORCPT ); Mon, 20 Apr 2015 11:02:17 -0400 Received: from mail-wi0-f173.google.com ([209.85.212.173]:33066 "EHLO mail-wi0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751185AbbDTPCR (ORCPT ); Mon, 20 Apr 2015 11:02:17 -0400 Received: by wiax7 with SMTP id x7so85716955wia.0 for ; Mon, 20 Apr 2015 08:02:15 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=7tmcYZFAUsZd0q6QIf8KxKOKq5uDi8N1A4JhJi231w4=; b=mJJG+l1ALjo0STcoz9Fogvf/xk7DzF9bve4nk7cR55kb4fbJt3zoaeI25dBKFEVoZD gRvaHF3NlKV88Xshyimyqk1YaxzrdW3KfmccqkELeoMPsUdO/I3pV7MGmxLEcMuEp1fZ PrBpc7kYHMBjr4t1yaZkk6Af/kSo3gDyBHb6ksJ6IXnJUbsUrwI0atYCRVzgak2S8+36 zlNgRNY1bt6Gek501H0Sqa3q4iW4VfxkNDHo6Wc+ZA1mgRtKM7xzCdmq/stjCKEyzqer 6kV4vunFixtUw2MYHDYQHTaOo2gZ12xC2yKvMVRrdcLEDRnCbYfkXYL+zxIY8LPZIWnH 7Tnw== X-Gm-Message-State: ALoCoQmARP8/9zUEacHRsoHwjI3T3ArmXzGCARrjWUiCpmuz9NG00tORoMVqzEBMdwEsy7KCWa/7 X-Received: by 10.194.142.168 with SMTP id rx8mr32263286wjb.43.1429542135726; Mon, 20 Apr 2015 08:02:15 -0700 (PDT) Received: from axelh-ThinkPad-T440s.home (LPoitiers-656-1-62-228.w90-63.abo.wanadoo.fr. [90.63.143.228]) by mx.google.com with ESMTPSA id fm8sm11119190wib.9.2015.04.20.08.02.13 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 20 Apr 2015 08:02:14 -0700 (PDT) From: ahaslam@baylibre.com To: ulf.hansson@linaro.org, khilman@linaro.org, rjw@rjwysocki.net Cc: bcousson@baylibre.com, linux-pm@vger.kernel.org, Axel Haslam Subject: [RFC v3 1/7] PM / Domains: structure changes for multiple states Date: Mon, 20 Apr 2015 17:02:02 +0200 Message-Id: <1429542128-21021-2-git-send-email-ahaslam@baylibre.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1429542128-21021-1-git-send-email-ahaslam@baylibre.com> References: <1429542128-21021-1-git-send-email-ahaslam@baylibre.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Axel Haslam Add the structure changes to be able to declare multiple states. When trying to set a power domain to off, genpd will be able to choose from an array of states declared by the platform. The power on and off callbacks and latencies are now tied to a particular state. States should be declared in ascending order from shallowest to deepest, deepest meaning the state which takes longer to enter and exit. if the genpd is initially off, the user should set the .init_state field when registering the genpd, so that genpd knows which callbacks to call to set the domain to on. An genpd with multiple states would look something like: struct generic_pm_domain pd1 = { .name = "PD1", .states = { [0] = { .name= "D0", .power_off = D0_power_off_cb, .power_on = D0_power_on_cb, .power_off_latency_ns = D0_POWER_OFF_LATENCY, .power_on_latency_ns = D0_POWER_ON_LATENCY, }, [1] = { .name= "D1", .power_off = D1_power_off_cb, .power_on = D1_power_on_cb, .power_off_latency_ns = D1_POWER_OFF_LATENCY, .power_on_latency_ns = D1_POWER_ON_LATENCY, }, [2] = { .name= "D2", .power_off = D2_power_off_cb, .power_on = D2_power_on_cb, .power_off_latency_ns = D2_POWER_OFF_LATENCY, .power_on_latency_ns = D2_POWER_ON_LATENCY, }, }, .state_count = 3, .init_state = 2, [...] }; Signed-off-by: Axel Haslam --- drivers/base/power/domain.c | 43 +++++++++++++++++++++++++++--------- drivers/base/power/domain_governor.c | 8 ++++--- include/linux/pm_domain.h | 21 ++++++++++++++---- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 45937f8..5fa5fb6 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -141,12 +141,13 @@ static void genpd_set_active(struct generic_pm_domain *genpd) static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) { + int target_state = genpd->target_state; s64 usecs64; if (!genpd->cpuidle_data) return; - usecs64 = genpd->power_on_latency_ns; + usecs64 = genpd->states[target_state].power_on_latency_ns; do_div(usecs64, NSEC_PER_USEC); usecs64 += genpd->cpuidle_data->saved_exit_latency; genpd->cpuidle_data->idle_state->exit_latency = usecs64; @@ -154,23 +155,24 @@ static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) static int genpd_power_on(struct generic_pm_domain *genpd) { + int target_state = genpd->target_state; ktime_t time_start; s64 elapsed_ns; int ret; - if (!genpd->power_on) + if (!genpd->states[target_state].power_on) return 0; time_start = ktime_get(); - ret = genpd->power_on(genpd); + ret = genpd->states[target_state].power_on(genpd); if (ret) return ret; elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); - if (elapsed_ns <= genpd->power_on_latency_ns) + if (elapsed_ns <= genpd->states[target_state].power_on_latency_ns) return ret; - genpd->power_on_latency_ns = elapsed_ns; + genpd->states[target_state].power_on_latency_ns = elapsed_ns; genpd->max_off_time_changed = true; genpd_recalc_cpu_exit_latency(genpd); pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n", @@ -181,23 +183,24 @@ static int genpd_power_on(struct generic_pm_domain *genpd) static int genpd_power_off(struct generic_pm_domain *genpd) { + int target_state = genpd->target_state; ktime_t time_start; s64 elapsed_ns; int ret; - if (!genpd->power_off) + if (!genpd->states[target_state].power_off) return 0; time_start = ktime_get(); - ret = genpd->power_off(genpd); + ret = genpd->states[target_state].power_off(genpd); if (ret == -EBUSY) return ret; elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); - if (elapsed_ns <= genpd->power_off_latency_ns) + if (elapsed_ns <= genpd->states[target_state].power_off_latency_ns) return ret; - genpd->power_off_latency_ns = elapsed_ns; + genpd->states[target_state].power_off_latency_ns = elapsed_ns; genpd->max_off_time_changed = true; pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n", genpd->name, "off", elapsed_ns); @@ -486,6 +489,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) struct pm_domain_data *pdd; struct gpd_link *link; unsigned int not_suspended; + int target_state; int ret = 0; start: @@ -538,6 +542,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) genpd->status = GPD_STATE_BUSY; genpd->poweroff_task = current; + target_state = genpd->target_state; list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { ret = atomic_read(&genpd->sd_count) == 0 ? @@ -572,7 +577,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) goto out; } - if (genpd->power_off) { + if (genpd->states[target_state].power_off) { if (atomic_read(&genpd->sd_count) > 0) { ret = -EBUSY; goto out; @@ -1877,6 +1882,17 @@ void pm_genpd_init(struct generic_pm_domain *genpd, genpd->resume_count = 0; genpd->device_count = 0; genpd->max_off_time_ns = -1; + + /* + * we have to set the target off state to the initial off state + * so that when the genpd powers on, it knows what callback + * to use. + */ + genpd->target_state = genpd->init_state; + if (genpd->state_count < 1) { + pr_err("Tried to init pm domain %s without states!\n", + genpd->name); + } genpd->max_off_time_changed = true; genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; @@ -2251,6 +2267,7 @@ static int pm_genpd_summary_one(struct seq_file *s, [GPD_STATE_REPEAT] = "off-in-progress", [GPD_STATE_POWER_OFF] = "off" }; + int target_state = genpd->target_state; struct pm_domain_data *pm_data; const char *kobj_path; struct gpd_link *link; @@ -2262,7 +2279,11 @@ static int pm_genpd_summary_one(struct seq_file *s, if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) goto exit; - seq_printf(s, "%-30s %-15s ", genpd->name, status_lookup[genpd->status]); + + seq_printf(s, "%-30s %s ", genpd->name, + (genpd->status == GPD_STATE_POWER_OFF) ? + genpd->states[target_state].name : + status_lookup[genpd->status]); /* * Modifications on the list require holding locks on both diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index 2a4154a..8630fce 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -104,6 +104,7 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) struct pm_domain_data *pdd; s64 min_off_time_ns; s64 off_on_time_ns; + int state = 0; if (genpd->max_off_time_changed) { struct gpd_link *link; @@ -124,8 +125,8 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) return genpd->cached_power_down_ok; } - off_on_time_ns = genpd->power_off_latency_ns + - genpd->power_on_latency_ns; + off_on_time_ns = genpd->states[state].power_off_latency_ns + + genpd->states[state].power_on_latency_ns; /* * It doesn't make sense to remove power from the domain if saving * the state of all devices in it and the power off/power on operations @@ -216,7 +217,8 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) * time and the time needed to turn the domain on is the maximum * theoretical time this domain can spend in the "off" state. */ - genpd->max_off_time_ns = min_off_time_ns - genpd->power_on_latency_ns; + genpd->max_off_time_ns = min_off_time_ns - + genpd->states[state].power_on_latency_ns; return true; } diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 080e778..9bf9217 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -17,6 +17,8 @@ #include #include +#define GENPD_MAX_NSTATES 10 + /* Defines used for the flags field in the struct generic_pm_domain */ #define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */ @@ -46,6 +48,16 @@ struct gpd_cpuidle_data { struct cpuidle_state *idle_state; }; +struct generic_pm_domain; + +struct genpd_power_state { + char *name; + s64 power_off_latency_ns; + s64 power_on_latency_ns; + int (*power_off)(struct generic_pm_domain *domain); + int (*power_on)(struct generic_pm_domain *domain); +}; + struct generic_pm_domain { struct dev_pm_domain domain; /* PM domain operations */ struct list_head gpd_list_node; /* Node in the global PM domains list */ @@ -66,10 +78,6 @@ struct generic_pm_domain { unsigned int suspended_count; /* System suspend device counter */ unsigned int prepared_count; /* Suspend counter of prepared devices */ bool suspend_power_off; /* Power status before system suspend */ - int (*power_off)(struct generic_pm_domain *domain); - s64 power_off_latency_ns; - int (*power_on)(struct generic_pm_domain *domain); - s64 power_on_latency_ns; struct gpd_dev_ops dev_ops; s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ bool max_off_time_changed; @@ -80,6 +88,11 @@ struct generic_pm_domain { void (*detach_dev)(struct generic_pm_domain *domain, struct device *dev); unsigned int flags; /* Bit field of configs for genpd */ + + struct genpd_power_state states[GENPD_MAX_NSTATES]; + int target_state; /* state that genpd will go to when off */ + int init_state; /* initial genpd state */ + int state_count; /* number of states */ }; static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)