aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/devfreq/devfreq-event.c
diff options
context:
space:
mode:
authorChanwoo Choi <cw00.choi@samsung.com>2015-01-25 23:16:27 -0500
committerMyungJoo Ham <myungjoo.ham@samsung.com>2015-01-30 03:56:40 -0500
commitf262f28c147051e7aa6daaf4fb5996833ffadff4 (patch)
treed12bfa2a472245c2ac39a9a734f6cc2c0541fc3c /drivers/devfreq/devfreq-event.c
parent6234f38016ad56321ad0e4bfb57a10a3d940380a (diff)
PM / devfreq: event: Add devfreq_event class
This patch adds a new class in devfreq, devfreq_event, which provides raw data (e.g., memory bus utilization, GPU utilization) for devfreq governors. - devfreq_event device : Provides raw data for a governor of a devfreq device - devfreq device : Monitors device state and changes frequency/voltage of the device using the raw data from its devfreq_event device. A devfreq device dertermines performance states (normally the frequency and the voltage vlues) based on the results its designtated devfreq governor: e.g., ondemand, performance, powersave. In order to give such results required by a devfreq device, the devfreq governor requires data that indicates the performance requirement given to the devfreq device. The conventional (previous) implementatino of devfreq subsystem requires a devfreq device driver to implement its own mechanism to acquire performance requirement for its governor. However, there had been issues with such requirements: 1. Although performance requirement of such devices is usually acquired from common devices (PMU/PPMU), we do not have any abstract structure to represent them properly. 2. Such performance requirement devices (PMU/PPMU) are actual hardware pieces that may be represented by Device Tree directly while devfreq device itself is a virtual entity that are not considered to be represented by Device Tree according to Device Tree folks. In order to address such issues, a devferq_event device (represented by this patch) provides a template for device drivers representing performance monitoring unit, which gives the basic or raw data for preformance requirement, which in turn, is required by devfreq governors. The following description explains the feature of two kind of devfreq class: - devfreq class (existing) : devfreq consumer device use raw data from devfreq_event device for determining proper current system state and change voltage/frequency dynamically using various governors. - devfreq_event class (new) : Provide measured raw data to devfreq device for governor Cc: MyungJoo Ham <myungjoo.ham@samsung.com> Cc: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> [Commit message rewritten & conflict resolved by MyungJoo] Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Diffstat (limited to 'drivers/devfreq/devfreq-event.c')
-rw-r--r--drivers/devfreq/devfreq-event.c494
1 files changed, 494 insertions, 0 deletions
diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c
new file mode 100644
index 000000000000..f304a0289eda
--- /dev/null
+++ b/drivers/devfreq/devfreq-event.c
@@ -0,0 +1,494 @@
1/*
2 * devfreq-event: a framework to provide raw data and events of devfreq devices
3 *
4 * Copyright (C) 2015 Samsung Electronics
5 * Author: Chanwoo Choi <cw00.choi@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This driver is based on drivers/devfreq/devfreq.c.
12 */
13
14#include <linux/devfreq-event.h>
15#include <linux/kernel.h>
16#include <linux/err.h>
17#include <linux/init.h>
18#include <linux/module.h>
19#include <linux/slab.h>
20#include <linux/list.h>
21#include <linux/of.h>
22
23static struct class *devfreq_event_class;
24
25/* The list of all devfreq event list */
26static LIST_HEAD(devfreq_event_list);
27static DEFINE_MUTEX(devfreq_event_list_lock);
28
29#define to_devfreq_event(DEV) container_of(DEV, struct devfreq_event_dev, dev)
30
31/**
32 * devfreq_event_enable_edev() - Enable the devfreq-event dev and increase
33 * the enable_count of devfreq-event dev.
34 * @edev : the devfreq-event device
35 *
36 * Note that this function increase the enable_count and enable the
37 * devfreq-event device. The devfreq-event device should be enabled before
38 * using it by devfreq device.
39 */
40int devfreq_event_enable_edev(struct devfreq_event_dev *edev)
41{
42 int ret = 0;
43
44 if (!edev || !edev->desc)
45 return -EINVAL;
46
47 mutex_lock(&edev->lock);
48 if (edev->desc->ops && edev->desc->ops->enable
49 && edev->enable_count == 0) {
50 ret = edev->desc->ops->enable(edev);
51 if (ret < 0)
52 goto err;
53 }
54 edev->enable_count++;
55err:
56 mutex_unlock(&edev->lock);
57
58 return ret;
59}
60EXPORT_SYMBOL_GPL(devfreq_event_enable_edev);
61
62/**
63 * devfreq_event_disable_edev() - Disable the devfreq-event dev and decrease
64 * the enable_count of the devfreq-event dev.
65 * @edev : the devfreq-event device
66 *
67 * Note that this function decrease the enable_count and disable the
68 * devfreq-event device. After the devfreq-event device is disabled,
69 * devfreq device can't use the devfreq-event device for get/set/reset
70 * operations.
71 */
72int devfreq_event_disable_edev(struct devfreq_event_dev *edev)
73{
74 int ret = 0;
75
76 if (!edev || !edev->desc)
77 return -EINVAL;
78
79 mutex_lock(&edev->lock);
80 if (edev->enable_count <= 0) {
81 dev_warn(&edev->dev, "unbalanced enable_count\n");
82 ret = -EIO;
83 goto err;
84 }
85
86 if (edev->desc->ops && edev->desc->ops->disable
87 && edev->enable_count == 1) {
88 ret = edev->desc->ops->disable(edev);
89 if (ret < 0)
90 goto err;
91 }
92 edev->enable_count--;
93err:
94 mutex_unlock(&edev->lock);
95
96 return ret;
97}
98EXPORT_SYMBOL_GPL(devfreq_event_disable_edev);
99
100/**
101 * devfreq_event_is_enabled() - Check whether devfreq-event dev is enabled or
102 * not.
103 * @edev : the devfreq-event device
104 *
105 * Note that this function check whether devfreq-event dev is enabled or not.
106 * If return true, the devfreq-event dev is enabeld. If return false, the
107 * devfreq-event dev is disabled.
108 */
109bool devfreq_event_is_enabled(struct devfreq_event_dev *edev)
110{
111 bool enabled = false;
112
113 if (!edev || !edev->desc)
114 return enabled;
115
116 mutex_lock(&edev->lock);
117
118 if (edev->enable_count > 0)
119 enabled = true;
120
121 mutex_unlock(&edev->lock);
122
123 return enabled;
124}
125EXPORT_SYMBOL_GPL(devfreq_event_is_enabled);
126
127/**
128 * devfreq_event_set_event() - Set event to devfreq-event dev to start.
129 * @edev : the devfreq-event device
130 *
131 * Note that this function set the event to the devfreq-event device to start
132 * for getting the event data which could be various event type.
133 */
134int devfreq_event_set_event(struct devfreq_event_dev *edev)
135{
136 int ret;
137
138 if (!edev || !edev->desc)
139 return -EINVAL;
140
141 if (!edev->desc->ops || !edev->desc->ops->set_event)
142 return -EINVAL;
143
144 if (!devfreq_event_is_enabled(edev))
145 return -EPERM;
146
147 mutex_lock(&edev->lock);
148 ret = edev->desc->ops->set_event(edev);
149 mutex_unlock(&edev->lock);
150
151 return ret;
152}
153EXPORT_SYMBOL_GPL(devfreq_event_set_event);
154
155/**
156 * devfreq_event_get_event() - Get {load|total}_count from devfreq-event dev.
157 * @edev : the devfreq-event device
158 * @edata : the calculated data of devfreq-event device
159 *
160 * Note that this function get the calculated event data from devfreq-event dev
161 * after stoping the progress of whole sequence of devfreq-event dev.
162 */
163int devfreq_event_get_event(struct devfreq_event_dev *edev,
164 struct devfreq_event_data *edata)
165{
166 int ret;
167
168 if (!edev || !edev->desc)
169 return -EINVAL;
170
171 if (!edev->desc->ops || !edev->desc->ops->get_event)
172 return -EINVAL;
173
174 if (!devfreq_event_is_enabled(edev))
175 return -EINVAL;
176
177 edata->total_count = edata->load_count = 0;
178
179 mutex_lock(&edev->lock);
180 ret = edev->desc->ops->get_event(edev, edata);
181 if (ret < 0)
182 edata->total_count = edata->load_count = 0;
183 mutex_unlock(&edev->lock);
184
185 return ret;
186}
187EXPORT_SYMBOL_GPL(devfreq_event_get_event);
188
189/**
190 * devfreq_event_reset_event() - Reset all opeations of devfreq-event dev.
191 * @edev : the devfreq-event device
192 *
193 * Note that this function stop all operations of devfreq-event dev and reset
194 * the current event data to make the devfreq-event device into initial state.
195 */
196int devfreq_event_reset_event(struct devfreq_event_dev *edev)
197{
198 int ret = 0;
199
200 if (!edev || !edev->desc)
201 return -EINVAL;
202
203 if (!devfreq_event_is_enabled(edev))
204 return -EPERM;
205
206 mutex_lock(&edev->lock);
207 if (edev->desc->ops && edev->desc->ops->reset)
208 ret = edev->desc->ops->reset(edev);
209 mutex_unlock(&edev->lock);
210
211 return ret;
212}
213EXPORT_SYMBOL_GPL(devfreq_event_reset_event);
214
215/**
216 * devfreq_event_get_edev_by_phandle() - Get the devfreq-event dev from
217 * devicetree.
218 * @dev : the pointer to the given device
219 * @index : the index into list of devfreq-event device
220 *
221 * Note that this function return the pointer of devfreq-event device.
222 */
223struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev,
224 int index)
225{
226 struct device_node *node;
227 struct devfreq_event_dev *edev;
228
229 if (!dev->of_node) {
230 dev_err(dev, "device does not have a device node entry\n");
231 return ERR_PTR(-EINVAL);
232 }
233
234 node = of_parse_phandle(dev->of_node, "devfreq-events", index);
235 if (!node) {
236 dev_err(dev, "failed to get phandle in %s node\n",
237 dev->of_node->full_name);
238 return ERR_PTR(-ENODEV);
239 }
240
241 mutex_lock(&devfreq_event_list_lock);
242 list_for_each_entry(edev, &devfreq_event_list, node) {
243 if (!strcmp(edev->desc->name, node->name))
244 goto out;
245 }
246 edev = NULL;
247out:
248 mutex_unlock(&devfreq_event_list_lock);
249
250 if (!edev) {
251 dev_err(dev, "unable to get devfreq-event device : %s\n",
252 node->name);
253 of_node_put(node);
254 return ERR_PTR(-ENODEV);
255 }
256
257 of_node_put(node);
258
259 return edev;
260}
261EXPORT_SYMBOL_GPL(devfreq_event_get_edev_by_phandle);
262
263/**
264 * devfreq_event_get_edev_count() - Get the count of devfreq-event dev
265 * @dev : the pointer to the given device
266 *
267 * Note that this function return the count of devfreq-event devices.
268 */
269int devfreq_event_get_edev_count(struct device *dev)
270{
271 int count;
272
273 if (!dev->of_node) {
274 dev_err(dev, "device does not have a device node entry\n");
275 return -EINVAL;
276 }
277
278 count = of_property_count_elems_of_size(dev->of_node, "devfreq-events",
279 sizeof(u32));
280 if (count < 0 ) {
281 dev_err(dev,
282 "failed to get the count of devfreq-event in %s node\n",
283 dev->of_node->full_name);
284 return count;
285 }
286
287 return count;
288}
289EXPORT_SYMBOL_GPL(devfreq_event_get_edev_count);
290
291static void devfreq_event_release_edev(struct device *dev)
292{
293 struct devfreq_event_dev *edev = to_devfreq_event(dev);
294
295 kfree(edev);
296}
297
298/**
299 * devfreq_event_add_edev() - Add new devfreq-event device.
300 * @dev : the device owning the devfreq-event device being created
301 * @desc : the devfreq-event device's decriptor which include essential
302 * data for devfreq-event device.
303 *
304 * Note that this function add new devfreq-event device to devfreq-event class
305 * list and register the device of the devfreq-event device.
306 */
307struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev,
308 struct devfreq_event_desc *desc)
309{
310 struct devfreq_event_dev *edev;
311 static atomic_t event_no = ATOMIC_INIT(0);
312 int ret;
313
314 if (!dev || !desc)
315 return ERR_PTR(-EINVAL);
316
317 if (!desc->name || !desc->ops)
318 return ERR_PTR(-EINVAL);
319
320 if (!desc->ops->set_event || !desc->ops->get_event)
321 return ERR_PTR(-EINVAL);
322
323 edev = kzalloc(sizeof(struct devfreq_event_dev), GFP_KERNEL);
324 if (!edev)
325 return ERR_PTR(-ENOMEM);
326
327 mutex_init(&edev->lock);
328 edev->desc = desc;
329 edev->enable_count = 0;
330 edev->dev.parent = dev;
331 edev->dev.class = devfreq_event_class;
332 edev->dev.release = devfreq_event_release_edev;
333
334 dev_set_name(&edev->dev, "event.%d", atomic_inc_return(&event_no) - 1);
335 ret = device_register(&edev->dev);
336 if (ret < 0) {
337 put_device(&edev->dev);
338 return ERR_PTR(ret);
339 }
340 dev_set_drvdata(&edev->dev, edev);
341
342 INIT_LIST_HEAD(&edev->node);
343
344 mutex_lock(&devfreq_event_list_lock);
345 list_add(&edev->node, &devfreq_event_list);
346 mutex_unlock(&devfreq_event_list_lock);
347
348 return edev;
349}
350EXPORT_SYMBOL_GPL(devfreq_event_add_edev);
351
352/**
353 * devfreq_event_remove_edev() - Remove the devfreq-event device registered.
354 * @dev : the devfreq-event device
355 *
356 * Note that this function remove the registered devfreq-event device.
357 */
358int devfreq_event_remove_edev(struct devfreq_event_dev *edev)
359{
360 if (!edev)
361 return -EINVAL;
362
363 WARN_ON(edev->enable_count);
364
365 mutex_lock(&devfreq_event_list_lock);
366 list_del(&edev->node);
367 mutex_unlock(&devfreq_event_list_lock);
368
369 device_unregister(&edev->dev);
370
371 return 0;
372}
373EXPORT_SYMBOL_GPL(devfreq_event_remove_edev);
374
375static int devm_devfreq_event_match(struct device *dev, void *res, void *data)
376{
377 struct devfreq_event_dev **r = res;
378
379 if (WARN_ON(!r || !*r))
380 return 0;
381
382 return *r == data;
383}
384
385static void devm_devfreq_event_release(struct device *dev, void *res)
386{
387 devfreq_event_remove_edev(*(struct devfreq_event_dev **)res);
388}
389
390/**
391 * devm_devfreq_event_add_edev() - Resource-managed devfreq_event_add_edev()
392 * @dev : the device owning the devfreq-event device being created
393 * @desc : the devfreq-event device's decriptor which include essential
394 * data for devfreq-event device.
395 *
396 * Note that this function manages automatically the memory of devfreq-event
397 * device using device resource management and simplify the free operation
398 * for memory of devfreq-event device.
399 */
400struct devfreq_event_dev *devm_devfreq_event_add_edev(struct device *dev,
401 struct devfreq_event_desc *desc)
402{
403 struct devfreq_event_dev **ptr, *edev;
404
405 ptr = devres_alloc(devm_devfreq_event_release, sizeof(*ptr), GFP_KERNEL);
406 if (!ptr)
407 return ERR_PTR(-ENOMEM);
408
409 edev = devfreq_event_add_edev(dev, desc);
410 if (IS_ERR(edev)) {
411 devres_free(ptr);
412 return ERR_PTR(-ENOMEM);
413 }
414
415 *ptr = edev;
416 devres_add(dev, ptr);
417
418 return edev;
419}
420EXPORT_SYMBOL_GPL(devm_devfreq_event_add_edev);
421
422/**
423 * devm_devfreq_event_remove_edev()- Resource-managed devfreq_event_remove_edev()
424 * @dev : the device owning the devfreq-event device being created
425 * @edev : the devfreq-event device
426 *
427 * Note that this function manages automatically the memory of devfreq-event
428 * device using device resource management.
429 */
430void devm_devfreq_event_remove_edev(struct device *dev,
431 struct devfreq_event_dev *edev)
432{
433 WARN_ON(devres_release(dev, devm_devfreq_event_release,
434 devm_devfreq_event_match, edev));
435}
436EXPORT_SYMBOL_GPL(devm_devfreq_event_remove_edev);
437
438/*
439 * Device attributes for devfreq-event class.
440 */
441static ssize_t name_show(struct device *dev, struct device_attribute *attr,
442 char *buf)
443{
444 struct devfreq_event_dev *edev = to_devfreq_event(dev);
445
446 if (!edev || !edev->desc)
447 return -EINVAL;
448
449 return sprintf(buf, "%s\n", edev->desc->name);
450}
451static DEVICE_ATTR_RO(name);
452
453static ssize_t enable_count_show(struct device *dev,
454 struct device_attribute *attr, char *buf)
455{
456 struct devfreq_event_dev *edev = to_devfreq_event(dev);
457
458 if (!edev || !edev->desc)
459 return -EINVAL;
460
461 return sprintf(buf, "%d\n", edev->enable_count);
462}
463static DEVICE_ATTR_RO(enable_count);
464
465static struct attribute *devfreq_event_attrs[] = {
466 &dev_attr_name.attr,
467 &dev_attr_enable_count.attr,
468 NULL,
469};
470ATTRIBUTE_GROUPS(devfreq_event);
471
472static int __init devfreq_event_init(void)
473{
474 devfreq_event_class = class_create(THIS_MODULE, "devfreq-event");
475 if (IS_ERR(devfreq_event_class)) {
476 pr_err("%s: couldn't create class\n", __FILE__);
477 return PTR_ERR(devfreq_event_class);
478 }
479
480 devfreq_event_class->dev_groups = devfreq_event_groups;
481
482 return 0;
483}
484subsys_initcall(devfreq_event_init);
485
486static void __exit devfreq_event_exit(void)
487{
488 class_destroy(devfreq_event_class);
489}
490module_exit(devfreq_event_exit);
491
492MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
493MODULE_DESCRIPTION("DEVFREQ-Event class support");
494MODULE_LICENSE("GPL");