* [PATCH V5 1/7] PM / QOS: Pass request type to dev_pm_qos_{add|remove}_notifier()
From: Viresh Kumar @ 2019-07-01 11:26 UTC (permalink / raw)
To: Rafael Wysocki, Len Brown, Pavel Machek, Kevin Hilman,
Ulf Hansson
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Qais.Yousef, mka,
juri.lelli, linux-kernel
In-Reply-To: <cover.1561979715.git.viresh.kumar@linaro.org>
In order to use the same set of routines to register notifiers for
different request types, update the existing
dev_pm_qos_{add|remove}_notifier() routines with an additional
parameter: request-type.
For now, it only supports resume-latency request type but will be
extended to frequency limit (min/max) constraints later on.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
Documentation/power/pm_qos_interface.txt | 10 ++++++----
drivers/base/power/domain.c | 8 +++++---
drivers/base/power/qos.c | 14 ++++++++++++--
include/linux/pm_qos.h | 12 ++++++++----
4 files changed, 31 insertions(+), 13 deletions(-)
diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt
index 19c5f7b1a7ba..ec7d662d1707 100644
--- a/Documentation/power/pm_qos_interface.txt
+++ b/Documentation/power/pm_qos_interface.txt
@@ -164,12 +164,14 @@ directory.
Notification mechanisms:
The per-device PM QoS framework has a per-device notification tree.
-int dev_pm_qos_add_notifier(device, notifier):
-Adds a notification callback function for the device.
+int dev_pm_qos_add_notifier(device, notifier, type):
+Adds a notification callback function for the device for a particular request
+type.
+
The callback is called when the aggregated value of the device constraints list
-is changed (for resume latency device PM QoS only).
+is changed.
-int dev_pm_qos_remove_notifier(device, notifier):
+int dev_pm_qos_remove_notifier(device, notifier, type):
Removes the notification callback function for the device.
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 33c30c1e6a30..b063bc41b0a9 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1536,7 +1536,8 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (ret)
genpd_free_dev_data(dev, gpd_data);
else
- dev_pm_qos_add_notifier(dev, &gpd_data->nb);
+ dev_pm_qos_add_notifier(dev, &gpd_data->nb,
+ DEV_PM_QOS_RESUME_LATENCY);
return ret;
}
@@ -1569,7 +1570,8 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
pdd = dev->power.subsys_data->domain_data;
gpd_data = to_gpd_data(pdd);
- dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
+ dev_pm_qos_remove_notifier(dev, &gpd_data->nb,
+ DEV_PM_QOS_RESUME_LATENCY);
genpd_lock(genpd);
@@ -1597,7 +1599,7 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
out:
genpd_unlock(genpd);
- dev_pm_qos_add_notifier(dev, &gpd_data->nb);
+ dev_pm_qos_add_notifier(dev, &gpd_data->nb, DEV_PM_QOS_RESUME_LATENCY);
return ret;
}
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 6c91f8df1d59..cfd463212513 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -467,6 +467,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
*
* @dev: target device for the constraint
* @notifier: notifier block managed by caller.
+ * @type: request type.
*
* Will register the notifier into a notification chain that gets called
* upon changes to the target value for the device.
@@ -474,10 +475,14 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
* If the device's constraints object doesn't exist when this routine is called,
* it will be created (or error code will be returned if that fails).
*/
-int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
+int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type)
{
int ret = 0;
+ if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY))
+ return -EINVAL;
+
mutex_lock(&dev_pm_qos_mtx);
if (IS_ERR(dev->power.qos))
@@ -500,15 +505,20 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
*
* @dev: target device for the constraint
* @notifier: notifier block to be removed.
+ * @type: request type.
*
* Will remove the notifier from the notification chain that gets called
* upon changes to the target value.
*/
int dev_pm_qos_remove_notifier(struct device *dev,
- struct notifier_block *notifier)
+ struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type)
{
int retval = 0;
+ if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY))
+ return -EINVAL;
+
mutex_lock(&dev_pm_qos_mtx);
/* Silently return if the constraints object is not present. */
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 6ea1ae373d77..1f4d456e8fff 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -146,9 +146,11 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
int dev_pm_qos_remove_request(struct dev_pm_qos_request *req);
int dev_pm_qos_add_notifier(struct device *dev,
- struct notifier_block *notifier);
+ struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type);
int dev_pm_qos_remove_notifier(struct device *dev,
- struct notifier_block *notifier);
+ struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type);
void dev_pm_qos_constraints_init(struct device *dev);
void dev_pm_qos_constraints_destroy(struct device *dev);
int dev_pm_qos_add_ancestor_request(struct device *dev,
@@ -202,10 +204,12 @@ static inline int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
static inline int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
{ return 0; }
static inline int dev_pm_qos_add_notifier(struct device *dev,
- struct notifier_block *notifier)
+ struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type);
{ return 0; }
static inline int dev_pm_qos_remove_notifier(struct device *dev,
- struct notifier_block *notifier)
+ struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type)
{ return 0; }
static inline void dev_pm_qos_constraints_init(struct device *dev)
{
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V5 3/7] PM / QOS: Pass request type to dev_pm_qos_read_value()
From: Viresh Kumar @ 2019-07-01 11:26 UTC (permalink / raw)
To: Rafael Wysocki, Len Brown, Pavel Machek, Kevin Hilman,
Ulf Hansson
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Qais.Yousef, mka,
juri.lelli, linux-kernel
In-Reply-To: <cover.1561979715.git.viresh.kumar@linaro.org>
In order to allow dev_pm_qos_read_value() to read values for different
QoS requests, pass request type as a parameter to these routines.
For now, it only supports resume-latency request type but will be
extended to frequency limit (min/max) constraints later on.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
Documentation/power/pm_qos_interface.txt | 2 +-
drivers/base/power/domain_governor.c | 2 +-
drivers/base/power/qos.c | 17 ++++++++++++-----
include/linux/pm_qos.h | 16 +++++++++++++---
4 files changed, 27 insertions(+), 10 deletions(-)
diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt
index ec7d662d1707..cfcb1df39799 100644
--- a/Documentation/power/pm_qos_interface.txt
+++ b/Documentation/power/pm_qos_interface.txt
@@ -123,7 +123,7 @@ Will remove the element. After removal it will update the aggregate target and
call the notification trees if the target was changed as a result of removing
the request.
-s32 dev_pm_qos_read_value(device):
+s32 dev_pm_qos_read_value(device, type):
Returns the aggregated value for a given device's constraints list.
enum pm_qos_flags_status dev_pm_qos_flags(device, mask)
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index 20e56a5be01f..daa8c7689f7e 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -33,7 +33,7 @@ static int dev_update_qos_constraint(struct device *dev, void *data)
* take its current PM QoS constraint (that's the only thing
* known at this point anyway).
*/
- constraint_ns = dev_pm_qos_read_value(dev);
+ constraint_ns = dev_pm_qos_read_value(dev, DEV_PM_QOS_RESUME_LATENCY);
constraint_ns *= NSEC_PER_USEC;
}
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 7a0d197f0809..2461fed0efa0 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -105,18 +105,25 @@ s32 __dev_pm_qos_resume_latency(struct device *dev)
/**
* dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked).
* @dev: Device to get the PM QoS constraint value for.
+ * @type: QoS request type.
*/
-s32 dev_pm_qos_read_value(struct device *dev)
+s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type)
{
+ struct dev_pm_qos *qos = dev->power.qos;
unsigned long flags;
s32 ret;
spin_lock_irqsave(&dev->power.lock, flags);
- if (IS_ERR_OR_NULL(dev->power.qos))
- ret = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
- else
- ret = pm_qos_read_value(&dev->power.qos->resume_latency);
+ switch (type) {
+ case DEV_PM_QOS_RESUME_LATENCY:
+ ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT
+ : pm_qos_read_value(&qos->resume_latency);
+ break;
+ default:
+ WARN_ON(1);
+ ret = 0;
+ }
spin_unlock_irqrestore(&dev->power.lock, flags);
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 06aa88496761..5ce1c310373e 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -140,7 +140,7 @@ s32 pm_qos_read_value(struct pm_qos_constraints *c);
enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask);
enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask);
s32 __dev_pm_qos_resume_latency(struct device *dev);
-s32 dev_pm_qos_read_value(struct device *dev);
+s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type);
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
enum dev_pm_qos_req_type type, s32 value);
int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -191,8 +191,18 @@ static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev,
{ return PM_QOS_FLAGS_UNDEFINED; }
static inline s32 __dev_pm_qos_resume_latency(struct device *dev)
{ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
-static inline s32 dev_pm_qos_read_value(struct device *dev)
- { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
+static inline s32 dev_pm_qos_read_value(struct device *dev,
+ enum dev_pm_qos_req_type type)
+{
+ switch type {
+ case DEV_PM_QOS_RESUME_LATENCY:
+ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
+ default:
+ WARN_ON(1);
+ return 0;
+ }
+}
+
static inline int dev_pm_qos_add_request(struct device *dev,
struct dev_pm_qos_request *req,
enum dev_pm_qos_req_type type,
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V5 7/7] cpufreq: Add QoS requests for userspace constraints
From: Viresh Kumar @ 2019-07-01 11:26 UTC (permalink / raw)
To: Rafael Wysocki
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Qais.Yousef, mka,
juri.lelli, Ulf Hansson, linux-kernel
In-Reply-To: <cover.1561979715.git.viresh.kumar@linaro.org>
This implements QoS requests to manage userspace configuration of min
and max frequency.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/cpufreq/cpufreq.c | 87 ++++++++++++++++++++-------------------
include/linux/cpufreq.h | 8 +---
2 files changed, 46 insertions(+), 49 deletions(-)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 091789e868ee..13c2f119cc0c 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -718,23 +718,15 @@ static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf)
static ssize_t store_##file_name \
(struct cpufreq_policy *policy, const char *buf, size_t count) \
{ \
- int ret, temp; \
- struct cpufreq_policy new_policy; \
+ unsigned long val; \
+ int ret; \
\
- memcpy(&new_policy, policy, sizeof(*policy)); \
- new_policy.min = policy->user_policy.min; \
- new_policy.max = policy->user_policy.max; \
- \
- ret = sscanf(buf, "%u", &new_policy.object); \
+ ret = sscanf(buf, "%lu", &val); \
if (ret != 1) \
return -EINVAL; \
\
- temp = new_policy.object; \
- ret = cpufreq_set_policy(policy, &new_policy); \
- if (!ret) \
- policy->user_policy.object = temp; \
- \
- return ret ? ret : count; \
+ ret = dev_pm_qos_update_request(policy->object##_freq_req, val);\
+ return ret >= 0 ? count : ret; \
}
store_one(scaling_min_freq, min);
@@ -1126,8 +1118,6 @@ void refresh_frequency_limits(struct cpufreq_policy *policy)
new_policy = *policy;
pr_debug("updating policy for CPU %u\n", policy->cpu);
- new_policy.min = policy->user_policy.min;
- new_policy.max = policy->user_policy.max;
cpufreq_set_policy(policy, &new_policy);
}
@@ -1238,6 +1228,12 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
goto err_min_qos_notifier;
}
+ policy->min_freq_req = kzalloc(2 * sizeof(*policy->min_freq_req),
+ GFP_KERNEL);
+ if (!policy->min_freq_req)
+ goto err_max_qos_notifier;
+
+ policy->max_freq_req = policy->min_freq_req + 1;
INIT_LIST_HEAD(&policy->policy_list);
init_rwsem(&policy->rwsem);
spin_lock_init(&policy->transition_lock);
@@ -1248,6 +1244,9 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
policy->cpu = cpu;
return policy;
+err_max_qos_notifier:
+ dev_pm_qos_remove_notifier(dev, &policy->nb_max,
+ DEV_PM_QOS_MAX_FREQUENCY);
err_min_qos_notifier:
dev_pm_qos_remove_notifier(dev, &policy->nb_min,
DEV_PM_QOS_MIN_FREQUENCY);
@@ -1283,6 +1282,9 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
DEV_PM_QOS_MAX_FREQUENCY);
dev_pm_qos_remove_notifier(dev, &policy->nb_min,
DEV_PM_QOS_MIN_FREQUENCY);
+ dev_pm_qos_remove_request(policy->max_freq_req);
+ dev_pm_qos_remove_request(policy->min_freq_req);
+ kfree(policy->min_freq_req);
cpufreq_policy_put_kobj(policy);
free_cpumask_var(policy->real_cpus);
@@ -1361,16 +1363,30 @@ static int cpufreq_online(unsigned int cpu)
cpumask_and(policy->cpus, policy->cpus, cpu_online_mask);
if (new_policy) {
- policy->user_policy.min = policy->min;
- policy->user_policy.max = policy->max;
+ struct device *dev = get_cpu_device(cpu);
for_each_cpu(j, policy->related_cpus) {
per_cpu(cpufreq_cpu_data, j) = policy;
add_cpu_dev_symlink(policy, j);
}
- } else {
- policy->min = policy->user_policy.min;
- policy->max = policy->user_policy.max;
+
+ ret = dev_pm_qos_add_request(dev, policy->min_freq_req,
+ DEV_PM_QOS_MIN_FREQUENCY,
+ policy->min);
+ if (ret < 0) {
+ dev_err(dev, "Failed to add min-freq constraint (%d)\n",
+ ret);
+ goto out_destroy_policy;
+ }
+
+ ret = dev_pm_qos_add_request(dev, policy->max_freq_req,
+ DEV_PM_QOS_MAX_FREQUENCY,
+ policy->max);
+ if (ret < 0) {
+ dev_err(dev, "Failed to add max-freq constraint (%d)\n",
+ ret);
+ goto out_destroy_policy;
+ }
}
if (cpufreq_driver->get && has_target()) {
@@ -2344,7 +2360,6 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
{
struct cpufreq_governor *old_gov;
struct device *cpu_dev = get_cpu_device(policy->cpu);
- unsigned long min, max;
int ret;
pr_debug("setting new policy for CPU %u: %u - %u kHz\n",
@@ -2352,24 +2367,12 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
memcpy(&new_policy->cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo));
- /*
- * This check works well when we store new min/max freq attributes,
- * because new_policy is a copy of policy with one field updated.
- */
- if (new_policy->min > new_policy->max)
- return -EINVAL;
-
/*
* PM QoS framework collects all the requests from users and provide us
* the final aggregated value here.
*/
- min = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MIN_FREQUENCY);
- max = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MAX_FREQUENCY);
-
- if (min > new_policy->min)
- new_policy->min = min;
- if (max < new_policy->max)
- new_policy->max = max;
+ new_policy->min = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MIN_FREQUENCY);
+ new_policy->max = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MAX_FREQUENCY);
/* verify the cpu speed can be set within this limit */
ret = cpufreq_driver->verify(new_policy);
@@ -2458,10 +2461,9 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
* @cpu: CPU to re-evaluate the policy for.
*
* Update the current frequency for the cpufreq policy of @cpu and use
- * cpufreq_set_policy() to re-apply the min and max limits saved in the
- * user_policy sub-structure of that policy, which triggers the evaluation
- * of policy notifiers and the cpufreq driver's ->verify() callback for the
- * policy in question, among other things.
+ * cpufreq_set_policy() to re-apply the min and max limits, which triggers the
+ * evaluation of policy notifiers and the cpufreq driver's ->verify() callback
+ * for the policy in question, among other things.
*/
void cpufreq_update_policy(unsigned int cpu)
{
@@ -2521,10 +2523,9 @@ static int cpufreq_boost_set_sw(int state)
break;
}
- down_write(&policy->rwsem);
- policy->user_policy.max = policy->max;
- cpufreq_governor_limits(policy);
- up_write(&policy->rwsem);
+ ret = dev_pm_qos_update_request(policy->max_freq_req, policy->max);
+ if (ret)
+ break;
}
return ret;
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 1fa37b675a80..afc683021ac5 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -50,11 +50,6 @@ struct cpufreq_cpuinfo {
unsigned int transition_latency;
};
-struct cpufreq_user_policy {
- unsigned int min; /* in kHz */
- unsigned int max; /* in kHz */
-};
-
struct cpufreq_policy {
/* CPUs sharing clock, require sw coordination */
cpumask_var_t cpus; /* Online CPUs only */
@@ -84,7 +79,8 @@ struct cpufreq_policy {
struct work_struct update; /* if update_policy() needs to be
* called, but you're in IRQ context */
- struct cpufreq_user_policy user_policy;
+ struct dev_pm_qos_request *min_freq_req;
+ struct dev_pm_qos_request *max_freq_req;
struct cpufreq_frequency_table *freq_table;
enum cpufreq_table_sorting freq_table_sorted;
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V5 6/7] cpufreq: intel_pstate: Reuse refresh_frequency_limits()
From: Viresh Kumar @ 2019-07-01 11:26 UTC (permalink / raw)
To: Rafael Wysocki, Srinivas Pandruvada, Len Brown
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Qais.Yousef, mka,
juri.lelli, linux-kernel
In-Reply-To: <cover.1561979715.git.viresh.kumar@linaro.org>
The implementation of intel_pstate_update_max_freq() is quite similar to
refresh_frequency_limits(), lets reuse it.
Finding minimum of policy->user_policy.max and policy->cpuinfo.max_freq
in intel_pstate_update_max_freq() is redundant as cpufreq_set_policy()
will call the ->verify() callback of intel-pstate driver, which will do
this comparison anyway and so dropping it from
intel_pstate_update_max_freq() doesn't harm.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/cpufreq/cpufreq.c | 3 ++-
drivers/cpufreq/intel_pstate.c | 7 +------
include/linux/cpufreq.h | 1 +
3 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 81117e4d43cc..091789e868ee 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1116,7 +1116,7 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, unsigned int cp
return ret;
}
-static void refresh_frequency_limits(struct cpufreq_policy *policy)
+void refresh_frequency_limits(struct cpufreq_policy *policy)
{
struct cpufreq_policy new_policy;
@@ -1133,6 +1133,7 @@ static void refresh_frequency_limits(struct cpufreq_policy *policy)
up_write(&policy->rwsem);
}
+EXPORT_SYMBOL(refresh_frequency_limits);
static void handle_update(struct work_struct *work)
{
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index f2ff5de988c1..cc27d4c59dca 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -898,7 +898,6 @@ static void intel_pstate_update_policies(void)
static void intel_pstate_update_max_freq(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu);
- struct cpufreq_policy new_policy;
struct cpudata *cpudata;
if (!policy)
@@ -908,11 +907,7 @@ static void intel_pstate_update_max_freq(unsigned int cpu)
policy->cpuinfo.max_freq = global.turbo_disabled_mf ?
cpudata->pstate.max_freq : cpudata->pstate.turbo_freq;
- memcpy(&new_policy, policy, sizeof(*policy));
- new_policy.max = min(policy->user_policy.max, policy->cpuinfo.max_freq);
- new_policy.min = min(policy->user_policy.min, new_policy.max);
-
- cpufreq_set_policy(policy, &new_policy);
+ refresh_frequency_limits(policy);
cpufreq_cpu_release(policy);
}
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 95425941f46d..1fa37b675a80 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -207,6 +207,7 @@ void cpufreq_cpu_release(struct cpufreq_policy *policy);
int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu);
int cpufreq_set_policy(struct cpufreq_policy *policy,
struct cpufreq_policy *new_policy);
+void refresh_frequency_limits(struct cpufreq_policy *policy);
void cpufreq_update_policy(unsigned int cpu);
void cpufreq_update_limits(unsigned int cpu);
bool have_governor_per_policy(void);
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V5 5/7] cpufreq: Register notifiers with the PM QoS framework
From: Viresh Kumar @ 2019-07-01 11:26 UTC (permalink / raw)
To: Rafael Wysocki
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Qais.Yousef, mka,
juri.lelli, Ulf Hansson, linux-kernel
In-Reply-To: <cover.1561979715.git.viresh.kumar@linaro.org>
This registers the notifiers for min/max frequency constraints with the
PM QoS framework. The constraints are also taken into consideration in
cpufreq_set_policy().
This also relocates cpufreq_policy_put_kobj() as it is required to be
called from cpufreq_policy_alloc() now.
refresh_frequency_limits() is updated to have proper locking in place and
avoid calling cpufreq_set_policy() for inactive policies.
No constraints are added until now though.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/cpufreq/cpufreq.c | 135 ++++++++++++++++++++++++++++++--------
include/linux/cpufreq.h | 3 +
2 files changed, 109 insertions(+), 29 deletions(-)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index ceb57af15ca0..81117e4d43cc 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -26,6 +26,7 @@
#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/syscore_ops.h>
@@ -999,7 +1000,7 @@ static void add_cpu_dev_symlink(struct cpufreq_policy *policy, unsigned int cpu)
{
struct device *dev = get_cpu_device(cpu);
- if (!dev)
+ if (unlikely(!dev))
return;
if (cpumask_test_and_set_cpu(cpu, policy->real_cpus))
@@ -1117,14 +1118,20 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, unsigned int cp
static void refresh_frequency_limits(struct cpufreq_policy *policy)
{
- struct cpufreq_policy new_policy = *policy;
+ struct cpufreq_policy new_policy;
- pr_debug("updating policy for CPU %u\n", policy->cpu);
+ down_write(&policy->rwsem);
+
+ if (!policy_is_inactive(policy)) {
+ new_policy = *policy;
+ pr_debug("updating policy for CPU %u\n", policy->cpu);
- new_policy.min = policy->user_policy.min;
- new_policy.max = policy->user_policy.max;
+ new_policy.min = policy->user_policy.min;
+ new_policy.max = policy->user_policy.max;
+ cpufreq_set_policy(policy, &new_policy);
+ }
- cpufreq_set_policy(policy, &new_policy);
+ up_write(&policy->rwsem);
}
static void handle_update(struct work_struct *work)
@@ -1136,11 +1143,55 @@ static void handle_update(struct work_struct *work)
refresh_frequency_limits(policy);
}
+static int cpufreq_notifier_min(struct notifier_block *nb, unsigned long freq,
+ void *data)
+{
+ struct cpufreq_policy *policy = container_of(nb, struct cpufreq_policy, nb_min);
+
+ schedule_work(&policy->update);
+ return 0;
+}
+
+static int cpufreq_notifier_max(struct notifier_block *nb, unsigned long freq,
+ void *data)
+{
+ struct cpufreq_policy *policy = container_of(nb, struct cpufreq_policy, nb_max);
+
+ schedule_work(&policy->update);
+ return 0;
+}
+
+static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
+{
+ struct kobject *kobj;
+ struct completion *cmp;
+
+ down_write(&policy->rwsem);
+ cpufreq_stats_free_table(policy);
+ kobj = &policy->kobj;
+ cmp = &policy->kobj_unregister;
+ up_write(&policy->rwsem);
+ kobject_put(kobj);
+
+ /*
+ * We need to make sure that the underlying kobj is
+ * actually not referenced anymore by anybody before we
+ * proceed with unloading.
+ */
+ pr_debug("waiting for dropping of refcount\n");
+ wait_for_completion(cmp);
+ pr_debug("wait complete\n");
+}
+
static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
{
struct cpufreq_policy *policy;
+ struct device *dev = get_cpu_device(cpu);
int ret;
+ if (!dev)
+ return NULL;
+
policy = kzalloc(sizeof(*policy), GFP_KERNEL);
if (!policy)
return NULL;
@@ -1157,7 +1208,7 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq,
cpufreq_global_kobject, "policy%u", cpu);
if (ret) {
- pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret);
+ dev_err(dev, "%s: failed to init policy->kobj: %d\n", __func__, ret);
/*
* The entire policy object will be freed below, but the extra
* memory allocated for the kobject name needs to be freed by
@@ -1167,6 +1218,25 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
goto err_free_real_cpus;
}
+ policy->nb_min.notifier_call = cpufreq_notifier_min;
+ policy->nb_max.notifier_call = cpufreq_notifier_max;
+
+ ret = dev_pm_qos_add_notifier(dev, &policy->nb_min,
+ DEV_PM_QOS_MIN_FREQUENCY);
+ if (ret) {
+ dev_err(dev, "Failed to register MIN QoS notifier: %d (%*pbl)\n",
+ ret, cpumask_pr_args(policy->cpus));
+ goto err_kobj_remove;
+ }
+
+ ret = dev_pm_qos_add_notifier(dev, &policy->nb_max,
+ DEV_PM_QOS_MAX_FREQUENCY);
+ if (ret) {
+ dev_err(dev, "Failed to register MAX QoS notifier: %d (%*pbl)\n",
+ ret, cpumask_pr_args(policy->cpus));
+ goto err_min_qos_notifier;
+ }
+
INIT_LIST_HEAD(&policy->policy_list);
init_rwsem(&policy->rwsem);
spin_lock_init(&policy->transition_lock);
@@ -1177,6 +1247,11 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
policy->cpu = cpu;
return policy;
+err_min_qos_notifier:
+ dev_pm_qos_remove_notifier(dev, &policy->nb_min,
+ DEV_PM_QOS_MIN_FREQUENCY);
+err_kobj_remove:
+ cpufreq_policy_put_kobj(policy);
err_free_real_cpus:
free_cpumask_var(policy->real_cpus);
err_free_rcpumask:
@@ -1189,30 +1264,9 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
return NULL;
}
-static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
-{
- struct kobject *kobj;
- struct completion *cmp;
-
- down_write(&policy->rwsem);
- cpufreq_stats_free_table(policy);
- kobj = &policy->kobj;
- cmp = &policy->kobj_unregister;
- up_write(&policy->rwsem);
- kobject_put(kobj);
-
- /*
- * We need to make sure that the underlying kobj is
- * actually not referenced anymore by anybody before we
- * proceed with unloading.
- */
- pr_debug("waiting for dropping of refcount\n");
- wait_for_completion(cmp);
- pr_debug("wait complete\n");
-}
-
static void cpufreq_policy_free(struct cpufreq_policy *policy)
{
+ struct device *dev = get_cpu_device(policy->cpu);
unsigned long flags;
int cpu;
@@ -1224,6 +1278,11 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
per_cpu(cpufreq_cpu_data, cpu) = NULL;
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ dev_pm_qos_remove_notifier(dev, &policy->nb_max,
+ DEV_PM_QOS_MAX_FREQUENCY);
+ dev_pm_qos_remove_notifier(dev, &policy->nb_min,
+ DEV_PM_QOS_MIN_FREQUENCY);
+
cpufreq_policy_put_kobj(policy);
free_cpumask_var(policy->real_cpus);
free_cpumask_var(policy->related_cpus);
@@ -2283,6 +2342,8 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
struct cpufreq_policy *new_policy)
{
struct cpufreq_governor *old_gov;
+ struct device *cpu_dev = get_cpu_device(policy->cpu);
+ unsigned long min, max;
int ret;
pr_debug("setting new policy for CPU %u: %u - %u kHz\n",
@@ -2297,11 +2358,27 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
if (new_policy->min > new_policy->max)
return -EINVAL;
+ /*
+ * PM QoS framework collects all the requests from users and provide us
+ * the final aggregated value here.
+ */
+ min = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MIN_FREQUENCY);
+ max = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MAX_FREQUENCY);
+
+ if (min > new_policy->min)
+ new_policy->min = min;
+ if (max < new_policy->max)
+ new_policy->max = max;
+
/* verify the cpu speed can be set within this limit */
ret = cpufreq_driver->verify(new_policy);
if (ret)
return ret;
+ /*
+ * The notifier-chain shall be removed once all the users of
+ * CPUFREQ_ADJUST are moved to use the QoS framework.
+ */
/* adjust if necessary - all reasons */
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
CPUFREQ_ADJUST, new_policy);
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index a1467aa7f58b..95425941f46d 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -147,6 +147,9 @@ struct cpufreq_policy {
/* Pointer to the cooling device if used for thermal mitigation */
struct thermal_cooling_device *cdev;
+
+ struct notifier_block nb_min;
+ struct notifier_block nb_max;
};
struct cpufreq_freqs {
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V5 4/7] PM / QoS: Add support for MIN/MAX frequency constraints
From: Viresh Kumar @ 2019-07-01 11:26 UTC (permalink / raw)
To: Rafael Wysocki, Len Brown, Pavel Machek
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Qais.Yousef, mka,
juri.lelli, Ulf Hansson, linux-kernel
In-Reply-To: <cover.1561979715.git.viresh.kumar@linaro.org>
This patch introduces the min-frequency and max-frequency device
constraints, which will be used by the cpufreq core to begin with.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/base/power/qos.c | 111 ++++++++++++++++++++++++++++++++++-----
include/linux/pm_qos.h | 12 +++++
2 files changed, 109 insertions(+), 14 deletions(-)
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 2461fed0efa0..6c90fd7e2ff8 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -120,6 +120,14 @@ s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type)
ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT
: pm_qos_read_value(&qos->resume_latency);
break;
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE
+ : pm_qos_read_value(&qos->min_frequency);
+ break;
+ case DEV_PM_QOS_MAX_FREQUENCY:
+ ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE
+ : pm_qos_read_value(&qos->max_frequency);
+ break;
default:
WARN_ON(1);
ret = 0;
@@ -161,6 +169,14 @@ static int apply_constraint(struct dev_pm_qos_request *req,
req->dev->power.set_latency_tolerance(req->dev, value);
}
break;
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ ret = pm_qos_update_target(&qos->min_frequency,
+ &req->data.pnode, action, value);
+ break;
+ case DEV_PM_QOS_MAX_FREQUENCY:
+ ret = pm_qos_update_target(&qos->max_frequency,
+ &req->data.pnode, action, value);
+ break;
case DEV_PM_QOS_FLAGS:
ret = pm_qos_update_flags(&qos->flags, &req->data.flr,
action, value);
@@ -189,12 +205,11 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
if (!qos)
return -ENOMEM;
- n = kzalloc(sizeof(*n), GFP_KERNEL);
+ n = kzalloc(3 * sizeof(*n), GFP_KERNEL);
if (!n) {
kfree(qos);
return -ENOMEM;
}
- BLOCKING_INIT_NOTIFIER_HEAD(n);
c = &qos->resume_latency;
plist_head_init(&c->list);
@@ -203,6 +218,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
c->type = PM_QOS_MIN;
c->notifiers = n;
+ BLOCKING_INIT_NOTIFIER_HEAD(n);
c = &qos->latency_tolerance;
plist_head_init(&c->list);
@@ -211,6 +227,24 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT;
c->type = PM_QOS_MIN;
+ c = &qos->min_frequency;
+ plist_head_init(&c->list);
+ c->target_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
+ c->default_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
+ c->no_constraint_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
+ c->type = PM_QOS_MAX;
+ c->notifiers = ++n;
+ BLOCKING_INIT_NOTIFIER_HEAD(n);
+
+ c = &qos->max_frequency;
+ plist_head_init(&c->list);
+ c->target_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
+ c->default_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
+ c->no_constraint_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
+ c->type = PM_QOS_MIN;
+ c->notifiers = ++n;
+ BLOCKING_INIT_NOTIFIER_HEAD(n);
+
INIT_LIST_HEAD(&qos->flags.list);
spin_lock_irq(&dev->power.lock);
@@ -264,11 +298,25 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
memset(req, 0, sizeof(*req));
}
+
c = &qos->latency_tolerance;
plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
memset(req, 0, sizeof(*req));
}
+
+ c = &qos->min_frequency;
+ plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
+ apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE);
+ memset(req, 0, sizeof(*req));
+ }
+
+ c = &qos->max_frequency;
+ plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
+ apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
+ memset(req, 0, sizeof(*req));
+ }
+
f = &qos->flags;
list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) {
apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
@@ -380,6 +428,8 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req,
switch(req->type) {
case DEV_PM_QOS_RESUME_LATENCY:
case DEV_PM_QOS_LATENCY_TOLERANCE:
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ case DEV_PM_QOS_MAX_FREQUENCY:
curr_value = req->data.pnode.prio;
break;
case DEV_PM_QOS_FLAGS:
@@ -492,9 +542,6 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
{
int ret = 0;
- if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY))
- return -EINVAL;
-
mutex_lock(&dev_pm_qos_mtx);
if (IS_ERR(dev->power.qos))
@@ -502,10 +549,28 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
else if (!dev->power.qos)
ret = dev_pm_qos_constraints_allocate(dev);
- if (!ret)
+ if (ret)
+ goto unlock;
+
+ switch (type) {
+ case DEV_PM_QOS_RESUME_LATENCY:
ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers,
notifier);
+ break;
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ ret = blocking_notifier_chain_register(dev->power.qos->min_frequency.notifiers,
+ notifier);
+ break;
+ case DEV_PM_QOS_MAX_FREQUENCY:
+ ret = blocking_notifier_chain_register(dev->power.qos->max_frequency.notifiers,
+ notifier);
+ break;
+ default:
+ WARN_ON(1);
+ ret = -EINVAL;
+ }
+unlock:
mutex_unlock(&dev_pm_qos_mtx);
return ret;
}
@@ -526,20 +591,35 @@ int dev_pm_qos_remove_notifier(struct device *dev,
struct notifier_block *notifier,
enum dev_pm_qos_req_type type)
{
- int retval = 0;
-
- if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY))
- return -EINVAL;
+ int ret = 0;
mutex_lock(&dev_pm_qos_mtx);
/* Silently return if the constraints object is not present. */
- if (!IS_ERR_OR_NULL(dev->power.qos))
- retval = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers,
- notifier);
+ if (IS_ERR_OR_NULL(dev->power.qos))
+ goto unlock;
+
+ switch (type) {
+ case DEV_PM_QOS_RESUME_LATENCY:
+ ret = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers,
+ notifier);
+ break;
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ ret = blocking_notifier_chain_unregister(dev->power.qos->min_frequency.notifiers,
+ notifier);
+ break;
+ case DEV_PM_QOS_MAX_FREQUENCY:
+ ret = blocking_notifier_chain_unregister(dev->power.qos->max_frequency.notifiers,
+ notifier);
+ break;
+ default:
+ WARN_ON(1);
+ ret = -EINVAL;
+ }
+unlock:
mutex_unlock(&dev_pm_qos_mtx);
- return retval;
+ return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
@@ -599,6 +679,9 @@ static void __dev_pm_qos_drop_user_request(struct device *dev,
req = dev->power.qos->flags_req;
dev->power.qos->flags_req = NULL;
break;
+ default:
+ WARN_ON(1);
+ return;
}
__dev_pm_qos_remove_request(req);
kfree(req);
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 5ce1c310373e..17132b10b233 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -40,6 +40,8 @@ enum pm_qos_flags_status {
#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY
#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS
#define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0
+#define PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE 0
+#define PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE (-1)
#define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1)
#define PM_QOS_FLAG_NO_POWER_OFF (1 << 0)
@@ -58,6 +60,8 @@ struct pm_qos_flags_request {
enum dev_pm_qos_req_type {
DEV_PM_QOS_RESUME_LATENCY = 1,
DEV_PM_QOS_LATENCY_TOLERANCE,
+ DEV_PM_QOS_MIN_FREQUENCY,
+ DEV_PM_QOS_MAX_FREQUENCY,
DEV_PM_QOS_FLAGS,
};
@@ -99,10 +103,14 @@ struct pm_qos_flags {
struct dev_pm_qos {
struct pm_qos_constraints resume_latency;
struct pm_qos_constraints latency_tolerance;
+ struct pm_qos_constraints min_frequency;
+ struct pm_qos_constraints max_frequency;
struct pm_qos_flags flags;
struct dev_pm_qos_request *resume_latency_req;
struct dev_pm_qos_request *latency_tolerance_req;
struct dev_pm_qos_request *flags_req;
+ struct dev_pm_qos_request *min_frequency_req;
+ struct dev_pm_qos_request *max_frequency_req;
};
/* Action requested to pm_qos_update_target */
@@ -197,6 +205,10 @@ static inline s32 dev_pm_qos_read_value(struct device *dev,
switch type {
case DEV_PM_QOS_RESUME_LATENCY:
return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ return PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
+ case DEV_PM_QOS_MAX_FREQUENCY:
+ return PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
default:
WARN_ON(1);
return 0;
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V5 2/7] PM / QOS: Rename __dev_pm_qos_read_value() and dev_pm_qos_raw_read_value()
From: Viresh Kumar @ 2019-07-01 11:26 UTC (permalink / raw)
To: Rafael Wysocki, Kevin Hilman, Ulf Hansson, Len Brown,
Pavel Machek, Daniel Lezcano
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Qais.Yousef, mka,
juri.lelli, linux-kernel
In-Reply-To: <cover.1561979715.git.viresh.kumar@linaro.org>
dev_pm_qos_read_value() will soon need to support more constraint types
(min/max frequency) and will have another argument to it, i.e. type of
the constraint. While that is fine for the existing users of
dev_pm_qos_read_value(), but not that optimal for the callers of
__dev_pm_qos_read_value() and dev_pm_qos_raw_read_value() as all the
callers of these two routines are only looking for resume latency
constraint.
Lets make these two routines care only about the resume latency
constraint and rename them to __dev_pm_qos_resume_latency() and
dev_pm_qos_raw_resume_latency().
Suggested-by: Rafael J. Wysocki <rjw@rjwysocki.net>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/base/power/domain_governor.c | 2 +-
drivers/base/power/qos.c | 13 +++++++++----
drivers/base/power/runtime.c | 2 +-
drivers/cpuidle/governor.c | 2 +-
include/linux/pm_qos.h | 8 ++++----
5 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index 3838045c9277..20e56a5be01f 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -66,7 +66,7 @@ static bool default_suspend_ok(struct device *dev)
td->constraint_changed = false;
td->cached_suspend_ok = false;
td->effective_constraint_ns = 0;
- constraint_ns = __dev_pm_qos_read_value(dev);
+ constraint_ns = __dev_pm_qos_resume_latency(dev);
spin_unlock_irqrestore(&dev->power.lock, flags);
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index cfd463212513..7a0d197f0809 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -90,16 +90,16 @@ enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask)
EXPORT_SYMBOL_GPL(dev_pm_qos_flags);
/**
- * __dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * __dev_pm_qos_resume_latency - Get resume latency constraint for a given device.
* @dev: Device to get the PM QoS constraint value for.
*
* This routine must be called with dev->power.lock held.
*/
-s32 __dev_pm_qos_read_value(struct device *dev)
+s32 __dev_pm_qos_resume_latency(struct device *dev)
{
lockdep_assert_held(&dev->power.lock);
- return dev_pm_qos_raw_read_value(dev);
+ return dev_pm_qos_raw_resume_latency(dev);
}
/**
@@ -112,7 +112,12 @@ s32 dev_pm_qos_read_value(struct device *dev)
s32 ret;
spin_lock_irqsave(&dev->power.lock, flags);
- ret = __dev_pm_qos_read_value(dev);
+
+ if (IS_ERR_OR_NULL(dev->power.qos))
+ ret = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
+ else
+ ret = pm_qos_read_value(&dev->power.qos->resume_latency);
+
spin_unlock_irqrestore(&dev->power.lock, flags);
return ret;
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 952a1e7057c7..b75335508d2c 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -275,7 +275,7 @@ static int rpm_check_suspend_allowed(struct device *dev)
|| (dev->power.request_pending
&& dev->power.request == RPM_REQ_RESUME))
retval = -EAGAIN;
- else if (__dev_pm_qos_read_value(dev) == 0)
+ else if (__dev_pm_qos_resume_latency(dev) == 0)
retval = -EPERM;
else if (dev->power.runtime_status == RPM_SUSPENDED)
retval = 1;
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index 9fddf828a76f..2e3e14192bee 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -110,7 +110,7 @@ int cpuidle_governor_latency_req(unsigned int cpu)
{
int global_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
struct device *device = get_cpu_device(cpu);
- int device_req = dev_pm_qos_raw_read_value(device);
+ int device_req = dev_pm_qos_raw_resume_latency(device);
return device_req < global_req ? device_req : global_req;
}
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 1f4d456e8fff..06aa88496761 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -139,7 +139,7 @@ s32 pm_qos_read_value(struct pm_qos_constraints *c);
#ifdef CONFIG_PM
enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask);
enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask);
-s32 __dev_pm_qos_read_value(struct device *dev);
+s32 __dev_pm_qos_resume_latency(struct device *dev);
s32 dev_pm_qos_read_value(struct device *dev);
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
enum dev_pm_qos_req_type type, s32 value);
@@ -176,7 +176,7 @@ static inline s32 dev_pm_qos_requested_flags(struct device *dev)
return dev->power.qos->flags_req->data.flr.flags;
}
-static inline s32 dev_pm_qos_raw_read_value(struct device *dev)
+static inline s32 dev_pm_qos_raw_resume_latency(struct device *dev)
{
return IS_ERR_OR_NULL(dev->power.qos) ?
PM_QOS_RESUME_LATENCY_NO_CONSTRAINT :
@@ -189,7 +189,7 @@ static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev,
static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev,
s32 mask)
{ return PM_QOS_FLAGS_UNDEFINED; }
-static inline s32 __dev_pm_qos_read_value(struct device *dev)
+static inline s32 __dev_pm_qos_resume_latency(struct device *dev)
{ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
static inline s32 dev_pm_qos_read_value(struct device *dev)
{ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
@@ -245,7 +245,7 @@ static inline s32 dev_pm_qos_requested_resume_latency(struct device *dev)
return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
}
static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; }
-static inline s32 dev_pm_qos_raw_read_value(struct device *dev)
+static inline s32 dev_pm_qos_raw_resume_latency(struct device *dev)
{
return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
}
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V5 0/7] cpufreq: Use QoS layer to manage freq-constraints
From: Viresh Kumar @ 2019-07-01 11:26 UTC (permalink / raw)
To: Rafael Wysocki, Daniel Lezcano, Kevin Hilman, Len Brown,
Len Brown, Pavel Machek, Srinivas Pandruvada, Ulf Hansson
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Qais.Yousef, mka,
juri.lelli, linux-kernel
Hello,
This patchset attempts to manage CPU frequency constraints using the PM
QoS framework. It only does the basic stuff right now and moves the
userspace constraints to use the QoS infrastructure.
Todo:
- Migrate all users to the QoS framework and get rid of cpufreq specific
notifiers.
- Make PM QoS learn about the relation of CPUs in a policy, so a single
list of constraints is managed for all of them instead of per-cpu
constraints.
V4->V5:
- A new patch added, 6/7.
V3->V4:
- Few commit logs updated as suggested during reviews.
- Separate commit (2/6) to create resume-latency specific routines
- Reused earlier work ("update") for notifiers as well.
- Kept Reviewed-by tags as is as the patches normally got better only.
Please take them back if you find any issues.
V2->V3:
- Add a comment in cpufreq.c as suggested by Qais.
- Rebased on latest pm/linux-next.
V1->V2:
- The previous version introduced a completely new framework, this one
moves to PM QoS instead.
- Lots of changes because of this.
--
viresh
[1] http://lore.kernel.org/lkml/cover.1560999838.git.viresh.kumar@linaro.org
Viresh Kumar (7):
PM / QOS: Pass request type to dev_pm_qos_{add|remove}_notifier()
PM / QOS: Rename __dev_pm_qos_read_value() and
dev_pm_qos_raw_read_value()
PM / QOS: Pass request type to dev_pm_qos_read_value()
PM / QoS: Add support for MIN/MAX frequency constraints
cpufreq: Register notifiers with the PM QoS framework
cpufreq: intel_pstate: Reuse refresh_frequency_limits()
cpufreq: Add QoS requests for userspace constraints
Documentation/power/pm_qos_interface.txt | 12 +-
drivers/base/power/domain.c | 8 +-
drivers/base/power/domain_governor.c | 4 +-
drivers/base/power/qos.c | 135 +++++++++++++--
drivers/base/power/runtime.c | 2 +-
drivers/cpufreq/cpufreq.c | 201 ++++++++++++++++-------
drivers/cpufreq/intel_pstate.c | 7 +-
drivers/cpuidle/governor.c | 2 +-
include/linux/cpufreq.h | 12 +-
include/linux/pm_qos.h | 48 ++++--
10 files changed, 320 insertions(+), 111 deletions(-)
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply
* [PATCH v2 3/5] ACPI: PM: Simplify and fix PM domain hibernation callbacks
From: Rafael J. Wysocki @ 2019-07-01 10:54 UTC (permalink / raw)
To: Linux PM
Cc: Linux PCI, Linux ACPI, LKML, Bjorn Helgaas, Andy Shevchenko,
Mika Westerberg, Hans De Goede, Robert R. Howell
In-Reply-To: <4976412.ihyb9sT5jY@kreacher>
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
First, after a previous change causing all runtime-suspended devices
in the ACPI PM domain (and ACPI LPSS devices) to be resumed before
creating a snapshot image of memory during hibernation, it is not
necessary to worry about the case in which them might be left in
runtime-suspend any more, so get rid of the code related to that from
ACPI PM domain and ACPI LPSS hibernation callbacks.
Second, it is not correct to use pm_generic_resume_early() and
acpi_subsys_resume_noirq() in hibernation "restore" callbacks (which
currently happens in the ACPI PM domain and ACPI LPSS), so introduce
proper _restore_late and _restore_noirq callbacks for the ACPI PM
domain and ACPI LPSS.
Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account)
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
-> v2:
* Add a comment explaining the acpi_lpss_resume_noirq() behavior.
* Make the new LPSS "restore" callbacks follow that behavior.
---
drivers/acpi/acpi_lpss.c | 61 ++++++++++++++++++++++++++++++++++++++++-------
drivers/acpi/device_pm.c | 61 ++++++-----------------------------------------
include/linux/acpi.h | 10 -------
3 files changed, 61 insertions(+), 71 deletions(-)
Index: linux-pm/drivers/acpi/device_pm.c
===================================================================
--- linux-pm.orig/drivers/acpi/device_pm.c
+++ linux-pm/drivers/acpi/device_pm.c
@@ -1116,7 +1116,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_no
* acpi_subsys_resume_noirq - Run the device driver's "noirq" resume callback.
* @dev: Device to handle.
*/
-int acpi_subsys_resume_noirq(struct device *dev)
+static int acpi_subsys_resume_noirq(struct device *dev)
{
if (dev_pm_may_skip_resume(dev))
return 0;
@@ -1131,7 +1131,6 @@ int acpi_subsys_resume_noirq(struct devi
return pm_generic_resume_noirq(dev);
}
-EXPORT_SYMBOL_GPL(acpi_subsys_resume_noirq);
/**
* acpi_subsys_resume_early - Resume device using ACPI.
@@ -1141,12 +1140,11 @@ EXPORT_SYMBOL_GPL(acpi_subsys_resume_noi
* generic early resume procedure for it during system transition into the
* working state.
*/
-int acpi_subsys_resume_early(struct device *dev)
+static int acpi_subsys_resume_early(struct device *dev)
{
int ret = acpi_dev_resume(dev);
return ret ? ret : pm_generic_resume_early(dev);
}
-EXPORT_SYMBOL_GPL(acpi_subsys_resume_early);
/**
* acpi_subsys_freeze - Run the device driver's freeze callback.
@@ -1169,52 +1167,15 @@ int acpi_subsys_freeze(struct device *de
EXPORT_SYMBOL_GPL(acpi_subsys_freeze);
/**
- * acpi_subsys_freeze_late - Run the device driver's "late" freeze callback.
- * @dev: Device to handle.
- */
-int acpi_subsys_freeze_late(struct device *dev)
-{
-
- if (dev_pm_smart_suspend_and_suspended(dev))
- return 0;
-
- return pm_generic_freeze_late(dev);
-}
-EXPORT_SYMBOL_GPL(acpi_subsys_freeze_late);
-
-/**
- * acpi_subsys_freeze_noirq - Run the device driver's "noirq" freeze callback.
- * @dev: Device to handle.
- */
-int acpi_subsys_freeze_noirq(struct device *dev)
-{
-
- if (dev_pm_smart_suspend_and_suspended(dev))
- return 0;
-
- return pm_generic_freeze_noirq(dev);
-}
-EXPORT_SYMBOL_GPL(acpi_subsys_freeze_noirq);
-
-/**
- * acpi_subsys_thaw_noirq - Run the device driver's "noirq" thaw callback.
- * @dev: Device to handle.
+ * acpi_subsys_restore_early - Restore device using ACPI.
+ * @dev: Device to restore.
*/
-int acpi_subsys_thaw_noirq(struct device *dev)
+int acpi_subsys_restore_early(struct device *dev)
{
- /*
- * If the device is in runtime suspend, the "thaw" code may not work
- * correctly with it, so skip the driver callback and make the PM core
- * skip all of the subsequent "thaw" callbacks for the device.
- */
- if (dev_pm_smart_suspend_and_suspended(dev)) {
- dev_pm_skip_next_resume_phases(dev);
- return 0;
- }
-
- return pm_generic_thaw_noirq(dev);
+ int ret = acpi_dev_resume(dev);
+ return ret ? ret : pm_generic_restore_early(dev);
}
-EXPORT_SYMBOL_GPL(acpi_subsys_thaw_noirq);
+EXPORT_SYMBOL_GPL(acpi_subsys_restore_early);
#endif /* CONFIG_PM_SLEEP */
static struct dev_pm_domain acpi_general_pm_domain = {
@@ -1230,14 +1191,10 @@ static struct dev_pm_domain acpi_general
.resume_noirq = acpi_subsys_resume_noirq,
.resume_early = acpi_subsys_resume_early,
.freeze = acpi_subsys_freeze,
- .freeze_late = acpi_subsys_freeze_late,
- .freeze_noirq = acpi_subsys_freeze_noirq,
- .thaw_noirq = acpi_subsys_thaw_noirq,
.poweroff = acpi_subsys_suspend,
.poweroff_late = acpi_subsys_suspend_late,
.poweroff_noirq = acpi_subsys_suspend_noirq,
- .restore_noirq = acpi_subsys_resume_noirq,
- .restore_early = acpi_subsys_resume_early,
+ .restore_early = acpi_subsys_restore_early,
#endif
},
};
Index: linux-pm/include/linux/acpi.h
===================================================================
--- linux-pm.orig/include/linux/acpi.h
+++ linux-pm/include/linux/acpi.h
@@ -918,26 +918,16 @@ int acpi_subsys_prepare(struct device *d
void acpi_subsys_complete(struct device *dev);
int acpi_subsys_suspend_late(struct device *dev);
int acpi_subsys_suspend_noirq(struct device *dev);
-int acpi_subsys_resume_noirq(struct device *dev);
-int acpi_subsys_resume_early(struct device *dev);
int acpi_subsys_suspend(struct device *dev);
int acpi_subsys_freeze(struct device *dev);
-int acpi_subsys_freeze_late(struct device *dev);
-int acpi_subsys_freeze_noirq(struct device *dev);
-int acpi_subsys_thaw_noirq(struct device *dev);
#else
static inline int acpi_dev_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
static inline void acpi_subsys_complete(struct device *dev) {}
static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; }
static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; }
-static inline int acpi_subsys_resume_noirq(struct device *dev) { return 0; }
-static inline int acpi_subsys_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_suspend(struct device *dev) { return 0; }
static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
-static inline int acpi_subsys_freeze_late(struct device *dev) { return 0; }
-static inline int acpi_subsys_freeze_noirq(struct device *dev) { return 0; }
-static inline int acpi_subsys_thaw_noirq(struct device *dev) { return 0; }
#endif
#ifdef CONFIG_ACPI
Index: linux-pm/drivers/acpi/acpi_lpss.c
===================================================================
--- linux-pm.orig/drivers/acpi/acpi_lpss.c
+++ linux-pm/drivers/acpi/acpi_lpss.c
@@ -1091,16 +1091,62 @@ static int acpi_lpss_resume_noirq(struct
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;
- ret = acpi_subsys_resume_noirq(dev);
+ /* Follow acpi_subsys_resume_noirq(). */
+ if (dev_pm_may_skip_resume(dev))
+ return 0;
+
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ pm_runtime_set_active(dev);
+
+ ret = pm_generic_resume_noirq(dev);
if (ret)
return ret;
- if (!dev_pm_may_skip_resume(dev) && pdata->dev_desc->resume_from_noirq)
- ret = acpi_lpss_do_resume_early(dev);
+ if (!pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ /*
+ * The driver's ->resume_early callback will be invoked by
+ * acpi_lpss_do_resume_early(), with the assumption that the driver
+ * really wanted to run that code in ->resume_noirq, but it could not
+ * run before acpi_dev_resume() and the driver expected the latter to be
+ * called in the "early" phase.
+ */
+ return acpi_lpss_do_resume_early(dev);
+}
+
+static int acpi_lpss_do_restore_early(struct device *dev)
+{
+ int ret = acpi_lpss_resume(dev);
+
+ return ret ? ret : pm_generic_restore_early(dev);
+}
+
+static int acpi_lpss_restore_early(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (pdata->dev_desc->resume_from_noirq)
+ return 0;
- return ret;
+ return acpi_lpss_do_restore_early(dev);
}
+static int acpi_lpss_restore_noirq(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
+
+ ret = pm_generic_restore_noirq(dev);
+ if (ret)
+ return ret;
+
+ if (!pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ /* This is analogous to what happens in acpi_lpss_resume_noirq(). */
+ return acpi_lpss_do_restore_early(dev);
+}
#endif /* CONFIG_PM_SLEEP */
static int acpi_lpss_runtime_suspend(struct device *dev)
@@ -1134,14 +1180,11 @@ static struct dev_pm_domain acpi_lpss_pm
.resume_noirq = acpi_lpss_resume_noirq,
.resume_early = acpi_lpss_resume_early,
.freeze = acpi_subsys_freeze,
- .freeze_late = acpi_subsys_freeze_late,
- .freeze_noirq = acpi_subsys_freeze_noirq,
- .thaw_noirq = acpi_subsys_thaw_noirq,
.poweroff = acpi_subsys_suspend,
.poweroff_late = acpi_lpss_suspend_late,
.poweroff_noirq = acpi_lpss_suspend_noirq,
- .restore_noirq = acpi_lpss_resume_noirq,
- .restore_early = acpi_lpss_resume_early,
+ .restore_noirq = acpi_lpss_restore_noirq,
+ .restore_early = acpi_lpss_restore_early,
#endif
.runtime_suspend = acpi_lpss_runtime_suspend,
.runtime_resume = acpi_lpss_runtime_resume,
^ permalink raw reply
* [PATCH v2 2/5] PCI: PM: Simplify bus-level hibernation callbacks
From: Rafael J. Wysocki @ 2019-07-01 10:46 UTC (permalink / raw)
To: Linux PM
Cc: Linux PCI, Linux ACPI, LKML, Bjorn Helgaas, Andy Shevchenko,
Mika Westerberg, Hans De Goede, Robert R. Howell
In-Reply-To: <4976412.ihyb9sT5jY@kreacher>
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
After a previous change causing all runtime-suspended PCI devices
to be resumed before creating a snapshot image of memory during
hibernation, it is not necessary to worry about the case in which
them might be left in runtime-suspend any more, so get rid of the
code related to that from bus-level PCI hibernation callbacks.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
-> v2: No changes.
---
drivers/pci/pci-driver.c | 27 ---------------------------
1 file changed, 27 deletions(-)
Index: linux-pm/drivers/pci/pci-driver.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-driver.c
+++ linux-pm/drivers/pci/pci-driver.c
@@ -1034,22 +1034,11 @@ static int pci_pm_freeze(struct device *
return 0;
}
-static int pci_pm_freeze_late(struct device *dev)
-{
- if (dev_pm_smart_suspend_and_suspended(dev))
- return 0;
-
- return pm_generic_freeze_late(dev);
-}
-
static int pci_pm_freeze_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver;
- if (dev_pm_smart_suspend_and_suspended(dev))
- return 0;
-
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend_late(dev, PMSG_FREEZE);
@@ -1079,16 +1068,6 @@ static int pci_pm_thaw_noirq(struct devi
struct device_driver *drv = dev->driver;
int error = 0;
- /*
- * If the device is in runtime suspend, the code below may not work
- * correctly with it, so skip that code and make the PM core skip all of
- * the subsequent "thaw" callbacks for the device.
- */
- if (dev_pm_smart_suspend_and_suspended(dev)) {
- dev_pm_skip_next_resume_phases(dev);
- return 0;
- }
-
if (pcibios_pm_ops.thaw_noirq) {
error = pcibios_pm_ops.thaw_noirq(dev);
if (error)
@@ -1226,10 +1205,6 @@ static int pci_pm_restore_noirq(struct d
struct device_driver *drv = dev->driver;
int error = 0;
- /* This is analogous to the pci_pm_resume_noirq() case. */
- if (dev_pm_smart_suspend_and_suspended(dev))
- pm_runtime_set_active(dev);
-
if (pcibios_pm_ops.restore_noirq) {
error = pcibios_pm_ops.restore_noirq(dev);
if (error)
@@ -1279,7 +1254,6 @@ static int pci_pm_restore(struct device
#else /* !CONFIG_HIBERNATE_CALLBACKS */
#define pci_pm_freeze NULL
-#define pci_pm_freeze_late NULL
#define pci_pm_freeze_noirq NULL
#define pci_pm_thaw NULL
#define pci_pm_thaw_noirq NULL
@@ -1405,7 +1379,6 @@ static const struct dev_pm_ops pci_dev_p
.suspend_late = pci_pm_suspend_late,
.resume = pci_pm_resume,
.freeze = pci_pm_freeze,
- .freeze_late = pci_pm_freeze_late,
.thaw = pci_pm_thaw,
.poweroff = pci_pm_poweroff,
.poweroff_late = pci_pm_poweroff_late,
^ permalink raw reply
* [PATCH v2 4/5] ACPI: PM: Introduce "poweroff" callbacks for ACPI PM domain and LPSS
From: Rafael J. Wysocki @ 2019-07-01 10:54 UTC (permalink / raw)
To: Linux PM
Cc: Linux PCI, Linux ACPI, LKML, Bjorn Helgaas, Andy Shevchenko,
Mika Westerberg, Hans De Goede, Robert R. Howell
In-Reply-To: <4976412.ihyb9sT5jY@kreacher>
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
In general, it is not correct to call pm_generic_suspend(),
pm_generic_suspend_late() and pm_generic_suspend_noirq() during the
hibernation's "poweroff" transition, because device drivers may
provide special callbacks to be invoked then and the wrappers in
question cause system suspend callbacks to be run. Unfortunately,
that happens in the ACPI PM domain and ACPI LPSS.
To address this potential issue, introduce "poweroff" callbacks
for the ACPI PM and LPSS that will use pm_generic_poweroff(),
pm_generic_poweroff_late() and pm_generic_poweroff_noirq() as
appropriate.
Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account)
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
-> v2:
* Make the new "poweroff" callbacks for the LPSS follow the current
acpi_lpss_suspend_late/noirq() behavior.
---
drivers/acpi/acpi_lpss.c | 50 ++++++++++++++++++++++++++++++++++++++--
drivers/acpi/device_pm.c | 58 ++++++++++++++++++++++++++++++++++++++++++++---
include/linux/acpi.h | 2 +
3 files changed, 104 insertions(+), 6 deletions(-)
Index: linux-pm/drivers/acpi/acpi_lpss.c
===================================================================
--- linux-pm.orig/drivers/acpi/acpi_lpss.c
+++ linux-pm/drivers/acpi/acpi_lpss.c
@@ -1061,6 +1061,13 @@ static int acpi_lpss_suspend_noirq(struc
int ret;
if (pdata->dev_desc->resume_from_noirq) {
+ /*
+ * The driver's ->suspend_late callback will be invoked by
+ * acpi_lpss_do_suspend_late(), with the assumption that the
+ * driver really wanted to run that code in ->suspend_noirq, but
+ * it could not run after acpi_dev_suspend() and the driver
+ * expected the latter to be called in the "late" phase.
+ */
ret = acpi_lpss_do_suspend_late(dev);
if (ret)
return ret;
@@ -1147,6 +1154,43 @@ static int acpi_lpss_restore_noirq(struc
/* This is analogous to what happens in acpi_lpss_resume_noirq(). */
return acpi_lpss_do_restore_early(dev);
}
+
+static int acpi_lpss_do_poweroff_late(struct device *dev)
+{
+ int ret = pm_generic_poweroff_late(dev);
+
+ return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
+}
+
+static int acpi_lpss_poweroff_late(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ return 0;
+
+ if (pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ return acpi_lpss_do_poweroff_late(dev);
+}
+
+static int acpi_lpss_poweroff_noirq(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ return 0;
+
+ if (pdata->dev_desc->resume_from_noirq) {
+ /* This is analogous to the acpi_lpss_suspend_noirq() case. */
+ int ret = acpi_lpss_do_poweroff_late(dev);
+ if (ret)
+ return ret;
+ }
+
+ return pm_generic_poweroff_noirq(dev);
+}
#endif /* CONFIG_PM_SLEEP */
static int acpi_lpss_runtime_suspend(struct device *dev)
@@ -1180,9 +1224,9 @@ static struct dev_pm_domain acpi_lpss_pm
.resume_noirq = acpi_lpss_resume_noirq,
.resume_early = acpi_lpss_resume_early,
.freeze = acpi_subsys_freeze,
- .poweroff = acpi_subsys_suspend,
- .poweroff_late = acpi_lpss_suspend_late,
- .poweroff_noirq = acpi_lpss_suspend_noirq,
+ .poweroff = acpi_subsys_poweroff,
+ .poweroff_late = acpi_lpss_poweroff_late,
+ .poweroff_noirq = acpi_lpss_poweroff_noirq,
.restore_noirq = acpi_lpss_restore_noirq,
.restore_early = acpi_lpss_restore_early,
#endif
Index: linux-pm/drivers/acpi/device_pm.c
===================================================================
--- linux-pm.orig/drivers/acpi/device_pm.c
+++ linux-pm/drivers/acpi/device_pm.c
@@ -1176,6 +1176,58 @@ int acpi_subsys_restore_early(struct dev
return ret ? ret : pm_generic_restore_early(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_restore_early);
+
+/**
+ * acpi_subsys_poweroff - Run the device driver's poweroff callback.
+ * @dev: Device to handle.
+ *
+ * Follow PCI and resume devices from runtime suspend before running their
+ * system poweroff callbacks, unless the driver can cope with runtime-suspended
+ * devices during system suspend and there are no ACPI-specific reasons for
+ * resuming them.
+ */
+int acpi_subsys_poweroff(struct device *dev)
+{
+ if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
+ acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
+ pm_runtime_resume(dev);
+
+ return pm_generic_poweroff(dev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_poweroff);
+
+/**
+ * acpi_subsys_poweroff_late - Run the device driver's poweroff callback.
+ * @dev: Device to handle.
+ *
+ * Carry out the generic late poweroff procedure for @dev and use ACPI to put
+ * it into a low-power state during system transition into a sleep state.
+ */
+static int acpi_subsys_poweroff_late(struct device *dev)
+{
+ int ret;
+
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ return 0;
+
+ ret = pm_generic_poweroff_late(dev);
+ if (ret)
+ return ret;
+
+ return acpi_dev_suspend(dev, device_may_wakeup(dev));
+}
+
+/**
+ * acpi_subsys_poweroff_noirq - Run the driver's "noirq" poweroff callback.
+ * @dev: Device to suspend.
+ */
+static int acpi_subsys_poweroff_noirq(struct device *dev)
+{
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ return 0;
+
+ return pm_generic_poweroff_noirq(dev);
+}
#endif /* CONFIG_PM_SLEEP */
static struct dev_pm_domain acpi_general_pm_domain = {
@@ -1191,9 +1243,9 @@ static struct dev_pm_domain acpi_general
.resume_noirq = acpi_subsys_resume_noirq,
.resume_early = acpi_subsys_resume_early,
.freeze = acpi_subsys_freeze,
- .poweroff = acpi_subsys_suspend,
- .poweroff_late = acpi_subsys_suspend_late,
- .poweroff_noirq = acpi_subsys_suspend_noirq,
+ .poweroff = acpi_subsys_poweroff,
+ .poweroff_late = acpi_subsys_poweroff_late,
+ .poweroff_noirq = acpi_subsys_poweroff_noirq,
.restore_early = acpi_subsys_restore_early,
#endif
},
Index: linux-pm/include/linux/acpi.h
===================================================================
--- linux-pm.orig/include/linux/acpi.h
+++ linux-pm/include/linux/acpi.h
@@ -920,6 +920,7 @@ int acpi_subsys_suspend_late(struct devi
int acpi_subsys_suspend_noirq(struct device *dev);
int acpi_subsys_suspend(struct device *dev);
int acpi_subsys_freeze(struct device *dev);
+int acpi_subsys_poweroff(struct device *dev);
#else
static inline int acpi_dev_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
@@ -928,6 +929,7 @@ static inline int acpi_subsys_suspend_la
static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; }
static inline int acpi_subsys_suspend(struct device *dev) { return 0; }
static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
+static inline int acpi_subsys_poweroff(struct device *dev) { return 0; }
#endif
#ifdef CONFIG_ACPI
^ permalink raw reply
* [PATCH v2 1/5] PM: ACPI/PCI: Resume all devices during hibernation
From: Rafael J. Wysocki @ 2019-07-01 10:44 UTC (permalink / raw)
To: Linux PM
Cc: Linux PCI, Linux ACPI, LKML, Bjorn Helgaas, Andy Shevchenko,
Mika Westerberg, Hans De Goede, Robert R. Howell
In-Reply-To: <4976412.ihyb9sT5jY@kreacher>
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Both the PCI bus type and the ACPI PM domain avoid resuming
runtime-suspended devices with DPM_FLAG_SMART_SUSPEND set during
hibernation (before creating the snapshot image of system memory),
but that turns out to be a mistake. It leads to functional issues
and adds complexity that's hard to justify.
For this reason, resume all runtime-suspended PCI devices and all
devices in the ACPI PM domains before creating a snapshot image of
system memory during hibernation.
Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account)
Fixes: c4b65157aeef (PCI / PM: Take SMART_SUSPEND driver flag into account)
Link: https://lore.kernel.org/linux-acpi/917d4399-2e22-67b1-9d54-808561f9083f@uwyo.edu/T/#maf065fe6e4974f2a9d79f332ab99dfaba635f64c
Reported-by: Robert R. Howell <RHowell@uwyo.edu>
Tested-by: Robert R. Howell <RHowell@uwyo.edu>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
-> v2: No changes.
---
drivers/acpi/device_pm.c | 13 +++++++------
drivers/pci/pci-driver.c | 16 ++++++++--------
2 files changed, 15 insertions(+), 14 deletions(-)
Index: linux-pm/drivers/acpi/device_pm.c
===================================================================
--- linux-pm.orig/drivers/acpi/device_pm.c
+++ linux-pm/drivers/acpi/device_pm.c
@@ -1155,13 +1155,14 @@ EXPORT_SYMBOL_GPL(acpi_subsys_resume_ear
int acpi_subsys_freeze(struct device *dev)
{
/*
- * This used to be done in acpi_subsys_prepare() for all devices and
- * some drivers may depend on it, so do it here. Ideally, however,
- * runtime-suspended devices should not be touched during freeze/thaw
- * transitions.
+ * Resume all runtime-suspended devices before creating a snapshot
+ * image of system memory, because the restore kernel generally cannot
+ * be expected to always handle them consistently and they need to be
+ * put into the runtime-active metastate during system resume anyway,
+ * so it is better to ensure that the state saved in the image will be
+ * alwyas consistent with that.
*/
- if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
- pm_runtime_resume(dev);
+ pm_runtime_resume(dev);
return pm_generic_freeze(dev);
}
Index: linux-pm/drivers/pci/pci-driver.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-driver.c
+++ linux-pm/drivers/pci/pci-driver.c
@@ -1012,15 +1012,15 @@ static int pci_pm_freeze(struct device *
}
/*
- * This used to be done in pci_pm_prepare() for all devices and some
- * drivers may depend on it, so do it here. Ideally, runtime-suspended
- * devices should not be touched during freeze/thaw transitions,
- * however.
+ * Resume all runtime-suspended devices before creating a snapshot
+ * image of system memory, because the restore kernel generally cannot
+ * be expected to always handle them consistently and they need to be
+ * put into the runtime-active metastate during system resume anyway,
+ * so it is better to ensure that the state saved in the image will be
+ * alwyas consistent with that.
*/
- if (!dev_pm_smart_suspend_and_suspended(dev)) {
- pm_runtime_resume(dev);
- pci_dev->state_saved = false;
- }
+ pm_runtime_resume(dev);
+ pci_dev->state_saved = false;
if (pm->freeze) {
int error;
^ permalink raw reply
* [PATCH v2 5/5] ACPI: PM: Drop unused function and function header
From: Rafael J. Wysocki @ 2019-07-01 10:55 UTC (permalink / raw)
To: Linux PM
Cc: Linux PCI, Linux ACPI, LKML, Bjorn Helgaas, Andy Shevchenko,
Mika Westerberg, Hans De Goede, Robert R. Howell
In-Reply-To: <4976412.ihyb9sT5jY@kreacher>
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Remove a leftover function header and a static inline stub with no
users from the ACPI header file.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
-> v2: No changes.
---
include/linux/acpi.h | 2 --
1 file changed, 2 deletions(-)
Index: linux-pm/include/linux/acpi.h
===================================================================
--- linux-pm.orig/include/linux/acpi.h
+++ linux-pm/include/linux/acpi.h
@@ -913,7 +913,6 @@ static inline int acpi_dev_pm_attach(str
#endif
#if defined(CONFIG_ACPI) && defined(CONFIG_PM_SLEEP)
-int acpi_dev_suspend_late(struct device *dev);
int acpi_subsys_prepare(struct device *dev);
void acpi_subsys_complete(struct device *dev);
int acpi_subsys_suspend_late(struct device *dev);
@@ -922,7 +921,6 @@ int acpi_subsys_suspend(struct device *d
int acpi_subsys_freeze(struct device *dev);
int acpi_subsys_poweroff(struct device *dev);
#else
-static inline int acpi_dev_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
static inline void acpi_subsys_complete(struct device *dev) {}
static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; }
^ permalink raw reply
* [PATCH v2 0/5] PM: PCI/ACPI: Hibernation handling fixes
From: Rafael J. Wysocki @ 2019-07-01 10:42 UTC (permalink / raw)
To: Linux PM
Cc: Linux PCI, Linux ACPI, LKML, Bjorn Helgaas, Andy Shevchenko,
Mika Westerberg, Hans De Goede, Robert R. Howell
Hi All,
This series of patches addresses a few issues related to the handling of
hibernation in the PCI bus type and the ACPI PM domain and ACPI LPSS driver.
The v2 addresses Hans' concerns regarding the LPSS changes.
First of all, all of the runtime-suspended PCI devices and devices in the ACPI PM and LPSS
PM domains will be resumed during hibernation (first patch). This appears to be the
only way to avoid weird corner cases and the benefit from avoiding to resume those
devices during hibernation is questionable.
That change allows the the hibernation callbacks in all of the involved subsystems to be
simplified (patches 2 and 3).
Moreover, reusing bus-level suspend callbacks for the "poweroff" transition during
hibernation (which is the case for the ACPI PM domain and LPSS) is incorrect, so patch 4
fixes that.
Finally, there are some leftover items in linux/acpi.h that can be dropped (patch 5).
Thanks,
Rafael
^ permalink raw reply
* Re: [PATCH RFC 0/4] DDR/L3 Scaling support on SDM845 SoCs
From: Viresh Kumar @ 2019-07-01 9:29 UTC (permalink / raw)
To: Sibi Sankar
Cc: nm, sboyd, georgi.djakov, agross, david.brown, robh+dt,
mark.rutland, rjw, linux-arm-msm, devicetree, linux-kernel,
linux-pm, saravanak
In-Reply-To: <20190627133424.4980-1-sibis@codeaurora.org>
On 27-06-19, 19:04, Sibi Sankar wrote:
> This RFC series aims to extend cpu based scaling support to L3/DDR on
> SDM845 SoCs. The patch series depends on "Introduce OPP bandwidth bindings"
> series (https://patchwork.kernel.org/cover/10912993/). A part of the
> series will still be applicable if we decide to go ahead with the proposal
> from Saravana as well so I decided to post this out.
>
> v2:
> * Incorporated Viresh's comments from:
> [1]https://lore.kernel.org/lkml/20190410102429.r6j6brm5kspmqxc3@vireshk-i7/
> [2]https://lore.kernel.org/lkml/20190410112516.gnh77jcwawvld6et@vireshk-i7/
Did you get a chance to look at this ?
lore.kernel.org/lkml/20190622003449.33707-1-saravanak@google.com
--
viresh
^ permalink raw reply
* Re: [PATCH] devfreq: tegra20: add COMMON_CLK dependency
From: Chanwoo Choi @ 2019-07-01 1:49 UTC (permalink / raw)
To: Arnd Bergmann, MyungJoo Ham, Kyungmin Park
Cc: Dmitry Osipenko, linux-pm, linux-kernel
In-Reply-To: <20190628103232.2467959-1-arnd@arndb.de>
Hi Arnd,
Thanks for fixup.
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Hi Myungjoo,
This patch have to be merged for linux 5.3 before sending pull-request
because it fixed the build error of merged patch[1] on devfreq.git.
[1] https://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git/commit/?h=for-next&id=028b3793284fa1bb4db73a90608d7cb24664480c
Best Regards,
Chanwoo Choi
On 19. 6. 28. 오후 7:32, Arnd Bergmann wrote:
> Compile-testing the new driver on platforms without CONFIG_COMMON_CLK
> leads to a link error:
>
> drivers/devfreq/tegra20-devfreq.o: In function `tegra_devfreq_target':
> tegra20-devfreq.c:(.text+0x288): undefined reference to `clk_set_min_rate'
>
> Add a dependency on COMMON_CLK to avoid this.
>
> Fixes: 1d39ee8dad6d ("PM / devfreq: Introduce driver for NVIDIA Tegra20")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> drivers/devfreq/Kconfig | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
> index f3b242987fd9..defe1d438710 100644
> --- a/drivers/devfreq/Kconfig
> +++ b/drivers/devfreq/Kconfig
> @@ -107,6 +107,7 @@ config ARM_TEGRA_DEVFREQ
> config ARM_TEGRA20_DEVFREQ
> tristate "NVIDIA Tegra20 DEVFREQ Driver"
> depends on (TEGRA_MC && TEGRA20_EMC) || COMPILE_TEST
> + depends on COMMON_CLK
> select DEVFREQ_GOV_SIMPLE_ONDEMAND
> select PM_OPP
> help
>
^ permalink raw reply
* [PATCH 5/5] drm/bridge: ti-sn65dsi86: support booloader enabled display
From: Rob Clark @ 2019-06-30 15:01 UTC (permalink / raw)
To: dri-devel, linux-arm-msm
Cc: freedreno, aarch64-laptops, linux-clk, linux-pm, Rob Clark,
Andrzej Hajda, Laurent Pinchart, David Airlie, Daniel Vetter,
linux-kernel
In-Reply-To: <20190630150230.7878-1-robdclark@gmail.com>
From: Rob Clark <robdclark@chromium.org>
Request the enable gpio ASIS to avoid disabling bridge during probe, if
already enabled. And if already enabled, defer enabling runpm until
attach to avoid cutting off the power to the bridge.
Once we get to attach, we know panel and drm driver are probed
successfully, so at this point it i s safe to enable runpm and reset the
bridge. If we do it earlier, we kill efifb (in the case that panel or
drm driver do not probe successfully, giving the user no way to see what
is going on.
Signed-off-by: Rob Clark <robdclark@chromium.org>
---
drivers/gpu/drm/bridge/ti-sn65dsi86.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
index 7a046bcdd81b..8bdc33576992 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
@@ -257,6 +257,12 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge)
.node = NULL,
};
+ if (gpiod_get_value(pdata->enable_gpio)) {
+ pm_runtime_enable(pdata->dev);
+ ti_sn_bridge_resume(pdata->dev);
+ ti_sn_bridge_suspend(pdata->dev);
+ }
+
ret = drm_connector_init(bridge->dev, &pdata->connector,
&ti_sn_bridge_connector_funcs,
DRM_MODE_CONNECTOR_eDP);
@@ -813,7 +819,7 @@ static int ti_sn_bridge_probe(struct i2c_client *client,
dev_set_drvdata(&client->dev, pdata);
pdata->enable_gpio = devm_gpiod_get(pdata->dev, "enable",
- GPIOD_OUT_LOW);
+ GPIOD_ASIS);
if (IS_ERR(pdata->enable_gpio)) {
DRM_ERROR("failed to get enable gpio from DT\n");
ret = PTR_ERR(pdata->enable_gpio);
@@ -843,7 +849,9 @@ static int ti_sn_bridge_probe(struct i2c_client *client,
if (ret)
return ret;
- pm_runtime_enable(pdata->dev);
+ if (!gpiod_get_value(pdata->enable_gpio)) {
+ pm_runtime_enable(pdata->dev);
+ }
i2c_set_clientdata(client, pdata);
--
2.20.1
^ permalink raw reply related
* [PATCH 4/5] drm/msm/dsi: get the clocks into OFF state at init
From: Rob Clark @ 2019-06-30 15:01 UTC (permalink / raw)
To: dri-devel, linux-arm-msm
Cc: freedreno, aarch64-laptops, linux-clk, linux-pm, Rob Clark,
Rob Clark, Sean Paul, David Airlie, Daniel Vetter, Jordan Crouse,
Abhinav Kumar, Sibi Sankar, Mamta Shukla, Chandan Uddaraju,
Archit Taneja, Rajesh Yadav, linux-kernel
In-Reply-To: <20190630150230.7878-1-robdclark@gmail.com>
From: Rob Clark <robdclark@chromium.org>
Do an extra enable/disable cycle at init, to get the clks into disabled
state in case bootloader left them enabled.
In case they were already enabled, the clk_prepare_enable() has no real
effect, other than getting the enable_count/prepare_count into the right
state so that we can disable clocks in the correct order. This way we
avoid having stuck clocks when we later want to do a modeset and set the
clock rates.
Signed-off-by: Rob Clark <robdclark@chromium.org>
---
drivers/gpu/drm/msm/dsi/dsi_host.c | 18 +++++++++++++++---
drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c | 1 +
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index 87119d0afb91..d6e81f330db4 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -215,8 +215,6 @@ static const struct msm_dsi_cfg_handler *dsi_get_config(
goto put_gdsc;
}
- pm_runtime_get_sync(dev);
-
ret = regulator_enable(gdsc_reg);
if (ret) {
pr_err("%s: unable to enable gdsc\n", __func__);
@@ -243,7 +241,6 @@ static const struct msm_dsi_cfg_handler *dsi_get_config(
clk_disable_unprepare(ahb_clk);
disable_gdsc:
regulator_disable(gdsc_reg);
- pm_runtime_put_sync(dev);
put_gdsc:
regulator_put(gdsc_reg);
exit:
@@ -390,6 +387,8 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host)
__func__, cfg->bus_clk_names[i], ret);
goto exit;
}
+
+ clk_prepare_enable(msm_host->bus_clks[i]);
}
/* get link and source clocks */
@@ -436,6 +435,16 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host)
if (cfg_hnd->ops->clk_init_ver)
ret = cfg_hnd->ops->clk_init_ver(msm_host);
+
+ /*
+ * Do an extra enable/disable sequence initially to ensure the
+ * clocks are actually off, if left enabled by bootloader..
+ */
+ ret = cfg_hnd->ops->link_clk_enable(msm_host);
+ if (!ret)
+ cfg_hnd->ops->link_clk_disable(msm_host);
+ ret = 0;
+
exit:
return ret;
}
@@ -1855,6 +1864,7 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi)
}
pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
msm_host->cfg_hnd = dsi_get_config(msm_host);
if (!msm_host->cfg_hnd) {
@@ -1885,6 +1895,8 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi)
goto fail;
}
+ pm_runtime_put_sync(&pdev->dev);
+
msm_host->rx_buf = devm_kzalloc(&pdev->dev, SZ_4K, GFP_KERNEL);
if (!msm_host->rx_buf) {
ret = -ENOMEM;
diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c
index aabab6311043..d0172d8db882 100644
--- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c
+++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c
@@ -354,6 +354,7 @@ static int dsi_pll_10nm_lock_status(struct dsi_pll_10nm *pll)
if (rc)
pr_err("DSI PLL(%d) lock failed, status=0x%08x\n",
pll->id, status);
+rc = 0; // HACK, this will fail if PLL already running..
return rc;
}
--
2.20.1
^ permalink raw reply related
* [PATCH 3/5] drm/msm/dsi: split clk rate setting and enable
From: Rob Clark @ 2019-06-30 15:01 UTC (permalink / raw)
To: dri-devel, linux-arm-msm
Cc: freedreno, aarch64-laptops, linux-clk, linux-pm, Rob Clark,
Rob Clark, Sean Paul, David Airlie, Daniel Vetter,
Chandan Uddaraju, Archit Taneja, Sibi Sankar, Laurent Pinchart,
Thomas Gleixner, Greg Kroah-Hartman, Allison Randal,
Jordan Crouse, Abhinav Kumar, linux-kernel
In-Reply-To: <20190630150230.7878-1-robdclark@gmail.com>
From: Rob Clark <robdclark@chromium.org>
Prep work for the following patch.
Signed-off-by: Rob Clark <robdclark@chromium.org>
---
drivers/gpu/drm/msm/dsi/dsi.h | 2 ++
drivers/gpu/drm/msm/dsi/dsi_cfg.c | 3 +++
drivers/gpu/drm/msm/dsi/dsi_cfg.h | 1 +
drivers/gpu/drm/msm/dsi/dsi_host.c | 38 ++++++++++++++++++++++--------
4 files changed, 34 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h
index 4dd2a9a79257..c4e3c4cf96c5 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.h
+++ b/drivers/gpu/drm/msm/dsi/dsi.h
@@ -178,6 +178,8 @@ int msm_dsi_host_modeset_init(struct mipi_dsi_host *host,
int msm_dsi_host_init(struct msm_dsi *msm_dsi);
int msm_dsi_runtime_suspend(struct device *dev);
int msm_dsi_runtime_resume(struct device *dev);
+int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host);
+int dsi_link_clk_set_rate_v2(struct msm_dsi_host *msm_host);
int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host);
int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host);
void dsi_link_clk_disable_6g(struct msm_dsi_host *msm_host);
diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c
index 9ddf16380289..35e272c27780 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_cfg.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c
@@ -129,6 +129,7 @@ static const struct msm_dsi_config sdm845_dsi_cfg = {
};
const static struct msm_dsi_host_cfg_ops msm_dsi_v2_host_ops = {
+ .link_clk_set_rate = dsi_link_clk_set_rate_v2,
.link_clk_enable = dsi_link_clk_enable_v2,
.link_clk_disable = dsi_link_clk_disable_v2,
.clk_init_ver = dsi_clk_init_v2,
@@ -140,6 +141,7 @@ const static struct msm_dsi_host_cfg_ops msm_dsi_v2_host_ops = {
};
const static struct msm_dsi_host_cfg_ops msm_dsi_6g_host_ops = {
+ .link_clk_set_rate = dsi_link_clk_set_rate_6g,
.link_clk_enable = dsi_link_clk_enable_6g,
.link_clk_disable = dsi_link_clk_disable_6g,
.clk_init_ver = NULL,
@@ -151,6 +153,7 @@ const static struct msm_dsi_host_cfg_ops msm_dsi_6g_host_ops = {
};
const static struct msm_dsi_host_cfg_ops msm_dsi_6g_v2_host_ops = {
+ .link_clk_set_rate = dsi_link_clk_set_rate_6g,
.link_clk_enable = dsi_link_clk_enable_6g,
.link_clk_disable = dsi_link_clk_disable_6g,
.clk_init_ver = dsi_clk_init_6g_v2,
diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.h b/drivers/gpu/drm/msm/dsi/dsi_cfg.h
index a6a3d2bad263..7c1bc174537d 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_cfg.h
+++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.h
@@ -33,6 +33,7 @@ struct msm_dsi_config {
};
struct msm_dsi_host_cfg_ops {
+ int (*link_clk_set_rate)(struct msm_dsi_host *msm_host);
int (*link_clk_enable)(struct msm_dsi_host *msm_host);
void (*link_clk_disable)(struct msm_dsi_host *msm_host);
int (*clk_init_ver)(struct msm_dsi_host *msm_host);
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index d03212ef4853..87119d0afb91 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -503,7 +503,7 @@ int msm_dsi_runtime_resume(struct device *dev)
return dsi_bus_clk_enable(msm_host);
}
-int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host)
+int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host)
{
int ret;
@@ -513,13 +513,13 @@ int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host)
ret = clk_set_rate(msm_host->byte_clk, msm_host->byte_clk_rate);
if (ret) {
pr_err("%s: Failed to set rate byte clk, %d\n", __func__, ret);
- goto error;
+ return ret;
}
ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate);
if (ret) {
pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret);
- goto error;
+ return ret;
}
if (msm_host->byte_intf_clk) {
@@ -528,10 +528,18 @@ int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host)
if (ret) {
pr_err("%s: Failed to set rate byte intf clk, %d\n",
__func__, ret);
- goto error;
+ return ret;
}
}
+ return 0;
+}
+
+
+int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host)
+{
+ int ret;
+
ret = clk_prepare_enable(msm_host->esc_clk);
if (ret) {
pr_err("%s: Failed to enable dsi esc clk\n", __func__);
@@ -571,7 +579,7 @@ int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host)
return ret;
}
-int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host)
+int dsi_link_clk_set_rate_v2(struct msm_dsi_host *msm_host)
{
int ret;
@@ -582,27 +590,34 @@ int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host)
ret = clk_set_rate(msm_host->byte_clk, msm_host->byte_clk_rate);
if (ret) {
pr_err("%s: Failed to set rate byte clk, %d\n", __func__, ret);
- goto error;
+ return ret;
}
ret = clk_set_rate(msm_host->esc_clk, msm_host->esc_clk_rate);
if (ret) {
pr_err("%s: Failed to set rate esc clk, %d\n", __func__, ret);
- goto error;
+ return ret;
}
ret = clk_set_rate(msm_host->src_clk, msm_host->src_clk_rate);
if (ret) {
pr_err("%s: Failed to set rate src clk, %d\n", __func__, ret);
- goto error;
+ return ret;
}
ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate);
if (ret) {
pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret);
- goto error;
+ return ret;
}
+ return 0;
+}
+
+int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host)
+{
+ int ret;
+
ret = clk_prepare_enable(msm_host->byte_clk);
if (ret) {
pr_err("%s: Failed to enable dsi byte clk\n", __func__);
@@ -1997,6 +2012,7 @@ int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host,
* mdp clock need to be enabled to receive dsi interrupt
*/
pm_runtime_get_sync(&msm_host->pdev->dev);
+ cfg_hnd->ops->link_clk_set_rate(msm_host);
cfg_hnd->ops->link_clk_enable(msm_host);
/* TODO: vote for bus bandwidth */
@@ -2345,7 +2361,9 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host,
}
pm_runtime_get_sync(&msm_host->pdev->dev);
- ret = cfg_hnd->ops->link_clk_enable(msm_host);
+ ret = cfg_hnd->ops->link_clk_set_rate(msm_host);
+ if (!ret)
+ ret = cfg_hnd->ops->link_clk_enable(msm_host);
if (ret) {
pr_err("%s: failed to enable link clocks. ret=%d\n",
__func__, ret);
--
2.20.1
^ permalink raw reply related
* [PATCH 2/5] genpd/gdsc: inherit display powerdomain from bootloader
From: Rob Clark @ 2019-06-30 15:01 UTC (permalink / raw)
To: dri-devel, linux-arm-msm
Cc: freedreno, aarch64-laptops, linux-clk, linux-pm, Rob Clark,
Rafael J. Wysocki, Kevin Hilman, Ulf Hansson, Len Brown,
Pavel Machek, Greg Kroah-Hartman, Andy Gross, Michael Turquette,
Stephen Boyd, linux-kernel
In-Reply-To: <20190630150230.7878-1-robdclark@gmail.com>
From: Rob Clark <robdclark@chromium.org>
Mark power domains that may be enabled by bootloader, and which should
not be disabled until a driver takes them over.
This keeps efifb alive until the real driver can be probed. In a distro
kernel, the driver will most likely built as a module, and not probed
until we get to userspace (after late_initcall)
Signed-off-by: Rob Clark <robdclark@chromium.org>
---
drivers/base/power/domain.c | 10 ++++++++++
drivers/clk/qcom/dispcc-sdm845.c | 2 +-
drivers/clk/qcom/gdsc.c | 5 +++++
drivers/clk/qcom/gdsc.h | 1 +
include/linux/pm_domain.h | 4 ++++
5 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 33c30c1e6a30..513a8655fbd7 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -537,6 +537,16 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
not_suspended++;
}
+ /*
+ * If the power domain is enabled by the bootloader (for example
+ * display enabled by bootloader), but no devices attached yet
+ * (perhaps because driver built as kernel module), then do not
+ * suspend.
+ */
+ if ((genpd->flags & GENPD_FLAG_INHERIT_BL) &&
+ list_empty(&genpd->dev_list))
+ not_suspended++;
+
if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on))
return -EBUSY;
diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c
index 40d7e0ab4340..1d9365ac2315 100644
--- a/drivers/clk/qcom/dispcc-sdm845.c
+++ b/drivers/clk/qcom/dispcc-sdm845.c
@@ -575,7 +575,7 @@ static struct gdsc mdss_gdsc = {
.name = "mdss_gdsc",
},
.pwrsts = PWRSTS_OFF_ON,
- .flags = HW_CTRL | POLL_CFG_GDSCR,
+ .flags = HW_CTRL | POLL_CFG_GDSCR | INHERIT_BL,
};
static struct clk_regmap *disp_cc_sdm845_clocks[] = {
diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
index a250f59708d8..4639fbeb9a7f 100644
--- a/drivers/clk/qcom/gdsc.c
+++ b/drivers/clk/qcom/gdsc.c
@@ -331,6 +331,11 @@ static int gdsc_init(struct gdsc *sc)
if ((sc->flags & VOTABLE) && on)
gdsc_enable(&sc->pd);
+ if ((sc->flags & INHERIT_BL) && on) {
+ pr_debug("gdsc: %s is enabled from bootloader!\n", sc->pd.name);
+ sc->pd.flags |= GENPD_FLAG_INHERIT_BL;
+ }
+
/* If ALWAYS_ON GDSCs are not ON, turn them ON */
if (sc->flags & ALWAYS_ON) {
if (!on)
diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h
index 64cdc8cf0d4d..c6fe56247399 100644
--- a/drivers/clk/qcom/gdsc.h
+++ b/drivers/clk/qcom/gdsc.h
@@ -49,6 +49,7 @@ struct gdsc {
#define AON_RESET BIT(4)
#define POLL_CFG_GDSCR BIT(5)
#define ALWAYS_ON BIT(6)
+#define INHERIT_BL BIT(7)
struct reset_controller_dev *rcdev;
unsigned int *resets;
unsigned int reset_count;
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 91d9bf497071..5e421afcf6f3 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -55,6 +55,9 @@
*
* GENPD_FLAG_RPM_ALWAYS_ON: Instructs genpd to always keep the PM domain
* powered on except for system suspend.
+ *
+ * GENPD_FLAG_INHERIT_BL: The bootloader has already enabled this power
+ * domain.
*/
#define GENPD_FLAG_PM_CLK (1U << 0)
#define GENPD_FLAG_IRQ_SAFE (1U << 1)
@@ -62,6 +65,7 @@
#define GENPD_FLAG_ACTIVE_WAKEUP (1U << 3)
#define GENPD_FLAG_CPU_DOMAIN (1U << 4)
#define GENPD_FLAG_RPM_ALWAYS_ON (1U << 5)
+#define GENPD_FLAG_INHERIT_BL (1U << 6)
enum gpd_status {
GPD_STATE_ACTIVE = 0, /* PM domain is active */
--
2.20.1
^ permalink raw reply related
* [PATCH 1/5] clk: inherit clocks enabled by bootloader
From: Rob Clark @ 2019-06-30 15:01 UTC (permalink / raw)
To: dri-devel, linux-arm-msm
Cc: freedreno, aarch64-laptops, linux-clk, linux-pm, Rob Clark,
Michael Turquette, Stephen Boyd, Andy Gross, linux-kernel
In-Reply-To: <20190630150230.7878-1-robdclark@gmail.com>
From: Rob Clark <robdclark@chromium.org>
The goal here is to support inheriting a display setup by bootloader,
although there may also be some non-display related use-cases.
Rough idea is to add a flag for clks and power domains that might
already be enabled when kernel starts, and which should not be
disabled at late_initcall if the kernel thinks they are "unused".
If bootloader is enabling display, and kernel is using efifb before
real display driver is loaded (potentially from kernel module after
userspace starts, in a typical distro kernel), we don't want to kill
the clocks and power domains that are used by the display before
userspace starts.
Signed-off-by: Rob Clark <robdclark@chromium.org>
---
drivers/clk/clk.c | 48 ++++++++++++++++++++++++++++++++
drivers/clk/qcom/common.c | 25 +++++++++++++++++
drivers/clk/qcom/dispcc-sdm845.c | 22 ++++++++-------
drivers/clk/qcom/gcc-sdm845.c | 3 +-
include/linux/clk-provider.h | 10 +++++++
5 files changed, 97 insertions(+), 11 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index aa51756fd4d6..14460e87f508 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -66,6 +66,7 @@ struct clk_core {
unsigned long flags;
bool orphan;
bool rpm_enabled;
+ bool inherit_enabled; /* clock was enabled by bootloader */
unsigned int enable_count;
unsigned int prepare_count;
unsigned int protect_count;
@@ -1166,6 +1167,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
hlist_for_each_entry(child, &core->children, child_node)
clk_unprepare_unused_subtree(child);
+ if (core->inherit_enabled)
+ return;
+
if (core->prepare_count)
return;
@@ -1197,6 +1201,9 @@ static void clk_disable_unused_subtree(struct clk_core *core)
hlist_for_each_entry(child, &core->children, child_node)
clk_disable_unused_subtree(child);
+ if (core->inherit_enabled)
+ return;
+
if (core->flags & CLK_OPS_PARENT_ENABLE)
clk_core_prepare_enable(core->parent);
@@ -1270,6 +1277,45 @@ static int clk_disable_unused(void)
}
late_initcall_sync(clk_disable_unused);
+/* Ignore CLK_INHERIT_BOOTLOADER clocks enabled by bootloader. This
+ * gives a debug knob to disable inheriting clks from bootloader, so
+ * that drivers that used to work, when loaded as a module, thanks
+ * to disabling "unused" clocks at late_initcall(), can continue to
+ * work.
+ *
+ * The proper solution is to fix the drivers.
+ */
+static bool clk_ignore_inherited;
+static int __init clk_ignore_inherited_setup(char *__unused)
+{
+ clk_ignore_inherited = true;
+ return 1;
+}
+__setup("clk_ignore_inherited", clk_ignore_inherited_setup);
+
+/* clock and it's parents are already prepared/enabled from bootloader,
+ * so simply record the fact.
+ */
+static void __clk_inherit_enabled(struct clk_core *core)
+{
+ unsigned long parent_rate = 0;
+ core->inherit_enabled = true;
+ if (core->parent) {
+ __clk_inherit_enabled(core->parent);
+ parent_rate = core->parent->rate;
+ }
+ if (core->ops->recalc_rate)
+ core->rate = core->ops->recalc_rate(core->hw, parent_rate);
+}
+
+void clk_inherit_enabled(struct clk *clk)
+{
+ if (clk_ignore_inherited)
+ return;
+ __clk_inherit_enabled(clk->core);
+}
+EXPORT_SYMBOL_GPL(clk_inherit_enabled);
+
static int clk_core_determine_round_nolock(struct clk_core *core,
struct clk_rate_request *req)
{
@@ -3302,6 +3348,8 @@ static int __clk_core_init(struct clk_core *core)
* are enabled during init but might not have a parent yet.
*/
if (parent) {
+ if (orphan->inherit_enabled)
+ __clk_inherit_enabled(parent);
/* update the clk tree topology */
__clk_set_parent_before(orphan, parent);
__clk_set_parent_after(orphan, parent, NULL);
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index a6b2f86112d8..0d542eeef9aa 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -290,6 +290,31 @@ int qcom_cc_really_probe(struct platform_device *pdev,
if (ret)
return ret;
+ /*
+ * Check which of clocks that we inherit state from bootloader
+ * are enabled, and fixup enable/prepare state (as well as that
+ * of it's parents).
+ */
+ for (i = 0; i < num_clks; i++) {
+ struct clk_hw *hw;
+
+ if (!rclks[i])
+ continue;
+
+ hw = &rclks[i]->hw;
+
+ if (!(hw->init->flags & CLK_INHERIT_BOOTLOADER))
+ continue;
+
+ if (!clk_is_enabled_regmap(hw))
+ continue;
+
+ dev_dbg(dev, "%s is enabled from bootloader!\n",
+ hw->init->name);
+
+ clk_inherit_enabled(hw->clk);
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c
index 0cc4909b5dbe..40d7e0ab4340 100644
--- a/drivers/clk/qcom/dispcc-sdm845.c
+++ b/drivers/clk/qcom/dispcc-sdm845.c
@@ -263,6 +263,7 @@ static struct clk_branch disp_cc_mdss_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "disp_cc_mdss_ahb_clk",
+ .flags = CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -276,6 +277,7 @@ static struct clk_branch disp_cc_mdss_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "disp_cc_mdss_axi_clk",
+ .flags = CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -294,7 +296,7 @@ static struct clk_branch disp_cc_mdss_byte0_clk = {
"disp_cc_mdss_byte0_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -330,7 +332,7 @@ static struct clk_branch disp_cc_mdss_byte0_intf_clk = {
"disp_cc_mdss_byte0_div_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -349,7 +351,7 @@ static struct clk_branch disp_cc_mdss_byte1_clk = {
"disp_cc_mdss_byte1_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -385,7 +387,7 @@ static struct clk_branch disp_cc_mdss_byte1_intf_clk = {
"disp_cc_mdss_byte1_div_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -403,7 +405,7 @@ static struct clk_branch disp_cc_mdss_esc0_clk = {
"disp_cc_mdss_esc0_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -421,7 +423,7 @@ static struct clk_branch disp_cc_mdss_esc1_clk = {
"disp_cc_mdss_esc1_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -439,7 +441,7 @@ static struct clk_branch disp_cc_mdss_mdp_clk = {
"disp_cc_mdss_mdp_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -475,7 +477,7 @@ static struct clk_branch disp_cc_mdss_pclk0_clk = {
"disp_cc_mdss_pclk0_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -494,7 +496,7 @@ static struct clk_branch disp_cc_mdss_pclk1_clk = {
"disp_cc_mdss_pclk1_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -561,7 +563,7 @@ static struct clk_branch disp_cc_mdss_vsync_clk = {
"disp_cc_mdss_vsync_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 7131dcf9b060..fe2498be7bc7 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -1314,7 +1314,7 @@ static struct clk_branch gcc_disp_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_disp_ahb_clk",
- .flags = CLK_IS_CRITICAL,
+ .flags = CLK_IS_CRITICAL | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -1328,6 +1328,7 @@ static struct clk_branch gcc_disp_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_disp_axi_clk",
+ .flags = CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index bb6118f79784..41b951c8b92b 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -34,6 +34,7 @@
#define CLK_OPS_PARENT_ENABLE BIT(12)
/* duty cycle call may be forwarded to the parent clock */
#define CLK_DUTY_CYCLE_PARENT BIT(13)
+#define CLK_INHERIT_BOOTLOADER BIT(14) /* clk may be enabled from bootloader */
struct clk;
struct clk_hw;
@@ -349,6 +350,15 @@ void clk_hw_unregister_fixed_rate(struct clk_hw *hw);
void of_fixed_clk_setup(struct device_node *np);
+/**
+ * clk_inherit_enabled - update the enable/prepare count of a clock and it's
+ * parents for clock enabled by bootloader.
+ *
+ * Intended to be used by clock drivers to inform the clk core of a clock
+ * that is already running.
+ */
+void clk_inherit_enabled(struct clk *clk);
+
/**
* struct clk_gate - gating clock
*
--
2.20.1
^ permalink raw reply related
* [PATCH 0/5] drm+clk+genpd: support for bootloader enabled display
From: Rob Clark @ 2019-06-30 15:01 UTC (permalink / raw)
To: dri-devel, linux-arm-msm
Cc: freedreno, aarch64-laptops, linux-clk, linux-pm, Rob Clark
From: Rob Clark <robdclark@chromium.org>
The aarch64 laptops which ship with windows, have the display by the
bootloader, and efifb (yah!). But unlike x86 laptops, device power
management isn't handled via ACPI[1]. Currently the CCF and genpd
frameworks will turn off power domains and clocks that they think are
unused. This is rather unfortunate, as it kills efifb scanout before
getting to userspace and getting to the point where we can try to
probe the real display driver.
Also it has a few side-effects in that we can't set rate on running
clocks (in many cases).
The first two patches let us flag clocks and power domains which
might have been enabled by the bootloader, so we know not to disable
them in late_initcall.
The next two update drm/msm to cleanly shut down clocks which might
already be running. *Eventually* we'll want to detect that scanout
is already running, and readback the hw state, to avoid briefly
disabling the screen while the driver loads. But that is a big pile
of (mostly) drm/msm work. (Windows also seems to have this problem,
it appears to do a modeset during boot.. so I guess the first step
is to at least not suck more than windows ;-))
The last patch updates the bridge driver to handle the case where
display is already active. (AFAICT it is the same bridge chip used
so far on all the aarch64 laptops.) Because the bridge driver can
be probed before the drm driver, and in fact you might end up with
a bridge driver but no drm driver, care must be taken to not disable
the bridge until the drm driver is ready to go, so:
* Request enable gpio ASIS to avoid pulling down the enable
gpio
* Defer enabling runpm in the case that the bridge is already
running until bridge->attach(). This is a point where we
know the drm driver is ready to do a modeset.
(There are a couple related cleanups in drm/msm to avoid touching
the hw until we are past the point where we might -EPROBE_DEFER[2]
which I sent seperately as they are probably interesting to fewer
people.)
This has been tested on a lenovo yoga c630. I've a wip/c630 branch[3]
with this and various other work-in-progress stuff for this laptop.
Next step, figuring out how to pick the proper panel driver, from
the two or three possibilites that ship on this laptop ;-)
[1] On windows, they use a "Platform Extension Plugin" (PEP) driver
[2] https://patchwork.freedesktop.org/series/62999/
[3] https://github.com/freedreno/kernel-msm/commits/wip/c630
Rob Clark (5):
clk: inherit clocks enabled by bootloader
genpd/gdsc: inherit display powerdomain from bootloader
drm/msm/dsi: split clk rate setting and enable
drm/msm/dsi: get the clocks into OFF state at init
drm/bridge: ti-sn65dsi86: support booloader enabled display
drivers/base/power/domain.c | 10 ++++
drivers/clk/clk.c | 48 +++++++++++++++++++
drivers/clk/qcom/common.c | 25 ++++++++++
drivers/clk/qcom/dispcc-sdm845.c | 24 +++++-----
drivers/clk/qcom/gcc-sdm845.c | 3 +-
drivers/clk/qcom/gdsc.c | 5 ++
drivers/clk/qcom/gdsc.h | 1 +
drivers/gpu/drm/bridge/ti-sn65dsi86.c | 12 ++++-
drivers/gpu/drm/msm/dsi/dsi.h | 2 +
drivers/gpu/drm/msm/dsi/dsi_cfg.c | 3 ++
drivers/gpu/drm/msm/dsi/dsi_cfg.h | 1 +
drivers/gpu/drm/msm/dsi/dsi_host.c | 56 +++++++++++++++++-----
drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c | 1 +
include/linux/clk-provider.h | 10 ++++
include/linux/pm_domain.h | 4 ++
15 files changed, 178 insertions(+), 27 deletions(-)
--
2.20.1
^ permalink raw reply
* Re: [PATCH 4/6] ACPI: LPSS: Fix ->suspend_late callbacks handling
From: Rafael J. Wysocki @ 2019-06-30 9:48 UTC (permalink / raw)
To: Hans de Goede
Cc: Rafael J. Wysocki, Linux PM, Linux PCI, Linux ACPI, LKML,
Bjorn Helgaas, Andy Shevchenko, Mika Westerberg, Robert R. Howell
In-Reply-To: <CAJZ5v0iUOv0MYOHP7938V=6qsURBYO6B4LHqfdm_DCHH8GvfLA@mail.gmail.com>
On Sun, Jun 30, 2019 at 12:02 AM Rafael J. Wysocki <rafael@kernel.org> wrote:
>
> On Sat, Jun 29, 2019 at 1:34 PM Hans de Goede <hdegoede@redhat.com> wrote:
> >
> > Hi Rafael,
> >
> > On 29-06-19 11:50, Rafael J. Wysocki wrote:
> > > From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> > >
> > > If the resume_from_noirq flag is set in dev_desc, the ->suspend_late
> > > callback provided by the device driver will be invoked at the "noirq"
> > > stage of system suspend, via acpi_lpss_do_suspend_late(), which is
> > > incorrect.
> > >
> > > To fix that, drop acpi_lpss_do_suspend_late() and rearrange
> > > acpi_lpss_suspend_late() to call pm_generic_suspend_late()
> > > directly, before calling acpi_lpss_suspend(), in analogy with
> > > acpi_subsys_suspend_late().
> >
> > Ah now I see the logic in your previous test-patch.
> >
> > I'm afraid that this is going to break things though, the calling
> > of the device-driver's suspend-late method at noirq time is
> > *intentional* !
>
> But it is a bug too.
Well, not strictly a bug, but a departure from a clearly established convention.
What happens is that the driver provides ->suspend_late and
->resume_early, because the upper layer normally would remove power
and power up the device at those stages, respectively. However, due
to dependencies between devices, the upper layer kind of works around
its own limitation.
That is not straightforward at all.
I will retain this setup in the current patch series, but going
forward it would be good to clean it up. I wonder if using
non-PM-runtime device links to represent the dependencies would work
in this case.
^ permalink raw reply
* Re: cpupower: update German translation
From: Dominik Brodowski @ 2019-06-30 7:12 UTC (permalink / raw)
To: shuah; +Cc: Benjamin Weis, trenn, linux-pm
In-Reply-To: <b5f06bf0-e356-93c6-0547-f8a3439b9e4f@kernel.org>
Hi Shuah,
On Wed, Jun 26, 2019 at 12:11:01PM -0600, shuah wrote:
> Thanks for the patch. This patch appears to be corrupted.
Hmmm... it applies cleanly here.
Best,
Dominik
^ permalink raw reply
* Re: [PATCH 4/6] ACPI: LPSS: Fix ->suspend_late callbacks handling
From: Rafael J. Wysocki @ 2019-06-29 22:02 UTC (permalink / raw)
To: Hans de Goede
Cc: Rafael J. Wysocki, Linux PM, Linux PCI, Linux ACPI, LKML,
Bjorn Helgaas, Andy Shevchenko, Mika Westerberg, Robert R. Howell
In-Reply-To: <c41b5efe-049c-fc72-8697-b2ec9125a55e@redhat.com>
On Sat, Jun 29, 2019 at 1:34 PM Hans de Goede <hdegoede@redhat.com> wrote:
>
> Hi Rafael,
>
> On 29-06-19 11:50, Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> >
> > If the resume_from_noirq flag is set in dev_desc, the ->suspend_late
> > callback provided by the device driver will be invoked at the "noirq"
> > stage of system suspend, via acpi_lpss_do_suspend_late(), which is
> > incorrect.
> >
> > To fix that, drop acpi_lpss_do_suspend_late() and rearrange
> > acpi_lpss_suspend_late() to call pm_generic_suspend_late()
> > directly, before calling acpi_lpss_suspend(), in analogy with
> > acpi_subsys_suspend_late().
>
> Ah now I see the logic in your previous test-patch.
>
> I'm afraid that this is going to break things though, the calling
> of the device-driver's suspend-late method at noirq time is
> *intentional* !
But it is a bug too.
> The resume_from_noirq flag is only set for i2c controllers which
> use: drivers/i2c/busses/i2c-designware-platdrv.c as driver.
>
> This driver's suspend late method looks like this:
>
> static int dw_i2c_plat_suspend(struct device *dev)
> {
> struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
>
> i_dev->suspended = true;
>
> if (i_dev->shared_with_punit)
> return 0;
>
> i_dev->disable(i_dev);
> i2c_dw_prepare_clk(i_dev, false);
>
> return 0;
> }
>
> The i_dev->disable(i_dev) and i2c_dw_prepare_clk(i_dev, false) calls here
> will make the i2c controller non functional. But (some of) these i2c
> controllers are used by code in the _PS0 / _PS3 methods of some PCI
> devices and the PCI core calls _PS0 / _PS3 at *noirq* time, so as explained
> in the commit message which introduced acpi_lpss_do_suspend_late():
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=48402cee6889fb3fce58e95fea1471626286dc63
>
> We must not only make sure that the suspending of the i2c controller is
> ordered so that it happens after these PCI devices are suspended, we must
> also make sure that the i2c controller stays functional until the
> i2c-controller is put in the suspend-noirq state.
>
> If you really want to go this route, we must duplicate the resume_from_noirq
> flag inside drivers/i2c/busses/i2c-designware-platdrv.c, setting it
> only for acpi_lpss enumerated devices (the driver handles a whole lot more
> devices) ans then make the driver's suspend_late method a no-op and instead
> to the suspend from its suspend_noirq callback.
>
> Since pm_generic_suspend_late() is just a wrapper to call dev->driver->pm->suspend_late
> duplicating the resume_from_noirq flag inside i2c-designware-platdrv.c seems
> unproductive.
>
> Note that we have the same thing going on in acpi_lpss.c with resume_early vs
> resume_noirq, we call the resume_early callback from acpi_lpss_resume_noirq
> if the resume_from_noirq flag is set.
>
> TL;DR: the behavior you are trying to fix here is intentional and
> IMHO this patch should be dropped.
I can drop the patch, but the current code is simply incorrect.
If the driver provided a ->suspend_late callback, it wanted that
callback to be invoked during the "late" stage of suspend. Calling it
later simply papers over a driver bug. If invoking that callback
during the "late" stage doesn't work, the driver should have provided
a "noirq" callback instead.
> I guess we could / should do a patch adding a comment that the calling
> the drivers' suspend_late / resume_early callback at noirq time is intentional
> to avoid this confusing people in the future.
No. We need to fix drivers doing wrong things.
Thanks!
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox