xref: /openbmc/linux/drivers/base/power/qos.c (revision 6dbf5cea)
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 
1116dbf5ceaSRafael J. Wysocki 	return dev_pm_qos_raw_read_value(dev);
11200dc9ad1SRafael J. Wysocki }
11300dc9ad1SRafael J. Wysocki 
11400dc9ad1SRafael J. Wysocki /**
11500dc9ad1SRafael J. Wysocki  * dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked).
1161a9a9152SRafael J. Wysocki  * @dev: Device to get the PM QoS constraint value for.
1171a9a9152SRafael J. Wysocki  */
1181a9a9152SRafael J. Wysocki s32 dev_pm_qos_read_value(struct device *dev)
1191a9a9152SRafael J. Wysocki {
1201a9a9152SRafael J. Wysocki 	unsigned long flags;
12100dc9ad1SRafael J. Wysocki 	s32 ret;
1221a9a9152SRafael J. Wysocki 
1231a9a9152SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
12400dc9ad1SRafael J. Wysocki 	ret = __dev_pm_qos_read_value(dev);
1251a9a9152SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
1261a9a9152SRafael J. Wysocki 
1271a9a9152SRafael J. Wysocki 	return ret;
1281a9a9152SRafael J. Wysocki }
1291a9a9152SRafael J. Wysocki 
130ae0fb4b7SRafael J. Wysocki /**
131ae0fb4b7SRafael J. Wysocki  * apply_constraint - Add/modify/remove device PM QoS request.
132ae0fb4b7SRafael J. Wysocki  * @req: Constraint request to apply
133ae0fb4b7SRafael J. Wysocki  * @action: Action to perform (add/update/remove).
134ae0fb4b7SRafael J. Wysocki  * @value: Value to assign to the QoS request.
135b66213cdSJean Pihet  *
136b66213cdSJean Pihet  * Internal function to update the constraints list using the PM QoS core
137b66213cdSJean Pihet  * code and if needed call the per-device and the global notification
138b66213cdSJean Pihet  * callbacks
139b66213cdSJean Pihet  */
140b66213cdSJean Pihet static int apply_constraint(struct dev_pm_qos_request *req,
141ae0fb4b7SRafael J. Wysocki 			    enum pm_qos_req_action action, s32 value)
142b66213cdSJean Pihet {
143ae0fb4b7SRafael J. Wysocki 	struct dev_pm_qos *qos = req->dev->power.qos;
144ae0fb4b7SRafael J. Wysocki 	int ret;
145b66213cdSJean Pihet 
146ae0fb4b7SRafael J. Wysocki 	switch(req->type) {
147b02f6695SRafael J. Wysocki 	case DEV_PM_QOS_RESUME_LATENCY:
148b02f6695SRafael J. Wysocki 		ret = pm_qos_update_target(&qos->resume_latency,
149b02f6695SRafael J. Wysocki 					   &req->data.pnode, action, value);
150b66213cdSJean Pihet 		if (ret) {
151b02f6695SRafael J. Wysocki 			value = pm_qos_read_value(&qos->resume_latency);
152b66213cdSJean Pihet 			blocking_notifier_call_chain(&dev_pm_notifiers,
153ae0fb4b7SRafael J. Wysocki 						     (unsigned long)value,
154b66213cdSJean Pihet 						     req);
155b66213cdSJean Pihet 		}
156ae0fb4b7SRafael J. Wysocki 		break;
1572d984ad1SRafael J. Wysocki 	case DEV_PM_QOS_LATENCY_TOLERANCE:
1582d984ad1SRafael J. Wysocki 		ret = pm_qos_update_target(&qos->latency_tolerance,
1592d984ad1SRafael J. Wysocki 					   &req->data.pnode, action, value);
1602d984ad1SRafael J. Wysocki 		if (ret) {
1612d984ad1SRafael J. Wysocki 			value = pm_qos_read_value(&qos->latency_tolerance);
1622d984ad1SRafael J. Wysocki 			req->dev->power.set_latency_tolerance(req->dev, value);
1632d984ad1SRafael J. Wysocki 		}
1642d984ad1SRafael J. Wysocki 		break;
165ae0fb4b7SRafael J. Wysocki 	case DEV_PM_QOS_FLAGS:
166ae0fb4b7SRafael J. Wysocki 		ret = pm_qos_update_flags(&qos->flags, &req->data.flr,
167ae0fb4b7SRafael J. Wysocki 					  action, value);
168ae0fb4b7SRafael J. Wysocki 		break;
169ae0fb4b7SRafael J. Wysocki 	default:
170ae0fb4b7SRafael J. Wysocki 		ret = -EINVAL;
171ae0fb4b7SRafael J. Wysocki 	}
172b66213cdSJean Pihet 
173b66213cdSJean Pihet 	return ret;
174b66213cdSJean Pihet }
17591ff4cb8SJean Pihet 
17691ff4cb8SJean Pihet /*
17791ff4cb8SJean Pihet  * dev_pm_qos_constraints_allocate
17891ff4cb8SJean Pihet  * @dev: device to allocate data for
17991ff4cb8SJean Pihet  *
18091ff4cb8SJean Pihet  * Called at the first call to add_request, for constraint data allocation
18191ff4cb8SJean Pihet  * Must be called with the dev_pm_qos_mtx mutex held
18291ff4cb8SJean Pihet  */
18391ff4cb8SJean Pihet static int dev_pm_qos_constraints_allocate(struct device *dev)
18491ff4cb8SJean Pihet {
1855f986c59SRafael J. Wysocki 	struct dev_pm_qos *qos;
18691ff4cb8SJean Pihet 	struct pm_qos_constraints *c;
18791ff4cb8SJean Pihet 	struct blocking_notifier_head *n;
18891ff4cb8SJean Pihet 
1895f986c59SRafael J. Wysocki 	qos = kzalloc(sizeof(*qos), GFP_KERNEL);
1905f986c59SRafael J. Wysocki 	if (!qos)
19191ff4cb8SJean Pihet 		return -ENOMEM;
19291ff4cb8SJean Pihet 
19391ff4cb8SJean Pihet 	n = kzalloc(sizeof(*n), GFP_KERNEL);
19491ff4cb8SJean Pihet 	if (!n) {
1955f986c59SRafael J. Wysocki 		kfree(qos);
19691ff4cb8SJean Pihet 		return -ENOMEM;
19791ff4cb8SJean Pihet 	}
19891ff4cb8SJean Pihet 	BLOCKING_INIT_NOTIFIER_HEAD(n);
19991ff4cb8SJean Pihet 
200b02f6695SRafael J. Wysocki 	c = &qos->resume_latency;
2011a9a9152SRafael J. Wysocki 	plist_head_init(&c->list);
202b02f6695SRafael J. Wysocki 	c->target_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
203b02f6695SRafael J. Wysocki 	c->default_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
204327adaedSRafael J. Wysocki 	c->no_constraint_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
2051a9a9152SRafael J. Wysocki 	c->type = PM_QOS_MIN;
2061a9a9152SRafael J. Wysocki 	c->notifiers = n;
2071a9a9152SRafael J. Wysocki 
2082d984ad1SRafael J. Wysocki 	c = &qos->latency_tolerance;
2092d984ad1SRafael J. Wysocki 	plist_head_init(&c->list);
2102d984ad1SRafael J. Wysocki 	c->target_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE;
2112d984ad1SRafael J. Wysocki 	c->default_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE;
2122d984ad1SRafael J. Wysocki 	c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT;
2132d984ad1SRafael J. Wysocki 	c->type = PM_QOS_MIN;
2142d984ad1SRafael J. Wysocki 
215ae0fb4b7SRafael J. Wysocki 	INIT_LIST_HEAD(&qos->flags.list);
216ae0fb4b7SRafael J. Wysocki 
2171a9a9152SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
2185f986c59SRafael J. Wysocki 	dev->power.qos = qos;
2191a9a9152SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
22091ff4cb8SJean Pihet 
22191ff4cb8SJean Pihet 	return 0;
22291ff4cb8SJean Pihet }
22391ff4cb8SJean Pihet 
22437530f2bSRafael J. Wysocki static void __dev_pm_qos_hide_latency_limit(struct device *dev);
22537530f2bSRafael J. Wysocki static void __dev_pm_qos_hide_flags(struct device *dev);
22691ff4cb8SJean Pihet 
22791ff4cb8SJean Pihet /**
22891ff4cb8SJean Pihet  * dev_pm_qos_constraints_destroy
22991ff4cb8SJean Pihet  * @dev: target device
23091ff4cb8SJean Pihet  *
2311a9a9152SRafael J. Wysocki  * Called from the device PM subsystem on device removal under device_pm_lock().
23291ff4cb8SJean Pihet  */
23391ff4cb8SJean Pihet void dev_pm_qos_constraints_destroy(struct device *dev)
23491ff4cb8SJean Pihet {
2355f986c59SRafael J. Wysocki 	struct dev_pm_qos *qos;
23691ff4cb8SJean Pihet 	struct dev_pm_qos_request *req, *tmp;
2371a9a9152SRafael J. Wysocki 	struct pm_qos_constraints *c;
23835546bd4SRafael J. Wysocki 	struct pm_qos_flags *f;
23991ff4cb8SJean Pihet 
2400f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_sysfs_mtx);
24137530f2bSRafael J. Wysocki 
24285dc0b8aSRafael J. Wysocki 	/*
24335546bd4SRafael J. Wysocki 	 * If the device's PM QoS resume latency limit or PM QoS flags have been
24435546bd4SRafael J. Wysocki 	 * exposed to user space, they have to be hidden at this point.
24585dc0b8aSRafael J. Wysocki 	 */
246b02f6695SRafael J. Wysocki 	pm_qos_sysfs_remove_resume_latency(dev);
2470f703069SRafael J. Wysocki 	pm_qos_sysfs_remove_flags(dev);
2480f703069SRafael J. Wysocki 
2490f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
2500f703069SRafael J. Wysocki 
25137530f2bSRafael J. Wysocki 	__dev_pm_qos_hide_latency_limit(dev);
25237530f2bSRafael J. Wysocki 	__dev_pm_qos_hide_flags(dev);
25385dc0b8aSRafael J. Wysocki 
2545f986c59SRafael J. Wysocki 	qos = dev->power.qos;
2555f986c59SRafael J. Wysocki 	if (!qos)
2561a9a9152SRafael J. Wysocki 		goto out;
2571a9a9152SRafael J. Wysocki 
25835546bd4SRafael J. Wysocki 	/* Flush the constraints lists for the device. */
259b02f6695SRafael J. Wysocki 	c = &qos->resume_latency;
260021c870bSRafael J. Wysocki 	plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
26191ff4cb8SJean Pihet 		/*
262b66213cdSJean Pihet 		 * Update constraints list and call the notification
26391ff4cb8SJean Pihet 		 * callbacks if needed
26491ff4cb8SJean Pihet 		 */
2651a9a9152SRafael J. Wysocki 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
26691ff4cb8SJean Pihet 		memset(req, 0, sizeof(*req));
26791ff4cb8SJean Pihet 	}
2682d984ad1SRafael J. Wysocki 	c = &qos->latency_tolerance;
2692d984ad1SRafael J. Wysocki 	plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
2702d984ad1SRafael J. Wysocki 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
2712d984ad1SRafael J. Wysocki 		memset(req, 0, sizeof(*req));
2722d984ad1SRafael J. Wysocki 	}
27335546bd4SRafael J. Wysocki 	f = &qos->flags;
27435546bd4SRafael J. Wysocki 	list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) {
27535546bd4SRafael J. Wysocki 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
27635546bd4SRafael J. Wysocki 		memset(req, 0, sizeof(*req));
27735546bd4SRafael J. Wysocki 	}
27891ff4cb8SJean Pihet 
2791a9a9152SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
28037530f2bSRafael J. Wysocki 	dev->power.qos = ERR_PTR(-ENODEV);
2811a9a9152SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
28291ff4cb8SJean Pihet 
2831a9a9152SRafael J. Wysocki 	kfree(c->notifiers);
2849eaee2cdSLan,Tianyu 	kfree(qos);
2851a9a9152SRafael J. Wysocki 
2861a9a9152SRafael J. Wysocki  out:
28791ff4cb8SJean Pihet 	mutex_unlock(&dev_pm_qos_mtx);
2880f703069SRafael J. Wysocki 
2890f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
29091ff4cb8SJean Pihet }
29191ff4cb8SJean Pihet 
2922d984ad1SRafael J. Wysocki static bool dev_pm_qos_invalid_request(struct device *dev,
2932d984ad1SRafael J. Wysocki 				       struct dev_pm_qos_request *req)
2942d984ad1SRafael J. Wysocki {
2952d984ad1SRafael J. Wysocki 	return !req || (req->type == DEV_PM_QOS_LATENCY_TOLERANCE
2962d984ad1SRafael J. Wysocki 			&& !dev->power.set_latency_tolerance);
2972d984ad1SRafael J. Wysocki }
2982d984ad1SRafael J. Wysocki 
2992d984ad1SRafael J. Wysocki static int __dev_pm_qos_add_request(struct device *dev,
3002d984ad1SRafael J. Wysocki 				    struct dev_pm_qos_request *req,
3012d984ad1SRafael J. Wysocki 				    enum dev_pm_qos_req_type type, s32 value)
3022d984ad1SRafael J. Wysocki {
3032d984ad1SRafael J. Wysocki 	int ret = 0;
3042d984ad1SRafael J. Wysocki 
3052d984ad1SRafael J. Wysocki 	if (!dev || dev_pm_qos_invalid_request(dev, req))
3062d984ad1SRafael J. Wysocki 		return -EINVAL;
3072d984ad1SRafael J. Wysocki 
3082d984ad1SRafael J. Wysocki 	if (WARN(dev_pm_qos_request_active(req),
3092d984ad1SRafael J. Wysocki 		 "%s() called for already added request\n", __func__))
3102d984ad1SRafael J. Wysocki 		return -EINVAL;
3112d984ad1SRafael J. Wysocki 
3122d984ad1SRafael J. Wysocki 	if (IS_ERR(dev->power.qos))
3132d984ad1SRafael J. Wysocki 		ret = -ENODEV;
3142d984ad1SRafael J. Wysocki 	else if (!dev->power.qos)
3152d984ad1SRafael J. Wysocki 		ret = dev_pm_qos_constraints_allocate(dev);
3162d984ad1SRafael J. Wysocki 
3172d984ad1SRafael J. Wysocki 	trace_dev_pm_qos_add_request(dev_name(dev), type, value);
3182d984ad1SRafael J. Wysocki 	if (!ret) {
3192d984ad1SRafael J. Wysocki 		req->dev = dev;
3202d984ad1SRafael J. Wysocki 		req->type = type;
3212d984ad1SRafael J. Wysocki 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
3222d984ad1SRafael J. Wysocki 	}
3232d984ad1SRafael J. Wysocki 	return ret;
3242d984ad1SRafael J. Wysocki }
3252d984ad1SRafael J. Wysocki 
32691ff4cb8SJean Pihet /**
32791ff4cb8SJean Pihet  * dev_pm_qos_add_request - inserts new qos request into the list
32891ff4cb8SJean Pihet  * @dev: target device for the constraint
32991ff4cb8SJean Pihet  * @req: pointer to a preallocated handle
330ae0fb4b7SRafael J. Wysocki  * @type: type of the request
33191ff4cb8SJean Pihet  * @value: defines the qos request
33291ff4cb8SJean Pihet  *
33391ff4cb8SJean Pihet  * This function inserts a new entry in the device constraints list of
33491ff4cb8SJean Pihet  * requested qos performance characteristics. It recomputes the aggregate
33591ff4cb8SJean Pihet  * QoS expectations of parameters and initializes the dev_pm_qos_request
33691ff4cb8SJean Pihet  * handle.  Caller needs to save this handle for later use in updates and
33791ff4cb8SJean Pihet  * removal.
33891ff4cb8SJean Pihet  *
33991ff4cb8SJean Pihet  * Returns 1 if the aggregated constraint value has changed,
34091ff4cb8SJean Pihet  * 0 if the aggregated constraint value has not changed,
3411a9a9152SRafael J. Wysocki  * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
3421a9a9152SRafael J. Wysocki  * to allocate for data structures, -ENODEV if the device has just been removed
3431a9a9152SRafael J. Wysocki  * from the system.
344436ede89SRafael J. Wysocki  *
345436ede89SRafael J. Wysocki  * Callers should ensure that the target device is not RPM_SUSPENDED before
346436ede89SRafael J. Wysocki  * using this function for requests of type DEV_PM_QOS_FLAGS.
34791ff4cb8SJean Pihet  */
34891ff4cb8SJean Pihet int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
349ae0fb4b7SRafael J. Wysocki 			   enum dev_pm_qos_req_type type, s32 value)
35091ff4cb8SJean Pihet {
3512d984ad1SRafael J. Wysocki 	int ret;
35291ff4cb8SJean Pihet 
3531a9a9152SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
3542d984ad1SRafael J. Wysocki 	ret = __dev_pm_qos_add_request(dev, req, type, value);
35591ff4cb8SJean Pihet 	mutex_unlock(&dev_pm_qos_mtx);
35691ff4cb8SJean Pihet 	return ret;
35791ff4cb8SJean Pihet }
35891ff4cb8SJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
35991ff4cb8SJean Pihet 
36091ff4cb8SJean Pihet /**
361e39473d0SRafael J. Wysocki  * __dev_pm_qos_update_request - Modify an existing device PM QoS request.
362e39473d0SRafael J. Wysocki  * @req : PM QoS request to modify.
363e39473d0SRafael J. Wysocki  * @new_value: New value to request.
364e39473d0SRafael J. Wysocki  */
365e39473d0SRafael J. Wysocki static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req,
366e39473d0SRafael J. Wysocki 				       s32 new_value)
367e39473d0SRafael J. Wysocki {
368e39473d0SRafael J. Wysocki 	s32 curr_value;
369e39473d0SRafael J. Wysocki 	int ret = 0;
370e39473d0SRafael J. Wysocki 
371b81ea1b5SRafael J. Wysocki 	if (!req) /*guard against callers passing in null */
372b81ea1b5SRafael J. Wysocki 		return -EINVAL;
373b81ea1b5SRafael J. Wysocki 
374b81ea1b5SRafael J. Wysocki 	if (WARN(!dev_pm_qos_request_active(req),
375b81ea1b5SRafael J. Wysocki 		 "%s() called for unknown object\n", __func__))
376b81ea1b5SRafael J. Wysocki 		return -EINVAL;
377b81ea1b5SRafael J. Wysocki 
37837530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(req->dev->power.qos))
379e39473d0SRafael J. Wysocki 		return -ENODEV;
380e39473d0SRafael J. Wysocki 
381e39473d0SRafael J. Wysocki 	switch(req->type) {
382b02f6695SRafael J. Wysocki 	case DEV_PM_QOS_RESUME_LATENCY:
3832d984ad1SRafael J. Wysocki 	case DEV_PM_QOS_LATENCY_TOLERANCE:
384e39473d0SRafael J. Wysocki 		curr_value = req->data.pnode.prio;
385e39473d0SRafael J. Wysocki 		break;
386e39473d0SRafael J. Wysocki 	case DEV_PM_QOS_FLAGS:
387e39473d0SRafael J. Wysocki 		curr_value = req->data.flr.flags;
388e39473d0SRafael J. Wysocki 		break;
389e39473d0SRafael J. Wysocki 	default:
390e39473d0SRafael J. Wysocki 		return -EINVAL;
391e39473d0SRafael J. Wysocki 	}
392e39473d0SRafael J. Wysocki 
39396d9d0b5SSahara 	trace_dev_pm_qos_update_request(dev_name(req->dev), req->type,
39496d9d0b5SSahara 					new_value);
395e39473d0SRafael J. Wysocki 	if (curr_value != new_value)
396e39473d0SRafael J. Wysocki 		ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value);
397e39473d0SRafael J. Wysocki 
398e39473d0SRafael J. Wysocki 	return ret;
399e39473d0SRafael J. Wysocki }
400e39473d0SRafael J. Wysocki 
401e39473d0SRafael J. Wysocki /**
40291ff4cb8SJean Pihet  * dev_pm_qos_update_request - modifies an existing qos request
40391ff4cb8SJean Pihet  * @req : handle to list element holding a dev_pm_qos request to use
40491ff4cb8SJean Pihet  * @new_value: defines the qos request
40591ff4cb8SJean Pihet  *
40691ff4cb8SJean Pihet  * Updates an existing dev PM qos request along with updating the
40791ff4cb8SJean Pihet  * target value.
40891ff4cb8SJean Pihet  *
40991ff4cb8SJean Pihet  * Attempts are made to make this code callable on hot code paths.
41091ff4cb8SJean Pihet  *
41191ff4cb8SJean Pihet  * Returns 1 if the aggregated constraint value has changed,
41291ff4cb8SJean Pihet  * 0 if the aggregated constraint value has not changed,
41391ff4cb8SJean Pihet  * -EINVAL in case of wrong parameters, -ENODEV if the device has been
41491ff4cb8SJean Pihet  * removed from the system
415436ede89SRafael J. Wysocki  *
416436ede89SRafael J. Wysocki  * Callers should ensure that the target device is not RPM_SUSPENDED before
417436ede89SRafael J. Wysocki  * using this function for requests of type DEV_PM_QOS_FLAGS.
41891ff4cb8SJean Pihet  */
419e39473d0SRafael J. Wysocki int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value)
42091ff4cb8SJean Pihet {
421e39473d0SRafael J. Wysocki 	int ret;
42291ff4cb8SJean Pihet 
423b81ea1b5SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
424b81ea1b5SRafael J. Wysocki 	ret = __dev_pm_qos_update_request(req, new_value);
425b81ea1b5SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
426b81ea1b5SRafael J. Wysocki 	return ret;
427b81ea1b5SRafael J. Wysocki }
428b81ea1b5SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
429b81ea1b5SRafael J. Wysocki 
430b81ea1b5SRafael J. Wysocki static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
431b81ea1b5SRafael J. Wysocki {
43237530f2bSRafael J. Wysocki 	int ret;
433b81ea1b5SRafael J. Wysocki 
43491ff4cb8SJean Pihet 	if (!req) /*guard against callers passing in null */
43591ff4cb8SJean Pihet 		return -EINVAL;
43691ff4cb8SJean Pihet 
437af4c720eSGuennadi Liakhovetski 	if (WARN(!dev_pm_qos_request_active(req),
438af4c720eSGuennadi Liakhovetski 		 "%s() called for unknown object\n", __func__))
43991ff4cb8SJean Pihet 		return -EINVAL;
44091ff4cb8SJean Pihet 
44137530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(req->dev->power.qos))
44237530f2bSRafael J. Wysocki 		return -ENODEV;
44337530f2bSRafael J. Wysocki 
44496d9d0b5SSahara 	trace_dev_pm_qos_remove_request(dev_name(req->dev), req->type,
44596d9d0b5SSahara 					PM_QOS_DEFAULT_VALUE);
44637530f2bSRafael J. Wysocki 	ret = apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
447b81ea1b5SRafael J. Wysocki 	memset(req, 0, sizeof(*req));
44891ff4cb8SJean Pihet 	return ret;
44991ff4cb8SJean Pihet }
45091ff4cb8SJean Pihet 
45191ff4cb8SJean Pihet /**
45291ff4cb8SJean Pihet  * dev_pm_qos_remove_request - modifies an existing qos request
45391ff4cb8SJean Pihet  * @req: handle to request list element
45491ff4cb8SJean Pihet  *
45591ff4cb8SJean Pihet  * Will remove pm qos request from the list of constraints and
45691ff4cb8SJean Pihet  * recompute the current target value. Call this on slow code paths.
45791ff4cb8SJean Pihet  *
45891ff4cb8SJean Pihet  * Returns 1 if the aggregated constraint value has changed,
45991ff4cb8SJean Pihet  * 0 if the aggregated constraint value has not changed,
46091ff4cb8SJean Pihet  * -EINVAL in case of wrong parameters, -ENODEV if the device has been
46191ff4cb8SJean Pihet  * removed from the system
462436ede89SRafael J. Wysocki  *
463436ede89SRafael J. Wysocki  * Callers should ensure that the target device is not RPM_SUSPENDED before
464436ede89SRafael J. Wysocki  * using this function for requests of type DEV_PM_QOS_FLAGS.
46591ff4cb8SJean Pihet  */
46691ff4cb8SJean Pihet int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
46791ff4cb8SJean Pihet {
468b81ea1b5SRafael J. Wysocki 	int ret;
46991ff4cb8SJean Pihet 
47091ff4cb8SJean Pihet 	mutex_lock(&dev_pm_qos_mtx);
471b81ea1b5SRafael J. Wysocki 	ret = __dev_pm_qos_remove_request(req);
47291ff4cb8SJean Pihet 	mutex_unlock(&dev_pm_qos_mtx);
47391ff4cb8SJean Pihet 	return ret;
47491ff4cb8SJean Pihet }
47591ff4cb8SJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
47691ff4cb8SJean Pihet 
47791ff4cb8SJean Pihet /**
47891ff4cb8SJean Pihet  * dev_pm_qos_add_notifier - sets notification entry for changes to target value
47991ff4cb8SJean Pihet  * of per-device PM QoS constraints
48091ff4cb8SJean Pihet  *
48191ff4cb8SJean Pihet  * @dev: target device for the constraint
48291ff4cb8SJean Pihet  * @notifier: notifier block managed by caller.
48391ff4cb8SJean Pihet  *
48491ff4cb8SJean Pihet  * Will register the notifier into a notification chain that gets called
48591ff4cb8SJean Pihet  * upon changes to the target value for the device.
48623e0fc5aSRafael J. Wysocki  *
48723e0fc5aSRafael J. Wysocki  * If the device's constraints object doesn't exist when this routine is called,
48823e0fc5aSRafael J. Wysocki  * it will be created (or error code will be returned if that fails).
48991ff4cb8SJean Pihet  */
49091ff4cb8SJean Pihet int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
49191ff4cb8SJean Pihet {
49223e0fc5aSRafael J. Wysocki 	int ret = 0;
49391ff4cb8SJean Pihet 
49491ff4cb8SJean Pihet 	mutex_lock(&dev_pm_qos_mtx);
49591ff4cb8SJean Pihet 
49637530f2bSRafael J. Wysocki 	if (IS_ERR(dev->power.qos))
49737530f2bSRafael J. Wysocki 		ret = -ENODEV;
49837530f2bSRafael J. Wysocki 	else if (!dev->power.qos)
49937530f2bSRafael J. Wysocki 		ret = dev_pm_qos_constraints_allocate(dev);
50023e0fc5aSRafael J. Wysocki 
50123e0fc5aSRafael J. Wysocki 	if (!ret)
502b02f6695SRafael J. Wysocki 		ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers,
503b02f6695SRafael J. Wysocki 						       notifier);
50491ff4cb8SJean Pihet 
50591ff4cb8SJean Pihet 	mutex_unlock(&dev_pm_qos_mtx);
50623e0fc5aSRafael J. Wysocki 	return ret;
50791ff4cb8SJean Pihet }
50891ff4cb8SJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
50991ff4cb8SJean Pihet 
51091ff4cb8SJean Pihet /**
51191ff4cb8SJean Pihet  * dev_pm_qos_remove_notifier - deletes notification for changes to target value
51291ff4cb8SJean Pihet  * of per-device PM QoS constraints
51391ff4cb8SJean Pihet  *
51491ff4cb8SJean Pihet  * @dev: target device for the constraint
51591ff4cb8SJean Pihet  * @notifier: notifier block to be removed.
51691ff4cb8SJean Pihet  *
51791ff4cb8SJean Pihet  * Will remove the notifier from the notification chain that gets called
51891ff4cb8SJean Pihet  * upon changes to the target value.
51991ff4cb8SJean Pihet  */
52091ff4cb8SJean Pihet int dev_pm_qos_remove_notifier(struct device *dev,
52191ff4cb8SJean Pihet 			       struct notifier_block *notifier)
52291ff4cb8SJean Pihet {
52391ff4cb8SJean Pihet 	int retval = 0;
52491ff4cb8SJean Pihet 
52591ff4cb8SJean Pihet 	mutex_lock(&dev_pm_qos_mtx);
52691ff4cb8SJean Pihet 
5271a9a9152SRafael J. Wysocki 	/* Silently return if the constraints object is not present. */
52837530f2bSRafael J. Wysocki 	if (!IS_ERR_OR_NULL(dev->power.qos))
529b02f6695SRafael J. Wysocki 		retval = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers,
53091ff4cb8SJean Pihet 							    notifier);
53191ff4cb8SJean Pihet 
53291ff4cb8SJean Pihet 	mutex_unlock(&dev_pm_qos_mtx);
53391ff4cb8SJean Pihet 	return retval;
53491ff4cb8SJean Pihet }
53591ff4cb8SJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
536b66213cdSJean Pihet 
537b66213cdSJean Pihet /**
538b66213cdSJean Pihet  * dev_pm_qos_add_global_notifier - sets notification entry for changes to
539b66213cdSJean Pihet  * target value of the PM QoS constraints for any device
540b66213cdSJean Pihet  *
541b66213cdSJean Pihet  * @notifier: notifier block managed by caller.
542b66213cdSJean Pihet  *
543b66213cdSJean Pihet  * Will register the notifier into a notification chain that gets called
544b66213cdSJean Pihet  * upon changes to the target value for any device.
545b66213cdSJean Pihet  */
546b66213cdSJean Pihet int dev_pm_qos_add_global_notifier(struct notifier_block *notifier)
547b66213cdSJean Pihet {
548b66213cdSJean Pihet 	return blocking_notifier_chain_register(&dev_pm_notifiers, notifier);
549b66213cdSJean Pihet }
550b66213cdSJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier);
551b66213cdSJean Pihet 
552b66213cdSJean Pihet /**
553b66213cdSJean Pihet  * dev_pm_qos_remove_global_notifier - deletes notification for changes to
554b66213cdSJean Pihet  * target value of PM QoS constraints for any device
555b66213cdSJean Pihet  *
556b66213cdSJean Pihet  * @notifier: notifier block to be removed.
557b66213cdSJean Pihet  *
558b66213cdSJean Pihet  * Will remove the notifier from the notification chain that gets called
559b66213cdSJean Pihet  * upon changes to the target value for any device.
560b66213cdSJean Pihet  */
561b66213cdSJean Pihet int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier)
562b66213cdSJean Pihet {
563b66213cdSJean Pihet 	return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier);
564b66213cdSJean Pihet }
565b66213cdSJean Pihet EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier);
56640a5f8beSRafael J. Wysocki 
56740a5f8beSRafael J. Wysocki /**
56840a5f8beSRafael J. Wysocki  * dev_pm_qos_add_ancestor_request - Add PM QoS request for device's ancestor.
56940a5f8beSRafael J. Wysocki  * @dev: Device whose ancestor to add the request for.
57040a5f8beSRafael J. Wysocki  * @req: Pointer to the preallocated handle.
57171d821fdSRafael J. Wysocki  * @type: Type of the request.
57240a5f8beSRafael J. Wysocki  * @value: Constraint latency value.
57340a5f8beSRafael J. Wysocki  */
57440a5f8beSRafael J. Wysocki int dev_pm_qos_add_ancestor_request(struct device *dev,
57571d821fdSRafael J. Wysocki 				    struct dev_pm_qos_request *req,
57671d821fdSRafael J. Wysocki 				    enum dev_pm_qos_req_type type, s32 value)
57740a5f8beSRafael J. Wysocki {
57840a5f8beSRafael J. Wysocki 	struct device *ancestor = dev->parent;
5794ce47802SRafael J. Wysocki 	int ret = -ENODEV;
58040a5f8beSRafael J. Wysocki 
58171d821fdSRafael J. Wysocki 	switch (type) {
58271d821fdSRafael J. Wysocki 	case DEV_PM_QOS_RESUME_LATENCY:
58340a5f8beSRafael J. Wysocki 		while (ancestor && !ancestor->power.ignore_children)
58440a5f8beSRafael J. Wysocki 			ancestor = ancestor->parent;
58540a5f8beSRafael J. Wysocki 
58671d821fdSRafael J. Wysocki 		break;
58771d821fdSRafael J. Wysocki 	case DEV_PM_QOS_LATENCY_TOLERANCE:
58871d821fdSRafael J. Wysocki 		while (ancestor && !ancestor->power.set_latency_tolerance)
58971d821fdSRafael J. Wysocki 			ancestor = ancestor->parent;
59071d821fdSRafael J. Wysocki 
59171d821fdSRafael J. Wysocki 		break;
59271d821fdSRafael J. Wysocki 	default:
59371d821fdSRafael J. Wysocki 		ancestor = NULL;
59471d821fdSRafael J. Wysocki 	}
59540a5f8beSRafael J. Wysocki 	if (ancestor)
59671d821fdSRafael J. Wysocki 		ret = dev_pm_qos_add_request(ancestor, req, type, value);
59740a5f8beSRafael J. Wysocki 
5984ce47802SRafael J. Wysocki 	if (ret < 0)
59940a5f8beSRafael J. Wysocki 		req->dev = NULL;
60040a5f8beSRafael J. Wysocki 
6014ce47802SRafael J. Wysocki 	return ret;
60240a5f8beSRafael J. Wysocki }
60340a5f8beSRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request);
60485dc0b8aSRafael J. Wysocki 
605e39473d0SRafael J. Wysocki static void __dev_pm_qos_drop_user_request(struct device *dev,
606e39473d0SRafael J. Wysocki 					   enum dev_pm_qos_req_type type)
60785dc0b8aSRafael J. Wysocki {
608b81ea1b5SRafael J. Wysocki 	struct dev_pm_qos_request *req = NULL;
609b81ea1b5SRafael J. Wysocki 
610e39473d0SRafael J. Wysocki 	switch(type) {
611b02f6695SRafael J. Wysocki 	case DEV_PM_QOS_RESUME_LATENCY:
612b02f6695SRafael J. Wysocki 		req = dev->power.qos->resume_latency_req;
613b02f6695SRafael J. Wysocki 		dev->power.qos->resume_latency_req = NULL;
614e39473d0SRafael J. Wysocki 		break;
6152d984ad1SRafael J. Wysocki 	case DEV_PM_QOS_LATENCY_TOLERANCE:
6162d984ad1SRafael J. Wysocki 		req = dev->power.qos->latency_tolerance_req;
6172d984ad1SRafael J. Wysocki 		dev->power.qos->latency_tolerance_req = NULL;
6182d984ad1SRafael J. Wysocki 		break;
619e39473d0SRafael J. Wysocki 	case DEV_PM_QOS_FLAGS:
620b81ea1b5SRafael J. Wysocki 		req = dev->power.qos->flags_req;
621e39473d0SRafael J. Wysocki 		dev->power.qos->flags_req = NULL;
622e39473d0SRafael J. Wysocki 		break;
623e39473d0SRafael J. Wysocki 	}
624b81ea1b5SRafael J. Wysocki 	__dev_pm_qos_remove_request(req);
625b81ea1b5SRafael J. Wysocki 	kfree(req);
62685dc0b8aSRafael J. Wysocki }
62785dc0b8aSRafael J. Wysocki 
6280f703069SRafael J. Wysocki static void dev_pm_qos_drop_user_request(struct device *dev,
6290f703069SRafael J. Wysocki 					 enum dev_pm_qos_req_type type)
6300f703069SRafael J. Wysocki {
6310f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
6320f703069SRafael J. Wysocki 	__dev_pm_qos_drop_user_request(dev, type);
6330f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
6340f703069SRafael J. Wysocki }
6350f703069SRafael J. Wysocki 
63685dc0b8aSRafael J. Wysocki /**
63785dc0b8aSRafael J. Wysocki  * dev_pm_qos_expose_latency_limit - Expose PM QoS latency limit to user space.
63885dc0b8aSRafael J. Wysocki  * @dev: Device whose PM QoS latency limit is to be exposed to user space.
63985dc0b8aSRafael J. Wysocki  * @value: Initial value of the latency limit.
64085dc0b8aSRafael J. Wysocki  */
64185dc0b8aSRafael J. Wysocki int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value)
64285dc0b8aSRafael J. Wysocki {
64385dc0b8aSRafael J. Wysocki 	struct dev_pm_qos_request *req;
64485dc0b8aSRafael J. Wysocki 	int ret;
64585dc0b8aSRafael J. Wysocki 
64685dc0b8aSRafael J. Wysocki 	if (!device_is_registered(dev) || value < 0)
64785dc0b8aSRafael J. Wysocki 		return -EINVAL;
64885dc0b8aSRafael J. Wysocki 
64985dc0b8aSRafael J. Wysocki 	req = kzalloc(sizeof(*req), GFP_KERNEL);
65085dc0b8aSRafael J. Wysocki 	if (!req)
65185dc0b8aSRafael J. Wysocki 		return -ENOMEM;
65285dc0b8aSRafael J. Wysocki 
653b02f6695SRafael J. Wysocki 	ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_RESUME_LATENCY, value);
654b81ea1b5SRafael J. Wysocki 	if (ret < 0) {
655b81ea1b5SRafael J. Wysocki 		kfree(req);
65685dc0b8aSRafael J. Wysocki 		return ret;
657b81ea1b5SRafael J. Wysocki 	}
658b81ea1b5SRafael J. Wysocki 
6590f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_sysfs_mtx);
6600f703069SRafael J. Wysocki 
661b81ea1b5SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
662b81ea1b5SRafael J. Wysocki 
66337530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->power.qos))
664b81ea1b5SRafael J. Wysocki 		ret = -ENODEV;
665b02f6695SRafael J. Wysocki 	else if (dev->power.qos->resume_latency_req)
666b81ea1b5SRafael J. Wysocki 		ret = -EEXIST;
667b81ea1b5SRafael J. Wysocki 
668b81ea1b5SRafael J. Wysocki 	if (ret < 0) {
669b81ea1b5SRafael J. Wysocki 		__dev_pm_qos_remove_request(req);
670b81ea1b5SRafael J. Wysocki 		kfree(req);
6710f703069SRafael J. Wysocki 		mutex_unlock(&dev_pm_qos_mtx);
672b81ea1b5SRafael J. Wysocki 		goto out;
673b81ea1b5SRafael J. Wysocki 	}
674b02f6695SRafael J. Wysocki 	dev->power.qos->resume_latency_req = req;
6750f703069SRafael J. Wysocki 
6760f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
6770f703069SRafael J. Wysocki 
678b02f6695SRafael J. Wysocki 	ret = pm_qos_sysfs_add_resume_latency(dev);
67985dc0b8aSRafael J. Wysocki 	if (ret)
680b02f6695SRafael J. Wysocki 		dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_RESUME_LATENCY);
68185dc0b8aSRafael J. Wysocki 
682b81ea1b5SRafael J. Wysocki  out:
6830f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
68485dc0b8aSRafael J. Wysocki 	return ret;
68585dc0b8aSRafael J. Wysocki }
68685dc0b8aSRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit);
68785dc0b8aSRafael J. Wysocki 
68837530f2bSRafael J. Wysocki static void __dev_pm_qos_hide_latency_limit(struct device *dev)
68937530f2bSRafael J. Wysocki {
690b02f6695SRafael J. Wysocki 	if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->resume_latency_req)
691b02f6695SRafael J. Wysocki 		__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_RESUME_LATENCY);
69237530f2bSRafael J. Wysocki }
69337530f2bSRafael J. Wysocki 
69485dc0b8aSRafael J. Wysocki /**
69585dc0b8aSRafael J. Wysocki  * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space.
69685dc0b8aSRafael J. Wysocki  * @dev: Device whose PM QoS latency limit is to be hidden from user space.
69785dc0b8aSRafael J. Wysocki  */
69885dc0b8aSRafael J. Wysocki void dev_pm_qos_hide_latency_limit(struct device *dev)
69985dc0b8aSRafael J. Wysocki {
7000f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_sysfs_mtx);
7010f703069SRafael J. Wysocki 
702b02f6695SRafael J. Wysocki 	pm_qos_sysfs_remove_resume_latency(dev);
7030f703069SRafael J. Wysocki 
704b81ea1b5SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
70537530f2bSRafael J. Wysocki 	__dev_pm_qos_hide_latency_limit(dev);
706b81ea1b5SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
7070f703069SRafael J. Wysocki 
7080f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
70985dc0b8aSRafael J. Wysocki }
71085dc0b8aSRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit);
711e39473d0SRafael J. Wysocki 
712e39473d0SRafael J. Wysocki /**
713e39473d0SRafael J. Wysocki  * dev_pm_qos_expose_flags - Expose PM QoS flags of a device to user space.
714e39473d0SRafael J. Wysocki  * @dev: Device whose PM QoS flags are to be exposed to user space.
715e39473d0SRafael J. Wysocki  * @val: Initial values of the flags.
716e39473d0SRafael J. Wysocki  */
717e39473d0SRafael J. Wysocki int dev_pm_qos_expose_flags(struct device *dev, s32 val)
718e39473d0SRafael J. Wysocki {
719e39473d0SRafael J. Wysocki 	struct dev_pm_qos_request *req;
720e39473d0SRafael J. Wysocki 	int ret;
721e39473d0SRafael J. Wysocki 
722e39473d0SRafael J. Wysocki 	if (!device_is_registered(dev))
723e39473d0SRafael J. Wysocki 		return -EINVAL;
724e39473d0SRafael J. Wysocki 
725e39473d0SRafael J. Wysocki 	req = kzalloc(sizeof(*req), GFP_KERNEL);
726e39473d0SRafael J. Wysocki 	if (!req)
727e39473d0SRafael J. Wysocki 		return -ENOMEM;
728e39473d0SRafael J. Wysocki 
729e39473d0SRafael J. Wysocki 	ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val);
730b81ea1b5SRafael J. Wysocki 	if (ret < 0) {
731b81ea1b5SRafael J. Wysocki 		kfree(req);
732b81ea1b5SRafael J. Wysocki 		return ret;
733b81ea1b5SRafael J. Wysocki 	}
734b81ea1b5SRafael J. Wysocki 
735b81ea1b5SRafael J. Wysocki 	pm_runtime_get_sync(dev);
7360f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_sysfs_mtx);
7370f703069SRafael J. Wysocki 
738b81ea1b5SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
739b81ea1b5SRafael J. Wysocki 
74037530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->power.qos))
741b81ea1b5SRafael J. Wysocki 		ret = -ENODEV;
742b81ea1b5SRafael J. Wysocki 	else if (dev->power.qos->flags_req)
743b81ea1b5SRafael J. Wysocki 		ret = -EEXIST;
744b81ea1b5SRafael J. Wysocki 
745b81ea1b5SRafael J. Wysocki 	if (ret < 0) {
746b81ea1b5SRafael J. Wysocki 		__dev_pm_qos_remove_request(req);
747b81ea1b5SRafael J. Wysocki 		kfree(req);
7480f703069SRafael J. Wysocki 		mutex_unlock(&dev_pm_qos_mtx);
749b81ea1b5SRafael J. Wysocki 		goto out;
750b81ea1b5SRafael J. Wysocki 	}
751e39473d0SRafael J. Wysocki 	dev->power.qos->flags_req = req;
7520f703069SRafael J. Wysocki 
7530f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
7540f703069SRafael J. Wysocki 
755e39473d0SRafael J. Wysocki 	ret = pm_qos_sysfs_add_flags(dev);
756e39473d0SRafael J. Wysocki 	if (ret)
7570f703069SRafael J. Wysocki 		dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS);
758e39473d0SRafael J. Wysocki 
759b81ea1b5SRafael J. Wysocki  out:
7600f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
7617e4d6844SLan Tianyu 	pm_runtime_put(dev);
762e39473d0SRafael J. Wysocki 	return ret;
763e39473d0SRafael J. Wysocki }
764e39473d0SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags);
765e39473d0SRafael J. Wysocki 
76637530f2bSRafael J. Wysocki static void __dev_pm_qos_hide_flags(struct device *dev)
76737530f2bSRafael J. Wysocki {
7680f703069SRafael J. Wysocki 	if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->flags_req)
76937530f2bSRafael J. Wysocki 		__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS);
77037530f2bSRafael J. Wysocki }
77137530f2bSRafael J. Wysocki 
772e39473d0SRafael J. Wysocki /**
773e39473d0SRafael J. Wysocki  * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space.
774e39473d0SRafael J. Wysocki  * @dev: Device whose PM QoS flags are to be hidden from user space.
775e39473d0SRafael J. Wysocki  */
776e39473d0SRafael J. Wysocki void dev_pm_qos_hide_flags(struct device *dev)
777e39473d0SRafael J. Wysocki {
778b81ea1b5SRafael J. Wysocki 	pm_runtime_get_sync(dev);
7790f703069SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_sysfs_mtx);
7800f703069SRafael J. Wysocki 
7810f703069SRafael J. Wysocki 	pm_qos_sysfs_remove_flags(dev);
7820f703069SRafael J. Wysocki 
783b81ea1b5SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
78437530f2bSRafael J. Wysocki 	__dev_pm_qos_hide_flags(dev);
785b81ea1b5SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
7860f703069SRafael J. Wysocki 
7870f703069SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
788b81ea1b5SRafael J. Wysocki 	pm_runtime_put(dev);
789e39473d0SRafael J. Wysocki }
790e39473d0SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags);
791e39473d0SRafael J. Wysocki 
792e39473d0SRafael J. Wysocki /**
793e39473d0SRafael J. Wysocki  * dev_pm_qos_update_flags - Update PM QoS flags request owned by user space.
794e39473d0SRafael J. Wysocki  * @dev: Device to update the PM QoS flags request for.
795e39473d0SRafael J. Wysocki  * @mask: Flags to set/clear.
796e39473d0SRafael J. Wysocki  * @set: Whether to set or clear the flags (true means set).
797e39473d0SRafael J. Wysocki  */
798e39473d0SRafael J. Wysocki int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set)
799e39473d0SRafael J. Wysocki {
800e39473d0SRafael J. Wysocki 	s32 value;
801e39473d0SRafael J. Wysocki 	int ret;
802e39473d0SRafael J. Wysocki 
803e39473d0SRafael J. Wysocki 	pm_runtime_get_sync(dev);
804e39473d0SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
805e39473d0SRafael J. Wysocki 
80637530f2bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->power.qos) || !dev->power.qos->flags_req) {
807b81ea1b5SRafael J. Wysocki 		ret = -EINVAL;
808b81ea1b5SRafael J. Wysocki 		goto out;
809b81ea1b5SRafael J. Wysocki 	}
810b81ea1b5SRafael J. Wysocki 
811e39473d0SRafael J. Wysocki 	value = dev_pm_qos_requested_flags(dev);
812e39473d0SRafael J. Wysocki 	if (set)
813e39473d0SRafael J. Wysocki 		value |= mask;
814e39473d0SRafael J. Wysocki 	else
815e39473d0SRafael J. Wysocki 		value &= ~mask;
816e39473d0SRafael J. Wysocki 
817e39473d0SRafael J. Wysocki 	ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value);
818e39473d0SRafael J. Wysocki 
819b81ea1b5SRafael J. Wysocki  out:
820e39473d0SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
821e39473d0SRafael J. Wysocki 	pm_runtime_put(dev);
822e39473d0SRafael J. Wysocki 	return ret;
823e39473d0SRafael J. Wysocki }
8242d984ad1SRafael J. Wysocki 
8252d984ad1SRafael J. Wysocki /**
8262d984ad1SRafael J. Wysocki  * dev_pm_qos_get_user_latency_tolerance - Get user space latency tolerance.
8272d984ad1SRafael J. Wysocki  * @dev: Device to obtain the user space latency tolerance for.
8282d984ad1SRafael J. Wysocki  */
8292d984ad1SRafael J. Wysocki s32 dev_pm_qos_get_user_latency_tolerance(struct device *dev)
8302d984ad1SRafael J. Wysocki {
8312d984ad1SRafael J. Wysocki 	s32 ret;
8322d984ad1SRafael J. Wysocki 
8332d984ad1SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
8342d984ad1SRafael J. Wysocki 	ret = IS_ERR_OR_NULL(dev->power.qos)
8352d984ad1SRafael J. Wysocki 		|| !dev->power.qos->latency_tolerance_req ?
8362d984ad1SRafael J. Wysocki 			PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT :
8372d984ad1SRafael J. Wysocki 			dev->power.qos->latency_tolerance_req->data.pnode.prio;
8382d984ad1SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
8392d984ad1SRafael J. Wysocki 	return ret;
8402d984ad1SRafael J. Wysocki }
8412d984ad1SRafael J. Wysocki 
8422d984ad1SRafael J. Wysocki /**
8432d984ad1SRafael J. Wysocki  * dev_pm_qos_update_user_latency_tolerance - Update user space latency tolerance.
8442d984ad1SRafael J. Wysocki  * @dev: Device to update the user space latency tolerance for.
8452d984ad1SRafael J. Wysocki  * @val: New user space latency tolerance for @dev (negative values disable).
8462d984ad1SRafael J. Wysocki  */
8472d984ad1SRafael J. Wysocki int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val)
8482d984ad1SRafael J. Wysocki {
8492d984ad1SRafael J. Wysocki 	int ret;
8502d984ad1SRafael J. Wysocki 
8512d984ad1SRafael J. Wysocki 	mutex_lock(&dev_pm_qos_mtx);
8522d984ad1SRafael J. Wysocki 
8532d984ad1SRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->power.qos)
8542d984ad1SRafael J. Wysocki 	    || !dev->power.qos->latency_tolerance_req) {
8552d984ad1SRafael J. Wysocki 		struct dev_pm_qos_request *req;
8562d984ad1SRafael J. Wysocki 
8572d984ad1SRafael J. Wysocki 		if (val < 0) {
85880a6f7c7SAndrew Lutomirski 			if (val == PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT)
85980a6f7c7SAndrew Lutomirski 				ret = 0;
86080a6f7c7SAndrew Lutomirski 			else
8612d984ad1SRafael J. Wysocki 				ret = -EINVAL;
8622d984ad1SRafael J. Wysocki 			goto out;
8632d984ad1SRafael J. Wysocki 		}
8642d984ad1SRafael J. Wysocki 		req = kzalloc(sizeof(*req), GFP_KERNEL);
8652d984ad1SRafael J. Wysocki 		if (!req) {
8662d984ad1SRafael J. Wysocki 			ret = -ENOMEM;
8672d984ad1SRafael J. Wysocki 			goto out;
8682d984ad1SRafael J. Wysocki 		}
8692d984ad1SRafael J. Wysocki 		ret = __dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY_TOLERANCE, val);
8702d984ad1SRafael J. Wysocki 		if (ret < 0) {
8712d984ad1SRafael J. Wysocki 			kfree(req);
8722d984ad1SRafael J. Wysocki 			goto out;
8732d984ad1SRafael J. Wysocki 		}
8742d984ad1SRafael J. Wysocki 		dev->power.qos->latency_tolerance_req = req;
8752d984ad1SRafael J. Wysocki 	} else {
8762d984ad1SRafael J. Wysocki 		if (val < 0) {
8772d984ad1SRafael J. Wysocki 			__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY_TOLERANCE);
8782d984ad1SRafael J. Wysocki 			ret = 0;
8792d984ad1SRafael J. Wysocki 		} else {
8802d984ad1SRafael J. Wysocki 			ret = __dev_pm_qos_update_request(dev->power.qos->latency_tolerance_req, val);
8812d984ad1SRafael J. Wysocki 		}
8822d984ad1SRafael J. Wysocki 	}
8832d984ad1SRafael J. Wysocki 
8842d984ad1SRafael J. Wysocki  out:
8852d984ad1SRafael J. Wysocki 	mutex_unlock(&dev_pm_qos_mtx);
8862d984ad1SRafael J. Wysocki 	return ret;
8872d984ad1SRafael J. Wysocki }
888034e7906SAndrew Lutomirski EXPORT_SYMBOL_GPL(dev_pm_qos_update_user_latency_tolerance);
88913b2c4a0SMika Westerberg 
89013b2c4a0SMika Westerberg /**
89113b2c4a0SMika Westerberg  * dev_pm_qos_expose_latency_tolerance - Expose latency tolerance to userspace
89213b2c4a0SMika Westerberg  * @dev: Device whose latency tolerance to expose
89313b2c4a0SMika Westerberg  */
89413b2c4a0SMika Westerberg int dev_pm_qos_expose_latency_tolerance(struct device *dev)
89513b2c4a0SMika Westerberg {
89613b2c4a0SMika Westerberg 	int ret;
89713b2c4a0SMika Westerberg 
89813b2c4a0SMika Westerberg 	if (!dev->power.set_latency_tolerance)
89913b2c4a0SMika Westerberg 		return -EINVAL;
90013b2c4a0SMika Westerberg 
90113b2c4a0SMika Westerberg 	mutex_lock(&dev_pm_qos_sysfs_mtx);
90213b2c4a0SMika Westerberg 	ret = pm_qos_sysfs_add_latency_tolerance(dev);
90313b2c4a0SMika Westerberg 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
90413b2c4a0SMika Westerberg 
90513b2c4a0SMika Westerberg 	return ret;
90613b2c4a0SMika Westerberg }
90713b2c4a0SMika Westerberg EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_tolerance);
90813b2c4a0SMika Westerberg 
90913b2c4a0SMika Westerberg /**
91013b2c4a0SMika Westerberg  * dev_pm_qos_hide_latency_tolerance - Hide latency tolerance from userspace
91113b2c4a0SMika Westerberg  * @dev: Device whose latency tolerance to hide
91213b2c4a0SMika Westerberg  */
91313b2c4a0SMika Westerberg void dev_pm_qos_hide_latency_tolerance(struct device *dev)
91413b2c4a0SMika Westerberg {
91513b2c4a0SMika Westerberg 	mutex_lock(&dev_pm_qos_sysfs_mtx);
91613b2c4a0SMika Westerberg 	pm_qos_sysfs_remove_latency_tolerance(dev);
91713b2c4a0SMika Westerberg 	mutex_unlock(&dev_pm_qos_sysfs_mtx);
91813b2c4a0SMika Westerberg 
91913b2c4a0SMika Westerberg 	/* Remove the request from user space now */
92013b2c4a0SMika Westerberg 	pm_runtime_get_sync(dev);
92113b2c4a0SMika Westerberg 	dev_pm_qos_update_user_latency_tolerance(dev,
92213b2c4a0SMika Westerberg 		PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT);
92313b2c4a0SMika Westerberg 	pm_runtime_put(dev);
92413b2c4a0SMika Westerberg }
92513b2c4a0SMika Westerberg EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_tolerance);
926