1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e8db0be1SJean Pihet /*
3fe52de36SRafael J. Wysocki * Power Management Quality of Service (PM QoS) support base.
4e8db0be1SJean Pihet *
5fe52de36SRafael J. Wysocki * Copyright (C) 2020 Intel Corporation
6e8db0be1SJean Pihet *
7fe52de36SRafael J. Wysocki * Authors:
8e8db0be1SJean Pihet * Mark Gross <mgross@linux.intel.com>
9fe52de36SRafael J. Wysocki * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
10fe52de36SRafael J. Wysocki *
11fe52de36SRafael J. Wysocki * Provided here is an interface for specifying PM QoS dependencies. It allows
12fe52de36SRafael J. Wysocki * entities depending on QoS constraints to register their requests which are
13fe52de36SRafael J. Wysocki * aggregated as appropriate to produce effective constraints (target values)
14fe52de36SRafael J. Wysocki * that can be monitored by entities needing to respect them, either by polling
15fe52de36SRafael J. Wysocki * or through a built-in notification mechanism.
16fe52de36SRafael J. Wysocki *
17fe52de36SRafael J. Wysocki * In addition to the basic functionality, more specific interfaces for managing
18fe52de36SRafael J. Wysocki * global CPU latency QoS requests and frequency QoS requests are provided.
19e8db0be1SJean Pihet */
20e8db0be1SJean Pihet
21e8db0be1SJean Pihet /*#define DEBUG*/
22e8db0be1SJean Pihet
23e8db0be1SJean Pihet #include <linux/pm_qos.h>
24e8db0be1SJean Pihet #include <linux/sched.h>
25e8db0be1SJean Pihet #include <linux/spinlock.h>
26e8db0be1SJean Pihet #include <linux/slab.h>
27e8db0be1SJean Pihet #include <linux/time.h>
28e8db0be1SJean Pihet #include <linux/fs.h>
29e8db0be1SJean Pihet #include <linux/device.h>
30e8db0be1SJean Pihet #include <linux/miscdevice.h>
31e8db0be1SJean Pihet #include <linux/string.h>
32e8db0be1SJean Pihet #include <linux/platform_device.h>
33e8db0be1SJean Pihet #include <linux/init.h>
34e8db0be1SJean Pihet #include <linux/kernel.h>
35f5f4eda4SNishanth Menon #include <linux/debugfs.h>
36f5f4eda4SNishanth Menon #include <linux/seq_file.h>
37e8db0be1SJean Pihet
38e8db0be1SJean Pihet #include <linux/uaccess.h>
396e5fdeedSPaul Gortmaker #include <linux/export.h>
40247e9ee0SSahara #include <trace/events/power.h>
41e8db0be1SJean Pihet
42e8db0be1SJean Pihet /*
43cc749986SJean Pihet * locking rule: all changes to constraints or notifiers lists
44e8db0be1SJean Pihet * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
45e8db0be1SJean Pihet * held, taken with _irqsave. One lock to rule them all
46e8db0be1SJean Pihet */
47e8db0be1SJean Pihet static DEFINE_SPINLOCK(pm_qos_lock);
48e8db0be1SJean Pihet
49dcd70ca1SRafael J. Wysocki /**
50dcd70ca1SRafael J. Wysocki * pm_qos_read_value - Return the current effective constraint value.
51dcd70ca1SRafael J. Wysocki * @c: List of PM QoS constraint requests.
52dcd70ca1SRafael J. Wysocki */
pm_qos_read_value(struct pm_qos_constraints * c)53dcd70ca1SRafael J. Wysocki s32 pm_qos_read_value(struct pm_qos_constraints *c)
54dcd70ca1SRafael J. Wysocki {
55a534e924SQian Cai return READ_ONCE(c->target_value);
56dcd70ca1SRafael J. Wysocki }
57dcd70ca1SRafael J. Wysocki
pm_qos_get_value(struct pm_qos_constraints * c)58dcd70ca1SRafael J. Wysocki static int pm_qos_get_value(struct pm_qos_constraints *c)
59e8db0be1SJean Pihet {
60abe98ec2SJean Pihet if (plist_head_empty(&c->list))
61327adaedSRafael J. Wysocki return c->no_constraint_value;
62e8db0be1SJean Pihet
63abe98ec2SJean Pihet switch (c->type) {
64e8db0be1SJean Pihet case PM_QOS_MIN:
65abe98ec2SJean Pihet return plist_first(&c->list)->prio;
66e8db0be1SJean Pihet
67e8db0be1SJean Pihet case PM_QOS_MAX:
68abe98ec2SJean Pihet return plist_last(&c->list)->prio;
69e8db0be1SJean Pihet
70e8db0be1SJean Pihet default:
71dcd70ca1SRafael J. Wysocki WARN(1, "Unknown PM QoS type in %s\n", __func__);
72c6a57bffSLuis Gonzalez Fernandez return PM_QOS_DEFAULT_VALUE;
73e8db0be1SJean Pihet }
74e8db0be1SJean Pihet }
75e8db0be1SJean Pihet
pm_qos_set_value(struct pm_qos_constraints * c,s32 value)76dcd70ca1SRafael J. Wysocki static void pm_qos_set_value(struct pm_qos_constraints *c, s32 value)
77e8db0be1SJean Pihet {
78a534e924SQian Cai WRITE_ONCE(c->target_value, value);
79e8db0be1SJean Pihet }
80e8db0be1SJean Pihet
81abe98ec2SJean Pihet /**
827b35370bSRafael J. Wysocki * pm_qos_update_target - Update a list of PM QoS constraint requests.
837b35370bSRafael J. Wysocki * @c: List of PM QoS requests.
847b35370bSRafael J. Wysocki * @node: Target list entry.
857b35370bSRafael J. Wysocki * @action: Action to carry out (add, update or remove).
867b35370bSRafael J. Wysocki * @value: New request value for the target list entry.
87abe98ec2SJean Pihet *
887b35370bSRafael J. Wysocki * Update the given list of PM QoS constraint requests, @c, by carrying an
897b35370bSRafael J. Wysocki * @action involving the @node list entry and @value on it.
907b35370bSRafael J. Wysocki *
917b35370bSRafael J. Wysocki * The recognized values of @action are PM_QOS_ADD_REQ (store @value in @node
927b35370bSRafael J. Wysocki * and add it to the list), PM_QOS_UPDATE_REQ (remove @node from the list, store
937b35370bSRafael J. Wysocki * @value in it and add it to the list again), and PM_QOS_REMOVE_REQ (remove
947b35370bSRafael J. Wysocki * @node from the list, ignore @value).
957b35370bSRafael J. Wysocki *
967b35370bSRafael J. Wysocki * Return: 1 if the aggregate constraint value has changed, 0 otherwise.
97abe98ec2SJean Pihet */
pm_qos_update_target(struct pm_qos_constraints * c,struct plist_node * node,enum pm_qos_req_action action,int value)98abe98ec2SJean Pihet int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
99abe98ec2SJean Pihet enum pm_qos_req_action action, int value)
100e8db0be1SJean Pihet {
101abe98ec2SJean Pihet int prev_value, curr_value, new_value;
1027b35370bSRafael J. Wysocki unsigned long flags;
103e8db0be1SJean Pihet
104e8db0be1SJean Pihet spin_lock_irqsave(&pm_qos_lock, flags);
1057b35370bSRafael J. Wysocki
106abe98ec2SJean Pihet prev_value = pm_qos_get_value(c);
107abe98ec2SJean Pihet if (value == PM_QOS_DEFAULT_VALUE)
108abe98ec2SJean Pihet new_value = c->default_value;
109abe98ec2SJean Pihet else
110abe98ec2SJean Pihet new_value = value;
111abe98ec2SJean Pihet
112abe98ec2SJean Pihet switch (action) {
113abe98ec2SJean Pihet case PM_QOS_REMOVE_REQ:
114abe98ec2SJean Pihet plist_del(node, &c->list);
115abe98ec2SJean Pihet break;
116abe98ec2SJean Pihet case PM_QOS_UPDATE_REQ:
117e8db0be1SJean Pihet /*
1187b35370bSRafael J. Wysocki * To change the list, atomically remove, reinit with new value
1197b35370bSRafael J. Wysocki * and add, then see if the aggregate has changed.
120e8db0be1SJean Pihet */
121abe98ec2SJean Pihet plist_del(node, &c->list);
122df561f66SGustavo A. R. Silva fallthrough;
123abe98ec2SJean Pihet case PM_QOS_ADD_REQ:
124abe98ec2SJean Pihet plist_node_init(node, new_value);
125abe98ec2SJean Pihet plist_add(node, &c->list);
126abe98ec2SJean Pihet break;
127abe98ec2SJean Pihet default:
128abe98ec2SJean Pihet /* no action */
129abe98ec2SJean Pihet ;
130e8db0be1SJean Pihet }
131abe98ec2SJean Pihet
132abe98ec2SJean Pihet curr_value = pm_qos_get_value(c);
133abe98ec2SJean Pihet pm_qos_set_value(c, curr_value);
134abe98ec2SJean Pihet
135e8db0be1SJean Pihet spin_unlock_irqrestore(&pm_qos_lock, flags);
136e8db0be1SJean Pihet
137247e9ee0SSahara trace_pm_qos_update_target(action, prev_value, curr_value);
1387b35370bSRafael J. Wysocki
1397b35370bSRafael J. Wysocki if (prev_value == curr_value)
1407b35370bSRafael J. Wysocki return 0;
1417b35370bSRafael J. Wysocki
1422d984ad1SRafael J. Wysocki if (c->notifiers)
1437b35370bSRafael J. Wysocki blocking_notifier_call_chain(c->notifiers, curr_value, NULL);
1447b35370bSRafael J. Wysocki
1457b35370bSRafael J. Wysocki return 1;
146e8db0be1SJean Pihet }
147e8db0be1SJean Pihet
148e8db0be1SJean Pihet /**
1495efbe427SRafael J. Wysocki * pm_qos_flags_remove_req - Remove device PM QoS flags request.
1505efbe427SRafael J. Wysocki * @pqf: Device PM QoS flags set to remove the request from.
1515efbe427SRafael J. Wysocki * @req: Request to remove from the set.
1525efbe427SRafael J. Wysocki */
pm_qos_flags_remove_req(struct pm_qos_flags * pqf,struct pm_qos_flags_request * req)1535efbe427SRafael J. Wysocki static void pm_qos_flags_remove_req(struct pm_qos_flags *pqf,
1545efbe427SRafael J. Wysocki struct pm_qos_flags_request *req)
1555efbe427SRafael J. Wysocki {
1565efbe427SRafael J. Wysocki s32 val = 0;
1575efbe427SRafael J. Wysocki
1585efbe427SRafael J. Wysocki list_del(&req->node);
1595efbe427SRafael J. Wysocki list_for_each_entry(req, &pqf->list, node)
1605efbe427SRafael J. Wysocki val |= req->flags;
1615efbe427SRafael J. Wysocki
1625efbe427SRafael J. Wysocki pqf->effective_flags = val;
1635efbe427SRafael J. Wysocki }
1645efbe427SRafael J. Wysocki
1655efbe427SRafael J. Wysocki /**
1665efbe427SRafael J. Wysocki * pm_qos_update_flags - Update a set of PM QoS flags.
1677b35370bSRafael J. Wysocki * @pqf: Set of PM QoS flags to update.
1685efbe427SRafael J. Wysocki * @req: Request to add to the set, to modify, or to remove from the set.
1695efbe427SRafael J. Wysocki * @action: Action to take on the set.
1705efbe427SRafael J. Wysocki * @val: Value of the request to add or modify.
1715efbe427SRafael J. Wysocki *
1727b35370bSRafael J. Wysocki * Return: 1 if the aggregate constraint value has changed, 0 otherwise.
1735efbe427SRafael J. Wysocki */
pm_qos_update_flags(struct pm_qos_flags * pqf,struct pm_qos_flags_request * req,enum pm_qos_req_action action,s32 val)1745efbe427SRafael J. Wysocki bool pm_qos_update_flags(struct pm_qos_flags *pqf,
1755efbe427SRafael J. Wysocki struct pm_qos_flags_request *req,
1765efbe427SRafael J. Wysocki enum pm_qos_req_action action, s32 val)
1775efbe427SRafael J. Wysocki {
1785efbe427SRafael J. Wysocki unsigned long irqflags;
1795efbe427SRafael J. Wysocki s32 prev_value, curr_value;
1805efbe427SRafael J. Wysocki
1815efbe427SRafael J. Wysocki spin_lock_irqsave(&pm_qos_lock, irqflags);
1825efbe427SRafael J. Wysocki
1835efbe427SRafael J. Wysocki prev_value = list_empty(&pqf->list) ? 0 : pqf->effective_flags;
1845efbe427SRafael J. Wysocki
1855efbe427SRafael J. Wysocki switch (action) {
1865efbe427SRafael J. Wysocki case PM_QOS_REMOVE_REQ:
1875efbe427SRafael J. Wysocki pm_qos_flags_remove_req(pqf, req);
1885efbe427SRafael J. Wysocki break;
1895efbe427SRafael J. Wysocki case PM_QOS_UPDATE_REQ:
1905efbe427SRafael J. Wysocki pm_qos_flags_remove_req(pqf, req);
191df561f66SGustavo A. R. Silva fallthrough;
1925efbe427SRafael J. Wysocki case PM_QOS_ADD_REQ:
1935efbe427SRafael J. Wysocki req->flags = val;
1945efbe427SRafael J. Wysocki INIT_LIST_HEAD(&req->node);
1955efbe427SRafael J. Wysocki list_add_tail(&req->node, &pqf->list);
1965efbe427SRafael J. Wysocki pqf->effective_flags |= val;
1975efbe427SRafael J. Wysocki break;
1985efbe427SRafael J. Wysocki default:
1995efbe427SRafael J. Wysocki /* no action */
2005efbe427SRafael J. Wysocki ;
2015efbe427SRafael J. Wysocki }
2025efbe427SRafael J. Wysocki
2035efbe427SRafael J. Wysocki curr_value = list_empty(&pqf->list) ? 0 : pqf->effective_flags;
2045efbe427SRafael J. Wysocki
2055efbe427SRafael J. Wysocki spin_unlock_irqrestore(&pm_qos_lock, irqflags);
2065efbe427SRafael J. Wysocki
207247e9ee0SSahara trace_pm_qos_update_flags(action, prev_value, curr_value);
2087b35370bSRafael J. Wysocki
2095efbe427SRafael J. Wysocki return prev_value != curr_value;
2105efbe427SRafael J. Wysocki }
2115efbe427SRafael J. Wysocki
212814d51f8SRafael J. Wysocki #ifdef CONFIG_CPU_IDLE
2132552d352SRafael J. Wysocki /* Definitions related to the CPU latency QoS. */
2142552d352SRafael J. Wysocki
2152552d352SRafael J. Wysocki static struct pm_qos_constraints cpu_latency_constraints = {
2162552d352SRafael J. Wysocki .list = PLIST_HEAD_INIT(cpu_latency_constraints.list),
2172552d352SRafael J. Wysocki .target_value = PM_QOS_CPU_LATENCY_DEFAULT_VALUE,
2182552d352SRafael J. Wysocki .default_value = PM_QOS_CPU_LATENCY_DEFAULT_VALUE,
2192552d352SRafael J. Wysocki .no_constraint_value = PM_QOS_CPU_LATENCY_DEFAULT_VALUE,
2202552d352SRafael J. Wysocki .type = PM_QOS_MIN,
2212552d352SRafael J. Wysocki };
2222552d352SRafael J. Wysocki
cpu_latency_qos_value_invalid(s32 value)223*5f55836aSClive Lin static inline bool cpu_latency_qos_value_invalid(s32 value)
224*5f55836aSClive Lin {
225*5f55836aSClive Lin return value < 0 && value != PM_QOS_DEFAULT_VALUE;
226*5f55836aSClive Lin }
227*5f55836aSClive Lin
2285efbe427SRafael J. Wysocki /**
22967b06ba0SRafael J. Wysocki * cpu_latency_qos_limit - Return current system-wide CPU latency QoS limit.
230e8db0be1SJean Pihet */
cpu_latency_qos_limit(void)23167b06ba0SRafael J. Wysocki s32 cpu_latency_qos_limit(void)
232e8db0be1SJean Pihet {
2332552d352SRafael J. Wysocki return pm_qos_read_value(&cpu_latency_constraints);
234e8db0be1SJean Pihet }
235e8db0be1SJean Pihet
23667b06ba0SRafael J. Wysocki /**
23767b06ba0SRafael J. Wysocki * cpu_latency_qos_request_active - Check the given PM QoS request.
23867b06ba0SRafael J. Wysocki * @req: PM QoS request to check.
23967b06ba0SRafael J. Wysocki *
24067b06ba0SRafael J. Wysocki * Return: 'true' if @req has been added to the CPU latency QoS list, 'false'
24167b06ba0SRafael J. Wysocki * otherwise.
24267b06ba0SRafael J. Wysocki */
cpu_latency_qos_request_active(struct pm_qos_request * req)24367b06ba0SRafael J. Wysocki bool cpu_latency_qos_request_active(struct pm_qos_request *req)
244e8db0be1SJean Pihet {
2452552d352SRafael J. Wysocki return req->qos == &cpu_latency_constraints;
246e8db0be1SJean Pihet }
24767b06ba0SRafael J. Wysocki EXPORT_SYMBOL_GPL(cpu_latency_qos_request_active);
248e8db0be1SJean Pihet
cpu_latency_qos_apply(struct pm_qos_request * req,enum pm_qos_req_action action,s32 value)249333eed7dSRafael J. Wysocki static void cpu_latency_qos_apply(struct pm_qos_request *req,
2503a4a0042SRafael J. Wysocki enum pm_qos_req_action action, s32 value)
2513a4a0042SRafael J. Wysocki {
2523a4a0042SRafael J. Wysocki int ret = pm_qos_update_target(req->qos, &req->node, action, value);
2533a4a0042SRafael J. Wysocki if (ret > 0)
2543a4a0042SRafael J. Wysocki wake_up_all_idle_cpus();
2553a4a0042SRafael J. Wysocki }
2563a4a0042SRafael J. Wysocki
257e8db0be1SJean Pihet /**
25867b06ba0SRafael J. Wysocki * cpu_latency_qos_add_request - Add new CPU latency QoS request.
25967b06ba0SRafael J. Wysocki * @req: Pointer to a preallocated handle.
26067b06ba0SRafael J. Wysocki * @value: Requested constraint value.
261e8db0be1SJean Pihet *
26267b06ba0SRafael J. Wysocki * Use @value to initialize the request handle pointed to by @req, insert it as
26367b06ba0SRafael J. Wysocki * a new entry to the CPU latency QoS list and recompute the effective QoS
26467b06ba0SRafael J. Wysocki * constraint for that list.
26567b06ba0SRafael J. Wysocki *
26667b06ba0SRafael J. Wysocki * Callers need to save the handle for later use in updates and removal of the
26767b06ba0SRafael J. Wysocki * QoS request represented by it.
268e8db0be1SJean Pihet */
cpu_latency_qos_add_request(struct pm_qos_request * req,s32 value)26967b06ba0SRafael J. Wysocki void cpu_latency_qos_add_request(struct pm_qos_request *req, s32 value)
270e8db0be1SJean Pihet {
271*5f55836aSClive Lin if (!req || cpu_latency_qos_value_invalid(value))
272abe98ec2SJean Pihet return;
273e8db0be1SJean Pihet
27467b06ba0SRafael J. Wysocki if (cpu_latency_qos_request_active(req)) {
27567b06ba0SRafael J. Wysocki WARN(1, KERN_ERR "%s called for already added request\n", __func__);
276e8db0be1SJean Pihet return;
277e8db0be1SJean Pihet }
27802c92a37SRafael J. Wysocki
279333eed7dSRafael J. Wysocki trace_pm_qos_add_request(value);
28002c92a37SRafael J. Wysocki
2812552d352SRafael J. Wysocki req->qos = &cpu_latency_constraints;
282333eed7dSRafael J. Wysocki cpu_latency_qos_apply(req, PM_QOS_ADD_REQ, value);
283e8db0be1SJean Pihet }
28467b06ba0SRafael J. Wysocki EXPORT_SYMBOL_GPL(cpu_latency_qos_add_request);
285e8db0be1SJean Pihet
286e8db0be1SJean Pihet /**
28767b06ba0SRafael J. Wysocki * cpu_latency_qos_update_request - Modify existing CPU latency QoS request.
28867b06ba0SRafael J. Wysocki * @req : QoS request to update.
28967b06ba0SRafael J. Wysocki * @new_value: New requested constraint value.
290e8db0be1SJean Pihet *
29167b06ba0SRafael J. Wysocki * Use @new_value to update the QoS request represented by @req in the CPU
29267b06ba0SRafael J. Wysocki * latency QoS list along with updating the effective constraint value for that
29367b06ba0SRafael J. Wysocki * list.
294e8db0be1SJean Pihet */
cpu_latency_qos_update_request(struct pm_qos_request * req,s32 new_value)29567b06ba0SRafael J. Wysocki void cpu_latency_qos_update_request(struct pm_qos_request *req, s32 new_value)
296e8db0be1SJean Pihet {
297*5f55836aSClive Lin if (!req || cpu_latency_qos_value_invalid(new_value))
298e8db0be1SJean Pihet return;
299e8db0be1SJean Pihet
30067b06ba0SRafael J. Wysocki if (!cpu_latency_qos_request_active(req)) {
30167b06ba0SRafael J. Wysocki WARN(1, KERN_ERR "%s called for unknown object\n", __func__);
302e8db0be1SJean Pihet return;
303e8db0be1SJean Pihet }
304e8db0be1SJean Pihet
305333eed7dSRafael J. Wysocki trace_pm_qos_update_request(new_value);
30602c92a37SRafael J. Wysocki
30702c92a37SRafael J. Wysocki if (new_value == req->node.prio)
30802c92a37SRafael J. Wysocki return;
30902c92a37SRafael J. Wysocki
310333eed7dSRafael J. Wysocki cpu_latency_qos_apply(req, PM_QOS_UPDATE_REQ, new_value);
311e8db0be1SJean Pihet }
31267b06ba0SRafael J. Wysocki EXPORT_SYMBOL_GPL(cpu_latency_qos_update_request);
313e8db0be1SJean Pihet
314e8db0be1SJean Pihet /**
31567b06ba0SRafael J. Wysocki * cpu_latency_qos_remove_request - Remove existing CPU latency QoS request.
31667b06ba0SRafael J. Wysocki * @req: QoS request to remove.
317e8db0be1SJean Pihet *
31867b06ba0SRafael J. Wysocki * Remove the CPU latency QoS request represented by @req from the CPU latency
31967b06ba0SRafael J. Wysocki * QoS list along with updating the effective constraint value for that list.
320e8db0be1SJean Pihet */
cpu_latency_qos_remove_request(struct pm_qos_request * req)32167b06ba0SRafael J. Wysocki void cpu_latency_qos_remove_request(struct pm_qos_request *req)
322e8db0be1SJean Pihet {
32367b06ba0SRafael J. Wysocki if (!req)
324e8db0be1SJean Pihet return;
325e8db0be1SJean Pihet
32667b06ba0SRafael J. Wysocki if (!cpu_latency_qos_request_active(req)) {
32767b06ba0SRafael J. Wysocki WARN(1, KERN_ERR "%s called for unknown object\n", __func__);
328e8db0be1SJean Pihet return;
329e8db0be1SJean Pihet }
330e8db0be1SJean Pihet
331333eed7dSRafael J. Wysocki trace_pm_qos_remove_request(PM_QOS_DEFAULT_VALUE);
33202c92a37SRafael J. Wysocki
333333eed7dSRafael J. Wysocki cpu_latency_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
334cc749986SJean Pihet memset(req, 0, sizeof(*req));
335e8db0be1SJean Pihet }
33667b06ba0SRafael J. Wysocki EXPORT_SYMBOL_GPL(cpu_latency_qos_remove_request);
337e8db0be1SJean Pihet
3382552d352SRafael J. Wysocki /* User space interface to the CPU latency QoS via misc device. */
33963cffc05SRafael J. Wysocki
cpu_latency_qos_open(struct inode * inode,struct file * filp)3402552d352SRafael J. Wysocki static int cpu_latency_qos_open(struct inode *inode, struct file *filp)
341e8db0be1SJean Pihet {
34263cffc05SRafael J. Wysocki struct pm_qos_request *req;
343e8db0be1SJean Pihet
34463cffc05SRafael J. Wysocki req = kzalloc(sizeof(*req), GFP_KERNEL);
345e8db0be1SJean Pihet if (!req)
346e8db0be1SJean Pihet return -ENOMEM;
347e8db0be1SJean Pihet
34867b06ba0SRafael J. Wysocki cpu_latency_qos_add_request(req, PM_QOS_DEFAULT_VALUE);
349e8db0be1SJean Pihet filp->private_data = req;
350e8db0be1SJean Pihet
351e8db0be1SJean Pihet return 0;
352e8db0be1SJean Pihet }
353e8db0be1SJean Pihet
cpu_latency_qos_release(struct inode * inode,struct file * filp)3542552d352SRafael J. Wysocki static int cpu_latency_qos_release(struct inode *inode, struct file *filp)
355e8db0be1SJean Pihet {
356299a2298SRafael J. Wysocki struct pm_qos_request *req = filp->private_data;
357e8db0be1SJean Pihet
358299a2298SRafael J. Wysocki filp->private_data = NULL;
359299a2298SRafael J. Wysocki
36067b06ba0SRafael J. Wysocki cpu_latency_qos_remove_request(req);
361e8db0be1SJean Pihet kfree(req);
362e8db0be1SJean Pihet
363e8db0be1SJean Pihet return 0;
364e8db0be1SJean Pihet }
365e8db0be1SJean Pihet
cpu_latency_qos_read(struct file * filp,char __user * buf,size_t count,loff_t * f_pos)3662552d352SRafael J. Wysocki static ssize_t cpu_latency_qos_read(struct file *filp, char __user *buf,
367e8db0be1SJean Pihet size_t count, loff_t *f_pos)
368e8db0be1SJean Pihet {
369cc749986SJean Pihet struct pm_qos_request *req = filp->private_data;
370299a2298SRafael J. Wysocki unsigned long flags;
371299a2298SRafael J. Wysocki s32 value;
372e8db0be1SJean Pihet
37367b06ba0SRafael J. Wysocki if (!req || !cpu_latency_qos_request_active(req))
374e8db0be1SJean Pihet return -EINVAL;
375e8db0be1SJean Pihet
376e8db0be1SJean Pihet spin_lock_irqsave(&pm_qos_lock, flags);
3772552d352SRafael J. Wysocki value = pm_qos_get_value(&cpu_latency_constraints);
378e8db0be1SJean Pihet spin_unlock_irqrestore(&pm_qos_lock, flags);
379e8db0be1SJean Pihet
380e8db0be1SJean Pihet return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
381e8db0be1SJean Pihet }
382e8db0be1SJean Pihet
cpu_latency_qos_write(struct file * filp,const char __user * buf,size_t count,loff_t * f_pos)3832552d352SRafael J. Wysocki static ssize_t cpu_latency_qos_write(struct file *filp, const char __user *buf,
384e8db0be1SJean Pihet size_t count, loff_t *f_pos)
385e8db0be1SJean Pihet {
386e8db0be1SJean Pihet s32 value;
387e8db0be1SJean Pihet
388e8db0be1SJean Pihet if (count == sizeof(s32)) {
389e8db0be1SJean Pihet if (copy_from_user(&value, buf, sizeof(s32)))
390e8db0be1SJean Pihet return -EFAULT;
391d4f7ecf7SAndy Shevchenko } else {
392e8db0be1SJean Pihet int ret;
393e8db0be1SJean Pihet
394d4f7ecf7SAndy Shevchenko ret = kstrtos32_from_user(buf, count, 16, &value);
395d4f7ecf7SAndy Shevchenko if (ret)
396d4f7ecf7SAndy Shevchenko return ret;
397e8db0be1SJean Pihet }
398e8db0be1SJean Pihet
39967b06ba0SRafael J. Wysocki cpu_latency_qos_update_request(filp->private_data, value);
400e8db0be1SJean Pihet
401e8db0be1SJean Pihet return count;
402e8db0be1SJean Pihet }
403e8db0be1SJean Pihet
4042552d352SRafael J. Wysocki static const struct file_operations cpu_latency_qos_fops = {
4052552d352SRafael J. Wysocki .write = cpu_latency_qos_write,
4062552d352SRafael J. Wysocki .read = cpu_latency_qos_read,
4072552d352SRafael J. Wysocki .open = cpu_latency_qos_open,
4082552d352SRafael J. Wysocki .release = cpu_latency_qos_release,
409299a2298SRafael J. Wysocki .llseek = noop_llseek,
410299a2298SRafael J. Wysocki };
411299a2298SRafael J. Wysocki
41202c92a37SRafael J. Wysocki static struct miscdevice cpu_latency_qos_miscdev = {
41302c92a37SRafael J. Wysocki .minor = MISC_DYNAMIC_MINOR,
41402c92a37SRafael J. Wysocki .name = "cpu_dma_latency",
4152552d352SRafael J. Wysocki .fops = &cpu_latency_qos_fops,
41602c92a37SRafael J. Wysocki };
417299a2298SRafael J. Wysocki
cpu_latency_qos_init(void)4182552d352SRafael J. Wysocki static int __init cpu_latency_qos_init(void)
419e8db0be1SJean Pihet {
42063cffc05SRafael J. Wysocki int ret;
421e8db0be1SJean Pihet
42202c92a37SRafael J. Wysocki ret = misc_register(&cpu_latency_qos_miscdev);
42363cffc05SRafael J. Wysocki if (ret < 0)
42463cffc05SRafael J. Wysocki pr_err("%s: %s setup failed\n", __func__,
42502c92a37SRafael J. Wysocki cpu_latency_qos_miscdev.name);
426e8db0be1SJean Pihet
427e8db0be1SJean Pihet return ret;
428e8db0be1SJean Pihet }
4292552d352SRafael J. Wysocki late_initcall(cpu_latency_qos_init);
430814d51f8SRafael J. Wysocki #endif /* CONFIG_CPU_IDLE */
43177751a46SRafael J. Wysocki
43277751a46SRafael J. Wysocki /* Definitions related to the frequency QoS below. */
43377751a46SRafael J. Wysocki
freq_qos_value_invalid(s32 value)4343a8395b5SChungkai Yang static inline bool freq_qos_value_invalid(s32 value)
4353a8395b5SChungkai Yang {
4363a8395b5SChungkai Yang return value < 0 && value != PM_QOS_DEFAULT_VALUE;
4373a8395b5SChungkai Yang }
4383a8395b5SChungkai Yang
43977751a46SRafael J. Wysocki /**
44077751a46SRafael J. Wysocki * freq_constraints_init - Initialize frequency QoS constraints.
44177751a46SRafael J. Wysocki * @qos: Frequency QoS constraints to initialize.
44277751a46SRafael J. Wysocki */
freq_constraints_init(struct freq_constraints * qos)44377751a46SRafael J. Wysocki void freq_constraints_init(struct freq_constraints *qos)
44477751a46SRafael J. Wysocki {
44577751a46SRafael J. Wysocki struct pm_qos_constraints *c;
44677751a46SRafael J. Wysocki
44777751a46SRafael J. Wysocki c = &qos->min_freq;
44877751a46SRafael J. Wysocki plist_head_init(&c->list);
44977751a46SRafael J. Wysocki c->target_value = FREQ_QOS_MIN_DEFAULT_VALUE;
45077751a46SRafael J. Wysocki c->default_value = FREQ_QOS_MIN_DEFAULT_VALUE;
45177751a46SRafael J. Wysocki c->no_constraint_value = FREQ_QOS_MIN_DEFAULT_VALUE;
45277751a46SRafael J. Wysocki c->type = PM_QOS_MAX;
45377751a46SRafael J. Wysocki c->notifiers = &qos->min_freq_notifiers;
45477751a46SRafael J. Wysocki BLOCKING_INIT_NOTIFIER_HEAD(c->notifiers);
45577751a46SRafael J. Wysocki
45677751a46SRafael J. Wysocki c = &qos->max_freq;
45777751a46SRafael J. Wysocki plist_head_init(&c->list);
45877751a46SRafael J. Wysocki c->target_value = FREQ_QOS_MAX_DEFAULT_VALUE;
45977751a46SRafael J. Wysocki c->default_value = FREQ_QOS_MAX_DEFAULT_VALUE;
46077751a46SRafael J. Wysocki c->no_constraint_value = FREQ_QOS_MAX_DEFAULT_VALUE;
46177751a46SRafael J. Wysocki c->type = PM_QOS_MIN;
46277751a46SRafael J. Wysocki c->notifiers = &qos->max_freq_notifiers;
46377751a46SRafael J. Wysocki BLOCKING_INIT_NOTIFIER_HEAD(c->notifiers);
46477751a46SRafael J. Wysocki }
46577751a46SRafael J. Wysocki
46677751a46SRafael J. Wysocki /**
46777751a46SRafael J. Wysocki * freq_qos_read_value - Get frequency QoS constraint for a given list.
46877751a46SRafael J. Wysocki * @qos: Constraints to evaluate.
46977751a46SRafael J. Wysocki * @type: QoS request type.
47077751a46SRafael J. Wysocki */
freq_qos_read_value(struct freq_constraints * qos,enum freq_qos_req_type type)47177751a46SRafael J. Wysocki s32 freq_qos_read_value(struct freq_constraints *qos,
47277751a46SRafael J. Wysocki enum freq_qos_req_type type)
47377751a46SRafael J. Wysocki {
47477751a46SRafael J. Wysocki s32 ret;
47577751a46SRafael J. Wysocki
47677751a46SRafael J. Wysocki switch (type) {
47777751a46SRafael J. Wysocki case FREQ_QOS_MIN:
47877751a46SRafael J. Wysocki ret = IS_ERR_OR_NULL(qos) ?
47977751a46SRafael J. Wysocki FREQ_QOS_MIN_DEFAULT_VALUE :
48077751a46SRafael J. Wysocki pm_qos_read_value(&qos->min_freq);
48177751a46SRafael J. Wysocki break;
48277751a46SRafael J. Wysocki case FREQ_QOS_MAX:
48377751a46SRafael J. Wysocki ret = IS_ERR_OR_NULL(qos) ?
48477751a46SRafael J. Wysocki FREQ_QOS_MAX_DEFAULT_VALUE :
48577751a46SRafael J. Wysocki pm_qos_read_value(&qos->max_freq);
48677751a46SRafael J. Wysocki break;
48777751a46SRafael J. Wysocki default:
48877751a46SRafael J. Wysocki WARN_ON(1);
48977751a46SRafael J. Wysocki ret = 0;
49077751a46SRafael J. Wysocki }
49177751a46SRafael J. Wysocki
49277751a46SRafael J. Wysocki return ret;
49377751a46SRafael J. Wysocki }
49477751a46SRafael J. Wysocki
49577751a46SRafael J. Wysocki /**
49677751a46SRafael J. Wysocki * freq_qos_apply - Add/modify/remove frequency QoS request.
49777751a46SRafael J. Wysocki * @req: Constraint request to apply.
49877751a46SRafael J. Wysocki * @action: Action to perform (add/update/remove).
49977751a46SRafael J. Wysocki * @value: Value to assign to the QoS request.
50036a8015fSLeonard Crestez *
50136a8015fSLeonard Crestez * This is only meant to be called from inside pm_qos, not drivers.
50277751a46SRafael J. Wysocki */
freq_qos_apply(struct freq_qos_request * req,enum pm_qos_req_action action,s32 value)50336a8015fSLeonard Crestez int freq_qos_apply(struct freq_qos_request *req,
50477751a46SRafael J. Wysocki enum pm_qos_req_action action, s32 value)
50577751a46SRafael J. Wysocki {
50677751a46SRafael J. Wysocki int ret;
50777751a46SRafael J. Wysocki
50877751a46SRafael J. Wysocki switch(req->type) {
50977751a46SRafael J. Wysocki case FREQ_QOS_MIN:
51077751a46SRafael J. Wysocki ret = pm_qos_update_target(&req->qos->min_freq, &req->pnode,
51177751a46SRafael J. Wysocki action, value);
51277751a46SRafael J. Wysocki break;
51377751a46SRafael J. Wysocki case FREQ_QOS_MAX:
51477751a46SRafael J. Wysocki ret = pm_qos_update_target(&req->qos->max_freq, &req->pnode,
51577751a46SRafael J. Wysocki action, value);
51677751a46SRafael J. Wysocki break;
51777751a46SRafael J. Wysocki default:
51877751a46SRafael J. Wysocki ret = -EINVAL;
51977751a46SRafael J. Wysocki }
52077751a46SRafael J. Wysocki
52177751a46SRafael J. Wysocki return ret;
52277751a46SRafael J. Wysocki }
52377751a46SRafael J. Wysocki
52477751a46SRafael J. Wysocki /**
52577751a46SRafael J. Wysocki * freq_qos_add_request - Insert new frequency QoS request into a given list.
52677751a46SRafael J. Wysocki * @qos: Constraints to update.
52777751a46SRafael J. Wysocki * @req: Preallocated request object.
52877751a46SRafael J. Wysocki * @type: Request type.
52977751a46SRafael J. Wysocki * @value: Request value.
53077751a46SRafael J. Wysocki *
53177751a46SRafael J. Wysocki * Insert a new entry into the @qos list of requests, recompute the effective
53277751a46SRafael J. Wysocki * QoS constraint value for that list and initialize the @req object. The
53377751a46SRafael J. Wysocki * caller needs to save that object for later use in updates and removal.
53477751a46SRafael J. Wysocki *
53577751a46SRafael J. Wysocki * Return 1 if the effective constraint value has changed, 0 if the effective
53677751a46SRafael J. Wysocki * constraint value has not changed, or a negative error code on failures.
53777751a46SRafael J. Wysocki */
freq_qos_add_request(struct freq_constraints * qos,struct freq_qos_request * req,enum freq_qos_req_type type,s32 value)53877751a46SRafael J. Wysocki int freq_qos_add_request(struct freq_constraints *qos,
53977751a46SRafael J. Wysocki struct freq_qos_request *req,
54077751a46SRafael J. Wysocki enum freq_qos_req_type type, s32 value)
54177751a46SRafael J. Wysocki {
54277751a46SRafael J. Wysocki int ret;
54377751a46SRafael J. Wysocki
5443a8395b5SChungkai Yang if (IS_ERR_OR_NULL(qos) || !req || freq_qos_value_invalid(value))
54577751a46SRafael J. Wysocki return -EINVAL;
54677751a46SRafael J. Wysocki
54777751a46SRafael J. Wysocki if (WARN(freq_qos_request_active(req),
54877751a46SRafael J. Wysocki "%s() called for active request\n", __func__))
54977751a46SRafael J. Wysocki return -EINVAL;
55077751a46SRafael J. Wysocki
55177751a46SRafael J. Wysocki req->qos = qos;
55277751a46SRafael J. Wysocki req->type = type;
55377751a46SRafael J. Wysocki ret = freq_qos_apply(req, PM_QOS_ADD_REQ, value);
55477751a46SRafael J. Wysocki if (ret < 0) {
55577751a46SRafael J. Wysocki req->qos = NULL;
55677751a46SRafael J. Wysocki req->type = 0;
55777751a46SRafael J. Wysocki }
55877751a46SRafael J. Wysocki
55977751a46SRafael J. Wysocki return ret;
56077751a46SRafael J. Wysocki }
56177751a46SRafael J. Wysocki EXPORT_SYMBOL_GPL(freq_qos_add_request);
56277751a46SRafael J. Wysocki
56377751a46SRafael J. Wysocki /**
56477751a46SRafael J. Wysocki * freq_qos_update_request - Modify existing frequency QoS request.
56577751a46SRafael J. Wysocki * @req: Request to modify.
56677751a46SRafael J. Wysocki * @new_value: New request value.
56777751a46SRafael J. Wysocki *
56877751a46SRafael J. Wysocki * Update an existing frequency QoS request along with the effective constraint
56977751a46SRafael J. Wysocki * value for the list of requests it belongs to.
57077751a46SRafael J. Wysocki *
57177751a46SRafael J. Wysocki * Return 1 if the effective constraint value has changed, 0 if the effective
57277751a46SRafael J. Wysocki * constraint value has not changed, or a negative error code on failures.
57377751a46SRafael J. Wysocki */
freq_qos_update_request(struct freq_qos_request * req,s32 new_value)57477751a46SRafael J. Wysocki int freq_qos_update_request(struct freq_qos_request *req, s32 new_value)
57577751a46SRafael J. Wysocki {
5763a8395b5SChungkai Yang if (!req || freq_qos_value_invalid(new_value))
57777751a46SRafael J. Wysocki return -EINVAL;
57877751a46SRafael J. Wysocki
57977751a46SRafael J. Wysocki if (WARN(!freq_qos_request_active(req),
58077751a46SRafael J. Wysocki "%s() called for unknown object\n", __func__))
58177751a46SRafael J. Wysocki return -EINVAL;
58277751a46SRafael J. Wysocki
58377751a46SRafael J. Wysocki if (req->pnode.prio == new_value)
58477751a46SRafael J. Wysocki return 0;
58577751a46SRafael J. Wysocki
58677751a46SRafael J. Wysocki return freq_qos_apply(req, PM_QOS_UPDATE_REQ, new_value);
58777751a46SRafael J. Wysocki }
58877751a46SRafael J. Wysocki EXPORT_SYMBOL_GPL(freq_qos_update_request);
58977751a46SRafael J. Wysocki
59077751a46SRafael J. Wysocki /**
59177751a46SRafael J. Wysocki * freq_qos_remove_request - Remove frequency QoS request from its list.
59277751a46SRafael J. Wysocki * @req: Request to remove.
59377751a46SRafael J. Wysocki *
59477751a46SRafael J. Wysocki * Remove the given frequency QoS request from the list of constraints it
59577751a46SRafael J. Wysocki * belongs to and recompute the effective constraint value for that list.
59677751a46SRafael J. Wysocki *
59777751a46SRafael J. Wysocki * Return 1 if the effective constraint value has changed, 0 if the effective
59877751a46SRafael J. Wysocki * constraint value has not changed, or a negative error code on failures.
59977751a46SRafael J. Wysocki */
freq_qos_remove_request(struct freq_qos_request * req)60077751a46SRafael J. Wysocki int freq_qos_remove_request(struct freq_qos_request *req)
60177751a46SRafael J. Wysocki {
60205ff1ba4SRafael J. Wysocki int ret;
60305ff1ba4SRafael J. Wysocki
60477751a46SRafael J. Wysocki if (!req)
60577751a46SRafael J. Wysocki return -EINVAL;
60677751a46SRafael J. Wysocki
60777751a46SRafael J. Wysocki if (WARN(!freq_qos_request_active(req),
60877751a46SRafael J. Wysocki "%s() called for unknown object\n", __func__))
60977751a46SRafael J. Wysocki return -EINVAL;
61077751a46SRafael J. Wysocki
61105ff1ba4SRafael J. Wysocki ret = freq_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
61205ff1ba4SRafael J. Wysocki req->qos = NULL;
61305ff1ba4SRafael J. Wysocki req->type = 0;
61405ff1ba4SRafael J. Wysocki
61505ff1ba4SRafael J. Wysocki return ret;
61677751a46SRafael J. Wysocki }
61777751a46SRafael J. Wysocki EXPORT_SYMBOL_GPL(freq_qos_remove_request);
61877751a46SRafael J. Wysocki
61977751a46SRafael J. Wysocki /**
62077751a46SRafael J. Wysocki * freq_qos_add_notifier - Add frequency QoS change notifier.
62177751a46SRafael J. Wysocki * @qos: List of requests to add the notifier to.
62277751a46SRafael J. Wysocki * @type: Request type.
62377751a46SRafael J. Wysocki * @notifier: Notifier block to add.
62477751a46SRafael J. Wysocki */
freq_qos_add_notifier(struct freq_constraints * qos,enum freq_qos_req_type type,struct notifier_block * notifier)62577751a46SRafael J. Wysocki int freq_qos_add_notifier(struct freq_constraints *qos,
62677751a46SRafael J. Wysocki enum freq_qos_req_type type,
62777751a46SRafael J. Wysocki struct notifier_block *notifier)
62877751a46SRafael J. Wysocki {
62977751a46SRafael J. Wysocki int ret;
63077751a46SRafael J. Wysocki
63177751a46SRafael J. Wysocki if (IS_ERR_OR_NULL(qos) || !notifier)
63277751a46SRafael J. Wysocki return -EINVAL;
63377751a46SRafael J. Wysocki
63477751a46SRafael J. Wysocki switch (type) {
63577751a46SRafael J. Wysocki case FREQ_QOS_MIN:
63677751a46SRafael J. Wysocki ret = blocking_notifier_chain_register(qos->min_freq.notifiers,
63777751a46SRafael J. Wysocki notifier);
63877751a46SRafael J. Wysocki break;
63977751a46SRafael J. Wysocki case FREQ_QOS_MAX:
64077751a46SRafael J. Wysocki ret = blocking_notifier_chain_register(qos->max_freq.notifiers,
64177751a46SRafael J. Wysocki notifier);
64277751a46SRafael J. Wysocki break;
64377751a46SRafael J. Wysocki default:
64477751a46SRafael J. Wysocki WARN_ON(1);
64577751a46SRafael J. Wysocki ret = -EINVAL;
64677751a46SRafael J. Wysocki }
64777751a46SRafael J. Wysocki
64877751a46SRafael J. Wysocki return ret;
64977751a46SRafael J. Wysocki }
65077751a46SRafael J. Wysocki EXPORT_SYMBOL_GPL(freq_qos_add_notifier);
65177751a46SRafael J. Wysocki
65277751a46SRafael J. Wysocki /**
65377751a46SRafael J. Wysocki * freq_qos_remove_notifier - Remove frequency QoS change notifier.
65477751a46SRafael J. Wysocki * @qos: List of requests to remove the notifier from.
65577751a46SRafael J. Wysocki * @type: Request type.
65677751a46SRafael J. Wysocki * @notifier: Notifier block to remove.
65777751a46SRafael J. Wysocki */
freq_qos_remove_notifier(struct freq_constraints * qos,enum freq_qos_req_type type,struct notifier_block * notifier)65877751a46SRafael J. Wysocki int freq_qos_remove_notifier(struct freq_constraints *qos,
65977751a46SRafael J. Wysocki enum freq_qos_req_type type,
66077751a46SRafael J. Wysocki struct notifier_block *notifier)
66177751a46SRafael J. Wysocki {
66277751a46SRafael J. Wysocki int ret;
66377751a46SRafael J. Wysocki
66477751a46SRafael J. Wysocki if (IS_ERR_OR_NULL(qos) || !notifier)
66577751a46SRafael J. Wysocki return -EINVAL;
66677751a46SRafael J. Wysocki
66777751a46SRafael J. Wysocki switch (type) {
66877751a46SRafael J. Wysocki case FREQ_QOS_MIN:
66977751a46SRafael J. Wysocki ret = blocking_notifier_chain_unregister(qos->min_freq.notifiers,
67077751a46SRafael J. Wysocki notifier);
67177751a46SRafael J. Wysocki break;
67277751a46SRafael J. Wysocki case FREQ_QOS_MAX:
67377751a46SRafael J. Wysocki ret = blocking_notifier_chain_unregister(qos->max_freq.notifiers,
67477751a46SRafael J. Wysocki notifier);
67577751a46SRafael J. Wysocki break;
67677751a46SRafael J. Wysocki default:
67777751a46SRafael J. Wysocki WARN_ON(1);
67877751a46SRafael J. Wysocki ret = -EINVAL;
67977751a46SRafael J. Wysocki }
68077751a46SRafael J. Wysocki
68177751a46SRafael J. Wysocki return ret;
68277751a46SRafael J. Wysocki }
68377751a46SRafael J. Wysocki EXPORT_SYMBOL_GPL(freq_qos_remove_notifier);
684