xref: /openbmc/linux/drivers/base/power/qos.c (revision e84b4a84)
191ff4cb8SJean Pihet /*
291ff4cb8SJean Pihet  * Devices PM QoS constraints management
391ff4cb8SJean Pihet  *
491ff4cb8SJean Pihet  * Copyright (C) 2011 Texas Instruments, Inc.
591ff4cb8SJean Pihet  *
691ff4cb8SJean Pihet  * This program is free software; you can redistribute it and/or modify
791ff4cb8SJean Pihet  * it under the terms of the GNU General Public License version 2 as
891ff4cb8SJean Pihet  * published by the Free Software Foundation.
991ff4cb8SJean Pihet  *
1091ff4cb8SJean Pihet  *
1191ff4cb8SJean Pihet  * This module exposes the interface to kernel space for specifying
1291ff4cb8SJean Pihet  * per-device PM QoS dependencies. It provides infrastructure for registration
1391ff4cb8SJean Pihet  * of:
1491ff4cb8SJean Pihet  *
1591ff4cb8SJean Pihet  * Dependents on a QoS value : register requests
1691ff4cb8SJean Pihet  * Watchers of QoS value : get notified when target QoS value changes
1791ff4cb8SJean Pihet  *
1891ff4cb8SJean Pihet  * This QoS design is best effort based. Dependents register their QoS needs.
1991ff4cb8SJean Pihet  * Watchers register to keep track of the current QoS needs of the system.
20b66213cdSJean Pihet  * Watchers can register different types of notification callbacks:
21b66213cdSJean Pihet  *  . a per-device notification callback using the dev_pm_qos_*_notifier API.
22b66213cdSJean Pihet  *    The notification chain data is stored in the per-device constraint
23b66213cdSJean Pihet  *    data struct.
24b66213cdSJean Pihet  *  . a system-wide notification callback using the dev_pm_qos_*_global_notifier
25b66213cdSJean Pihet  *    API. The notification chain data is stored in a static variable.
2691ff4cb8SJean Pihet  *
2791ff4cb8SJean Pihet  * Note about the per-device constraint data struct allocation:
2891ff4cb8SJean Pihet  * . The per-device constraints data struct ptr is tored into the device
2991ff4cb8SJean Pihet  *    dev_pm_info.
3091ff4cb8SJean Pihet  * . To minimize the data usage by the per-device constraints, the data struct
3191ff4cb8SJean Pihet  *   is only allocated at the first call to dev_pm_qos_add_request.
3291ff4cb8SJean Pihet  * . The data is later free'd when the device is removed from the system.
3391ff4cb8SJean Pihet  *  . A global mutex protects the constraints users from the data being
3491ff4cb8SJean Pihet  *     allocated and free'd.
3591ff4cb8SJean Pihet  */
3691ff4cb8SJean Pihet 
3791ff4cb8SJean Pihet #include <linux/pm_qos.h>
3891ff4cb8SJean Pihet #include <linux/spinlock.h>
3991ff4cb8SJean Pihet #include <linux/slab.h>
4091ff4cb8SJean Pihet #include <linux/device.h>
4191ff4cb8SJean Pihet #include <linux/mutex.h>
421b6bc32fSPaul Gortmaker #include <linux/export.h>
43e39473d0SRafael J. Wysocki #include <linux/pm_runtime.h>
4437530f2bSRafael J. Wysocki #include <linux/err.h>
4596d9d0b5SSahara #include <trace/events/power.h>
4691ff4cb8SJean Pihet 
4785dc0b8aSRafael J. Wysocki #include "power.h"
4891ff4cb8SJean Pihet 
4991ff4cb8SJean Pihet static DEFINE_MUTEX(dev_pm_qos_mtx);
500f703069SRafael J. Wysocki static DEFINE_MUTEX(dev_pm_qos_sysfs_mtx);
511a9a9152SRafael J. Wysocki 
52b66213cdSJean Pihet static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
53b66213cdSJean Pihet 
541a9a9152SRafael J. Wysocki /**
55ae0fb4b7SRafael J. Wysocki  * __dev_pm_qos_flags - Check PM QoS flags for a given device.
56ae0fb4b7SRafael J. Wysocki  * @dev: Device to check the PM QoS flags for.
57ae0fb4b7SRafael J. Wysocki  * @mask: Flags to check against.
58ae0fb4b7SRafael J. Wysocki  *
59ae0fb4b7SRafael J. Wysocki  * This routine must be called with dev->power.lock held.
60ae0fb4b7SRafael J. Wysocki  */
61ae0fb4b7SRafael J. Wysocki enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask)
62ae0fb4b7SRafael J. Wysocki {
63ae0fb4b7SRafael J. Wysocki 	struct dev_pm_qos *qos = dev->power.qos;
64ae0fb4b7SRafael J. Wysocki 	struct pm_qos_flags *pqf;
65ae0fb4b7SRafael J. Wysocki 	s32 val;
66ae0fb4b7SRafael J. Wysocki 
67f90b8ad8SKrzysztof Kozlowski 	lockdep_assert_held(&dev->power.lock);
68f90b8ad8SKrzysztof Kozlowski 
6937530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(qos))
70ae0fb4b7SRafael J. Wysocki 		return PM_QOS_FLAGS_UNDEFINED;
71ae0fb4b7SRafael J. Wysocki 
72ae0fb4b7SRafael J. Wysocki 	pqf = &qos->flags;
73ae0fb4b7SRafael J. Wysocki 	if (list_empty(&pqf->list))
74ae0fb4b7SRafael J. Wysocki 		return PM_QOS_FLAGS_UNDEFINED;
75ae0fb4b7SRafael J. Wysocki 
76ae0fb4b7SRafael J. Wysocki 	val = pqf->effective_flags & mask;
77ae0fb4b7SRafael J. Wysocki 	if (val)
78ae0fb4b7SRafael J. Wysocki 		return (val == mask) ? PM_QOS_FLAGS_ALL : PM_QOS_FLAGS_SOME;
79ae0fb4b7SRafael J. Wysocki 
80ae0fb4b7SRafael J. Wysocki 	return PM_QOS_FLAGS_NONE;
81ae0fb4b7SRafael J. Wysocki }
82ae0fb4b7SRafael J. Wysocki 
83ae0fb4b7SRafael J. Wysocki /**
84ae0fb4b7SRafael J. Wysocki  * dev_pm_qos_flags - Check PM QoS flags for a given device (locked).
85ae0fb4b7SRafael J. Wysocki  * @dev: Device to check the PM QoS flags for.
86ae0fb4b7SRafael J. Wysocki  * @mask: Flags to check against.
87ae0fb4b7SRafael J. Wysocki  */
88ae0fb4b7SRafael J. Wysocki enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask)
89ae0fb4b7SRafael J. Wysocki {
90ae0fb4b7SRafael J. Wysocki 	unsigned long irqflags;
91ae0fb4b7SRafael J. Wysocki 	enum pm_qos_flags_status ret;
92ae0fb4b7SRafael J. Wysocki 
93ae0fb4b7SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, irqflags);
94ae0fb4b7SRafael J. Wysocki 	ret = __dev_pm_qos_flags(dev, mask);
95ae0fb4b7SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, irqflags);
96ae0fb4b7SRafael J. Wysocki 
97ae0fb4b7SRafael J. Wysocki 	return ret;
98ae0fb4b7SRafael J. Wysocki }
996802771bSLan Tianyu EXPORT_SYMBOL_GPL(dev_pm_qos_flags);
100ae0fb4b7SRafael J. Wysocki 
101ae0fb4b7SRafael J. Wysocki /**
10200dc9ad1SRafael J. Wysocki  * __dev_pm_qos_read_value - Get PM QoS constraint for a given device.
10300dc9ad1SRafael J. Wysocki  * @dev: Device to get the PM QoS constraint value for.
10400dc9ad1SRafael J. Wysocki  *
10500dc9ad1SRafael J. Wysocki  * This routine must be called with dev->power.lock held.
10600dc9ad1SRafael J. Wysocki  */
10700dc9ad1SRafael J. Wysocki s32 __dev_pm_qos_read_value(struct device *dev)
10800dc9ad1SRafael J. Wysocki {
109f90b8ad8SKrzysztof Kozlowski 	lockdep_assert_held(&dev->power.lock);
110f90b8ad8SKrzysztof Kozlowski 
11137530f2bSRafael J. Wysocki 	return IS_ERR_OR_NULL(dev->power.qos) ?
112b02f6695SRafael J. Wysocki 		0 : pm_qos_read_value(&dev->power.qos->resume_latency);
11300dc9ad1SRafael J. Wysocki }
11400dc9ad1SRafael J. Wysocki 
11500dc9ad1SRafael J. Wysocki /**
11600dc9ad1SRafael J. Wysocki  * dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked).
1171a9a9152SRafael J. Wysocki  * @dev: Device to get the PM QoS constraint value for.
1181a9a9152SRafael J. Wysocki  */
1191a9a9152SRafael J. Wysocki s32 dev_pm_qos_read_value(struct device *dev)
1201a9a9152SRafael J. Wysocki {
1211a9a9152SRafael J. Wysocki 	unsigned long flags;
12200dc9ad1SRafael J. Wysocki 	s32 ret;
1231a9a9152SRafael J. Wysocki 
1241a9a9152SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
12500dc9ad1SRafael J. Wysocki 	ret = __dev_pm_qos_read_value(dev);
1261a9a9152SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
1271a9a9152SRafael J. Wysocki 
1281a9a9152SRafael J. Wysocki 	return ret;
1291a9a9152SRafael J. Wysocki }
1301a9a9152SRafael J. Wysocki 
131ae0fb4b7SRafael J. Wysocki /**
132ae0fb4b7SRafael J. Wysocki  * apply_constraint - Add/modify/remove device PM QoS request.
133ae0fb4b7SRafael J. Wysocki  * @req: Constraint request to apply
134ae0fb4b7SRafael J. Wysocki  * @action: Action to perform (add/update/remove).
135ae0fb4b7SRafael J. Wysocki  * @value: Value to assign to the QoS request.
136b66213cdSJean Pihet  *
137b66213cdSJean Pihet  * Internal function to update the constraints list using the PM QoS core
138b66213cdSJean Pihet  * code and if needed call the per-device and the global notification
139b66213cdSJean Pihet  * callbacks
140b66213cdSJean Pihet  */
141b66213cdSJean Pihet static int apply_constraint(struct dev_pm_qos_request *req,
142ae0fb4b7SRafael J. Wysocki 			    enum pm_qos_req_action action, s32 value)
143b66213cdSJean Pihet {
144ae0fb4b7SRafael J. Wysocki 	struct dev_pm_qos *qos = req->dev->power.qos;
145ae0fb4b7SRafael J. Wysocki 	int ret;
146b66213cdSJean Pihet 
147ae0fb4b7SRafael J. Wysocki 	switch(req->type) {
148b02f6695SRafael J. Wysocki 	case DEV_PM_QOS_RESUME_LATENCY:
149b02f6695SRafael J. Wysocki 		ret = pm_qos_update_target(&qos->resume_latency,
150b02f6695SRafael J. Wysocki 					   &req->data.pnode, action, value);
151b66213cdSJean Pihet 		if (ret) {
152b02f6695SRafael J. Wysocki 			value = pm_qos_read_value(&qos->resume_latency);
153b66213cdSJean Pihet 			blocking_notifier_call_chain(&dev_pm_notifiers,
154ae0fb4b7SRafael J. Wysocki 						     (unsigned long)value,
155b66213cdSJean Pihet 						     req);
156b66213cdSJean Pihet 		}
157ae0fb4b7SRafael J. Wysocki 		break;
1582d984ad1SRafael J. Wysocki 	case DEV_PM_QOS_LATENCY_TOLERANCE:
1592d984ad1SRafael J. Wysocki 		ret = pm_qos_update_target(&qos->latency_tolerance,
1602d984ad1SRafael J. Wysocki 					   &req->data.pnode, action, value);
1612d984ad1SRafael J. Wysocki 		if (ret) {
1622d984ad1SRafael J. Wysocki 			value = pm_qos_read_value(&qos->latency_tolerance);
1632d984ad1SRafael J. Wysocki 			req->dev->power.set_latency_tolerance(req->dev, value);
1642d984ad1SRafael J. Wysocki 		}
1652d984ad1SRafael J. Wysocki 		break;
166ae0fb4b7SRafael J. Wysocki 	case DEV_PM_QOS_FLAGS:
167ae0fb4b7SRafael J. Wysocki 		ret = pm_qos_update_flags(&qos->flags, &req->data.flr,
168ae0fb4b7SRafael J. Wysocki 					  action, value);
169ae0fb4b7SRafael J. Wysocki 		break;
170ae0fb4b7SRafael J. Wysocki 	default:
171ae0fb4b7SRafael J. Wysocki 		ret = -EINVAL;
172ae0fb4b7SRafael J. Wysocki 	}
173b66213cdSJean Pihet 
174b66213cdSJean Pihet 	return ret;
175b66213cdSJean Pihet }
17691ff4cb8SJean Pihet 
17791ff4cb8SJean Pihet /*
17891ff4cb8SJean Pihet  * dev_pm_qos_constraints_allocate
17991ff4cb8SJean Pihet  * @dev: device to allocate data for
18091ff4cb8SJean Pihet  *
18191ff4cb8SJean Pihet  * Called at the first call to add_request, for constraint data allocation
18291ff4cb8SJean Pihet  * Must be called with the dev_pm_qos_mtx mutex held
18391ff4cb8SJean Pihet  */
18491ff4cb8SJean Pihet static int dev_pm_qos_constraints_allocate(struct device *dev)
18591ff4cb8SJean Pihet {
1865f986c59SRafael J. Wysocki 	struct dev_pm_qos *qos;
18791ff4cb8SJean Pihet 	struct pm_qos_constraints *c;
18891ff4cb8SJean Pihet 	struct blocking_notifier_head *n;
18991ff4cb8SJean Pihet 
1905f986c59SRafael J. Wysocki 	qos = kzalloc(sizeof(*qos), GFP_KERNEL);
1915f986c59SRafael J. Wysocki 	if (!qos)
19291ff4cb8SJean Pihet 		return -ENOMEM;
19391ff4cb8SJean Pihet 
19491ff4cb8SJean Pihet 	n = kzalloc(sizeof(*n), GFP_KERNEL);
19591ff4cb8SJean Pihet 	if (!n) {
1965f986c59SRafael J. Wysocki 		kfree(qos);
19791ff4cb8SJean Pihet 		return -ENOMEM;
19891ff4cb8SJean Pihet 	}
19991ff4cb8SJean Pihet 	BLOCKING_INIT_NOTIFIER_HEAD(n);
20091ff4cb8SJean Pihet 
201b02f6695SRafael J. Wysocki 	c = &qos->resume_latency;
2021a9a9152SRafael J. Wysocki 	plist_head_init(&c->list);
203b02f6695SRafael J. Wysocki 	c->target_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
204b02f6695SRafael J. Wysocki 	c->default_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
205327adaedSRafael J. Wysocki 	c->no_constraint_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
2061a9a9152SRafael J. Wysocki 	c->type = PM_QOS_MIN;
2071a9a9152SRafael J. Wysocki 	c->notifiers = n;
2081a9a9152SRafael J. Wysocki 
2092d984ad1SRafael J. Wysocki 	c = &qos->latency_tolerance;
2102d984ad1SRafael J. Wysocki 	plist_head_init(&c->list);
2112d984ad1SRafael J. Wysocki 	c->target_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE;
2122d984ad1SRafael J. Wysocki 	c->default_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE;
2132d984ad1SRafael J. Wysocki 	c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT;
2142d984ad1SRafael J. Wysocki 	c->type = PM_QOS_MIN;
2152d984ad1SRafael J. Wysocki 
216ae0fb4b7SRafael J. Wysocki 	INIT_LIST_HEAD(&qos->flags.list);
217ae0fb4b7SRafael J. Wysocki 
2181a9a9152SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
2195f986c59SRafael J. Wysocki 	dev->power.qos = qos;
2201a9a9152SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
22191ff4cb8SJean Pihet 
22291ff4cb8SJean Pihet 	return 0;
22391ff4cb8SJean Pihet }
22491ff4cb8SJean Pihet 
22537530f2bSRafael J. Wysocki static void __dev_pm_qos_hide_latency_limit(struct device *dev);
22637530f2bSRafael J. Wysocki static void __dev_pm_qos_hide_flags(struct device *dev);
22791ff4cb8SJean Pihet 
22891ff4cb8SJean Pihet /**
22991ff4cb8SJean Pihet  * dev_pm_qos_constraints_destroy
23091ff4cb8SJean Pihet  * @dev: target device
23191ff4cb8SJean Pihet  *
2321a9a9152SRafael J. Wysocki  * Called from the device PM subsystem on device removal under device_pm_lock().
23391ff4cb8SJean Pihet  */
23491ff4cb8SJean Pihet void dev_pm_qos_constraints_destroy(struct device *dev)
23591ff4cb8SJean Pihet {
2365f986c59SRafael J. Wysocki 	struct dev_pm_qos *qos;
23791ff4cb8SJean Pihet 	struct dev_pm_qos_request *req, *tmp;
2381a9a9152SRafael J. Wysocki 	struct pm_qos_constraints *c;
23935546bd4SRafael J. Wysocki 	struct pm_qos_flags *f;
24091ff4cb8SJean Pihet 
2410f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_sysfs_mtx);
24237530f2bSRafael J. Wysocki 
24385dc0b8aSRafael J. Wysocki 	/*
24435546bd4SRafael J. Wysocki 	 * If the device's PM QoS resume latency limit or PM QoS flags have been
24535546bd4SRafael J. Wysocki 	 * exposed to user space, they have to be hidden at this point.
24685dc0b8aSRafael J. Wysocki 	 */
247b02f6695SRafael J. Wysocki 	pm_qos_sysfs_remove_resume_latency(dev);
2480f703069SRafael J. Wysocki 	pm_qos_sysfs_remove_flags(dev);
2490f703069SRafael J. Wysocki 
2500f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
2510f703069SRafael J. Wysocki 
25237530f2bSRafael J. Wysocki 	__dev_pm_qos_hide_latency_limit(dev);
25337530f2bSRafael J. Wysocki 	__dev_pm_qos_hide_flags(dev);
25485dc0b8aSRafael J. Wysocki 
2555f986c59SRafael J. Wysocki 	qos = dev->power.qos;
2565f986c59SRafael J. Wysocki 	if (!qos)
2571a9a9152SRafael J. Wysocki 		goto out;
2581a9a9152SRafael J. Wysocki 
25935546bd4SRafael J. Wysocki 	/* Flush the constraints lists for the device. */
260b02f6695SRafael J. Wysocki 	c = &qos->resume_latency;
261021c870bSRafael J. Wysocki 	plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
26291ff4cb8SJean Pihet 		/*
263b66213cdSJean Pihet 		 * Update constraints list and call the notification
26491ff4cb8SJean Pihet 		 * callbacks if needed
26591ff4cb8SJean Pihet 		 */
2661a9a9152SRafael J. Wysocki 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
26791ff4cb8SJean Pihet 		memset(req, 0, sizeof(*req));
26891ff4cb8SJean Pihet 	}
2692d984ad1SRafael J. Wysocki 	c = &qos->latency_tolerance;
2702d984ad1SRafael J. Wysocki 	plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
2712d984ad1SRafael J. Wysocki 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
2722d984ad1SRafael J. Wysocki 		memset(req, 0, sizeof(*req));
2732d984ad1SRafael J. Wysocki 	}
27435546bd4SRafael J. Wysocki 	f = &qos->flags;
27535546bd4SRafael J. Wysocki 	list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) {
27635546bd4SRafael J. Wysocki 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
27735546bd4SRafael J. Wysocki 		memset(req, 0, sizeof(*req));
27835546bd4SRafael J. Wysocki 	}
27991ff4cb8SJean Pihet 
2801a9a9152SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
28137530f2bSRafael J. Wysocki 	dev->power.qos = ERR_PTR(-ENODEV);
2821a9a9152SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
28391ff4cb8SJean Pihet 
284e84b4a84SJohn Keeping 	kfree(qos->resume_latency.notifiers);
2859eaee2cdSLan,Tianyu 	kfree(qos);
2861a9a9152SRafael J. Wysocki 
2871a9a9152SRafael J. Wysocki  out:
28891ff4cb8SJean Pihet 	mutex_unlock(&dev_pm_qos_mtx);
2890f703069SRafael J. Wysocki 
2900f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
29191ff4cb8SJean Pihet }
29291ff4cb8SJean Pihet 
2932d984ad1SRafael J. Wysocki static bool dev_pm_qos_invalid_request(struct device *dev,
2942d984ad1SRafael J. Wysocki 				       struct dev_pm_qos_request *req)
2952d984ad1SRafael J. Wysocki {
2962d984ad1SRafael J. Wysocki 	return !req || (req->type == DEV_PM_QOS_LATENCY_TOLERANCE
2972d984ad1SRafael J. Wysocki 			&& !dev->power.set_latency_tolerance);
2982d984ad1SRafael J. Wysocki }
2992d984ad1SRafael J. Wysocki 
3002d984ad1SRafael J. Wysocki static int __dev_pm_qos_add_request(struct device *dev,
3012d984ad1SRafael J. Wysocki 				    struct dev_pm_qos_request *req,
3022d984ad1SRafael J. Wysocki 				    enum dev_pm_qos_req_type type, s32 value)
3032d984ad1SRafael J. Wysocki {
3042d984ad1SRafael J. Wysocki 	int ret = 0;
3052d984ad1SRafael J. Wysocki 
3062d984ad1SRafael J. Wysocki 	if (!dev || dev_pm_qos_invalid_request(dev, req))
3072d984ad1SRafael J. Wysocki 		return -EINVAL;
3082d984ad1SRafael J. Wysocki 
3092d984ad1SRafael J. Wysocki 	if (WARN(dev_pm_qos_request_active(req),
3102d984ad1SRafael J. Wysocki 		 "%s() called for already added request\n", __func__))
3112d984ad1SRafael J. Wysocki 		return -EINVAL;
3122d984ad1SRafael J. Wysocki 
3132d984ad1SRafael J. Wysocki 	if (IS_ERR(dev->power.qos))
3142d984ad1SRafael J. Wysocki 		ret = -ENODEV;
3152d984ad1SRafael J. Wysocki 	else if (!dev->power.qos)
3162d984ad1SRafael J. Wysocki 		ret = dev_pm_qos_constraints_allocate(dev);
3172d984ad1SRafael J. Wysocki 
3182d984ad1SRafael J. Wysocki 	trace_dev_pm_qos_add_request(dev_name(dev), type, value);
3192d984ad1SRafael J. Wysocki 	if (!ret) {
3202d984ad1SRafael J. Wysocki 		req->dev = dev;
3212d984ad1SRafael J. Wysocki 		req->type = type;
3222d984ad1SRafael J. Wysocki 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
3232d984ad1SRafael J. Wysocki 	}
3242d984ad1SRafael J. Wysocki 	return ret;
3252d984ad1SRafael J. Wysocki }
3262d984ad1SRafael J. Wysocki 
32791ff4cb8SJean Pihet /**
32891ff4cb8SJean Pihet  * dev_pm_qos_add_request - inserts new qos request into the list
32991ff4cb8SJean Pihet  * @dev: target device for the constraint
33091ff4cb8SJean Pihet  * @req: pointer to a preallocated handle
331ae0fb4b7SRafael J. Wysocki  * @type: type of the request
33291ff4cb8SJean Pihet  * @value: defines the qos request
33391ff4cb8SJean Pihet  *
33491ff4cb8SJean Pihet  * This function inserts a new entry in the device constraints list of
33591ff4cb8SJean Pihet  * requested qos performance characteristics. It recomputes the aggregate
33691ff4cb8SJean Pihet  * QoS expectations of parameters and initializes the dev_pm_qos_request
33791ff4cb8SJean Pihet  * handle.  Caller needs to save this handle for later use in updates and
33891ff4cb8SJean Pihet  * removal.
33991ff4cb8SJean Pihet  *
34091ff4cb8SJean Pihet  * Returns 1 if the aggregated constraint value has changed,
34191ff4cb8SJean Pihet  * 0 if the aggregated constraint value has not changed,
3421a9a9152SRafael J. Wysocki  * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
3431a9a9152SRafael J. Wysocki  * to allocate for data structures, -ENODEV if the device has just been removed
3441a9a9152SRafael J. Wysocki  * from the system.
345436ede89SRafael J. Wysocki  *
346436ede89SRafael J. Wysocki  * Callers should ensure that the target device is not RPM_SUSPENDED before
347436ede89SRafael J. Wysocki  * using this function for requests of type DEV_PM_QOS_FLAGS.
34891ff4cb8SJean Pihet  */
34991ff4cb8SJean Pihet int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
350ae0fb4b7SRafael J. Wysocki 			   enum dev_pm_qos_req_type type, s32 value)
35191ff4cb8SJean Pihet {
3522d984ad1SRafael J. Wysocki 	int ret;
35391ff4cb8SJean Pihet 
3541a9a9152SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
3552d984ad1SRafael J. Wysocki 	ret = __dev_pm_qos_add_request(dev, req, type, value);
35691ff4cb8SJean Pihet 	mutex_unlock(&dev_pm_qos_mtx);
35791ff4cb8SJean Pihet 	return ret;
35891ff4cb8SJean Pihet }
35991ff4cb8SJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
36091ff4cb8SJean Pihet 
36191ff4cb8SJean Pihet /**
362e39473d0SRafael J. Wysocki  * __dev_pm_qos_update_request - Modify an existing device PM QoS request.
363e39473d0SRafael J. Wysocki  * @req : PM QoS request to modify.
364e39473d0SRafael J. Wysocki  * @new_value: New value to request.
365e39473d0SRafael J. Wysocki  */
366e39473d0SRafael J. Wysocki static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req,
367e39473d0SRafael J. Wysocki 				       s32 new_value)
368e39473d0SRafael J. Wysocki {
369e39473d0SRafael J. Wysocki 	s32 curr_value;
370e39473d0SRafael J. Wysocki 	int ret = 0;
371e39473d0SRafael J. Wysocki 
372b81ea1b5SRafael J. Wysocki 	if (!req) /*guard against callers passing in null */
373b81ea1b5SRafael J. Wysocki 		return -EINVAL;
374b81ea1b5SRafael J. Wysocki 
375b81ea1b5SRafael J. Wysocki 	if (WARN(!dev_pm_qos_request_active(req),
376b81ea1b5SRafael J. Wysocki 		 "%s() called for unknown object\n", __func__))
377b81ea1b5SRafael J. Wysocki 		return -EINVAL;
378b81ea1b5SRafael J. Wysocki 
37937530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(req->dev->power.qos))
380e39473d0SRafael J. Wysocki 		return -ENODEV;
381e39473d0SRafael J. Wysocki 
382e39473d0SRafael J. Wysocki 	switch(req->type) {
383b02f6695SRafael J. Wysocki 	case DEV_PM_QOS_RESUME_LATENCY:
3842d984ad1SRafael J. Wysocki 	case DEV_PM_QOS_LATENCY_TOLERANCE:
385e39473d0SRafael J. Wysocki 		curr_value = req->data.pnode.prio;
386e39473d0SRafael J. Wysocki 		break;
387e39473d0SRafael J. Wysocki 	case DEV_PM_QOS_FLAGS:
388e39473d0SRafael J. Wysocki 		curr_value = req->data.flr.flags;
389e39473d0SRafael J. Wysocki 		break;
390e39473d0SRafael J. Wysocki 	default:
391e39473d0SRafael J. Wysocki 		return -EINVAL;
392e39473d0SRafael J. Wysocki 	}
393e39473d0SRafael J. Wysocki 
39496d9d0b5SSahara 	trace_dev_pm_qos_update_request(dev_name(req->dev), req->type,
39596d9d0b5SSahara 					new_value);
396e39473d0SRafael J. Wysocki 	if (curr_value != new_value)
397e39473d0SRafael J. Wysocki 		ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value);
398e39473d0SRafael J. Wysocki 
399e39473d0SRafael J. Wysocki 	return ret;
400e39473d0SRafael J. Wysocki }
401e39473d0SRafael J. Wysocki 
402e39473d0SRafael J. Wysocki /**
40391ff4cb8SJean Pihet  * dev_pm_qos_update_request - modifies an existing qos request
40491ff4cb8SJean Pihet  * @req : handle to list element holding a dev_pm_qos request to use
40591ff4cb8SJean Pihet  * @new_value: defines the qos request
40691ff4cb8SJean Pihet  *
40791ff4cb8SJean Pihet  * Updates an existing dev PM qos request along with updating the
40891ff4cb8SJean Pihet  * target value.
40991ff4cb8SJean Pihet  *
41091ff4cb8SJean Pihet  * Attempts are made to make this code callable on hot code paths.
41191ff4cb8SJean Pihet  *
41291ff4cb8SJean Pihet  * Returns 1 if the aggregated constraint value has changed,
41391ff4cb8SJean Pihet  * 0 if the aggregated constraint value has not changed,
41491ff4cb8SJean Pihet  * -EINVAL in case of wrong parameters, -ENODEV if the device has been
41591ff4cb8SJean Pihet  * removed from the system
416436ede89SRafael J. Wysocki  *
417436ede89SRafael J. Wysocki  * Callers should ensure that the target device is not RPM_SUSPENDED before
418436ede89SRafael J. Wysocki  * using this function for requests of type DEV_PM_QOS_FLAGS.
41991ff4cb8SJean Pihet  */
420e39473d0SRafael J. Wysocki int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value)
42191ff4cb8SJean Pihet {
422e39473d0SRafael J. Wysocki 	int ret;
42391ff4cb8SJean Pihet 
424b81ea1b5SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
425b81ea1b5SRafael J. Wysocki 	ret = __dev_pm_qos_update_request(req, new_value);
426b81ea1b5SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
427b81ea1b5SRafael J. Wysocki 	return ret;
428b81ea1b5SRafael J. Wysocki }
429b81ea1b5SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
430b81ea1b5SRafael J. Wysocki 
431b81ea1b5SRafael J. Wysocki static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
432b81ea1b5SRafael J. Wysocki {
43337530f2bSRafael J. Wysocki 	int ret;
434b81ea1b5SRafael J. Wysocki 
43591ff4cb8SJean Pihet 	if (!req) /*guard against callers passing in null */
43691ff4cb8SJean Pihet 		return -EINVAL;
43791ff4cb8SJean Pihet 
438af4c720eSGuennadi Liakhovetski 	if (WARN(!dev_pm_qos_request_active(req),
439af4c720eSGuennadi Liakhovetski 		 "%s() called for unknown object\n", __func__))
44091ff4cb8SJean Pihet 		return -EINVAL;
44191ff4cb8SJean Pihet 
44237530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(req->dev->power.qos))
44337530f2bSRafael J. Wysocki 		return -ENODEV;
44437530f2bSRafael J. Wysocki 
44596d9d0b5SSahara 	trace_dev_pm_qos_remove_request(dev_name(req->dev), req->type,
44696d9d0b5SSahara 					PM_QOS_DEFAULT_VALUE);
44737530f2bSRafael J. Wysocki 	ret = apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
448b81ea1b5SRafael J. Wysocki 	memset(req, 0, sizeof(*req));
44991ff4cb8SJean Pihet 	return ret;
45091ff4cb8SJean Pihet }
45191ff4cb8SJean Pihet 
45291ff4cb8SJean Pihet /**
45391ff4cb8SJean Pihet  * dev_pm_qos_remove_request - modifies an existing qos request
45491ff4cb8SJean Pihet  * @req: handle to request list element
45591ff4cb8SJean Pihet  *
45691ff4cb8SJean Pihet  * Will remove pm qos request from the list of constraints and
45791ff4cb8SJean Pihet  * recompute the current target value. Call this on slow code paths.
45891ff4cb8SJean Pihet  *
45991ff4cb8SJean Pihet  * Returns 1 if the aggregated constraint value has changed,
46091ff4cb8SJean Pihet  * 0 if the aggregated constraint value has not changed,
46191ff4cb8SJean Pihet  * -EINVAL in case of wrong parameters, -ENODEV if the device has been
46291ff4cb8SJean Pihet  * removed from the system
463436ede89SRafael J. Wysocki  *
464436ede89SRafael J. Wysocki  * Callers should ensure that the target device is not RPM_SUSPENDED before
465436ede89SRafael J. Wysocki  * using this function for requests of type DEV_PM_QOS_FLAGS.
46691ff4cb8SJean Pihet  */
46791ff4cb8SJean Pihet int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
46891ff4cb8SJean Pihet {
469b81ea1b5SRafael J. Wysocki 	int ret;
47091ff4cb8SJean Pihet 
47191ff4cb8SJean Pihet 	mutex_lock(&dev_pm_qos_mtx);
472b81ea1b5SRafael J. Wysocki 	ret = __dev_pm_qos_remove_request(req);
47391ff4cb8SJean Pihet 	mutex_unlock(&dev_pm_qos_mtx);
47491ff4cb8SJean Pihet 	return ret;
47591ff4cb8SJean Pihet }
47691ff4cb8SJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
47791ff4cb8SJean Pihet 
47891ff4cb8SJean Pihet /**
47991ff4cb8SJean Pihet  * dev_pm_qos_add_notifier - sets notification entry for changes to target value
48091ff4cb8SJean Pihet  * of per-device PM QoS constraints
48191ff4cb8SJean Pihet  *
48291ff4cb8SJean Pihet  * @dev: target device for the constraint
48391ff4cb8SJean Pihet  * @notifier: notifier block managed by caller.
48491ff4cb8SJean Pihet  *
48591ff4cb8SJean Pihet  * Will register the notifier into a notification chain that gets called
48691ff4cb8SJean Pihet  * upon changes to the target value for the device.
48723e0fc5aSRafael J. Wysocki  *
48823e0fc5aSRafael J. Wysocki  * If the device's constraints object doesn't exist when this routine is called,
48923e0fc5aSRafael J. Wysocki  * it will be created (or error code will be returned if that fails).
49091ff4cb8SJean Pihet  */
49191ff4cb8SJean Pihet int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
49291ff4cb8SJean Pihet {
49323e0fc5aSRafael J. Wysocki 	int ret = 0;
49491ff4cb8SJean Pihet 
49591ff4cb8SJean Pihet 	mutex_lock(&dev_pm_qos_mtx);
49691ff4cb8SJean Pihet 
49737530f2bSRafael J. Wysocki 	if (IS_ERR(dev->power.qos))
49837530f2bSRafael J. Wysocki 		ret = -ENODEV;
49937530f2bSRafael J. Wysocki 	else if (!dev->power.qos)
50037530f2bSRafael J. Wysocki 		ret = dev_pm_qos_constraints_allocate(dev);
50123e0fc5aSRafael J. Wysocki 
50223e0fc5aSRafael J. Wysocki 	if (!ret)
503b02f6695SRafael J. Wysocki 		ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers,
504b02f6695SRafael J. Wysocki 						       notifier);
50591ff4cb8SJean Pihet 
50691ff4cb8SJean Pihet 	mutex_unlock(&dev_pm_qos_mtx);
50723e0fc5aSRafael J. Wysocki 	return ret;
50891ff4cb8SJean Pihet }
50991ff4cb8SJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
51091ff4cb8SJean Pihet 
51191ff4cb8SJean Pihet /**
51291ff4cb8SJean Pihet  * dev_pm_qos_remove_notifier - deletes notification for changes to target value
51391ff4cb8SJean Pihet  * of per-device PM QoS constraints
51491ff4cb8SJean Pihet  *
51591ff4cb8SJean Pihet  * @dev: target device for the constraint
51691ff4cb8SJean Pihet  * @notifier: notifier block to be removed.
51791ff4cb8SJean Pihet  *
51891ff4cb8SJean Pihet  * Will remove the notifier from the notification chain that gets called
51991ff4cb8SJean Pihet  * upon changes to the target value.
52091ff4cb8SJean Pihet  */
52191ff4cb8SJean Pihet int dev_pm_qos_remove_notifier(struct device *dev,
52291ff4cb8SJean Pihet 			       struct notifier_block *notifier)
52391ff4cb8SJean Pihet {
52491ff4cb8SJean Pihet 	int retval = 0;
52591ff4cb8SJean Pihet 
52691ff4cb8SJean Pihet 	mutex_lock(&dev_pm_qos_mtx);
52791ff4cb8SJean Pihet 
5281a9a9152SRafael J. Wysocki 	/* Silently return if the constraints object is not present. */
52937530f2bSRafael J. Wysocki 	if (!IS_ERR_OR_NULL(dev->power.qos))
530b02f6695SRafael J. Wysocki 		retval = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers,
53191ff4cb8SJean Pihet 							    notifier);
53291ff4cb8SJean Pihet 
53391ff4cb8SJean Pihet 	mutex_unlock(&dev_pm_qos_mtx);
53491ff4cb8SJean Pihet 	return retval;
53591ff4cb8SJean Pihet }
53691ff4cb8SJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
537b66213cdSJean Pihet 
538b66213cdSJean Pihet /**
539b66213cdSJean Pihet  * dev_pm_qos_add_global_notifier - sets notification entry for changes to
540b66213cdSJean Pihet  * target value of the PM QoS constraints for any device
541b66213cdSJean Pihet  *
542b66213cdSJean Pihet  * @notifier: notifier block managed by caller.
543b66213cdSJean Pihet  *
544b66213cdSJean Pihet  * Will register the notifier into a notification chain that gets called
545b66213cdSJean Pihet  * upon changes to the target value for any device.
546b66213cdSJean Pihet  */
547b66213cdSJean Pihet int dev_pm_qos_add_global_notifier(struct notifier_block *notifier)
548b66213cdSJean Pihet {
549b66213cdSJean Pihet 	return blocking_notifier_chain_register(&dev_pm_notifiers, notifier);
550b66213cdSJean Pihet }
551b66213cdSJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier);
552b66213cdSJean Pihet 
553b66213cdSJean Pihet /**
554b66213cdSJean Pihet  * dev_pm_qos_remove_global_notifier - deletes notification for changes to
555b66213cdSJean Pihet  * target value of PM QoS constraints for any device
556b66213cdSJean Pihet  *
557b66213cdSJean Pihet  * @notifier: notifier block to be removed.
558b66213cdSJean Pihet  *
559b66213cdSJean Pihet  * Will remove the notifier from the notification chain that gets called
560b66213cdSJean Pihet  * upon changes to the target value for any device.
561b66213cdSJean Pihet  */
562b66213cdSJean Pihet int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier)
563b66213cdSJean Pihet {
564b66213cdSJean Pihet 	return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier);
565b66213cdSJean Pihet }
566b66213cdSJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier);
56740a5f8beSRafael J. Wysocki 
56840a5f8beSRafael J. Wysocki /**
56940a5f8beSRafael J. Wysocki  * dev_pm_qos_add_ancestor_request - Add PM QoS request for device's ancestor.
57040a5f8beSRafael J. Wysocki  * @dev: Device whose ancestor to add the request for.
57140a5f8beSRafael J. Wysocki  * @req: Pointer to the preallocated handle.
57271d821fdSRafael J. Wysocki  * @type: Type of the request.
57340a5f8beSRafael J. Wysocki  * @value: Constraint latency value.
57440a5f8beSRafael J. Wysocki  */
57540a5f8beSRafael J. Wysocki int dev_pm_qos_add_ancestor_request(struct device *dev,
57671d821fdSRafael J. Wysocki 				    struct dev_pm_qos_request *req,
57771d821fdSRafael J. Wysocki 				    enum dev_pm_qos_req_type type, s32 value)
57840a5f8beSRafael J. Wysocki {
57940a5f8beSRafael J. Wysocki 	struct device *ancestor = dev->parent;
5804ce47802SRafael J. Wysocki 	int ret = -ENODEV;
58140a5f8beSRafael J. Wysocki 
58271d821fdSRafael J. Wysocki 	switch (type) {
58371d821fdSRafael J. Wysocki 	case DEV_PM_QOS_RESUME_LATENCY:
58440a5f8beSRafael J. Wysocki 		while (ancestor && !ancestor->power.ignore_children)
58540a5f8beSRafael J. Wysocki 			ancestor = ancestor->parent;
58640a5f8beSRafael J. Wysocki 
58771d821fdSRafael J. Wysocki 		break;
58871d821fdSRafael J. Wysocki 	case DEV_PM_QOS_LATENCY_TOLERANCE:
58971d821fdSRafael J. Wysocki 		while (ancestor && !ancestor->power.set_latency_tolerance)
59071d821fdSRafael J. Wysocki 			ancestor = ancestor->parent;
59171d821fdSRafael J. Wysocki 
59271d821fdSRafael J. Wysocki 		break;
59371d821fdSRafael J. Wysocki 	default:
59471d821fdSRafael J. Wysocki 		ancestor = NULL;
59571d821fdSRafael J. Wysocki 	}
59640a5f8beSRafael J. Wysocki 	if (ancestor)
59771d821fdSRafael J. Wysocki 		ret = dev_pm_qos_add_request(ancestor, req, type, value);
59840a5f8beSRafael J. Wysocki 
5994ce47802SRafael J. Wysocki 	if (ret < 0)
60040a5f8beSRafael J. Wysocki 		req->dev = NULL;
60140a5f8beSRafael J. Wysocki 
6024ce47802SRafael J. Wysocki 	return ret;
60340a5f8beSRafael J. Wysocki }
60440a5f8beSRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request);
60585dc0b8aSRafael J. Wysocki 
606e39473d0SRafael J. Wysocki static void __dev_pm_qos_drop_user_request(struct device *dev,
607e39473d0SRafael J. Wysocki 					   enum dev_pm_qos_req_type type)
60885dc0b8aSRafael J. Wysocki {
609b81ea1b5SRafael J. Wysocki 	struct dev_pm_qos_request *req = NULL;
610b81ea1b5SRafael J. Wysocki 
611e39473d0SRafael J. Wysocki 	switch(type) {
612b02f6695SRafael J. Wysocki 	case DEV_PM_QOS_RESUME_LATENCY:
613b02f6695SRafael J. Wysocki 		req = dev->power.qos->resume_latency_req;
614b02f6695SRafael J. Wysocki 		dev->power.qos->resume_latency_req = NULL;
615e39473d0SRafael J. Wysocki 		break;
6162d984ad1SRafael J. Wysocki 	case DEV_PM_QOS_LATENCY_TOLERANCE:
6172d984ad1SRafael J. Wysocki 		req = dev->power.qos->latency_tolerance_req;
6182d984ad1SRafael J. Wysocki 		dev->power.qos->latency_tolerance_req = NULL;
6192d984ad1SRafael J. Wysocki 		break;
620e39473d0SRafael J. Wysocki 	case DEV_PM_QOS_FLAGS:
621b81ea1b5SRafael J. Wysocki 		req = dev->power.qos->flags_req;
622e39473d0SRafael J. Wysocki 		dev->power.qos->flags_req = NULL;
623e39473d0SRafael J. Wysocki 		break;
624e39473d0SRafael J. Wysocki 	}
625b81ea1b5SRafael J. Wysocki 	__dev_pm_qos_remove_request(req);
626b81ea1b5SRafael J. Wysocki 	kfree(req);
62785dc0b8aSRafael J. Wysocki }
62885dc0b8aSRafael J. Wysocki 
6290f703069SRafael J. Wysocki static void dev_pm_qos_drop_user_request(struct device *dev,
6300f703069SRafael J. Wysocki 					 enum dev_pm_qos_req_type type)
6310f703069SRafael J. Wysocki {
6320f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
6330f703069SRafael J. Wysocki 	__dev_pm_qos_drop_user_request(dev, type);
6340f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
6350f703069SRafael J. Wysocki }
6360f703069SRafael J. Wysocki 
63785dc0b8aSRafael J. Wysocki /**
63885dc0b8aSRafael J. Wysocki  * dev_pm_qos_expose_latency_limit - Expose PM QoS latency limit to user space.
63985dc0b8aSRafael J. Wysocki  * @dev: Device whose PM QoS latency limit is to be exposed to user space.
64085dc0b8aSRafael J. Wysocki  * @value: Initial value of the latency limit.
64185dc0b8aSRafael J. Wysocki  */
64285dc0b8aSRafael J. Wysocki int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value)
64385dc0b8aSRafael J. Wysocki {
64485dc0b8aSRafael J. Wysocki 	struct dev_pm_qos_request *req;
64585dc0b8aSRafael J. Wysocki 	int ret;
64685dc0b8aSRafael J. Wysocki 
64785dc0b8aSRafael J. Wysocki 	if (!device_is_registered(dev) || value < 0)
64885dc0b8aSRafael J. Wysocki 		return -EINVAL;
64985dc0b8aSRafael J. Wysocki 
65085dc0b8aSRafael J. Wysocki 	req = kzalloc(sizeof(*req), GFP_KERNEL);
65185dc0b8aSRafael J. Wysocki 	if (!req)
65285dc0b8aSRafael J. Wysocki 		return -ENOMEM;
65385dc0b8aSRafael J. Wysocki 
654b02f6695SRafael J. Wysocki 	ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_RESUME_LATENCY, value);
655b81ea1b5SRafael J. Wysocki 	if (ret < 0) {
656b81ea1b5SRafael J. Wysocki 		kfree(req);
65785dc0b8aSRafael J. Wysocki 		return ret;
658b81ea1b5SRafael J. Wysocki 	}
659b81ea1b5SRafael J. Wysocki 
6600f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_sysfs_mtx);
6610f703069SRafael J. Wysocki 
662b81ea1b5SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
663b81ea1b5SRafael J. Wysocki 
66437530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->power.qos))
665b81ea1b5SRafael J. Wysocki 		ret = -ENODEV;
666b02f6695SRafael J. Wysocki 	else if (dev->power.qos->resume_latency_req)
667b81ea1b5SRafael J. Wysocki 		ret = -EEXIST;
668b81ea1b5SRafael J. Wysocki 
669b81ea1b5SRafael J. Wysocki 	if (ret < 0) {
670b81ea1b5SRafael J. Wysocki 		__dev_pm_qos_remove_request(req);
671b81ea1b5SRafael J. Wysocki 		kfree(req);
6720f703069SRafael J. Wysocki 		mutex_unlock(&dev_pm_qos_mtx);
673b81ea1b5SRafael J. Wysocki 		goto out;
674b81ea1b5SRafael J. Wysocki 	}
675b02f6695SRafael J. Wysocki 	dev->power.qos->resume_latency_req = req;
6760f703069SRafael J. Wysocki 
6770f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
6780f703069SRafael J. Wysocki 
679b02f6695SRafael J. Wysocki 	ret = pm_qos_sysfs_add_resume_latency(dev);
68085dc0b8aSRafael J. Wysocki 	if (ret)
681b02f6695SRafael J. Wysocki 		dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_RESUME_LATENCY);
68285dc0b8aSRafael J. Wysocki 
683b81ea1b5SRafael J. Wysocki  out:
6840f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
68585dc0b8aSRafael J. Wysocki 	return ret;
68685dc0b8aSRafael J. Wysocki }
68785dc0b8aSRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit);
68885dc0b8aSRafael J. Wysocki 
68937530f2bSRafael J. Wysocki static void __dev_pm_qos_hide_latency_limit(struct device *dev)
69037530f2bSRafael J. Wysocki {
691b02f6695SRafael J. Wysocki 	if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->resume_latency_req)
692b02f6695SRafael J. Wysocki 		__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_RESUME_LATENCY);
69337530f2bSRafael J. Wysocki }
69437530f2bSRafael J. Wysocki 
69585dc0b8aSRafael J. Wysocki /**
69685dc0b8aSRafael J. Wysocki  * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space.
69785dc0b8aSRafael J. Wysocki  * @dev: Device whose PM QoS latency limit is to be hidden from user space.
69885dc0b8aSRafael J. Wysocki  */
69985dc0b8aSRafael J. Wysocki void dev_pm_qos_hide_latency_limit(struct device *dev)
70085dc0b8aSRafael J. Wysocki {
7010f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_sysfs_mtx);
7020f703069SRafael J. Wysocki 
703b02f6695SRafael J. Wysocki 	pm_qos_sysfs_remove_resume_latency(dev);
7040f703069SRafael J. Wysocki 
705b81ea1b5SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
70637530f2bSRafael J. Wysocki 	__dev_pm_qos_hide_latency_limit(dev);
707b81ea1b5SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
7080f703069SRafael J. Wysocki 
7090f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
71085dc0b8aSRafael J. Wysocki }
71185dc0b8aSRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit);
712e39473d0SRafael J. Wysocki 
713e39473d0SRafael J. Wysocki /**
714e39473d0SRafael J. Wysocki  * dev_pm_qos_expose_flags - Expose PM QoS flags of a device to user space.
715e39473d0SRafael J. Wysocki  * @dev: Device whose PM QoS flags are to be exposed to user space.
716e39473d0SRafael J. Wysocki  * @val: Initial values of the flags.
717e39473d0SRafael J. Wysocki  */
718e39473d0SRafael J. Wysocki int dev_pm_qos_expose_flags(struct device *dev, s32 val)
719e39473d0SRafael J. Wysocki {
720e39473d0SRafael J. Wysocki 	struct dev_pm_qos_request *req;
721e39473d0SRafael J. Wysocki 	int ret;
722e39473d0SRafael J. Wysocki 
723e39473d0SRafael J. Wysocki 	if (!device_is_registered(dev))
724e39473d0SRafael J. Wysocki 		return -EINVAL;
725e39473d0SRafael J. Wysocki 
726e39473d0SRafael J. Wysocki 	req = kzalloc(sizeof(*req), GFP_KERNEL);
727e39473d0SRafael J. Wysocki 	if (!req)
728e39473d0SRafael J. Wysocki 		return -ENOMEM;
729e39473d0SRafael J. Wysocki 
730e39473d0SRafael J. Wysocki 	ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val);
731b81ea1b5SRafael J. Wysocki 	if (ret < 0) {
732b81ea1b5SRafael J. Wysocki 		kfree(req);
733b81ea1b5SRafael J. Wysocki 		return ret;
734b81ea1b5SRafael J. Wysocki 	}
735b81ea1b5SRafael J. Wysocki 
736b81ea1b5SRafael J. Wysocki 	pm_runtime_get_sync(dev);
7370f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_sysfs_mtx);
7380f703069SRafael J. Wysocki 
739b81ea1b5SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
740b81ea1b5SRafael J. Wysocki 
74137530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->power.qos))
742b81ea1b5SRafael J. Wysocki 		ret = -ENODEV;
743b81ea1b5SRafael J. Wysocki 	else if (dev->power.qos->flags_req)
744b81ea1b5SRafael J. Wysocki 		ret = -EEXIST;
745b81ea1b5SRafael J. Wysocki 
746b81ea1b5SRafael J. Wysocki 	if (ret < 0) {
747b81ea1b5SRafael J. Wysocki 		__dev_pm_qos_remove_request(req);
748b81ea1b5SRafael J. Wysocki 		kfree(req);
7490f703069SRafael J. Wysocki 		mutex_unlock(&dev_pm_qos_mtx);
750b81ea1b5SRafael J. Wysocki 		goto out;
751b81ea1b5SRafael J. Wysocki 	}
752e39473d0SRafael J. Wysocki 	dev->power.qos->flags_req = req;
7530f703069SRafael J. Wysocki 
7540f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
7550f703069SRafael J. Wysocki 
756e39473d0SRafael J. Wysocki 	ret = pm_qos_sysfs_add_flags(dev);
757e39473d0SRafael J. Wysocki 	if (ret)
7580f703069SRafael J. Wysocki 		dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS);
759e39473d0SRafael J. Wysocki 
760b81ea1b5SRafael J. Wysocki  out:
7610f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
7627e4d6844SLan Tianyu 	pm_runtime_put(dev);
763e39473d0SRafael J. Wysocki 	return ret;
764e39473d0SRafael J. Wysocki }
765e39473d0SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags);
766e39473d0SRafael J. Wysocki 
76737530f2bSRafael J. Wysocki static void __dev_pm_qos_hide_flags(struct device *dev)
76837530f2bSRafael J. Wysocki {
7690f703069SRafael J. Wysocki 	if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->flags_req)
77037530f2bSRafael J. Wysocki 		__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS);
77137530f2bSRafael J. Wysocki }
77237530f2bSRafael J. Wysocki 
773e39473d0SRafael J. Wysocki /**
774e39473d0SRafael J. Wysocki  * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space.
775e39473d0SRafael J. Wysocki  * @dev: Device whose PM QoS flags are to be hidden from user space.
776e39473d0SRafael J. Wysocki  */
777e39473d0SRafael J. Wysocki void dev_pm_qos_hide_flags(struct device *dev)
778e39473d0SRafael J. Wysocki {
779b81ea1b5SRafael J. Wysocki 	pm_runtime_get_sync(dev);
7800f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_sysfs_mtx);
7810f703069SRafael J. Wysocki 
7820f703069SRafael J. Wysocki 	pm_qos_sysfs_remove_flags(dev);
7830f703069SRafael J. Wysocki 
784b81ea1b5SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
78537530f2bSRafael J. Wysocki 	__dev_pm_qos_hide_flags(dev);
786b81ea1b5SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
7870f703069SRafael J. Wysocki 
7880f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
789b81ea1b5SRafael J. Wysocki 	pm_runtime_put(dev);
790e39473d0SRafael J. Wysocki }
791e39473d0SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags);
792e39473d0SRafael J. Wysocki 
793e39473d0SRafael J. Wysocki /**
794e39473d0SRafael J. Wysocki  * dev_pm_qos_update_flags - Update PM QoS flags request owned by user space.
795e39473d0SRafael J. Wysocki  * @dev: Device to update the PM QoS flags request for.
796e39473d0SRafael J. Wysocki  * @mask: Flags to set/clear.
797e39473d0SRafael J. Wysocki  * @set: Whether to set or clear the flags (true means set).
798e39473d0SRafael J. Wysocki  */
799e39473d0SRafael J. Wysocki int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set)
800e39473d0SRafael J. Wysocki {
801e39473d0SRafael J. Wysocki 	s32 value;
802e39473d0SRafael J. Wysocki 	int ret;
803e39473d0SRafael J. Wysocki 
804e39473d0SRafael J. Wysocki 	pm_runtime_get_sync(dev);
805e39473d0SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
806e39473d0SRafael J. Wysocki 
80737530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->power.qos) || !dev->power.qos->flags_req) {
808b81ea1b5SRafael J. Wysocki 		ret = -EINVAL;
809b81ea1b5SRafael J. Wysocki 		goto out;
810b81ea1b5SRafael J. Wysocki 	}
811b81ea1b5SRafael J. Wysocki 
812e39473d0SRafael J. Wysocki 	value = dev_pm_qos_requested_flags(dev);
813e39473d0SRafael J. Wysocki 	if (set)
814e39473d0SRafael J. Wysocki 		value |= mask;
815e39473d0SRafael J. Wysocki 	else
816e39473d0SRafael J. Wysocki 		value &= ~mask;
817e39473d0SRafael J. Wysocki 
818e39473d0SRafael J. Wysocki 	ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value);
819e39473d0SRafael J. Wysocki 
820b81ea1b5SRafael J. Wysocki  out:
821e39473d0SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
822e39473d0SRafael J. Wysocki 	pm_runtime_put(dev);
823e39473d0SRafael J. Wysocki 	return ret;
824e39473d0SRafael J. Wysocki }
8252d984ad1SRafael J. Wysocki 
8262d984ad1SRafael J. Wysocki /**
8272d984ad1SRafael J. Wysocki  * dev_pm_qos_get_user_latency_tolerance - Get user space latency tolerance.
8282d984ad1SRafael J. Wysocki  * @dev: Device to obtain the user space latency tolerance for.
8292d984ad1SRafael J. Wysocki  */
8302d984ad1SRafael J. Wysocki s32 dev_pm_qos_get_user_latency_tolerance(struct device *dev)
8312d984ad1SRafael J. Wysocki {
8322d984ad1SRafael J. Wysocki 	s32 ret;
8332d984ad1SRafael J. Wysocki 
8342d984ad1SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
8352d984ad1SRafael J. Wysocki 	ret = IS_ERR_OR_NULL(dev->power.qos)
8362d984ad1SRafael J. Wysocki 		|| !dev->power.qos->latency_tolerance_req ?
8372d984ad1SRafael J. Wysocki 			PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT :
8382d984ad1SRafael J. Wysocki 			dev->power.qos->latency_tolerance_req->data.pnode.prio;
8392d984ad1SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
8402d984ad1SRafael J. Wysocki 	return ret;
8412d984ad1SRafael J. Wysocki }
8422d984ad1SRafael J. Wysocki 
8432d984ad1SRafael J. Wysocki /**
8442d984ad1SRafael J. Wysocki  * dev_pm_qos_update_user_latency_tolerance - Update user space latency tolerance.
8452d984ad1SRafael J. Wysocki  * @dev: Device to update the user space latency tolerance for.
8462d984ad1SRafael J. Wysocki  * @val: New user space latency tolerance for @dev (negative values disable).
8472d984ad1SRafael J. Wysocki  */
8482d984ad1SRafael J. Wysocki int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val)
8492d984ad1SRafael J. Wysocki {
8502d984ad1SRafael J. Wysocki 	int ret;
8512d984ad1SRafael J. Wysocki 
8522d984ad1SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
8532d984ad1SRafael J. Wysocki 
8542d984ad1SRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->power.qos)
8552d984ad1SRafael J. Wysocki 	    || !dev->power.qos->latency_tolerance_req) {
8562d984ad1SRafael J. Wysocki 		struct dev_pm_qos_request *req;
8572d984ad1SRafael J. Wysocki 
8582d984ad1SRafael J. Wysocki 		if (val < 0) {
85980a6f7c7SAndrew Lutomirski 			if (val == PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT)
86080a6f7c7SAndrew Lutomirski 				ret = 0;
86180a6f7c7SAndrew Lutomirski 			else
8622d984ad1SRafael J. Wysocki 				ret = -EINVAL;
8632d984ad1SRafael J. Wysocki 			goto out;
8642d984ad1SRafael J. Wysocki 		}
8652d984ad1SRafael J. Wysocki 		req = kzalloc(sizeof(*req), GFP_KERNEL);
8662d984ad1SRafael J. Wysocki 		if (!req) {
8672d984ad1SRafael J. Wysocki 			ret = -ENOMEM;
8682d984ad1SRafael J. Wysocki 			goto out;
8692d984ad1SRafael J. Wysocki 		}
8702d984ad1SRafael J. Wysocki 		ret = __dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY_TOLERANCE, val);
8712d984ad1SRafael J. Wysocki 		if (ret < 0) {
8722d984ad1SRafael J. Wysocki 			kfree(req);
8732d984ad1SRafael J. Wysocki 			goto out;
8742d984ad1SRafael J. Wysocki 		}
8752d984ad1SRafael J. Wysocki 		dev->power.qos->latency_tolerance_req = req;
8762d984ad1SRafael J. Wysocki 	} else {
8772d984ad1SRafael J. Wysocki 		if (val < 0) {
8782d984ad1SRafael J. Wysocki 			__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY_TOLERANCE);
8792d984ad1SRafael J. Wysocki 			ret = 0;
8802d984ad1SRafael J. Wysocki 		} else {
8812d984ad1SRafael J. Wysocki 			ret = __dev_pm_qos_update_request(dev->power.qos->latency_tolerance_req, val);
8822d984ad1SRafael J. Wysocki 		}
8832d984ad1SRafael J. Wysocki 	}
8842d984ad1SRafael J. Wysocki 
8852d984ad1SRafael J. Wysocki  out:
8862d984ad1SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
8872d984ad1SRafael J. Wysocki 	return ret;
8882d984ad1SRafael J. Wysocki }
889034e7906SAndrew Lutomirski EXPORT_SYMBOL_GPL(dev_pm_qos_update_user_latency_tolerance);
89013b2c4a0SMika Westerberg 
89113b2c4a0SMika Westerberg /**
89213b2c4a0SMika Westerberg  * dev_pm_qos_expose_latency_tolerance - Expose latency tolerance to userspace
89313b2c4a0SMika Westerberg  * @dev: Device whose latency tolerance to expose
89413b2c4a0SMika Westerberg  */
89513b2c4a0SMika Westerberg int dev_pm_qos_expose_latency_tolerance(struct device *dev)
89613b2c4a0SMika Westerberg {
89713b2c4a0SMika Westerberg 	int ret;
89813b2c4a0SMika Westerberg 
89913b2c4a0SMika Westerberg 	if (!dev->power.set_latency_tolerance)
90013b2c4a0SMika Westerberg 		return -EINVAL;
90113b2c4a0SMika Westerberg 
90213b2c4a0SMika Westerberg 	mutex_lock(&dev_pm_qos_sysfs_mtx);
90313b2c4a0SMika Westerberg 	ret = pm_qos_sysfs_add_latency_tolerance(dev);
90413b2c4a0SMika Westerberg 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
90513b2c4a0SMika Westerberg 
90613b2c4a0SMika Westerberg 	return ret;
90713b2c4a0SMika Westerberg }
90813b2c4a0SMika Westerberg EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_tolerance);
90913b2c4a0SMika Westerberg 
91013b2c4a0SMika Westerberg /**
91113b2c4a0SMika Westerberg  * dev_pm_qos_hide_latency_tolerance - Hide latency tolerance from userspace
91213b2c4a0SMika Westerberg  * @dev: Device whose latency tolerance to hide
91313b2c4a0SMika Westerberg  */
91413b2c4a0SMika Westerberg void dev_pm_qos_hide_latency_tolerance(struct device *dev)
91513b2c4a0SMika Westerberg {
91613b2c4a0SMika Westerberg 	mutex_lock(&dev_pm_qos_sysfs_mtx);
91713b2c4a0SMika Westerberg 	pm_qos_sysfs_remove_latency_tolerance(dev);
91813b2c4a0SMika Westerberg 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
91913b2c4a0SMika Westerberg 
92013b2c4a0SMika Westerberg 	/* Remove the request from user space now */
92113b2c4a0SMika Westerberg 	pm_runtime_get_sync(dev);
92213b2c4a0SMika Westerberg 	dev_pm_qos_update_user_latency_tolerance(dev,
92313b2c4a0SMika Westerberg 		PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT);
92413b2c4a0SMika Westerberg 	pm_runtime_put(dev);
92513b2c4a0SMika Westerberg }
92613b2c4a0SMika Westerberg EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_tolerance);
927