|
@@ -41,6 +41,8 @@
|
|
|
#include <linux/platform_device.h>
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/kernel.h>
|
|
|
+#include <linux/debugfs.h>
|
|
|
+#include <linux/seq_file.h>
|
|
|
|
|
|
#include <linux/uaccess.h>
|
|
|
#include <linux/export.h>
|
|
@@ -182,6 +184,81 @@ static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value)
|
|
|
c->target_value = value;
|
|
|
}
|
|
|
|
|
|
+static inline int pm_qos_get_value(struct pm_qos_constraints *c);
|
|
|
+static int pm_qos_dbg_show_requests(struct seq_file *s, void *unused)
|
|
|
+{
|
|
|
+ struct pm_qos_object *qos = (struct pm_qos_object *)s->private;
|
|
|
+ struct pm_qos_constraints *c;
|
|
|
+ struct pm_qos_request *req;
|
|
|
+ char *type;
|
|
|
+ unsigned long flags;
|
|
|
+ int tot_reqs = 0;
|
|
|
+ int active_reqs = 0;
|
|
|
+
|
|
|
+ if (IS_ERR_OR_NULL(qos)) {
|
|
|
+ pr_err("%s: bad qos param!\n", __func__);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ c = qos->constraints;
|
|
|
+ if (IS_ERR_OR_NULL(c)) {
|
|
|
+ pr_err("%s: Bad constraints on qos?\n", __func__);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Lock to ensure we have a snapshot */
|
|
|
+ spin_lock_irqsave(&pm_qos_lock, flags);
|
|
|
+ if (plist_head_empty(&c->list)) {
|
|
|
+ seq_puts(s, "Empty!\n");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (c->type) {
|
|
|
+ case PM_QOS_MIN:
|
|
|
+ type = "Minimum";
|
|
|
+ break;
|
|
|
+ case PM_QOS_MAX:
|
|
|
+ type = "Maximum";
|
|
|
+ break;
|
|
|
+ case PM_QOS_SUM:
|
|
|
+ type = "Sum";
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ type = "Unknown";
|
|
|
+ }
|
|
|
+
|
|
|
+ plist_for_each_entry(req, &c->list, node) {
|
|
|
+ char *state = "Default";
|
|
|
+
|
|
|
+ if ((req->node).prio != c->default_value) {
|
|
|
+ active_reqs++;
|
|
|
+ state = "Active";
|
|
|
+ }
|
|
|
+ tot_reqs++;
|
|
|
+ seq_printf(s, "%d: %d: %s\n", tot_reqs,
|
|
|
+ (req->node).prio, state);
|
|
|
+ }
|
|
|
+
|
|
|
+ seq_printf(s, "Type=%s, Value=%d, Requests: active=%d / total=%d\n",
|
|
|
+ type, pm_qos_get_value(c), active_reqs, tot_reqs);
|
|
|
+
|
|
|
+out:
|
|
|
+ spin_unlock_irqrestore(&pm_qos_lock, flags);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int pm_qos_dbg_open(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ return single_open(file, pm_qos_dbg_show_requests,
|
|
|
+ inode->i_private);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations pm_qos_debug_fops = {
|
|
|
+ .open = pm_qos_dbg_open,
|
|
|
+ .read = seq_read,
|
|
|
+ .llseek = seq_lseek,
|
|
|
+ .release = single_release,
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* pm_qos_update_target - manages the constraints list and calls the notifiers
|
|
|
* if needed
|
|
@@ -509,12 +586,17 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
|
|
|
EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
|
|
|
|
|
|
/* User space interface to PM QoS classes via misc devices */
|
|
|
-static int register_pm_qos_misc(struct pm_qos_object *qos)
|
|
|
+static int register_pm_qos_misc(struct pm_qos_object *qos, struct dentry *d)
|
|
|
{
|
|
|
qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
|
|
|
qos->pm_qos_power_miscdev.name = qos->name;
|
|
|
qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
|
|
|
|
|
|
+ if (d) {
|
|
|
+ (void)debugfs_create_file(qos->name, S_IRUGO, d,
|
|
|
+ (void *)qos, &pm_qos_debug_fops);
|
|
|
+ }
|
|
|
+
|
|
|
return misc_register(&qos->pm_qos_power_miscdev);
|
|
|
}
|
|
|
|
|
@@ -608,11 +690,16 @@ static int __init pm_qos_power_init(void)
|
|
|
{
|
|
|
int ret = 0;
|
|
|
int i;
|
|
|
+ struct dentry *d;
|
|
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(pm_qos_array) != PM_QOS_NUM_CLASSES);
|
|
|
|
|
|
+ d = debugfs_create_dir("pm_qos", NULL);
|
|
|
+ if (IS_ERR_OR_NULL(d))
|
|
|
+ d = NULL;
|
|
|
+
|
|
|
for (i = PM_QOS_CPU_DMA_LATENCY; i < PM_QOS_NUM_CLASSES; i++) {
|
|
|
- ret = register_pm_qos_misc(pm_qos_array[i]);
|
|
|
+ ret = register_pm_qos_misc(pm_qos_array[i], d);
|
|
|
if (ret < 0) {
|
|
|
printk(KERN_ERR "pm_qos_param: %s setup failed\n",
|
|
|
pm_qos_array[i]->name);
|