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