summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/pm_qos.h17
-rw-r--r--kernel/power/qos.c63
2 files changed, 78 insertions, 2 deletions
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 30e9ad72e797..413ada3c7c97 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -33,6 +33,11 @@ struct pm_qos_request {
struct delayed_work work; /* for pm_qos_update_request_timeout */
};
+struct pm_qos_flags_request {
+ struct list_head node;
+ s32 flags; /* Do not change to 64 bit */
+};
+
struct dev_pm_qos_request {
struct plist_node node;
struct device *dev;
@@ -45,8 +50,8 @@ enum pm_qos_type {
};
/*
- * Note: The lockless read path depends on the CPU accessing
- * target_value atomically. Atomic access is only guaranteed on all CPU
+ * Note: The lockless read path depends on the CPU accessing target_value
+ * or effective_flags atomically. Atomic access is only guaranteed on all CPU
* types linux supports for 32 bit quantites
*/
struct pm_qos_constraints {
@@ -57,6 +62,11 @@ struct pm_qos_constraints {
struct blocking_notifier_head *notifiers;
};
+struct pm_qos_flags {
+ struct list_head list;
+ s32 effective_flags; /* Do not change to 64 bit */
+};
+
struct dev_pm_qos {
struct pm_qos_constraints latency;
};
@@ -75,6 +85,9 @@ static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req)
int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
enum pm_qos_req_action action, int value);
+bool pm_qos_update_flags(struct pm_qos_flags *pqf,
+ struct pm_qos_flags_request *req,
+ enum pm_qos_req_action action, s32 val);
void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class,
s32 value);
void pm_qos_update_request(struct pm_qos_request *req,
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 846bd42c7ed1..2ab2819aee65 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -213,6 +213,69 @@ int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
}
/**
+ * pm_qos_flags_remove_req - Remove device PM QoS flags request.
+ * @pqf: Device PM QoS flags set to remove the request from.
+ * @req: Request to remove from the set.
+ */
+static void pm_qos_flags_remove_req(struct pm_qos_flags *pqf,
+ struct pm_qos_flags_request *req)
+{
+ s32 val = 0;
+
+ list_del(&req->node);
+ list_for_each_entry(req, &pqf->list, node)
+ val |= req->flags;
+
+ pqf->effective_flags = val;
+}
+
+/**
+ * pm_qos_update_flags - Update a set of PM QoS flags.
+ * @pqf: Set of flags to update.
+ * @req: Request to add to the set, to modify, or to remove from the set.
+ * @action: Action to take on the set.
+ * @val: Value of the request to add or modify.
+ *
+ * Update the given set of PM QoS flags and call notifiers if the aggregate
+ * value has changed. Returns 1 if the aggregate constraint value has changed,
+ * 0 otherwise.
+ */
+bool pm_qos_update_flags(struct pm_qos_flags *pqf,
+ struct pm_qos_flags_request *req,
+ enum pm_qos_req_action action, s32 val)
+{
+ unsigned long irqflags;
+ s32 prev_value, curr_value;
+
+ spin_lock_irqsave(&pm_qos_lock, irqflags);
+
+ prev_value = list_empty(&pqf->list) ? 0 : pqf->effective_flags;
+
+ switch (action) {
+ case PM_QOS_REMOVE_REQ:
+ pm_qos_flags_remove_req(pqf, req);
+ break;
+ case PM_QOS_UPDATE_REQ:
+ pm_qos_flags_remove_req(pqf, req);
+ case PM_QOS_ADD_REQ:
+ req->flags = val;
+ INIT_LIST_HEAD(&req->node);
+ list_add_tail(&req->node, &pqf->list);
+ pqf->effective_flags |= val;
+ break;
+ default:
+ /* no action */
+ ;
+ }
+
+ curr_value = list_empty(&pqf->list) ? 0 : pqf->effective_flags;
+
+ spin_unlock_irqrestore(&pm_qos_lock, irqflags);
+
+ return prev_value != curr_value;
+}
+
+/**
* pm_qos_request - returns current system wide qos expectation
* @pm_qos_class: identification of which qos value is requested
*