162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Devices PM QoS constraints management 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This module exposes the interface to kernel space for specifying 862306a36Sopenharmony_ci * per-device PM QoS dependencies. It provides infrastructure for registration 962306a36Sopenharmony_ci * of: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Dependents on a QoS value : register requests 1262306a36Sopenharmony_ci * Watchers of QoS value : get notified when target QoS value changes 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This QoS design is best effort based. Dependents register their QoS needs. 1562306a36Sopenharmony_ci * Watchers register to keep track of the current QoS needs of the system. 1662306a36Sopenharmony_ci * Watchers can register a per-device notification callback using the 1762306a36Sopenharmony_ci * dev_pm_qos_*_notifier API. The notification chain data is stored in the 1862306a36Sopenharmony_ci * per-device constraint data struct. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Note about the per-device constraint data struct allocation: 2162306a36Sopenharmony_ci * . The per-device constraints data struct ptr is stored into the device 2262306a36Sopenharmony_ci * dev_pm_info. 2362306a36Sopenharmony_ci * . To minimize the data usage by the per-device constraints, the data struct 2462306a36Sopenharmony_ci * is only allocated at the first call to dev_pm_qos_add_request. 2562306a36Sopenharmony_ci * . The data is later free'd when the device is removed from the system. 2662306a36Sopenharmony_ci * . A global mutex protects the constraints users from the data being 2762306a36Sopenharmony_ci * allocated and free'd. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <linux/pm_qos.h> 3162306a36Sopenharmony_ci#include <linux/spinlock.h> 3262306a36Sopenharmony_ci#include <linux/slab.h> 3362306a36Sopenharmony_ci#include <linux/device.h> 3462306a36Sopenharmony_ci#include <linux/mutex.h> 3562306a36Sopenharmony_ci#include <linux/export.h> 3662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 3762306a36Sopenharmony_ci#include <linux/err.h> 3862306a36Sopenharmony_ci#include <trace/events/power.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include "power.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic DEFINE_MUTEX(dev_pm_qos_mtx); 4362306a36Sopenharmony_cistatic DEFINE_MUTEX(dev_pm_qos_sysfs_mtx); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/** 4662306a36Sopenharmony_ci * __dev_pm_qos_flags - Check PM QoS flags for a given device. 4762306a36Sopenharmony_ci * @dev: Device to check the PM QoS flags for. 4862306a36Sopenharmony_ci * @mask: Flags to check against. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * This routine must be called with dev->power.lock held. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cienum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct dev_pm_qos *qos = dev->power.qos; 5562306a36Sopenharmony_ci struct pm_qos_flags *pqf; 5662306a36Sopenharmony_ci s32 val; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci lockdep_assert_held(&dev->power.lock); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (IS_ERR_OR_NULL(qos)) 6162306a36Sopenharmony_ci return PM_QOS_FLAGS_UNDEFINED; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci pqf = &qos->flags; 6462306a36Sopenharmony_ci if (list_empty(&pqf->list)) 6562306a36Sopenharmony_ci return PM_QOS_FLAGS_UNDEFINED; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci val = pqf->effective_flags & mask; 6862306a36Sopenharmony_ci if (val) 6962306a36Sopenharmony_ci return (val == mask) ? PM_QOS_FLAGS_ALL : PM_QOS_FLAGS_SOME; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return PM_QOS_FLAGS_NONE; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/** 7562306a36Sopenharmony_ci * dev_pm_qos_flags - Check PM QoS flags for a given device (locked). 7662306a36Sopenharmony_ci * @dev: Device to check the PM QoS flags for. 7762306a36Sopenharmony_ci * @mask: Flags to check against. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cienum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci unsigned long irqflags; 8262306a36Sopenharmony_ci enum pm_qos_flags_status ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci spin_lock_irqsave(&dev->power.lock, irqflags); 8562306a36Sopenharmony_ci ret = __dev_pm_qos_flags(dev, mask); 8662306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->power.lock, irqflags); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return ret; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_flags); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/** 9362306a36Sopenharmony_ci * __dev_pm_qos_resume_latency - Get resume latency constraint for a given device. 9462306a36Sopenharmony_ci * @dev: Device to get the PM QoS constraint value for. 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * This routine must be called with dev->power.lock held. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cis32 __dev_pm_qos_resume_latency(struct device *dev) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci lockdep_assert_held(&dev->power.lock); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return dev_pm_qos_raw_resume_latency(dev); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/** 10662306a36Sopenharmony_ci * dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked). 10762306a36Sopenharmony_ci * @dev: Device to get the PM QoS constraint value for. 10862306a36Sopenharmony_ci * @type: QoS request type. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cis32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct dev_pm_qos *qos = dev->power.qos; 11362306a36Sopenharmony_ci unsigned long flags; 11462306a36Sopenharmony_ci s32 ret; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci spin_lock_irqsave(&dev->power.lock, flags); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci switch (type) { 11962306a36Sopenharmony_ci case DEV_PM_QOS_RESUME_LATENCY: 12062306a36Sopenharmony_ci ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT 12162306a36Sopenharmony_ci : pm_qos_read_value(&qos->resume_latency); 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci case DEV_PM_QOS_MIN_FREQUENCY: 12462306a36Sopenharmony_ci ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE 12562306a36Sopenharmony_ci : freq_qos_read_value(&qos->freq, FREQ_QOS_MIN); 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci case DEV_PM_QOS_MAX_FREQUENCY: 12862306a36Sopenharmony_ci ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE 12962306a36Sopenharmony_ci : freq_qos_read_value(&qos->freq, FREQ_QOS_MAX); 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci default: 13262306a36Sopenharmony_ci WARN_ON(1); 13362306a36Sopenharmony_ci ret = 0; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->power.lock, flags); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return ret; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/** 14262306a36Sopenharmony_ci * apply_constraint - Add/modify/remove device PM QoS request. 14362306a36Sopenharmony_ci * @req: Constraint request to apply 14462306a36Sopenharmony_ci * @action: Action to perform (add/update/remove). 14562306a36Sopenharmony_ci * @value: Value to assign to the QoS request. 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * Internal function to update the constraints list using the PM QoS core 14862306a36Sopenharmony_ci * code and if needed call the per-device callbacks. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistatic int apply_constraint(struct dev_pm_qos_request *req, 15162306a36Sopenharmony_ci enum pm_qos_req_action action, s32 value) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct dev_pm_qos *qos = req->dev->power.qos; 15462306a36Sopenharmony_ci int ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci switch(req->type) { 15762306a36Sopenharmony_ci case DEV_PM_QOS_RESUME_LATENCY: 15862306a36Sopenharmony_ci if (WARN_ON(action != PM_QOS_REMOVE_REQ && value < 0)) 15962306a36Sopenharmony_ci value = 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci ret = pm_qos_update_target(&qos->resume_latency, 16262306a36Sopenharmony_ci &req->data.pnode, action, value); 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci case DEV_PM_QOS_LATENCY_TOLERANCE: 16562306a36Sopenharmony_ci ret = pm_qos_update_target(&qos->latency_tolerance, 16662306a36Sopenharmony_ci &req->data.pnode, action, value); 16762306a36Sopenharmony_ci if (ret) { 16862306a36Sopenharmony_ci value = pm_qos_read_value(&qos->latency_tolerance); 16962306a36Sopenharmony_ci req->dev->power.set_latency_tolerance(req->dev, value); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci case DEV_PM_QOS_MIN_FREQUENCY: 17362306a36Sopenharmony_ci case DEV_PM_QOS_MAX_FREQUENCY: 17462306a36Sopenharmony_ci ret = freq_qos_apply(&req->data.freq, action, value); 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci case DEV_PM_QOS_FLAGS: 17762306a36Sopenharmony_ci ret = pm_qos_update_flags(&qos->flags, &req->data.flr, 17862306a36Sopenharmony_ci action, value); 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci default: 18162306a36Sopenharmony_ci ret = -EINVAL; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return ret; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* 18862306a36Sopenharmony_ci * dev_pm_qos_constraints_allocate 18962306a36Sopenharmony_ci * @dev: device to allocate data for 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * Called at the first call to add_request, for constraint data allocation 19262306a36Sopenharmony_ci * Must be called with the dev_pm_qos_mtx mutex held 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_cistatic int dev_pm_qos_constraints_allocate(struct device *dev) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct dev_pm_qos *qos; 19762306a36Sopenharmony_ci struct pm_qos_constraints *c; 19862306a36Sopenharmony_ci struct blocking_notifier_head *n; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci qos = kzalloc(sizeof(*qos), GFP_KERNEL); 20162306a36Sopenharmony_ci if (!qos) 20262306a36Sopenharmony_ci return -ENOMEM; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci n = kzalloc(3 * sizeof(*n), GFP_KERNEL); 20562306a36Sopenharmony_ci if (!n) { 20662306a36Sopenharmony_ci kfree(qos); 20762306a36Sopenharmony_ci return -ENOMEM; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci c = &qos->resume_latency; 21162306a36Sopenharmony_ci plist_head_init(&c->list); 21262306a36Sopenharmony_ci c->target_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; 21362306a36Sopenharmony_ci c->default_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; 21462306a36Sopenharmony_ci c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; 21562306a36Sopenharmony_ci c->type = PM_QOS_MIN; 21662306a36Sopenharmony_ci c->notifiers = n; 21762306a36Sopenharmony_ci BLOCKING_INIT_NOTIFIER_HEAD(n); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci c = &qos->latency_tolerance; 22062306a36Sopenharmony_ci plist_head_init(&c->list); 22162306a36Sopenharmony_ci c->target_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE; 22262306a36Sopenharmony_ci c->default_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE; 22362306a36Sopenharmony_ci c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; 22462306a36Sopenharmony_ci c->type = PM_QOS_MIN; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci freq_constraints_init(&qos->freq); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci INIT_LIST_HEAD(&qos->flags.list); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci spin_lock_irq(&dev->power.lock); 23162306a36Sopenharmony_ci dev->power.qos = qos; 23262306a36Sopenharmony_ci spin_unlock_irq(&dev->power.lock); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void __dev_pm_qos_hide_latency_limit(struct device *dev); 23862306a36Sopenharmony_cistatic void __dev_pm_qos_hide_flags(struct device *dev); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/** 24162306a36Sopenharmony_ci * dev_pm_qos_constraints_destroy 24262306a36Sopenharmony_ci * @dev: target device 24362306a36Sopenharmony_ci * 24462306a36Sopenharmony_ci * Called from the device PM subsystem on device removal under device_pm_lock(). 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_civoid dev_pm_qos_constraints_destroy(struct device *dev) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct dev_pm_qos *qos; 24962306a36Sopenharmony_ci struct dev_pm_qos_request *req, *tmp; 25062306a36Sopenharmony_ci struct pm_qos_constraints *c; 25162306a36Sopenharmony_ci struct pm_qos_flags *f; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_sysfs_mtx); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* 25662306a36Sopenharmony_ci * If the device's PM QoS resume latency limit or PM QoS flags have been 25762306a36Sopenharmony_ci * exposed to user space, they have to be hidden at this point. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci pm_qos_sysfs_remove_resume_latency(dev); 26062306a36Sopenharmony_ci pm_qos_sysfs_remove_flags(dev); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci __dev_pm_qos_hide_latency_limit(dev); 26562306a36Sopenharmony_ci __dev_pm_qos_hide_flags(dev); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci qos = dev->power.qos; 26862306a36Sopenharmony_ci if (!qos) 26962306a36Sopenharmony_ci goto out; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Flush the constraints lists for the device. */ 27262306a36Sopenharmony_ci c = &qos->resume_latency; 27362306a36Sopenharmony_ci plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * Update constraints list and call the notification 27662306a36Sopenharmony_ci * callbacks if needed 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); 27962306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci c = &qos->latency_tolerance; 28362306a36Sopenharmony_ci plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { 28462306a36Sopenharmony_ci apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); 28562306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci c = &qos->freq.min_freq; 28962306a36Sopenharmony_ci plist_for_each_entry_safe(req, tmp, &c->list, data.freq.pnode) { 29062306a36Sopenharmony_ci apply_constraint(req, PM_QOS_REMOVE_REQ, 29162306a36Sopenharmony_ci PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE); 29262306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci c = &qos->freq.max_freq; 29662306a36Sopenharmony_ci plist_for_each_entry_safe(req, tmp, &c->list, data.freq.pnode) { 29762306a36Sopenharmony_ci apply_constraint(req, PM_QOS_REMOVE_REQ, 29862306a36Sopenharmony_ci PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE); 29962306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci f = &qos->flags; 30362306a36Sopenharmony_ci list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { 30462306a36Sopenharmony_ci apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); 30562306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci spin_lock_irq(&dev->power.lock); 30962306a36Sopenharmony_ci dev->power.qos = ERR_PTR(-ENODEV); 31062306a36Sopenharmony_ci spin_unlock_irq(&dev->power.lock); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci kfree(qos->resume_latency.notifiers); 31362306a36Sopenharmony_ci kfree(qos); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci out: 31662306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_sysfs_mtx); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic bool dev_pm_qos_invalid_req_type(struct device *dev, 32262306a36Sopenharmony_ci enum dev_pm_qos_req_type type) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci return type == DEV_PM_QOS_LATENCY_TOLERANCE && 32562306a36Sopenharmony_ci !dev->power.set_latency_tolerance; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int __dev_pm_qos_add_request(struct device *dev, 32962306a36Sopenharmony_ci struct dev_pm_qos_request *req, 33062306a36Sopenharmony_ci enum dev_pm_qos_req_type type, s32 value) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci int ret = 0; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!dev || !req || dev_pm_qos_invalid_req_type(dev, type)) 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (WARN(dev_pm_qos_request_active(req), 33862306a36Sopenharmony_ci "%s() called for already added request\n", __func__)) 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (IS_ERR(dev->power.qos)) 34262306a36Sopenharmony_ci ret = -ENODEV; 34362306a36Sopenharmony_ci else if (!dev->power.qos) 34462306a36Sopenharmony_ci ret = dev_pm_qos_constraints_allocate(dev); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci trace_dev_pm_qos_add_request(dev_name(dev), type, value); 34762306a36Sopenharmony_ci if (ret) 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci req->dev = dev; 35162306a36Sopenharmony_ci req->type = type; 35262306a36Sopenharmony_ci if (req->type == DEV_PM_QOS_MIN_FREQUENCY) 35362306a36Sopenharmony_ci ret = freq_qos_add_request(&dev->power.qos->freq, 35462306a36Sopenharmony_ci &req->data.freq, 35562306a36Sopenharmony_ci FREQ_QOS_MIN, value); 35662306a36Sopenharmony_ci else if (req->type == DEV_PM_QOS_MAX_FREQUENCY) 35762306a36Sopenharmony_ci ret = freq_qos_add_request(&dev->power.qos->freq, 35862306a36Sopenharmony_ci &req->data.freq, 35962306a36Sopenharmony_ci FREQ_QOS_MAX, value); 36062306a36Sopenharmony_ci else 36162306a36Sopenharmony_ci ret = apply_constraint(req, PM_QOS_ADD_REQ, value); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return ret; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/** 36762306a36Sopenharmony_ci * dev_pm_qos_add_request - inserts new qos request into the list 36862306a36Sopenharmony_ci * @dev: target device for the constraint 36962306a36Sopenharmony_ci * @req: pointer to a preallocated handle 37062306a36Sopenharmony_ci * @type: type of the request 37162306a36Sopenharmony_ci * @value: defines the qos request 37262306a36Sopenharmony_ci * 37362306a36Sopenharmony_ci * This function inserts a new entry in the device constraints list of 37462306a36Sopenharmony_ci * requested qos performance characteristics. It recomputes the aggregate 37562306a36Sopenharmony_ci * QoS expectations of parameters and initializes the dev_pm_qos_request 37662306a36Sopenharmony_ci * handle. Caller needs to save this handle for later use in updates and 37762306a36Sopenharmony_ci * removal. 37862306a36Sopenharmony_ci * 37962306a36Sopenharmony_ci * Returns 1 if the aggregated constraint value has changed, 38062306a36Sopenharmony_ci * 0 if the aggregated constraint value has not changed, 38162306a36Sopenharmony_ci * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory 38262306a36Sopenharmony_ci * to allocate for data structures, -ENODEV if the device has just been removed 38362306a36Sopenharmony_ci * from the system. 38462306a36Sopenharmony_ci * 38562306a36Sopenharmony_ci * Callers should ensure that the target device is not RPM_SUSPENDED before 38662306a36Sopenharmony_ci * using this function for requests of type DEV_PM_QOS_FLAGS. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ciint dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, 38962306a36Sopenharmony_ci enum dev_pm_qos_req_type type, s32 value) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci int ret; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 39462306a36Sopenharmony_ci ret = __dev_pm_qos_add_request(dev, req, type, value); 39562306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_add_request); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/** 40162306a36Sopenharmony_ci * __dev_pm_qos_update_request - Modify an existing device PM QoS request. 40262306a36Sopenharmony_ci * @req : PM QoS request to modify. 40362306a36Sopenharmony_ci * @new_value: New value to request. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_cistatic int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, 40662306a36Sopenharmony_ci s32 new_value) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci s32 curr_value; 40962306a36Sopenharmony_ci int ret = 0; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!req) /*guard against callers passing in null */ 41262306a36Sopenharmony_ci return -EINVAL; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (WARN(!dev_pm_qos_request_active(req), 41562306a36Sopenharmony_ci "%s() called for unknown object\n", __func__)) 41662306a36Sopenharmony_ci return -EINVAL; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (IS_ERR_OR_NULL(req->dev->power.qos)) 41962306a36Sopenharmony_ci return -ENODEV; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci switch(req->type) { 42262306a36Sopenharmony_ci case DEV_PM_QOS_RESUME_LATENCY: 42362306a36Sopenharmony_ci case DEV_PM_QOS_LATENCY_TOLERANCE: 42462306a36Sopenharmony_ci curr_value = req->data.pnode.prio; 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci case DEV_PM_QOS_MIN_FREQUENCY: 42762306a36Sopenharmony_ci case DEV_PM_QOS_MAX_FREQUENCY: 42862306a36Sopenharmony_ci curr_value = req->data.freq.pnode.prio; 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci case DEV_PM_QOS_FLAGS: 43162306a36Sopenharmony_ci curr_value = req->data.flr.flags; 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci default: 43462306a36Sopenharmony_ci return -EINVAL; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci trace_dev_pm_qos_update_request(dev_name(req->dev), req->type, 43862306a36Sopenharmony_ci new_value); 43962306a36Sopenharmony_ci if (curr_value != new_value) 44062306a36Sopenharmony_ci ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return ret; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/** 44662306a36Sopenharmony_ci * dev_pm_qos_update_request - modifies an existing qos request 44762306a36Sopenharmony_ci * @req : handle to list element holding a dev_pm_qos request to use 44862306a36Sopenharmony_ci * @new_value: defines the qos request 44962306a36Sopenharmony_ci * 45062306a36Sopenharmony_ci * Updates an existing dev PM qos request along with updating the 45162306a36Sopenharmony_ci * target value. 45262306a36Sopenharmony_ci * 45362306a36Sopenharmony_ci * Attempts are made to make this code callable on hot code paths. 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * Returns 1 if the aggregated constraint value has changed, 45662306a36Sopenharmony_ci * 0 if the aggregated constraint value has not changed, 45762306a36Sopenharmony_ci * -EINVAL in case of wrong parameters, -ENODEV if the device has been 45862306a36Sopenharmony_ci * removed from the system 45962306a36Sopenharmony_ci * 46062306a36Sopenharmony_ci * Callers should ensure that the target device is not RPM_SUSPENDED before 46162306a36Sopenharmony_ci * using this function for requests of type DEV_PM_QOS_FLAGS. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ciint dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci int ret; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 46862306a36Sopenharmony_ci ret = __dev_pm_qos_update_request(req, new_value); 46962306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 47062306a36Sopenharmony_ci return ret; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_update_request); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci int ret; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (!req) /*guard against callers passing in null */ 47962306a36Sopenharmony_ci return -EINVAL; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (WARN(!dev_pm_qos_request_active(req), 48262306a36Sopenharmony_ci "%s() called for unknown object\n", __func__)) 48362306a36Sopenharmony_ci return -EINVAL; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(req->dev->power.qos)) 48662306a36Sopenharmony_ci return -ENODEV; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci trace_dev_pm_qos_remove_request(dev_name(req->dev), req->type, 48962306a36Sopenharmony_ci PM_QOS_DEFAULT_VALUE); 49062306a36Sopenharmony_ci ret = apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); 49162306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 49262306a36Sopenharmony_ci return ret; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci/** 49662306a36Sopenharmony_ci * dev_pm_qos_remove_request - modifies an existing qos request 49762306a36Sopenharmony_ci * @req: handle to request list element 49862306a36Sopenharmony_ci * 49962306a36Sopenharmony_ci * Will remove pm qos request from the list of constraints and 50062306a36Sopenharmony_ci * recompute the current target value. Call this on slow code paths. 50162306a36Sopenharmony_ci * 50262306a36Sopenharmony_ci * Returns 1 if the aggregated constraint value has changed, 50362306a36Sopenharmony_ci * 0 if the aggregated constraint value has not changed, 50462306a36Sopenharmony_ci * -EINVAL in case of wrong parameters, -ENODEV if the device has been 50562306a36Sopenharmony_ci * removed from the system 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * Callers should ensure that the target device is not RPM_SUSPENDED before 50862306a36Sopenharmony_ci * using this function for requests of type DEV_PM_QOS_FLAGS. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ciint dev_pm_qos_remove_request(struct dev_pm_qos_request *req) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci int ret; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 51562306a36Sopenharmony_ci ret = __dev_pm_qos_remove_request(req); 51662306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 51762306a36Sopenharmony_ci return ret; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/** 52262306a36Sopenharmony_ci * dev_pm_qos_add_notifier - sets notification entry for changes to target value 52362306a36Sopenharmony_ci * of per-device PM QoS constraints 52462306a36Sopenharmony_ci * 52562306a36Sopenharmony_ci * @dev: target device for the constraint 52662306a36Sopenharmony_ci * @notifier: notifier block managed by caller. 52762306a36Sopenharmony_ci * @type: request type. 52862306a36Sopenharmony_ci * 52962306a36Sopenharmony_ci * Will register the notifier into a notification chain that gets called 53062306a36Sopenharmony_ci * upon changes to the target value for the device. 53162306a36Sopenharmony_ci * 53262306a36Sopenharmony_ci * If the device's constraints object doesn't exist when this routine is called, 53362306a36Sopenharmony_ci * it will be created (or error code will be returned if that fails). 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ciint dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, 53662306a36Sopenharmony_ci enum dev_pm_qos_req_type type) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci int ret = 0; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (IS_ERR(dev->power.qos)) 54362306a36Sopenharmony_ci ret = -ENODEV; 54462306a36Sopenharmony_ci else if (!dev->power.qos) 54562306a36Sopenharmony_ci ret = dev_pm_qos_constraints_allocate(dev); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (ret) 54862306a36Sopenharmony_ci goto unlock; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci switch (type) { 55162306a36Sopenharmony_ci case DEV_PM_QOS_RESUME_LATENCY: 55262306a36Sopenharmony_ci ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers, 55362306a36Sopenharmony_ci notifier); 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci case DEV_PM_QOS_MIN_FREQUENCY: 55662306a36Sopenharmony_ci ret = freq_qos_add_notifier(&dev->power.qos->freq, 55762306a36Sopenharmony_ci FREQ_QOS_MIN, notifier); 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci case DEV_PM_QOS_MAX_FREQUENCY: 56062306a36Sopenharmony_ci ret = freq_qos_add_notifier(&dev->power.qos->freq, 56162306a36Sopenharmony_ci FREQ_QOS_MAX, notifier); 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci default: 56462306a36Sopenharmony_ci WARN_ON(1); 56562306a36Sopenharmony_ci ret = -EINVAL; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciunlock: 56962306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci/** 57562306a36Sopenharmony_ci * dev_pm_qos_remove_notifier - deletes notification for changes to target value 57662306a36Sopenharmony_ci * of per-device PM QoS constraints 57762306a36Sopenharmony_ci * 57862306a36Sopenharmony_ci * @dev: target device for the constraint 57962306a36Sopenharmony_ci * @notifier: notifier block to be removed. 58062306a36Sopenharmony_ci * @type: request type. 58162306a36Sopenharmony_ci * 58262306a36Sopenharmony_ci * Will remove the notifier from the notification chain that gets called 58362306a36Sopenharmony_ci * upon changes to the target value. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ciint dev_pm_qos_remove_notifier(struct device *dev, 58662306a36Sopenharmony_ci struct notifier_block *notifier, 58762306a36Sopenharmony_ci enum dev_pm_qos_req_type type) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci int ret = 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Silently return if the constraints object is not present. */ 59462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dev->power.qos)) 59562306a36Sopenharmony_ci goto unlock; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci switch (type) { 59862306a36Sopenharmony_ci case DEV_PM_QOS_RESUME_LATENCY: 59962306a36Sopenharmony_ci ret = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers, 60062306a36Sopenharmony_ci notifier); 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci case DEV_PM_QOS_MIN_FREQUENCY: 60362306a36Sopenharmony_ci ret = freq_qos_remove_notifier(&dev->power.qos->freq, 60462306a36Sopenharmony_ci FREQ_QOS_MIN, notifier); 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci case DEV_PM_QOS_MAX_FREQUENCY: 60762306a36Sopenharmony_ci ret = freq_qos_remove_notifier(&dev->power.qos->freq, 60862306a36Sopenharmony_ci FREQ_QOS_MAX, notifier); 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci default: 61162306a36Sopenharmony_ci WARN_ON(1); 61262306a36Sopenharmony_ci ret = -EINVAL; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ciunlock: 61662306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 61762306a36Sopenharmony_ci return ret; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/** 62262306a36Sopenharmony_ci * dev_pm_qos_add_ancestor_request - Add PM QoS request for device's ancestor. 62362306a36Sopenharmony_ci * @dev: Device whose ancestor to add the request for. 62462306a36Sopenharmony_ci * @req: Pointer to the preallocated handle. 62562306a36Sopenharmony_ci * @type: Type of the request. 62662306a36Sopenharmony_ci * @value: Constraint latency value. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ciint dev_pm_qos_add_ancestor_request(struct device *dev, 62962306a36Sopenharmony_ci struct dev_pm_qos_request *req, 63062306a36Sopenharmony_ci enum dev_pm_qos_req_type type, s32 value) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct device *ancestor = dev->parent; 63362306a36Sopenharmony_ci int ret = -ENODEV; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci switch (type) { 63662306a36Sopenharmony_ci case DEV_PM_QOS_RESUME_LATENCY: 63762306a36Sopenharmony_ci while (ancestor && !ancestor->power.ignore_children) 63862306a36Sopenharmony_ci ancestor = ancestor->parent; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci case DEV_PM_QOS_LATENCY_TOLERANCE: 64262306a36Sopenharmony_ci while (ancestor && !ancestor->power.set_latency_tolerance) 64362306a36Sopenharmony_ci ancestor = ancestor->parent; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci default: 64762306a36Sopenharmony_ci ancestor = NULL; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci if (ancestor) 65062306a36Sopenharmony_ci ret = dev_pm_qos_add_request(ancestor, req, type, value); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (ret < 0) 65362306a36Sopenharmony_ci req->dev = NULL; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return ret; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void __dev_pm_qos_drop_user_request(struct device *dev, 66062306a36Sopenharmony_ci enum dev_pm_qos_req_type type) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct dev_pm_qos_request *req = NULL; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci switch(type) { 66562306a36Sopenharmony_ci case DEV_PM_QOS_RESUME_LATENCY: 66662306a36Sopenharmony_ci req = dev->power.qos->resume_latency_req; 66762306a36Sopenharmony_ci dev->power.qos->resume_latency_req = NULL; 66862306a36Sopenharmony_ci break; 66962306a36Sopenharmony_ci case DEV_PM_QOS_LATENCY_TOLERANCE: 67062306a36Sopenharmony_ci req = dev->power.qos->latency_tolerance_req; 67162306a36Sopenharmony_ci dev->power.qos->latency_tolerance_req = NULL; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case DEV_PM_QOS_FLAGS: 67462306a36Sopenharmony_ci req = dev->power.qos->flags_req; 67562306a36Sopenharmony_ci dev->power.qos->flags_req = NULL; 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci default: 67862306a36Sopenharmony_ci WARN_ON(1); 67962306a36Sopenharmony_ci return; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci __dev_pm_qos_remove_request(req); 68262306a36Sopenharmony_ci kfree(req); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic void dev_pm_qos_drop_user_request(struct device *dev, 68662306a36Sopenharmony_ci enum dev_pm_qos_req_type type) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 68962306a36Sopenharmony_ci __dev_pm_qos_drop_user_request(dev, type); 69062306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci/** 69462306a36Sopenharmony_ci * dev_pm_qos_expose_latency_limit - Expose PM QoS latency limit to user space. 69562306a36Sopenharmony_ci * @dev: Device whose PM QoS latency limit is to be exposed to user space. 69662306a36Sopenharmony_ci * @value: Initial value of the latency limit. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ciint dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct dev_pm_qos_request *req; 70162306a36Sopenharmony_ci int ret; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (!device_is_registered(dev) || value < 0) 70462306a36Sopenharmony_ci return -EINVAL; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci req = kzalloc(sizeof(*req), GFP_KERNEL); 70762306a36Sopenharmony_ci if (!req) 70862306a36Sopenharmony_ci return -ENOMEM; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_RESUME_LATENCY, value); 71162306a36Sopenharmony_ci if (ret < 0) { 71262306a36Sopenharmony_ci kfree(req); 71362306a36Sopenharmony_ci return ret; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_sysfs_mtx); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dev->power.qos)) 72162306a36Sopenharmony_ci ret = -ENODEV; 72262306a36Sopenharmony_ci else if (dev->power.qos->resume_latency_req) 72362306a36Sopenharmony_ci ret = -EEXIST; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (ret < 0) { 72662306a36Sopenharmony_ci __dev_pm_qos_remove_request(req); 72762306a36Sopenharmony_ci kfree(req); 72862306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 72962306a36Sopenharmony_ci goto out; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci dev->power.qos->resume_latency_req = req; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci ret = pm_qos_sysfs_add_resume_latency(dev); 73662306a36Sopenharmony_ci if (ret) 73762306a36Sopenharmony_ci dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_RESUME_LATENCY); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci out: 74062306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_sysfs_mtx); 74162306a36Sopenharmony_ci return ret; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic void __dev_pm_qos_hide_latency_limit(struct device *dev) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->resume_latency_req) 74862306a36Sopenharmony_ci __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_RESUME_LATENCY); 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/** 75262306a36Sopenharmony_ci * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space. 75362306a36Sopenharmony_ci * @dev: Device whose PM QoS latency limit is to be hidden from user space. 75462306a36Sopenharmony_ci */ 75562306a36Sopenharmony_civoid dev_pm_qos_hide_latency_limit(struct device *dev) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_sysfs_mtx); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci pm_qos_sysfs_remove_resume_latency(dev); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 76262306a36Sopenharmony_ci __dev_pm_qos_hide_latency_limit(dev); 76362306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_sysfs_mtx); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/** 77062306a36Sopenharmony_ci * dev_pm_qos_expose_flags - Expose PM QoS flags of a device to user space. 77162306a36Sopenharmony_ci * @dev: Device whose PM QoS flags are to be exposed to user space. 77262306a36Sopenharmony_ci * @val: Initial values of the flags. 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_ciint dev_pm_qos_expose_flags(struct device *dev, s32 val) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci struct dev_pm_qos_request *req; 77762306a36Sopenharmony_ci int ret; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (!device_is_registered(dev)) 78062306a36Sopenharmony_ci return -EINVAL; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci req = kzalloc(sizeof(*req), GFP_KERNEL); 78362306a36Sopenharmony_ci if (!req) 78462306a36Sopenharmony_ci return -ENOMEM; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val); 78762306a36Sopenharmony_ci if (ret < 0) { 78862306a36Sopenharmony_ci kfree(req); 78962306a36Sopenharmony_ci return ret; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci pm_runtime_get_sync(dev); 79362306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_sysfs_mtx); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dev->power.qos)) 79862306a36Sopenharmony_ci ret = -ENODEV; 79962306a36Sopenharmony_ci else if (dev->power.qos->flags_req) 80062306a36Sopenharmony_ci ret = -EEXIST; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (ret < 0) { 80362306a36Sopenharmony_ci __dev_pm_qos_remove_request(req); 80462306a36Sopenharmony_ci kfree(req); 80562306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 80662306a36Sopenharmony_ci goto out; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci dev->power.qos->flags_req = req; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci ret = pm_qos_sysfs_add_flags(dev); 81362306a36Sopenharmony_ci if (ret) 81462306a36Sopenharmony_ci dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci out: 81762306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_sysfs_mtx); 81862306a36Sopenharmony_ci pm_runtime_put(dev); 81962306a36Sopenharmony_ci return ret; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic void __dev_pm_qos_hide_flags(struct device *dev) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->flags_req) 82662306a36Sopenharmony_ci __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci/** 83062306a36Sopenharmony_ci * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. 83162306a36Sopenharmony_ci * @dev: Device whose PM QoS flags are to be hidden from user space. 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_civoid dev_pm_qos_hide_flags(struct device *dev) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci pm_runtime_get_sync(dev); 83662306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_sysfs_mtx); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci pm_qos_sysfs_remove_flags(dev); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 84162306a36Sopenharmony_ci __dev_pm_qos_hide_flags(dev); 84262306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_sysfs_mtx); 84562306a36Sopenharmony_ci pm_runtime_put(dev); 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci/** 85062306a36Sopenharmony_ci * dev_pm_qos_update_flags - Update PM QoS flags request owned by user space. 85162306a36Sopenharmony_ci * @dev: Device to update the PM QoS flags request for. 85262306a36Sopenharmony_ci * @mask: Flags to set/clear. 85362306a36Sopenharmony_ci * @set: Whether to set or clear the flags (true means set). 85462306a36Sopenharmony_ci */ 85562306a36Sopenharmony_ciint dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci s32 value; 85862306a36Sopenharmony_ci int ret; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci pm_runtime_get_sync(dev); 86162306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dev->power.qos) || !dev->power.qos->flags_req) { 86462306a36Sopenharmony_ci ret = -EINVAL; 86562306a36Sopenharmony_ci goto out; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci value = dev_pm_qos_requested_flags(dev); 86962306a36Sopenharmony_ci if (set) 87062306a36Sopenharmony_ci value |= mask; 87162306a36Sopenharmony_ci else 87262306a36Sopenharmony_ci value &= ~mask; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci out: 87762306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 87862306a36Sopenharmony_ci pm_runtime_put(dev); 87962306a36Sopenharmony_ci return ret; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci/** 88362306a36Sopenharmony_ci * dev_pm_qos_get_user_latency_tolerance - Get user space latency tolerance. 88462306a36Sopenharmony_ci * @dev: Device to obtain the user space latency tolerance for. 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_cis32 dev_pm_qos_get_user_latency_tolerance(struct device *dev) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci s32 ret; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 89162306a36Sopenharmony_ci ret = IS_ERR_OR_NULL(dev->power.qos) 89262306a36Sopenharmony_ci || !dev->power.qos->latency_tolerance_req ? 89362306a36Sopenharmony_ci PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT : 89462306a36Sopenharmony_ci dev->power.qos->latency_tolerance_req->data.pnode.prio; 89562306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 89662306a36Sopenharmony_ci return ret; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/** 90062306a36Sopenharmony_ci * dev_pm_qos_update_user_latency_tolerance - Update user space latency tolerance. 90162306a36Sopenharmony_ci * @dev: Device to update the user space latency tolerance for. 90262306a36Sopenharmony_ci * @val: New user space latency tolerance for @dev (negative values disable). 90362306a36Sopenharmony_ci */ 90462306a36Sopenharmony_ciint dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci int ret; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_mtx); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dev->power.qos) 91162306a36Sopenharmony_ci || !dev->power.qos->latency_tolerance_req) { 91262306a36Sopenharmony_ci struct dev_pm_qos_request *req; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (val < 0) { 91562306a36Sopenharmony_ci if (val == PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT) 91662306a36Sopenharmony_ci ret = 0; 91762306a36Sopenharmony_ci else 91862306a36Sopenharmony_ci ret = -EINVAL; 91962306a36Sopenharmony_ci goto out; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci req = kzalloc(sizeof(*req), GFP_KERNEL); 92262306a36Sopenharmony_ci if (!req) { 92362306a36Sopenharmony_ci ret = -ENOMEM; 92462306a36Sopenharmony_ci goto out; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci ret = __dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY_TOLERANCE, val); 92762306a36Sopenharmony_ci if (ret < 0) { 92862306a36Sopenharmony_ci kfree(req); 92962306a36Sopenharmony_ci goto out; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci dev->power.qos->latency_tolerance_req = req; 93262306a36Sopenharmony_ci } else { 93362306a36Sopenharmony_ci if (val < 0) { 93462306a36Sopenharmony_ci __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY_TOLERANCE); 93562306a36Sopenharmony_ci ret = 0; 93662306a36Sopenharmony_ci } else { 93762306a36Sopenharmony_ci ret = __dev_pm_qos_update_request(dev->power.qos->latency_tolerance_req, val); 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci out: 94262306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_mtx); 94362306a36Sopenharmony_ci return ret; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_update_user_latency_tolerance); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci/** 94862306a36Sopenharmony_ci * dev_pm_qos_expose_latency_tolerance - Expose latency tolerance to userspace 94962306a36Sopenharmony_ci * @dev: Device whose latency tolerance to expose 95062306a36Sopenharmony_ci */ 95162306a36Sopenharmony_ciint dev_pm_qos_expose_latency_tolerance(struct device *dev) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci int ret; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (!dev->power.set_latency_tolerance) 95662306a36Sopenharmony_ci return -EINVAL; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_sysfs_mtx); 95962306a36Sopenharmony_ci ret = pm_qos_sysfs_add_latency_tolerance(dev); 96062306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_sysfs_mtx); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci return ret; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_tolerance); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/** 96762306a36Sopenharmony_ci * dev_pm_qos_hide_latency_tolerance - Hide latency tolerance from userspace 96862306a36Sopenharmony_ci * @dev: Device whose latency tolerance to hide 96962306a36Sopenharmony_ci */ 97062306a36Sopenharmony_civoid dev_pm_qos_hide_latency_tolerance(struct device *dev) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci mutex_lock(&dev_pm_qos_sysfs_mtx); 97362306a36Sopenharmony_ci pm_qos_sysfs_remove_latency_tolerance(dev); 97462306a36Sopenharmony_ci mutex_unlock(&dev_pm_qos_sysfs_mtx); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Remove the request from user space now */ 97762306a36Sopenharmony_ci pm_runtime_get_sync(dev); 97862306a36Sopenharmony_ci dev_pm_qos_update_user_latency_tolerance(dev, 97962306a36Sopenharmony_ci PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT); 98062306a36Sopenharmony_ci pm_runtime_put(dev); 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_tolerance); 983