diff options
author | Nishanth Menon <nm@ti.com> | 2012-10-29 16:01:43 -0400 |
---|---|---|
committer | MyungJoo Ham <myungjoo.ham@samsung.com> | 2012-11-20 04:46:12 -0500 |
commit | 3aa173b8db200bb96354481acc0a5b9e123119fe (patch) | |
tree | f24511e8b86dd4c482f8551317363ed07fb327a6 | |
parent | 2df5021fa9738905e8b1ab92fa0cd685430f54da (diff) |
PM / devfreq: provide hooks for governors to be registered
Add devfreq_add_governor and devfreq_remove_governor which
can be invoked by governors to register with devfreq.
This sets up the stage to dynamically switch governors and
allow governors to be dynamically loaded as well.
Cc: Rajagopal Venkat <rajagopal.venkat@linaro.org>
Cc: MyungJoo Ham <myungjoo.ham@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
Cc: Kevin Hilman <khilman@ti.com>
Cc: linux-pm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Nishanth Menon <nm@ti.com>
Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>
-rw-r--r-- | drivers/devfreq/devfreq.c | 91 | ||||
-rw-r--r-- | drivers/devfreq/governor.h | 4 | ||||
-rw-r--r-- | include/linux/devfreq.h | 3 |
3 files changed, 98 insertions, 0 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index e0002c5cbadc..679ac424472f 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c | |||
@@ -36,6 +36,8 @@ static struct class *devfreq_class; | |||
36 | */ | 36 | */ |
37 | static struct workqueue_struct *devfreq_wq; | 37 | static struct workqueue_struct *devfreq_wq; |
38 | 38 | ||
39 | /* The list of all device-devfreq governors */ | ||
40 | static LIST_HEAD(devfreq_governor_list); | ||
39 | /* The list of all device-devfreq */ | 41 | /* The list of all device-devfreq */ |
40 | static LIST_HEAD(devfreq_list); | 42 | static LIST_HEAD(devfreq_list); |
41 | static DEFINE_MUTEX(devfreq_list_lock); | 43 | static DEFINE_MUTEX(devfreq_list_lock); |
@@ -111,6 +113,32 @@ static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) | |||
111 | return 0; | 113 | return 0; |
112 | } | 114 | } |
113 | 115 | ||
116 | /** | ||
117 | * find_devfreq_governor() - find devfreq governor from name | ||
118 | * @name: name of the governor | ||
119 | * | ||
120 | * Search the list of devfreq governors and return the matched | ||
121 | * governor's pointer. devfreq_list_lock should be held by the caller. | ||
122 | */ | ||
123 | static struct devfreq_governor *find_devfreq_governor(const char *name) | ||
124 | { | ||
125 | struct devfreq_governor *tmp_governor; | ||
126 | |||
127 | if (unlikely(IS_ERR_OR_NULL(name))) { | ||
128 | pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); | ||
129 | return ERR_PTR(-EINVAL); | ||
130 | } | ||
131 | WARN(!mutex_is_locked(&devfreq_list_lock), | ||
132 | "devfreq_list_lock must be locked."); | ||
133 | |||
134 | list_for_each_entry(tmp_governor, &devfreq_governor_list, node) { | ||
135 | if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN)) | ||
136 | return tmp_governor; | ||
137 | } | ||
138 | |||
139 | return ERR_PTR(-ENODEV); | ||
140 | } | ||
141 | |||
114 | /* Load monitoring helper functions for governors use */ | 142 | /* Load monitoring helper functions for governors use */ |
115 | 143 | ||
116 | /** | 144 | /** |
@@ -515,6 +543,69 @@ int devfreq_resume_device(struct devfreq *devfreq) | |||
515 | } | 543 | } |
516 | EXPORT_SYMBOL(devfreq_resume_device); | 544 | EXPORT_SYMBOL(devfreq_resume_device); |
517 | 545 | ||
546 | /** | ||
547 | * devfreq_add_governor() - Add devfreq governor | ||
548 | * @governor: the devfreq governor to be added | ||
549 | */ | ||
550 | int devfreq_add_governor(struct devfreq_governor *governor) | ||
551 | { | ||
552 | struct devfreq_governor *g; | ||
553 | int err = 0; | ||
554 | |||
555 | if (!governor) { | ||
556 | pr_err("%s: Invalid parameters.\n", __func__); | ||
557 | return -EINVAL; | ||
558 | } | ||
559 | |||
560 | mutex_lock(&devfreq_list_lock); | ||
561 | g = find_devfreq_governor(governor->name); | ||
562 | if (!IS_ERR(g)) { | ||
563 | pr_err("%s: governor %s already registered\n", __func__, | ||
564 | g->name); | ||
565 | err = -EINVAL; | ||
566 | goto err_out; | ||
567 | } | ||
568 | |||
569 | list_add(&governor->node, &devfreq_governor_list); | ||
570 | |||
571 | err_out: | ||
572 | mutex_unlock(&devfreq_list_lock); | ||
573 | |||
574 | return err; | ||
575 | } | ||
576 | EXPORT_SYMBOL(devfreq_add_governor); | ||
577 | |||
578 | /** | ||
579 | * devfreq_remove_device() - Remove devfreq feature from a device. | ||
580 | * @governor: the devfreq governor to be removed | ||
581 | */ | ||
582 | int devfreq_remove_governor(struct devfreq_governor *governor) | ||
583 | { | ||
584 | struct devfreq_governor *g; | ||
585 | int err = 0; | ||
586 | |||
587 | if (!governor) { | ||
588 | pr_err("%s: Invalid parameters.\n", __func__); | ||
589 | return -EINVAL; | ||
590 | } | ||
591 | |||
592 | mutex_lock(&devfreq_list_lock); | ||
593 | g = find_devfreq_governor(governor->name); | ||
594 | if (IS_ERR(g)) { | ||
595 | pr_err("%s: governor %s not registered\n", __func__, | ||
596 | g->name); | ||
597 | err = -EINVAL; | ||
598 | goto err_out; | ||
599 | } | ||
600 | |||
601 | list_del(&governor->node); | ||
602 | err_out: | ||
603 | mutex_unlock(&devfreq_list_lock); | ||
604 | |||
605 | return err; | ||
606 | } | ||
607 | EXPORT_SYMBOL(devfreq_remove_governor); | ||
608 | |||
518 | static ssize_t show_governor(struct device *dev, | 609 | static ssize_t show_governor(struct device *dev, |
519 | struct device_attribute *attr, char *buf) | 610 | struct device_attribute *attr, char *buf) |
520 | { | 611 | { |
diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index 26432ac0a398..fad7d6321978 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h | |||
@@ -34,4 +34,8 @@ extern void devfreq_monitor_suspend(struct devfreq *devfreq); | |||
34 | extern void devfreq_monitor_resume(struct devfreq *devfreq); | 34 | extern void devfreq_monitor_resume(struct devfreq *devfreq); |
35 | extern void devfreq_interval_update(struct devfreq *devfreq, | 35 | extern void devfreq_interval_update(struct devfreq *devfreq, |
36 | unsigned int *delay); | 36 | unsigned int *delay); |
37 | |||
38 | extern int devfreq_add_governor(struct devfreq_governor *governor); | ||
39 | extern int devfreq_remove_governor(struct devfreq_governor *governor); | ||
40 | |||
37 | #endif /* _GOVERNOR_H */ | 41 | #endif /* _GOVERNOR_H */ |
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index bc35c4aee6a3..6484a3f8dda8 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h | |||
@@ -92,6 +92,7 @@ struct devfreq_dev_profile { | |||
92 | 92 | ||
93 | /** | 93 | /** |
94 | * struct devfreq_governor - Devfreq policy governor | 94 | * struct devfreq_governor - Devfreq policy governor |
95 | * @node: list node - contains registered devfreq governors | ||
95 | * @name: Governor's name | 96 | * @name: Governor's name |
96 | * @get_target_freq: Returns desired operating frequency for the device. | 97 | * @get_target_freq: Returns desired operating frequency for the device. |
97 | * Basically, get_target_freq will run | 98 | * Basically, get_target_freq will run |
@@ -107,6 +108,8 @@ struct devfreq_dev_profile { | |||
107 | * Note that the callbacks are called with devfreq->lock locked by devfreq. | 108 | * Note that the callbacks are called with devfreq->lock locked by devfreq. |
108 | */ | 109 | */ |
109 | struct devfreq_governor { | 110 | struct devfreq_governor { |
111 | struct list_head node; | ||
112 | |||
110 | const char name[DEVFREQ_NAME_LEN]; | 113 | const char name[DEVFREQ_NAME_LEN]; |
111 | int (*get_target_freq)(struct devfreq *this, unsigned long *freq); | 114 | int (*get_target_freq)(struct devfreq *this, unsigned long *freq); |
112 | int (*event_handler)(struct devfreq *devfreq, | 115 | int (*event_handler)(struct devfreq *devfreq, |