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